Merge pull request #6597 from dcbaker/full-project-config

Set project and meson options in cross/native files
pull/7524/head
Dylan Baker 4 years ago committed by GitHub
commit 3040f3c8dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      cross/armcc.txt
  2. 5
      cross/armclang-linux.txt
  3. 2
      cross/armclang.txt
  4. 6
      cross/c2000.txt
  5. 2
      cross/ccrx.txt
  6. 6
      cross/iphone.txt
  7. 7
      cross/tvos.txt
  8. 6
      cross/ubuntu-armhf.txt
  9. 3
      cross/wasm.txt
  10. 2
      cross/xc16.txt
  11. 121
      docs/markdown/Machine-files.md
  12. 52
      docs/markdown/snippets/project_options_in_machine_files.md
  13. 2
      mesonbuild/ast/introspection.py
  14. 196
      mesonbuild/coredata.py
  15. 40
      mesonbuild/envconfig.py
  16. 161
      mesonbuild/environment.py
  17. 9
      mesonbuild/interpreter.py
  18. 14
      mesonbuild/mconf.py
  19. 12
      mesonbuild/mesonlib.py
  20. 14
      mesonbuild/mintro.py
  21. 335
      run_unittests.py
  22. 0
      test cases/cmake/20 cmake file/foolib.cmake.in
  23. 0
      test cases/cmake/20 cmake file/meson.build
  24. 0
      test cases/cmake/20 cmake file/test.json
  25. 1
      test cases/failing/107 number in combo/meson.build
  26. 2
      test cases/failing/107 number in combo/nativefile.ini
  27. 5
      test cases/failing/107 number in combo/test.json
  28. 1
      test cases/failing/108 bool in combo/meson.build
  29. 5
      test cases/failing/108 bool in combo/meson_options.txt
  30. 2
      test cases/failing/108 bool in combo/nativefile.ini
  31. 5
      test cases/failing/108 bool in combo/test.json
  32. 0
      test cases/unit/77 pkgconfig prefixes/client/client.c
  33. 0
      test cases/unit/77 pkgconfig prefixes/client/meson.build
  34. 0
      test cases/unit/77 pkgconfig prefixes/val1/meson.build
  35. 0
      test cases/unit/77 pkgconfig prefixes/val1/val1.c
  36. 0
      test cases/unit/77 pkgconfig prefixes/val1/val1.h
  37. 0
      test cases/unit/77 pkgconfig prefixes/val2/meson.build
  38. 0
      test cases/unit/77 pkgconfig prefixes/val2/val2.c
  39. 0
      test cases/unit/77 pkgconfig prefixes/val2/val2.h
  40. 0
      test cases/unit/78 subdir libdir/meson.build
  41. 0
      test cases/unit/78 subdir libdir/subprojects/flub/meson.build
  42. 1
      test cases/unit/79 user options for subproject/75 user options for subproject/.gitignore
  43. 3
      test cases/unit/79 user options for subproject/75 user options for subproject/meson.build
  44. 0
      test cases/unit/80 global-rpath/meson.build
  45. 0
      test cases/unit/80 global-rpath/rpathified.cpp
  46. 0
      test cases/unit/80 global-rpath/yonder/meson.build
  47. 0
      test cases/unit/80 global-rpath/yonder/yonder.cpp
  48. 0
      test cases/unit/80 global-rpath/yonder/yonder.h
  49. 0
      test cases/unit/81 wrap-git/meson.build
  50. 0
      test cases/unit/81 wrap-git/subprojects/packagefiles/wrap_git_builddef/meson.build
  51. 0
      test cases/unit/81 wrap-git/subprojects/wrap_git_upstream/main.c

@ -7,7 +7,7 @@ cpp = 'armcc'
ar = 'armar'
strip = 'armar'
[properties]
[built-in options]
# The '--cpu' option with the appropriate target type should be mentioned
# to cross compile c/c++ code with armcc,.
c_args = ['--cpu=Cortex-M0plus']

@ -12,7 +12,7 @@
# Armcc is only available in toolchain version 5.
# Armclang is only available in toolchain version 6.
# Start shell with /opt/arm/developmentstudio-2019.0/bin/suite_exec zsh
# Now the compilers will work.
# Now the compilers will work.
[binaries]
# we could set exe_wrapper = qemu-arm-static but to test the case
@ -24,8 +24,7 @@ ar = '/opt/arm/developmentstudio-2019.0/sw/ARMCompiler6.12/bin/armar'
#strip = '/usr/arm-linux-gnueabihf/bin/strip'
#pkgconfig = '/usr/bin/arm-linux-gnueabihf-pkg-config'
[properties]
[built-in options]
c_args = ['--target=aarch64-arm-none-eabi']
[host_machine]

@ -7,7 +7,7 @@ cpp = 'armclang'
ar = 'armar'
strip = 'armar'
[properties]
[built-in options]
# The '--target', '-mcpu' options with the appropriate values should be mentioned
# to cross compile c/c++ code with armclang.
c_args = ['--target=arm-arm-none-eabi', '-mcpu=cortex-m0plus']

