Skip to content

Commit

Permalink
Allow setting meson options from cross/native files
Browse files Browse the repository at this point in the history
This is like the project options, but for meson builtin options.

The only real differences here have to do with the differences between
meson builtin options and project options. Some meson options can be set
on a per-machine basis (build.pkg_config_path vs pkg_config_path) others
can be set on a persubproject basis, but should inherit the parent
setting.
  • Loading branch information
dcbaker committed Feb 7, 2020
1 parent 892fd79 commit f7e39f8
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 10 deletions.
52 changes: 49 additions & 3 deletions docs/markdown/Machine-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ file](Native-environments.md).
The following sections are allowed:
- binaries
- paths
- properties

### Binaries

Expand Down Expand Up @@ -63,13 +64,21 @@ command line will override any options in the native file. For example, passing
`--libdir=otherlibdir` would result in a prefix of `/my prefix` and a libdir of
`otherlibdir`.

### Project specific options
### Meson and Project specific options

*New in 0.54.0*

Being able to set project specific options in a cross or native file can be
done using the `project options` section of the specific file (if doing a
cross build the options from a native file will be ignored).
done using the `project options` section of the specific file. If doing a
cross build most options from a native file will be ignored, only those that
allow a `build.<option>` on the command line are honored, and they only apply
to `native : true` cases.

Path options are not allowed, those must be set in the `[paths]` section.

An incomplete list of options is:
- pkg_config_path
- cmake_prefix_path

