Bug 1313306 - Allow @depends(when=something) without additional dependencies. r=chmanchester draft
authorMike Hommey <mh+mozilla@glandium.org>
Wed, 26 Oct 2016 16:42:06 +0900
changeset 431899 967dd2ae27b92a7a06f14183af6cf25b572349c1
parent 431898 0872ec7059c2bc417d44ac8295780a935c96d7d3
child 431900 5d4f3f7617a3dfec5392dfd7de7360f2a533a66e
push id34144
push userbmo:mh+mozilla@glandium.org
push dateMon, 31 Oct 2016 22:40:13 +0000
reviewerschmanchester
bugs1313306
milestone52.0a1
Bug 1313306 - Allow @depends(when=something) without additional dependencies. r=chmanchester Also allow when=True/False to avoid the chicken-egg problem of using a generic `when` to use in replacement of @depends('--help') for things like @dependable.
python/mozbuild/mozbuild/configure/__init__.py
python/mozbuild/mozbuild/configure/lint.py
python/mozbuild/mozbuild/test/configure/test_checks_configure.py
python/mozbuild/mozbuild/test/configure/test_compile_checks.py
python/mozbuild/mozbuild/test/configure/test_configure.py
--- a/python/mozbuild/mozbuild/configure/__init__.py
+++ b/python/mozbuild/mozbuild/configure/__init__.py
@@ -279,16 +279,20 @@ class ConfigureSandbox(dict):
         }
         log_namespace['queue_debug'] = queue_debug
         self.log_impl = ReadOnlyNamespace(**log_namespace)
 
         self._help = None
         self._help_option = self.option_impl('--help',
                                              help='print this message')
         self._seen.add(self._help_option)
+
+        self._always = DependsFunction(self, lambda: True, [])
+        self._never = DependsFunction(self, lambda: False, [])
+
         if self._value_for(self._help_option):
             self._help = HelpFormatter(argv[0])
             self._help.add(self._help_option)
         elif moz_logger:
             handler = logging.FileHandler('config.log', mode='w', delay=True)
             handler.setFormatter(formatter)
             logger.addHandler(handler)
 
@@ -489,17 +493,21 @@ class ConfigureSandbox(dict):
         else:
             raise TypeError(
                 "Cannot use object of type '%s' as %sargument to %s"
                 % (type(arg).__name__, '`%s` ' % arg_name if arg_name else '',
                    callee_name))
         return arg
 
     def _normalize_when(self, when, callee_name):
-        if when is not None:
+        if when is True:
+            when = self._always
+        elif when is False:
+            when = self._never
+        elif when is not None:
             when = self._dependency(when, callee_name, 'when')
 
         if self._default_conditions:
             # Create a pseudo @depends function for the combination of all
             # default conditions and `when`.
             dependencies = [when] if when else []
             dependencies.extend(self._default_conditions)
             if len(dependencies) == 1:
@@ -566,27 +574,27 @@ class ConfigureSandbox(dict):
         function results corresponding to each of the arguments to @depends.
         As an exception, when a HelpFormatter is attached, only functions that
         have '--help' in their @depends argument list are called.
 
         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 = self._normalize_when(kwargs.get('when'), '@depends')
 
+        if not when and not args:
+            raise ConfigureError('@depends needs at least one argument')
+
         dependencies = tuple(self._dependency(arg, '@depends') for arg in args)
 
         conditions = [
             self._conditions[d]
             for d in dependencies
             if d in self._conditions and isinstance(d, Option)
         ]
         for c in conditions:
--- a/python/mozbuild/mozbuild/configure/lint.py
+++ b/python/mozbuild/mozbuild/configure/lint.py
@@ -29,17 +29,18 @@ class LintSandbox(ConfigureSandbox):
     def run(self, path=None):
         if path:
             self.include_file(path)
 
     def _missing_help_dependency(self, obj):
         if isinstance(obj, CombinedDependsFunction):
             return False
         if isinstance(obj, DependsFunction):
