Esempio n. 1
0
    def compileflags(self, what):
        from mozbuild.util import resolve_target_to_make
        import shlex

        top_make = os.path.join(self.topobjdir, 'Makefile')
        if not os.path.exists(top_make):
            print('Your tree has not been built yet. Please run '
                '|mach build| with no arguments.')
            return 1

        path_arg = self._wrap_path_argument(what)

        make_dir, make_target = resolve_target_to_make(self.topobjdir,
            path_arg.relpath())

        if make_dir is None and make_target is None:
            return 1

        build_vars = {}

        def on_line(line):
            elements = [s.strip() for s in line.split('=', 1)]

            if len(elements) != 2:
                return

            build_vars[elements[0]] = elements[1]

        try:
            old_logger = self.log_manager.replace_terminal_handler(None)
            self._run_make(directory=make_dir, target='showbuild', log=False,
                    print_directory=False, allow_parallel=False, silent=True,
                    line_handler=on_line)
        finally:
            self.log_manager.replace_terminal_handler(old_logger)

        if what.endswith('.c'):
            name = 'COMPILE_CFLAGS'
        else:
            name = 'COMPILE_CXXFLAGS'

        if name not in build_vars:
            return

        flags = ['-isystem', '-I', '-include', '-MF']
        new_args = []
        path = os.path.join(self.topobjdir, make_dir)
        for arg in shlex.split(build_vars[name]):
            if new_args and new_args[-1] in flags:
                arg = os.path.normpath(os.path.join(path, arg))
            else:
                flag = [(f, arg[len(f):]) for f in flags + ['--sysroot=']
                        if arg.startswith(f)]
                if flag:
                    flag, val = flag[0]
                    if val:
                        arg = flag + os.path.normpath(os.path.join(path, val))
            new_args.append(arg)

        print(' '.join(new_args))
Esempio n. 2
0
def compileflags(command_context, what):
    from mozbuild.util import resolve_target_to_make
    from mozbuild.compilation import util

    if not util.check_top_objdir(command_context.topobjdir):
        return 1

    path_arg = command_context._wrap_path_argument(what)

    make_dir, make_target = resolve_target_to_make(command_context.topobjdir,
                                                   path_arg.relpath())

    if make_dir is None and make_target is None:
        return 1

    build_vars = util.get_build_vars(make_dir, command_context)

    if what.endswith(".c"):
        cc = "CC"
        name = "COMPILE_CFLAGS"
    else:
        cc = "CXX"
        name = "COMPILE_CXXFLAGS"

    if name not in build_vars:
        return

    # Drop the first flag since that is the pathname of the compiler.
    flags = (shell_split(build_vars[cc]) + shell_split(build_vars[name]))[1:]

    print(" ".join(shell_quote(arg) for arg in util.sanitize_cflags(flags)))
Esempio n. 3
0
    def compileflags(self, what):
        from mozbuild.util import resolve_target_to_make
        from mozbuild.compilation import util

        if not util.check_top_objdir(self.topobjdir):
            return 1

        path_arg = self._wrap_path_argument(what)

        make_dir, make_target = resolve_target_to_make(self.topobjdir,
            path_arg.relpath())

        if make_dir is None and make_target is None:
            return 1

        build_vars = util.get_build_vars(make_dir, self)

        if what.endswith('.c'):
            name = 'COMPILE_CFLAGS'
        else:
            name = 'COMPILE_CXXFLAGS'

        if name not in build_vars:
            return

        print(' '.join(shell_quote(arg)
                       for arg in util.get_flags(self.topobjdir, make_dir,
                                                 build_vars, name)))
Esempio n. 4
0
    def compileflags(self, what):
        from mozbuild.util import resolve_target_to_make
        from mozbuild.compilation import util

        if not util.check_top_objdir(self.topobjdir):
            return 1

        path_arg = self._wrap_path_argument(what)

        make_dir, make_target = resolve_target_to_make(self.topobjdir, path_arg.relpath())

        if make_dir is None and make_target is None:
            return 1

        build_vars = util.get_build_vars(make_dir, self)

        if what.endswith(".c"):
            cc = "CC"
            name = "COMPILE_CFLAGS"
        else:
            cc = "CXX"
            name = "COMPILE_CXXFLAGS"

        if name not in build_vars:
            return

        # Drop the first flag since that is the pathname of the compiler.
        flags = (shell_split(build_vars[cc]) + shell_split(build_vars[name]))[1:]

        print(" ".join(shell_quote(arg) for arg in util.sanitize_cflags(flags)))
Esempio n. 5
0
    def compileflags(self, what):
        from mozbuild.util import resolve_target_to_make
        from mozbuild.compilation import util

        if not util.check_top_objdir(self.topobjdir):
            return 1

        path_arg = self._wrap_path_argument(what)

        make_dir, make_target = resolve_target_to_make(self.topobjdir,
            path_arg.relpath())

        if make_dir is None and make_target is None:
            return 1

        build_vars = util.get_build_vars(make_dir, self)

        if what.endswith('.c'):
            name = 'COMPILE_CFLAGS'
        else:
            name = 'COMPILE_CXXFLAGS'

        if name not in build_vars:
            return

        print(' '.join(shell_quote(arg)
                       for arg in shell_split(build_vars[name])))
Esempio n. 6
0
    def compileflags(self, what):
        from mozbuild.util import resolve_target_to_make
        from mozbuild.compilation import util

        if not util.check_top_objdir(self.topobjdir):
            return 1

        path_arg = self._wrap_path_argument(what)

        make_dir, make_target = resolve_target_to_make(self.topobjdir,
            path_arg.relpath())

        if make_dir is None and make_target is None:
            return 1

        build_vars = util.get_build_vars(make_dir, self)

        if what.endswith('.c'):
            cc = 'CC'
            name = 'COMPILE_CFLAGS'
        else:
            cc = 'CXX'
            name = 'COMPILE_CXXFLAGS'

        if name not in build_vars:
            return

        # Drop the first flag since that is the pathname of the compiler.
        flags = (shell_split(build_vars[cc]) + shell_split(build_vars[name]))[1:]

        print(' '.join(shell_quote(arg)
                       for arg in util.sanitize_cflags(flags)))
Esempio n. 7
0
    def _get_dir_flags(self, directory):
        if directory in self._flags:
            return self._flags[directory]

        from mozbuild.util import resolve_target_to_make

        make_dir, make_target = resolve_target_to_make(
            self.environment.topobjdir, directory)
        if make_dir is None and make_target is None:
            raise Exception('Cannot figure out the make dir and target for ' +
                            directory)

        build_vars = util.get_build_vars(directory, self._cmd)

        # We only care about the following build variables.
        for name in ('COMPILE_CFLAGS', 'COMPILE_CXXFLAGS', 'COMPILE_CMFLAGS',
                     'COMPILE_CMMFLAGS'):
            if name not in build_vars:
                continue

            build_vars[name] = util.sanitize_cflags(
                shell_split(build_vars[name]))

        self._flags[directory] = build_vars
        return self._flags[directory]
Esempio n. 8
0
 def assertResolve(self, path, expected):
     # Handle Windows path separators.
     (reldir, target) = resolve_target_to_make(self.topobjdir, path)
     if reldir is not None:
         reldir = reldir.replace(os.sep, "/")
     if target is not None:
         target = target.replace(os.sep, "/")
     self.assertEqual((reldir, target), expected)
Esempio n. 9
0
 def assertResolve(self, path, expected):
     # Handle Windows path separators.
     (reldir, target) = resolve_target_to_make(self.topobjdir, path)
     if reldir is not None:
         reldir = reldir.replace(os.sep, '/')
     if target is not None:
         target = target.replace(os.sep, '/')
     self.assertEqual((reldir, target), expected)
Esempio n. 10
0
    def _get_dir_flags(self, directory):
        if directory in self._flags:
            return self._flags[directory]

        from mozbuild.util import resolve_target_to_make

        make_dir, make_target = resolve_target_to_make(self.environment.topobjdir, directory)
        if make_dir is None and make_target is None:
            raise Exception("Cannot figure out the make dir and target for " + directory)

        build_vars = util.get_build_vars(directory, self._cmd)

        # We only care about the following build variables.
        for name in ("COMPILE_CFLAGS", "COMPILE_CXXFLAGS", "COMPILE_CMFLAGS", "COMPILE_CMMFLAGS"):
            if name not in build_vars:
                continue

            build_vars[name] = util.get_flags(self.environment.topobjdir, directory, build_vars, name)

        self._flags[directory] = build_vars
        return self._flags[directory]
