diff --git a/python/mozbuild/mozbuild/base.py b/python/mozbuild/mozbuild/base.py index 16dbb2e4709a..3d5a24fa849c 100644 --- a/python/mozbuild/mozbuild/base.py +++ b/python/mozbuild/mozbuild/base.py @@ -5,6 +5,7 @@ from __future__ import print_function, unicode_literals import logging +import multiprocessing import os import subprocess import sys @@ -159,8 +160,8 @@ class MozbuildObject(ProcessExecutionMixin): if filename: args.extend(['-f', filename]) - #if allow_parallel: - # args.append('-j%d' % self.settings.build.threads) + if allow_parallel: + args.append('-j%d' % multiprocessing.cpu_count()) if ignore_errors: args.append('-k') diff --git a/python/mozbuild/mozbuild/mach_commands.py b/python/mozbuild/mozbuild/mach_commands.py index 69df519be550..70db2257280d 100644 --- a/python/mozbuild/mozbuild/mach_commands.py +++ b/python/mozbuild/mozbuild/mach_commands.py @@ -17,12 +17,20 @@ from mach.decorators import ( from mozbuild.base import MachCommandBase +BUILD_WHAT_HELP = ''' +What to build. Can be a top-level make target or a relative directory. If +multiple options are provided, they will be built serially. BUILDING ONLY PARTS +OF THE TREE CAN RESULT IN BAD TREE STATE. USE AT YOUR OWN RISK. +'''.strip() + + @CommandProvider class Build(MachCommandBase): """Interface to build the tree.""" @Command('build', help='Build the tree.') - def build(self): + @CommandArgument('what', default=None, nargs='*', help=BUILD_WHAT_HELP) + 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 @@ -52,18 +60,83 @@ class Build(MachCommandBase): self.log(logging.INFO, 'build_output', {'line': line}, '{line}') - status = self._run_make(srcdir=True, filename='client.mk', - line_handler=on_line, log=False, print_directory=False, - ensure_exit_code=False) + def resolve_target_to_make(target): + if os.path.isabs(target): + print('Absolute paths for make targets are not allowed.') + return (None, None) - self.log(logging.WARNING, 'warning_summary', - {'count': len(warnings_collector.database)}, - '{count} compiler warnings present.') + target = target.replace(os.sep, '/') + + abs_target = os.path.join(self.topobjdir, target) + + # For directories, run |make -C dir|. If the directory does not + # contain a Makefile, check parents until we find one. At worst, + # this will terminate at the root. + if os.path.isdir(abs_target): + current = abs_target + + while True: + make_path = os.path.join(current, 'Makefile') + if os.path.exists(make_path): + return (current[len(self.topobjdir) + 1:], None) + + current = os.path.dirname(current) + + # If it's not in a directory, this is probably a top-level make + # target. Treat it as such. + if '/' not in target: + return (None, target) + + # We have a relative path within the tree. We look for a Makefile + # as far into the path as possible. Then, we compute the make + # target as relative to that directory. + reldir = os.path.dirname(target) + target = os.path.basename(target) + + while True: + make_path = os.path.join(self.topobjdir, reldir, 'Makefile') + + if os.path.exists(make_path): + return (reldir, target) + + target = os.path.join(os.path.basename(reldir), target) + reldir = os.path.dirname(reldir) + + # End of resolve_target_to_make. + + 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: + make_dir, make_target = resolve_target_to_make(target) + + 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) + print('Finished building. Built files are in %s' % self.topobjdir) + return status @Command('clobber', help='Clobber the tree (delete the object directory).')