Bug 1257823 - Keep track of the dependencies of @depends functions. r=nalexander
authorMike Hommey <mh+mozilla@glandium.org>
Wed, 23 Mar 2016 11:58:57 +0900
changeset 290140 407e18a1a0241fb62f0391d698ff9954625ad06b
parent 290139 62ae3968b2d82f17ec2853714ab565c02a7cb9bc
child 290141 06dc23858ed715e62229c2f80147af28b0416fff
push id74120
push usermh@glandium.org
push dateThu, 24 Mar 2016 00:04:54 +0000
treeherdermozilla-inbound@76d58b17343e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnalexander
bugs1257823
milestone48.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 1257823 - Keep track of the dependencies of @depends functions. r=nalexander @depends functions are declared like the following: @depends('--option', other_function, '--other-option', ...) def ... To simplify some of the processing related to those arguments it's passed, keep a tuple of Option and DummyFunction objects corresponding to those arguments.
python/mozbuild/mozbuild/configure/__init__.py
--- a/python/mozbuild/mozbuild/configure/__init__.py
+++ b/python/mozbuild/mozbuild/configure/__init__.py
@@ -112,16 +112,18 @@ class ConfigureSandbox(dict):
     ))
 
     def __init__(self, config, environ=os.environ, argv=sys.argv,
                  stdout=sys.stdout, stderr=sys.stderr):
         dict.__setitem__(self, '__builtins__', self.BUILTINS)
 
         self._paths = []
         self._templates = set()
+        # Store the real function and its dependencies, behind each
+        # DummyFunction generated from @depends.
         self._depends = {}
         self._seen = set()
 
         self._options = OrderedDict()
         # Store the raw values returned by @depends functions
         self._results = {}
         # Store values for each Option, as per returned by Option.get_value
         self._option_values = {}
@@ -221,20 +223,20 @@ class ConfigureSandbox(dict):
             raise KeyError('Cannot assign `%s` because it is neither a '
                            '@depends nor a @template' % key)
 
         return super(ConfigureSandbox, self).__setitem__(key, value)
 
     def _resolve(self, arg, need_help_dependency=True):
         if isinstance(arg, DummyFunction):
             assert arg in self._depends
-            func = self._depends[arg]
+            func, deps = self._depends[arg]
             assert not inspect.isgeneratorfunction(func)
             assert func in self._results
-            if need_help_dependency and not func.with_help:
+            if need_help_dependency and self._help_option not in deps:
                 raise ConfigureError("Missing @depends for `%s`: '--help'" %
                                      func.__name__)
             result = self._results[func]
             return result
         return arg
 
     def option_impl(self, *args, **kwargs):
         '''Implementation of option()
@@ -293,83 +295,85 @@ class ConfigureSandbox(dict):
         for its execution. This different global namespace exposes a limited
         set of functions from os.path, and one additional functions:
         `imply_option`. It allows to inject additional options as if they had
         been passed on the command line.
         '''
         if not args:
             raise ConfigureError('@depends needs at least one argument')
 
-        with_help = False
         resolved_args = []
+        dependencies = []
         for arg in args:
             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:
                     raise ConfigureError("'%s' is not a known option. "
                                          "Maybe it's declared too late?"
                                          % arg)
                 arg = self._options[name]
                 if arg == self._help_option:
                     with_help = True
                 self._seen.add(arg)
+                dependencies.append(arg)
                 assert arg in self._option_values or self._help
                 resolved_arg = self._option_values.get(arg)
             elif isinstance(arg, DummyFunction):
                 assert arg in self._depends
-                arg = self._depends[arg]
+                dependencies.append(arg)
+                arg, _ = self._depends[arg]
                 resolved_arg = self._results.get(arg)
             else:
                 raise TypeError(
                     "Cannot use object of type '%s' as argument to @depends"
                     % type(arg))
             resolved_args.append(resolved_arg)
+        dependencies = tuple(dependencies)
 
         def decorator(func):
             if inspect.isgeneratorfunction(func):
                 raise ConfigureError(
                     'Cannot decorate generator functions with @depends')
             func, glob = self._prepare_function(func)
             result = DependsOutput()
             glob.update(
                 imply_option=result.imply_option,
             )
             dummy = wraps(func)(DummyFunction())
-            self._depends[dummy] = func
-            func.with_help = with_help
+            self._depends[dummy] = func, dependencies
+            with_help = self._help_option in dependencies
             if with_help:
                 for arg in args:
-                    if (isinstance(arg, DummyFunction) and
-                            not self._depends[arg].with_help):
-                        raise ConfigureError(
-                            "`%s` depends on '--help' and `%s`. "
-                            "`%s` must depend on '--help'"
-                            % (func.__name__, arg.__name__, arg.__name__))
+                    if isinstance(arg, DummyFunction):
+                        _, deps = self._depends[arg]
+                        if self._help_option not in deps:
+                            raise ConfigureError(
+                                "`%s` depends on '--help' and `%s`. "
+                                "`%s` must depend on '--help'"
+                                % (func.__name__, arg.__name__, arg.__name__))
 
             if self._help and not with_help:
                 return dummy
 
             self._results[func] = func(*resolved_args)
 
             for option, reason in result.implied_options:
                 self._helper.add(option, 'implied')
                 if not reason:
                     deps = []
-                    for name, value in zip(args, resolved_args):
-                        if not isinstance(value, OptionValue):
+                    for arg in dependencies:
+                        if not isinstance(arg, Option):
                             raise ConfigureError(
                                 "Cannot infer what implied '%s'" % option)
-                        if name == '--help':
+                        if arg == self._help_option:
                             continue
-                        prefix, opt, values = Option.split_option(name)
-                        deps.append(value.format(
-                            self._raw_options.get(self._options[opt])
-                            or name))
+                        deps.append(self._raw_options.get(arg) or
+                                    self.arg.option)
                     if len(deps) != 1:
                         raise ConfigureError(
                             "Cannot infer what implied '%s'" % option)
                     reason = deps[0]
 
                 self._implied_options[option] = func, reason
 
             return dummy