aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDylan Baker <dylan@pnwbakers.com>2020-08-02 10:03:13 -0700
committerGitHub <noreply@github.com>2020-08-02 10:03:13 -0700
commit3040f3c8dc620152afea73066f0048c3bbeb7266 (patch)
tree683fd1aaa7ac1d71abfd55ba68044ccc43d67d14
parent894623ad5aabaac06fab219173183c4533af74ef (diff)
parent4d2a17041f0dd54001a7d32b36e75608330f41f5 (diff)
downloadmeson-3040f3c8dc620152afea73066f0048c3bbeb7266.zip
meson-3040f3c8dc620152afea73066f0048c3bbeb7266.tar.gz
meson-3040f3c8dc620152afea73066f0048c3bbeb7266.tar.bz2
Merge pull request #6597 from dcbaker/full-project-config
Set project and meson options in cross/native files
-rw-r--r--cross/armcc.txt2
-rw-r--r--cross/armclang-linux.txt5
-rw-r--r--cross/armclang.txt2
-rw-r--r--cross/c2000.txt6
-rw-r--r--cross/ccrx.txt2
-rw-r--r--cross/iphone.txt6
-rw-r--r--cross/tvos.txt7
-rw-r--r--cross/ubuntu-armhf.txt6
-rw-r--r--cross/wasm.txt3
-rw-r--r--cross/xc16.txt2
-rw-r--r--docs/markdown/Machine-files.md121
-rw-r--r--docs/markdown/snippets/project_options_in_machine_files.md52
-rw-r--r--mesonbuild/ast/introspection.py2
-rw-r--r--mesonbuild/coredata.py196
-rw-r--r--mesonbuild/envconfig.py40
-rw-r--r--mesonbuild/environment.py161
-rw-r--r--mesonbuild/interpreter.py9
-rw-r--r--mesonbuild/mconf.py14
-rw-r--r--mesonbuild/mesonlib.py12
-rw-r--r--mesonbuild/mintro.py14
-rwxr-xr-xrun_unittests.py335
-rw-r--r--test cases/cmake/20 cmake file/foolib.cmake.in (renamed from test cases/cmake/19 cmake file/foolib.cmake.in)0
-rw-r--r--test cases/cmake/20 cmake file/meson.build (renamed from test cases/cmake/19 cmake file/meson.build)0
-rw-r--r--test cases/cmake/20 cmake file/test.json (renamed from test cases/cmake/19 cmake file/test.json)0
-rw-r--r--test cases/failing/107 number in combo/meson.build1
-rw-r--r--test cases/failing/107 number in combo/nativefile.ini2
-rw-r--r--test cases/failing/107 number in combo/test.json5
-rw-r--r--test cases/failing/108 bool in combo/meson.build1
-rw-r--r--test cases/failing/108 bool in combo/meson_options.txt5
-rw-r--r--test cases/failing/108 bool in combo/nativefile.ini2
-rw-r--r--test cases/failing/108 bool in combo/test.json5
-rw-r--r--test cases/unit/77 pkgconfig prefixes/client/client.c (renamed from test cases/unit/76 pkgconfig prefixes/client/client.c)0
-rw-r--r--test cases/unit/77 pkgconfig prefixes/client/meson.build (renamed from test cases/unit/76 pkgconfig prefixes/client/meson.build)0
-rw-r--r--test cases/unit/77 pkgconfig prefixes/val1/meson.build (renamed from test cases/unit/76 pkgconfig prefixes/val1/meson.build)0
-rw-r--r--test cases/unit/77 pkgconfig prefixes/val1/val1.c (renamed from test cases/unit/76 pkgconfig prefixes/val1/val1.c)0
-rw-r--r--test cases/unit/77 pkgconfig prefixes/val1/val1.h (renamed from test cases/unit/76 pkgconfig prefixes/val1/val1.h)0
-rw-r--r--test cases/unit/77 pkgconfig prefixes/val2/meson.build (renamed from test cases/unit/76 pkgconfig prefixes/val2/meson.build)0
-rw-r--r--test cases/unit/77 pkgconfig prefixes/val2/val2.c (renamed from test cases/unit/76 pkgconfig prefixes/val2/val2.c)0
-rw-r--r--test cases/unit/77 pkgconfig prefixes/val2/val2.h (renamed from test cases/unit/76 pkgconfig prefixes/val2/val2.h)0
-rw-r--r--test cases/unit/78 subdir libdir/meson.build (renamed from test cases/unit/76 subdir libdir/meson.build)0
-rw-r--r--test cases/unit/78 subdir libdir/subprojects/flub/meson.build (renamed from test cases/unit/76 subdir libdir/subprojects/flub/meson.build)0
-rw-r--r--test cases/unit/79 user options for subproject/75 user options for subproject/.gitignore1
-rw-r--r--test cases/unit/79 user options for subproject/75 user options for subproject/meson.build3
-rw-r--r--test cases/unit/80 global-rpath/meson.build (renamed from test cases/unit/77 global-rpath/meson.build)0
-rw-r--r--test cases/unit/80 global-rpath/rpathified.cpp (renamed from test cases/unit/77 global-rpath/rpathified.cpp)0
-rw-r--r--test cases/unit/80 global-rpath/yonder/meson.build (renamed from test cases/unit/77 global-rpath/yonder/meson.build)0
-rw-r--r--test cases/unit/80 global-rpath/yonder/yonder.cpp (renamed from test cases/unit/77 global-rpath/yonder/yonder.cpp)0
-rw-r--r--test cases/unit/80 global-rpath/yonder/yonder.h (renamed from test cases/unit/77 global-rpath/yonder/yonder.h)0
-rw-r--r--test cases/unit/81 wrap-git/meson.build (renamed from test cases/unit/78 wrap-git/meson.build)0
-rw-r--r--test cases/unit/81 wrap-git/subprojects/packagefiles/wrap_git_builddef/meson.build (renamed from test cases/unit/78 wrap-git/subprojects/packagefiles/wrap_git_builddef/meson.build)0
-rw-r--r--test cases/unit/81 wrap-git/subprojects/wrap_git_upstream/main.c (renamed from test cases/unit/78 wrap-git/subprojects/wrap_git_upstream/main.c)0
51 files changed, 791 insertions, 231 deletions
diff --git a/cross/armcc.txt b/cross/armcc.txt
index c884ffa..ae65c9e 100644
--- a/cross/armcc.txt
+++ b/cross/armcc.txt
@@ -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']
diff --git a/cross/armclang-linux.txt b/cross/armclang-linux.txt
index 6df78d6..10f6fa4 100644
--- a/cross/armclang-linux.txt
+++ b/cross/armclang-linux.txt
@@ -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]
diff --git a/cross/armclang.txt b/cross/armclang.txt
index 955b7ef..6146e0d 100644
--- a/cross/armclang.txt
+++ b/cross/armclang.txt
@@ -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']
diff --git a/cross/c2000.txt b/cross/c2000.txt
index e624f25..61c0310 100644
--- a/cross/c2000.txt
+++ b/cross/c2000.txt
@@ -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
diff --git a/cross/ccrx.txt b/cross/ccrx.txt
index 097ec06..f1b536c 100644
--- a/cross/ccrx.txt
+++ b/cross/ccrx.txt
@@ -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']
diff --git a/cross/iphone.txt b/cross/iphone.txt
index e714da5..9659407 100644
--- a/cross/iphone.txt
+++ b/cross/iphone.txt
@@ -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
diff --git a/cross/tvos.txt b/cross/tvos.txt
index dd6d5c1..833f04b 100644
--- a/cross/tvos.txt
+++ b/cross/tvos.txt
@@ -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
diff --git a/cross/ubuntu-armhf.txt b/cross/ubuntu-armhf.txt
index 4600c22..69e0c86 100644
--- a/cross/ubuntu-armhf.txt
+++ b/cross/ubuntu-armhf.txt
@@ -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
diff --git a/cross/wasm.txt b/cross/wasm.txt
index a43636f..f2d0cd7 100644
--- a/cross/wasm.txt
+++ b/cross/wasm.txt
@@ -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']
diff --git a/cross/xc16.txt b/cross/xc16.txt
index 1e67362..c66889d 100644
--- a/cross/xc16.txt
+++ b/cross/xc16.txt
@@ -14,6 +14,8 @@ endian = 'little'
[properties]
needs_exe_wrapper = true
+
+[built-in options]
c_args = [
'-c',
'-mcpu=33EP64MC203',
diff --git a/docs/markdown/Machine-files.md b/docs/markdown/Machine-files.md
index 9011f79..5ac66a8 100644
--- a/docs/markdown/Machine-files.md
+++ b/docs/markdown/Machine-files.md
@@ -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
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..8dab951
--- /dev/null
+++ b/docs/markdown/snippets/project_options_in_machine_files.md
@@ -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'
+```
diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py
index 142c219..6e6927f 100644
--- a/mesonbuild/ast/introspection.py
+++ b/mesonbuild/ast/introspection.py
@@ -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)
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index e2a6954..724e111 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -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,
}
+
diff --git a/mesonbuild/envconfig.py b/mesonbuild/envconfig.py
index 219b62e..9402d38 100644
--- a/mesonbuild/envconfig.py
+++ b/mesonbuild/envconfig.py
@@ -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())
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index a82c8f8..9830b45 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -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.
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index 45813c1..cf7f282 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -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:
diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py
index 2e03cab..f070355 100644
--- a/mesonbuild/mconf.py
+++ b/mesonbuild/mconf.py
@@ -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]
diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py
index a43d4c4..2c563e4 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib.py
@@ -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'
diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py
index cccedaa..0049bbd 100644
--- a/mesonbuild/mintro.py
+++ b/mesonbuild/mintro.py
@@ -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]
diff --git a/run_unittests.py b/run_unittests.py
index 2b0e4e1..6d7eba2 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -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:
diff --git a/test cases/cmake/19 cmake file/foolib.cmake.in b/test cases/cmake/20 cmake file/foolib.cmake.in
index 16e992b..16e992b 100644
--- a/test cases/cmake/19 cmake file/foolib.cmake.in
+++ b/test cases/cmake/20 cmake file/foolib.cmake.in
diff --git a/test cases/cmake/19 cmake file/meson.build b/test cases/cmake/20 cmake file/meson.build
index 758bbee..758bbee 100644
--- a/test cases/cmake/19 cmake file/meson.build
+++ b/test cases/cmake/20 cmake file/meson.build
diff --git a/test cases/cmake/19 cmake file/test.json b/test cases/cmake/20 cmake file/test.json
index a8c4ba3..a8c4ba3 100644
--- a/test cases/cmake/19 cmake file/test.json
+++ b/test cases/cmake/20 cmake file/test.json
diff --git a/test cases/failing/107 number in combo/meson.build b/test cases/failing/107 number in combo/meson.build
new file mode 100644
index 0000000..1a647df
--- /dev/null
+++ b/test cases/failing/107 number in combo/meson.build
@@ -0,0 +1 @@
+project('number in combo')
diff --git a/test cases/failing/107 number in combo/nativefile.ini b/test cases/failing/107 number in combo/nativefile.ini
new file mode 100644
index 0000000..55f10fc
--- /dev/null
+++ b/test cases/failing/107 number in combo/nativefile.ini
@@ -0,0 +1,2 @@
+[built-in options]
+optimization = 1
diff --git a/test cases/failing/107 number in combo/test.json b/test cases/failing/107 number in combo/test.json
new file mode 100644
index 0000000..f5aeb4e
--- /dev/null
+++ b/test cases/failing/107 number in combo/test.json
@@ -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\"." }
+ ]
+}
diff --git a/test cases/failing/108 bool in combo/meson.build b/test cases/failing/108 bool in combo/meson.build
new file mode 100644
index 0000000..c5efd67
--- /dev/null
+++ b/test cases/failing/108 bool in combo/meson.build
@@ -0,0 +1 @@
+project('bool in combo')
diff --git a/test cases/failing/108 bool in combo/meson_options.txt b/test cases/failing/108 bool in combo/meson_options.txt
new file mode 100644
index 0000000..0c8f5de
--- /dev/null
+++ b/test cases/failing/108 bool in combo/meson_options.txt
@@ -0,0 +1,5 @@
+option(
+ 'opt',
+ type : 'combo',
+ choices : ['true', 'false']
+)
diff --git a/test cases/failing/108 bool in combo/nativefile.ini b/test cases/failing/108 bool in combo/nativefile.ini
new file mode 100644
index 0000000..b423957
--- /dev/null
+++ b/test cases/failing/108 bool in combo/nativefile.ini
@@ -0,0 +1,2 @@
+[project options]
+opt = true
diff --git a/test cases/failing/108 bool in combo/test.json b/test cases/failing/108 bool in combo/test.json
new file mode 100644
index 0000000..729ad3d
--- /dev/null
+++ b/test cases/failing/108 bool in combo/test.json
@@ -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\"." }
+ ]
+}
diff --git a/test cases/unit/76 pkgconfig prefixes/client/client.c b/test cases/unit/77 pkgconfig prefixes/client/client.c
index be9bead..be9bead 100644
--- a/test cases/unit/76 pkgconfig prefixes/client/client.c
+++ b/test cases/unit/77 pkgconfig prefixes/client/client.c
diff --git a/test cases/unit/76 pkgconfig prefixes/client/meson.build b/test cases/unit/77 pkgconfig prefixes/client/meson.build
index 491937b..491937b 100644
--- a/test cases/unit/76 pkgconfig prefixes/client/meson.build
+++ b/test cases/unit/77 pkgconfig prefixes/client/meson.build
diff --git a/test cases/unit/76 pkgconfig prefixes/val1/meson.build b/test cases/unit/77 pkgconfig prefixes/val1/meson.build
index cc63e31..cc63e31 100644
--- a/test cases/unit/76 pkgconfig prefixes/val1/meson.build
+++ b/test cases/unit/77 pkgconfig prefixes/val1/meson.build
diff --git a/test cases/unit/76 pkgconfig prefixes/val1/val1.c b/test cases/unit/77 pkgconfig prefixes/val1/val1.c
index 591e521..591e521 100644
--- a/test cases/unit/76 pkgconfig prefixes/val1/val1.c
+++ b/test cases/unit/77 pkgconfig prefixes/val1/val1.c
diff --git a/test cases/unit/76 pkgconfig prefixes/val1/val1.h b/test cases/unit/77 pkgconfig prefixes/val1/val1.h
index 6bd435e..6bd435e 100644
--- a/test cases/unit/76 pkgconfig prefixes/val1/val1.h
+++ b/test cases/unit/77 pkgconfig prefixes/val1/val1.h
diff --git a/test cases/unit/76 pkgconfig prefixes/val2/meson.build b/test cases/unit/77 pkgconfig prefixes/val2/meson.build
index ce69481..ce69481 100644
--- a/test cases/unit/76 pkgconfig prefixes/val2/meson.build
+++ b/test cases/unit/77 pkgconfig prefixes/val2/meson.build
diff --git a/test cases/unit/76 pkgconfig prefixes/val2/val2.c b/test cases/unit/77 pkgconfig prefixes/val2/val2.c
index d7d4857..d7d4857 100644
--- a/test cases/unit/76 pkgconfig prefixes/val2/val2.c
+++ b/test cases/unit/77 pkgconfig prefixes/val2/val2.c
diff --git a/test cases/unit/76 pkgconfig prefixes/val2/val2.h b/test cases/unit/77 pkgconfig prefixes/val2/val2.h
index 995023d..995023d 100644
--- a/test cases/unit/76 pkgconfig prefixes/val2/val2.h
+++ b/test cases/unit/77 pkgconfig prefixes/val2/val2.h
diff --git a/test cases/unit/76 subdir libdir/meson.build b/test cases/unit/78 subdir libdir/meson.build
index 5099c91..5099c91 100644
--- a/test cases/unit/76 subdir libdir/meson.build
+++ b/test cases/unit/78 subdir libdir/meson.build
diff --git a/test cases/unit/76 subdir libdir/subprojects/flub/meson.build b/test cases/unit/78 subdir libdir/subprojects/flub/meson.build
index 7bfd2c5..7bfd2c5 100644
--- a/test cases/unit/76 subdir libdir/subprojects/flub/meson.build
+++ b/test cases/unit/78 subdir libdir/subprojects/flub/meson.build
diff --git a/test cases/unit/79 user options for subproject/75 user options for subproject/.gitignore b/test cases/unit/79 user options for subproject/75 user options for subproject/.gitignore
new file mode 100644
index 0000000..4976afc
--- /dev/null
+++ b/test cases/unit/79 user options for subproject/75 user options for subproject/.gitignore
@@ -0,0 +1 @@
+subprojects/*
diff --git a/test cases/unit/79 user options for subproject/75 user options for subproject/meson.build b/test cases/unit/79 user options for subproject/75 user options for subproject/meson.build
new file mode 100644
index 0000000..0bc395b
--- /dev/null
+++ b/test cases/unit/79 user options for subproject/75 user options for subproject/meson.build
@@ -0,0 +1,3 @@
+project('user option for subproject')
+
+p = subproject('sub')
diff --git a/test cases/unit/77 global-rpath/meson.build b/test cases/unit/80 global-rpath/meson.build
index c67d9e0..c67d9e0 100644
--- a/test cases/unit/77 global-rpath/meson.build
+++ b/test cases/unit/80 global-rpath/meson.build
diff --git a/test cases/unit/77 global-rpath/rpathified.cpp b/test cases/unit/80 global-rpath/rpathified.cpp
index 3788906..3788906 100644
--- a/test cases/unit/77 global-rpath/rpathified.cpp
+++ b/test cases/unit/80 global-rpath/rpathified.cpp
diff --git a/test cases/unit/77 global-rpath/yonder/meson.build b/test cases/unit/80 global-rpath/yonder/meson.build
index e32f383..e32f383 100644
--- a/test cases/unit/77 global-rpath/yonder/meson.build
+++ b/test cases/unit/80 global-rpath/yonder/meson.build
diff --git a/test cases/unit/77 global-rpath/yonder/yonder.cpp b/test cases/unit/80 global-rpath/yonder/yonder.cpp
index b182d34..b182d34 100644
--- a/test cases/unit/77 global-rpath/yonder/yonder.cpp
+++ b/test cases/unit/80 global-rpath/yonder/yonder.cpp
diff --git a/test cases/unit/77 global-rpath/yonder/yonder.h b/test cases/unit/80 global-rpath/yonder/yonder.h
index 9d9ad16..9d9ad16 100644
--- a/test cases/unit/77 global-rpath/yonder/yonder.h
+++ b/test cases/unit/80 global-rpath/yonder/yonder.h
diff --git a/test cases/unit/78 wrap-git/meson.build b/test cases/unit/81 wrap-git/meson.build
index b0af30a..b0af30a 100644
--- a/test cases/unit/78 wrap-git/meson.build
+++ b/test cases/unit/81 wrap-git/meson.build
diff --git a/test cases/unit/78 wrap-git/subprojects/packagefiles/wrap_git_builddef/meson.build b/test cases/unit/81 wrap-git/subprojects/packagefiles/wrap_git_builddef/meson.build
index 2570f77..2570f77 100644
--- a/test cases/unit/78 wrap-git/subprojects/packagefiles/wrap_git_builddef/meson.build
+++ b/test cases/unit/81 wrap-git/subprojects/packagefiles/wrap_git_builddef/meson.build
diff --git a/test cases/unit/78 wrap-git/subprojects/wrap_git_upstream/main.c b/test cases/unit/81 wrap-git/subprojects/wrap_git_upstream/main.c
index 8488f4e..8488f4e 100644
--- a/test cases/unit/78 wrap-git/subprojects/wrap_git_upstream/main.c
+++ b/test cases/unit/81 wrap-git/subprojects/wrap_git_upstream/main.c