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
--- a/.gitignore
+++ b/.gitignore
@@ -23,17 +23,18 @@ ID
# User files that may appear at the root
/.mozconfig*
/mozconfig
/configure
/old-configure
/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
# Build directories
/obj*/
# Build directories for js shell
--- a/.hgignore
+++ b/.hgignore
@@ -19,17 +19,17 @@
# User files that may appear at the root
^\.mozconfig
^mozconfig*
^configure$
^old-configure$
^config\.cache$
^config\.log$
^\.clang_complete
-^mach.ini$
+^\.?machrc$
# Empty marker file that's generated when we check out NSS
^security/manager/\.nss\.checkout$
# Build directories
^obj
# Build directories for js shell
--- a/build/mach_bootstrap.py
+++ b/build/mach_bootstrap.py
@@ -115,16 +115,17 @@ SEARCH_PATHS = [
MACH_MODULES = [
'addon-sdk/mach_commands.py',
'build/valgrind/mach_commands.py',
'dom/bindings/mach_commands.py',
'dom/media/test/external/mach_commands.py',
'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',
'python/mozbuild/mozbuild/backend/mach_commands.py',
'python/mozbuild/mozbuild/compilation/codecomplete.py',
'python/mozbuild/mozbuild/frontend/mach_commands.py',
'services/common/tests/mach_commands.py',
'testing/firefox-ui/mach_commands.py',
@@ -395,16 +396,22 @@ def bootstrap(topsrcdir, mozilla_dir=Non
if key == 'post_dispatch_handler':
return post_dispatch_handler
raise AttributeError(key)
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'])
for path in MACH_MODULES:
mach.load_commands_from_file(os.path.join(mozilla_dir, path))
return mach
--- a/python/mach/docs/index.rst
+++ b/python/mach/docs/index.rst
@@ -67,8 +67,9 @@ mach is suitable for anybody to use. Unt
best fit for you.
.. toctree::
:maxdepth: 1
commands
driver
logging
+ settings
--- a/python/mach/mach/main.py
+++ b/python/mach/mach/main.py
@@ -180,16 +180,19 @@ class Mach(object):
for use in command handlers.
For backwards compatibility, it is also called before command
dispatch without a key, allowing the context handler to add
attributes to the context instance.
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]
mach (German for "do") is the main interface to the Mozilla build system and
common developer tasks.
You tell mach the command you want to perform and it does it for you.
@@ -206,16 +209,20 @@ To see more help for a specific command,
def __init__(self, cwd):
assert os.path.isdir(cwd)
self.cwd = cwd
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 = []
self.populate_context_handler = None
def add_global_argument(self, *args, **kwargs):
"""Register a global argument with the argument parser.
@@ -355,16 +362,22 @@ To see more help for a specific command,
return 1
finally:
sys.stdin = orig_stdin
sys.stdout = orig_stdout
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)
if self.populate_context_handler:
self.populate_context_handler(context)
context = ContextWrapper(context, self.populate_context_handler)
@@ -407,17 +420,20 @@ To see more help for a specific command,
if args.log_no_times or 'MACH_NO_WRITE_TIMES' in os.environ:
write_times = False
# Always enable terminal logging. The log manager figures out if we are
# actually in a TTY or are a pipe and does the right thing.
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.')
handler = getattr(args, 'mach_handler')
try:
return Registrar._run_command_handler(handler, context=context,
@@ -487,67 +503,52 @@ To see more help for a specific command,
for l in traceback.format_exception_only(exc_type, exc_value):
fh.write(l)
fh.write('\n')
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."""
parser = ArgumentParser(add_help=False,
usage='%(prog)s [global arguments] command [command arguments]')
# Order is important here as it dictates the order the auto-generated
# 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.')
global_group.add_argument('-l', '--log-file', dest='logfile',
metavar='FILENAME', type=argparse.FileType('ab'),
help='Filename to write log data to.')
global_group.add_argument('--log-interval', dest='log_interval',
action='store_true', default=False,
@@ -561,16 +562,19 @@ To see more help for a specific command,
action='store_true', default=suppress_log_by_default,
help='Do not prefix log lines with times. By default, mach will '
'prefix each output line with the time since command start.')
global_group.add_argument('-h', '--help', dest='help',
action='store_true', default=False,
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)
# We need to be last because CommandAction swallows all remaining
# arguments and argparse parses arguments in the order they were added.
parser.add_argument('command', action=CommandAction,
registrar=Registrar, context=context)