From fbc37fe1166709a30ec86ab9316ae1121212ccf3 Mon Sep 17 00:00:00 2001 From: Andrew Halberstadt Date: Mon, 28 Mar 2016 11:18:24 -0400 Subject: [PATCH] Bug 1255450 - [mach] Enable runtime configuration files, r=gps Runtime configs have been implemented for awhile, but disabled. This patch enables configuration. Config files will be loaded in the following order (later files override earlier ones): 1a. $MACHRC 1b. $MOZBUILD_STATE_PATH/machrc (if $MACHRC is unset) 2. topsrcdir/machrc 3. CLI via --settings Note: .machrc may be used instead of machrc if desired. MozReview-Commit-ID: IntONAZLGML --HG-- extra : rebase_source : ff79b129eaea7cca5064d30fa6ddc76fceb9669b --- .gitignore | 3 +- .hgignore | 2 +- build/mach_bootstrap.py | 7 ++++ python/mach/docs/index.rst | 1 + python/mach/mach/main.py | 72 ++++++++++++++++++++------------------ 5 files changed, 49 insertions(+), 36 deletions(-) diff --git a/.gitignore b/.gitignore index 9e7f4f6a210b..31ab036c98f4 100644 --- a/.gitignore +++ b/.gitignore @@ -28,7 +28,8 @@ ID /config.cache /config.log /.clang_complete -/mach.ini +/machrc +/.machrc # Empty marker file that's generated when we check out NSS security/manager/.nss.checkout diff --git a/.hgignore b/.hgignore index 83a351aa9884..1b177b080065 100644 --- a/.hgignore +++ b/.hgignore @@ -24,7 +24,7 @@ ^config\.cache$ ^config\.log$ ^\.clang_complete -^mach.ini$ +^\.?machrc$ # Empty marker file that's generated when we check out NSS ^security/manager/\.nss\.checkout$ diff --git a/build/mach_bootstrap.py b/build/mach_bootstrap.py index d7c2742467e1..190fd6497c29 100644 --- a/build/mach_bootstrap.py +++ b/build/mach_bootstrap.py @@ -120,6 +120,7 @@ MACH_MODULES = [ 'layout/tools/reftest/mach_commands.py', 'python/mach_commands.py', 'python/mach/mach/commands/commandinfo.py', + 'python/mach/mach/commands/settings.py', 'python/compare-locales/mach_commands.py', 'python/mozboot/mozboot/mach_commands.py', 'python/mozbuild/mozbuild/mach_commands.py', @@ -400,6 +401,12 @@ def bootstrap(topsrcdir, mozilla_dir=None): mach = mach.main.Mach(os.getcwd()) mach.populate_context_handler = populate_context + if not mach.settings_paths: + # default global machrc location + mach.settings_paths.append(get_state_dir()[0]) + # always load local repository configuration + mach.settings_paths.append(mozilla_dir) + for category, meta in CATEGORIES.items(): mach.define_category(category, meta['short'], meta['long'], meta['priority']) diff --git a/python/mach/docs/index.rst b/python/mach/docs/index.rst index b4213cb7834d..cd2056333f08 100644 --- a/python/mach/docs/index.rst +++ b/python/mach/docs/index.rst @@ -72,3 +72,4 @@ best fit for you. commands driver logging + settings diff --git a/python/mach/mach/main.py b/python/mach/mach/main.py index 93f058f9fd0d..164c85d67dd8 100644 --- a/python/mach/mach/main.py +++ b/python/mach/mach/main.py @@ -185,6 +185,9 @@ class Mach(object): require_conditions -- If True, commands that do not have any condition functions applied will be skipped. Defaults to False. + settings_paths -- A list of files or directories in which to search + for settings files to load. + """ USAGE = """%(prog)s [global arguments] command [command arguments] @@ -211,6 +214,10 @@ To see more help for a specific command, run: self.log_manager = LoggingManager() self.logger = logging.getLogger(__name__) self.settings = ConfigSettings() + self.settings_paths = [] + + if 'MACHRC' in os.environ: + self.settings_paths.append(os.environ['MACHRC']) self.log_manager.register_structured_logger(self.logger) self.global_arguments = [] @@ -360,6 +367,12 @@ To see more help for a specific command, run: sys.stderr = orig_stderr def _run(self, argv): + # Load settings as early as possible so things in dispatcher.py + # can use them. + for provider in Registrar.settings_providers: + self.settings.register_provider(provider) + self.load_settings(self.settings_paths) + context = CommandContext(cwd=self.cwd, settings=self.settings, log_manager=self.log_manager, commands=Registrar) @@ -412,7 +425,10 @@ To see more help for a specific command, run: self.log_manager.add_terminal_logging(level=log_level, write_interval=args.log_interval, write_times=write_times) - self.load_settings(args) + if args.settings_file: + # Argument parsing has already happened, so settings that apply + # to command line handling (e.g alias, defaults) will be ignored. + self.load_settings(args.settings_file) if not hasattr(args, 'mach_handler'): raise MachError('ArgumentParser result missing mach handler info.') @@ -492,43 +508,31 @@ To see more help for a specific command, run: for l in traceback.format_list(stack): fh.write(l) - def load_settings(self, args): - """Determine which settings files apply and load them. + def load_settings(self, paths): + """Load the specified settings files. - Currently, we only support loading settings from a single file. - Ideally, we support loading from multiple files. This is supported by - the ConfigSettings API. However, that API currently doesn't track where - individual values come from, so if we load from multiple sources then - save, we effectively do a full copy. We don't want this. Until - ConfigSettings does the right thing, we shouldn't expose multi-file - loading. + If a directory is specified, the following basenames will be + searched for in this order: - We look for a settings file in the following locations. The first one - found wins: - - 1) Command line argument - 2) Environment variable - 3) Default path + machrc, .machrc """ - # Settings are disabled until integration with command providers is - # worked out. - self.settings = None - return False + if isinstance(paths, basestring): + paths = [paths] - for provider in Registrar.settings_providers: - provider.register_settings() - self.settings.register_provider(provider) + valid_names = ('machrc', '.machrc') + def find_in_dir(base): + if os.path.isfile(base): + return base - p = os.path.join(self.cwd, 'mach.ini') + for name in valid_names: + path = os.path.join(base, name) + if os.path.isfile(path): + return path - if args.settings_file: - p = args.settings_file - elif 'MACH_SETTINGS_FILE' in os.environ: - p = os.environ['MACH_SETTINGS_FILE'] + files = map(find_in_dir, self.settings_paths) + files = filter(bool, files) - self.settings.load_file(p) - - return os.path.exists(p) + self.settings.load_files(files) def get_argument_parser(self, context): """Returns an argument parser for the command-line interface.""" @@ -540,9 +544,6 @@ To see more help for a specific command, run: # help messages are printed. global_group = parser.add_argument_group('Global Arguments') - #global_group.add_argument('--settings', dest='settings_file', - # metavar='FILENAME', help='Path to settings file.') - global_group.add_argument('-v', '--verbose', dest='verbose', action='store_true', default=False, help='Print verbose output.') @@ -566,6 +567,9 @@ To see more help for a specific command, run: help='Show this help message.') global_group.add_argument('--debug-command', action='store_true', help='Start a Python debugger when command is dispatched.') + global_group.add_argument('--settings', dest='settings_file', + metavar='FILENAME', default=None, + help='Path to settings file.') for args, kwargs in self.global_arguments: global_group.add_argument(*args, **kwargs)