Esempio n. 11
0
    def build(self, what=None, disable_extra_make_dependencies=None, jobs=0, verbose=False):
        from mozbuild.controller.building import BuildMonitor
        from mozbuild.util import resolve_target_to_make

        warnings_path = self._get_state_filename('warnings.json')
        monitor = BuildMonitor(self.topobjdir, warnings_path)

        with BuildOutputManager(self.log_manager, monitor) as output:
            monitor.start()

            if what:
                top_make = os.path.join(self.topobjdir, 'Makefile')
                if not os.path.exists(top_make):
                    print('Your tree has not been configured yet. Please run '
                        '|mach build| with no arguments.')
                    return 1

                # Collect target pairs.
                target_pairs = []
                for target in what:
                    path_arg = self._wrap_path_argument(target)

                    make_dir, make_target = resolve_target_to_make(self.topobjdir,
                        path_arg.relpath())

                    if make_dir is None and make_target is None:
                        return 1

                    target_pairs.append((make_dir, make_target))

                # Possibly add extra make depencies using dumbmake.
                if not disable_extra_make_dependencies:
                    from dumbmake.dumbmake import (dependency_map,
                                                   add_extra_dependencies)
                    depfile = os.path.join(self.topsrcdir, 'build',
                                           'dumbmake-dependencies')
                    with open(depfile) as f:
                        dm = dependency_map(f.readlines())
                    new_pairs = list(add_extra_dependencies(target_pairs, dm))
                    self.log(logging.DEBUG, 'dumbmake',
                             {'target_pairs': target_pairs,
                              'new_pairs': new_pairs},
                             'Added extra dependencies: will build {new_pairs} ' +
                             'instead of {target_pairs}.')
                    target_pairs = new_pairs

                # Build target pairs.
                for make_dir, make_target in target_pairs:
                    status = self._run_make(directory=make_dir, target=make_target,
                        line_handler=output.on_line, log=False, print_directory=False,
                        ensure_exit_code=False, num_jobs=jobs, silent=not verbose)

                    if status != 0:
                        break
            else:
                status = self._run_make(srcdir=True, filename='client.mk',
                    line_handler=output.on_line, log=False, print_directory=False,
                    allow_parallel=False, ensure_exit_code=False, num_jobs=jobs,
                    silent=not verbose)

                self.log(logging.WARNING, 'warning_summary',
                    {'count': len(monitor.warnings_database)},
                    '{count} compiler warnings present.')

            monitor.finish()

        high_finder, finder_percent = monitor.have_high_finder_usage()
        if high_finder:
            print(FINDER_SLOW_MESSAGE % finder_percent)

        long_build = monitor.elapsed > 600

        if status:
            return status

        if long_build:
            print('We know it took a while, but your build finally finished successfully!')
        else:
            print('Your build was successful!')

        # Only for full builds because incremental builders likely don't
        # need to be burdened with this.
        if not what:
            # Fennec doesn't have useful output from just building. We should
            # arguably make the build action useful for Fennec. Another day...
            if self.substs['MOZ_BUILD_APP'] != 'mobile/android':
                app_path = self.get_binary_path('app')
                print('To take your build for a test drive, run: %s' % app_path)
            app = self.substs['MOZ_BUILD_APP']
            if app in ('browser', 'mobile/android'):
                print('For more information on what to do now, see '
                    'https://developer.mozilla.org/docs/Developer_Guide/So_You_Just_Built_Firefox')

        return status
Esempio n. 12
0
    def build(self, what=None):
        # This code is only meant to be temporary until the more robust tree
        # building code in bug 780329 lands.
        from mozbuild.compilation.warnings import WarningsCollector
        from mozbuild.compilation.warnings import WarningsDatabase
        from mozbuild.util import resolve_target_to_make

        warnings_path = self._get_state_filename('warnings.json')
        warnings_database = WarningsDatabase()

        if os.path.exists(warnings_path):
            try:
                warnings_database.load_from_file(warnings_path)
            except ValueError:
                os.remove(warnings_path)

        warnings_collector = WarningsCollector(database=warnings_database,
            objdir=self.topobjdir)

        def on_line(line):
            try:
                warning = warnings_collector.process_line(line)
                if warning:
                    self.log(logging.INFO, 'compiler_warning', warning,
                        'Warning: {flag} in {filename}: {message}')
            except:
                # This will get logged in the more robust implementation.
                pass

            self.log(logging.INFO, 'build_output', {'line': line}, '{line}')

        finder_start_cpu = self._get_finder_cpu_usage()
        time_start = time.time()

        if what:
            top_make = os.path.join(self.topobjdir, 'Makefile')
            if not os.path.exists(top_make):
                print('Your tree has not been configured yet. Please run '
                    '|mach build| with no arguments.')
                return 1

            for target in what:
                path_arg = self._wrap_path_argument(target)

                make_dir, make_target = resolve_target_to_make(self.topobjdir,
                    path_arg.relpath())

                if make_dir is None and make_target is None:
                    return 1

                status = self._run_make(directory=make_dir, target=make_target,
                    line_handler=on_line, log=False, print_directory=False,
                    ensure_exit_code=False)

                if status != 0:
                    break
        else:
            status = self._run_make(srcdir=True, filename='client.mk',
                line_handler=on_line, log=False, print_directory=False,
                allow_parallel=False, ensure_exit_code=False)

            self.log(logging.WARNING, 'warning_summary',
                {'count': len(warnings_collector.database)},
                '{count} compiler warnings present.')

        warnings_database.prune()
        warnings_database.save_to_file(warnings_path)

        time_end = time.time()
        self._handle_finder_cpu_usage(time_end - time_start, finder_start_cpu)

        print('Finished building. Built files are in %s' % self.topobjdir)

        return status
Esempio n. 13
0
    def build(self, what=None):
        # This code is only meant to be temporary until the more robust tree
        # building code in bug 780329 lands.
        from mozbuild.compilation.warnings import WarningsCollector
        from mozbuild.compilation.warnings import WarningsDatabase
        from mozbuild.util import resolve_target_to_make

        warnings_path = self._get_state_filename('warnings.json')
        warnings_database = WarningsDatabase()

        if os.path.exists(warnings_path):
            try:
                warnings_database.load_from_file(warnings_path)
            except ValueError:
                os.remove(warnings_path)

        warnings_collector = WarningsCollector(database=warnings_database,
            objdir=self.topobjdir)

        def on_line(line):
            try:
                warning = warnings_collector.process_line(line)
                if warning:
                    self.log(logging.INFO, 'compiler_warning', warning,
                        'Warning: {flag} in {filename}: {message}')
            except:
                # This will get logged in the more robust implementation.
                pass

            self.log(logging.INFO, 'build_output', {'line': line}, '{line}')

        finder_start_cpu = self._get_finder_cpu_usage()
        time_start = time.time()

        if what:
            top_make = os.path.join(self.topobjdir, 'Makefile')
            if not os.path.exists(top_make):
                print('Your tree has not been configured yet. Please run '
                    '|mach build| with no arguments.')
                return 1

            for target in what:
                path_arg = self._wrap_path_argument(target)

                make_dir, make_target = resolve_target_to_make(self.topobjdir,
                    path_arg.relpath())

                if make_dir is None and make_target is None:
                    return 1

                status = self._run_make(directory=make_dir, target=make_target,
                    line_handler=on_line, log=False, print_directory=False,
                    ensure_exit_code=False)

                if status != 0:
                    break
        else:
            status = self._run_make(srcdir=True, filename='client.mk',
                line_handler=on_line, log=False, print_directory=False,
                allow_parallel=False, ensure_exit_code=False)

            self.log(logging.WARNING, 'warning_summary',
                {'count': len(warnings_collector.database)},
                '{count} compiler warnings present.')

        warnings_database.prune()
        warnings_database.save_to_file(warnings_path)

        time_end = time.time()
        time_elapsed = time_end - time_start
        self._handle_finder_cpu_usage(time_elapsed, finder_start_cpu)

        long_build = time_elapsed > 600

        if status:
            return status

        if long_build:
            print('We know it took a while, but your build finally finished successfully!')
        else:
            print('Your build was successful!')

        app_path = self.get_binary_path('app')
        print('To take your build for a test drive, run: %s' % app_path)

        # Only for full builds because incremental builders likely don't
        # need to be burdened with this.
        if not what:
            app = self.substs['MOZ_BUILD_APP']
            if app in ('browser', 'mobile/android'):
                print('For more information on what to do now, see '
                    'https://developer.mozilla.org/docs/Developer_Guide/So_You_Just_Built_Firefox')

        return status