-            if self._help_option in obj.dependencies:
+            if (self._help_option in obj.dependencies or
+                obj in (self._always, self._never)):
                 return False
             func = self._wrapped[obj.func]
             # We allow missing --help dependencies for functions that:
             # - don't use @imports
             # - don't have a closure
             # - don't use global variables
             if func in self._imports or func.func_closure:
                 return True
--- a/python/mozbuild/mozbuild/test/configure/test_checks_configure.py
+++ b/python/mozbuild/mozbuild/test/configure/test_checks_configure.py
@@ -400,27 +400,27 @@ class TestChecksConfigure(unittest.TestC
         with self.assertRaises(ConfigureError) as e:
             self.get_result('check_prog("FOO", "foo")')
 
         self.assertEqual(e.exception.message,
                          'progs must resolve to a list or tuple!')
 
         with self.assertRaises(ConfigureError) as e:
             self.get_result(
-                'foo = depends("--help")(lambda h: ("a", "b"))\n'
+                'foo = depends(when=True)(lambda: ("a", "b"))\n'
                 'check_prog("FOO", ("known-a",), input=foo)'
             )
 
         self.assertEqual(e.exception.message,
                          'input must resolve to a tuple or a list with a '
                          'single element, or a string')
 
         with self.assertRaises(ConfigureError) as e:
             self.get_result(
-                'foo = depends("--help")(lambda h: {"a": "b"})\n'
+                'foo = depends(when=True)(lambda: {"a": "b"})\n'
                 'check_prog("FOO", ("known-a",), input=foo)'
             )
 
         self.assertEqual(e.exception.message,
                          'input must resolve to a tuple or a list with a '
                          'single element, or a string')
 
     def test_check_prog_with_path(self):
--- a/python/mozbuild/mozbuild/test/configure/test_compile_checks.py
+++ b/python/mozbuild/mozbuild/test/configure/test_compile_checks.py
@@ -42,36 +42,36 @@ class BaseCompileChecks(unittest.TestCas
             os.path.abspath('/usr/bin/mockcc'): self.get_mock_compiler(
                 expected_test_content=expected_test_content,
                 expected_flags=expected_flags),
         }
 
         base_dir = os.path.join(topsrcdir, 'build', 'moz.configure')
 
         mock_compiler_defs = textwrap.dedent('''\
-            @depends('--help')
-            def extra_toolchain_flags(_):
+            @depends(when=True)
+            def extra_toolchain_flags():
                 return []
 
             include('%s/compilers-util.configure')
 
             @compiler_class
-            @depends('--help')
-            def c_compiler(_):
+            @depends(when=True)
+            def c_compiler():
                 return namespace(
                     flags=[],
                     type='gcc',
                     compiler=os.path.abspath('/usr/bin/mockcc'),
                     wrapper=[],
                     language='C',
                 )
 
             @compiler_class
-            @depends('--help')
-            def cxx_compiler(_):
+            @depends(when=True)
+            def cxx_compiler():
                 return namespace(
                     flags=[],
                     type='gcc',
                     compiler=os.path.abspath('/usr/bin/mockcc'),
                     wrapper=[],
                     language='C++',
                 )
         ''' % mozpath.normsep(base_dir))
@@ -305,33 +305,33 @@ class TestWarningChecks(BaseCompileCheck
             '_WARNINGS_CXXFLAGS': ['-Wfoo'],
         })
         self.assertEqual(out, textwrap.dedent('''\
             checking whether the C++ compiler supports -Wfoo... yes
         '''))
 
     def test_check_and_add_gcc_warning_when(self):
         cmd = textwrap.dedent('''\
-            @depends('--help')
-            def never(_):
+            @depends(when=True)
+            def never():
                 return False
             check_and_add_gcc_warning('-Wfoo', cxx_compiler, when=never)
         ''') + self.get_warnings()
 
         config, out, status = self.do_compile_test(cmd)
         self.assertEqual(status, 0)
         self.assertEqual(config, {
             '_WARNINGS_CFLAGS': [],
             '_WARNINGS_CXXFLAGS': [],
         })
         self.assertEqual(out, '')
 
         cmd = textwrap.dedent('''\
-            @depends('--help')
-            def always(_):
+            @depends(when=True)
+            def always():
                 return True
             check_and_add_gcc_warning('-Wfoo', cxx_compiler, when=always)
         ''') + self.get_warnings()
 
         config, out, status = self.do_compile_test(cmd)
         self.assertEqual(status, 0)
         self.assertEqual(config, {
             '_WARNINGS_CFLAGS': [],
@@ -364,33 +364,33 @@ class TestWarningChecks(BaseCompileCheck
         self.assertEqual(config, {
             '_WARNINGS_CFLAGS': ['-Wfoo'],
             '_WARNINGS_CXXFLAGS': [],
         })
         self.assertEqual(out, '')
 
     def test_add_gcc_warning_when(self):
         cmd = textwrap.dedent('''\
-            @depends('--help')
-            def never(_):
+            @depends(when=True)
+            def never():
                 return False
             add_gcc_warning('-Wfoo', c_compiler, when=never)
         ''') + self.get_warnings()
 
         config, out, status = self.do_compile_test(cmd)
         self.assertEqual(status, 0)
         self.assertEqual(config, {
             '_WARNINGS_CFLAGS': [],
             '_WARNINGS_CXXFLAGS': [],
         })
         self.assertEqual(out, '')
 
         cmd = textwrap.dedent('''\
-            @depends('--help')
-            def always(_):
+            @depends(when=True)
+            def always():
                 return True
             add_gcc_warning('-Wfoo', c_compiler, when=always)
         ''') + self.get_warnings()
 
         config, out, status = self.do_compile_test(cmd)
         self.assertEqual(status, 0)
         self.assertEqual(config, {
             '_WARNINGS_CFLAGS': ['-Wfoo'],
--- a/python/mozbuild/mozbuild/test/configure/test_configure.py
+++ b/python/mozbuild/mozbuild/test/configure/test_configure.py
@@ -425,25 +425,19 @@ class TestConfigure(unittest.TestCase):
 
         with self.assertRaises(ConfigureError):
             # Both --set-foo and --set-name=FOO are going to try to
             # set_config('FOO'...)
             get_config(['--set-foo', '--set-name=FOO'])
 
     def test_set_config_when(self):
         with self.moz_configure('''
-            @depends('--help')
-            def always(_):
-                return True
-            @depends('--help')
-            def never(_):
-                return False
             option('--with-qux', help='qux')
-            set_config('FOO', 'foo', when=always)
-            set_config('BAR', 'bar', when=never)
+            set_config('FOO', 'foo', when=True)
+            set_config('BAR', 'bar', when=False)
             set_config('QUX', 'qux', when='--with-qux')
         '''):
             config = self.get_config()
             self.assertEquals(config, {
                 'FOO': 'foo',
             })
             config = self.get_config(['--with-qux'])
             self.assertEquals(config, {
@@ -480,25 +474,19 @@ class TestConfigure(unittest.TestCase):
 
         with self.assertRaises(ConfigureError):
             # Both --set-foo and --set-name=FOO are going to try to
             # set_define('FOO'...)
             get_config(['--set-foo', '--set-name=FOO'])
 
     def test_set_define_when(self):
         with self.moz_configure('''
-            @depends('--help')
-            def always(_):
-                return True
-            @depends('--help')
-            def never(_):
-                return False
             option('--with-qux', help='qux')
-            set_define('FOO', 'foo', when=always)
-            set_define('BAR', 'bar', when=never)
+            set_define('FOO', 'foo', when=True)
+            set_define('BAR', 'bar', when=False)
             set_define('QUX', 'qux', when='--with-qux')
         '''):
             config = self.get_config()
             self.assertEquals(config['DEFINES'], {
                 'FOO': 'foo',
             })
             config = self.get_config(['--with-qux'])
             self.assertEquals(config['DEFINES'], {
@@ -756,28 +744,22 @@ class TestConfigure(unittest.TestCase):
 
         self.assertEquals(
             e.exception.message,
             'Option `--with-foo` already defined'
         )
 
     def test_option_when(self):
         with self.moz_configure('''
-            @depends('--help')
-            def always(_):
-                return True
-            @depends('--help')
-            def never(_):
-                return False
-            option('--with-foo', help='foo', when=always)
-            option('--with-bar', help='bar', when=never)
+            option('--with-foo', help='foo', when=True)
+            option('--with-bar', help='bar', when=False)
             option('--with-qux', env="QUX", help='qux', when='--with-foo')
 
-            set_config('FOO', depends('--with-foo', when=always)(lambda x: x))
-            set_config('BAR', depends('--with-bar', when=never)(lambda x: x))
+            set_config('FOO', depends('--with-foo', when=True)(lambda x: x))
+            set_config('BAR', depends('--with-bar', when=False)(lambda x: x))
             set_config('QUX', depends('--with-qux', when='--with-foo')(lambda x: x))
         '''):
             config = self.get_config()
             self.assertEquals(config, {
                 'FOO': NegativeOptionValue(),
             })
 
             config = self.get_config(['--with-foo'])
@@ -840,35 +822,32 @@ class TestConfigure(unittest.TestCase):
                   --help                    print this message
                   --with-foo                foo
                   --with-qux                qux
 
                 Environment variables:
             '''))
 
         with self.moz_configure('''
-            @depends('--help')
-            def always(_):
-                return True
-            option('--with-foo', help='foo', when=always)
+            option('--with-foo', help='foo', when=True)
             set_config('FOO', depends('--with-foo')(lambda x: x))
         '''):
             with self.assertRaises(ConfigureError) as e:
                 self.get_config()
 
             self.assertEquals(e.exception.message,
                               '@depends function needs the same `when` as '
                               'options it depends on')
 
         with self.moz_configure('''
-            @depends('--help')
-            def always(_):
+            @depends(when=True)
+            def always():
                 return True
-            @depends('--help')
-            def always2(_):
+            @depends(when=True)
+            def always2():
                 return True
             option('--with-foo', help='foo', when=always)
             set_config('FOO', depends('--with-foo', when=always2)(lambda x: x))
         '''):
             with self.assertRaises(ConfigureError) as e:
                 self.get_config()
 
             self.assertEquals(e.exception.message,
@@ -908,27 +887,20 @@ class TestConfigure(unittest.TestCase):
             '''):
                 self.get_config()
 
         self.assertEquals(e.exception.message, "Unexpected type: 'int'")
 
     def test_include_when(self):
         with MockedOpen({
             os.path.join(test_data_path, 'moz.configure'): textwrap.dedent('''
-                @depends('--help')
-                def always(_):
-                    return True
-                @depends('--help')
-                def never(_):
-                    return False
-
                 option('--with-foo', help='foo')
 
-                include('always.configure', when=always)
-                include('never.configure', when=never)
+                include('always.configure', when=True)
+                include('never.configure', when=False)
                 include('foo.configure', when='--with-foo')
 
                 set_config('FOO', foo)
                 set_config('BAR', bar)
                 set_config('QUX', qux)
             '''),
             os.path.join(test_data_path, 'always.configure'): textwrap.dedent('''
                 option('--with-bar', help='bar')
@@ -1090,38 +1062,31 @@ class TestConfigure(unittest.TestCase):
             '''):
                 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(_):
+            @depends(when=True)
+            def foo():
                 return 'foo'
 
             set_config('FOO', foo)
 
-            @depends('--help', when=never)
-            def bar(_):
+            @depends(when=False)
+            def bar():
                 return 'bar'
 
             set_config('BAR', bar)
 
             option('--with-qux', help='qux')
-            @depends('--help', when='--with-qux')
-            def qux(_):
+            @depends(when='--with-qux')
+            def qux():
                 return 'qux'
 
             set_config('QUX', qux)
         '''):
             config = self.get_config()
             self.assertEquals(config, {
                 'FOO': 'foo',
             })