Bug 1296530 - Add a `when` argument to @depends(). r?chmanchester draft
authorMike Hommey <mh+mozilla@glandium.org>
Wed, 12 Oct 2016 13:52:08 +0900
changeset 425036 499adad7b025e63760f2ac19208efac6a2c481a7
parent 425035 370840715b43ecba02afe2162a678e1a70385c00
child 425037 a542279b7c654e698237a0f6093f4917b6e09748
push id32321
push userbmo:mh+mozilla@glandium.org
push dateFri, 14 Oct 2016 02:53:47 +0000
reviewerschmanchester
bugs1296530
milestone52.0a1
Bug 1296530 - Add a `when` argument to @depends(). r?chmanchester
python/mozbuild/mozbuild/configure/__init__.py
python/mozbuild/mozbuild/test/configure/test_configure.py
--- a/python/mozbuild/mozbuild/configure/__init__.py
+++ b/python/mozbuild/mozbuild/configure/__init__.py
@@ -160,16 +160,19 @@ class ConfigureSandbox(dict):
         self._implied_options = []
 
         # Store all results from _prepare_function
         self._prepared_functions = set()
 
         # Queue of functions to execute, with their arguments
         self._execution_queue = []
 
+        # Store the `when`s associated to some options/depends.
+        self._conditions = {}
+
         self._helper = CommandLineHelper(environ, argv)
 
         assert isinstance(config, dict)
         self._config = config
 
         if logger is None:
             logger = moz_logger = logging.getLogger('moz.configure')
             logger.setLevel(logging.DEBUG)
@@ -348,16 +351,20 @@ class ConfigureSandbox(dict):
                             % (obj.name, arg.name, arg.name))
         elif self._help or need_help_dependency:
             raise ConfigureError("Missing @depends for `%s`: '--help'" %
                                  obj.name)
         return self._value_for_depends_real(obj)
 
     @memoize
     def _value_for_depends_real(self, obj):
+        when = self._conditions.get(obj)
+        if when and not self._value_for(when):
+            return None
+
         resolved_args = [self._value_for(d) for d in obj.dependencies]
         return obj.func(*resolved_args)
 
     @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):
@@ -444,17 +451,17 @@ class ConfigureSandbox(dict):
         if option.env:
             self._options[option.env] = option
 
         if self._help:
             self._help.add(option)
 
         return option
 
-    def depends_impl(self, *args):
+    def depends_impl(self, *args, **kwargs):
         '''Implementation of @depends()
         This function is a decorator. It returns a function that subsequently
         takes a function and returns a dummy function. The dummy function
         identifies the actual function for the sandbox, while preventing
         further function calls from within the sandbox.
 
         @depends() takes a variable number of option strings or dummy function
         references. The decorated function is called as soon as the decorator
@@ -465,24 +472,36 @@ class ConfigureSandbox(dict):
 
         The decorated function is altered to use a different global namespace
         for its execution. This different global namespace exposes a limited
         set of functions from os.path.
         '''
         if not args:
             raise ConfigureError('@depends needs at least one argument')
 
+        for k in kwargs:
+            if k != 'when':
+                raise TypeError(
+                    "depends_impl() got an unexpected keyword argument '%s'"
+                    % k)
+
+        when = kwargs.get('when')
+        if when is not None:
+            when = self._dependency(when, '@depends', 'when')
+
         dependencies = tuple(self._dependency(arg, '@depends') for arg in args)
 
         def decorator(func):
             if inspect.isgeneratorfunction(func):
                 raise ConfigureError(
                     'Cannot decorate generator functions with @depends')
             func, glob = self._prepare_function(func)
             depends = DependsFunction(self, func, dependencies)
+            if when:
+                self._conditions[depends] = when
 
             # Only @depends functions with a dependency on '--help' are
             # executed immediately. Everything else is queued for later
             # execution.
             if self._help_option in dependencies:
                 self._value_for(depends)
             elif not self._help:
                 self._execution_queue.append((self._value_for, (depends,)))
--- a/python/mozbuild/mozbuild/test/configure/test_configure.py
+++ b/python/mozbuild/mozbuild/test/configure/test_configure.py
@@ -873,16 +873,55 @@ class TestConfigure(unittest.TestCase):
                 def foo(_):
                     return
             '''):
                 self.get_config()
 
         self.assertEquals(e.exception.message,
                           "depends_impl() got an unexpected keyword argument 'foo'")
 
+    def test_depends_when(self):
+        with self.moz_configure('''
+            @depends('--help')
+            def always(_):
+                return True
+            @depends('--help')
+            def never(_):
+                return False
+
+            @depends('--help', when=always)
+            def foo(_):
+                return 'foo'
+
+            set_config('FOO', foo)
+
+            @depends('--help', when=never)
+            def bar(_):
+                return 'bar'
+
+            set_config('BAR', bar)
+
+            option('--with-qux', help='qux')
+            @depends('--help', when='--with-qux')
+            def qux(_):
+                return 'qux'
+
+            set_config('QUX', qux)
+        '''):
+            config = self.get_config()
+            self.assertEquals(config, {
+                'FOO': 'foo',
+            })
+
+            config = self.get_config(['--with-qux'])
+            self.assertEquals(config, {
+                'FOO': 'foo',
+                'QUX': 'qux',
+            })
+
     def test_imports_failures(self):
         with self.assertRaises(ConfigureError) as e:
             with self.moz_configure('''
                 @imports('os')
                 @template
                 def foo(value):
                     return value
             '''):