Esempio n. 14
0
    def build(self,
              what=None,
              disable_extra_make_dependencies=None,
              jobs=0,
              verbose=False):
        import which
        from mozbuild.controller.building import BuildMonitor
        from mozbuild.util import resolve_target_to_make

        self.log_manager.register_structured_logger(
            logging.getLogger('mozbuild'))

        warnings_path = self._get_state_filename('warnings.json')
        monitor = self._spawn(BuildMonitor)
        monitor.init(warnings_path)
        ccache_start = monitor.ccache_stats()

        with BuildOutputManager(self.log_manager, monitor) as output:
            monitor.start()

            if what:
                top_make = os.path.join(self.topobjdir, 'Makefile')
                if not os.path.exists(top_make):
                    print('Your tree has not been configured yet. Please run '
                          '|mach build| with no arguments.')
                    return 1

                # Collect target pairs.
                target_pairs = []
                for target in what:
                    path_arg = self._wrap_path_argument(target)

                    make_dir, make_target = resolve_target_to_make(
                        self.topobjdir, path_arg.relpath())

                    if make_dir is None and make_target is None:
                        return 1

                    # See bug 886162 - we don't want to "accidentally" build
                    # the entire tree (if that's really the intent, it's
                    # unlikely they would have specified a directory.)
                    if not make_dir and not make_target:
                        print("The specified directory doesn't contain a "
                              "Makefile and the first parent with one is the "
                              "root of the tree. Please specify a directory "
                              "with a Makefile or run |mach build| if you "
                              "want to build the entire tree.")
                        return 1

                    target_pairs.append((make_dir, make_target))

                # Possibly add extra make depencies using dumbmake.
                if not disable_extra_make_dependencies:
                    from dumbmake.dumbmake import (dependency_map,
                                                   add_extra_dependencies)
                    depfile = os.path.join(self.topsrcdir, 'build',
                                           'dumbmake-dependencies')
                    with open(depfile) as f:
                        dm = dependency_map(f.readlines())
                    new_pairs = list(add_extra_dependencies(target_pairs, dm))
                    self.log(
                        logging.DEBUG, 'dumbmake', {
                            'target_pairs': target_pairs,
                            'new_pairs': new_pairs
                        },
                        'Added extra dependencies: will build {new_pairs} ' +
                        'instead of {target_pairs}.')
                    target_pairs = new_pairs

                # Ensure build backend is up to date. The alternative is to
                # have rules in the invoked Makefile to rebuild the build
                # backend. But that involves make reinvoking itself and there
                # are undesired side-effects of this. See bug 877308 for a
                # comprehensive history lesson.
                self._run_make(directory=self.topobjdir,
                               target='backend.RecursiveMakeBackend',
                               line_handler=output.on_line,
                               log=False,
                               print_directory=False)

                # Build target pairs.
                for make_dir, make_target in target_pairs:
                    # We don't display build status messages during partial
                    # tree builds because they aren't reliable there. This
                    # could potentially be fixed if the build monitor were more
                    # intelligent about encountering undefined state.
                    status = self._run_make(
                        directory=make_dir,
                        target=make_target,
                        line_handler=output.on_line,
                        log=False,
                        print_directory=False,
                        ensure_exit_code=False,
                        num_jobs=jobs,
                        silent=not verbose,
                        append_env={b'NO_BUILDSTATUS_MESSAGES': b'1'})

                    if status != 0:
                        break
            else:
                monitor.start_resource_recording()
                status = self._run_make(srcdir=True,
                                        filename='client.mk',
                                        line_handler=output.on_line,
                                        log=False,
                                        print_directory=False,
                                        allow_parallel=False,
                                        ensure_exit_code=False,
                                        num_jobs=jobs,
                                        silent=not verbose)

                make_extra = self.mozconfig['make_extra'] or []
                make_extra = dict(m.split('=', 1) for m in make_extra)

                # For universal builds, we need to run the automation steps in
                # the first architecture from MOZ_BUILD_PROJECTS
                projects = make_extra.get('MOZ_BUILD_PROJECTS')
                if projects:
                    subdir = os.path.join(self.topobjdir, projects.split()[0])
                else:
                    subdir = self.topobjdir
                moz_automation = os.getenv('MOZ_AUTOMATION') or make_extra.get(
                    'export MOZ_AUTOMATION', None)
                if moz_automation and status == 0:
                    status = self._run_make(target='automation/build',
                                            directory=subdir,
                                            line_handler=output.on_line,
                                            log=False,
                                            print_directory=False,
                                            ensure_exit_code=False,
                                            num_jobs=jobs,
                                            silent=not verbose)

                self.log(logging.WARNING, 'warning_summary',
                         {'count': len(monitor.warnings_database)},
                         '{count} compiler warnings present.')

            monitor.finish(record_usage=status == 0)

        high_finder, finder_percent = monitor.have_high_finder_usage()
        if high_finder:
            print(FINDER_SLOW_MESSAGE % finder_percent)

        ccache_end = monitor.ccache_stats()

        if ccache_start and ccache_end:
            ccache_diff = ccache_end - ccache_start
            if ccache_diff:
                self.log(logging.INFO, 'ccache',
                         {'msg': ccache_diff.hit_rate_message()}, "{msg}")

        moz_nospam = os.environ.get('MOZ_NOSPAM')
        if monitor.elapsed > 300 and not moz_nospam:
            # Display a notification when the build completes.
            # This could probably be uplifted into the mach core or at least
            # into a helper API. It is here as an experimentation to see how it
            # is received.
            try:
                if sys.platform.startswith('darwin'):
                    try:
                        notifier = which.which('terminal-notifier')
                    except which.WhichError:
                        raise Exception(
                            'Install terminal-notifier to get '
                            'a notification when the build finishes.')
                    self.run_process([
                        notifier, '-title', 'Mozilla Build System', '-group',
                        'mozbuild', '-message', 'Build complete'
                    ],
                                     ensure_exit_code=False)
                elif sys.platform.startswith('linux'):
                    try:
                        import dbus
                    except ImportError:
                        raise Exception(
                            'Install the python dbus module to '
                            'get a notification when the build finishes.')
                    bus = dbus.SessionBus()
                    notify = bus.get_object('org.freedesktop.Notifications',
                                            '/org/freedesktop/Notifications')
                    method = notify.get_dbus_method(
                        'Notify', 'org.freedesktop.Notifications')
                    method('Mozilla Build System', 0, '', 'Build complete', '',
                           [], [], -1)
                elif sys.platform.startswith('win'):
                    from ctypes import Structure, windll, POINTER, sizeof
                    from ctypes.wintypes import DWORD, HANDLE, WINFUNCTYPE, BOOL, UINT

                    class FLASHWINDOW(Structure):
                        _fields_ = [("cbSize", UINT), ("hwnd", HANDLE),
                                    ("dwFlags", DWORD), ("uCount", UINT),
                                    ("dwTimeout", DWORD)]

                    FlashWindowExProto = WINFUNCTYPE(BOOL,
                                                     POINTER(FLASHWINDOW))
                    FlashWindowEx = FlashWindowExProto(
                        ("FlashWindowEx", windll.user32))
                    FLASHW_CAPTION = 0x01
                    FLASHW_TRAY = 0x02
                    FLASHW_TIMERNOFG = 0x0C
                    params = FLASHWINDOW(
                        sizeof(FLASHWINDOW),
                        windll.kernel32.GetConsoleWindow(),
                        FLASHW_CAPTION | FLASHW_TRAY | FLASHW_TIMERNOFG, 3, 0)
                    FlashWindowEx(params)
            except Exception as e:
                self.log(logging.WARNING, 'notifier-failed',
                         {'error': e.message},
                         'Notification center failed: {error}')

        if status:
            return status

        long_build = monitor.elapsed > 600

        if long_build:
            output.on_line(
                'We know it took a while, but your build finally finished successfully!'
            )
        else:
            output.on_line('Your build was successful!')

        if monitor.have_resource_usage:
            excessive, swap_in, swap_out = monitor.have_excessive_swapping()
            # if excessive:
            #    print(EXCESSIVE_SWAP_MESSAGE)

            print('To view resource usage of the build, run |mach '
                  'resource-usage|.')

        # Only for full builds because incremental builders likely don't
        # need to be burdened with this.
        if not what:
            try:
                # Fennec doesn't have useful output from just building. We should
                # arguably make the build action useful for Fennec. Another day...
                if self.substs['MOZ_BUILD_APP'] != 'mobile/android':
                    print(
                        'To take your build for a test drive, run: |mach run|')
                app = self.substs['MOZ_BUILD_APP']
                if app in ('browser', 'mobile/android'):
                    print(
                        'For more information on what to do now, see '
                        'https://developer.mozilla.org/docs/Developer_Guide/So_You_Just_Built_Firefox'
                    )
            except Exception:
                # Ignore Exceptions in case we can't find config.status (such
                # as when doing OSX Universal builds)
                pass

        return status
