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))
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)))
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)))
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)))
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])))
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)))
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]
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)
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)
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]
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
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
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
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
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
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
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
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
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
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
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