For setting options in supbprojects use the `<subproject>:project options`
section instead.
Expand All @@ -82,6 +91,43 @@ build-tests = true
build-tests = false
```

Meson level options can be set the same way:

```ini
[meson options]
c_std = 'c99'
```

You can set some meson level options on a per-subproject basis.

The current list is:
- default_library

```ini
[zlib:meson options]
default_library = 'static'
```

Options set on a per-subproject basis will inherit the
option from the parent if the parent has a setting but the subproject
doesn't, even when here is a default set meson language.


```ini
[meson options]
default_library = 'static'
```

will make subprojects use default_library as static.


## Properties

*New for native files in 0.54.0*

The properties section can contain any variable you like, and is accessed via
`meson.get_external_property`, or `meson.get_cross_property`.

## Loading multiple machine files

Native files allow layering (cross files can be layered since meson 0.52.0).
Expand Down
16 changes: 15 additions & 1 deletion docs/markdown/snippets/project_options_in_machine_files.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## Project options can be set in native or cross files
## Project and Meson 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`
Expand Down Expand Up @@ -35,3 +35,17 @@ for subprojects this looks like:
[zlib:project options]
foo = 'some val'
```

Additionally meson level options can be set in the same way, using the
`[meson options]` section.

```ini
[meson options]
c_std = 'c99'
```

Subprojects works too, although only `default_library` can currently be set:
```ini
[zlib:meson options]
default_library = 'static'
```
8 changes: 7 additions & 1 deletion mesonbuild/coredata.py
Original file line number Diff line number Diff line change
Expand Up @@ -737,14 +737,20 @@ def set_default_options(self, default_options, subproject, env):
# Subprojects can only define default for user options and not yielding
# builtin option.
from . import optinterpreter
for k, v in default_options.items():
for k, v in chain(default_options.items(), env.meson_options.host.get(subproject, {}).items()):
if subproject:
if (k not in builtin_options or builtin_options[k].yielding) \
and optinterpreter.is_invalid_name(k, log=False):
continue
k = subproject + ':' + k
env.cmd_line_options.setdefault(k, v)

# For cross builds we need to get the build specifc options
if env.meson_options.host != env.meson_options.build:
for k in builtin_options_per_machine.keys():
if k in env.meson_options.build:
env.cmd_line_options.setdefault('build.{}'.format(k), env.meson_options.build[k])

# Create a subset of cmd_line_options, keeping only options for this
# subproject. Also take builtin options if it's the main project.
# Language and backend specific options will be set later when adding
Expand Down
27 changes: 22 additions & 5 deletions mesonbuild/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,9 @@ def __init__(self, source_dir, build_dir, options):
# We only need one of these as project options are not per machine
user_options = {}

# meson builtin options, as passed through cross or native files
meson_options = PerMachineDefaultable()

## Setup build machine defaults

# Will be fully initialized later using compilers later.
Expand All @@ -536,14 +539,14 @@ def __init__(self, source_dir, build_dir, options):

## Read in native file(s) to override build machine configuration

def load_user_options():
def load_options(tag: str, store: T.Dict[str, T.Any]) -> None:
for section in config.keys():
if section.endswith('project options'):
if section.endswith(tag):
if ':' in section:
project = section.split(':')[0]
else:
project = ''
user_options[project] = config.get(section, {})
store[project] = config.get(section, {})

if self.coredata.config_files is not None:
config = MesonConfigFile.from_config_parser(
Expand All @@ -555,7 +558,9 @@ def load_user_options():
# Don't run this if there are cross file, 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()
load_options('project options', user_options)
meson_options.build = {}
load_options('meson options', meson_options.build)

## Read in cross file(s) to override host machine configuration

Expand All @@ -569,7 +574,9 @@ def load_user_options():
if 'target_machine' in config:
machines.target = MachineInfo.from_literal(config['target_machine'])
paths.host = Directories(**config.get('paths', {}))
load_user_options()
load_options('project options', user_options)
meson_options.host = {}
load_options('meson options', meson_options.host)

## "freeze" now initialized configuration, and "save" to the class.

Expand All @@ -578,6 +585,16 @@ def load_user_options():
self.properties = properties.default_missing()
self.paths = paths.default_missing()
self.user_options = user_options
self.meson_options = meson_options.default_missing()

# Ensure that no paths are passed via meson options:
if '' in self.meson_options.host:
for each in ['bindir', 'datadir', 'includedir', 'infodir', 'libdir',
'libexecdir', 'localedir', 'localstatedir', 'mandir',
'prefix', 'sbindir', 'sharedstatedir', 'sysconfdir']:
# These are not per-subdirectory and probably never will be
if each in self.meson_options.host['']:
raise EnvironmentException('Invalid entry {} in [meson options] section')

exe_wrapper = self.binaries.host.lookup_entry('exe_wrapper')
if exe_wrapper is not None:
Expand Down
9 changes: 9 additions & 0 deletions mesonbuild/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2613,6 +2613,15 @@ def do_subproject(self, dirname: str, method: str, kwargs):

default_options = mesonlib.stringlistify(kwargs.get('default_options', []))
default_options = coredata.create_options_dict(default_options)

# We want to overwrite the keyword arguments with options from the
# cross/native file. Take the parent options first, then overrise that
# with the subproject specific options
for n in ['', dirname]:
default_options.update(self.environment.meson_options.host.get(n, {}))
default_options.update({'build.{}'.format(k): v for k, v in self.environment.meson_options.build.get(n, {}).items()
if k in coredata.builtin_options_per_machine})

if dirname == '':
raise InterpreterException('Subproject dir name must not be empty.')
if dirname[0] == '.':
Expand Down
56 changes: 56 additions & 0 deletions run_unittests.py
Original file line number Diff line number Diff line change
Expand Up @@ -7002,6 +7002,51 @@ def test_user_options_subproject(self):
self.init(testcase, extra_args=['--native-file', config])
self.assertRegex(cm.exception.stdout, r'Incorrect value to [a-z]+ option')

def test_meson_options(self):
testcase = os.path.join(self.common_test_dir, '1 trivial')
config = self.helper_create_native_file({'meson options': {'c_std': 'c11'}})

self.init(testcase, extra_args=['--native-file', config])
configuration = self.introspect('--buildoptions')
for each in configuration:
if each['name'] == 'c_std':
self.assertEqual(each['value'], 'c11')
break

def test_meson_options_subprojects(self):
testcase = os.path.join(self.common_test_dir, '102 subproject subdir')
config = self.helper_create_native_file({'meson options': {'default_library': 'both', 'c_std': 'c11'}, 'sub:meson options': {'default_library': 'static'}})

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 'c_std' in each['name']:
self.assertEqual(each['value'], 'c11')
if each['name'] == 'default_library':
self.assertEqual(each['value'], 'both')
if each['name'] == 'sub:default_library':
self.assertEqual(each['value'], 'static')

def test_meson_options_subprojects_overrides_buildfiles(self):
# If the buildfile says subproject(... default_library: shared), ensure that's overwritten
testcase = os.path.join(self.common_test_dir, '229 persubproject options')
config = self.helper_create_native_file({'sub2:meson options': {'default_library': 'shared'}})

with self.assertRaises(subprocess.CalledProcessError) as cm:
self.init(testcase, extra_args=['--native-file', config])
self.assertIn(cm.exception.stdout, 'Parent should override default_library')

def test_meson_options_subprojects_inherits_parent_override(self):
# If the buildfile says subproject(... default_library: shared), ensure that's overwritten
testcase = os.path.join(self.common_test_dir, '229 persubproject options')
config = self.helper_create_native_file({'meson options': {'default_library': 'both'}})

with self.assertRaises(subprocess.CalledProcessError) as cm:
self.init(testcase, extra_args=['--native-file', config])
self.assertIn(cm.exception.stdout, 'Parent should override default_library')



class CrossFileTests(BasePlatformTests):

Expand Down Expand Up @@ -7096,6 +7141,17 @@ def test_user_options(self):
self.init(testcase, extra_args=['--native-file', config])
self.assertRegex(cm.exception.stdout, r'Incorrect value to [a-z]+ option')

def test_meson_options(self):
testcase = os.path.join(self.common_test_dir, '1 trivial')
config = self.helper_create_cross_file({'meson options': {'c_std': 'c11'}})

self.init(testcase, extra_args=['--native-file', config])
configuration = self.introspect('--buildoptions')
for each in configuration:
if each['name'] == 'c_std':
self.assertEqual(each['value'], 'c11')
break


class TAPParserTests(unittest.TestCase):
def assert_test(self, events, **kwargs):
Expand Down

0 comments on commit f7e39f8

Please sign in to comment.