Esempio n. 15
0
    def build(self, what=None):
        # This code is only meant to be temporary until the more robust tree
        # building code in bug 780329 lands.
        from mozbuild.compilation.warnings import WarningsCollector
        from mozbuild.compilation.warnings import WarningsDatabase
        from mozbuild.util import resolve_target_to_make

        warnings_path = self._get_state_filename('warnings.json')
        warnings_database = WarningsDatabase()

        if os.path.exists(warnings_path):
            try:
                warnings_database.load_from_file(warnings_path)
            except ValueError:
                os.remove(warnings_path)

        warnings_collector = WarningsCollector(database=warnings_database,
                                               objdir=self.topobjdir)

        def on_line(line):
            try:
                warning = warnings_collector.process_line(line)
                if warning:
                    self.log(logging.INFO, 'compiler_warning', warning,
                             'Warning: {flag} in {filename}: {message}')
            except:
                # This will get logged in the more robust implementation.
                pass

            self.log(logging.INFO, 'build_output', {'line': line}, '{line}')

        finder_start_cpu = self._get_finder_cpu_usage()
        time_start = time.time()

        if what:
            top_make = os.path.join(self.topobjdir, 'Makefile')
            if not os.path.exists(top_make):
                print('Your tree has not been configured yet. Please run '
                      '|mach build| with no arguments.')
                return 1

            for target in what:
                path_arg = self._wrap_path_argument(target)

                make_dir, make_target = resolve_target_to_make(
                    self.topobjdir, path_arg.relpath())

                if make_dir is None and make_target is None:
                    return 1

                status = self._run_make(directory=make_dir,
                                        target=make_target,
                                        line_handler=on_line,
                                        log=False,
                                        print_directory=False,
                                        ensure_exit_code=False)

                if status != 0:
                    break
        else:
            status = self._run_make(srcdir=True,
                                    filename='client.mk',
                                    line_handler=on_line,
                                    log=False,
                                    print_directory=False,
                                    allow_parallel=False,
                                    ensure_exit_code=False)

            self.log(logging.WARNING, 'warning_summary',
                     {'count': len(warnings_collector.database)},
                     '{count} compiler warnings present.')

        warnings_database.prune()
        warnings_database.save_to_file(warnings_path)

        time_end = time.time()
        self._handle_finder_cpu_usage(time_end - time_start, finder_start_cpu)

        print('Finished building. Built files are in %s' % self.topobjdir)

        return status
Esempio n. 16
0
    def build(self, what=None, disable_extra_make_dependencies=None, jobs=0):
        # This code is only meant to be temporary until the more robust tree
        # building code in bug 780329 lands.
        from mozbuild.compilation.warnings import WarningsCollector
        from mozbuild.compilation.warnings import WarningsDatabase
        from mozbuild.util import resolve_target_to_make

        warnings_path = self._get_state_filename('warnings.json')
        warnings_database = WarningsDatabase()

        if os.path.exists(warnings_path):
            try:
                warnings_database.load_from_file(warnings_path)
            except ValueError:
                os.remove(warnings_path)

        warnings_collector = WarningsCollector(database=warnings_database,
            objdir=self.topobjdir)

        def on_line(line):
            try:
                warning = warnings_collector.process_line(line)
                if warning:
                    self.log(logging.INFO, 'compiler_warning', warning,
                        'Warning: {flag} in {filename}: {message}')
            except:
                # This will get logged in the more robust implementation.
                pass

            self.log(logging.INFO, 'build_output', {'line': line}, '{line}')

        finder_start_cpu = self._get_finder_cpu_usage()
        time_start = time.time()

        if what:
            top_make = os.path.join(self.topobjdir, 'Makefile')
            if not os.path.exists(top_make):
                print('Your tree has not been configured yet. Please run '
                    '|mach build| with no arguments.')
                return 1

            # Collect target pairs.
            target_pairs = []
            for target in what:
                path_arg = self._wrap_path_argument(target)

                make_dir, make_target = resolve_target_to_make(self.topobjdir,
                    path_arg.relpath())

                if make_dir is None and make_target is None:
                    return 1

                target_pairs.append((make_dir, make_target))

            # Possibly add extra make depencies using dumbmake.
            if not disable_extra_make_dependencies:
                from dumbmake.dumbmake import (dependency_map,
                                               add_extra_dependencies)
                depfile = os.path.join(self.topsrcdir, 'build',
                                       'dumbmake-dependencies')
                with open(depfile) as f:
                    dm = dependency_map(f.readlines())
                new_pairs = list(add_extra_dependencies(target_pairs, dm))
                self.log(logging.DEBUG, 'dumbmake',
                         {'target_pairs': target_pairs,
                          'new_pairs': new_pairs},
                         'Added extra dependencies: will build {new_pairs} ' +
                         'instead of {target_pairs}.')
                target_pairs = new_pairs

            # Build target pairs.
            for make_dir, make_target in target_pairs:
                status = self._run_make(directory=make_dir, target=make_target,
                    line_handler=on_line, log=False, print_directory=False,
                    ensure_exit_code=False, num_jobs=jobs)

                if status != 0:
                    break
        else:
            status = self._run_make(srcdir=True, filename='client.mk',
                line_handler=on_line, log=False, print_directory=False,
                allow_parallel=False, ensure_exit_code=False, num_jobs=jobs)

            self.log(logging.WARNING, 'warning_summary',
                {'count': len(warnings_collector.database)},
                '{count} compiler warnings present.')

        warnings_database.prune()
        warnings_database.save_to_file(warnings_path)

        time_end = time.time()
        time_elapsed = time_end - time_start
        self._handle_finder_cpu_usage(time_elapsed, finder_start_cpu)

        long_build = time_elapsed > 600

        if status:
            return status

        if long_build:
            print('We know it took a while, but your build finally finished successfully!')
        else:
            print('Your build was successful!')

        # Only for full builds because incremental builders likely don't
        # need to be burdened with this.
        if not what:
            # Fennec doesn't have useful output from just building. We should
            # arguably make the build action useful for Fennec. Another day...
            if self.substs['MOZ_BUILD_APP'] != 'mobile/android':
                app_path = self.get_binary_path('app')
                print('To take your build for a test drive, run: %s' % app_path)
            app = self.substs['MOZ_BUILD_APP']
            if app in ('browser', 'mobile/android'):
                print('For more information on what to do now, see '
                    'https://developer.mozilla.org/docs/Developer_Guide/So_You_Just_Built_Firefox')

        return status
