Bug 1257823 - Move set_config() to the global scope
The way set_config is set currently makes it difficult to introspect
moz.configure files to know what configuration items are being set,
because they're hidden in the control flow of functions.
This makes some of the moz.configure more convoluted, but this is why
there are templates, and we can improve the recurring cases afterwards.
Note: this is only the part where a global set_config() is added. this
is not how this would land. This would land folded with the next two
commits. This was split to make the review easier.
In this whole series, we can see some emerging patterns, I'm ignoring
them on purpose here. We can deal with templating them in followups, cf.
paragraph above.
--- a/python/mozbuild/mozbuild/configure/__init__.py
+++ b/python/mozbuild/mozbuild/configure/__init__.py
@@ -217,23 +217,23 @@ class ConfigureSandbox(dict):
if (not isinstance(value, DummyFunction) and
value not in self._templates):
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):
+ def _resolve(self, arg, need_help_dependency=True):
if isinstance(arg, DummyFunction):
assert arg in self._depends
func = self._depends[arg]
assert not inspect.isgeneratorfunction(func)
assert func in self._results
- if not func.with_help:
+ if need_help_dependency and not func.with_help:
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()
@@ -410,29 +410,53 @@ class ConfigureSandbox(dict):
Templates allow to simplify repetitive constructs, or to implement
helper decorators and somesuch.
'''
template, glob = self._prepare_function(func)
glob.update(
advanced=self.advanced_impl,
depends=self.depends_impl,
option=self.option_impl,
+ set_config=self.set_config_impl,
)
self._templates.add(template)
return template
def advanced_impl(self, func):
'''Implementation of @advanced.
This function gives the decorated function access to the complete set
of builtins, allowing the import keyword as an expected side effect.
'''
func, glob = self._prepare_function(func)
glob.update(__builtins__=__builtins__)
return func
+ def set_config_impl(self, name, value):
+ '''Implementation of set_config().
+ Sets a configuration items with the given name to the given value.
+ Both `name` and `value` can be references to @depends functions,
+ in which case the result from these functions is used. If the result
+ of such functions is None, the configuration item is not set.
+ '''
+ # Don't set anything when --help was on the command line
+ if self._help:
+ return
+ name = self._resolve(name, need_help_dependency=False)
+ if name is None:
+ return
+ if not isinstance(name, types.StringTypes):
+ raise TypeError("Unexpected type: '%s'" % type(name))
+ if name in self._config:
+ raise ConfigureError(
+ "Cannot add '%s' to configuration: Key already "
+ "exists" % name)
+ value = self._resolve(value, need_help_dependency=False)
+ if value is not None:
+ self._config[name] = value
+
def _set_define(self, name, value):
defines = self._config.setdefault('DEFINES', {})
if name in defines:
raise ConfigureError("'%s' is already defined" % name)
defines[name] = value
def _prepare_function(self, func):
'''Alter the given function global namespace with the common ground
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozbuild/test/configure/data/set_config.configure
@@ -0,0 +1,43 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+option('--set-foo', help='set foo')
+
+@depends('--set-foo')
+def foo(value):
+ if value:
+ return True
+
+set_config('FOO', foo)
+
+
+option('--set-bar', help='set bar')
+
+@depends('--set-bar')
+def bar(value):
+ return bool(value)
+
+set_config('BAR', bar)
+
+
+option('--set-value', nargs=1, help='set value')
+
+@depends('--set-value')
+def set_value(value):
+ if value:
+ return value[0]
+
+set_config('VALUE', set_value)
+
+
+option('--set-name', nargs=1, help='set name')
+
+@depends('--set-name')
+def set_name(value):
+ if value:
+ return value[0]
+
+set_config(set_name, True)
--- a/python/mozbuild/mozbuild/test/configure/test_configure.py
+++ b/python/mozbuild/mozbuild/test/configure/test_configure.py
@@ -10,36 +10,40 @@ import unittest
from mozunit import main
from mozbuild.configure.options import (
InvalidOptionError,
NegativeOptionValue,
PositiveOptionValue,
)
-from mozbuild.configure import ConfigureSandbox
+from mozbuild.configure import (
+ ConfigureError,
+ ConfigureSandbox,
+)
import mozpack.path as mozpath
test_data_path = mozpath.abspath(mozpath.dirname(__file__))
test_data_path = mozpath.join(test_data_path, 'data')
class TestConfigure(unittest.TestCase):
- def get_result(self, args=[], environ={}, prog='/bin/configure'):
+ def get_result(self, args=[], environ={}, configure='moz.configure',
+ prog='/bin/configure'):
config = {}
out = StringIO()
sandbox = ConfigureSandbox(config, environ, [prog] + args, out, out)
- sandbox.run(mozpath.join(test_data_path, 'moz.configure'))
+ sandbox.run(mozpath.join(test_data_path, configure))
return config, out.getvalue()
- def get_config(self, options=[], env={}):
- config, out = self.get_result(options, environ=env)
+ def get_config(self, options=[], env={}, **kwargs):
+ config, out = self.get_result(options, environ=env, **kwargs)
self.assertEquals('', out)
return config
def test_defaults(self):
config = self.get_config()
self.maxDiff = None
self.assertEquals({
'CHOICES': NegativeOptionValue(),
@@ -297,11 +301,46 @@ class TestConfigure(unittest.TestCase):
self.assertIn('TEMPLATE_VALUE_2', config)
self.assertEquals(config['TEMPLATE_VALUE_2'], 21)
def test_template_advanced(self):
config = self.get_config(['--enable-advanced-template'])
self.assertIn('PLATFORM', config)
self.assertEquals(config['PLATFORM'], sys.platform)
+ def test_set_config(self):
+ config, out = self.get_result(['--help'],
+ configure='set_config.configure')
+ self.assertEquals(config, {})
+
+ config = self.get_config(['--set-foo'],
+ configure='set_config.configure')
+ self.assertIn('FOO', config)
+ self.assertEquals(config['FOO'], True)
+
+ config = self.get_config(['--set-bar'],
+ configure='set_config.configure')
+ self.assertNotIn('FOO', config)
+ self.assertIn('BAR', config)
+ self.assertEquals(config['BAR'], True)
+
+ config = self.get_config(['--set-value=qux'],
+ configure='set_config.configure')
+ self.assertIn('VALUE', config)
+ self.assertEquals(config['VALUE'], 'qux')
+
+ config = self.get_config(['--set-name=hoge'],
+ configure='set_config.configure')
+ self.assertIn('hoge', config)
+ self.assertEquals(config['hoge'], True)
+
+ config = self.get_config([], configure='set_config.configure')
+ self.assertEquals(config, {'BAR': False})
+
+ with self.assertRaises(ConfigureError):
+ # Both --set-foo and --set-name=FOO are going to try to
+ # set_config('FOO'...)
+ self.get_config(['--set-foo', '--set-name=FOO'],
+ configure='set_config.configure')
+
if __name__ == '__main__':
main()