Bug 1296530 - Add a `when` argument to option(). r?chmanchester
--- a/python/mozbuild/mozbuild/configure/__init__.py
+++ b/python/mozbuild/mozbuild/configure/__init__.py
@@ -319,23 +319,24 @@ class ConfigureSandbox(dict):
return super(ConfigureSandbox, self).__setitem__(key, value)
def _resolve(self, arg, need_help_dependency=True):
if isinstance(arg, SandboxDependsFunction):
return self._value_for_depends(self._depends[arg],
need_help_dependency)
return arg
- def _value_for(self, obj):
+ def _value_for(self, obj, need_help_dependency=False):
if isinstance(obj, SandboxDependsFunction):
assert obj in self._depends
- return self._value_for_depends(self._depends[obj])
+ return self._value_for_depends(self._depends[obj],
+ need_help_dependency)
elif isinstance(obj, DependsFunction):
- return self._value_for_depends(obj)
+ return self._value_for_depends(obj, need_help_dependency)
elif isinstance(obj, Option):
return self._value_for_option(obj)
assert False
@memoize
def _value_for_depends(self, obj, need_help_dependency=False):
@@ -402,16 +403,22 @@ class ConfigureSandbox(dict):
reason = reason.split('=', 1)[0]
raise InvalidOptionError(
"'%s' implied by '%s' conflicts with '%s' from the %s"
% (e.arg, reason, e.old_arg, e.old_origin))
if option_string:
self._raw_options[option] = option_string
+ when = self._conditions.get(option)
+ if (when and not self._value_for(when, need_help_dependency=True) and
+ value is not None and value.origin != 'default'):
+ raise InvalidOptionError(
+ '%s is not available in this configuration' % option_string)
+
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:
@@ -434,29 +441,36 @@ class ConfigureSandbox(dict):
'''Implementation of option()
This function creates and returns an Option() object, passing it the
resolved arguments (uses the result of functions when functions are
passed). In most cases, the result of this function is not expected to
be used.
Command line argument/environment variable parsing for this Option is
handled here.
'''
+ when = kwargs.get('when')
+ if when is not None:
+ when = self._dependency(when, 'option', 'when')
args = [self._resolve(arg) for arg in args]
- kwargs = {k: self._resolve(v) for k, v in kwargs.iteritems()}
+ kwargs = {k: self._resolve(v) for k, v in kwargs.iteritems()
+ if k != 'when'}
option = Option(*args, **kwargs)
+ if when:
+ self._conditions[option] = when
if option.name in self._options:
raise ConfigureError('Option `%s` already defined' % option.option)
if option.env in self._options:
raise ConfigureError('Option `%s` already defined' % option.env)
if option.name:
self._options[option.name] = option
if option.env:
self._options[option.env] = option
- if self._help:
+ if self._help and (when is None or
+ self._value_for(when, need_help_dependency=True)):
self._help.add(option)
return option
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
@@ -484,16 +498,26 @@ class ConfigureSandbox(dict):
% 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)
+ conditions = [
+ self._conditions[d]
+ for d in dependencies
+ if d in self._conditions and isinstance(d, Option)
+ ]
+ for c in conditions:
+ if c != when:
+ raise ConfigureError('@depends function needs the same `when` '
+ 'as options it depends on')
+
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
--- a/python/mozbuild/mozbuild/test/configure/test_configure.py
+++ b/python/mozbuild/mozbuild/test/configure/test_configure.py
@@ -696,16 +696,119 @@ class TestConfigure(unittest.TestCase):
'''):
self.get_config()
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-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('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'])
+ self.assertEquals(config, {
+ 'FOO': PositiveOptionValue(),
+ 'QUX': NegativeOptionValue(),
+ })
+
+ config = self.get_config(['--with-foo', '--with-qux'])
+ self.assertEquals(config, {
+ 'FOO': PositiveOptionValue(),
+ 'QUX': PositiveOptionValue(),
+ })
+
+ with self.assertRaises(InvalidOptionError) as e:
+ self.get_config(['--with-bar'])
+
+ self.assertEquals(
+ e.exception.message,
+ '--with-bar is not available in this configuration'
+ )
+
+ with self.assertRaises(InvalidOptionError) as e:
+ self.get_config(['--with-qux'])
+
+ self.assertEquals(
+ e.exception.message,
+ '--with-qux is not available in this configuration'
+ )
+
+ help, config = self.get_config(['--help'])
+ self.assertEquals(help, textwrap.dedent('''\
+ Usage: configure [options]
+
+ Options: [defaults in brackets after descriptions]
+ --help print this message
+ --with-foo foo
+
+ Environment variables:
+ '''))
+
+ help, config = self.get_config(['--help', '--with-foo'])
+ self.assertEquals(help, textwrap.dedent('''\
+ Usage: configure [options]
+
+ Options: [defaults in brackets after descriptions]
+ --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)
+ 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(_):
+ return True
+ @depends('--help')
+ 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,
+ '@depends function needs the same `when` as '
+ 'options it depends on')
+
def test_include_failures(self):
with self.assertRaises(ConfigureError) as e:
with self.moz_configure('include("../foo.configure")'):
self.get_config()
self.assertEquals(
e.exception.message,
'Cannot include `%s` because it is not in a subdirectory of `%s`'