Esempio n. 17
0
    def build(self, what=None, disable_extra_make_dependencies=None, jobs=0,
        directory=None, verbose=False):
        import which
        from mozbuild.controller.building import BuildMonitor
        from mozbuild.util import resolve_target_to_make

        self.log_manager.register_structured_logger(logging.getLogger('mozbuild'))

        warnings_path = self._get_state_filename('warnings.json')
        monitor = self._spawn(BuildMonitor)
        monitor.init(warnings_path)
        ccache_start = monitor.ccache_stats()

        with BuildOutputManager(self.log_manager, monitor) as output:
            monitor.start()

            if directory is not None and not what:
                print('Can only use -C/--directory with an explicit target '
                    'name.')
                return 1

            if directory is not None:
                disable_extra_make_dependencies=True
                directory = mozpath.normsep(directory)
                if directory.startswith('/'):
                    directory = directory[1:]

            if what:
                top_make = os.path.join(self.topobjdir, 'Makefile')
                if not os.path.exists(top_make):
                    print('Your tree has not been configured yet. Please run '
                        '|mach build| with no arguments.')
                    return 1

                # Collect target pairs.
                target_pairs = []
                for target in what:
                    path_arg = self._wrap_path_argument(target)

                    if directory is not None:
                        make_dir = os.path.join(self.topobjdir, directory)
                        make_target = target
                    else:
                        make_dir, make_target = \
                            resolve_target_to_make(self.topobjdir,
                                path_arg.relpath())

                    if make_dir is None and make_target is None:
                        return 1

                    # See bug 886162 - we don't want to "accidentally" build
                    # the entire tree (if that's really the intent, it's
                    # unlikely they would have specified a directory.)
                    if not make_dir and not make_target:
                        print("The specified directory doesn't contain a "
                              "Makefile and the first parent with one is the "
                              "root of the tree. Please specify a directory "
                              "with a Makefile or run |mach build| if you "
                              "want to build the entire tree.")
                        return 1

                    target_pairs.append((make_dir, make_target))

                # Possibly add extra make depencies using dumbmake.
                if not disable_extra_make_dependencies:
                    from dumbmake.dumbmake import (dependency_map,
                                                   add_extra_dependencies)
                    depfile = os.path.join(self.topsrcdir, 'build',
                                           'dumbmake-dependencies')
                    with open(depfile) as f:
                        dm = dependency_map(f.readlines())
                    new_pairs = list(add_extra_dependencies(target_pairs, dm))
                    self.log(logging.DEBUG, 'dumbmake',
                             {'target_pairs': target_pairs,
                              'new_pairs': new_pairs},
                             'Added extra dependencies: will build {new_pairs} ' +
                             'instead of {target_pairs}.')
                    target_pairs = new_pairs

                # Ensure build backend is up to date. The alternative is to
                # have rules in the invoked Makefile to rebuild the build
                # backend. But that involves make reinvoking itself and there
                # are undesired side-effects of this. See bug 877308 for a
                # comprehensive history lesson.
                self._run_make(directory=self.topobjdir,
                    target='backend.RecursiveMakeBackend',
                    line_handler=output.on_line, log=False,
                    print_directory=False)

                # Build target pairs.
                for make_dir, make_target in target_pairs:
                    # We don't display build status messages during partial
                    # tree builds because they aren't reliable there. This
                    # could potentially be fixed if the build monitor were more
                    # intelligent about encountering undefined state.
                    status = self._run_make(directory=make_dir, target=make_target,
                        line_handler=output.on_line, log=False, print_directory=False,
                        ensure_exit_code=False, num_jobs=jobs, silent=not verbose,
                        append_env={b'NO_BUILDSTATUS_MESSAGES': b'1'})

                    if status != 0:
                        break
            else:
                monitor.start_resource_recording()
                status = self._run_make(srcdir=True, filename='client.mk',
                    line_handler=output.on_line, log=False, print_directory=False,
                    allow_parallel=False, ensure_exit_code=False, num_jobs=jobs,
                    silent=not verbose)

                make_extra = self.mozconfig['make_extra'] or []
                make_extra = dict(m.split('=', 1) for m in make_extra)

                # For universal builds, we need to run the automation steps in
                # the first architecture from MOZ_BUILD_PROJECTS
                projects = make_extra.get('MOZ_BUILD_PROJECTS')
                if projects:
                    subdir = os.path.join(self.topobjdir, projects.split()[0])
                else:
                    subdir = self.topobjdir
                moz_automation = os.getenv('MOZ_AUTOMATION') or make_extra.get('export MOZ_AUTOMATION', None)
                if moz_automation and status == 0:
                    status = self._run_make(target='automation/build', directory=subdir,
                        line_handler=output.on_line, log=False, print_directory=False,
                        ensure_exit_code=False, num_jobs=jobs, silent=not verbose)

                self.log(logging.WARNING, 'warning_summary',
                    {'count': len(monitor.warnings_database)},
                    '{count} compiler warnings present.')

            monitor.finish(record_usage=status==0)

        high_finder, finder_percent = monitor.have_high_finder_usage()
        if high_finder:
            print(FINDER_SLOW_MESSAGE % finder_percent)

        ccache_end = monitor.ccache_stats()

        if ccache_start and ccache_end:
            ccache_diff = ccache_end - ccache_start
            if ccache_diff:
                self.log(logging.INFO, 'ccache',
                         {'msg': ccache_diff.hit_rate_message()}, "{msg}")

        if monitor.elapsed > 300:
            # Display a notification when the build completes.
            self.notify('Build complete')

        if status:
            return status

        long_build = monitor.elapsed > 600

        if long_build:
            output.on_line('We know it took a while, but your build finally finished successfully!')
        else:
            output.on_line('Your build was successful!')

        if monitor.have_resource_usage:
            excessive, swap_in, swap_out = monitor.have_excessive_swapping()
            # if excessive:
            #    print(EXCESSIVE_SWAP_MESSAGE)

            print('To view resource usage of the build, run |mach '
                'resource-usage|.')

        # Only for full builds because incremental builders likely don't
        # need to be burdened with this.
        if not what:
            try:
                # Fennec doesn't have useful output from just building. We should
                # arguably make the build action useful for Fennec. Another day...
                if self.substs['MOZ_BUILD_APP'] != 'mobile/android':
                    print('To take your build for a test drive, run: |mach run|')
                app = self.substs['MOZ_BUILD_APP']
                if app in ('browser', 'mobile/android'):
                    print('For more information on what to do now, see '
                        'https://developer.mozilla.org/docs/Developer_Guide/So_You_Just_Built_Firefox')
            except Exception:
                # Ignore Exceptions in case we can't find config.status (such
                # as when doing OSX Universal builds)
                pass

        return status
Esempio n. 18
0
    def compileflags(self, what):
        from mozbuild.util import resolve_target_to_make
        import shlex

        top_make = os.path.join(self.topobjdir, 'Makefile')
        if not os.path.exists(top_make):
            print('Your tree has not been built yet. Please run '
                  '|mach build| with no arguments.')
            return 1

        path_arg = self._wrap_path_argument(what)

        make_dir, make_target = resolve_target_to_make(self.topobjdir,
                                                       path_arg.relpath())

        if make_dir is None and make_target is None:
            return 1

        build_vars = {}

        def on_line(line):
            elements = [s.strip() for s in line.split('=', 1)]

            if len(elements) != 2:
                return

            build_vars[elements[0]] = elements[1]

        try:
            old_logger = self.log_manager.replace_terminal_handler(None)
            self._run_make(directory=make_dir,
                           target='showbuild',
                           log=False,
                           print_directory=False,
                           allow_parallel=False,
                           silent=True,
                           line_handler=on_line)
        finally:
            self.log_manager.replace_terminal_handler(old_logger)

        if what.endswith('.c'):
            name = 'COMPILE_CFLAGS'
        else:
            name = 'COMPILE_CXXFLAGS'

        if name not in build_vars:
            return

        flags = ['-isystem', '-I', '-include', '-MF']
        new_args = []
        path = os.path.join(self.topobjdir, make_dir)
        for arg in shlex.split(build_vars[name]):
            if new_args and new_args[-1] in flags:
                arg = os.path.normpath(os.path.join(path, arg))
            else:
                flag = [(f, arg[len(f):]) for f in flags + ['--sysroot=']
                        if arg.startswith(f)]
                if flag:
                    flag, val = flag[0]
                    if val:
                        arg = flag + os.path.normpath(os.path.join(path, val))
            new_args.append(arg)

        print(' '.join(new_args))