@ -12,8 +12,7 @@ cpu_family = 'c2000'
cpu = 'c28x'
endian = 'little'
[properties]
needs_exe_wrapper = true
[built-in options]
c_args = [
'-v28',
'-ml',
@ -24,3 +23,6 @@ c_link_args = [
'\f28004x_flash.cmd']
cpp_args = []
cpp_link_args = []
[properties]
needs_exe_wrapper = true

@ -7,7 +7,7 @@ cpp = 'ccrx'
ar = 'rlink'
strip = 'rlink'
[properties]
[built-in options]
# The '--cpu' option with the appropriate target type should be mentioned
# to cross compile c/c++ code with ccrx,.
c_args = ['-cpu=rx600']

@ -8,14 +8,14 @@ cpp = 'clang++'
ar = 'ar'
strip = 'strip'
[properties]
root = '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer'
[built-in options]
c_args = ['-arch', 'armv7', '-miphoneos-version-min=8.0', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.4.sdk']
cpp_args = ['-arch', 'armv7', '-miphoneos-version-min=8.0', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.4.sdk']
c_link_args = ['-arch', 'armv7', '-miphoneos-version-min=8.0', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.4.sdk']
cpp_link_args = ['-arch', 'armv7', '-miphoneos-version-min=8.0', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.4.sdk']
[properties]
root = '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer'
has_function_printf = true
has_function_hfkerhisadf = false

@ -8,14 +8,15 @@ cpp = 'clang++'
ar = 'ar'
strip = 'strip'
[properties]
root = '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVOS.platform/Developer'
[built-in options]
c_args = ['-arch', 'arm64', '-mtvos-version-min=12.0', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk']
cpp_args = ['-arch', 'arm64', '-mtvos-version-min=12.0', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk']
c_link_args = ['-arch', 'arm64', '-mtvos-version-min=12.0', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk']
cpp_link_args = ['-arch', 'arm64', '-mtvos-version-min=12.0', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk']
[properties]
root = '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVOS.platform/Developer'
has_function_printf = true
has_function_hfkerhisadf = false

@ -9,12 +9,14 @@ strip = '/usr/arm-linux-gnueabihf/bin/strip'
pkgconfig = '/usr/bin/arm-linux-gnueabihf-pkg-config'
ld = '/usr/bin/arm-linux/gnueabihf-ld'
[properties]
root = '/usr/arm-linux-gnueabihf'
[built-in options]
# Used in unit test '140 get define'
c_args = ['-DMESON_TEST_ISSUE_1665=1']
cpp_args = '-DMESON_TEST_ISSUE_1665=1'
[properties]
root = '/usr/arm-linux-gnueabihf'
has_function_printf = true
has_function_hfkerhisadf = false

@ -3,8 +3,7 @@ c = '/home/jpakkane/emsdk/fastcomp/emscripten/emcc'
cpp = '/home/jpakkane/emsdk/fastcomp/emscripten/em++'
ar = '/home/jpakkane/emsdk/fastcomp/emscripten/emar'
[properties]
[built-in options]
c_args = ['-s', 'WASM=1', '-s', 'EXPORT_ALL=1']
c_link_args = ['-s','EXPORT_ALL=1']
cpp_args = ['-s', 'WASM=1', '-s', 'EXPORT_ALL=1']

@ -14,6 +14,8 @@ endian = 'little'
[properties]
needs_exe_wrapper = true
[built-in options]
c_args = [
'-c',
'-mcpu=33EP64MC203',

@ -5,6 +5,37 @@ documentation on the common values used by both, for the specific values of
one or the other see the [cross compilation](Cross-compilation.md) and [native
environments](Native-environments.md).
## Data Types
There are four basic data types in a machine file:
- strings
- arrays
- booleans
- integers
A string is specified single quoted:
```ini
[section]
option1 = 'false'
option2 = '2'
```
An array is enclosed in square brackets, and must consist of strings or booleans
```ini
[section]
option = ['value']
```
A boolean must be either `true` or `false`, and unquoted.
```ini
option = false
```
An integer must be either an unquoted numeric constant;
```ini
option = 42
```
## Sections
The following sections are allowed:
@ -12,10 +43,12 @@ The following sections are allowed:
- binaries
- paths
- properties
- project options
- built-in options
### constants
*Since 0.55.0*
*Since 0.56.0*
String and list concatenation is supported using the `+` operator, joining paths
is supported using the `/` operator.
@ -88,14 +121,16 @@ a = 'Hello'
### Binaries
The binaries section contains a list of binaries. These can be used
internally by meson, or by the `find_program` function:
internally by meson, or by the `find_program` function.
These values must be either strings or an array of strings
Compilers and linkers are defined here using `<lang>` and `<lang>_ld`.
`<lang>_ld` is special because it is compiler specific. For compilers like
gcc and clang which are used to invoke the linker this is a value to pass to
their "choose the linker" argument (-fuse-ld= in this case). For compilers
like MSVC and Clang-Cl, this is the path to a linker for meson to invoke,
such as `link.exe` or `lld-link.exe`. Support for ls is *new in 0.53.0*
such as `link.exe` or `lld-link.exe`. Support for `ld` is *new in 0.53.0*
*changed in 0.53.1* the `ld` variable was replaced by `<lang>_ld`, because it
*regressed a large number of projects. in 0.53.0 the `ld` variable was used
@ -113,8 +148,8 @@ llvm-config = '/usr/lib/llvm8/bin/llvm-config'
Cross example:
```ini
c = '/usr/bin/i586-mingw32msvc-gcc'
cpp = '/usr/bin/i586-mingw32msvc-g++'
c = ['ccache', '/usr/bin/i586-mingw32msvc-gcc']
cpp = ['ccache', '/usr/bin/i586-mingw32msvc-g++']
c_ld = 'gold'
cpp_ld = 'gold'
ar = '/usr/i586-mingw32msvc/bin/ar'
@ -137,8 +172,10 @@ An incomplete list of internally used programs that can be overridden here is:
### Paths and Directories
*Deprecated in 0.56.0* use the built-in section instead.
As of 0.50.0 paths and directories such as libdir can be defined in the native
file in a paths section
and cross files in a paths section. These should be strings.
```ini
[paths]
@ -157,21 +194,79 @@ command line will override any options in the native file. For example, passing
In addition to special data that may be specified in cross files, this
section may contain random key value pairs accessed using the
`meson.get_external_property()`
`meson.get_external_property()`, or `meson.get_cross_property()`.
*Changed in 0.56.0* putting `<lang>_args` and `<lang>_link_args` in the
properties section has been deprecated, and should be put in the built-in
options section.
### Project specific options
*New in 0.56.0*
Path options are not allowed, those must be set in the `[paths]` section.
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 the native file will be ignored)
For setting options in subprojects use the `[<subproject>:project options]`
section instead.
```ini
[project options]
build-tests = true
[zlib:project options]
build-tests = false
```
### Meson built-in options
Meson built-in options can be set the same way:
```ini
[built-in options]
c_std = 'c99'
```
You can set some meson built-in options on a per-subproject basis, such as
`default_library` and `werror`. The order of precedence is:
1) Command line
2) Machine file
3) Build system definitions
```ini
[zlib:built-in options]
default_library = 'static'
werror = false
```
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 there is a default set meson language.
```ini
[built-in options]
default_library = 'static'
```
## Properties
will make subprojects use default_library as static.
*New for native files in 0.54.0*
Some options can be set on a per-machine basis (in other words, the value of
the build machine can be different than the host machine in a cross compile).
In these cases the values from both a cross file and a native file are used.
The properties section can contain any variable you like, and is accessed via
`meson.get_external_property`, or `meson.get_cross_property`.
An incomplete list of options is:
- pkg_config_path
- cmake_prefix_path
## Loading multiple machine files
Native files allow layering (cross files can be layered since meson 0.52.0).
More than one native file can be loaded, with values from a previous file being
More than one file can be loaded, with values from a previous file being
overridden by the next. The intention of this is not overriding, but to allow
composing native files. This composition is done by passing the command line
composing files. This composition is done by passing the command line
argument multiple times:
```console

@ -0,0 +1,52 @@
## Project and built-in 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'
```
Additionally meson level options can be set in the same way, using the
`[built-in options]` section.
```ini
[built-in options]
c_std = 'c99'
```
These options can also be set on a per-subproject basis, although only
`default_library` and `werror` can currently be set:
```ini
[zlib:built-in options]
default_library = 'static'
```

@ -120,7 +120,7 @@ class IntrospectionInterpreter(AstInterpreter):
self.do_subproject(i)
self.coredata.init_backend_options(self.backend)
options = {k: v for k, v in self.environment.cmd_line_options.items() if k.startswith('backend_')}
options = {k: v for k, v in self.environment.meson_options.host[''].items() if k.startswith('backend_')}
self.coredata.set_options(options)
self.func_add_languages(None, proj_langs, None)

@ -20,10 +20,8 @@ from pathlib import PurePath
from collections import OrderedDict, defaultdict
from .mesonlib import (
MesonException, EnvironmentException, MachineChoice, PerMachine,
OrderedSet, default_libdir, default_libexecdir, default_prefix,
split_args
default_libdir, default_libexecdir, default_prefix, split_args
)
from .envconfig import get_env_var_pair
from .wrap import WrapMode
import ast
import argparse
@ -161,10 +159,16 @@ class UserComboOption(UserOption[str]):
def validate_value(self, value):
if value not in self.choices:
if isinstance(value, bool):
_type = 'boolean'
elif isinstance(value, (int, float)):
_type = 'number'
else:
_type = 'string'
optionsstring = ', '.join(['"%s"' % (item,) for item in self.choices])
raise MesonException('Value "{}" for combo option "{}" is not one of the choices.'
' Possible choices are: {}.'.format(
value, self.description, optionsstring))
raise MesonException('Value "{}" (of type "{}") for combo option "{}" is not one of the choices.'
' Possible choices are (as string): {}.'.format(
value, _type, self.description, optionsstring))
return value
class UserArrayOption(UserOption[T.List[str]]):
@ -361,15 +365,15 @@ class CoreData:
self.install_guid = str(uuid.uuid4()).upper()
self.target_guids = {}
self.version = version
self.builtins = {} # : OptionDictType
self.builtins = {} # type: OptionDictType
self.builtins_per_machine = PerMachine({}, {})
self.backend_options = {} # : OptionDictType
self.user_options = {} # : OptionDictType
self.backend_options = {} # type: OptionDictType
self.user_options = {} # type: OptionDictType
self.compiler_options = PerMachine(
defaultdict(dict),
defaultdict(dict),
) # : PerMachine[T.defaultdict[str, OptionDictType]]
self.base_options = {} # : OptionDictType
) # type: PerMachine[T.defaultdict[str, OptionDictType]]
self.base_options = {} # type: OptionDictType
self.cross_files = self.__load_config_files(options, scratch_dir, 'cross')
self.compilers = PerMachine(OrderedDict(), OrderedDict())
@ -377,6 +381,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,87 +739,54 @@ class CoreData:
if not self.is_cross_build():
self.copy_build_options_from_regular_ones()
def set_default_options(self, default_options, subproject, env):
# 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 \
('optimization' in env.cmd_line_options or 'debug' in env.cmd_line_options):
mlog.warning('Recommend using either -Dbuildtype or -Doptimization + -Ddebug. '
'Using both is redundant since they override each other. '
'See: https://mesonbuild.com/Builtin-options.html#build-type-options')
cmd_line_options = OrderedDict()
# Set project default_options as if they were passed to the cmdline.
# Subprojects can only define default for user options and not yielding
# builtin option.
from . import optinterpreter
for k, v in default_options.items():
def set_default_options(self, default_options: 'T.OrderedDict[str, str]', subproject: str, env: 'Environment') -> None:
def make_key(key: str) -> str:
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
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:
cmd_line_options[k] = v
# Override all the above defaults using the command-line arguments
# actually passed to us
cmd_line_options.update(env.cmd_line_options)
env.cmd_line_options = cmd_line_options
# 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
# languages and setting the backend (builtin options must be set first
# to know which backend we'll use).
return '{}:{}'.format(subproject, key)
return key
options = OrderedDict()
# Some options default to environment variables if they are
# unset, set those now. These will either be overwritten
# below, or they won't. These should only be set on the first run.
for for_machine in MachineChoice:
p_env_pair = get_env_var_pair(for_machine, self.is_cross_build(), 'PKG_CONFIG_PATH')
if p_env_pair is not None:
p_env_var, p_env = p_env_pair
# PKG_CONFIG_PATH may contain duplicates, which must be
# removed, else a duplicates-in-array-option warning arises.
p_list = list(OrderedSet(p_env.split(':')))
key = 'pkg_config_path'
if for_machine == MachineChoice.BUILD:
key = 'build.' + key
if env.first_invocation:
options[key] = p_list
elif options.get(key, []) != p_list:
mlog.warning(
p_env_var +
' environment variable has changed '
'between configurations, meson ignores this. '
'Use -Dpkg_config_path to change pkg-config search '
'path instead.'
)
def remove_prefix(text, prefix):
if text.startswith(prefix):
return text[len(prefix):]
return text
for k, v in env.cmd_line_options.items():
if subproject:
if not k.startswith(subproject + ':'):
continue
elif k not in builtin_options.keys() \
and remove_prefix(k, 'build.') not in builtin_options_per_machine.keys():
if ':' in k:
continue
if optinterpreter.is_invalid_name(k, log=False):
# TODO: validate these
from .compilers import all_languages, base_options
lang_prefixes = tuple('{}_'.format(l) for l in all_languages)
# split arguments that can be set now, and those that cannot so they
# can be set later, when they've been initialized.
for k, v in default_options.items():
if k.startswith(lang_prefixes):
lang, key = k.split('_', 1)
for machine in MachineChoice:
if key not in env.compiler_options[machine][lang]:
env.compiler_options[machine][lang][key] = v
elif k in base_options:
if not subproject and k not in env.base_options:
env.base_options[k] = v
else:
options[make_key(k)] = v
for k, v in chain(env.meson_options.host.get('', {}).items(),
env.meson_options.host.get(subproject, {}).items()):
options[make_key(k)] = v
for k, v in chain(env.meson_options.build.get('', {}).items(),
env.meson_options.build.get(subproject, {}).items()):
if k in builtin_options_per_machine:
options[make_key('build.{}'.format(k))] = v
options.update({make_key(k): v for k, v in env.user_options.get(subproject, {}).items()})
# Some options (namely the compiler options) are not preasant in
# coredata until the compiler is fully initialized. As such, we need to
# put those options into env.meson_options, only if they're not already
# in there, as the machine files and command line have precendence.
for k, v in default_options.items():
if k in builtin_options and not builtin_options[k].yielding:
continue
for machine in MachineChoice:
if machine is MachineChoice.BUILD and not self.is_cross_build():
continue
options[k] = v
if k not in env.meson_options[machine][subproject]:
env.meson_options[machine][subproject][k] = v
self.set_options(options, subproject=subproject)
@ -830,24 +802,19 @@ class CoreData:
env.is_cross_build(),
env.properties[for_machine]).items():
# prefixed compiler options affect just this machine
opt_prefix = for_machine.get_prefix()
user_k = opt_prefix + lang + '_' + k
if user_k in env.cmd_line_options:
o.set_value(env.cmd_line_options[user_k])
if k in env.compiler_options[for_machine].get(lang, {}):
o.set_value(env.compiler_options[for_machine][lang][k])
self.compiler_options[for_machine][lang].setdefault(k, o)
def process_new_compiler(self, lang: str, comp: T.Type['Compiler'], env: 'Environment') -> None:
def process_new_compiler(self, lang: str, comp: 'Compiler', env: 'Environment') -> None:
from . import compilers
self.compilers[comp.for_machine][lang] = comp
enabled_opts = []
for k, o in comp.get_options().items():
# prefixed compiler options affect just this machine
opt_prefix = comp.for_machine.get_prefix()
user_k = opt_prefix + lang + '_' + k
if user_k in env.cmd_line_options:
o.set_value(env.cmd_line_options[user_k])
if k in env.compiler_options[comp.for_machine].get(lang, {}):
o.set_value(env.compiler_options[comp.for_machine][lang][k])
self.compiler_options[comp.for_machine][lang].setdefault(k, o)
enabled_opts = []
@ -855,8 +822,8 @@ class CoreData:
if optname in self.base_options:
continue
oobj = compilers.base_options[optname]
if optname in env.cmd_line_options:
oobj.set_value(env.cmd_line_options[optname])
if optname in env.base_options:
oobj.set_value(env.base_options[optname])
enabled_opts.append(optname)
self.base_options[optname] = oobj
self.emit_base_options_warnings(enabled_opts)
@ -1150,23 +1117,25 @@ class BuiltinOption(T.Generic[_T, _U]):
cmdline_name = self.argparse_name_to_arg(prefix + name)
parser.add_argument(cmdline_name, help=h + help_suffix, **kwargs)
# Update `docs/markdown/Builtin-options.md` after changing the options below
builtin_options = OrderedDict([
# Directories
('prefix', BuiltinOption(UserStringOption, 'Installation prefix', default_prefix())),
('bindir', BuiltinOption(UserStringOption, 'Executable directory', 'bin')),
('datadir', BuiltinOption(UserStringOption, 'Data file directory', 'share')),
('includedir', BuiltinOption(UserStringOption, 'Header file directory', 'include')),
('infodir', BuiltinOption(UserStringOption, 'Info page directory', 'share/info')),
('libdir', BuiltinOption(UserStringOption, 'Library directory', default_libdir())),
('libexecdir', BuiltinOption(UserStringOption, 'Library executable directory', default_libexecdir())),
('localedir', BuiltinOption(UserStringOption, 'Locale data directory', 'share/locale')),
BUILTIN_DIR_OPTIONS = OrderedDict([
('prefix', BuiltinOption(UserStringOption, 'Installation prefix', default_prefix())),
('bindir', BuiltinOption(UserStringOption, 'Executable directory', 'bin')),
('datadir', BuiltinOption(UserStringOption, 'Data file directory', 'share')),
('includedir', BuiltinOption(UserStringOption, 'Header file directory', 'include')),
('infodir', BuiltinOption(UserStringOption, 'Info page directory', 'share/info')),
('libdir', BuiltinOption(UserStringOption, 'Library directory', default_libdir())),
('libexecdir', BuiltinOption(UserStringOption, 'Library executable directory', default_libexecdir())),
('localedir', BuiltinOption(UserStringOption, 'Locale data directory', 'share/locale')),
('localstatedir', BuiltinOption(UserStringOption, 'Localstate data directory', 'var')),
('mandir', BuiltinOption(UserStringOption, 'Manual page directory', 'share/man')),
('sbindir', BuiltinOption(UserStringOption, 'System executable directory', 'sbin')),
('sharedstatedir', BuiltinOption(UserStringOption, 'Architecture-independent data directory', 'com')),
('sysconfdir', BuiltinOption(UserStringOption, 'Sysconf data directory', 'etc')),
# Core options
]) # type: OptionDictType
BUILTIN_CORE_OPTIONS = OrderedDict([
('auto_features', BuiltinOption(UserFeatureOption, "Override value of all 'auto' features", 'auto')),
('backend', BuiltinOption(UserComboOption, 'Backend to use', 'ninja', choices=backendlist)),
('buildtype', BuiltinOption(UserComboOption, 'Build type to use', 'debug',
@ -1186,7 +1155,9 @@ builtin_options = OrderedDict([
('werror', BuiltinOption(UserBooleanOption, 'Treat warnings as errors', False, yielding=False)),
('wrap_mode', BuiltinOption(UserComboOption, 'Wrap mode', 'default', choices=['default', 'nofallback', 'nodownload', 'forcefallback'])),
('force_fallback_for', BuiltinOption(UserArrayOption, 'Force fallback for those subprojects', [])),
])
]) # type: OptionDictType
builtin_options = OrderedDict(chain(BUILTIN_DIR_OPTIONS.items(), BUILTIN_CORE_OPTIONS.items()))
builtin_options_per_machine = OrderedDict([
('pkg_config_path', BuiltinOption(UserArrayOption, 'List of additional paths for pkg-config to search', [])),
@ -1222,3 +1193,4 @@ forbidden_target_names = {'clean': None,
'dist': None,
'distcheck': None,
}

@ -407,43 +407,3 @@ class BinaryTable:
if command is not None and (len(command) == 0 or len(command[0].strip()) == 0):
command = None
return command
class Directories:
"""Data class that holds information about directories for native and cross
builds.
"""
def __init__(self, bindir: T.Optional[str] = None, datadir: T.Optional[str] = None,
includedir: T.Optional[str] = None, infodir: T.Optional[str] = None,
libdir: T.Optional[str] = None, libexecdir: T.Optional[str] = None,
localedir: T.Optional[str] = None, localstatedir: T.Optional[str] = None,
mandir: T.Optional[str] = None, prefix: T.Optional[str] = None,
sbindir: T.Optional[str] = None, sharedstatedir: T.Optional[str] = None,
sysconfdir: T.Optional[str] = None):
self.bindir = bindir
self.datadir = datadir
self.includedir = includedir
self.infodir = infodir
self.libdir = libdir
self.libexecdir = libexecdir
self.localedir = localedir
self.localstatedir = localstatedir
self.mandir = mandir
self.prefix = prefix
self.sbindir = sbindir
self.sharedstatedir = sharedstatedir
self.sysconfdir = sysconfdir
def __contains__(self, key: str) -> bool:
return hasattr(self, key)
def __getitem__(self, key: str) -> T.Optional[str]:
# Mypy can't figure out what to do with getattr here, so we'll case for it
return T.cast(T.Optional[str], getattr(self, key))
def __setitem__(self, key: str, value: T.Optional[str]) -> None:
setattr(self, key, value)
def __iter__(self) -> T.Iterator[T.Tuple[str, str]]:
return iter(self.__dict__.items())

@ -16,6 +16,7 @@ import os, platform, re, sys, shutil, subprocess
import tempfile
import shlex
import typing as T
import collections
from . import coredata
from .linkers import ArLinker, ArmarLinker, VisualStudioLinker, DLinker, CcrxLinker, Xc16Linker, C2000Linker, IntelVisualStudioLinker
@ -27,12 +28,14 @@ from .mesonlib import (
from . import mlog
from .envconfig import (
BinaryTable, Directories, MachineInfo,
Properties, known_cpu_families,
BinaryTable, MachineInfo,
Properties, known_cpu_families, get_env_var_pair,
)
from . import compilers
from .compilers import (
Compiler,
all_languages,
base_options,
is_assembly,
is_header,
is_library,
@ -548,10 +551,11 @@ class Environment:
# Misc other properties about each machine.
properties = PerMachineDefaultable()
# Store paths for native and cross build files. There is no target
# machine information here because nothing is installed for the target
# architecture, just the build and host architectures
paths = PerMachineDefaultable()
# We only need one of these as project options are not per machine
user_options = collections.defaultdict(dict) # type: T.DefaultDict[str, T.Dict[str, object]]
# meson builtin options, as passed through cross or native files
meson_options = PerMachineDefaultable() # type: PerMachineDefaultable[T.DefaultDict[str, T.Dict[str, object]]]
## Setup build machine defaults
@ -563,14 +567,80 @@ class Environment:
binaries.build = BinaryTable()
properties.build = Properties()
# meson base options
_base_options = {} # type: T.Dict[str, object]
# Per language compiler arguments
compiler_options = PerMachineDefaultable() # type: PerMachineDefaultable[T.DefaultDict[str, T.Dict[str, object]]]
compiler_options.build = collections.defaultdict(dict)
## Read in native file(s) to override build machine configuration
def load_options(tag: str, store: T.Dict[str, T.Any]) -> None:
for section in config.keys():
if section.endswith(tag):
if ':' in section:
project = section.split(':')[0]
else:
project = ''
store[project].update(config.get(section, {}))
def split_base_options(mopts: T.DefaultDict[str, T.Dict[str, object]]) -> None:
for k, v in list(mopts.get('', {}).items()):
if k in base_options:
_base_options[k] = v
del mopts[k]
lang_prefixes = tuple('{}_'.format(l) for l in all_languages)
def split_compiler_options(mopts: T.DefaultDict[str, T.Dict[str, object]], machine: MachineChoice) -> None:
for k, v in list(mopts.get('', {}).items()):
if k.startswith(lang_prefixes):
lang, key = k.split('_', 1)
if compiler_options[machine] is None:
compiler_options[machine] = collections.defaultdict(dict)
if lang not in compiler_options[machine]:
compiler_options[machine][lang] = collections.defaultdict(dict)
compiler_options[machine][lang][key] = v
del mopts[''][k]
def move_compiler_options(properties: Properties, compopts: T.Dict[str, T.DefaultDict[str, object]]) -> None:
for k, v in properties.properties.copy().items():
for lang in all_languages:
if k == '{}_args'.format(lang):
if 'args' not in compopts[lang]:
compopts[lang]['args'] = v
else:
mlog.warning('Ignoring {}_args in [properties] section for those in the [built-in options]'.format(lang))
elif k == '{}_link_args'.format(lang):
if 'link_args' not in compopts[lang]:
compopts[lang]['link_args'] = v
else:
mlog.warning('Ignoring {}_link_args in [properties] section in favor of the [built-in options] section.')
else:
continue
mlog.deprecation('{} in the [properties] section of the machine file is deprecated, use the [built-in options] section.'.format(k))
del properties.properties[k]
break
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_options('project options', user_options)
meson_options.build = collections.defaultdict(dict)
if config.get('paths') is not None:
mlog.deprecation('The [paths] section is deprecated, use the [built-in options] section instead.')
load_options('paths', meson_options.build)
load_options('built-in options', meson_options.build)
if not self.coredata.cross_files:
split_base_options(meson_options.build)
split_compiler_options(meson_options.build, MachineChoice.BUILD)
move_compiler_options(properties.build, compiler_options.build)
## Read in cross file(s) to override host machine configuration
if self.coredata.cross_files:
@ -581,14 +651,85 @@ class Environment:
machines.host = MachineInfo.from_literal(config['host_machine'])
if 'target_machine' in config:
machines.target = MachineInfo.from_literal(config['target_machine'])
paths.host = Directories(**config.get('paths', {}))
load_options('project options', user_options)
meson_options.host = collections.defaultdict(dict)
compiler_options.host = collections.defaultdict(dict)
if config.get('paths') is not None:
mlog.deprecation('The [paths] section is deprecated, use the [built-in options] section instead.')
load_options('paths', meson_options.host)
load_options('built-in options', meson_options.host)
split_base_options(meson_options.host)
split_compiler_options(meson_options.host, MachineChoice.HOST)
move_compiler_options(properties.host, compiler_options.host)
## "freeze" now initialized configuration, and "save" to the class.
self.machines = machines.default_missing()
self.binaries = binaries.default_missing()
self.properties = properties.default_missing()
self.paths = paths.default_missing()
self.user_options = user_options
self.meson_options = meson_options.default_missing()
self.base_options = _base_options
self.compiler_options = compiler_options.default_missing()
# Some options default to environment variables if they are
# unset, set those now.
for for_machine in MachineChoice:
p_env_pair = get_env_var_pair(for_machine, self.coredata.is_cross_build(), 'PKG_CONFIG_PATH')
if p_env_pair is not None:
p_env_var, p_env = p_env_pair
# PKG_CONFIG_PATH may contain duplicates, which must be
# removed, else a duplicates-in-array-option warning arises.
p_list = list(mesonlib.OrderedSet(p_env.split(':')))
key = 'pkg_config_path'
if self.first_invocation:
# Environment variables override config
self.meson_options[for_machine][''][key] = p_list
elif self.meson_options[for_machine][''].get(key, []) != p_list:
mlog.warning(
p_env_var,
'environment variable does not match configured',
'between configurations, meson ignores this.',
'Use -Dpkg_config_path to change pkg-config search',
'path instead.'
)
# Read in command line and populate options
# TODO: validate all of this
all_builtins = set(coredata.builtin_options) | set(coredata.builtin_options_per_machine) | set(coredata.builtin_dir_noprefix_options)
for k, v in options.cmd_line_options.items():
try:
subproject, k = k.split(':')
except ValueError:
subproject = ''
if k in base_options:
self.base_options[k] = v
elif k.startswith(lang_prefixes):
lang, key = k.split('_', 1)
self.compiler_options.host[lang][key] = v
elif k in all_builtins or k.startswith('backend_'):
self.meson_options.host[subproject][k] = v
elif k.startswith('build.'):
k = k.lstrip('build.')
if k in coredata.builtin_options_per_machine:
if self.meson_options.build is None:
self.meson_options.build = collections.defaultdict(dict)
self.meson_options.build[subproject][k] = v
else:
assert not k.startswith('build.')
self.user_options[subproject][k] = v
# Warn if the user is using two different ways of setting build-type
# options that override each other
if meson_options.build and 'buildtype' in meson_options.build[''] and \
('optimization' in meson_options.build[''] or 'debug' in meson_options.build['']):
mlog.warning('Recommend using either -Dbuildtype or -Doptimization + -Ddebug. '
'Using both is redundant since they override each other. '
'See: https://mesonbuild.com/Builtin-options.html#build-type-options')
exe_wrapper = self.lookup_binary_entry(MachineChoice.HOST, 'exe_wrapper')
if exe_wrapper is not None:
@ -597,8 +738,6 @@ class Environment:
else:
self.exe_wrapper = None
self.cmd_line_options = options.cmd_line_options.copy()
# List of potential compilers.
if mesonlib.is_windows():
# Intel C and C++ compiler is icl on Windows, but icc and icpc elsewhere.

@ -2791,6 +2791,7 @@ external dependencies (including libraries) must go to "dependencies".''')
default_options = mesonlib.stringlistify(kwargs.get('default_options', []))
default_options = coredata.create_options_dict(default_options)
if dirname == '':
raise InterpreterException('Subproject dir name must not be empty.')
if dirname[0] == '.':
@ -2945,6 +2946,7 @@ external dependencies (including libraries) must go to "dependencies".''')
if self.is_subproject():
optname = self.subproject + ':' + optname
for opts in [
self.coredata.base_options, compilers.base_options, self.coredata.builtins,
dict(self.coredata.get_prefixed_options_per_machine(self.coredata.builtins_per_machine)),
@ -3030,8 +3032,9 @@ external dependencies (including libraries) must go to "dependencies".''')
if self.environment.first_invocation:
self.coredata.init_backend_options(backend)
options = {k: v for k, v in self.environment.cmd_line_options.items() if k.startswith('backend_')}
self.coredata.set_options(options)
if '' in self.environment.meson_options.host:
options = {k: v for k, v in self.environment.meson_options.host[''].items() if k.startswith('backend_')}
self.coredata.set_options(options)
@stringArgs
@permittedKwargs(permitted_kwargs['project'])
@ -3064,7 +3067,7 @@ external dependencies (including libraries) must go to "dependencies".''')
self.project_default_options = mesonlib.stringlistify(kwargs.get('default_options', []))
self.project_default_options = coredata.create_options_dict(self.project_default_options)
if self.environment.first_invocation:
default_options = self.project_default_options
default_options = self.project_default_options.copy()
default_options.update(self.default_project_options)
self.coredata.init_builtins(self.subproject)
else:

@ -184,19 +184,7 @@ class Conf:
if not self.default_values_only:
print(' Build dir ', self.build_dir)
dir_option_names = ['bindir',
'datadir',
'includedir',
'infodir',
'libdir',
'libexecdir',
'localedir',
'localstatedir',
'mandir',
'prefix',
'sbindir',
'sharedstatedir',
'sysconfdir']
dir_option_names = list(coredata.BUILTIN_DIR_OPTIONS)
test_option_names = ['errorlogs',
'stdsplit']
core_option_names = [k for k in self.coredata.builtins if k not in dir_option_names + test_option_names]

@ -389,6 +389,9 @@ class PerMachine(T.Generic[_T]):
unfreeze.host = None
return unfreeze
def __repr__(self) -> str:
return 'PerMachine({!r}, {!r})'.format(self.build, self.host)
class PerThreeMachine(PerMachine[_T]):
"""Like `PerMachine` but includes `target` too.
@ -421,6 +424,9 @@ class PerThreeMachine(PerMachine[_T]):
def matches_build_machine(self, machine: MachineChoice) -> bool:
return self.build == self[machine]
def __repr__(self) -> str:
return 'PerThreeMachine({!r}, {!r}, {!r})'.format(self.build, self.host, self.target)
class PerMachineDefaultable(PerMachine[T.Optional[_T]]):
"""Extends `PerMachine` with the ability to default from `None`s.
@ -439,6 +445,9 @@ class PerMachineDefaultable(PerMachine[T.Optional[_T]]):
freeze.host = freeze.build
return freeze
def __repr__(self) -> str:
return 'PerMachineDefaultable({!r}, {!r})'.format(self.build, self.host)
class PerThreeMachineDefaultable(PerMachineDefaultable, PerThreeMachine[T.Optional[_T]]):
"""Extends `PerThreeMachine` with the ability to default from `None`s.
@ -460,6 +469,9 @@ class PerThreeMachineDefaultable(PerMachineDefaultable, PerThreeMachine[T.Option
freeze.target = freeze.host
return freeze
def __repr__(self) -> str:
return 'PerThreeMachineDefaultable({!r}, {!r}, {!r})'.format(self.build, self.host, self.target)
def is_sunos() -> bool:
return platform.system().lower() == 'sunos'

@ -200,19 +200,7 @@ def list_buildoptions_from_source(intr: IntrospectionInterpreter) -> T.List[T.Di
def list_buildoptions(coredata: cdata.CoreData, subprojects: T.Optional[T.List[str]] = None) -> T.List[T.Dict[str, T.Union[str, bool, int, T.List[str]]]]:
optlist = [] # type: T.List[T.Dict[str, T.Union[str, bool, int, T.List[str]]]]
dir_option_names = ['bindir',
'datadir',
'includedir',
'infodir',
'libdir',
'libexecdir',
'localedir',
'localstatedir',
'mandir',
'prefix',
'sbindir',
'sharedstatedir',
'sysconfdir']
dir_option_names = list(cdata.BUILTIN_DIR_OPTIONS)
test_option_names = ['errorlogs',
'stdsplit']
core_option_names = [k for k in coredata.builtins if k not in dir_option_names + test_option_names]

@ -5014,7 +5014,7 @@ recommended as it is not supported on some platforms''')
def test_wrap_git(self):
with tempfile.TemporaryDirectory() as tmpdir:
srcdir = os.path.join(tmpdir, 'src')
shutil.copytree(os.path.join(self.unit_test_dir, '78 wrap-git'), srcdir)
shutil.copytree(os.path.join(self.unit_test_dir, '81 wrap-git'), srcdir)
upstream = os.path.join(srcdir, 'subprojects', 'wrap_git_upstream')
upstream_uri = Path(upstream).as_uri()
_git_init(upstream)
@ -6473,7 +6473,7 @@ class LinuxlikeTests(BasePlatformTests):
if is_osx():
raise unittest.SkipTest('Global RPATHs via LDFLAGS not yet supported on MacOS (does anybody need it?)')
testdir = os.path.join(self.unit_test_dir, '77 global-rpath')
testdir = os.path.join(self.unit_test_dir, '80 global-rpath')
oldinstalldir = self.installdir
# Build and install an external library without DESTDIR.
@ -6846,7 +6846,7 @@ class LinuxlikeTests(BasePlatformTests):
oldinstalldir = self.installdir
# Build and install both external libraries without DESTDIR
val1dir = os.path.join(self.unit_test_dir, '76 pkgconfig prefixes', 'val1')
val1dir = os.path.join(self.unit_test_dir, '77 pkgconfig prefixes', 'val1')
val1prefix = os.path.join(oldinstalldir, 'val1')
self.prefix = val1prefix
self.installdir = val1prefix
@ -6857,7 +6857,7 @@ class LinuxlikeTests(BasePlatformTests):
env1 = {}
env1['PKG_CONFIG_PATH'] = os.path.join(val1prefix, self.libdir, 'pkgconfig')
val2dir = os.path.join(self.unit_test_dir, '76 pkgconfig prefixes', 'val2')
val2dir = os.path.join(self.unit_test_dir, '77 pkgconfig prefixes', 'val2')
val2prefix = os.path.join(oldinstalldir, 'val2')
self.prefix = val2prefix
self.installdir = val2prefix
@ -6869,7 +6869,7 @@ class LinuxlikeTests(BasePlatformTests):
# Build, install, and run the client program
env2 = {}
env2['PKG_CONFIG_PATH'] = os.path.join(val2prefix, self.libdir, 'pkgconfig')
testdir = os.path.join(self.unit_test_dir, '76 pkgconfig prefixes', 'client')
testdir = os.path.join(self.unit_test_dir, '77 pkgconfig prefixes', 'client')
testprefix = os.path.join(oldinstalldir, 'client')
self.prefix = testprefix
self.installdir = testprefix
@ -7180,7 +7180,7 @@ class LinuxCrossArmTests(BaseLinuxCrossTests):
def test_cross_libdir_subproject(self):
# Guard against a regression where calling "subproject"
# would reset the value of libdir to its default value.
testdir = os.path.join(self.unit_test_dir, '76 subdir libdir')
testdir = os.path.join(self.unit_test_dir, '78 subdir libdir')
self.init(testdir, extra_args=['--libdir=fuf'])
for i in self.introspect('--buildoptions'):
if i['name'] == 'libdir':
@ -7672,7 +7672,12 @@ 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, int, float)):
f.write("{}={}\n".format(k, v))
elif isinstance(v, list):
f.write("{}=[{}]\n".format(k, ', '.join(["'{}'".format(w) for w in 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 +8001,219 @@ 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, '79 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({'sub: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_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?')
def test_option_integer(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': {'unity_size': 100}})
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 'unity_size' in each['name']:
self.assertEqual(each['value'], 100)
break
else:
self.fail('Did not find unity_size in build options?')
def test_builtin_options(self):
testcase = os.path.join(self.common_test_dir, '2 cpp')
config = self.helper_create_native_file({'built-in options': {'cpp_std': 'c++14'}})
self.init(testcase, extra_args=['--native-file', config])
configuration = self.introspect('--buildoptions')
for each in configuration:
if each['name'] == 'cpp_std':
self.assertEqual(each['value'], 'c++14')
break
else:
self.fail('Did not find werror in build options?')
def test_builtin_options_env_overrides_conf(self):
testcase = os.path.join(self.common_test_dir, '2 cpp')
config = self.helper_create_native_file({'built-in options': {'pkg_config_path': '/foo'}})
self.init(testcase, extra_args=['--native-file', config], override_envvars={'PKG_CONFIG_PATH': '/bar'})
configuration = self.introspect('--buildoptions')
for each in configuration:
if each['name'] == 'pkg_config_path':
self.assertEqual(each['value'], ['/bar'])
break
else:
self.fail('Did not find pkg_config_path in build options?')
def test_builtin_options_subprojects(self):
testcase = os.path.join(self.common_test_dir, '102 subproject subdir')
config = self.helper_create_native_file({'built-in options': {'default_library': 'both', 'c_args': ['-Dfoo']}, 'sub:built-in options': {'default_library': 'static'}})
self.init(testcase, extra_args=['--native-file', config])
configuration = self.introspect('--buildoptions')
found = 0
for each in configuration:
# Test that no-per subproject options are inherited from the parent
if 'c_args' in each['name']:
# This path will be hit twice, once for build and once for host,
self.assertEqual(each['value'], ['-Dfoo'])
found += 1
elif each['name'] == 'default_library':
self.assertEqual(each['value'], 'both')
found += 1
elif each['name'] == 'sub:default_library':
self.assertEqual(each['value'], 'static')
found += 1
self.assertEqual(found, 4, 'Did not find all three sections')
def test_builtin_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, '230 persubproject options')
config = self.helper_create_native_file({'sub2:built-in 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_builtin_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, '230 persubproject options')
config = self.helper_create_native_file({'built-in 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')
def test_builtin_options_compiler_properties(self):
# the properties section can have lang_args, and those need to be
# overwritten by the built-in options
testcase = os.path.join(self.common_test_dir, '1 trivial')
config = self.helper_create_native_file({
'built-in options': {'c_args': ['-DFOO']},
'properties': {'c_args': ['-DBAR']},
})
self.init(testcase, extra_args=['--native-file', config])
configuration = self.introspect('--buildoptions')
for each in configuration:
if each['name'] == 'c_args':
self.assertEqual(each['value'], ['-DFOO'])
break
else:
self.fail('Did not find c_args in build options?')
def test_builtin_options_compiler_properties_legacy(self):
# The legacy placement in properties is still valid if a 'built-in
# options' setting is present, but doesn't have the lang_args
testcase = os.path.join(self.common_test_dir, '1 trivial')
config = self.helper_create_native_file({
'built-in options': {'default_library': 'static'},
'properties': {'c_args': ['-DBAR']},
})
self.init(testcase, extra_args=['--native-file', config])
configuration = self.introspect('--buildoptions')
for each in configuration:
if each['name'] == 'c_args':
self.assertEqual(each['value'], ['-DBAR'])
break
else:
self.fail('Did not find c_args in build options?')
def test_builtin_options_paths(self):
# the properties section can have lang_args, and those need to be
# overwritten by the built-in options
testcase = os.path.join(self.common_test_dir, '1 trivial')
config = self.helper_create_native_file({
'built-in options': {'bindir': 'foo'},
'paths': {'bindir': 'bar'},
})
self.init(testcase, extra_args=['--native-file', config])
configuration = self.introspect('--buildoptions')
for each in configuration:
if each['name'] == 'bindir':
self.assertEqual(each['value'], 'foo')
break
else:
self.fail('Did not find bindir in build options?')
def test_builtin_options_paths_legacy(self):
testcase = os.path.join(self.common_test_dir, '1 trivial')
config = self.helper_create_native_file({
'built-in options': {'default_library': 'static'},
'paths': {'bindir': 'bar'},
})
self.init(testcase, extra_args=['--native-file', config])
configuration = self.introspect('--buildoptions')
for each in configuration:
if each['name'] == 'bindir':
self.assertEqual(each['value'], 'bar')
break
else:
self.fail('Did not find bindir in build options?')
def test_builtin_options_paths_legacy(self):
testcase = os.path.join(self.common_test_dir, '1 trivial')
config = self.helper_create_native_file({
'built-in options': {'default_library': 'static'},
'paths': {'bindir': 'bar'},
})
self.init(testcase, extra_args=['--native-file', config])
configuration = self.introspect('--buildoptions')
for each in configuration:
if each['name'] == 'bindir':
self.assertEqual(each['value'], 'bar')
break
else:
self.fail('Did not find bindir in build options?')
class CrossFileTests(BasePlatformTests):
@ -8005,6 +8223,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 +8356,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 +8427,89 @@ 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=['--cross-file', config])
self.assertRegex(cm.exception.stdout, r'Incorrect value to [a-z]+ option')
def test_builtin_options(self):
testcase = os.path.join(self.common_test_dir, '2 cpp')
config = self.helper_create_cross_file({'built-in options': {'cpp_std': 'c++14'}})
self.init(testcase, extra_args=['--cross-file', config])
configuration = self.introspect('--buildoptions')
for each in configuration:
if each['name'] == 'cpp_std':
self.assertEqual(each['value'], 'c++14')
break
else:
self.fail('No c++ standard set?')
def test_builtin_options_per_machine(self):
"""Test options that are allowed to be set on a per-machine basis.
Such options could be passed twice, once for the build machine, and
once for the host machine. I've picked pkg-config path, but any would
do that can be set for both.
"""
testcase = os.path.join(self.common_test_dir, '2 cpp')
cross = self.helper_create_cross_file({'built-in options': {'pkg_config_path': '/cross/path', 'cpp_std': 'c++17'}})
native = self.helper_create_cross_file({'built-in options': {'pkg_config_path': '/native/path', 'cpp_std': 'c++14'}})
# Ensure that PKG_CONFIG_PATH is not set in the environment
with mock.patch.dict('os.environ'):
for k in ['PKG_CONFIG_PATH', 'PKG_CONFIG_PATH_FOR_BUILD']:
try:
del os.environ[k]
except KeyError:
pass
self.init(testcase, extra_args=['--cross-file', cross, '--native-file', native])
configuration = self.introspect('--buildoptions')
found = 0
for each in configuration:
if each['name'] == 'pkg_config_path':
self.assertEqual(each['value'], ['/cross/path'])
found += 1
elif each['name'] == 'cpp_std':
self.assertEqual(each['value'], 'c++17')
found += 1
elif each['name'] == 'build.pkg_config_path':
self.assertEqual(each['value'], ['/native/path'])
found += 1
elif each['name'] == 'build.cpp_std':
self.assertEqual(each['value'], 'c++14')
found += 1
if found == 4:
break
self.assertEqual(found, 4, 'Did not find all sections.')
def test_builtin_options_env_overrides_conf(self):
testcase = os.path.join(self.common_test_dir, '2 cpp')
config = self.helper_create_cross_file({'built-in options': {'pkg_config_path': '/foo'}})
cross = self.helper_create_cross_file({'built-in options': {'pkg_config_path': '/foo'}})
self.init(testcase, extra_args=['--native-file', config, '--cross-file', cross],
override_envvars={'PKG_CONFIG_PATH': '/bar', 'PKG_CONFIG_PATH_FOR_BUILD': '/dir'})
configuration = self.introspect('--buildoptions')
found = 0
for each in configuration:
if each['name'] == 'pkg_config_path':
self.assertEqual(each['value'], ['/bar'])
found += 1
elif each['name'] == 'build.pkg_config_path':
self.assertEqual(each['value'], ['/dir'])
found += 1
if found == 2:
break
self.assertEqual(found, 2, 'Did not find all sections.')
class TAPParserTests(unittest.TestCase):
def assert_test(self, events, **kwargs):
if 'explanation' not in kwargs:

@ -0,0 +1,2 @@
[built-in options]
optimization = 1

@ -0,0 +1,5 @@
{
"stdout": [
{ "line": "test cases/failing/107 number in combo/meson.build:1:0: ERROR: Value \"1\" (of type \"number\") for combo option \"Optimization level\" is not one of the choices. Possible choices are (as string): \"0\", \"g\", \"1\", \"2\", \"3\", \"s\"." }
]
}

@ -0,0 +1,5 @@
option(
'opt',
type : 'combo',
choices : ['true', 'false']
)

@ -0,0 +1,2 @@
[project options]
opt = true

@ -0,0 +1,5 @@
{
"stdout": [
{ "line": "test cases/failing/108 bool in combo/meson.build:1:0: ERROR: Value \"True\" (of type \"boolean\") for combo option \"opt\" is not one of the choices. Possible choices are (as string): \"true\", \"false\"." }
]
}

@ -0,0 +1,3 @@
project('user option for subproject')
p = subproject('sub')
Loading…
Cancel
Save