Bug 1528241 - Add a trace mode to python configure that logs internal values. r=chmanchester
authorMike Hommey <mh+mozilla@glandium.org>
Fri, 15 Feb 2019 22:52:47 +0900
changeset 460007 effd6611eaccae8fcc3572286a229f9efe96ce7d
parent 460006 20edea24a191284d3547fed803e9faae40192be1
child 460008 e48a5648d0674bcee624f67a898ce17a21898b22
push id35581
push userdvarga@mozilla.com
push dateWed, 20 Feb 2019 04:05:40 +0000
treeherdermozilla-central@bf3951daded0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerschmanchester
bugs1528241
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1528241 - Add a trace mode to python configure that logs internal values. r=chmanchester Differential Revision: https://phabricator.services.mozilla.com/D19940
configure.py
python/mozbuild/mozbuild/configure/__init__.py
python/mozbuild/mozbuild/configure/options.py
--- a/configure.py
+++ b/configure.py
@@ -9,29 +9,37 @@ import itertools
 import logging
 import os
 import sys
 import textwrap
 
 
 base_dir = os.path.abspath(os.path.dirname(__file__))
 sys.path.insert(0, os.path.join(base_dir, 'python', 'mozbuild'))
-from mozbuild.configure import ConfigureSandbox
+from mozbuild.configure import (
+    ConfigureSandbox,
+    TRACE,
+)
 from mozbuild.pythonutil import iter_modules_in_path
 from mozbuild.backend.configenvironment import PartialConfigEnvironment
 from mozbuild.util import (
     indented_repr,
     encode,
 )
 import mozpack.path as mozpath
 
 
 def main(argv):
     config = {}
+
     sandbox = ConfigureSandbox(config, os.environ, argv)
+
+    if os.environ.get('MOZ_CONFIGURE_TRACE'):
+        sandbox._logger.setLevel(TRACE)
+
     sandbox.run(os.path.join(os.path.dirname(__file__), 'moz.configure'))
 
     if sandbox._help:
         return 0
 
     return config_status(config)
 
 
--- a/python/mozbuild/mozbuild/configure/__init__.py
+++ b/python/mozbuild/mozbuild/configure/__init__.py
@@ -36,16 +36,20 @@ from mozbuild.util import (
     memoized_property,
     ReadOnlyDict,
     ReadOnlyNamespace,
 )
 
 import mozpack.path as mozpath
 
 
+# TRACE logging level, below (thus more verbose than) DEBUG
+TRACE = 5
+
+
 class ConfigureError(Exception):
     pass
 
 
 class SandboxDependsFunction(object):
     '''Sandbox-visible representation of @depends functions.'''
     def __init__(self, unsandboxed):
         self._or = unsandboxed.__or__
@@ -144,18 +148,17 @@ class DependsFunction(object):
         if self.when and not self.sandbox._value_for(self.when):
             return None
 
         resolved_args = [self.sandbox._value_for(d)
                          for d in self.dependencies]
         return self._func(*resolved_args)
 
     def __repr__(self):
-        return '<%s.%s %s(%s)>' % (
-            self.__class__.__module__,
+        return '<%s %s(%s)>' % (
             self.__class__.__name__,
             self.name,
             ', '.join(repr(d) for d in self.dependencies),
         )
 
     def __or__(self, other):
         if isinstance(other, SandboxDependsFunction):
             other = self.sandbox._depends.get(other)
@@ -328,16 +331,17 @@ class ConfigureSandbox(dict):
         # A list of conditions to apply as a default `when` for every *_impl()
         self._default_conditions = []
 
         self._helper = CommandLineHelper(environ, argv)
 
         assert isinstance(config, dict)
         self._config = config
 
+        logging.addLevelName(TRACE, 'TRACE')
         if logger is None:
             logger = moz_logger = logging.getLogger('moz.configure')
             logger.setLevel(logging.DEBUG)
             formatter = logging.Formatter('%(levelname)s: %(message)s')
             handler = ConfigureOutputHandler(stdout, stderr)
             handler.setFormatter(formatter)
             queue_debug = handler.queue_debug
             logger.addHandler(handler)
@@ -506,17 +510,19 @@ class ConfigureSandbox(dict):
 
         elif isinstance(obj, Option):
             return self._value_for_option(obj)
 
         assert False
 
     @memoize
     def _value_for_depends(self, obj):
-        return obj.result()
+        value = obj.result()
+        self._logger.log(TRACE, '%r = %r', obj, value)
+        return value
 
     @memoize
     def _value_for_option(self, option):
         implied = {}
         for implied_option in self._implied_options[:]:
             if implied_option.name not in (option.name, option.env):
                 continue
             self._implied_options.remove(implied_option)
@@ -567,18 +573,20 @@ class ConfigureSandbox(dict):
         if when and not self._value_for(when) and value is not None:
             # If the option was passed explicitly, we throw an error that
             # the option is not available. Except when the option was passed
             # from the environment, because that would be too cumbersome.
             if value.origin not in ('default', 'environment'):
                 raise InvalidOptionError(
                     '%s is not available in this configuration'
                     % option_string.split('=', 1)[0])
+            self._logger.log(TRACE, '%r = None', option)
             return None
 
+        self._logger.log(TRACE, '%r = %r', option, value)
         return value
 
     def _dependency(self, arg, callee_name, arg_name=None):
         if isinstance(arg, types.StringTypes):
             prefix, name, values = Option.split_option(arg)
             if values != ():
                 raise ConfigureError("Option must not contain an '='")
             if name not in self._options:
@@ -901,16 +909,21 @@ class ConfigureSandbox(dict):
         if not isinstance(name, types.StringTypes):
             raise TypeError("Unexpected type: '%s'" % type(name).__name__)
         if name in data:
             raise ConfigureError(
                 "Cannot add '%s' to configuration: Key already "
                 "exists" % name)
         value = self._resolve(value)
         if value is not None:
+            if self._logger.isEnabledFor(TRACE):
+                if data is self._config:
+                    self._logger.log(TRACE, 'set_config(%s, %r)', name, value)
+                elif data is self._config.get('DEFINES'):
+                    self._logger.log(TRACE, 'set_define(%s, %r)', name, value)
             data[name] = value
 
     def set_config_impl(self, name, value, when=None):
         '''Implementation of set_config().
         Set the configuration items with the given name to the given value.
         Both `name` and `value` can be references to @depends functions,
         in which case the result from these functions is used. If the result
         of either function is None, the configuration item is not set.
--- a/python/mozbuild/mozbuild/configure/options.py
+++ b/python/mozbuild/mozbuild/configure/options.py
@@ -383,18 +383,17 @@ class Option(object):
                         % (val, ', '.join("'%s'" % c for c in self.choices)))
 
             if relative_result is not None:
                 values = PositiveOptionValue(relative_result, origin=origin)
 
         return values
 
     def __repr__(self):
-        return '<%s.%s [%s]>' % (self.__class__.__module__,
-                                 self.__class__.__name__, self.option)
+        return '<%s [%s]>' % (self.__class__.__name__, self.option)
 
 
 class CommandLineHelper(object):
     '''Helper class to handle the various ways options can be given either
     on the command line of through the environment.
 
     For instance, an Option('--foo', env='FOO') can be passed as --foo on the
     command line, or as FOO=1 in the environment *or* on the command line.