Esempio n. 19
0
    def build(self, what=None, disable_extra_make_dependencies=None, jobs=0,
        directory=None, verbose=False):
        import which
        from mozbuild.controller.building import BuildMonitor
        from mozbuild.util import resolve_target_to_make

        self.log_manager.register_structured_logger(logging.getLogger('mozbuild'))

        warnings_path = self._get_state_filename('warnings.json')
        monitor = self._spawn(BuildMonitor)
        monitor.init(warnings_path)
        ccache_start = monitor.ccache_stats()

        with BuildOutputManager(self.log_manager, monitor) as output:
            monitor.start()

            if directory is not None and not what:
                print('Can only use -C/--directory with an explicit target '
                    'name.')
                return 1

            if directory is not None:
                disable_extra_make_dependencies=True
                directory = mozpath.normsep(directory)
                if directory.startswith('/'):
                    directory = directory[1:]

            if what:
                top_make = os.path.join(self.topobjdir, 'Makefile')
                if not os.path.exists(top_make):
                    print('Your tree has not been configured yet. Please run '
                        '|mach build| with no arguments.')
                    return 1

                # Collect target pairs.
                target_pairs = []
                for target in what:
                    path_arg = self._wrap_path_argument(target)

                    if directory is not None:
                        make_dir = os.path.join(self.topobjdir, directory)
                        make_target = target
                    else:
                        make_dir, make_target = \
                            resolve_target_to_make(self.topobjdir,
                                path_arg.relpath())

                    if make_dir is None and make_target is None:
                        return 1

                    # See bug 886162 - we don't want to "accidentally" build
                    # the entire tree (if that's really the intent, it's
                    # unlikely they would have specified a directory.)
                    if not make_dir and not make_target:
                        print("The specified directory doesn't contain a "
                              "Makefile and the first parent with one is the "
                              "root of the tree. Please specify a directory "
                              "with a Makefile or run |mach build| if you "
                              "want to build the entire tree.")
                        return 1

                    target_pairs.append((make_dir, make_target))

                # Possibly add extra make depencies using dumbmake.
                if not disable_extra_make_dependencies:
                    from dumbmake.dumbmake import (dependency_map,
                                                   add_extra_dependencies)
                    depfile = os.path.join(self.topsrcdir, 'build',
                                           'dumbmake-dependencies')
                    with open(depfile) as f:
                        dm = dependency_map(f.readlines())
                    new_pairs = list(add_extra_dependencies(target_pairs, dm))
                    self.log(logging.DEBUG, 'dumbmake',
                             {'target_pairs': target_pairs,
                              'new_pairs': new_pairs},
                             'Added extra dependencies: will build {new_pairs} ' +
                             'instead of {target_pairs}.')
                    target_pairs = new_pairs

                # Ensure build backend is up to date. The alternative is to
                # have rules in the invoked Makefile to rebuild the build
                # backend. But that involves make reinvoking itself and there
                # are undesired side-effects of this. See bug 877308 for a
                # comprehensive history lesson.
                self._run_make(directory=self.topobjdir,
                    target='backend.RecursiveMakeBackend',
                    line_handler=output.on_line, log=False,
                    print_directory=False)

                # Build target pairs.
                for make_dir, make_target in target_pairs:
                    # We don't display build status messages during partial
                    # tree builds because they aren't reliable there. This
                    # could potentially be fixed if the build monitor were more
                    # intelligent about encountering undefined state.
                    status = self._run_make(directory=make_dir, target=make_target,
                        line_handler=output.on_line, log=False, print_directory=False,
                        ensure_exit_code=False, num_jobs=jobs, silent=not verbose,
                        append_env={b'NO_BUILDSTATUS_MESSAGES': b'1'})

                    if status != 0:
                        break
            else:
                monitor.start_resource_recording()
                status = self._run_make(srcdir=True, filename='client.mk',
                    line_handler=output.on_line, log=False, print_directory=False,
                    allow_parallel=False, ensure_exit_code=False, num_jobs=jobs,
                    silent=not verbose)

                make_extra = self.mozconfig['make_extra'] or []
                make_extra = dict(m.split('=', 1) for m in make_extra)

                # For universal builds, we need to run the automation steps in
                # the first architecture from MOZ_BUILD_PROJECTS
                projects = make_extra.get('MOZ_BUILD_PROJECTS')
                if projects:
                    subdir = os.path.join(self.topobjdir, projects.split()[0])
                else:
                    subdir = self.topobjdir
                moz_automation = os.getenv('MOZ_AUTOMATION') or make_extra.get('export MOZ_AUTOMATION', None)
                if moz_automation and status == 0:
                    status = self._run_make(target='automation/build', directory=subdir,
                        line_handler=output.on_line, log=False, print_directory=False,
                        ensure_exit_code=False, num_jobs=jobs, silent=not verbose)

                self.log(logging.WARNING, 'warning_summary',
                    {'count': len(monitor.warnings_database)},
                    '{count} compiler warnings present.')

            monitor.finish(record_usage=status==0)

        high_finder, finder_percent = monitor.have_high_finder_usage()
        if high_finder:
            print(FINDER_SLOW_MESSAGE % finder_percent)

        ccache_end = monitor.ccache_stats()

        if ccache_start and ccache_end:
            ccache_diff = ccache_end - ccache_start
            if ccache_diff:
                self.log(logging.INFO, 'ccache',
                         {'msg': ccache_diff.hit_rate_message()}, "{msg}")

        moz_nospam = os.environ.get('MOZ_NOSPAM')
        if monitor.elapsed > 300 and not moz_nospam:
            # Display a notification when the build completes.
            # This could probably be uplifted into the mach core or at least
            # into a helper API. It is here as an experimentation to see how it
            # is received.
            try:
                if sys.platform.startswith('darwin'):
                    try:
                        notifier = which.which('terminal-notifier')
                    except which.WhichError:
                        raise Exception('Install terminal-notifier to get '
                            'a notification when the build finishes.')
                    self.run_process([notifier, '-title',
                        'Mozilla Build System', '-group', 'mozbuild',
                        '-message', 'Build complete'], ensure_exit_code=False)
                elif sys.platform.startswith('linux'):
                    try:
                        import dbus
                    except ImportError:
                        raise Exception('Install the python dbus module to '
                            'get a notification when the build finishes.')
                    bus = dbus.SessionBus()
                    notify = bus.get_object('org.freedesktop.Notifications',
                                            '/org/freedesktop/Notifications')
                    method = notify.get_dbus_method('Notify',
                                                    'org.freedesktop.Notifications')
                    method('Mozilla Build System', 0, '', 'Build complete', '', [], [], -1)
                elif sys.platform.startswith('win'):
                    from ctypes import Structure, windll, POINTER, sizeof
                    from ctypes.wintypes import DWORD, HANDLE, WINFUNCTYPE, BOOL, UINT
                    class FLASHWINDOW(Structure):
                        _fields_ = [("cbSize", UINT),
                                    ("hwnd", HANDLE),
                                    ("dwFlags", DWORD),
                                    ("uCount", UINT),
                                    ("dwTimeout", DWORD)]
                    FlashWindowExProto = WINFUNCTYPE(BOOL, POINTER(FLASHWINDOW))
                    FlashWindowEx = FlashWindowExProto(("FlashWindowEx", windll.user32))
                    FLASHW_CAPTION = 0x01
                    FLASHW_TRAY = 0x02
                    FLASHW_TIMERNOFG = 0x0C
                    params = FLASHWINDOW(sizeof(FLASHWINDOW),
                                        windll.kernel32.GetConsoleWindow(),
                                        FLASHW_CAPTION | FLASHW_TRAY | FLASHW_TIMERNOFG, 3, 0)
                    FlashWindowEx(params)
            except Exception as e:
                self.log(logging.WARNING, 'notifier-failed', {'error':
                    e.message}, 'Notification center failed: {error}')

        if status:
            return status

        long_build = monitor.elapsed > 600

        if long_build:
            output.on_line('We know it took a while, but your build finally finished successfully!')
        else:
            output.on_line('Your build was successful!')

        if monitor.have_resource_usage:
            excessive, swap_in, swap_out = monitor.have_excessive_swapping()
            # if excessive:
            #    print(EXCESSIVE_SWAP_MESSAGE)

            print('To view resource usage of the build, run |mach '
                'resource-usage|.')

        # Only for full builds because incremental builders likely don't
        # need to be burdened with this.
        if not what:
            try:
                # Fennec doesn't have useful output from just building. We should
                # arguably make the build action useful for Fennec. Another day...
                if self.substs['MOZ_BUILD_APP'] != 'mobile/android':
                    print('To take your build for a test drive, run: |mach run|')
                app = self.substs['MOZ_BUILD_APP']
                if app in ('browser', 'mobile/android'):
                    print('For more information on what to do now, see '
                        'https://developer.mozilla.org/docs/Developer_Guide/So_You_Just_Built_Firefox')
            except Exception:
                # Ignore Exceptions in case we can't find config.status (such
                # as when doing OSX Universal builds)
                pass

        return status
