aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDylan Baker <dylan@pnwbakers.com>2020-02-06 09:10:01 -0800
committerDylan Baker <dylan@pnwbakers.com>2020-07-30 19:34:37 -0700
commita6164ca5a81224b7ed672401a47260f498f06e44 (patch)
treec79d79a3e701c8ce995bfbd104468e71dd45ce4a
parentcc201a539674babf46f726859587afb5ed6a6867 (diff)
downloadmeson-a6164ca5a81224b7ed672401a47260f498f06e44.zip
meson-a6164ca5a81224b7ed672401a47260f498f06e44.tar.gz
meson-a6164ca5a81224b7ed672401a47260f498f06e44.tar.bz2
Allow setting project options from cross or native files
This allows adding a `[project options]` section to a cross or native file that contains the options defined for a project in it's meson_option.txt file.
-rw-r--r--docs/markdown/Machine-files.md20
-rw-r--r--docs/markdown/snippets/project_options_in_machine_files.md37
-rw-r--r--mesonbuild/coredata.py10
-rw-r--r--mesonbuild/environment.py19
-rwxr-xr-xrun_unittests.py83
-rw-r--r--test cases/unit/75 user options for subproject/.gitignore1
-rw-r--r--test cases/unit/75 user options for subproject/meson.build3
7 files changed, 171 insertions, 2 deletions
diff --git a/docs/markdown/Machine-files.md b/docs/markdown/Machine-files.md
index 9011f79..26af44a 100644
--- a/docs/markdown/Machine-files.md
+++ b/docs/markdown/Machine-files.md
@@ -12,6 +12,7 @@ The following sections are allowed:
- binaries
- paths
- properties
+- project options
### constants
@@ -166,6 +167,25 @@ section may contain random key value pairs accessed using the
The properties section can contain any variable you like, and is accessed via
`meson.get_external_property`, or `meson.get_cross_property`.
+### Project specific options
+
+*New in 0.54.0*
+
+Being able to set project specific options in a native or cross files can be
+done using the `[project options]` section of the specific file (if doing a
+cross build the options from the native file will be ignored)
+
+For setting options in supbprojects use the `<subproject>:project options`
+section instead.
+
+```ini
+[project options]
+build-tests = true
+
+[zlib:project options]
+build-tests = false
+```
+
## Loading multiple machine files
Native files allow layering (cross files can be layered since meson 0.52.0).
diff --git a/docs/markdown/snippets/project_options_in_machine_files.md b/docs/markdown/snippets/project_options_in_machine_files.md
new file mode 100644
index 0000000..78b129a
--- /dev/null
+++ b/docs/markdown/snippets/project_options_in_machine_files.md
@@ -0,0 +1,37 @@
+## Project options can be set in native or cross files
+
+A new set of sections has been added to the cross and native files, `[project
+options]` and `[<subproject_name>:project options]`, where `subproject_name`
+is the name of a subproject. Any options that are allowed in the project can
+be set from this section. They have the lowest precedent, and will be
+overwritten by command line arguments.
+
+
+```meson
+option('foo', type : 'string', value : 'foo')
+```
+
+```ini
+[project options]
+foo = 'other val'
+```
+
+```console
+meson build --native-file my.ini
+```
+
+Will result in the option foo having the value `other val`,
+
+```console
+meson build --native-file my.ini -Dfoo='different val'
+```
+
+Will result in the option foo having the value `different val`,
+
+
+Subproject options are assigned like this:
+
+```ini
+[zlib:project options]
+foo = 'some val'
+```
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index e2a6954..49104a7 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -377,6 +377,7 @@ class CoreData:
host_cache = DependencyCache(self.builtins_per_machine, MachineChoice.BUILD)
self.deps = PerMachine(build_cache, host_cache) # type: PerMachine[DependencyCache]
self.compiler_check_cache = OrderedDict()
+
# Only to print a warning if it changes between Meson invocations.
self.config_files = self.__load_config_files(options, scratch_dir, 'native')
self.builtin_options_libdir_cross_fixup()
@@ -734,7 +735,7 @@ class CoreData:
if not self.is_cross_build():
self.copy_build_options_from_regular_ones()
- def set_default_options(self, default_options, subproject, env):
+ def set_default_options(self, default_options: T.Mapping[str, str], subproject: str, env: 'Environment') -> None:
# Warn if the user is using two different ways of setting build-type
# options that override each other
if 'buildtype' in env.cmd_line_options and \
@@ -755,6 +756,13 @@ class CoreData:
k = subproject + ':' + k
cmd_line_options[k] = v
+ # load the values for user options out of the appropriate machine file,
+ # then overload the command line
+ for k, v in env.user_options.get(subproject, {}).items():
+ if subproject:
+ k = '{}:{}'.format(subproject, k)
+ cmd_line_options[k] = v
+
# Override project default_options using conf files (cross or native)
for k, v in env.paths.host:
if v is not None:
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index a82c8f8..c872aee 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -553,6 +553,9 @@ class Environment:
# architecture, just the build and host architectures
paths = PerMachineDefaultable()
+ # We only need one of these as project options are not per machine
+ user_options = {}
+
## Setup build machine defaults
# Will be fully initialized later using compilers later.
@@ -565,12 +568,26 @@ class Environment:
## Read in native file(s) to override build machine configuration
+ def load_user_options():
+ for section in config.keys():
+ if section.endswith('project options'):
+ if ':' in section:
+ project = section.split(':')[0]
+ else:
+ project = ''
+ user_options[project] = config.get(section, {})
+
if self.coredata.config_files is not None:
config = coredata.parse_machine_files(self.coredata.config_files)
binaries.build = BinaryTable(config.get('binaries', {}))
paths.build = Directories(**config.get('paths', {}))
properties.build = Properties(config.get('properties', {}))
+ # Don't run this if there are any cross files, we don't want to use
+ # the native values if we're doing a cross build
+ if not self.coredata.cross_files:
+ load_user_options()
+
## Read in cross file(s) to override host machine configuration
if self.coredata.cross_files:
@@ -582,6 +599,7 @@ class Environment:
if 'target_machine' in config:
machines.target = MachineInfo.from_literal(config['target_machine'])
paths.host = Directories(**config.get('paths', {}))
+ load_user_options()
## "freeze" now initialized configuration, and "save" to the class.
@@ -589,6 +607,7 @@ class Environment:
self.binaries = binaries.default_missing()
self.properties = properties.default_missing()
self.paths = paths.default_missing()
+ self.user_options = user_options
exe_wrapper = self.lookup_binary_entry(MachineChoice.HOST, 'exe_wrapper')
if exe_wrapper is not None:
diff --git a/run_unittests.py b/run_unittests.py
index 2b0e4e1..9d96ce0 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -7672,7 +7672,10 @@ class NativeFileTests(BasePlatformTests):
for section, entries in values.items():
f.write('[{}]\n'.format(section))
for k, v in entries.items():
- f.write("{}='{}'\n".format(k, v))
+ if isinstance(v, bool):
+ f.write("{}={}\n".format(k, v))
+ else:
+ f.write("{}='{}'\n".format(k, v))
return filename
def helper_create_binary_wrapper(self, binary, dir_=None, extra_args=None, **kwargs):
@@ -7996,6 +7999,54 @@ class NativeFileTests(BasePlatformTests):
self.init(testcase, extra_args=['--native-file', config])
self.build()
+ def test_user_options(self):
+ testcase = os.path.join(self.common_test_dir, '43 options')
+ for opt, value in [('testoption', 'some other val'), ('other_one', True),
+ ('combo_opt', 'one'), ('array_opt', ['two']),
+ ('integer_opt', 0)]:
+ config = self.helper_create_native_file({'project options': {opt: value}})
+ with self.assertRaises(subprocess.CalledProcessError) as cm:
+ self.init(testcase, extra_args=['--native-file', config])
+ self.assertRegex(cm.exception.stdout, r'Incorrect value to [a-z]+ option')
+
+ def test_user_options_command_line_overrides(self):
+ testcase = os.path.join(self.common_test_dir, '43 options')
+ config = self.helper_create_native_file({'project options': {'other_one': True}})
+ self.init(testcase, extra_args=['--native-file', config, '-Dother_one=false'])
+
+ def test_user_options_subproject(self):
+ testcase = os.path.join(self.unit_test_dir, '75 user options for subproject')
+
+ s = os.path.join(testcase, 'subprojects')
+ if not os.path.exists(s):
+ os.mkdir(s)
+ s = os.path.join(s, 'sub')
+ if not os.path.exists(s):
+ sub = os.path.join(self.common_test_dir, '43 options')
+ shutil.copytree(sub, s)
+
+ for opt, value in [('testoption', 'some other val'), ('other_one', True),
+ ('combo_opt', 'one'), ('array_opt', ['two']),
+ ('integer_opt', 0)]:
+ config = self.helper_create_native_file({'project options': {'sub:{}'.format(opt): value}})
+ with self.assertRaises(subprocess.CalledProcessError) as cm:
+ self.init(testcase, extra_args=['--native-file', config])
+ self.assertRegex(cm.exception.stdout, r'Incorrect value to [a-z]+ option')
+
+ def test_option_bool(self):
+ # Bools are allowed to be unquoted
+ testcase = os.path.join(self.common_test_dir, '1 trivial')
+ config = self.helper_create_native_file({'built-in options': {'werror': True}})
+ self.init(testcase, extra_args=['--native-file', config])
+ configuration = self.introspect('--buildoptions')
+ for each in configuration:
+ # Test that no-per subproject options are inherited from the parent
+ if 'werror' in each['name']:
+ self.assertEqual(each['value'], True)
+ break
+ else:
+ self.fail('Did not find werror in build options?')
+
class CrossFileTests(BasePlatformTests):
@@ -8005,6 +8056,11 @@ class CrossFileTests(BasePlatformTests):
This is mainly aimed to testing overrides from cross files.
"""
+ def setUp(self):
+ super().setUp()
+ self.current_config = 0
+ self.current_wrapper = 0
+
def _cross_file_generator(self, *, needs_exe_wrapper: bool = False,
exe_wrapper: T.Optional[T.List[str]] = None) -> str:
if is_windows():
@@ -8133,6 +8189,21 @@ class CrossFileTests(BasePlatformTests):
self.init(testdir, extra_args=['--cross-file=' + name], inprocess=True)
self.wipe()
+ def helper_create_cross_file(self, values):
+ """Create a config file as a temporary file.
+
+ values should be a nested dictionary structure of {section: {key:
+ value}}
+ """
+ filename = os.path.join(self.builddir, 'generated{}.config'.format(self.current_config))
+ self.current_config += 1
+ with open(filename, 'wt') as f:
+ for section, entries in values.items():
+ f.write('[{}]\n'.format(section))
+ for k, v in entries.items():
+ f.write("{}='{}'\n".format(k, v))
+ return filename
+
def test_cross_file_dirs(self):
testcase = os.path.join(self.unit_test_dir, '60 native file override')
self.init(testcase, default_args=False,
@@ -8189,6 +8260,16 @@ class CrossFileTests(BasePlatformTests):
'-Ddef_sharedstatedir=sharedstatebar',
'-Ddef_sysconfdir=sysconfbar'])
+ def test_user_options(self):
+ # This is just a touch test for cross file, since the implementation
+ # shares code after loading from the files
+ testcase = os.path.join(self.common_test_dir, '43 options')
+ config = self.helper_create_cross_file({'project options': {'testoption': 'some other value'}})
+ with self.assertRaises(subprocess.CalledProcessError) as cm:
+ self.init(testcase, extra_args=['--native-file', config])
+ self.assertRegex(cm.exception.stdout, r'Incorrect value to [a-z]+ option')
+
+
class TAPParserTests(unittest.TestCase):
def assert_test(self, events, **kwargs):
if 'explanation' not in kwargs:
diff --git a/test cases/unit/75 user options for subproject/.gitignore b/test cases/unit/75 user options for subproject/.gitignore
new file mode 100644
index 0000000..4976afc
--- /dev/null
+++ b/test cases/unit/75 user options for subproject/.gitignore
@@ -0,0 +1 @@
+subprojects/*
diff --git a/test cases/unit/75 user options for subproject/meson.build b/test cases/unit/75 user options for subproject/meson.build
new file mode 100644
index 0000000..0bc395b
--- /dev/null
+++ b/test cases/unit/75 user options for subproject/meson.build
@@ -0,0 +1,3 @@
+project('user option for subproject')
+
+p = subproject('sub')