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 290217 407e18a1a0241fb62f0391d698ff9954625ad06b
parent 290216 62ae3968b2d82f17ec2853714ab565c02a7cb9bc
child 290218 06dc23858ed715e62229c2f80147af28b0416fff
push id19656
push usergwagner@mozilla.com
push dateMon, 04 Apr 2016 13:43:23 +0000
treeherderb2g-inbound@e99061fde28a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnalexander
bugs1257823
milestone48.0a1
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