Esempio n. 20
0
    def build(self,
              what=None,
              pymake=False,
              disable_extra_make_dependencies=None,
              jobs=0,
              verbose=False):
        import which
        from mozbuild.controller.building import BuildMonitor
        from mozbuild.util import resolve_target_to_make

        self.log_manager.register_structured_logger(
            logging.getLogger('mozbuild'))

        warnings_path = self._get_state_filename('warnings.json')
        monitor = self._spawn(BuildMonitor)
        monitor.init(warnings_path)

        with BuildOutputManager(self.log_manager, monitor) as output:
            monitor.start()

            if what:
                top_make = os.path.join(self.topobjdir, 'Makefile')
                if not os.path.exists(top_make):
                    print('Your tree has not been configured yet. Please run '
                          '|mach build| with no arguments.')
                    return 1

                # Collect target pairs.
                target_pairs = []
                for target in what:
                    path_arg = self._wrap_path_argument(target)

                    make_dir, make_target = resolve_target_to_make(
                        self.topobjdir, path_arg.relpath())

                    if make_dir is None and make_target is None:
                        return 1

                    # See bug 886162 - we don't want to "accidentally" build
                    # the entire tree (if that's really the intent, it's
                    # unlikely they would have specified a directory.)
                    if not make_dir and not make_target:
                        print("The specified directory doesn't contain a "
                              "Makefile and the first parent with one is the "
                              "root of the tree. Please specify a directory "
                              "with a Makefile or run |mach build| if you "
                              "want to build the entire tree.")
                        return 1

                    target_pairs.append((make_dir, make_target))

                # Possibly add extra make depencies using dumbmake.
                if not disable_extra_make_dependencies:
                    from dumbmake.dumbmake import (dependency_map,
                                                   add_extra_dependencies)
                    depfile = os.path.join(self.topsrcdir, 'build',
                                           'dumbmake-dependencies')
                    with open(depfile) as f:
                        dm = dependency_map(f.readlines())
                    new_pairs = list(add_extra_dependencies(target_pairs, dm))
                    self.log(
                        logging.DEBUG, 'dumbmake', {
                            'target_pairs': target_pairs,
                            'new_pairs': new_pairs
                        },
                        'Added extra dependencies: will build {new_pairs} ' +
                        'instead of {target_pairs}.')
                    target_pairs = new_pairs

                # Ensure build backend is up to date. The alternative is to
                # have rules in the invoked Makefile to rebuild the build
                # backend. But that involves make reinvoking itself and there
                # are undesired side-effects of this. See bug 877308 for a
                # comprehensive history lesson.
                self._run_make(directory=self.topobjdir,
                               target='backend.RecursiveMakeBackend',
                               force_pymake=pymake,
                               line_handler=output.on_line,
                               log=False,
                               print_directory=False)

                # Build target pairs.
                for make_dir, make_target in target_pairs:
                    # We don't display build status messages during partial
                    # tree builds because they aren't reliable there. This
                    # could potentially be fixed if the build monitor were more
                    # intelligent about encountering undefined state.
                    status = self._run_make(
                        directory=make_dir,
                        target=make_target,
                        line_handler=output.on_line,
                        log=False,
                        print_directory=False,
                        ensure_exit_code=False,
                        num_jobs=jobs,
                        silent=not verbose,
                        append_env={b'NO_BUILDSTATUS_MESSAGES': b'1'},
                        force_pymake=pymake)

                    if status != 0:
                        break
            else:
                monitor.start_resource_recording()
                status = self._run_make(srcdir=True,
                                        filename='client.mk',
                                        line_handler=output.on_line,
                                        log=False,
                                        print_directory=False,
                                        allow_parallel=False,
                                        ensure_exit_code=False,
                                        num_jobs=jobs,
                                        silent=not verbose,
                                        force_pymake=pymake)

                self.log(logging.WARNING, 'warning_summary',
                         {'count': len(monitor.warnings_database)},
                         '{count} compiler warnings present.')

            monitor.finish(record_usage=status == 0)

        high_finder, finder_percent = monitor.have_high_finder_usage()
        if high_finder:
            print(FINDER_SLOW_MESSAGE % finder_percent)

        if monitor.elapsed > 300:
            # Display a notification when the build completes.
            # This could probably be uplifted into the mach core or at least
            # into a helper API. It is here as an experimentation to see how it
            # is received.
            try:
                if sys.platform.startswith('darwin'):
                    notifier = which.which('terminal-notifier')
                    self.run_process([
                        notifier, '-title', 'Mozilla Build System', '-group',
                        'mozbuild', '-message', 'Build complete'
                    ],
                                     ensure_exit_code=False)
            except which.WhichError:
                pass
            except Exception as e:
                self.log(logging.WARNING, 'notifier-failed',
                         {'error': e.message},
                         'Notification center failed: {error}')

        if status:
            return status

        long_build = monitor.elapsed > 600

        if long_build:
            print(
                'We know it took a while, but your build finally finished successfully!'
            )
        else:
            print('Your build was successful!')

        if monitor.have_resource_usage:
            excessive, swap_in, swap_out = monitor.have_excessive_swapping()
            # if excessive:
            #    print(EXCESSIVE_SWAP_MESSAGE)

            print('To view resource usage of the build, run |mach '
                  'resource-usage|.')

        # Only for full builds because incremental builders likely don't
        # need to be burdened with this.
        if not what:
            # Fennec doesn't have useful output from just building. We should
            # arguably make the build action useful for Fennec. Another day...
            if self.substs['MOZ_BUILD_APP'] != 'mobile/android':
                print('To take your build for a test drive, run: |mach run|')
            app = self.substs['MOZ_BUILD_APP']
            if app in ('browser', 'mobile/android'):
                print(
                    'For more information on what to do now, see '
                    'https://developer.mozilla.org/docs/Developer_Guide/So_You_Just_Built_Firefox'
                )

        return status
Esempio n. 21
0
    def build(self, what=None, pymake=False,
        disable_extra_make_dependencies=None, jobs=0, verbose=False):
        import which
        from mozbuild.controller.building import BuildMonitor
        from mozbuild.util import resolve_target_to_make

        self.log_manager.register_structured_logger(logging.getLogger('mozbuild'))

        warnings_path = self._get_state_filename('warnings.json')
        monitor = self._spawn(BuildMonitor)
        monitor.init(warnings_path)

        with BuildOutputManager(self.log_manager, monitor) as output:
            monitor.start()

            if what:
                top_make = os.path.join(self.topobjdir, 'Makefile')
                if not os.path.exists(top_make):
                    print('Your tree has not been configured yet. Please run '
                        '|mach build| with no arguments.')
                    return 1

                # Collect target pairs.
                target_pairs = []
                for target in what:
                    path_arg = self._wrap_path_argument(target)

                    make_dir, make_target = resolve_target_to_make(self.topobjdir,
                        path_arg.relpath())

                    if make_dir is None and make_target is None:
                        return 1

                    # See bug 886162 - we don't want to "accidentally" build
                    # the entire tree (if that's really the intent, it's
                    # unlikely they would have specified a directory.)
                    if not make_dir and not make_target:
                        print("The specified directory doesn't contain a "
                              "Makefile and the first parent with one is the "
                              "root of the tree. Please specify a directory "
                              "with a Makefile or run |mach build| if you "
                              "want to build the entire tree.")
                        return 1

                    target_pairs.append((make_dir, make_target))

                # Possibly add extra make depencies using dumbmake.
                if not disable_extra_make_dependencies:
                    from dumbmake.dumbmake import (dependency_map,
                                                   add_extra_dependencies)
                    depfile = os.path.join(self.topsrcdir, 'build',
                                           'dumbmake-dependencies')
                    with open(depfile) as f:
                        dm = dependency_map(f.readlines())
                    new_pairs = list(add_extra_dependencies(target_pairs, dm))
                    self.log(logging.DEBUG, 'dumbmake',
                             {'target_pairs': target_pairs,
                              'new_pairs': new_pairs},
                             'Added extra dependencies: will build {new_pairs} ' +
                             'instead of {target_pairs}.')
                    target_pairs = new_pairs

                # Ensure build backend is up to date. The alternative is to
                # have rules in the invoked Makefile to rebuild the build
                # backend. But that involves make reinvoking itself and there
                # are undesired side-effects of this. See bug 877308 for a
                # comprehensive history lesson.
                self._run_make(directory=self.topobjdir,
                    target='backend.RecursiveMakeBackend',
                    force_pymake=pymake, line_handler=output.on_line,
                    log=False, print_directory=False)

                # Build target pairs.
                for make_dir, make_target in target_pairs:
                    # We don't display build status messages during partial
                    # tree builds because they aren't reliable there. This
                    # could potentially be fixed if the build monitor were more
                    # intelligent about encountering undefined state.
                    status = self._run_make(directory=make_dir, target=make_target,
                        line_handler=output.on_line, log=False, print_directory=False,
                        ensure_exit_code=False, num_jobs=jobs, silent=not verbose,
                        append_env={b'NO_BUILDSTATUS_MESSAGES': b'1'},
                        force_pymake=pymake)

                    if status != 0:
                        break
            else:
                monitor.start_resource_recording()
                status = self._run_make(srcdir=True, filename='client.mk',
                    line_handler=output.on_line, log=False, print_directory=False,
                    allow_parallel=False, ensure_exit_code=False, num_jobs=jobs,
                    silent=not verbose, force_pymake=pymake)

                self.log(logging.WARNING, 'warning_summary',
                    {'count': len(monitor.warnings_database)},
                    '{count} compiler warnings present.')

            monitor.finish(record_usage=status==0)

        high_finder, finder_percent = monitor.have_high_finder_usage()
        if high_finder:
            print(FINDER_SLOW_MESSAGE % finder_percent)

        if monitor.elapsed > 300:
            # Display a notification when the build completes.
            # This could probably be uplifted into the mach core or at least
            # into a helper API. It is here as an experimentation to see how it
            # is received.
            try:
                if sys.platform.startswith('darwin'):
                    notifier = which.which('terminal-notifier')
                    self.run_process([notifier, '-title',
                        'Mozilla Build System', '-group', 'mozbuild',
                        '-message', 'Build complete'], ensure_exit_code=False)
            except which.WhichError:
                pass
            except Exception as e:
                self.log(logging.WARNING, 'notifier-failed', {'error':
                    e.message}, 'Notification center failed: {error}')

        if status:
            return status

        long_build = monitor.elapsed > 600

        if long_build:
            print('We know it took a while, but your build finally finished successfully!')
        else:
            print('Your build was successful!')

        if monitor.have_resource_usage:
            excessive, swap_in, swap_out = monitor.have_excessive_swapping()
            # if excessive:
            #    print(EXCESSIVE_SWAP_MESSAGE)

            print('To view resource usage of the build, run |mach '
                'resource-usage|.')

        # Only for full builds because incremental builders likely don't
        # need to be burdened with this.
        if not what:
            # Fennec doesn't have useful output from just building. We should
            # arguably make the build action useful for Fennec. Another day...
            if self.substs['MOZ_BUILD_APP'] != 'mobile/android':
                app_path = self.get_binary_path('app')
                print('To take your build for a test drive, run: %s' % app_path)
            app = self.substs['MOZ_BUILD_APP']
            if app in ('browser', 'mobile/android'):
                print('For more information on what to do now, see '
                    'https://developer.mozilla.org/docs/Developer_Guide/So_You_Just_Built_Firefox')

        return status
Esempio n. 22
0
    def build(self,
              what=None,
              disable_extra_make_dependencies=None,
              jobs=0,
              verbose=False):
        from mozbuild.controller.building import BuildMonitor
        from mozbuild.util import resolve_target_to_make

        warnings_path = self._get_state_filename('warnings.json')
        monitor = BuildMonitor(self.topobjdir, warnings_path)

        with BuildOutputManager(self.log_manager, monitor) as output:
            monitor.start()

            if what:
                top_make = os.path.join(self.topobjdir, 'Makefile')
                if not os.path.exists(top_make):
                    print('Your tree has not been configured yet. Please run '
                          '|mach build| with no arguments.')
                    return 1

                # Collect target pairs.
                target_pairs = []
                for target in what:
                    path_arg = self._wrap_path_argument(target)

                    make_dir, make_target = resolve_target_to_make(
                        self.topobjdir, path_arg.relpath())

                    if make_dir is None and make_target is None:
                        return 1

                    target_pairs.append((make_dir, make_target))

                # Possibly add extra make depencies using dumbmake.
                if not disable_extra_make_dependencies:
                    from dumbmake.dumbmake import (dependency_map,
                                                   add_extra_dependencies)
                    depfile = os.path.join(self.topsrcdir, 'build',
                                           'dumbmake-dependencies')
                    with open(depfile) as f:
                        dm = dependency_map(f.readlines())
                    new_pairs = list(add_extra_dependencies(target_pairs, dm))
                    self.log(
                        logging.DEBUG, 'dumbmake', {
                            'target_pairs': target_pairs,
                            'new_pairs': new_pairs
                        },
                        'Added extra dependencies: will build {new_pairs} ' +
                        'instead of {target_pairs}.')
                    target_pairs = new_pairs

                # Build target pairs.
                for make_dir, make_target in target_pairs:
                    status = self._run_make(directory=make_dir,
                                            target=make_target,
                                            line_handler=output.on_line,
                                            log=False,
                                            print_directory=False,
                                            ensure_exit_code=False,
                                            num_jobs=jobs,
                                            silent=not verbose)

                    if status != 0:
                        break
            else:
                status = self._run_make(srcdir=True,
                                        filename='client.mk',
                                        line_handler=output.on_line,
                                        log=False,
                                        print_directory=False,
                                        allow_parallel=False,
                                        ensure_exit_code=False,
                                        num_jobs=jobs,
                                        silent=not verbose)

                self.log(logging.WARNING, 'warning_summary',
                         {'count': len(monitor.warnings_database)},
                         '{count} compiler warnings present.')

            monitor.finish()

        high_finder, finder_percent = monitor.have_high_finder_usage()
        if high_finder:
            print(FINDER_SLOW_MESSAGE % finder_percent)

        long_build = monitor.elapsed > 600

        if status:
            return status

        if long_build:
            print(
                'We know it took a while, but your build finally finished successfully!'
            )
        else:
            print('Your build was successful!')

        # Only for full builds because incremental builders likely don't
        # need to be burdened with this.
        if not what:
            # Fennec doesn't have useful output from just building. We should
            # arguably make the build action useful for Fennec. Another day...
            if self.substs['MOZ_BUILD_APP'] != 'mobile/android':
                app_path = self.get_binary_path('app')
                print('To take your build for a test drive, run: %s' %
                      app_path)
            app = self.substs['MOZ_BUILD_APP']
            if app in ('browser', 'mobile/android'):
                print(
                    'Please remember that you also need to PACKAGE your build '
                    'to have all components properly included and unnecessary files removed.'
                )

        return status