aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mesonbuild/ast/introspection.py12
-rw-r--r--mesonbuild/backend/backends.py54
-rw-r--r--mesonbuild/backend/ninjabackend.py70
-rw-r--r--mesonbuild/backend/vs2010backend.py41
-rw-r--r--mesonbuild/backend/xcodebackend.py2
-rw-r--r--mesonbuild/build.py47
-rw-r--r--mesonbuild/cmake/executor.py4
-rw-r--r--mesonbuild/cmake/interpreter.py10
-rw-r--r--mesonbuild/compilers/c.py142
-rw-r--r--mesonbuild/compilers/compilers.py133
-rw-r--r--mesonbuild/compilers/cpp.py204
-rw-r--r--mesonbuild/compilers/cuda.py21
-rw-r--r--mesonbuild/compilers/d.py12
-rw-r--r--mesonbuild/compilers/fortran.py42
-rw-r--r--mesonbuild/compilers/mixins/arm.py7
-rw-r--r--mesonbuild/compilers/mixins/clang.py7
-rw-r--r--mesonbuild/compilers/mixins/clike.py7
-rw-r--r--mesonbuild/compilers/mixins/elbrus.py6
-rw-r--r--mesonbuild/compilers/mixins/emscripten.py8
-rw-r--r--mesonbuild/compilers/mixins/gnu.py14
-rw-r--r--mesonbuild/compilers/mixins/intel.py5
-rw-r--r--mesonbuild/compilers/mixins/islinker.py6
-rw-r--r--mesonbuild/compilers/mixins/pgi.py3
-rw-r--r--mesonbuild/compilers/mixins/visualstudio.py2
-rw-r--r--mesonbuild/compilers/objc.py2
-rw-r--r--mesonbuild/compilers/objcpp.py2
-rw-r--r--mesonbuild/compilers/rust.py21
-rw-r--r--mesonbuild/compilers/swift.py2
-rw-r--r--mesonbuild/compilers/vala.py8
-rw-r--r--mesonbuild/coredata.py545
-rw-r--r--mesonbuild/dependencies/base.py15
-rw-r--r--mesonbuild/dependencies/boost.py6
-rw-r--r--mesonbuild/dependencies/ui.py6
-rw-r--r--mesonbuild/environment.py124
-rw-r--r--mesonbuild/interpreter.py103
-rw-r--r--mesonbuild/linkers.py12
-rw-r--r--mesonbuild/mcompile.py2
-rw-r--r--mesonbuild/mconf.py77
-rw-r--r--mesonbuild/mesonlib.py272
-rw-r--r--mesonbuild/mintro.py80
-rw-r--r--mesonbuild/modules/cmake.py6
-rw-r--r--mesonbuild/modules/gnome.py24
-rw-r--r--mesonbuild/modules/hotdoc.py2
-rw-r--r--mesonbuild/modules/i18n.py2
-rw-r--r--mesonbuild/modules/pkgconfig.py10
-rw-r--r--mesonbuild/modules/python.py2
-rw-r--r--mesonbuild/modules/unstable_external_project.py7
-rw-r--r--mesonbuild/msetup.py2
-rw-r--r--mesonbuild/munstable_coredata.py4
-rw-r--r--mesonbuild/optinterpreter.py7
-rw-r--r--mesonbuild/rewriter.py13
-rw-r--r--mesonbuild/scripts/regen_checker.py3
-rwxr-xr-xrun_tests.py3
-rwxr-xr-xrun_unittests.py202
54 files changed, 1341 insertions, 1082 deletions
diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py
index eca869f..97ebb5a 100644
--- a/mesonbuild/ast/introspection.py
+++ b/mesonbuild/ast/introspection.py
@@ -19,7 +19,7 @@ from .interpreter import AstInterpreter
from .visitor import AstVisitor
from .. import compilers, environment, mesonlib, optinterpreter
from .. import coredata as cdata
-from ..mesonlib import MachineChoice
+from ..mesonlib import MachineChoice, OptionKey
from ..interpreterbase import InvalidArguments, TYPE_nvar
from ..build import BuildTarget, Executable, Jar, SharedLibrary, SharedModule, StaticLibrary
from ..mparser import BaseNode, ArithmeticNode, ArrayNode, ElementaryNode, IdNode, FunctionNode, StringNode
@@ -65,7 +65,7 @@ class IntrospectionInterpreter(AstInterpreter):
self.coredata = self.environment.get_coredata()
self.option_file = os.path.join(self.source_root, self.subdir, 'meson_options.txt')
self.backend = backend
- self.default_options = {'backend': self.backend}
+ self.default_options = {OptionKey('backend'): self.backend}
self.project_data = {} # type: T.Dict[str, T.Any]
self.targets = [] # type: T.List[T.Dict[str, T.Any]]
self.dependencies = [] # type: T.List[T.Dict[str, T.Any]]
@@ -103,11 +103,11 @@ class IntrospectionInterpreter(AstInterpreter):
if os.path.exists(self.option_file):
oi = optinterpreter.OptionInterpreter(self.subproject)
oi.process(self.option_file)
- self.coredata.merge_user_options(oi.options)
+ self.coredata.update_project_options(oi.options)
def_opts = self.flatten_args(kwargs.get('default_options', []))
_project_default_options = mesonlib.stringlistify(def_opts)
- self.project_default_options = cdata.create_options_dict(_project_default_options)
+ self.project_default_options = cdata.create_options_dict(_project_default_options, self.subproject)
self.default_options.update(self.project_default_options)
self.coredata.set_default_options(self.default_options, self.subproject, self.environment)
@@ -125,7 +125,7 @@ class IntrospectionInterpreter(AstInterpreter):
self.do_subproject(i)
self.coredata.init_backend_options(self.backend)
- options = {k: v for k, v in self.environment.raw_options.items() if k.startswith('backend_')}
+ options = {k: v for k, v in self.environment.options.items() if k.is_backend()}
self.coredata.set_options(options)
self._add_languages(proj_langs, MachineChoice.HOST)
@@ -269,7 +269,7 @@ class IntrospectionInterpreter(AstInterpreter):
return new_target
def build_library(self, node: BaseNode, args: T.List[TYPE_nvar], kwargs: T.Dict[str, TYPE_nvar]) -> T.Optional[T.Dict[str, T.Any]]:
- default_library = self.coredata.get_builtin_option('default_library')
+ default_library = self.coredata.get_option(OptionKey('default_library'))
if default_library == 'shared':
return self.build_target(node, args, kwargs, SharedLibrary)
elif default_library == 'static':
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index b9f175a..ec3aca6 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -31,11 +31,13 @@ from .. import mesonlib
from .. import mlog
from ..compilers import languages_using_ldflags
from ..mesonlib import (
- File, MachineChoice, MesonException, OrderedSet, OptionOverrideProxy,
- classify_unity_sources, unholder
+ File, MachineChoice, MesonException, OptionType, OrderedSet, OptionOverrideProxy,
+ classify_unity_sources, unholder, OptionKey
)
if T.TYPE_CHECKING:
+ from ..arglist import CompilerArgs
+ from ..compilers import Compiler
from ..interpreter import Interpreter, Test
@@ -209,24 +211,21 @@ class Backend:
def get_target_filename_abs(self, target):
return os.path.join(self.environment.get_build_dir(), self.get_target_filename(target))
- def get_base_options_for_target(self, target):
+ def get_base_options_for_target(self, target: build.BuildTarget) -> OptionOverrideProxy:
return OptionOverrideProxy(target.option_overrides_base,
- self.environment.coredata.builtins,
- self.environment.coredata.base_options)
+ {k: v for k, v in self.environment.coredata.options.items()
+ if k.type in {OptionType.BASE, OptionType.BUILTIN}})
- def get_compiler_options_for_target(self, target):
- comp_reg = self.environment.coredata.compiler_options[target.for_machine]
+ def get_compiler_options_for_target(self, target: build.BuildTarget) -> OptionOverrideProxy:
+ comp_reg = {k: v for k, v in self.environment.coredata.options.items() if k.is_compiler()}
comp_override = target.option_overrides_compiler
- return {
- lang: OptionOverrideProxy(comp_override[lang], comp_reg[lang])
- for lang in set(comp_reg.keys()) | set(comp_override.keys())
- }
+ return OptionOverrideProxy(comp_override, comp_reg)
- def get_option_for_target(self, option_name, target):
+ def get_option_for_target(self, option_name: 'OptionKey', target: build.BuildTarget):
if option_name in target.option_overrides_base:
override = target.option_overrides_base[option_name]
return self.environment.coredata.validate_option_value(option_name, override)
- return self.environment.coredata.get_builtin_option(option_name, target.subproject)
+ return self.environment.coredata.get_option(option_name.evolve(subproject=target.subproject))
def get_target_filename_for_linking(self, target):
# On some platforms (msvc for instance), the file that is used for
@@ -251,7 +250,7 @@ class Backend:
@lru_cache(maxsize=None)
def get_target_dir(self, target):
- if self.environment.coredata.get_builtin_option('layout') == 'mirror':
+ if self.environment.coredata.get_option(OptionKey('layout')) == 'mirror':
dirname = target.get_subdir()
else:
dirname = 'meson-out'
@@ -300,7 +299,7 @@ class Backend:
abs_files = []
result = []
compsrcs = classify_unity_sources(target.compilers.values(), unity_src)
- unity_size = self.get_option_for_target('unity_size', target)
+ unity_size = self.get_option_for_target(OptionKey('unity_size'), target)
def init_language_file(suffix, unity_file_number):
unity_src = self.get_unity_source_file(target, suffix, unity_file_number)
@@ -542,7 +541,7 @@ class Backend:
return paths
def determine_rpath_dirs(self, target):
- if self.environment.coredata.get_builtin_option('layout') == 'mirror':
+ if self.environment.coredata.get_option(OptionKey('layout')) == 'mirror':
result = target.get_link_dep_subdirs()
else:
result = OrderedSet()
@@ -621,7 +620,8 @@ class Backend:
if self.is_unity(extobj.target):
compsrcs = classify_unity_sources(extobj.target.compilers.values(), sources)
sources = []
- unity_size = self.get_option_for_target('unity_size', extobj.target)
+ unity_size = self.get_option_for_target(OptionKey('unity_size'), extobj.target)
+
for comp, srcs in compsrcs.items():
for i in range(len(srcs) // unity_size + 1):
osrc = self.get_unity_source_file(extobj.target,
@@ -672,13 +672,13 @@ class Backend:
return extra_args
- def generate_basic_compiler_args(self, target, compiler, no_warn_args=False):
+ def generate_basic_compiler_args(self, target: build.BuildTarget, compiler: 'Compiler', no_warn_args: bool = False) -> 'CompilerArgs':
# Create an empty commands list, and start adding arguments from
# various sources in the order in which they must override each other
# starting from hard-coded defaults followed by build options and so on.
commands = compiler.compiler_args()
- copt_proxy = self.get_compiler_options_for_target(target)[compiler.language]
+ copt_proxy = self.get_compiler_options_for_target(target)
# First, the trivial ones that are impossible to override.
#
# Add -nostdinc/-nostdinc++ if needed; can't be overridden
@@ -690,20 +690,20 @@ class Backend:
if no_warn_args:
commands += compiler.get_no_warn_args()
else:
- commands += compiler.get_warn_args(self.get_option_for_target('warning_level', target))
+ commands += compiler.get_warn_args(self.get_option_for_target(OptionKey('warning_level'), target))
# Add -Werror if werror=true is set in the build options set on the
# command-line or default_options inside project(). This only sets the
# action to be done for warnings if/when they are emitted, so it's ok
# to set it after get_no_warn_args() or get_warn_args().
- if self.get_option_for_target('werror', target):
+ if self.get_option_for_target(OptionKey('werror'), target):
commands += compiler.get_werror_args()
# Add compile args for c_* or cpp_* build options set on the
# command-line or default_options inside project().
commands += compiler.get_option_compile_args(copt_proxy)
# Add buildtype args: optimization level, debugging, etc.
- commands += compiler.get_buildtype_args(self.get_option_for_target('buildtype', target))
- commands += compiler.get_optimization_args(self.get_option_for_target('optimization', target))
- commands += compiler.get_debug_args(self.get_option_for_target('debug', target))
+ commands += compiler.get_buildtype_args(self.get_option_for_target(OptionKey('buildtype'), target))
+ commands += compiler.get_optimization_args(self.get_option_for_target(OptionKey('optimization'), target))
+ commands += compiler.get_debug_args(self.get_option_for_target(OptionKey('debug'), target))
# MSVC debug builds have /ZI argument by default and /Zi is added with debug flag
# /ZI needs to be removed in that case to avoid cl's warning to that effect (D9025 : overriding '/ZI' with '/Zi')
if ('/ZI' in commands) and ('/Zi' in commands):
@@ -1022,7 +1022,7 @@ class Backend:
return libs
def is_unity(self, target):
- optval = self.get_option_for_target('unity', target)
+ optval = self.get_option_for_target(OptionKey('unity'), target)
if optval == 'on' or (optval == 'subprojects' and target.subproject != ''):
return True
return False
@@ -1183,7 +1183,7 @@ class Backend:
self.environment.get_build_dir(),
self.environment.get_prefix(),
strip_bin,
- self.environment.coredata.get_builtin_option('install_umask'),
+ self.environment.coredata.get_option(OptionKey('install_umask')),
self.environment.get_build_command() + ['introspect'],
self.environment.coredata.version)
self.generate_depmf_install(d)
@@ -1228,7 +1228,7 @@ class Backend:
#
# TODO: Create GNUStrip/AppleStrip/etc. hierarchy for more
# fine-grained stripping of static archives.
- should_strip = not isinstance(t, build.StaticLibrary) and self.get_option_for_target('strip', t)
+ should_strip = not isinstance(t, build.StaticLibrary) and self.get_option_for_target(OptionKey('strip'), t)
# Install primary build output (library/executable/jar, etc)
# Done separately because of strip/aliases/rpath
if outdirs[0] is not False:
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index b847c2c..d66708c 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -43,11 +43,14 @@ from ..mesonlib import (
File, LibType, MachineChoice, MesonException, OrderedSet, PerMachine,
ProgressBar, quote_arg, unholder,
)
-from ..mesonlib import get_compiler_for_source, has_path_sep
+from ..mesonlib import get_compiler_for_source, has_path_sep, OptionKey
from .backends import CleanTrees
from ..build import InvalidArguments
from ..interpreter import Interpreter
+if T.TYPE_CHECKING:
+ from ..linkers import StaticLinker
+
FORTRAN_INCLUDE_PAT = r"^\s*#?include\s*['\"](\w+\.\w+)['\"]"
FORTRAN_MODULE_PAT = r"^\s*\bmodule\b\s+(\w+)\s*(?:!+.*)*$"
FORTRAN_SUBMOD_PAT = r"^\s*\bsubmodule\b\s*\((\w+:?\w+)\)\s*(\w+)"
@@ -514,7 +517,7 @@ int dummy;
outfile.write('# Do not edit by hand.\n\n')
outfile.write('ninja_required_version = 1.8.2\n\n')
- num_pools = self.environment.coredata.backend_options['backend_max_links'].value
+ num_pools = self.environment.coredata.options[OptionKey('backend_max_links')].value
if num_pools > 0:
outfile.write('''pool link_pool
depth = {}
@@ -534,8 +537,9 @@ int dummy;
self.add_build_comment(NinjaComment('Install rules'))
self.generate_install()
self.generate_dist()
- if 'b_coverage' in self.environment.coredata.base_options and \
- self.environment.coredata.base_options['b_coverage'].value:
+ key = OptionKey('b_coverage')
+ if (key in self.environment.coredata.options and
+ self.environment.coredata.options[key].value):
self.add_build_comment(NinjaComment('Coverage rules'))
self.generate_coverage_rules()
self.add_build_comment(NinjaComment('Suffix'))
@@ -811,7 +815,7 @@ int dummy;
source2object[s] = o
obj_list.append(o)
- use_pch = self.environment.coredata.base_options.get('b_pch', False)
+ use_pch = self.environment.coredata.options.get(OptionKey('b_pch'))
if use_pch and target.has_pch():
pch_objects = self.generate_pch(target, header_deps=header_deps)
else:
@@ -890,7 +894,7 @@ int dummy;
cpp = target.compilers['cpp']
if cpp.get_id() != 'msvc':
return False
- if self.environment.coredata.compiler_options[target.for_machine]['cpp']['std'] != 'c++latest':
+ if self.environment.coredata.options[OptionKey('std', machine=target.for_machine, lang='cpp')] != 'latest':
return False
if not mesonlib.current_vs_supports_modules():
return False
@@ -1124,9 +1128,9 @@ int dummy;
def generate_tests(self):
self.serialize_tests()
cmd = self.environment.get_build_command(True) + ['test', '--no-rebuild']
- if not self.environment.coredata.get_builtin_option('stdsplit'):
+ if not self.environment.coredata.get_option(OptionKey('stdsplit')):
cmd += ['--no-stdsplit']
- if self.environment.coredata.get_builtin_option('errorlogs'):
+ if self.environment.coredata.get_option(OptionKey('errorlogs')):
cmd += ['--print-errorlogs']
elem = NinjaBuildElement(self.all_outputs, 'meson-test', 'CUSTOM_COMMAND', ['all', 'PHONY'])
elem.add_item('COMMAND', cmd)
@@ -1294,8 +1298,8 @@ int dummy;
args.append(a)
return args, deps
- def generate_cs_target(self, target):
- buildtype = self.get_option_for_target('buildtype', target)
+ def generate_cs_target(self, target: build.BuildTarget):
+ buildtype = self.get_option_for_target(OptionKey('buildtype'), target)
fname = target.get_filename()
outname_rel = os.path.join(self.get_target_dir(target), fname)
src_list = target.get_sources()
@@ -1304,8 +1308,8 @@ int dummy;
deps = []
commands = compiler.compiler_args(target.extra_args.get('cs', []))
commands += compiler.get_buildtype_args(buildtype)
- commands += compiler.get_optimization_args(self.get_option_for_target('optimization', target))
- commands += compiler.get_debug_args(self.get_option_for_target('debug', target))
+ commands += compiler.get_optimization_args(self.get_option_for_target(OptionKey('optimization'), target))
+ commands += compiler.get_debug_args(self.get_option_for_target(OptionKey('debug'), target))
if isinstance(target, build.Executable):
commands.append('-target:exe')
elif isinstance(target, build.SharedLibrary):
@@ -1346,7 +1350,7 @@ int dummy;
def determine_single_java_compile_args(self, target, compiler):
args = []
- args += compiler.get_buildtype_args(self.get_option_for_target('buildtype', target))
+ args += compiler.get_buildtype_args(self.get_option_for_target(OptionKey('buildtype'), target))
args += self.build.get_global_args(compiler, target.for_machine)
args += self.build.get_project_args(compiler, target.subproject, target.for_machine)
args += target.get_java_args()
@@ -1509,7 +1513,7 @@ int dummy;
valac_outputs.append(vala_c_file)
args = self.generate_basic_compiler_args(target, valac)
- args += valac.get_colorout_args(self.environment.coredata.base_options.get('b_colorout').value)
+ args += valac.get_colorout_args(self.environment.coredata.options.get(OptionKey('b_colorout')).value)
# Tell Valac to output everything in our private directory. Sadly this
# means it will also preserve the directory components of Vala sources
# found inside the build tree (generated sources).
@@ -1613,12 +1617,12 @@ int dummy;
for a in rustc.linker.get_always_args():
args += ['-C', 'link-arg={}'.format(a)]
- opt_proxy = self.get_compiler_options_for_target(target)[rustc.language]
+ opt_proxy = self.get_compiler_options_for_target(target)
args += ['--crate-name', target.name]
- args += rustc.get_buildtype_args(self.get_option_for_target('buildtype', target))
- args += rustc.get_debug_args(self.get_option_for_target('debug', target))
- args += rustc.get_optimization_args(self.get_option_for_target('optimization', target))
+ args += rustc.get_buildtype_args(self.get_option_for_target(OptionKey('buildtype'), target))
+ args += rustc.get_debug_args(self.get_option_for_target(OptionKey('debug'), target))
+ args += rustc.get_optimization_args(self.get_option_for_target(OptionKey('optimization'), target))
args += rustc.get_option_compile_args(opt_proxy)
args += self.build.get_global_args(rustc, target.for_machine)
args += self.build.get_project_args(rustc, target.subproject, target.for_machine)
@@ -1769,8 +1773,8 @@ int dummy;
raise InvalidArguments('Swift target {} contains a non-swift source file.'.format(target.get_basename()))
os.makedirs(self.get_target_private_dir_abs(target), exist_ok=True)
compile_args = swiftc.get_compile_only_args()
- compile_args += swiftc.get_optimization_args(self.get_option_for_target('optimization', target))
- compile_args += swiftc.get_debug_args(self.get_option_for_target('debug', target))
+ compile_args += swiftc.get_optimization_args(self.get_option_for_target(OptionKey('optimization'), target))
+ compile_args += swiftc.get_debug_args(self.get_option_for_target(OptionKey('debug'), target))
compile_args += swiftc.get_module_args(module_name)
compile_args += self.build.get_project_args(swiftc, target.subproject, target.for_machine)
compile_args += self.build.get_global_args(swiftc, target.for_machine)
@@ -1846,7 +1850,7 @@ int dummy;
self.create_target_source_introspection(target, swiftc, compile_args + header_imports + module_includes, relsrc, rel_generated)
def generate_static_link_rules(self):
- num_pools = self.environment.coredata.backend_options['backend_max_links'].value
+ num_pools = self.environment.coredata.options[OptionKey('backend_max_links')].value
if 'java' in self.environment.coredata.compilers.host:
self.generate_java_link()
for for_machine in MachineChoice:
@@ -1879,7 +1883,7 @@ int dummy;
extra=pool))
def generate_dynamic_link_rules(self):
- num_pools = self.environment.coredata.backend_options['backend_max_links'].value
+ num_pools = self.environment.coredata.options[OptionKey('backend_max_links')].value
for for_machine in MachineChoice:
complist = self.environment.coredata.compilers[for_machine]
for langname, compiler in complist.items():
@@ -2495,7 +2499,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
commands += self.get_compile_debugfile_args(compiler, target, rel_obj)
# PCH handling
- if self.environment.coredata.base_options.get('b_pch', False):
+ if self.environment.coredata.options.get(OptionKey('b_pch')):
commands += self.get_pch_include_args(compiler, target)
pchlist = target.get_pch(compiler.language)
else:
@@ -2695,7 +2699,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
commands += linker.get_pie_link_args()
elif isinstance(target, build.SharedLibrary):
if isinstance(target, build.SharedModule):
- options = self.environment.coredata.base_options
+ options = self.environment.coredata.options
commands += linker.get_std_shared_module_link_args(options)
else:
commands += linker.get_std_shared_lib_link_args()
@@ -2831,7 +2835,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
self.add_build(elem)
return [prelink_name]
- def generate_link(self, target, outname, obj_list, linker, extra_args=None, stdlib_args=None):
+ def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T.Union['Compiler', 'StaticLinker'], extra_args=None, stdlib_args=None):
extra_args = extra_args if extra_args is not None else []
stdlib_args = stdlib_args if stdlib_args is not None else []
implicit_outs = []
@@ -2866,9 +2870,9 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
# Add things like /NOLOGO; usually can't be overridden
commands += linker.get_linker_always_args()
# Add buildtype linker args: optimization level, etc.
- commands += linker.get_buildtype_linker_args(self.get_option_for_target('buildtype', target))
+ commands += linker.get_buildtype_linker_args(self.get_option_for_target(OptionKey('buildtype'), target))
# Add /DEBUG and the pdb filename when using MSVC
- if self.get_option_for_target('debug', target):
+ if self.get_option_for_target(OptionKey('debug'), target):
commands += self.get_link_debugfile_args(linker, target, outname)
debugfile = self.get_link_debugfile_name(linker, target, outname)
if debugfile is not None:
@@ -2928,14 +2932,14 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
# to be after all internal and external libraries so that unresolved
# symbols from those can be found here. This is needed when the
# *_winlibs that we want to link to are static mingw64 libraries.
- if hasattr(linker, 'get_language'):
+ if isinstance(linker, Compiler):
# The static linker doesn't know what language it is building, so we
# don't know what option. Fortunately, it doesn't care to see the
# language-specific options either.
#
# We shouldn't check whether we are making a static library, because
# in the LTO case we do use a real compiler here.
- commands += linker.get_option_link_args(self.environment.coredata.compiler_options[target.for_machine][linker.get_language()])
+ commands += linker.get_option_link_args(self.environment.coredata.options)
dep_targets = []
dep_targets.extend(self.guess_external_link_dependencies(linker, target, commands, internal))
@@ -3029,8 +3033,8 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
def get_user_option_args(self):
cmds = []
- for (k, v) in self.environment.coredata.user_options.items():
- cmds.append('-D' + k + '=' + (v.value if isinstance(v.value, str) else str(v.value).lower()))
+ for (k, v) in self.environment.coredata.options.items():
+ cmds.append('-D' + str(k) + '=' + (v.value if isinstance(v.value, str) else str(v.value).lower()))
# The order of these arguments must be the same between runs of Meson
# to ensure reproducible output. The order we pass them shouldn't
# affect behavior in any other way.
@@ -3152,8 +3156,8 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
if ctlist:
elem.add_dep(self.generate_custom_target_clean(ctlist))
- if 'b_coverage' in self.environment.coredata.base_options and \
- self.environment.coredata.base_options['b_coverage'].value:
+ if OptionKey('b_coverage') in self.environment.coredata.options and \
+ self.environment.coredata.options[OptionKey('b_coverage')].value:
self.generate_gcov_clean()
elem.add_dep('clean-gcda')
elem.add_dep('clean-gcno')
diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py
index 6d81e69..6e070a7 100644
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -28,7 +28,7 @@ from .. import mlog
from .. import compilers
from ..interpreter import Interpreter
from ..mesonlib import (
- MesonException, File, python_command, replace_if_different
+ MesonException, File, python_command, replace_if_different, OptionKey,
)
from ..environment import Environment, build_filename
@@ -181,9 +181,9 @@ class Vs2010Backend(backends.Backend):
self.platform = 'ARM'
else:
raise MesonException('Unsupported Visual Studio platform: ' + target_machine)
- self.buildtype = self.environment.coredata.get_builtin_option('buildtype')
- self.optimization = self.environment.coredata.get_builtin_option('optimization')
- self.debug = self.environment.coredata.get_builtin_option('debug')
+ self.buildtype = self.environment.coredata.get_option(OptionKey('buildtype'))
+ self.optimization = self.environment.coredata.get_option(OptionKey('optimization'))
+ self.debug = self.environment.coredata.get_option(OptionKey('debug'))
sln_filename = os.path.join(self.environment.get_build_dir(), self.build.project_name + '.sln')
projlist = self.generate_projects()
self.gen_testproj('RUN_TESTS', os.path.join(self.environment.get_build_dir(), 'RUN_TESTS.vcxproj'))
@@ -316,7 +316,7 @@ class Vs2010Backend(backends.Backend):
prj_templ = 'Project("{%s}") = "%s", "%s", "{%s}"\n'
for prj in projlist:
coredata = self.environment.coredata
- if coredata.get_builtin_option('layout') == 'mirror':
+ if coredata.get_option(OptionKey('layout')) == 'mirror':
self.generate_solution_dirs(ofile, prj[1].parents)
target = self.build.targets[prj[0]]
lang = 'default'
@@ -403,7 +403,7 @@ class Vs2010Backend(backends.Backend):
replace_if_different(sln_filename, sln_filename_tmp)
def generate_projects(self):
- startup_project = self.environment.coredata.backend_options['backend_startup_project'].value
+ startup_project = self.environment.coredata.options[OptionKey('backend_startup_project')].value
projlist = []
startup_idx = 0
for (i, (name, target)) in enumerate(self.build.targets.items()):
@@ -785,7 +785,7 @@ class Vs2010Backend(backends.Backend):
build_args += compiler.get_optimization_args(self.optimization)
build_args += compiler.get_debug_args(self.debug)
buildtype_link_args = compiler.get_buildtype_linker_args(self.buildtype)
- vscrt_type = self.environment.coredata.base_options['b_vscrt']
+ vscrt_type = self.environment.coredata.options[OptionKey('b_vscrt')]
project_name = target.name
target_name = target.name
root = ET.Element('Project', {'DefaultTargets': "Build",
@@ -878,7 +878,7 @@ class Vs2010Backend(backends.Backend):
# Exception handling has to be set in the xml in addition to the "AdditionalOptions" because otherwise
# cl will give warning D9025: overriding '/Ehs' with cpp_eh value
if 'cpp' in target.compilers:
- eh = self.environment.coredata.compiler_options[target.for_machine]['cpp']['eh']
+ eh = self.environment.coredata.options[OptionKey('eh', machine=target.for_machine, lang='cpp')]
if eh.value == 'a':
ET.SubElement(clconf, 'ExceptionHandling').text = 'Async'
elif eh.value == 's':
@@ -926,7 +926,7 @@ class Vs2010Backend(backends.Backend):
file_args[l] += compilers.get_base_compile_args(
self.get_base_options_for_target(target), comp)
file_args[l] += comp.get_option_compile_args(
- self.environment.coredata.compiler_options[target.for_machine][comp.language])
+ self.environment.coredata.options)
# Add compile args added using add_project_arguments()
for l, args in self.build.projects_args[target.for_machine].get(target.subproject, {}).items():
@@ -940,10 +940,8 @@ class Vs2010Backend(backends.Backend):
# Compile args added from the env or cross file: CFLAGS/CXXFLAGS, etc. We want these
# to override all the defaults, but not the per-target compile args.
for l in file_args.keys():
- opts = self.environment.coredata.compiler_options[target.for_machine][l]
- k = 'args'
- if k in opts:
- file_args[l] += opts[k].value
+ opts = self.environment.coredata.options[OptionKey('args', machine=target.for_machine, lang=l)]
+ file_args[l] += opts.value
for args in file_args.values():
# This is where Visual Studio will insert target_args, target_defines,
# etc, which are added later from external deps (see below).
@@ -1050,9 +1048,9 @@ class Vs2010Backend(backends.Backend):
ET.SubElement(clconf, 'PreprocessorDefinitions').text = ';'.join(target_defines)
ET.SubElement(clconf, 'FunctionLevelLinking').text = 'true'
# Warning level
- warning_level = self.get_option_for_target('warning_level', target)
+ warning_level = self.get_option_for_target(OptionKey('warning_level'), target)
ET.SubElement(clconf, 'WarningLevel').text = 'Level' + str(1 + int(warning_level))
- if self.get_option_for_target('werror', target):
+ if self.get_option_for_target(OptionKey('werror'), target):
ET.SubElement(clconf, 'TreatWarningAsError').text = 'true'
# Optimization flags
o_flags = split_o_flags_args(build_args)
@@ -1077,7 +1075,7 @@ class Vs2010Backend(backends.Backend):
ET.SubElement(clconf, 'FavorSizeOrSpeed').text = 'Speed'
# Note: SuppressStartupBanner is /NOLOGO and is 'true' by default
pch_sources = {}
- if self.environment.coredata.base_options.get('b_pch', False):
+ if self.environment.coredata.options.get(OptionKey('b_pch')):
for lang in ['c', 'cpp']:
pch = target.get_pch(lang)
if not pch:
@@ -1113,7 +1111,7 @@ class Vs2010Backend(backends.Backend):
ET.SubElement(link, 'GenerateDebugInformation').text = 'false'
if not isinstance(target, build.StaticLibrary):
if isinstance(target, build.SharedModule):
- options = self.environment.coredata.base_options
+ options = self.environment.coredata.options
extra_link_args += compiler.get_std_shared_module_link_args(options)
# Add link args added using add_project_link_arguments()
extra_link_args += self.build.get_project_link_args(compiler, target.subproject, target.for_machine)
@@ -1146,8 +1144,7 @@ class Vs2010Backend(backends.Backend):
# to be after all internal and external libraries so that unresolved
# symbols from those can be found here. This is needed when the
# *_winlibs that we want to link to are static mingw64 libraries.
- extra_link_args += compiler.get_option_link_args(
- self.environment.coredata.compiler_options[compiler.for_machine][comp.language])
+ extra_link_args += compiler.get_option_link_args(self.environment.coredata.options)
(additional_libpaths, additional_links, extra_link_args) = self.split_link_args(extra_link_args.to_native())
# Add more libraries to be linked if needed
@@ -1226,7 +1223,7 @@ class Vs2010Backend(backends.Backend):
# /nologo
ET.SubElement(link, 'SuppressStartupBanner').text = 'true'
# /release
- if not self.environment.coredata.get_builtin_option('debug'):
+ if not self.environment.coredata.get_option(OptionKey('debug')):
ET.SubElement(link, 'SetChecksum').text = 'true'
meson_file_group = ET.SubElement(root, 'ItemGroup')
@@ -1426,9 +1423,9 @@ class Vs2010Backend(backends.Backend):
ET.SubElement(midl, 'ProxyFileName').text = '%(Filename)_p.c'
# FIXME: No benchmarks?
test_command = self.environment.get_build_command() + ['test', '--no-rebuild']
- if not self.environment.coredata.get_builtin_option('stdsplit'):
+ if not self.environment.coredata.get_option(OptionKey('stdsplit')):
test_command += ['--no-stdsplit']
- if self.environment.coredata.get_builtin_option('errorlogs'):
+ if self.environment.coredata.get_option(OptionKey('errorlogs')):
test_command += ['--print-errorlogs']
self.serialize_tests()
self.add_custom_build(root, 'run_tests', '"%s"' % ('" "'.join(test_command)))
diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py
index ef0c956..0e39c65 100644
--- a/mesonbuild/backend/xcodebackend.py
+++ b/mesonbuild/backend/xcodebackend.py
@@ -61,7 +61,7 @@ class XCodeBackend(backends.Backend):
return str(uuid.uuid4()).upper().replace('-', '')[:24]
def get_target_dir(self, target):
- dirname = os.path.join(target.get_subdir(), self.environment.coredata.get_builtin_option('buildtype'))
+ dirname = os.path.join(target.get_subdir(), self.environment.coredata.get_option(mesonlib.OptionKey('buildtype')))
os.makedirs(os.path.join(self.environment.get_build_dir(), dirname), exist_ok=True)
return dirname
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 091bfc8..dacf68b 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from collections import OrderedDict, defaultdict
+from collections import OrderedDict
from functools import lru_cache
import copy
import hashlib
@@ -28,16 +28,18 @@ from . import mlog
from .mesonlib import (
File, MesonException, MachineChoice, PerMachine, OrderedSet, listify,
extract_as_list, typeslistify, stringlistify, classify_unity_sources,
- get_filenames_templates_dict, substitute_values, has_path_sep, unholder
+ get_filenames_templates_dict, substitute_values, has_path_sep, unholder,
+ OptionKey,
)
from .compilers import (
- Compiler, all_languages, is_object, clink_langs, sort_clink, lang_suffixes,
+ Compiler, is_object, clink_langs, sort_clink, lang_suffixes,
is_known_suffix
)
from .linkers import StaticLinker
from .interpreterbase import FeatureNew
if T.TYPE_CHECKING:
+ from .coredata import KeyedOptionDictType, OptionDictType
from .interpreter import Test
from .mesonlib import FileMode, FileOrString
@@ -402,7 +404,7 @@ class EnvironmentVariables:
return env
class Target:
- def __init__(self, name, subdir, subproject, build_by_default, for_machine: MachineChoice):
+ def __init__(self, name: str, subdir: str, subproject: str, build_by_default: bool, for_machine: MachineChoice):
if has_path_sep(name):
# Fix failing test 53 when this becomes an error.
mlog.warning('''Target "{}" has a path separator in its name.
@@ -415,8 +417,8 @@ a hard error in the future.'''.format(name))
self.for_machine = for_machine
self.install = False
self.build_always_stale = False
- self.option_overrides_base = {}
- self.option_overrides_compiler = defaultdict(dict)
+ self.option_overrides_base: T.Dict[OptionKey, str] = {}
+ self.option_overrides_compiler: T.Dict[OptionKey, str] = {}
self.extra_files = [] # type: T.List[File]
if not hasattr(self, 'typename'):
raise RuntimeError('Target type is not set for target class "{}". This is a bug'.format(type(self).__name__))
@@ -497,7 +499,7 @@ a hard error in the future.'''.format(name))
return self.construct_id_from_path(
self.subdir, self.name, self.type_suffix())
- def process_kwargs_base(self, kwargs):
+ def process_kwargs_base(self, kwargs: T.Dict[str, T.Any]) -> None:
if 'build_by_default' in kwargs:
self.build_by_default = kwargs['build_by_default']
if not isinstance(self.build_by_default, bool):
@@ -510,23 +512,22 @@ a hard error in the future.'''.format(name))
option_overrides = self.parse_overrides(kwargs)
for k, v in option_overrides.items():
- if '_' in k:
- lang, k2 = k.split('_', 1)
- if lang in all_languages:
- self.option_overrides_compiler[lang][k2] = v
- continue
+ if k.lang:
+ self.option_overrides_compiler[k.evolve(machine=self.for_machine)] = v
+ continue
self.option_overrides_base[k] = v
- def parse_overrides(self, kwargs) -> dict:
- result = {}
+ @staticmethod
+ def parse_overrides(kwargs: T.Dict[str, T.Any]) -> T.Dict[OptionKey, str]:
+ result: T.Dict[OptionKey, str] = {}
overrides = stringlistify(kwargs.get('override_options', []))
for o in overrides:
if '=' not in o:
raise InvalidArguments('Overrides must be of form "key=value"')
k, v = o.split('=', 1)
- k = k.strip()
+ key = OptionKey.from_string(k.strip())
v = v.strip()
- result[k] = v
+ result[key] = v
return result
def is_linkable_target(self) -> bool:
@@ -544,7 +545,7 @@ class BuildTarget(Target):
def __init__(self, name: str, subdir: str, subproject: str, for_machine: MachineChoice,
sources: T.List[File], objects, environment: environment.Environment, kwargs):
super().__init__(name, subdir, subproject, True, for_machine)
- unity_opt = environment.coredata.get_builtin_option('unity')
+ unity_opt = environment.coredata.get_option(OptionKey('unity'))
self.is_unity = unity_opt == 'on' or (unity_opt == 'subprojects' and subproject != '')
self.environment = environment
self.sources = []
@@ -1064,17 +1065,18 @@ This will become a hard error in a future Meson release.''')
raise InvalidArguments('Invalid value for win_subsystem: {}.'.format(value))
return value
- def _extract_pic_pie(self, kwargs, arg, environment, option):
+ def _extract_pic_pie(self, kwargs, arg: str, environment, option: str):
# Check if we have -fPIC, -fpic, -fPIE, or -fpie in cflags
all_flags = self.extra_args['c'] + self.extra_args['cpp']
if '-f' + arg.lower() in all_flags or '-f' + arg.upper() in all_flags:
mlog.warning("Use the '{}' kwarg instead of passing '{}' manually to {!r}".format(arg, '-f' + arg, self.name))
return True
+ k = OptionKey(option)
if arg in kwargs:
val = kwargs[arg]
- elif option in environment.coredata.base_options:
- val = environment.coredata.base_options[option].value
+ elif k in environment.coredata.options:
+ val = environment.coredata.options[k].value
else:
val = False
@@ -1595,8 +1597,9 @@ class Executable(BuildTarget):
def __init__(self, name: str, subdir: str, subproject: str, for_machine: MachineChoice,
sources: T.List[File], objects, environment: environment.Environment, kwargs):
self.typename = 'executable'
- if 'pie' not in kwargs and 'b_pie' in environment.coredata.base_options:
- kwargs['pie'] = environment.coredata.base_options['b_pie'].value
+ key = OptionKey('b_pie')
+ if 'pie' not in kwargs and key in environment.coredata.options:
+ kwargs['pie'] = environment.coredata.options[key].value
super().__init__(name, subdir, subproject, for_machine, sources, objects, environment, kwargs)
# Unless overridden, executables have no suffix or prefix. Except on
# Windows and with C#/Mono executables where the suffix is 'exe'
diff --git a/mesonbuild/cmake/executor.py b/mesonbuild/cmake/executor.py
index 19971e3..674b854 100644
--- a/mesonbuild/cmake/executor.py
+++ b/mesonbuild/cmake/executor.py
@@ -23,7 +23,7 @@ import re
import os
from .. import mlog
-from ..mesonlib import PerMachine, Popen_safe, version_compare, MachineChoice, is_windows
+from ..mesonlib import PerMachine, Popen_safe, version_compare, MachineChoice, is_windows, OptionKey
from ..envconfig import get_env_var
if T.TYPE_CHECKING:
@@ -62,7 +62,7 @@ class CMakeExecutor:
self.cmakebin = None
return
- self.prefix_paths = self.environment.coredata.builtins_per_machine[self.for_machine]['cmake_prefix_path'].value
+ self.prefix_paths = self.environment.coredata.options[OptionKey('cmake_prefix_path', machine=self.for_machine)].value
env_pref_path_raw = get_env_var(
self.for_machine,
self.environment.is_cross_build(),
diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py
index 8aaeb19..1a533db 100644
--- a/mesonbuild/cmake/interpreter.py
+++ b/mesonbuild/cmake/interpreter.py
@@ -22,7 +22,7 @@ from .executor import CMakeExecutor
from .toolchain import CMakeToolchain, CMakeExecScope
from .traceparser import CMakeTraceParser, CMakeGeneratorTarget
from .. import mlog, mesonlib
-from ..mesonlib import MachineChoice, OrderedSet, version_compare, path_is_in_root, relative_to_if_possible
+from ..mesonlib import MachineChoice, OrderedSet, version_compare, path_is_in_root, relative_to_if_possible, OptionKey
from ..mesondata import mesondata
from ..compilers.compilers import lang_suffixes, header_suffixes, obj_suffixes, lib_suffixes, is_header
from enum import Enum
@@ -383,7 +383,7 @@ class ConverterTarget:
cfgs += [x for x in tgt.properties['CONFIGURATIONS'] if x]
cfg = cfgs[0]
- is_debug = self.env.coredata.get_builtin_option('debug');
+ is_debug = self.env.coredata.get_option(OptionKey('debug'));
if is_debug:
if 'DEBUG' in cfgs:
cfg = 'DEBUG'
@@ -598,10 +598,10 @@ class ConverterTarget:
@lru_cache(maxsize=None)
def _all_lang_stds(self, lang: str) -> T.List[str]:
- lang_opts = self.env.coredata.compiler_options.build.get(lang, None)
- if not lang_opts or 'std' not in lang_opts:
+ try:
+ res = self.env.coredata.options[OptionKey('std', machine=MachineChoice.BUILD, lang=lang)].choices
+ except KeyError:
return []
- res = lang_opts['std'].choices
# TODO: Get rid of this once we have propper typing for options
assert isinstance(res, list)
diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py
index 985f6f3..311e65a 100644
--- a/mesonbuild/compilers/c.py
+++ b/mesonbuild/compilers/c.py
@@ -16,7 +16,7 @@ import os.path
import typing as T
from .. import coredata
-from ..mesonlib import MachineChoice, MesonException, mlog, version_compare
+from ..mesonlib import MachineChoice, MesonException, mlog, version_compare, OptionKey
from ..linkers import LinkerEnvVarsMixin
from .c_function_attributes import C_FUNC_ATTRIBUTES
from .mixins.clike import CLikeCompiler
@@ -39,7 +39,7 @@ from .compilers import (
)
if T.TYPE_CHECKING:
- from ..coredata import OptionDictType
+ from ..coredata import KeyedOptionDictType
from ..dependencies import Dependency, ExternalProgram
from ..envconfig import MachineInfo
from ..environment import Environment
@@ -95,10 +95,10 @@ class CCompiler(CLikeCompiler, Compiler):
return self.compiles(t.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies)
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
opts = super().get_options()
opts.update({
- 'std': coredata.UserComboOption(
+ OptionKey('std', machine=self.for_machine, lang=self.language): coredata.UserComboOption(
'C langauge standard to use',
['none'],
'none',
@@ -119,7 +119,7 @@ class _ClangCStds(CompilerMixinBase):
_C18_VERSION = '>=8.0.0'
_C2X_VERSION = '>=9.0.0'
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
opts = super().get_options()
c_stds = ['c89', 'c99', 'c11']
g_stds = ['gnu89', 'gnu99', 'gnu11']
@@ -134,7 +134,7 @@ class _ClangCStds(CompilerMixinBase):
if version_compare(self.version, self._C2X_VERSION):
c_stds += ['c2x']
g_stds += ['gnu2x']
- opts['std'].choices = ['none'] + c_stds + g_stds # type: ignore
+ opts[OptionKey('std', machine=self.for_machine, lang=self.language)].choices = ['none'] + c_stds + g_stds
return opts
@@ -153,28 +153,28 @@ class ClangCCompiler(_ClangCStds, ClangCompiler, CCompiler):
'2': default_warn_args + ['-Wextra'],
'3': default_warn_args + ['-Wextra', '-Wpedantic']}
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
opts = super().get_options()
if self.info.is_windows() or self.info.is_cygwin():
opts.update({
- 'winlibs': coredata.UserArrayOption(
+ OptionKey('winlibs', machine=self.for_machine, lang=self.language): coredata.UserArrayOption(
'Standard Win libraries to link against',
gnu_winlibs,
),
})
return opts
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
args = []
- std = options['std']
+ std = options[OptionKey('std', machine=self.for_machine, lang=self.language)]
if std.value != 'none':
args.append('-std=' + std.value)
return args
- def get_option_link_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
if self.info.is_windows() or self.info.is_cygwin():
# without a typedict mypy can't understand this.
- libs = options['winlibs'].value.copy()
+ libs = options[OptionKey('winlibs', machine=self.for_machine, lang=self.language)].value.copy()
assert isinstance(libs, list)
for l in libs:
assert isinstance(l, str)
@@ -223,19 +223,20 @@ class ArmclangCCompiler(ArmclangCompiler, CCompiler):
'2': default_warn_args + ['-Wextra'],
'3': default_warn_args + ['-Wextra', '-Wpedantic']}
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
opts = CCompiler.get_options(self)
- opts['std'].choices = ['none', 'c90', 'c99', 'c11', 'gnu90', 'gnu99', 'gnu11'] # type: ignore
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none', 'c90', 'c99', 'c11', 'gnu90', 'gnu99', 'gnu11']
return opts
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
args = []
- std = options['std']
+ std = options[OptionKey('std', machine=self.for_machine, lang=self.language)]
if std.value != 'none':
args.append('-std=' + std.value)
return args
- def get_option_link_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
return []
@@ -257,7 +258,7 @@ class GnuCCompiler(GnuCompiler, CCompiler):
'2': default_warn_args + ['-Wextra'],
'3': default_warn_args + ['-Wextra', '-Wpedantic']}
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
opts = CCompiler.get_options(self)
c_stds = ['c89', 'c99', 'c11']
g_stds = ['gnu89', 'gnu99', 'gnu11']
@@ -267,27 +268,28 @@ class GnuCCompiler(GnuCompiler, CCompiler):
if version_compare(self.version, self._C2X_VERSION):
c_stds += ['c2x']
g_stds += ['gnu2x']
- opts['std'].choices = ['none'] + c_stds + g_stds # type: ignore
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none'] + c_stds + g_stds
if self.info.is_windows() or self.info.is_cygwin():
opts.update({
- 'winlibs': coredata.UserArrayOption(
+ key.evolve('winlibs'): coredata.UserArrayOption(
'Standard Win libraries to link against',
gnu_winlibs,
),
})
return opts
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
args = []
- std = options['std']
+ std = options[OptionKey('std', lang=self.language, machine=self.for_machine)]
if std.value != 'none':
args.append('-std=' + std.value)
return args
- def get_option_link_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
if self.info.is_windows() or self.info.is_cygwin():
# without a typeddict mypy can't figure this out
- libs = options['winlibs'].value.copy()
+ libs: T.List[str] = options[OptionKey('winlibs', lang=self.language, machine=self.for_machine)].value.copy()
assert isinstance(libs, list)
for l in libs:
assert isinstance(l, str)
@@ -331,9 +333,9 @@ class ElbrusCCompiler(GnuCCompiler, ElbrusCompiler):
ElbrusCompiler.__init__(self)
# It does support some various ISO standards and c/gnu 90, 9x, 1x in addition to those which GNU CC supports.
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
opts = CCompiler.get_options(self)
- opts['std'].choices = [ # type: ignore
+ opts[OptionKey('std', machine=self.for_machine, lang=self.language)].choices = [
'none', 'c89', 'c90', 'c9x', 'c99', 'c1x', 'c11',
'gnu89', 'gnu90', 'gnu9x', 'gnu99', 'gnu1x', 'gnu11',
'iso9899:2011', 'iso9899:1990', 'iso9899:199409', 'iso9899:1999',
@@ -368,18 +370,18 @@ class IntelCCompiler(IntelGnuLikeCompiler, CCompiler):
'2': default_warn_args + ['-Wextra'],
'3': default_warn_args + ['-Wextra']}
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
opts = CCompiler.get_options(self)
c_stds = ['c89', 'c99']
g_stds = ['gnu89', 'gnu99']
if version_compare(self.version, '>=16.0.0'):
c_stds += ['c11']
- opts['std'].choices = ['none'] + c_stds + g_stds # type: ignore
+ opts[OptionKey('std', machine=self.for_machine, lang=self.language)].choices = ['none'] + c_stds + g_stds
return opts
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
args = []
- std = options['std']
+ std = options[OptionKey('std', machine=self.for_machine, lang=self.language)]
if std.value != 'none':
args.append('-std=' + std.value)
return args
@@ -389,19 +391,20 @@ class VisualStudioLikeCCompilerMixin(CompilerMixinBase):
"""Shared methods that apply to MSVC-like C compilers."""
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
opts = super().get_options()
opts.update({
- 'winlibs': coredata.UserArrayOption(
+ OptionKey('winlibs', machine=self.for_machine, lang=self.language): coredata.UserArrayOption(
'Windows libs to link against.',
msvc_winlibs,
),
})
return opts
- def get_option_link_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
# need a TypeDict to make this work
- libs = options['winlibs'].value.copy()
+ key = OptionKey('winlibs', machine=self.for_machine, lang=self.language)
+ libs = options[key].value.copy()
assert isinstance(libs, list)
for l in libs:
assert isinstance(l, str)
@@ -423,7 +426,7 @@ class VisualStudioCCompiler(MSVCCompiler, VisualStudioLikeCCompilerMixin, CCompi
full_version=full_version)
MSVCCompiler.__init__(self, target)
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
opts = super().get_options()
c_stds = ['c89', 'c99']
# Need to have these to be compatible with projects
@@ -436,12 +439,13 @@ class VisualStudioCCompiler(MSVCCompiler, VisualStudioLikeCCompilerMixin, CCompi
if version_compare(self.version, self._C17_VERSION):
c_stds += ['c17', 'c18']
g_stds += ['gnu17', 'gnu18']
- opts['std'].choices = ['none'] + c_stds + g_stds # type: ignore
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none'] + c_stds + g_stds
return opts
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
args = []
- std = options['std']
+ std = options[OptionKey('std', machine=self.for_machine, lang=self.language)]
if std.value.startswith('gnu'):
mlog.log_once(
'cl.exe does not actually support gnu standards, and meson '
@@ -466,8 +470,9 @@ class ClangClCCompiler(_ClangCStds, ClangClCompiler, VisualStudioLikeCCompilerMi
full_version=full_version)
ClangClCompiler.__init__(self, target)
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
- std = options['std'].value
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key].value
if std != "none":
return ['/clang:-std={}'.format(std)]
return []
@@ -487,14 +492,16 @@ class IntelClCCompiler(IntelVisualStudioLikeCompiler, VisualStudioLikeCCompilerM
full_version=full_version)
IntelVisualStudioLikeCompiler.__init__(self, target)
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
opts = super().get_options()
- opts['std'].choices = ['none', 'c89', 'c99', 'c11'] # type: ignore
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none', 'c89', 'c99', 'c11']
return opts
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
args = []
- std = options['std']
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
if std.value == 'c89':
mlog.log_once("ICL doesn't explicitly implement c89, setting the standard to 'none', which is close.")
elif std.value != 'none':
@@ -513,14 +520,16 @@ class ArmCCompiler(ArmCompiler, CCompiler):
full_version=full_version)
ArmCompiler.__init__(self)
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
opts = CCompiler.get_options(self)
- opts['std'].choices = ['none', 'c89', 'c99', 'c11'] # type: ignore
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none', 'c89', 'c99', 'c11']
return opts
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
args = []
- std = options['std']
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
if std.value != 'none':
args.append('--' + std.value)
return args
@@ -540,17 +549,19 @@ class CcrxCCompiler(CcrxCompiler, CCompiler):
def get_always_args(self) -> T.List[str]:
return ['-nologo']
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
opts = CCompiler.get_options(self)
- opts['std'].choices = ['none', 'c89', 'c99'] # type: ignore
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none', 'c89', 'c99']
return opts
def get_no_stdinc_args(self) -> T.List[str]:
return []
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
args = []
- std = options['std']
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
if std.value == 'c89':
args.append('-lang=c')
elif std.value == 'c99':
@@ -585,17 +596,19 @@ class Xc16CCompiler(Xc16Compiler, CCompiler):
info, exe_wrapper, linker=linker, full_version=full_version)
Xc16Compiler.__init__(self)
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
opts = CCompiler.get_options(self)
- opts['std'].choices = ['none', 'c89', 'c99', 'gnu89', 'gnu99'] # type: ignore
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none', 'c89', 'c99', 'gnu89', 'gnu99']
return opts
def get_no_stdinc_args(self) -> T.List[str]:
return []
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
args = []
- std = options['c_std']
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
if std.value != 'none':
args.append('-ansi')
args.append('-std=' + std.value)
@@ -628,12 +641,13 @@ class CompCertCCompiler(CompCertCompiler, CCompiler):
info, exe_wrapper, linker=linker, full_version=full_version)
CompCertCompiler.__init__(self)
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
opts = CCompiler.get_options(self)
- opts['std'].choices = ['none', 'c89', 'c99'] # type: ignore
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none', 'c89', 'c99']
return opts
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
return []
def get_no_optimization_args(self) -> T.List[str]:
@@ -664,17 +678,19 @@ class C2000CCompiler(C2000Compiler, CCompiler):
def get_always_args(self) -> T.List[str]:
return []
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
opts = CCompiler.get_options(self)
- opts['std'].choices = ['none', 'c89', 'c99', 'c11'] # type: ignore
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none', 'c89', 'c99', 'c11']
return opts
def get_no_stdinc_args(self) -> T.List[str]:
return []
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
args = []
- std = options['c_std']
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
if std.value != 'none':
args.append('--' + std.value)
return args
diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py
index 0bd2b4c..234ce06 100644
--- a/mesonbuild/compilers/compilers.py
+++ b/mesonbuild/compilers/compilers.py
@@ -25,7 +25,7 @@ from .. import mesonlib
from ..linkers import LinkerEnvVarsMixin
from ..mesonlib import (
EnvironmentException, MachineChoice, MesonException,
- Popen_safe, split_args, LibType, TemporaryDirectoryWinProof
+ Popen_safe, split_args, LibType, TemporaryDirectoryWinProof, OptionKey,
)
from ..envconfig import (
get_env_var
@@ -35,7 +35,7 @@ from ..arglist import CompilerArgs
if T.TYPE_CHECKING:
from ..build import BuildTarget
- from ..coredata import OptionDictType
+ from ..coredata import OptionDictType, KeyedOptionDictType
from ..envconfig import MachineInfo
from ..environment import Environment
from ..linkers import DynamicLinker # noqa: F401
@@ -265,36 +265,32 @@ cuda_debug_args = {False: [],
clike_debug_args = {False: [],
True: ['-g']} # type: T.Dict[bool, T.List[str]]
-base_options = {'b_pch': coredata.UserBooleanOption('Use precompiled headers', True),
- 'b_lto': coredata.UserBooleanOption('Use link time optimization', False),
- 'b_sanitize': coredata.UserComboOption('Code sanitizer to use',
- ['none', 'address', 'thread', 'undefined', 'memory', 'address,undefined'],
- 'none'),
- 'b_lundef': coredata.UserBooleanOption('Use -Wl,--no-undefined when linking', True),
- 'b_asneeded': coredata.UserBooleanOption('Use -Wl,--as-needed when linking', True),
- 'b_pgo': coredata.UserComboOption('Use profile guided optimization',
- ['off', 'generate', 'use'],
- 'off'),
- 'b_coverage': coredata.UserBooleanOption('Enable coverage tracking.',
- False),
- 'b_colorout': coredata.UserComboOption('Use colored output',
- ['auto', 'always', 'never'],
- 'always'),
- 'b_ndebug': coredata.UserComboOption('Disable asserts',
- ['true', 'false', 'if-release'], 'false'),
- 'b_staticpic': coredata.UserBooleanOption('Build static libraries as position independent',
- True),
- 'b_pie': coredata.UserBooleanOption('Build executables as position independent',
- False),
- 'b_bitcode': coredata.UserBooleanOption('Generate and embed bitcode (only macOS/iOS/tvOS)',
- False),
- 'b_vscrt': coredata.UserComboOption('VS run-time library type to use.',
- ['none', 'md', 'mdd', 'mt', 'mtd', 'from_buildtype', 'static_from_buildtype'],
- 'from_buildtype'),
- } # type: OptionDictType
-
-def option_enabled(boptions: T.List[str], options: 'OptionDictType',
- option: str) -> bool:
+base_options: 'KeyedOptionDictType' = {
+ OptionKey('b_pch'): coredata.UserBooleanOption('Use precompiled headers', True),
+ OptionKey('b_lto'): coredata.UserBooleanOption('Use link time optimization', False),
+ OptionKey('b_sanitize'): coredata.UserComboOption('Code sanitizer to use',
+ ['none', 'address', 'thread', 'undefined', 'memory', 'address,undefined'],
+ 'none'),
+ OptionKey('b_lundef'): coredata.UserBooleanOption('Use -Wl,--no-undefined when linking', True),
+ OptionKey('b_asneeded'): coredata.UserBooleanOption('Use -Wl,--as-needed when linking', True),
+ OptionKey('b_pgo'): coredata.UserComboOption('Use profile guided optimization',
+ ['off', 'generate', 'use'],
+ 'off'),
+ OptionKey('b_coverage'): coredata.UserBooleanOption('Enable coverage tracking.', False),
+ OptionKey('b_colorout'): coredata.UserComboOption('Use colored output',
+ ['auto', 'always', 'never'],
+ 'always'),
+ OptionKey('b_ndebug'): coredata.UserComboOption('Disable asserts', ['true', 'false', 'if-release'], 'false'),
+ OptionKey('b_staticpic'): coredata.UserBooleanOption('Build static libraries as position independent', True),
+ OptionKey('b_pie'): coredata.UserBooleanOption('Build executables as position independent', False),
+ OptionKey('b_bitcode'): coredata.UserBooleanOption('Generate and embed bitcode (only macOS/iOS/tvOS)', False),
+ OptionKey('b_vscrt'): coredata.UserComboOption('VS run-time library type to use.',
+ ['none', 'md', 'mdd', 'mt', 'mtd', 'from_buildtype', 'static_from_buildtype'],
+ 'from_buildtype'),
+}
+
+def option_enabled(boptions: T.Set[OptionKey], options: 'KeyedOptionDictType',
+ option: OptionKey) -> bool:
try:
if option not in boptions:
return False
@@ -304,23 +300,23 @@ def option_enabled(boptions: T.List[str], options: 'OptionDictType',
except KeyError:
return False
-def get_base_compile_args(options: 'OptionDictType', compiler: 'Compiler') -> T.List[str]:
+def get_base_compile_args(options: 'KeyedOptionDictType', compiler: 'Compiler') -> T.List[str]:
args = [] # type T.List[str]
try:
- if options['b_lto'].value:
+ if options[OptionKey('b_lto')].value:
args.extend(compiler.get_lto_compile_args())
except KeyError:
pass
try:
- args += compiler.get_colorout_args(options['b_colorout'].value)
+ args += compiler.get_colorout_args(options[OptionKey('b_colorout')].value)
except KeyError:
pass
try:
- args += compiler.sanitizer_compile_args(options['b_sanitize'].value)
+ args += compiler.sanitizer_compile_args(options[OptionKey('b_sanitize')].value)
except KeyError:
pass
try:
- pgo_val = options['b_pgo'].value
+ pgo_val = options[OptionKey('b_pgo')].value
if pgo_val == 'generate':
args.extend(compiler.get_profile_generate_args())
elif pgo_val == 'use':
@@ -328,23 +324,23 @@ def get_base_compile_args(options: 'OptionDictType', compiler: 'Compiler') -> T.
except KeyError:
pass
try:
- if options['b_coverage'].value:
+ if options[OptionKey('b_coverage')].value:
args += compiler.get_coverage_args()
except KeyError:
pass
try:
- if (options['b_ndebug'].value == 'true' or
- (options['b_ndebug'].value == 'if-release' and
- options['buildtype'].value in {'release', 'plain'})):
+ if (options[OptionKey('b_ndebug')].value == 'true' or
+ (options[OptionKey('b_ndebug')].value == 'if-release' and
+ options[OptionKey('buildtype')].value in {'release', 'plain'})):
args += compiler.get_disable_assert_args()
except KeyError:
pass
# This does not need a try...except
- if option_enabled(compiler.base_options, options, 'b_bitcode'):
+ if option_enabled(compiler.base_options, options, OptionKey('b_bitcode')):
args.append('-fembed-bitcode')
try:
- crt_val = options['b_vscrt'].value
- buildtype = options['buildtype'].value
+ crt_val = options[OptionKey('b_vscrt')].value
+ buildtype = options[OptionKey('buildtype')].value
try:
args += compiler.get_crt_compile_args(crt_val, buildtype)
except AttributeError:
@@ -353,20 +349,20 @@ def get_base_compile_args(options: 'OptionDictType', compiler: 'Compiler') -> T.
pass
return args
-def get_base_link_args(options: 'OptionDictType', linker: 'Compiler',
+def get_base_link_args(options: 'KeyedOptionDictType', linker: 'Compiler',
is_shared_module: bool) -> T.List[str]:
args = [] # type: T.List[str]
try:
- if options['b_lto'].value:
+ if options[OptionKey('b_lto')].value:
args.extend(linker.get_lto_link_args())
except KeyError:
pass
try:
- args += linker.sanitizer_link_args(options['b_sanitize'].value)
+ args += linker.sanitizer_link_args(options[OptionKey('b_sanitize')].value)
except KeyError:
pass
try:
- pgo_val = options['b_pgo'].value
+ pgo_val = options[OptionKey('b_pgo')].value
if pgo_val == 'generate':
args.extend(linker.get_profile_generate_args())
elif pgo_val == 'use':
@@ -374,13 +370,13 @@ def get_base_link_args(options: 'OptionDictType', linker: 'Compiler',
except KeyError:
pass
try:
- if options['b_coverage'].value:
+ if options[OptionKey('b_coverage')].value:
args += linker.get_coverage_link_args()
except KeyError:
pass
- as_needed = option_enabled(linker.base_options, options, 'b_asneeded')
- bitcode = option_enabled(linker.base_options, options, 'b_bitcode')
+ as_needed = option_enabled(linker.base_options, options, OptionKey('b_asneeded'))
+ bitcode = option_enabled(linker.base_options, options, OptionKey('b_bitcode'))
# Shared modules cannot be built with bitcode_bundle because
# -bitcode_bundle is incompatible with -undefined and -bundle
if bitcode and not is_shared_module:
@@ -394,14 +390,14 @@ def get_base_link_args(options: 'OptionDictType', linker: 'Compiler',
if not bitcode:
args.extend(linker.headerpad_args())
if (not is_shared_module and
- option_enabled(linker.base_options, options, 'b_lundef')):
+ option_enabled(linker.base_options, options, OptionKey('b_lundef'))):
args.extend(linker.no_undefined_link_args())
else:
args.extend(linker.get_allow_undefined_link_args())
try:
- crt_val = options['b_vscrt'].value
- buildtype = options['buildtype'].value
+ crt_val = options[OptionKey('b_vscrt')].value
+ buildtype = options[OptionKey('buildtype')].value
try:
args += linker.get_crt_link_args(crt_val, buildtype)
except AttributeError:
@@ -477,7 +473,7 @@ class Compiler(metaclass=abc.ABCMeta):
self.version = version
self.full_version = full_version
self.for_machine = for_machine
- self.base_options = [] # type: T.List[str]
+ self.base_options: T.Set[OptionKey] = set()
self.linker = linker
self.info = info
self.is_cross = is_cross
@@ -596,13 +592,13 @@ class Compiler(metaclass=abc.ABCMeta):
is_cross: bool) -> T.List[str]:
return self.linker.get_args_from_envvars(for_machine, is_cross)
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
return {}
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
return []
- def get_option_link_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
return self.linker.get_option_args(options)
def check_header(self, hname: str, prefix: str, env: 'Environment', *,
@@ -826,7 +822,7 @@ class Compiler(metaclass=abc.ABCMeta):
def get_std_shared_lib_link_args(self) -> T.List[str]:
return self.linker.get_std_shared_lib_args()
- def get_std_shared_module_link_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_std_shared_module_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
return self.linker.get_std_shared_module_args(options)
def get_link_whole_for(self, args: T.List[str]) -> T.List[str]:
@@ -1243,17 +1239,19 @@ def get_args_from_envvars(lang: str,
def get_global_options(lang: str,
comp: T.Type[Compiler],
for_machine: MachineChoice,
- is_cross: bool) -> 'OptionDictType':
+ is_cross: bool) -> 'KeyedOptionDictType':
"""Retreive options that apply to all compilers for a given language."""
description = 'Extra arguments passed to the {}'.format(lang)
- opts = {
- 'args': coredata.UserArrayOption(
+ argkey = OptionKey('args', lang=lang, machine=for_machine)
+ largkey = argkey.evolve('link_args')
+ opts: 'KeyedOptionDictType' = {
+ argkey: coredata.UserArrayOption(
description + ' compiler',
[], split_args=True, user_input=True, allow_dups=True),
- 'link_args': coredata.UserArrayOption(
+ largkey: coredata.UserArrayOption(
description + ' linker',
[], split_args=True, user_input=True, allow_dups=True),
- } # type: OptionDictType
+ }
# Get from env vars.
compile_args, link_args = get_args_from_envvars(
@@ -1262,10 +1260,7 @@ def get_global_options(lang: str,
is_cross,
comp.INVOKES_LINKER)
- for k, o in opts.items():
- if k == 'args':
- o.set_value(compile_args)
- elif k == 'link_args':
- o.set_value(link_args)
+ opts[argkey].set_value(compile_args)
+ opts[largkey].set_value(link_args)
return opts
diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py
index 607bea7..2e94e48 100644
--- a/mesonbuild/compilers/cpp.py
+++ b/mesonbuild/compilers/cpp.py
@@ -19,7 +19,7 @@ import typing as T
from .. import coredata
from .. import mlog
-from ..mesonlib import MesonException, MachineChoice, version_compare
+from ..mesonlib import MesonException, MachineChoice, version_compare, OptionKey
from ..linkers import LinkerEnvVarsMixin
from .compilers import (
@@ -42,7 +42,7 @@ from .mixins.pgi import PGICompiler
from .mixins.emscripten import EmscriptenMixin
if T.TYPE_CHECKING:
- from ..coredata import OptionDictType
+ from ..coredata import KeyedOptionDictType
from ..dependencies import Dependency, ExternalProgram
from ..envconfig import MachineInfo
from ..environment import Environment
@@ -169,10 +169,11 @@ class CPPCompiler(CLikeCompiler, Compiler):
raise MesonException('C++ Compiler does not support -std={}'.format(cpp_std))
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
opts = super().get_options()
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
opts.update({
- 'std': coredata.UserComboOption(
+ key: coredata.UserComboOption(
'C++ language standard to use',
['none'],
'none',
@@ -196,47 +197,50 @@ class ClangCPPCompiler(ClangCompiler, CPPCompiler):
'2': default_warn_args + ['-Wextra'],
'3': default_warn_args + ['-Wextra', '-Wpedantic']}
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
opts = CPPCompiler.get_options(self)
+ key = OptionKey('key', machine=self.for_machine, lang=self.language)
opts.update({
- 'eh': coredata.UserComboOption(
+ key.evolve('eh'): coredata.UserComboOption(
'C++ exception handling type.',
['none', 'default', 'a', 's', 'sc'],
'default',
),
- 'rtti': coredata.UserBooleanOption('Enable RTTI', True),
+ key.evolve('rtti'): coredata.UserBooleanOption('Enable RTTI', True),
})
- opts['std'].choices = [ # type: ignore
+ opts[key.evolve('std')].choices = [
'none', 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', 'c++1z',
'c++2a', 'c++20', 'gnu++11', 'gnu++14', 'gnu++17', 'gnu++1z',
'gnu++2a', 'gnu++20',
]
if self.info.is_windows() or self.info.is_cygwin():
opts.update({
- 'winlibs': coredata.UserArrayOption(
+ key.evolve('winlibs'): coredata.UserArrayOption(
'Standard Win libraries to link against',
gnu_winlibs,
),
})
return opts
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
args = []
- std = options['std']
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
if std.value != 'none':
args.append(self._find_best_cpp_std(std.value))
- non_msvc_eh_options(options['eh'].value, args)
+ non_msvc_eh_options(options[key.evolve('eh')].value, args)
- if not options['rtti'].value:
+ if not options[key.evolve('rtti')].value:
args.append('-fno-rtti')
return args
- def get_option_link_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
if self.info.is_windows() or self.info.is_cygwin():
# without a typedict mypy can't understand this.
- libs = options['winlibs'].value.copy()
+ key = OptionKey('winlibs', machine=self.for_machine, lang=self.language)
+ libs = options[key].value.copy()
assert isinstance(libs, list)
for l in libs:
assert isinstance(l, str)
@@ -265,9 +269,10 @@ class EmscriptenCPPCompiler(EmscriptenMixin, LinkerEnvVarsMixin, ClangCPPCompile
defines=defines, full_version=full_version)
self.id = 'emscripten'
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
args = []
- std = options['std']
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
if std.value != 'none':
args.append(self._find_best_cpp_std(std.value))
return args
@@ -287,32 +292,34 @@ class ArmclangCPPCompiler(ArmclangCompiler, CPPCompiler):
'2': default_warn_args + ['-Wextra'],
'3': default_warn_args + ['-Wextra', '-Wpedantic']}
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
opts = CPPCompiler.get_options(self)
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
opts.update({
- 'eh': coredata.UserComboOption(
+ key.evolve('eh'): coredata.UserComboOption(
'C++ exception handling type.',
['none', 'default', 'a', 's', 'sc'],
'default',
),
})
- opts['std'].choices = [ # type: ignore
+ opts[key].choices = [
'none', 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', 'gnu++98',
'gnu++03', 'gnu++11', 'gnu++14', 'gnu++17',
]
return opts
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
args = []
- std = options['std']
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
if std.value != 'none':
args.append('-std=' + std.value)
- non_msvc_eh_options(options['eh'].value, args)
+ non_msvc_eh_options(options[key.evolve('eh')].value, args)
return args
- def get_option_link_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
return []
@@ -331,53 +338,56 @@ class GnuCPPCompiler(GnuCompiler, CPPCompiler):
'2': default_warn_args + ['-Wextra'],
'3': default_warn_args + ['-Wextra', '-Wpedantic']}
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
opts = CPPCompiler.get_options(self)
opts.update({
- 'eh': coredata.UserComboOption(
+ key.evolve('eh'): coredata.UserComboOption(
'C++ exception handling type.',
['none', 'default', 'a', 's', 'sc'],
'default',
),
- 'rtti': coredata.UserBooleanOption('Enable RTTI', True),
- 'debugstl': coredata.UserBooleanOption(
+ key.evolve('rtti'): coredata.UserBooleanOption('Enable RTTI', True),
+ key.evolve('debugstl'): coredata.UserBooleanOption(
'STL debug mode',
False,
)
})
- opts['std'].choices = [ # type: ignore
+ opts[key].choices = [
'none', 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', 'c++1z',
'c++2a', 'c++20', 'gnu++03', 'gnu++11', 'gnu++14', 'gnu++17',
'gnu++1z', 'gnu++2a', 'gnu++20',
]
if self.info.is_windows() or self.info.is_cygwin():
opts.update({
- 'winlibs': coredata.UserArrayOption(
+ key.evolve('winlibs'): coredata.UserArrayOption(
'Standard Win libraries to link against',
gnu_winlibs,
),
})
return opts
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
args = []
- std = options['std']
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
if std.value != 'none':
args.append(self._find_best_cpp_std(std.value))
- non_msvc_eh_options(options['eh'].value, args)
+ non_msvc_eh_options(options[key.evolve('eh')].value, args)
- if not options['rtti'].value:
+ if not options[key.evolve('rtti')].value:
args.append('-fno-rtti')
- if options['debugstl'].value:
+ if options[key.evolve('debugstl')].value:
args.append('-D_GLIBCXX_DEBUG=1')
return args
- def get_option_link_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
if self.info.is_windows() or self.info.is_cygwin():
# without a typedict mypy can't understand this.
- libs = options['winlibs'].value.copy()
+ key = OptionKey('winlibs', machine=self.for_machine, lang=self.language)
+ libs = options[key].value.copy()
assert isinstance(libs, list)
for l in libs:
assert isinstance(l, str)
@@ -424,7 +434,7 @@ class ElbrusCPPCompiler(GnuCPPCompiler, ElbrusCompiler):
full_version=full_version, defines=defines)
ElbrusCompiler.__init__(self)
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
opts = CPPCompiler.get_options(self)
cpp_stds = [
@@ -438,18 +448,19 @@ class ElbrusCPPCompiler(GnuCPPCompiler, ElbrusCompiler):
if version_compare(self.version, '>=1.25.00'):
cpp_stds += [ 'c++2a', 'gnu++2a' ]
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
opts.update({
- 'eh': coredata.UserComboOption(
+ key.evolve('eh'): coredata.UserComboOption(
'C++ exception handling type.',
['none', 'default', 'a', 's', 'sc'],
'default',
),
- 'debugstl': coredata.UserBooleanOption(
+ key.evolve('debugstl'): coredata.UserBooleanOption(
'STL debug mode',
False,
),
})
- opts['std'].choices = cpp_stds # type: ignore
+ opts[key].choices = cpp_stds
return opts
# Elbrus C++ compiler does not have lchmod, but there is only linker warning, not compiler error.
@@ -465,15 +476,16 @@ class ElbrusCPPCompiler(GnuCPPCompiler, ElbrusCompiler):
dependencies=dependencies)
# Elbrus C++ compiler does not support RTTI, so don't check for it.
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
args = []
- std = options['std']
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
if std.value != 'none':
args.append(self._find_best_cpp_std(std.value))
- non_msvc_eh_options(options['eh'].value, args)
+ non_msvc_eh_options(options[key.evolve('eh')].value, args)
- if options['debugstl'].value:
+ if options[key.evolve('debugstl')].value:
args.append('-D_GLIBCXX_DEBUG=1')
return args
@@ -494,7 +506,7 @@ class IntelCPPCompiler(IntelGnuLikeCompiler, CPPCompiler):
'2': default_warn_args + ['-Wextra'],
'3': default_warn_args + ['-Wextra']}
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
opts = CPPCompiler.get_options(self)
# Every Unix compiler under the sun seems to accept -std=c++03,
# with the exception of ICC. Instead of preventing the user from
@@ -511,36 +523,40 @@ class IntelCPPCompiler(IntelGnuLikeCompiler, CPPCompiler):
if version_compare(self.version, '>=19.1.0'):
c_stds += ['c++2a']
g_stds += ['gnu++2a']
+
+
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
opts.update({
- 'eh': coredata.UserComboOption(
+ key.evolve('eh'): coredata.UserComboOption(
'C++ exception handling type.',
['none', 'default', 'a', 's', 'sc'],
'default',
),
- 'rtti': coredata.UserBooleanOption('Enable RTTI', True),
- 'debugstl': coredata.UserBooleanOption('STL debug mode', False),
+ key.evolve('rtti'): coredata.UserBooleanOption('Enable RTTI', True),
+ key.evolve('debugstl'): coredata.UserBooleanOption('STL debug mode', False),
})
- opts['std'].choices = ['none'] + c_stds + g_stds # type: ignore
+ opts[key].choices = ['none'] + c_stds + g_stds
return opts
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
args = []
- std = options['std']
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
if std.value != 'none':
remap_cpp03 = {
'c++03': 'c++98',
'gnu++03': 'gnu++98'
}
args.append('-std=' + remap_cpp03.get(std.value, std.value))
- if options['eh'].value == 'none':
+ if options[key.evolve('eh')].value == 'none':
args.append('-fno-exceptions')
- if not options['rtti'].value:
+ if not options[key.evolve('rtti')].value:
args.append('-fno-rtti')
- if options['debugstl'].value:
+ if options[key.evolve('debugstl')].value:
args.append('-D_GLIBCXX_DEBUG=1')
return args
- def get_option_link_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
return []
@@ -560,30 +576,33 @@ class VisualStudioLikeCPPCompilerMixin(CompilerMixinBase):
'c++latest': (False, "latest"),
}
- def get_option_link_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
# need a typeddict for this
- return T.cast(T.List[str], options['winlibs'].value[:])
+ key = OptionKey('winlibs', machine=self.for_machine, lang=self.language)
+ return T.cast(T.List[str], options[key].value[:])
- def _get_options_impl(self, opts: 'OptionDictType', cpp_stds: T.List[str]) -> 'OptionDictType':
+ def _get_options_impl(self, opts: 'KeyedOptionDictType', cpp_stds: T.List[str]) -> 'KeyedOptionDictType':
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
opts.update({
- 'eh': coredata.UserComboOption(
+ key.evolve('eh'): coredata.UserComboOption(
'C++ exception handling type.',
['none', 'default', 'a', 's', 'sc'],
'default',
),
- 'rtti': coredata.UserBooleanOption('Enable RTTI', True),
- 'winlibs': coredata.UserArrayOption(
+ key.evolve('rtti'): coredata.UserBooleanOption('Enable RTTI', True),
+ key.evolve('winlibs'): coredata.UserArrayOption(
'Windows libs to link against.',
msvc_winlibs,
),
})
- opts['std'].choices = cpp_stds # type: ignore
+ opts[key.evolve('std')].choices = cpp_stds
return opts
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
args = []
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
- eh = options['eh']
+ eh = options[key.evolve('eh')]
if eh.value == 'default':
args.append('/EHsc')
elif eh.value == 'none':
@@ -591,10 +610,10 @@ class VisualStudioLikeCPPCompilerMixin(CompilerMixinBase):
else:
args.append('/EH' + eh.value)
- if not options['rtti'].value:
+ if not options[key.evolve('rtti')].value:
args.append('/GR-')
- permissive, ver = self.VC_VERSION_MAP[options['std'].value]
+ permissive, ver = self.VC_VERSION_MAP[options[key].value]
if ver is not None:
args.append('/std:c++{}'.format(ver))
@@ -616,22 +635,23 @@ class CPP11AsCPP14Mixin(CompilerMixinBase):
This is a limitation of Clang and MSVC that ICL doesn't share.
"""
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
# Note: there is no explicit flag for supporting C++11; we attempt to do the best we can
# which means setting the C++ standard version to C++14, in compilers that support it
# (i.e., after VS2015U3)
# if one is using anything before that point, one cannot set the standard.
- if options['std'].value in {'vc++11', 'c++11'}:
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ if options[key].value in {'vc++11', 'c++11'}:
mlog.warning(self.id, 'does not support C++11;',
'attempting best effort; setting the standard to C++14', once=True)
# Don't mutate anything we're going to change, we need to use
# deepcopy since we're messing with members, and we can't simply
# copy the members because the option proxy doesn't support it.
options = copy.deepcopy(options)
- if options['std'].value == 'vc++11':
- options['std'].value = 'vc++14'
+ if options[key].value == 'vc++11':
+ options[key].value = 'vc++14'
else:
- options['std'].value = 'c++14'
+ options[key].value = 'c++14'
return super().get_option_compile_args(options)
@@ -644,10 +664,10 @@ class VisualStudioCPPCompiler(CPP11AsCPP14Mixin, VisualStudioLikeCPPCompilerMixi
CPPCompiler.__init__(self, exelist, version, for_machine, is_cross,
info, exe_wrapper, linker=linker, full_version=full_version)
MSVCCompiler.__init__(self, target)
- self.base_options = ['b_pch', 'b_vscrt', 'b_ndebug'] # FIXME add lto, pgo and the like
+ self.base_options = {OptionKey(o) for o in ['b_pch', 'b_vscrt', 'b_ndebug']} # FIXME add lto, pgo and the like
self.id = 'msvc'
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
cpp_stds = ['none', 'c++11', 'vc++11']
# Visual Studio 2015 and later
if version_compare(self.version, '>=19'):
@@ -657,11 +677,12 @@ class VisualStudioCPPCompiler(CPP11AsCPP14Mixin, VisualStudioLikeCPPCompilerMixi
cpp_stds.extend(['vc++14', 'c++17', 'vc++17'])
return self._get_options_impl(super().get_options(), cpp_stds)
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
- if options['std'].value != 'none' and version_compare(self.version, '<19.00.24210'):
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ if options[key].value != 'none' and version_compare(self.version, '<19.00.24210'):
mlog.warning('This version of MSVC does not support cpp_std arguments')
options = copy.copy(options)
- options['std'].value = 'none'
+ options[key].value = 'none'
args = super().get_option_compile_args(options)
@@ -684,7 +705,7 @@ class ClangClCPPCompiler(CPP11AsCPP14Mixin, VisualStudioLikeCPPCompilerMixin, Cl
ClangClCompiler.__init__(self, target)
self.id = 'clang-cl'
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
cpp_stds = ['none', 'c++11', 'vc++11', 'c++14', 'vc++14', 'c++17', 'vc++17', 'c++latest']
return self._get_options_impl(super().get_options(), cpp_stds)
@@ -700,7 +721,7 @@ class IntelClCPPCompiler(VisualStudioLikeCPPCompilerMixin, IntelVisualStudioLike
info, exe_wrapper, linker=linker, full_version=full_version)
IntelVisualStudioLikeCompiler.__init__(self, target)
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
# This has only been tested with version 19.0,
cpp_stds = ['none', 'c++11', 'vc++11', 'c++14', 'vc++14', 'c++17', 'vc++17', 'c++latest']
return self._get_options_impl(super().get_options(), cpp_stds)
@@ -719,21 +740,23 @@ class ArmCPPCompiler(ArmCompiler, CPPCompiler):
info, exe_wrapper, linker=linker, full_version=full_version)
ArmCompiler.__init__(self)
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
opts = CPPCompiler.get_options(self)
- opts['std'].choices = ['none', 'c++03', 'c++11'] # type: ignore
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none', 'c++03', 'c++11']
return opts
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
args = []
- std = options['std']
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
if std.value == 'c++11':
args.append('--cpp11')
elif std.value == 'c++03':
args.append('--cpp')
return args
- def get_option_link_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
return []
def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]:
@@ -753,7 +776,7 @@ class CcrxCPPCompiler(CcrxCompiler, CPPCompiler):
def get_always_args(self) -> T.List[str]:
return ['-nologo', '-lang=cpp']
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
return []
def get_compile_only_args(self) -> T.List[str]:
@@ -762,7 +785,7 @@ class CcrxCPPCompiler(CcrxCompiler, CPPCompiler):
def get_output_args(self, target: str) -> T.List[str]:
return ['-output=obj=%s' % target]
- def get_option_link_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
return []
def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]:
@@ -777,15 +800,16 @@ class C2000CPPCompiler(C2000Compiler, CPPCompiler):
info, exe_wrapper, linker=linker, full_version=full_version)
C2000Compiler.__init__(self)
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
opts = CPPCompiler.get_options(self)
- opts['std'].choices = ['none', 'c++03'] # type: ignore
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none', 'c++03']
return opts
def get_always_args(self) -> T.List[str]:
return ['-nologo', '-lang=cpp']
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
return []
def get_compile_only_args(self) -> T.List[str]:
@@ -794,7 +818,7 @@ class C2000CPPCompiler(C2000Compiler, CPPCompiler):
def get_output_args(self, target: str) -> T.List[str]:
return ['-output=obj=%s' % target]
- def get_option_link_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
return []
def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]:
diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py
index 89fcf40..7fa3e4f 100644
--- a/mesonbuild/compilers/cuda.py
+++ b/mesonbuild/compilers/cuda.py
@@ -18,13 +18,16 @@ import typing as T
from .. import coredata
from .. import mlog
-from ..mesonlib import EnvironmentException, MachineChoice, Popen_safe, OptionOverrideProxy, is_windows, LibType
+from ..mesonlib import (
+ EnvironmentException, MachineChoice, Popen_safe, OptionOverrideProxy,
+ is_windows, LibType, OptionKey,
+)
from .compilers import (Compiler, cuda_buildtype_args, cuda_optimization_args,
cuda_debug_args)
if T.TYPE_CHECKING:
from ..build import BuildTarget
- from ..coredata import OptionDictType
+ from ..coredata import KeyedOptionDictType
from ..dependencies import Dependency, ExternalProgram
from ..environment import Environment # noqa: F401
from ..envconfig import MachineInfo
@@ -195,24 +198,26 @@ class CudaCompiler(Compiler):
}}'''
return self.compiles(t.format_map(fargs), env, extra_args=extra_args, dependencies=dependencies)
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
opts = super().get_options()
- opts.update({'std': coredata.UserComboOption('C++ language standard to use with cuda',
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts.update({key: coredata.UserComboOption('C++ language standard to use with cuda',
['none', 'c++03', 'c++11', 'c++14'],
'none')})
return opts
- def _to_host_compiler_options(self, options: 'OptionDictType') -> 'OptionDictType':
+ def _to_host_compiler_options(self, options: 'KeyedOptionDictType') -> 'KeyedOptionDictType':
overrides = {name: opt.value for name, opt in options.items()}
return OptionOverrideProxy(overrides, self.host_compiler.get_options())
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
args = []
# On Windows, the version of the C++ standard used by nvcc is dictated by
# the combination of CUDA version and MSVC version; the --std= is thus ignored
# and attempting to use it will result in a warning: https://stackoverflow.com/a/51272091/741027
if not is_windows():
- std = options['std']
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
if std.value != 'none':
args.append('--std=' + std.value)
@@ -229,7 +234,7 @@ class CudaCompiler(Compiler):
cooked.append(arg)
return cls._to_host_flags(cooked, _Phase.LINKER)
- def get_option_link_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
return self._cook_link_args(self.host_compiler.get_option_link_args(self._to_host_compiler_options(options)))
def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str,
diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py
index ca6de38..eac2aa7 100644
--- a/mesonbuild/compilers/d.py
+++ b/mesonbuild/compilers/d.py
@@ -18,7 +18,7 @@ import subprocess
import typing as T
from ..mesonlib import (
- EnvironmentException, MachineChoice, version_compare,
+ EnvironmentException, MachineChoice, version_compare, OptionKey,
)
from ..arglist import CompilerArgs
@@ -653,8 +653,10 @@ class GnuDCompiler(GnuCompiler, DCompiler):
'1': default_warn_args,
'2': default_warn_args + ['-Wextra'],
'3': default_warn_args + ['-Wextra', '-Wpedantic']}
- self.base_options = ['b_colorout', 'b_sanitize', 'b_staticpic',
- 'b_vscrt', 'b_coverage', 'b_pgo', 'b_ndebug']
+ self.base_options = {
+ OptionKey(o) for o in [
+ 'b_colorout', 'b_sanitize', 'b_staticpic', 'b_vscrt',
+ 'b_coverage', 'b_pgo', 'b_ndebug']}
self._has_color_support = version_compare(self.version, '>=4.9')
# dependencies were implemented before, but broken - support was fixed in GCC 7.1+
@@ -724,7 +726,7 @@ class LLVMDCompiler(DmdLikeCompilerMixin, DCompiler):
full_version=full_version, is_cross=is_cross)
DmdLikeCompilerMixin.__init__(self, dmd_frontend_version=find_ldc_dmd_frontend_version(version_output))
self.id = 'llvm'
- self.base_options = ['b_coverage', 'b_colorout', 'b_vscrt', 'b_ndebug']
+ self.base_options = {OptionKey(o) for o in ['b_coverage', 'b_colorout', 'b_vscrt', 'b_ndebug']}
def get_colorout_args(self, colortype: str) -> T.List[str]:
if colortype == 'always':
@@ -782,7 +784,7 @@ class DmdDCompiler(DmdLikeCompilerMixin, DCompiler):
full_version=full_version, is_cross=is_cross)
DmdLikeCompilerMixin.__init__(self, version)
self.id = 'dmd'
- self.base_options = ['b_coverage', 'b_colorout', 'b_vscrt', 'b_ndebug']
+ self.base_options = {OptionKey(o) for o in ['b_coverage', 'b_colorout', 'b_vscrt', 'b_ndebug']}
def get_colorout_args(self, colortype: str) -> T.List[str]:
if colortype == 'always':
diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py
index 4b49e36..d65d585 100644
--- a/mesonbuild/compilers/fortran.py
+++ b/mesonbuild/compilers/fortran.py
@@ -31,11 +31,12 @@ from .mixins.elbrus import ElbrusCompiler
from .mixins.pgi import PGICompiler
from mesonbuild.mesonlib import (
- version_compare, EnvironmentException, MesonException, MachineChoice, LibType
+ version_compare, EnvironmentException, MesonException, MachineChoice,
+ LibType, OptionKey,
)
if T.TYPE_CHECKING:
- from ..coredata import OptionDictType
+ from ..coredata import KeyedOptionDictType
from ..dependencies import Dependency, ExternalProgram
from ..envconfig import MachineInfo
from ..environment import Environment
@@ -71,7 +72,7 @@ class FortranCompiler(CLikeCompiler, Compiler):
source_name.write_text('print *, "Fortran compilation is working."; end')
- extra_flags = []
+ extra_flags: T.List[str] = []
extra_flags += environment.coredata.get_external_args(self.for_machine, self.language)
extra_flags += environment.coredata.get_external_link_args(self.for_machine, self.language)
extra_flags += self.get_always_args()
@@ -150,10 +151,11 @@ class FortranCompiler(CLikeCompiler, Compiler):
def has_multi_link_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]:
return self._has_multi_link_arguments(args, env, 'stop; end program')
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
opts = super().get_options()
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
opts.update({
- 'std': coredata.UserComboOption(
+ key: coredata.UserComboOption(
'Fortran language standard to use',
['none'],
'none',
@@ -179,19 +181,21 @@ class GnuFortranCompiler(GnuCompiler, FortranCompiler):
'2': default_warn_args + ['-Wextra'],
'3': default_warn_args + ['-Wextra', '-Wpedantic', '-fimplicit-none']}
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
opts = FortranCompiler.get_options(self)
fortran_stds = ['legacy', 'f95', 'f2003']
if version_compare(self.version, '>=4.4.0'):
fortran_stds += ['f2008']
if version_compare(self.version, '>=8.0.0'):
fortran_stds += ['f2018']
- opts['std'].choices = ['none'] + fortran_stds # type: ignore
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none'] + fortran_stds
return opts
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
args = []
- std = options['std']
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
if std.value != 'none':
args.append('-std=' + std.value)
return args
@@ -313,14 +317,16 @@ class IntelFortranCompiler(IntelGnuLikeCompiler, FortranCompiler):
'2': default_warn_args + ['-warn', 'unused'],
'3': ['-warn', 'all']}
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
opts = FortranCompiler.get_options(self)
- opts['std'].choices = ['none', 'legacy', 'f95', 'f2003', 'f2008', 'f2018'] # type: ignore
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none', 'legacy', 'f95', 'f2003', 'f2008', 'f2018']
return opts
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
args = []
- std = options['std']
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
stds = {'legacy': 'none', 'f95': 'f95', 'f2003': 'f03', 'f2008': 'f08', 'f2018': 'f18'}
if std.value != 'none':
args.append('-stand=' + stds[std.value])
@@ -363,14 +369,16 @@ class IntelClFortranCompiler(IntelVisualStudioLikeCompiler, FortranCompiler):
'2': default_warn_args + ['/warn:unused'],
'3': ['/warn:all']}
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
opts = FortranCompiler.get_options(self)
- opts['std'].choices = ['none', 'legacy', 'f95', 'f2003', 'f2008', 'f2018'] # type: ignore
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none', 'legacy', 'f95', 'f2003', 'f2008', 'f2018']
return opts
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
args = []
- std = options['std']
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
stds = {'legacy': 'none', 'f95': 'f95', 'f2003': 'f03', 'f2008': 'f08', 'f2018': 'f18'}
if std.value != 'none':
args.append('/stand:' + stds[std.value])
diff --git a/mesonbuild/compilers/mixins/arm.py b/mesonbuild/compilers/mixins/arm.py
index ee7d337..beb5fd5 100644
--- a/mesonbuild/compilers/mixins/arm.py
+++ b/mesonbuild/compilers/mixins/arm.py
@@ -19,6 +19,7 @@ import typing as T
from ... import mesonlib
from ...linkers import ArmClangDynamicLinker
+from ...mesonlib import OptionKey
from ..compilers import clike_debug_args
from .clang import clang_color_args
@@ -145,8 +146,10 @@ class ArmclangCompiler(Compiler):
if not mesonlib.version_compare(self.version, '==' + self.linker.version):
raise mesonlib.EnvironmentException('armlink version does not match with compiler version')
self.id = 'armclang'
- self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage',
- 'b_ndebug', 'b_staticpic', 'b_colorout']
+ self.base_options = {
+ OptionKey(o) for o in
+ ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage',
+ 'b_ndebug', 'b_staticpic', 'b_colorout']}
# Assembly
self.can_compile_suffixes.add('s')
diff --git a/mesonbuild/compilers/mixins/clang.py b/mesonbuild/compilers/mixins/clang.py
index 2e50577..fcb2225 100644
--- a/mesonbuild/compilers/mixins/clang.py
+++ b/mesonbuild/compilers/mixins/clang.py
@@ -20,6 +20,7 @@ import typing as T
from ... import mesonlib
from ...linkers import AppleDynamicLinker
+from ...mesonlib import OptionKey
from ..compilers import CompileCheckMode
from .gnu import GnuLikeCompiler
@@ -48,11 +49,11 @@ class ClangCompiler(GnuLikeCompiler):
super().__init__()
self.id = 'clang'
self.defines = defines or {}
- self.base_options.append('b_colorout')
+ self.base_options.add(OptionKey('b_colorout'))
# TODO: this really should be part of the linker base_options, but
# linkers don't have base_options.
if isinstance(self.linker, AppleDynamicLinker):
- self.base_options.append('b_bitcode')
+ self.base_options.add(OptionKey('b_bitcode'))
# All Clang backends can also do LLVM IR
self.can_compile_suffixes.add('ll')
@@ -108,7 +109,7 @@ class ClangCompiler(GnuLikeCompiler):
else:
# Shouldn't work, but it'll be checked explicitly in the OpenMP dependency.
return []
-
+
@classmethod
def use_linker_args(cls, linker: str) -> T.List[str]:
# Clang additionally can use a linker specified as a path, which GCC
diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py
index dca09ea..3288c00 100644
--- a/mesonbuild/compilers/mixins/clike.py
+++ b/mesonbuild/compilers/mixins/clike.py
@@ -35,6 +35,7 @@ from ... import mesonlib
from ... import mlog
from ...linkers import GnuLikeDynamicLinkerMixin, SolarisDynamicLinker, CompCertDynamicLinker
from ...mesonlib import LibType
+from ...coredata import OptionKey
from .. import compilers
from ..compilers import CompileCheckMode
from .visualstudio import VisualStudioLikeCompiler
@@ -393,14 +394,16 @@ class CLikeCompiler(Compiler):
# linking with static libraries since MSVC won't select a CRT for
# us in that case and will error out asking us to pick one.
try:
- crt_val = env.coredata.base_options['b_vscrt'].value
- buildtype = env.coredata.builtins['buildtype'].value
+ crt_val = env.coredata.options[OptionKey('b_vscrt')].value
+ buildtype = env.coredata.options[OptionKey('buildtype')].value
cargs += self.get_crt_compile_args(crt_val, buildtype)
except (KeyError, AttributeError):
pass
# Add CFLAGS/CXXFLAGS/OBJCFLAGS/OBJCXXFLAGS and CPPFLAGS from the env
sys_args = env.coredata.get_external_args(self.for_machine, self.language)
+ if isinstance(sys_args, str):
+ sys_args = [sys_args]
# Apparently it is a thing to inject linker flags both
# via CFLAGS _and_ LDFLAGS, even though the former are
# also used during linking. These flags can break
diff --git a/mesonbuild/compilers/mixins/elbrus.py b/mesonbuild/compilers/mixins/elbrus.py
index 2ea3599..16f6210 100644
--- a/mesonbuild/compilers/mixins/elbrus.py
+++ b/mesonbuild/compilers/mixins/elbrus.py
@@ -21,7 +21,7 @@ import re
from .gnu import GnuLikeCompiler
from .gnu import gnu_optimization_args
-from ...mesonlib import Popen_safe
+from ...mesonlib import Popen_safe, OptionKey
if T.TYPE_CHECKING:
from ...environment import Environment
@@ -34,9 +34,7 @@ class ElbrusCompiler(GnuLikeCompiler):
def __init__(self) -> None:
super().__init__()
self.id = 'lcc'
- self.base_options = ['b_pgo', 'b_coverage',
- 'b_ndebug', 'b_staticpic',
- 'b_lundef', 'b_asneeded']
+ self.base_options = {OptionKey(o) for o in ['b_pgo', 'b_coverage', 'b_ndebug', 'b_staticpic', 'b_lundef', 'b_asneeded']}
# FIXME: use _build_wrapper to call this so that linker flags from the env
# get applied
diff --git a/mesonbuild/compilers/mixins/emscripten.py b/mesonbuild/compilers/mixins/emscripten.py
index cd18b35..fc0b21e 100644
--- a/mesonbuild/compilers/mixins/emscripten.py
+++ b/mesonbuild/compilers/mixins/emscripten.py
@@ -18,6 +18,7 @@ import os.path
import typing as T
from ... import coredata
+from ...mesonlib import OptionKey
if T.TYPE_CHECKING:
from ...environment import Environment
@@ -50,15 +51,16 @@ class EmscriptenMixin(Compiler):
def thread_link_flags(self, env: 'Environment') -> T.List[str]:
args = ['-s', 'USE_PTHREADS=1']
- count = env.coredata.compiler_options[self.for_machine][self.language]['thread_count'].value # type: int
+ count: int = env.coredata.options[OptionKey('thread_count', lang=self.language, machine=self.for_machine)].value
if count:
args.extend(['-s', 'PTHREAD_POOL_SIZE={}'.format(count)])
return args
- def get_options(self) -> 'coredata.OptionDictType':
+ def get_options(self) -> 'coredata.KeyedOptionDictType':
opts = super().get_options()
+ key = OptionKey('thread_count', machine=self.for_machine, lang=self.language)
opts.update({
- 'thread_count': coredata.UserIntegerOption(
+ key: coredata.UserIntegerOption(
'Number of threads to use in web assembly, set to 0 to disable',
(0, None, 4), # Default was picked at random
),
diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py
index 3d43162..95bcd7c 100644
--- a/mesonbuild/compilers/mixins/gnu.py
+++ b/mesonbuild/compilers/mixins/gnu.py
@@ -24,6 +24,7 @@ import typing as T
from ... import mesonlib
from ... import mlog
+from ...mesonlib import OptionKey
if T.TYPE_CHECKING:
from ...environment import Environment
@@ -146,14 +147,15 @@ class GnuLikeCompiler(Compiler, metaclass=abc.ABCMeta):
LINKER_PREFIX = '-Wl,'
def __init__(self) -> None:
- self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_coverage',
- 'b_ndebug', 'b_staticpic', 'b_pie']
+ self.base_options = {
+ OptionKey(o) for o in ['b_pch', 'b_lto', 'b_pgo', 'b_coverage',
+ 'b_ndebug', 'b_staticpic', 'b_pie']}
if not (self.info.is_windows() or self.info.is_cygwin() or self.info.is_openbsd()):
- self.base_options.append('b_lundef')
+ self.base_options.add(OptionKey('b_lundef'))
if not self.info.is_windows() or self.info.is_cygwin():
- self.base_options.append('b_asneeded')
+ self.base_options.add(OptionKey('b_asneeded'))
if not self.info.is_hurd():
- self.base_options.append('b_sanitize')
+ self.base_options.add(OptionKey('b_sanitize'))
# All GCC-like backends can do assembly
self.can_compile_suffixes.add('s')
@@ -328,7 +330,7 @@ class GnuCompiler(GnuLikeCompiler):
super().__init__()
self.id = 'gcc'
self.defines = defines or {}
- self.base_options.append('b_colorout')
+ self.base_options.add(OptionKey('b_colorout'))
def get_colorout_args(self, colortype: str) -> T.List[str]:
if mesonlib.version_compare(self.version, '>=4.9.0'):
diff --git a/mesonbuild/compilers/mixins/intel.py b/mesonbuild/compilers/mixins/intel.py
index 442e8c7..5bca254 100644
--- a/mesonbuild/compilers/mixins/intel.py
+++ b/mesonbuild/compilers/mixins/intel.py
@@ -79,8 +79,9 @@ class IntelGnuLikeCompiler(GnuLikeCompiler):
# It does have IPO, which serves much the same purpose as LOT, but
# there is an unfortunate rule for using IPO (you can't control the
# name of the output file) which break assumptions meson makes
- self.base_options = ['b_pch', 'b_lundef', 'b_asneeded', 'b_pgo',
- 'b_coverage', 'b_ndebug', 'b_staticpic', 'b_pie']
+ self.base_options = {mesonlib.OptionKey(o) for o in [
+ 'b_pch', 'b_lundef', 'b_asneeded', 'b_pgo', 'b_coverage',
+ 'b_ndebug', 'b_staticpic', 'b_pie']}
self.id = 'intel'
self.lang_header = 'none'
diff --git a/mesonbuild/compilers/mixins/islinker.py b/mesonbuild/compilers/mixins/islinker.py
index 2445eec..3fe3382 100644
--- a/mesonbuild/compilers/mixins/islinker.py
+++ b/mesonbuild/compilers/mixins/islinker.py
@@ -25,7 +25,7 @@ import typing as T
from ... import mesonlib
if T.TYPE_CHECKING:
- from ...coredata import OptionDictType
+ from ...coredata import KeyedOptionDictType
from ...environment import Environment
from ...compilers.compilers import Compiler
else:
@@ -66,7 +66,7 @@ class BasicLinkerIsCompilerMixin(Compiler):
def get_linker_lib_prefix(self) -> str:
return ''
- def get_option_link_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
return []
def has_multi_link_args(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]:
@@ -78,7 +78,7 @@ class BasicLinkerIsCompilerMixin(Compiler):
def get_std_shared_lib_link_args(self) -> T.List[str]:
return []
- def get_std_shared_module_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_std_shared_module_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
return self.get_std_shared_lib_link_args()
def get_link_whole_for(self, args: T.List[str]) -> T.List[str]:
diff --git a/mesonbuild/compilers/mixins/pgi.py b/mesonbuild/compilers/mixins/pgi.py
index 61dee8d..8461574 100644
--- a/mesonbuild/compilers/mixins/pgi.py
+++ b/mesonbuild/compilers/mixins/pgi.py
@@ -19,6 +19,7 @@ import os
from pathlib import Path
from ..compilers import clike_debug_args, clike_optimization_args
+from ...mesonlib import OptionKey
if T.TYPE_CHECKING:
from ...environment import Environment
@@ -43,7 +44,7 @@ pgi_buildtype_args = {
class PGICompiler(Compiler):
def __init__(self) -> None:
- self.base_options = ['b_pch']
+ self.base_options = {OptionKey('b_pch')}
self.id = 'pgi'
default_warn_args = ['-Minform=inform']
diff --git a/mesonbuild/compilers/mixins/visualstudio.py b/mesonbuild/compilers/mixins/visualstudio.py
index c38d59a..92f4fcd 100644
--- a/mesonbuild/compilers/mixins/visualstudio.py
+++ b/mesonbuild/compilers/mixins/visualstudio.py
@@ -129,7 +129,7 @@ class VisualStudioLikeCompiler(Compiler, metaclass=abc.ABCMeta):
INVOKES_LINKER = False
def __init__(self, target: str):
- self.base_options = ['b_pch', 'b_ndebug', 'b_vscrt'] # FIXME add lto, pgo and the like
+ self.base_options = {mesonlib.OptionKey(o) for o in ['b_pch', 'b_ndebug', 'b_vscrt']} # FIXME add lto, pgo and the like
self.target = target
self.is_64 = ('x64' in target) or ('x86_64' in target)
# do some canonicalization of target machine
diff --git a/mesonbuild/compilers/objc.py b/mesonbuild/compilers/objc.py
index 1b280eb..e47bf2f 100644
--- a/mesonbuild/compilers/objc.py
+++ b/mesonbuild/compilers/objc.py
@@ -51,7 +51,7 @@ class ObjCCompiler(CLikeCompiler, Compiler):
# TODO try to use sanity_check_impl instead of duplicated code
source_name = os.path.join(work_dir, 'sanitycheckobjc.m')
binary_name = os.path.join(work_dir, 'sanitycheckobjc')
- extra_flags = []
+ extra_flags: T.List[str] = []
extra_flags += environment.coredata.get_external_args(self.for_machine, self.language)
if self.is_cross:
extra_flags += self.get_compile_only_args()
diff --git a/mesonbuild/compilers/objcpp.py b/mesonbuild/compilers/objcpp.py
index 16ba77e..c0f93d7 100644
--- a/mesonbuild/compilers/objcpp.py
+++ b/mesonbuild/compilers/objcpp.py
@@ -50,7 +50,7 @@ class ObjCPPCompiler(CLikeCompiler, Compiler):
# TODO try to use sanity_check_impl instead of duplicated code
source_name = os.path.join(work_dir, 'sanitycheckobjcpp.mm')
binary_name = os.path.join(work_dir, 'sanitycheckobjcpp')
- extra_flags = []
+ extra_flags: T.List[str] = []
extra_flags += environment.coredata.get_external_args(self.for_machine, self.language)
if self.is_cross:
extra_flags += self.get_compile_only_args()
diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py
index 312b3b6..fd58819 100644
--- a/mesonbuild/compilers/rust.py
+++ b/mesonbuild/compilers/rust.py
@@ -17,11 +17,14 @@ import textwrap
import typing as T
from .. import coredata
-from ..mesonlib import EnvironmentException, MachineChoice, MesonException, Popen_safe
+from ..mesonlib import (
+ EnvironmentException, MachineChoice, MesonException, Popen_safe,
+ OptionKey,
+)
from .compilers import Compiler, rust_buildtype_args, clike_debug_args
if T.TYPE_CHECKING:
- from ..coredata import OptionDictType
+ from ..coredata import KeyedOptionDictType
from ..dependencies import ExternalProgram
from ..envconfig import MachineInfo
from ..environment import Environment # noqa: F401
@@ -52,9 +55,9 @@ class RustCompiler(Compiler):
linker=linker)
self.exe_wrapper = exe_wrapper
self.id = 'rustc'
- self.base_options.append('b_colorout')
+ self.base_options.add(OptionKey('b_colorout'))
if 'link' in self.linker.id:
- self.base_options.append('b_vscrt')
+ self.base_options.add(OptionKey('b_vscrt'))
def needs_static_linker(self) -> bool:
return False
@@ -133,18 +136,20 @@ class RustCompiler(Compiler):
# C compiler for dynamic linking, as such we invoke the C compiler's
# use_linker_args method instead.
- def get_options(self) -> 'OptionDictType':
+ def get_options(self) -> 'KeyedOptionDictType':
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
return {
- 'std': coredata.UserComboOption(
+ key: coredata.UserComboOption(
'Rust Eddition to use',
['none', '2015', '2018'],
'none',
),
}
- def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
args = []
- std = options['std']
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
if std.value != 'none':
args.append('--edition=' + std.value)
return args
diff --git a/mesonbuild/compilers/swift.py b/mesonbuild/compilers/swift.py
index 8682e54..7b18591 100644
--- a/mesonbuild/compilers/swift.py
+++ b/mesonbuild/compilers/swift.py
@@ -101,7 +101,7 @@ class SwiftCompiler(Compiler):
src = 'swifttest.swift'
source_name = os.path.join(work_dir, src)
output_name = os.path.join(work_dir, 'swifttest')
- extra_flags = []
+ extra_flags: T.List[str] = []
extra_flags += environment.coredata.get_external_args(self.for_machine, self.language)
if self.is_cross:
extra_flags += self.get_compile_only_args()
diff --git a/mesonbuild/compilers/vala.py b/mesonbuild/compilers/vala.py
index af800c2..80e91f6 100644
--- a/mesonbuild/compilers/vala.py
+++ b/mesonbuild/compilers/vala.py
@@ -16,7 +16,7 @@ import os.path
import typing as T
from .. import mlog
-from ..mesonlib import EnvironmentException, MachineChoice, version_compare
+from ..mesonlib import EnvironmentException, MachineChoice, version_compare, OptionKey
from .compilers import Compiler, LibType
@@ -33,7 +33,7 @@ class ValaCompiler(Compiler):
super().__init__(exelist, version, for_machine, info, is_cross=is_cross)
self.version = version
self.id = 'valac'
- self.base_options = ['b_colorout']
+ self.base_options = {OptionKey('b_colorout')}
def needs_static_linker(self) -> bool:
return False # Because compiles into C.
@@ -92,7 +92,7 @@ class ValaCompiler(Compiler):
def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
code = 'class MesonSanityCheck : Object { }'
- extra_flags = []
+ extra_flags: T.List[str] = []
extra_flags += environment.coredata.get_external_args(self.for_machine, self.language)
if self.is_cross:
extra_flags += self.get_compile_only_args()
@@ -116,7 +116,7 @@ class ValaCompiler(Compiler):
# no extra dirs are specified.
if not extra_dirs:
code = 'class MesonFindLibrary : Object { }'
- args = []
+ args: T.List[str] = []
args += env.coredata.get_external_args(self.for_machine, self.language)
vapi_args = ['--pkg', libname]
args += vapi_args
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index 310174f..cda0566 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -1,4 +1,4 @@
-# Copyrigh 2012-2020 The Meson development team
+# Copyright 2012-2020 The Meson development team
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,10 +17,11 @@ import pickle, os, uuid
import sys
from itertools import chain
from pathlib import PurePath
-from collections import OrderedDict, defaultdict
+from collections import OrderedDict
from .mesonlib import (
MesonException, EnvironmentException, MachineChoice, PerMachine,
- default_libdir, default_libexecdir, default_prefix, split_args
+ default_libdir, default_libexecdir, default_prefix, split_args,
+ OptionKey, OptionType,
)
from .wrap import WrapMode
import ast
@@ -37,6 +38,7 @@ if T.TYPE_CHECKING:
from .mesonlib import OptionOverrideProxy
OptionDictType = T.Union[T.Dict[str, 'UserOption[T.Any]'], OptionOverrideProxy]
+ KeyedOptionDictType = T.Union[T.Dict['OptionKey', 'UserOption[T.Any]'], OptionOverrideProxy]
CompilerCheckCacheKey = T.Tuple[T.Tuple[str, ...], str, str, T.Tuple[str, ...], str]
version = '0.56.99'
@@ -47,6 +49,7 @@ default_yielding = False
# Can't bind this near the class method it seems, sadly.
_T = T.TypeVar('_T')
+
class MesonVersionMismatchException(MesonException):
'''Build directory generated with Meson version is incompatible with current version'''
def __init__(self, old_version: str, current_version: str) -> None:
@@ -298,16 +301,17 @@ class DependencyCache:
successfully lookup by providing a simple get/put interface.
"""
- def __init__(self, builtins_per_machine: PerMachine[T.Dict[str, UserOption[T.Any]]], for_machine: MachineChoice):
+ def __init__(self, builtins: 'KeyedOptionDictType', for_machine: MachineChoice):
self.__cache = OrderedDict() # type: T.MutableMapping[CacheKeyType, DependencySubCache]
- self.__builtins_per_machine = builtins_per_machine
- self.__for_machine = for_machine
+ self.__builtins = builtins
+ self.__pkg_conf_key = OptionKey('pkg_config_path', machine=for_machine)
+ self.__cmake_key = OptionKey('cmake_prefix_path', machine=for_machine)
def __calculate_subkey(self, type_: DependencyCacheType) -> T.Tuple[T.Any, ...]:
if type_ is DependencyCacheType.PKG_CONFIG:
- return tuple(self.__builtins_per_machine[self.__for_machine]['pkg_config_path'].value)
+ return tuple(self.__builtins[self.__pkg_conf_key].value)
elif type_ is DependencyCacheType.CMAKE:
- return tuple(self.__builtins_per_machine[self.__for_machine]['cmake_prefix_path'].value)
+ return tuple(self.__builtins[self.__cmake_key].value)
assert type_ is DependencyCacheType.OTHER, 'Someone forgot to update subkey calculations for a new type'
return tuple()
@@ -381,20 +385,12 @@ class CoreData:
self.meson_command = meson_command
self.target_guids = {}
self.version = version
- self.builtins = {} # type: OptionDictType
- self.builtins_per_machine = PerMachine({}, {})
- self.backend_options = {} # type: OptionDictType
- self.user_options = {} # type: OptionDictType
- self.compiler_options = PerMachine(
- defaultdict(dict),
- defaultdict(dict),
- ) # type: PerMachine[T.defaultdict[str, OptionDictType]]
- self.base_options = {} # type: OptionDictType
+ self.options: 'KeyedOptionDictType' = {}
self.cross_files = self.__load_config_files(options, scratch_dir, 'cross')
self.compilers = PerMachine(OrderedDict(), OrderedDict()) # type: PerMachine[T.Dict[str, Compiler]]
- build_cache = DependencyCache(self.builtins_per_machine, MachineChoice.BUILD)
- host_cache = DependencyCache(self.builtins_per_machine, MachineChoice.BUILD)
+ build_cache = DependencyCache(self.options, MachineChoice.BUILD)
+ host_cache = DependencyCache(self.options, MachineChoice.BUILD)
self.deps = PerMachine(build_cache, host_cache) # type: PerMachine[DependencyCache]
self.compiler_check_cache = OrderedDict() # type: T.Dict[CompilerCheckCacheKey, compiler.CompileResult]
@@ -466,7 +462,7 @@ class CoreData:
# getting the "system default" is always wrong on multiarch
# platforms as it gets a value like lib/x86_64-linux-gnu.
if self.cross_files:
- BUILTIN_OPTIONS['libdir'].default = 'lib'
+ BUILTIN_OPTIONS[OptionKey('libdir')].default = 'lib'
def sanitize_prefix(self, prefix):
prefix = os.path.expanduser(prefix)
@@ -486,7 +482,7 @@ class CoreData:
prefix = prefix[:-1]
return prefix
- def sanitize_dir_option_value(self, prefix: str, option: str, value: T.Any) -> T.Any:
+ def sanitize_dir_option_value(self, prefix: str, option: OptionKey, value: T.Any) -> T.Any:
'''
If the option is an installation directory option and the value is an
absolute path, check that it resides within prefix and return the value
@@ -501,13 +497,13 @@ class CoreData:
value = PurePath(value)
except TypeError:
return value
- if option.endswith('dir') and value.is_absolute() and \
- option not in builtin_dir_noprefix_options:
+ if option.name.endswith('dir') and value.is_absolute() and \
+ option not in BULITIN_DIR_NOPREFIX_OPTIONS:
# Value must be a subdir of the prefix
# commonpath will always return a path in the native format, so we
# must use pathlib.PurePath to do the same conversion before
# comparing.
- msg = ('The value of the {!r} option is \'{!s}\' which must be a '
+ msg = ('The value of the \'{!s}\' option is \'{!s}\' which must be a '
'subdir of the prefix {!r}.\nNote that if you pass a '
'relative path, it is assumed to be a subdir of prefix.')
# os.path.commonpath doesn't understand case-insensitive filesystems,
@@ -520,81 +516,76 @@ class CoreData:
raise MesonException(msg.format(option, value, prefix))
return value.as_posix()
- def init_builtins(self, subproject: str):
+ def init_builtins(self, subproject: str) -> None:
# Create builtin options with default values
for key, opt in BUILTIN_OPTIONS.items():
- self.add_builtin_option(self.builtins, key, opt, subproject)
+ self.add_builtin_option(self.options, key.evolve(subproject=subproject), opt)
for for_machine in iter(MachineChoice):
for key, opt in BUILTIN_OPTIONS_PER_MACHINE.items():
- self.add_builtin_option(self.builtins_per_machine[for_machine], key, opt, subproject)
+ self.add_builtin_option(self.options, key.evolve(subproject=subproject, machine=for_machine), opt)
- def add_builtin_option(self, opts_map, key, opt, subproject):
- if subproject:
+ @staticmethod
+ def add_builtin_option(opts_map: 'KeyedOptionDictType', key: OptionKey,
+ opt: 'BuiltinOption') -> None:
+ if key.subproject:
if opt.yielding:
# This option is global and not per-subproject
return
- optname = subproject + ':' + key
- value = opts_map[key].value
+ value = opts_map[key.as_root()].value
else:
- optname = key
value = None
- opts_map[optname] = opt.init_option(key, value, default_prefix())
+ opts_map[key] = opt.init_option(key, value, default_prefix())
def init_backend_options(self, backend_name: str) -> None:
if backend_name == 'ninja':
- self.backend_options['backend_max_links'] = \
- UserIntegerOption(
- 'Maximum number of linker processes to run or 0 for no '
- 'limit',
- (0, None, 0))
+ self.options[OptionKey('backend_max_links')] = UserIntegerOption(
+ 'Maximum number of linker processes to run or 0 for no '
+ 'limit',
+ (0, None, 0))
elif backend_name.startswith('vs'):
- self.backend_options['backend_startup_project'] = \
- UserStringOption(
- 'Default project to execute in Visual Studio',
- '')
+ self.options[OptionKey('backend_startup_project')] = UserStringOption(
+ 'Default project to execute in Visual Studio',
+ '')
- def get_builtin_option(self, optname: str, subproject: str = '') -> T.Union[str, int, bool]:
- raw_optname = optname
- if subproject:
- optname = subproject + ':' + optname
- for opts in self._get_all_builtin_options():
- v = opts.get(optname)
- if v is None or v.yielding:
- v = opts.get(raw_optname)
- if v is None:
- continue
- if raw_optname == 'wrap_mode':
- return WrapMode.from_string(v.value)
- return v.value
- raise RuntimeError('Tried to get unknown builtin option %s.' % raw_optname)
-
- def _try_set_builtin_option(self, optname, value):
- for opts in self._get_all_builtin_options():
- opt = opts.get(optname)
- if opt is None:
- continue
- if optname == 'prefix':
+ def get_option(self, key: OptionKey) -> T.Union[str, int, bool, WrapMode]:
+ try:
+ v = self.options[key].value
+ if key.name == 'wrap_mode':
+ return WrapMode[v]
+ return v
+ except KeyError:
+ pass
+
+ try:
+ v = self.options[key.as_root()]
+ if v.yielding:
+ if key.name == 'wrap_mode':
+ return WrapMode[v.value]
+ return v.value
+ except KeyError:
+ pass
+
+ raise MesonException(f'Tried to get unknown builtin option {str(key)}')
+
+ def set_option(self, key: OptionKey, value) -> None:
+ if key.is_builtin():
+ if key.name == 'prefix':
value = self.sanitize_prefix(value)
else:
- prefix = self.builtins['prefix'].value
- value = self.sanitize_dir_option_value(prefix, optname, value)
- break
- else:
- return False
- opt.set_value(value)
- # Make sure that buildtype matches other settings.
- if optname == 'buildtype':
- self.set_others_from_buildtype(value)
- else:
- self.set_buildtype_from_others()
- return True
+ prefix = self.options[OptionKey('prefix')].value
+ value = self.sanitize_dir_option_value(prefix, key, value)
+
+ try:
+ self.options[key].set_value(value)
+ except KeyError:
+ raise MesonException(f'Tried to set unknown builtin option {str(key)}')
- def set_builtin_option(self, optname, value):
- res = self._try_set_builtin_option(optname, value)
- if not res:
- raise RuntimeError('Tried to set unknown builtin option %s.' % optname)
+ if key.name == 'buildtype':
+ self._set_others_from_buildtype(value)
+ elif key.name in {'debug', 'optimization'}:
+ self._set_buildtype_from_others()
- def set_others_from_buildtype(self, value):
+ def _set_others_from_buildtype(self, value: str) -> None:
if value == 'plain':
opt = '0'
debug = False
@@ -613,12 +604,12 @@ class CoreData:
else:
assert(value == 'custom')
return
- self.builtins['optimization'].set_value(opt)
- self.builtins['debug'].set_value(debug)
+ self.options[OptionKey('optimization')].set_value(opt)
+ self.options[OptionKey('debug')].set_value(debug)
- def set_buildtype_from_others(self):
- opt = self.builtins['optimization'].value
- debug = self.builtins['debug'].value
+ def _set_buildtype_from_others(self) -> None:
+ opt = self.options[OptionKey('optimization')].value
+ debug = self.options[OptionKey('debug')].value
if opt == '0' and not debug:
mode = 'plain'
elif opt == '0' and debug:
@@ -631,214 +622,144 @@ class CoreData:
mode = 'minsize'
else:
mode = 'custom'
- self.builtins['buildtype'].set_value(mode)
-
- @classmethod
- def get_prefixed_options_per_machine(
- cls,
- options_per_machine # : PerMachine[T.Dict[str, _V]]]
- ) -> T.Iterable[T.Tuple[str, _V]]:
- return cls._flatten_pair_iterator(
- (for_machine.get_prefix(), options_per_machine[for_machine])
- for for_machine in iter(MachineChoice)
- )
-
- @classmethod
- def flatten_lang_iterator(
- cls,
- outer # : T.Iterable[T.Tuple[str, T.Dict[str, _V]]]
- ) -> T.Iterable[T.Tuple[str, _V]]:
- return cls._flatten_pair_iterator((lang + '_', opts) for lang, opts in outer)
+ self.options[OptionKey('buildtype')].set_value(mode)
@staticmethod
- def _flatten_pair_iterator(
- outer # : T.Iterable[T.Tuple[str, T.Dict[str, _V]]]
- ) -> T.Iterable[T.Tuple[str, _V]]:
- for k0, v0 in outer:
- for k1, v1 in v0.items():
- yield (k0 + k1, v1)
-
- @classmethod
- def insert_build_prefix(cls, k):
- idx = k.find(':')
- if idx < 0:
- return 'build.' + k
- return k[:idx + 1] + 'build.' + k[idx + 1:]
-
- @classmethod
- def is_per_machine_option(cls, optname):
- if optname in BUILTIN_OPTIONS_PER_MACHINE:
+ def is_per_machine_option(optname: OptionKey) -> bool:
+ if optname.name in BUILTIN_OPTIONS_PER_MACHINE:
return True
- from .compilers import compilers
- for lang_prefix in [lang + '_' for lang in compilers.all_languages]:
- if optname.startswith(lang_prefix):
- return True
- return False
-
- def _get_all_nonbuiltin_options(self) -> T.Iterable[T.Dict[str, UserOption]]:
- yield self.backend_options
- yield self.user_options
- yield dict(self.flatten_lang_iterator(self.get_prefixed_options_per_machine(self.compiler_options)))
- yield self.base_options
-
- def _get_all_builtin_options(self) -> T.Iterable[T.Dict[str, UserOption]]:
- yield dict(self.get_prefixed_options_per_machine(self.builtins_per_machine))
- yield self.builtins
-
- def get_all_options(self) -> T.Iterable[T.Dict[str, UserOption]]:
- yield from self._get_all_nonbuiltin_options()
- yield from self._get_all_builtin_options()
-
- def validate_option_value(self, option_name, override_value):
- for opts in self.get_all_options():
- opt = opts.get(option_name)
- if opt is not None:
- try:
- return opt.validate_value(override_value)
- except MesonException as e:
- raise type(e)(('Validation failed for option %s: ' % option_name) + str(e)) \
- .with_traceback(sys.exc_info()[2])
- raise MesonException('Tried to validate unknown option %s.' % option_name)
+ return optname.lang is not None
+
+ def validate_option_value(self, option_name: OptionKey, override_value):
+ try:
+ opt = self.options[option_name]
+ except KeyError:
+ raise MesonException(f'Tried to validate unknown option {str(option_name)}')
+ try:
+ return opt.validate_value(override_value)
+ except MesonException as e:
+ raise type(e)(('Validation failed for option %s: ' % option_name) + str(e)) \
+ .with_traceback(sys.exc_info()[2])
- def get_external_args(self, for_machine: MachineChoice, lang):
- return self.compiler_options[for_machine][lang]['args'].value
+ def get_external_args(self, for_machine: MachineChoice, lang: str) -> T.Union[str, T.List[str]]:
+ return self.options[OptionKey('args', machine=for_machine, lang=lang)].value
- def get_external_link_args(self, for_machine: MachineChoice, lang):
- return self.compiler_options[for_machine][lang]['link_args'].value
+ def get_external_link_args(self, for_machine: MachineChoice, lang: str) -> T.Union[str, T.List[str]]:
+ return self.options[OptionKey('link_args', machine=for_machine, lang=lang)].value
- def merge_user_options(self, options: T.Dict[str, UserOption[T.Any]]) -> None:
- for (name, value) in options.items():
- if name not in self.user_options:
- self.user_options[name] = value
+ def update_project_options(self, options: 'KeyedOptionDictType') -> None:
+ for key, value in options.items():
+ if not key.is_project():
+ continue
+ if key not in self.options:
+ self.options[key] = value
continue
- oldval = self.user_options[name]
+ oldval = self.options[key]
if type(oldval) != type(value):
- self.user_options[name] = value
+ self.options[key] = value
elif oldval.choices != value.choices:
# If the choices have changed, use the new value, but attempt
# to keep the old options. If they are not valid keep the new
# defaults but warn.
- self.user_options[name] = value
+ self.options[key] = value
try:
value.set_value(oldval.value)
except MesonException as e:
- mlog.warning('Old value(s) of {} are no longer valid, resetting to default ({}).'.format(name, value.value))
+ mlog.warning('Old value(s) of {} are no longer valid, resetting to default ({}).'.format(key, value.value))
def is_cross_build(self, when_building_for: MachineChoice = MachineChoice.HOST) -> bool:
if when_building_for == MachineChoice.BUILD:
return False
return len(self.cross_files) > 0
- def strip_build_option_names(self, options):
- res = OrderedDict()
- for k, v in options.items():
- if k.startswith('build.'):
- k = k.split('.', 1)[1]
- res.setdefault(k, v)
- else:
- res[k] = v
- return res
-
- def copy_build_options_from_regular_ones(self):
- assert(not self.is_cross_build())
- for k, o in self.builtins_per_machine.host.items():
- self.builtins_per_machine.build[k].set_value(o.value)
- for lang, host_opts in self.compiler_options.host.items():
- build_opts = self.compiler_options.build[lang]
- for k, o in host_opts.items():
- if k in build_opts:
- build_opts[k].set_value(o.value)
-
- def set_options(self, options: T.Dict[str, T.Any], subproject: str = '', warn_unknown: bool = True) -> None:
+ def copy_build_options_from_regular_ones(self) -> None:
+ assert not self.is_cross_build()
+ for k in BUILTIN_OPTIONS_PER_MACHINE:
+ o = self.options[k]
+ self.options[k.as_build()].set_value(o.value)
+ for bk, bv in self.options.items():
+ if bk.machine is MachineChoice.BUILD:
+ hk = bk.as_host()
+ try:
+ hv = self.options[hk]
+ bv.set_value(hv.value)
+ except KeyError:
+ continue
+
+ def set_options(self, options: T.Dict[OptionKey, T.Any], subproject: str = '', warn_unknown: bool = True) -> None:
if not self.is_cross_build():
- options = self.strip_build_option_names(options)
+ options = {k: v for k, v in options.items() if k.machine is not MachineChoice.BUILD}
# Set prefix first because it's needed to sanitize other options
- if 'prefix' in options:
- prefix = self.sanitize_prefix(options['prefix'])
- self.builtins['prefix'].set_value(prefix)
- for key in builtin_dir_noprefix_options:
+ pfk = OptionKey('prefix')
+ if pfk in options:
+ prefix = self.sanitize_prefix(options[pfk])
+ self.options[OptionKey('prefix')].set_value(prefix)
+ for key in BULITIN_DIR_NOPREFIX_OPTIONS:
if key not in options:
- self.builtins[key].set_value(BUILTIN_OPTIONS[key].prefixed_default(key, prefix))
+ self.options[key].set_value(BUILTIN_OPTIONS[key].prefixed_default(key, prefix))
- unknown_options = []
+ unknown_options: T.List[OptionKey] = []
for k, v in options.items():
- if k == 'prefix':
- continue
- if self._try_set_builtin_option(k, v):
+ if k == pfk:
continue
- for opts in self._get_all_nonbuiltin_options():
- tgt = opts.get(k)
- if tgt is None:
- continue
- tgt.set_value(v)
- break
- else:
+ elif k not in self.options:
unknown_options.append(k)
+ else:
+ self.set_option(k, v)
if unknown_options and warn_unknown:
- unknown_options = ', '.join(sorted(unknown_options))
+ unknown_options_str = ', '.join(sorted(str(s) for s in unknown_options))
sub = 'In subproject {}: '.format(subproject) if subproject else ''
- mlog.warning('{}Unknown options: "{}"'.format(sub, unknown_options))
+ mlog.warning('{}Unknown options: "{}"'.format(sub, unknown_options_str))
mlog.log('The value of new options can be set with:')
mlog.log(mlog.bold('meson setup <builddir> --reconfigure -Dnew_option=new_value ...'))
if not self.is_cross_build():
self.copy_build_options_from_regular_ones()
- def set_default_options(self, default_options: 'T.OrderedDict[str, str]', subproject: str, env: 'Environment') -> None:
- # Preserve order: if env.raw_options has 'buildtype' it must come after
+ def set_default_options(self, default_options: T.MutableMapping[OptionKey, str], subproject: str, env: 'Environment') -> None:
+ # Preserve order: if env.options has 'buildtype' it must come after
# 'optimization' if it is in default_options.
- raw_options = OrderedDict()
- for k, v in default_options.items():
- if subproject:
- k = subproject + ':' + k
- raw_options[k] = v
- raw_options.update(env.raw_options)
- env.raw_options = raw_options
-
- # Create a subset of raw_options, keeping only project and builtin
+ options: T.MutableMapping[OptionKey, T.Any]
+ if not subproject:
+ options = OrderedDict(default_options)
+ options.update(env.options)
+ env.options = options
+
+ # Create a subset of options, keeping only project and builtin
# options for this subproject.
# 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).
options = OrderedDict()
- from . import optinterpreter
- for k, v in env.raw_options.items():
- raw_optname = k
- if subproject:
- # Subproject: skip options for other subprojects
- if not k.startswith(subproject + ':'):
- continue
- raw_optname = k.split(':')[1]
- elif ':' in k:
- # Main prject: skip options for subprojects
+ for k, v in chain(default_options.items(), env.options.items()):
+ # If this is a subproject, don't use other subproject options
+ if k.subproject and k.subproject != subproject:
+ continue
+ # If the option is a builtin and is yielding then it's not allowed per subproject.
+ if subproject and k.is_builtin() and self.options[k.as_root()].yielding:
continue
# Skip base, compiler, and backend options, they are handled when
# adding languages and setting backend.
- if (k not in self.builtins and
- k not in self.get_prefixed_options_per_machine(self.builtins_per_machine) and
- optinterpreter.is_invalid_name(raw_optname, log=False)):
+ if k.type in {OptionType.COMPILER, OptionType.BACKEND, OptionType.BASE}:
continue
options[k] = v
self.set_options(options, subproject=subproject)
- def add_compiler_options(self, options, lang, for_machine, env):
- # prefixed compiler options affect just this machine
- opt_prefix = for_machine.get_prefix()
+ def add_compiler_options(self, options: 'KeyedOptionDictType', lang: str, for_machine: MachineChoice,
+ env: 'Environment') -> None:
for k, o in options.items():
- optname = opt_prefix + lang + '_' + k
- value = env.raw_options.get(optname)
+ value = env.options.get(k)
if value is not None:
o.set_value(value)
- self.compiler_options[for_machine][lang].setdefault(k, o)
+ self.options.setdefault(k, o)
def add_lang_args(self, lang: str, comp: T.Type['Compiler'],
for_machine: MachineChoice, env: 'Environment') -> None:
"""Add global language arguments that are needed before compiler/linker detection."""
from .compilers import compilers
- options = compilers.get_global_options(lang, comp, for_machine,
- env.is_cross_build())
+ options = compilers.get_global_options(lang, comp, for_machine, env.is_cross_build())
self.add_compiler_options(options, lang, for_machine, env)
def process_new_compiler(self, lang: str, comp: 'Compiler', env: 'Environment') -> None:
@@ -847,19 +768,19 @@ class CoreData:
self.compilers[comp.for_machine][lang] = comp
self.add_compiler_options(comp.get_options(), lang, comp.for_machine, env)
- enabled_opts = []
- for optname in comp.base_options:
- if optname in self.base_options:
+ enabled_opts: T.List[OptionKey] = []
+ for key in comp.base_options:
+ if key in self.options:
continue
- oobj = compilers.base_options[optname]
- if optname in env.raw_options:
- oobj.set_value(env.raw_options[optname])
- enabled_opts.append(optname)
- self.base_options[optname] = oobj
+ oobj = compilers.base_options[key]
+ if key in env.options:
+ oobj.set_value(env.options[key])
+ enabled_opts.append(key)
+ self.options[key] = oobj
self.emit_base_options_warnings(enabled_opts)
- def emit_base_options_warnings(self, enabled_opts: list):
- if 'b_bitcode' in enabled_opts:
+ def emit_base_options_warnings(self, enabled_opts: T.List[OptionKey]) -> None:
+ if OptionKey('b_bitcode') in enabled_opts:
mlog.warning('Base option \'b_bitcode\' is enabled, which is incompatible with many linker options. Incompatible options such as \'b_asneeded\' have been disabled.', fatal=False)
mlog.warning('Please see https://mesonbuild.com/Builtin-options.html#Notes_about_Apple_Bitcode_support for more details.', fatal=False)
@@ -949,7 +870,7 @@ def read_cmd_line_file(build_dir: str, options: argparse.Namespace) -> None:
# Do a copy because config is not really a dict. options.cmd_line_options
# overrides values from the file.
- d = dict(config['options'])
+ d = {OptionKey.from_string(k): v for k, v in config['options'].items()}
d.update(options.cmd_line_options)
options.cmd_line_options = d
@@ -961,9 +882,6 @@ def read_cmd_line_file(build_dir: str, options: argparse.Namespace) -> None:
# literal_eval to get it into the list of strings.
options.native_file = ast.literal_eval(properties.get('native_file', '[]'))
-def cmd_line_options_to_string(options: argparse.Namespace) -> T.Dict[str, str]:
- return {k: str(v) for k, v in options.cmd_line_options.items()}
-
def write_cmd_line_file(build_dir: str, options: argparse.Namespace) -> None:
filename = get_cmd_line_file(build_dir)
config = CmdLineFileParser()
@@ -974,7 +892,7 @@ def write_cmd_line_file(build_dir: str, options: argparse.Namespace) -> None:
if options.native_file:
properties['native_file'] = [os.path.abspath(f) for f in options.native_file]
- config['options'] = cmd_line_options_to_string(options)
+ config['options'] = {str(k): str(v) for k, v in options.cmd_line_options.items()}
config['properties'] = properties
with open(filename, 'w') as f:
config.write(f)
@@ -983,14 +901,14 @@ def update_cmd_line_file(build_dir: str, options: argparse.Namespace):
filename = get_cmd_line_file(build_dir)
config = CmdLineFileParser()
config.read(filename)
- config['options'].update(cmd_line_options_to_string(options))
+ config['options'].update({str(k): str(v) for k, v in options.cmd_line_options.items()})
with open(filename, 'w') as f:
config.write(f)
def get_cmd_line_options(build_dir: str, options: argparse.Namespace) -> str:
copy = argparse.Namespace(**vars(options))
read_cmd_line_file(build_dir, copy)
- cmdline = ['-D{}={}'.format(k, v) for k, v in copy.cmd_line_options.items()]
+ cmdline = ['-D{}={}'.format(str(k), v) for k, v in copy.cmd_line_options.items()]
if options.cross_file:
cmdline += ['--cross-file {}'.format(f) for f in options.cross_file]
if options.native_file:
@@ -1038,39 +956,43 @@ def save(obj: CoreData, build_dir: str) -> str:
def register_builtin_arguments(parser: argparse.ArgumentParser) -> None:
for n, b in BUILTIN_OPTIONS.items():
- b.add_to_argparse(n, parser, '', '')
+ b.add_to_argparse(str(n), parser, '')
for n, b in BUILTIN_OPTIONS_PER_MACHINE.items():
- b.add_to_argparse(n, parser, '', ' (just for host machine)')
- b.add_to_argparse(n, parser, 'build.', ' (just for build machine)')
+ b.add_to_argparse(str(n), parser, ' (just for host machine)')
+ b.add_to_argparse(str(n.as_build()), parser, ' (just for build machine)')
parser.add_argument('-D', action='append', dest='projectoptions', default=[], metavar="option",
help='Set the value of an option, can be used several times to set multiple options.')
-def create_options_dict(options: T.List[str]) -> T.Dict[str, str]:
- result = OrderedDict()
+def create_options_dict(options: T.List[str], subproject: str = '') -> T.Dict[OptionKey, str]:
+ result: T.OrderedDict[OptionKey, str] = OrderedDict()
for o in options:
try:
(key, value) = o.split('=', 1)
except ValueError:
raise MesonException('Option {!r} must have a value separated by equals sign.'.format(o))
- result[key] = value
+ k = OptionKey.from_string(key)
+ if subproject:
+ k = k.evolve(subproject=subproject)
+ result[k] = value
return result
def parse_cmd_line_options(args: argparse.Namespace) -> None:
args.cmd_line_options = create_options_dict(args.projectoptions)
# Merge builtin options set with --option into the dict.
- for name in chain(
+ for key in chain(
BUILTIN_OPTIONS.keys(),
- ('build.' + k for k in BUILTIN_OPTIONS_PER_MACHINE.keys()),
+ (k.as_build() for k in BUILTIN_OPTIONS_PER_MACHINE.keys()),
BUILTIN_OPTIONS_PER_MACHINE.keys(),
):
+ name = str(key)
value = getattr(args, name, None)
if value is not None:
- if name in args.cmd_line_options:
+ if key in args.cmd_line_options:
cmdline_name = BuiltinOption.argparse_name_to_arg(name)
raise MesonException(
'Got argument {0} as both -D{0} and {1}. Pick one.'.format(name, cmdline_name))
- args.cmd_line_options[name] = value
+ args.cmd_line_options[key] = value
delattr(args, name)
@@ -1091,7 +1013,7 @@ class BuiltinOption(T.Generic[_T, _U]):
self.choices = choices
self.yielding = yielding
- def init_option(self, name: str, value: T.Optional[T.Any], prefix: str) -> _U:
+ def init_option(self, name: 'OptionKey', value: T.Optional[T.Any], prefix: str) -> _U:
"""Create an instance of opt_type and return it."""
if value is None:
value = self.prefixed_default(name, prefix)
@@ -1122,16 +1044,16 @@ class BuiltinOption(T.Generic[_T, _U]):
else:
return '--' + name.replace('_', '-')
- def prefixed_default(self, name: str, prefix: str = '') -> T.Any:
+ def prefixed_default(self, name: 'OptionKey', prefix: str = '') -> T.Any:
if self.opt_type in [UserComboOption, UserIntegerOption]:
return self.default
try:
- return builtin_dir_noprefix_options[name][prefix]
+ return BULITIN_DIR_NOPREFIX_OPTIONS[name][prefix]
except KeyError:
pass
return self.default
- def add_to_argparse(self, name: str, parser: argparse.ArgumentParser, prefix: str, help_suffix: str) -> None:
+ def add_to_argparse(self, name: str, parser: argparse.ArgumentParser, help_suffix: str) -> None:
kwargs = OrderedDict()
c = self._argparse_choices()
@@ -1144,64 +1066,65 @@ class BuiltinOption(T.Generic[_T, _U]):
if c and not b:
kwargs['choices'] = c
kwargs['default'] = argparse.SUPPRESS
- kwargs['dest'] = prefix + name
+ kwargs['dest'] = name
- cmdline_name = self.argparse_name_to_arg(prefix + name)
+ cmdline_name = self.argparse_name_to_arg(name)
parser.add_argument(cmdline_name, help=h + help_suffix, **kwargs)
# Update `docs/markdown/Builtin-options.md` after changing the options below
-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')),
-]) # 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',
- choices=['plain', 'debug', 'debugoptimized', 'release', 'minsize', 'custom'])),
- ('debug', BuiltinOption(UserBooleanOption, 'Debug', True)),
- ('default_library', BuiltinOption(UserComboOption, 'Default library type', 'shared', choices=['shared', 'static', 'both'],
- yielding=False)),
- ('errorlogs', BuiltinOption(UserBooleanOption, "Whether to print the logs from failing tests", True)),
- ('install_umask', BuiltinOption(UserUmaskOption, 'Default umask to apply on permissions of installed files', '022')),
- ('layout', BuiltinOption(UserComboOption, 'Build directory layout', 'mirror', choices=['mirror', 'flat'])),
- ('optimization', BuiltinOption(UserComboOption, 'Optimization level', '0', choices=['0', 'g', '1', '2', '3', 's'])),
- ('stdsplit', BuiltinOption(UserBooleanOption, 'Split stdout and stderr in test logs', True)),
- ('strip', BuiltinOption(UserBooleanOption, 'Strip targets on install', False)),
- ('unity', BuiltinOption(UserComboOption, 'Unity build', 'off', choices=['on', 'off', 'subprojects'])),
- ('unity_size', BuiltinOption(UserIntegerOption, 'Unity block size', (2, None, 4))),
- ('warning_level', BuiltinOption(UserComboOption, 'Compiler warning level to use', '1', choices=['0', '1', '2', '3'], yielding=False)),
- ('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
+# Also update mesonlib._BUILTIN_NAMES. See the comment there for why this is required.
+BUILTIN_DIR_OPTIONS: 'KeyedOptionDictType' = OrderedDict([
+ (OptionKey('prefix'), BuiltinOption(UserStringOption, 'Installation prefix', default_prefix())),
+ (OptionKey('bindir'), BuiltinOption(UserStringOption, 'Executable directory', 'bin')),
+ (OptionKey('datadir'), BuiltinOption(UserStringOption, 'Data file directory', 'share')),
+ (OptionKey('includedir'), BuiltinOption(UserStringOption, 'Header file directory', 'include')),
+ (OptionKey('infodir'), BuiltinOption(UserStringOption, 'Info page directory', 'share/info')),
+ (OptionKey('libdir'), BuiltinOption(UserStringOption, 'Library directory', default_libdir())),
+ (OptionKey('libexecdir'), BuiltinOption(UserStringOption, 'Library executable directory', default_libexecdir())),
+ (OptionKey('localedir'), BuiltinOption(UserStringOption, 'Locale data directory', 'share/locale')),
+ (OptionKey('localstatedir'), BuiltinOption(UserStringOption, 'Localstate data directory', 'var')),
+ (OptionKey('mandir'), BuiltinOption(UserStringOption, 'Manual page directory', 'share/man')),
+ (OptionKey('sbindir'), BuiltinOption(UserStringOption, 'System executable directory', 'sbin')),
+ (OptionKey('sharedstatedir'), BuiltinOption(UserStringOption, 'Architecture-independent data directory', 'com')),
+ (OptionKey('sysconfdir'), BuiltinOption(UserStringOption, 'Sysconf data directory', 'etc')),
+])
+
+BUILTIN_CORE_OPTIONS: 'KeyedOptionDictType' = OrderedDict([
+ (OptionKey('auto_features'), BuiltinOption(UserFeatureOption, "Override value of all 'auto' features", 'auto')),
+ (OptionKey('backend'), BuiltinOption(UserComboOption, 'Backend to use', 'ninja', choices=backendlist)),
+ (OptionKey('buildtype'), BuiltinOption(UserComboOption, 'Build type to use', 'debug',
+ choices=['plain', 'debug', 'debugoptimized', 'release', 'minsize', 'custom'])),
+ (OptionKey('debug'), BuiltinOption(UserBooleanOption, 'Debug', True)),
+ (OptionKey('default_library'), BuiltinOption(UserComboOption, 'Default library type', 'shared', choices=['shared', 'static', 'both'],
+ yielding=False)),
+ (OptionKey('errorlogs'), BuiltinOption(UserBooleanOption, "Whether to print the logs from failing tests", True)),
+ (OptionKey('install_umask'), BuiltinOption(UserUmaskOption, 'Default umask to apply on permissions of installed files', '022')),
+ (OptionKey('layout'), BuiltinOption(UserComboOption, 'Build directory layout', 'mirror', choices=['mirror', 'flat'])),
+ (OptionKey('optimization'), BuiltinOption(UserComboOption, 'Optimization level', '0', choices=['0', 'g', '1', '2', '3', 's'])),
+ (OptionKey('stdsplit'), BuiltinOption(UserBooleanOption, 'Split stdout and stderr in test logs', True)),
+ (OptionKey('strip'), BuiltinOption(UserBooleanOption, 'Strip targets on install', False)),
+ (OptionKey('unity'), BuiltinOption(UserComboOption, 'Unity build', 'off', choices=['on', 'off', 'subprojects'])),
+ (OptionKey('unity_size'), BuiltinOption(UserIntegerOption, 'Unity block size', (2, None, 4))),
+ (OptionKey('warning_level'), BuiltinOption(UserComboOption, 'Compiler warning level to use', '1', choices=['0', '1', '2', '3'], yielding=False)),
+ (OptionKey('werror'), BuiltinOption(UserBooleanOption, 'Treat warnings as errors', False, yielding=False)),
+ (OptionKey('wrap_mode'), BuiltinOption(UserComboOption, 'Wrap mode', 'default', choices=['default', 'nofallback', 'nodownload', 'forcefallback'])),
+ (OptionKey('force_fallback_for'), BuiltinOption(UserArrayOption, 'Force fallback for those subprojects', [])),
+])
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', [])),
- ('cmake_prefix_path', BuiltinOption(UserArrayOption, 'List of additional prefixes for cmake to search', [])),
+BUILTIN_OPTIONS_PER_MACHINE: 'KeyedOptionDictType' = OrderedDict([
+ (OptionKey('pkg_config_path'), BuiltinOption(UserArrayOption, 'List of additional paths for pkg-config to search', [])),
+ (OptionKey('cmake_prefix_path'), BuiltinOption(UserArrayOption, 'List of additional prefixes for cmake to search', [])),
])
# Special prefix-dependent defaults for installation directories that reside in
# a path outside of the prefix in FHS and common usage.
-builtin_dir_noprefix_options = {
- 'sysconfdir': {'/usr': '/etc'},
- 'localstatedir': {'/usr': '/var', '/usr/local': '/var/local'},
- 'sharedstatedir': {'/usr': '/var/lib', '/usr/local': '/var/local/lib'},
+BULITIN_DIR_NOPREFIX_OPTIONS: T.Dict[OptionKey, T.Dict[str, str]] = {
+ OptionKey('sysconfdir'): {'/usr': '/etc'},
+ OptionKey('localstatedir'): {'/usr': '/var', '/usr/local': '/var/local'},
+ OptionKey('sharedstatedir'): {'/usr': '/var/lib', '/usr/local': '/var/local/lib'},
}
FORBIDDEN_TARGET_NAMES = {'clean': None,
diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py
index 6f568d3..e359219 100644
--- a/mesonbuild/dependencies/base.py
+++ b/mesonbuild/dependencies/base.py
@@ -37,7 +37,7 @@ from ..environment import Environment, MachineInfo
from ..cmake import CMakeExecutor, CMakeTraceParser, CMakeException, CMakeToolchain, CMakeExecScope, check_cmake_args
from ..mesonlib import MachineChoice, MesonException, OrderedSet, PerMachine
from ..mesonlib import Popen_safe, version_compare_many, version_compare, listify, stringlistify, extract_as_list, split_args
-from ..mesonlib import Version, LibType
+from ..mesonlib import Version, LibType, OptionKey
from ..mesondata import mesondata
if T.TYPE_CHECKING:
@@ -656,8 +656,9 @@ class PkgConfigDependency(ExternalDependency):
return rc, out, err
@staticmethod
- def setup_env(env, environment, for_machine, extra_path=None):
- extra_paths = environment.coredata.builtins_per_machine[for_machine]['pkg_config_path'].value
+ def setup_env(env: T.MutableMapping[str, str], environment: 'Environment', for_machine: MachineChoice,
+ extra_path: T.Optional[str] = None) -> None:
+ extra_paths: T.List[str] = environment.coredata.options[OptionKey('pkg_config_path', machine=for_machine)].value
if extra_path:
extra_paths.append(extra_path)
sysroot = environment.properties[for_machine].get_sys_root()
@@ -1484,12 +1485,12 @@ class CMakeDependency(ExternalDependency):
cfgs = [x for x in tgt.properties['IMPORTED_CONFIGURATIONS'] if x]
cfg = cfgs[0]
- if 'b_vscrt' in self.env.coredata.base_options:
- is_debug = self.env.coredata.get_builtin_option('buildtype') == 'debug'
- if self.env.coredata.base_options['b_vscrt'].value in ('mdd', 'mtd'):
+ if OptionKey('b_vscrt') in self.env.coredata.options:
+ is_debug = self.env.coredata.get_option(OptionKey('buildtype')) == 'debug'
+ if self.env.coredata.options[OptionKey('b_vscrt')].value in {'mdd', 'mtd'}:
is_debug = True
else:
- is_debug = self.env.coredata.get_builtin_option('debug')
+ is_debug = self.env.coredata.get_option(OptionKey('debug'))
if is_debug:
if 'DEBUG' in cfgs:
cfg = 'DEBUG'
diff --git a/mesonbuild/dependencies/boost.py b/mesonbuild/dependencies/boost.py
index 370fa72..622ee37 100644
--- a/mesonbuild/dependencies/boost.py
+++ b/mesonbuild/dependencies/boost.py
@@ -341,7 +341,7 @@ class BoostLibraryFile():
class BoostDependency(ExternalDependency):
def __init__(self, environment: Environment, kwargs: T.Dict[str, T.Any]) -> None:
super().__init__('boost', environment, kwargs, language='cpp')
- buildtype = environment.coredata.get_builtin_option('buildtype')
+ buildtype = environment.coredata.get_option(mesonlib.OptionKey('buildtype'))
assert isinstance(buildtype, str)
self.debug = buildtype.startswith('debug')
self.multithreading = kwargs.get('threading', 'multi') == 'multi'
@@ -616,8 +616,8 @@ class BoostDependency(ExternalDependency):
# MSVC is very picky with the library tags
vscrt = ''
try:
- crt_val = self.env.coredata.base_options['b_vscrt'].value
- buildtype = self.env.coredata.builtins['buildtype'].value
+ crt_val = self.env.coredata.options[mesonlib.OptionKey('b_vscrt')].value
+ buildtype = self.env.coredata.options[mesonlib.OptionKey('buildtype')].value
vscrt = self.clib_compiler.get_crt_compile_args(crt_val, buildtype)[0]
except (KeyError, IndexError, AttributeError):
pass
diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py
index baf8e94..0b0d96d 100644
--- a/mesonbuild/dependencies/ui.py
+++ b/mesonbuild/dependencies/ui.py
@@ -382,9 +382,9 @@ class QtBaseDependency(ExternalDependency):
# Use the buildtype by default, but look at the b_vscrt option if the
# compiler supports it.
- is_debug = self.env.coredata.get_builtin_option('buildtype') == 'debug'
- if 'b_vscrt' in self.env.coredata.base_options:
- if self.env.coredata.base_options['b_vscrt'].value in ('mdd', 'mtd'):
+ is_debug = self.env.coredata.get_option(mesonlib.OptionKey('buildtype')) == 'debug'
+ if mesonlib.OptionKey('b_vscrt') in self.env.coredata.options:
+ if self.env.coredata.options[mesonlib.OptionKey('b_vscrt')].value in {'mdd', 'mtd'}:
is_debug = True
modules_lib_suffix = self._get_modules_lib_suffix(is_debug)
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index 74d8bde..59675ff 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -23,7 +23,7 @@ from .linkers import ArLinker, ArmarLinker, VisualStudioLinker, DLinker, CcrxLin
from . import mesonlib
from .mesonlib import (
MesonException, EnvironmentException, MachineChoice, Popen_safe,
- PerMachineDefaultable, PerThreeMachineDefaultable, split_args, quote_arg
+ PerMachineDefaultable, PerThreeMachineDefaultable, split_args, quote_arg, OptionKey
)
from . import mlog
@@ -130,6 +130,8 @@ from .compilers import (
)
if T.TYPE_CHECKING:
+ from configparser import ConfigParser
+
from .dependencies import ExternalProgram
build_filename = 'meson.build'
@@ -599,12 +601,11 @@ class Environment:
binaries.build = BinaryTable()
properties.build = Properties()
- # Unparsed options as given by the user in machine files, command line,
- # and project()'s default_options. Keys are in the command line format:
- # "[<subproject>:][build.]option_name".
+ # Options with the key parsed into an OptionKey type.
+ #
# Note that order matters because of 'buildtype', if it is after
# 'optimization' and 'debug' keys, it override them.
- self.raw_options = collections.OrderedDict() # type: collections.OrderedDict[str, str]
+ self.options: T.MutableMapping[OptionKey, str] = collections.OrderedDict()
## Read in native file(s) to override build machine configuration
@@ -613,7 +614,7 @@ class Environment:
binaries.build = BinaryTable(config.get('binaries', {}))
properties.build = Properties(config.get('properties', {}))
cmakevars.build = CMakeVariables(config.get('cmake', {}))
- self.load_machine_file_options(config, properties.build)
+ self.load_machine_file_options(config, properties.build, MachineChoice.BUILD)
## Read in cross file(s) to override host machine configuration
@@ -626,10 +627,19 @@ class Environment:
machines.host = MachineInfo.from_literal(config['host_machine'])
if 'target_machine' in config:
machines.target = MachineInfo.from_literal(config['target_machine'])
- # Keep only per machine options from the native file and prefix them
- # with "build.". The cross file takes precedence over all other options.
- self.keep_per_machine_options()
- self.load_machine_file_options(config, properties.host)
+ # Keep only per machine options from the native file. The cross
+ # file takes precedence over all other options.
+ for key, value in list(self.options.items()):
+ if self.coredata.is_per_machine_option(key):
+ self.options[key.as_build()] = value
+ self.load_machine_file_options(config, properties.host, MachineChoice.HOST)
+ else:
+ # IF we aren't cross compiling, but we hav ea native file, the
+ # native file is for the host. This is due to an mismatch between
+ # meson internals which talk about build an host, and external
+ # interfaces which talk about native and cross.
+ self.options = {k.as_host(): v for k, v in self.options.items()}
+
## "freeze" now initialized configuration, and "save" to the class.
@@ -639,15 +649,17 @@ class Environment:
self.cmakevars = cmakevars.default_missing()
# Command line options override those from cross/native files
- self.raw_options.update(options.cmd_line_options)
+ self.options.update(options.cmd_line_options)
# Take default value from env if not set in cross/native files or command line.
self.set_default_options_from_env()
# Warn if the user is using two different ways of setting build-type
# options that override each other
- if 'buildtype' in self.raw_options and \
- ('optimization' in self.raw_options or 'debug' in self.raw_options):
+ bt = OptionKey('buildtype')
+ db = OptionKey('debug')
+ op = OptionKey('optimization')
+ if bt in self.options and (db in self.options or op in self.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')
@@ -706,11 +718,13 @@ class Environment:
self.default_pkgconfig = ['pkg-config']
self.wrap_resolver = None
- def load_machine_file_options(self, config, properties):
+ def load_machine_file_options(self, config: 'ConfigParser', properties: Properties, machine: MachineChoice) -> None:
+ """Read the contents of a Machine file and put it in the options store."""
paths = config.get('paths')
if paths:
mlog.deprecation('The [paths] section is deprecated, use the [built-in options] section instead.')
- self.raw_options.update(paths)
+ for k, v in paths.items():
+ self.options[OptionKey.from_string(k).evolve(machine=machine)] = v
deprecated_properties = set()
for lang in compilers.all_languages:
deprecated_properties.add(lang + '_args')
@@ -718,44 +732,40 @@ class Environment:
for k, v in properties.properties.copy().items():
if k in deprecated_properties:
mlog.deprecation('{} in the [properties] section of the machine file is deprecated, use the [built-in options] section.'.format(k))
- self.raw_options[k] = v
+ self.options[OptionKey.from_string(k).evolve(machine=machine)] = v
del properties.properties[k]
for section, values in config.items():
- prefix = ''
if ':' in section:
subproject, section = section.split(':')
- prefix = subproject + ':'
- if section in ['project options', 'built-in options']:
- self.raw_options.update({prefix + k: v for k, v in values.items()})
-
- def keep_per_machine_options(self):
- per_machine_options = {}
- for optname, value in self.raw_options.items():
- if self.coredata.is_per_machine_option(optname):
- build_optname = self.coredata.insert_build_prefix(optname)
- per_machine_options[build_optname] = value
- self.raw_options = per_machine_options
-
- def set_default_options_from_env(self):
+ else:
+ subproject = ''
+ if section == 'built-in options':
+ for k, v in values.items():
+ key = OptionKey.from_string(k).evolve(subproject=subproject, machine=machine)
+ self.options[key] = v
+ elif section == 'project options':
+ for k, v in values.items():
+ # Project options are always for the machine machine
+ key = OptionKey.from_string(k).evolve(subproject=subproject)
+ self.options[key] = v
+
+ def set_default_options_from_env(self) -> None:
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(mesonlib.OrderedSet(p_env.split(':')))
-
- key = 'pkg_config_path'
- if for_machine == MachineChoice.BUILD:
- key = 'build.' + key
-
- # Take env vars only on first invocation, if the env changes when
- # reconfiguring it gets ignored.
- # FIXME: We should remember if we took the value from env to warn
- # if it changes on future invocations.
- if self.first_invocation:
- self.raw_options.setdefault(key, p_list)
+ for evar, keyname in [('PKG_CONFIG_PATH', 'pkg_config_path')]:
+ p_env_pair = get_env_var_pair(for_machine, self.is_cross_build(), evar)
+ if p_env_pair is not None:
+ _, 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(':')))
+ # Take env vars only on first invocation, if the env changes when
+ # reconfiguring it gets ignored.
+ # FIXME: We should remember if we took the value from env to warn
+ # if it changes on future invocations.
+ if self.first_invocation:
+ key = OptionKey(keyname, machine=for_machine)
+ self.options.setdefault(key, p_list)
def create_new_coredata(self, options: 'argparse.Namespace') -> None:
# WARNING: Don't use any values from coredata in __init__. It gets
@@ -929,7 +939,7 @@ class Environment:
elif isinstance(comp_class.LINKER_PREFIX, list):
check_args = comp_class.LINKER_PREFIX + ['/logo'] + comp_class.LINKER_PREFIX + ['--version']
- check_args += self.coredata.compiler_options[for_machine][comp_class.language]['args'].value
+ check_args += self.coredata.options[OptionKey('args', lang=comp_class.language, machine=for_machine)].value
override = [] # type: T.List[str]
value = self.lookup_binary_entry(for_machine, comp_class.language + '_ld')
@@ -995,7 +1005,7 @@ class Environment:
"""
self.coredata.add_lang_args(comp_class.language, comp_class, for_machine, self)
extra_args = extra_args or []
- extra_args += self.coredata.compiler_options[for_machine][comp_class.language]['args'].value
+ extra_args += self.coredata.options[OptionKey('args', lang=comp_class.language, machine=for_machine)].value
if isinstance(comp_class.LINKER_PREFIX, str):
check_args = [comp_class.LINKER_PREFIX + '--version'] + extra_args
@@ -2000,25 +2010,25 @@ class Environment:
return self.get_libdir()
def get_prefix(self) -> str:
- return self.coredata.get_builtin_option('prefix')
+ return self.coredata.get_option(OptionKey('prefix'))
def get_libdir(self) -> str:
- return self.coredata.get_builtin_option('libdir')
+ return self.coredata.get_option(OptionKey('libdir'))
def get_libexecdir(self) -> str:
- return self.coredata.get_builtin_option('libexecdir')
+ return self.coredata.get_option(OptionKey('libexecdir'))
def get_bindir(self) -> str:
- return self.coredata.get_builtin_option('bindir')
+ return self.coredata.get_option(OptionKey('bindir'))
def get_includedir(self) -> str:
- return self.coredata.get_builtin_option('includedir')
+ return self.coredata.get_option(OptionKey('includedir'))
def get_mandir(self) -> str:
- return self.coredata.get_builtin_option('mandir')
+ return self.coredata.get_option(OptionKey('mandir'))
def get_datadir(self) -> str:
- return self.coredata.get_builtin_option('datadir')
+ return self.coredata.get_option(OptionKey('datadir'))
def get_compiler_system_dirs(self, for_machine: MachineChoice):
for comp in self.coredata.compilers[for_machine].values():
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index a4a9fb2..c20c205 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -21,7 +21,7 @@ from . import optinterpreter
from . import compilers
from .wrap import wrap, WrapMode
from . import mesonlib
-from .mesonlib import FileMode, MachineChoice, Popen_safe, listify, extract_as_list, has_path_sep, unholder
+from .mesonlib import FileMode, MachineChoice, OptionKey, Popen_safe, listify, extract_as_list, has_path_sep, unholder
from .dependencies import ExternalProgram
from .dependencies import InternalDependency, Dependency, NotFoundDependency, DependencyException
from .depfile import DepFile
@@ -51,6 +51,7 @@ import typing as T
import importlib
if T.TYPE_CHECKING:
+ from .compilers import Compiler
from .envconfig import MachineInfo
from .environment import Environment
from .modules import ExtensionModule
@@ -77,11 +78,11 @@ class OverrideProgram(dependencies.ExternalProgram):
class FeatureOptionHolder(InterpreterObject, ObjectHolder):
- def __init__(self, env, name, option):
+ def __init__(self, env: 'Environment', name, option):
InterpreterObject.__init__(self)
ObjectHolder.__init__(self, option)
if option.is_auto():
- self.held_object = env.coredata.builtins['auto_features']
+ self.held_object = env.coredata.options[OptionKey('auto_features')]
self.name = name
self.methods.update({'enabled': self.enabled_method,
'disabled': self.disabled_method,
@@ -1057,7 +1058,7 @@ find_library_permitted_kwargs = set([
find_library_permitted_kwargs |= set(['header_' + k for k in header_permitted_kwargs])
class CompilerHolder(InterpreterObject):
- def __init__(self, compiler, env, subproject):
+ def __init__(self, compiler: 'Compiler', env: 'Environment', subproject: str):
InterpreterObject.__init__(self)
self.compiler = compiler
self.environment = env
@@ -1141,8 +1142,7 @@ class CompilerHolder(InterpreterObject):
i.held_object.get_curdir(), idir)
args += self.compiler.get_include_args(idir, False)
if not nobuiltins:
- for_machine = Interpreter.machine_from_native_kwarg(kwargs)
- opts = self.environment.coredata.compiler_options[for_machine][self.compiler.language]
+ opts = self.environment.coredata.options
args += self.compiler.get_option_compile_args(opts)
if mode == 'link':
args += self.compiler.get_option_link_args(opts)
@@ -2150,7 +2150,7 @@ class MesonMain(InterpreterObject):
@noPosargs
@permittedKwargs({})
def is_unity_method(self, args, kwargs):
- optval = self.interpreter.environment.coredata.get_builtin_option('unity')
+ optval = self.interpreter.environment.coredata.get_option(OptionKey('unity'))
if optval == 'on' or (optval == 'subprojects' and self.interpreter.is_subproject()):
return True
return False
@@ -2473,12 +2473,11 @@ class Interpreter(InterpreterBase):
def get_non_matching_default_options(self) -> T.Iterator[T.Tuple[str, str, coredata.UserOption]]:
env = self.environment
for def_opt_name, def_opt_value in self.project_default_options.items():
- for opts in env.coredata.get_all_options():
- cur_opt_value = opts.get(def_opt_name)
- if cur_opt_value is not None:
- def_opt_value = env.coredata.validate_option_value(def_opt_name, def_opt_value)
- if def_opt_value != cur_opt_value.value:
- yield (def_opt_name, def_opt_value, cur_opt_value)
+ cur_opt_value = self.coredata.options.get(def_opt_name)
+ if cur_opt_value is not None:
+ def_opt_value = env.coredata.validate_option_value(def_opt_name, def_opt_value)
+ if def_opt_value != cur_opt_value.value:
+ yield (str(def_opt_name), def_opt_value, cur_opt_value)
def build_func_dict(self):
self.funcs.update({'add_global_arguments': self.func_add_global_arguments,
@@ -2905,7 +2904,7 @@ external dependencies (including libraries) must go to "dependencies".''')
return self.disabled_subproject(subp_name, disabled_feature=feature)
default_options = mesonlib.stringlistify(kwargs.get('default_options', []))
- default_options = coredata.create_options_dict(default_options)
+ default_options = coredata.create_options_dict(default_options, subp_name)
if subp_name == '':
raise InterpreterException('Subproject name must not be empty.')
@@ -3008,7 +3007,7 @@ external dependencies (including libraries) must go to "dependencies".''')
def _do_subproject_cmake(self, subp_name, subdir, subdir_abs, default_options, kwargs):
with mlog.nested():
new_build = self.build.copy()
- prefix = self.coredata.builtins['prefix'].value
+ prefix = self.coredata.options[OptionKey('prefix')].value
from .modules.cmake import CMakeSubprojectOptions
options = kwargs.get('options', CMakeSubprojectOptions())
@@ -3049,28 +3048,21 @@ external dependencies (including libraries) must go to "dependencies".''')
mlog.log()
return result
- def get_option_internal(self, optname):
- raw_optname = optname
- if self.is_subproject():
- optname = self.subproject + ':' + optname
-
+ def get_option_internal(self, optname: str):
+ key = OptionKey.from_string(optname).evolve(subproject=self.subproject)
- 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)),
- dict(self.coredata.flatten_lang_iterator(
- self.coredata.get_prefixed_options_per_machine(self.coredata.compiler_options))),
- ]:
- v = opts.get(optname)
- if v is None or v.yielding:
- v = opts.get(raw_optname)
- if v is not None:
- return v
+ if not key.is_project():
+ for opts in [self.coredata.options, compilers.base_options]:
+ v = opts.get(key)
+ if v is None or v.yielding:
+ v = opts.get(key.as_root())
+ if v is not None:
+ return v
try:
- opt = self.coredata.user_options[optname]
- if opt.yielding and ':' in optname and raw_optname in self.coredata.user_options:
- popt = self.coredata.user_options[raw_optname]
+ opt = self.coredata.options[key]
+ if opt.yielding and key.subproject and key.as_root() in self.coredata.options:
+ popt = self.coredata.options[key.as_root()]
if type(opt) is type(popt):
opt = popt
else:
@@ -3082,7 +3074,7 @@ external dependencies (including libraries) must go to "dependencies".''')
mlog.warning('Option {0!r} of type {1!r} in subproject {2!r} cannot yield '
'to parent option of type {3!r}, ignoring parent value. '
'Use -D{2}:{0}=value to set the value for this option manually'
- '.'.format(raw_optname, opt_type, self.subproject, popt_type),
+ '.'.format(optname, opt_type, self.subproject, popt_type),
location=self.current_node)
return opt
except KeyError:
@@ -3124,7 +3116,7 @@ external dependencies (including libraries) must go to "dependencies".''')
# The backend is already set when parsing subprojects
if self.backend is not None:
return
- backend = self.coredata.get_builtin_option('backend')
+ backend = self.coredata.get_option(OptionKey('backend'))
from .backend import backends
self.backend = backends.get_backend_from_name(backend, self.build, self)
@@ -3133,14 +3125,14 @@ external dependencies (including libraries) must go to "dependencies".''')
if backend != self.backend.name:
if self.backend.name.startswith('vs'):
mlog.log('Auto detected Visual Studio backend:', mlog.bold(self.backend.name))
- self.coredata.set_builtin_option('backend', self.backend.name)
+ self.coredata.set_option(OptionKey('backend'), self.backend.name)
# Only init backend options on first invocation otherwise it would
# override values previously set from command line.
if self.environment.first_invocation:
self.coredata.init_backend_options(backend)
- options = {k: v for k, v in self.environment.raw_options.items() if k.startswith('backend_')}
+ options = {k: v for k, v in self.environment.options.items() if k.is_backend()}
self.coredata.set_options(options)
@stringArgs
@@ -3164,7 +3156,7 @@ external dependencies (including libraries) must go to "dependencies".''')
if os.path.exists(self.option_file):
oi = optinterpreter.OptionInterpreter(self.subproject)
oi.process(self.option_file)
- self.coredata.merge_user_options(oi.options)
+ self.coredata.update_project_options(oi.options)
self.add_build_def_file(self.option_file)
# Do not set default_options on reconfigure otherwise it would override
@@ -3172,7 +3164,7 @@ external dependencies (including libraries) must go to "dependencies".''')
# default_options in a project will trigger a reconfigure but won't
# have any effect.
self.project_default_options = mesonlib.stringlistify(kwargs.get('default_options', []))
- self.project_default_options = coredata.create_options_dict(self.project_default_options)
+ self.project_default_options = coredata.create_options_dict(self.project_default_options, self.subproject)
if self.environment.first_invocation:
default_options = self.project_default_options.copy()
default_options.update(self.default_project_options)
@@ -3214,7 +3206,7 @@ external dependencies (including libraries) must go to "dependencies".''')
self.build.subproject_dir = self.subproject_dir
# Load wrap files from this (sub)project.
- wrap_mode = self.coredata.get_builtin_option('wrap_mode')
+ wrap_mode = self.coredata.get_option(OptionKey('wrap_mode'))
if not self.is_subproject() or wrap_mode != WrapMode.nopromote:
subdir = os.path.join(self.subdir, spdirname)
r = wrap.Resolver(self.environment.get_source_dir(), subdir, wrap_mode)
@@ -3526,7 +3518,7 @@ external dependencies (including libraries) must go to "dependencies".''')
return progobj
fallback = None
- wrap_mode = self.coredata.get_builtin_option('wrap_mode')
+ wrap_mode = self.coredata.get_option(OptionKey('wrap_mode'))
if wrap_mode != WrapMode.nofallback and self.environment.wrap_resolver:
fallback = self.environment.wrap_resolver.find_program_provider(args)
if fallback and wrap_mode == WrapMode.forcefallback:
@@ -3830,8 +3822,8 @@ external dependencies (including libraries) must go to "dependencies".''')
if self.get_subproject(subp_name):
return self.get_subproject_dep(name, display_name, subp_name, varname, kwargs)
- wrap_mode = self.coredata.get_builtin_option('wrap_mode')
- force_fallback_for = self.coredata.get_builtin_option('force_fallback_for')
+ wrap_mode = self.coredata.get_option(OptionKey('wrap_mode'))
+ force_fallback_for = self.coredata.get_option(OptionKey('force_fallback_for'))
force_fallback = (force_fallback or
wrap_mode == WrapMode.forcefallback or
name in force_fallback_for or
@@ -3877,11 +3869,11 @@ external dependencies (including libraries) must go to "dependencies".''')
# Explicitly listed fallback preferences for specific subprojects
# take precedence over wrap-mode
- force_fallback_for = self.coredata.get_builtin_option('force_fallback_for')
+ force_fallback_for = self.coredata.get_option(OptionKey('force_fallback_for'))
if name in force_fallback_for or subp_name in force_fallback_for:
mlog.log('Looking for a fallback subproject for the dependency',
mlog.bold(display_name), 'because:\nUse of fallback was forced for that specific subproject')
- elif self.coredata.get_builtin_option('wrap_mode') == WrapMode.nofallback:
+ elif self.coredata.get_option(OptionKey('wrap_mode')) == WrapMode.nofallback:
mlog.log('Not looking for a fallback subproject for the dependency',
mlog.bold(display_name), 'because:\nUse of fallback '
'dependencies is disabled.')
@@ -3889,7 +3881,7 @@ external dependencies (including libraries) must go to "dependencies".''')
m = 'Dependency {!r} not found and fallback is disabled'
raise DependencyException(m.format(display_name))
return self.notfound_dependency()
- elif self.coredata.get_builtin_option('wrap_mode') == WrapMode.forcefallback:
+ elif self.coredata.get_option(OptionKey('wrap_mode')) == WrapMode.forcefallback:
mlog.log('Looking for a fallback subproject for the dependency',
mlog.bold(display_name), 'because:\nUse of fallback dependencies is forced.')
else:
@@ -4778,15 +4770,15 @@ different subdirectory.
break
def check_clang_asan_lundef(self) -> None:
- if 'b_lundef' not in self.coredata.base_options:
+ if OptionKey('b_lundef') not in self.coredata.options:
return
- if 'b_sanitize' not in self.coredata.base_options:
+ if OptionKey('b_sanitize') not in self.coredata.options:
return
- if (self.coredata.base_options['b_lundef'].value and
- self.coredata.base_options['b_sanitize'].value != 'none'):
+ if (self.coredata.options[OptionKey('b_lundef')].value and
+ self.coredata.options[OptionKey('b_sanitize')].value != 'none'):
mlog.warning('''Trying to use {} sanitizer on Clang with b_lundef.
This will probably not work.
-Try setting b_lundef to false instead.'''.format(self.coredata.base_options['b_sanitize'].value),
+Try setting b_lundef to false instead.'''.format(self.coredata.options[OptionKey('b_sanitize')].value),
location=self.current_node)
def evaluate_subproject_info(self, path_from_source_root, subproject_dir):
@@ -4881,10 +4873,11 @@ Try setting b_lundef to false instead.'''.format(self.coredata.base_options['b_s
# Check if user forces non-PIC static library.
pic = True
+ key = OptionKey('b_staticpic')
if 'pic' in kwargs:
pic = kwargs['pic']
- elif 'b_staticpic' in self.environment.coredata.base_options:
- pic = self.environment.coredata.base_options['b_staticpic'].value
+ elif key in self.environment.coredata.options:
+ pic = self.environment.coredata.options[key].value
if pic:
# Exclude sources from args and kwargs to avoid building them twice
@@ -4901,7 +4894,7 @@ Try setting b_lundef to false instead.'''.format(self.coredata.base_options['b_s
return BothLibrariesHolder(shared_holder, static_holder, self)
def build_library(self, node, args, kwargs):
- default_library = self.coredata.get_builtin_option('default_library', self.subproject)
+ default_library = self.coredata.get_option(OptionKey('default_library', subproject=self.subproject))
if default_library == 'shared':
return self.build_target(node, args, kwargs, SharedLibraryHolder)
elif default_library == 'static':
diff --git a/mesonbuild/linkers.py b/mesonbuild/linkers.py
index 0f05fef..141c8fd 100644
--- a/mesonbuild/linkers.py
+++ b/mesonbuild/linkers.py
@@ -21,7 +21,7 @@ from .arglist import CompilerArgs
from .envconfig import get_env_var
if T.TYPE_CHECKING:
- from .coredata import OptionDictType
+ from .coredata import KeyedOptionDictType
from .envconfig import MachineChoice
from .environment import Environment
@@ -40,7 +40,7 @@ class StaticLinker:
"""
return mesonlib.is_windows()
- def get_base_link_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_base_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
"""Like compilers.get_base_link_args, but for the static linker."""
return []
@@ -70,7 +70,7 @@ class StaticLinker:
def openmp_flags(self) -> T.List[str]:
return []
- def get_option_link_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
return []
@classmethod
@@ -378,7 +378,7 @@ class DynamicLinker(LinkerEnvVarsMixin, metaclass=abc.ABCMeta):
# XXX: is use_ldflags a compiler or a linker attribute?
- def get_option_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_option_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
return []
def has_multi_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]:
@@ -401,7 +401,7 @@ class DynamicLinker(LinkerEnvVarsMixin, metaclass=abc.ABCMeta):
def get_std_shared_lib_args(self) -> T.List[str]:
return []
- def get_std_shared_module_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_std_shared_module_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
return self.get_std_shared_lib_args()
def get_pie_args(self) -> T.List[str]:
@@ -693,7 +693,7 @@ class AppleDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker):
def get_allow_undefined_args(self) -> T.List[str]:
return self._apply_prefix('-undefined,dynamic_lookup')
- def get_std_shared_module_args(self, options: 'OptionDictType') -> T.List[str]:
+ def get_std_shared_module_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
return ['-bundle'] + self._apply_prefix('-undefined,dynamic_lookup')
def get_pie_args(self) -> T.List[str]:
diff --git a/mesonbuild/mcompile.py b/mesonbuild/mcompile.py
index 41ed1b7..140e88f 100644
--- a/mesonbuild/mcompile.py
+++ b/mesonbuild/mcompile.py
@@ -47,7 +47,7 @@ def get_backend_from_coredata(builddir: Path) -> str:
"""
Gets `backend` option value from coredata
"""
- backend = coredata.load(str(builddir)).get_builtin_option('backend')
+ backend = coredata.load(str(builddir)).get_option(mesonlib.OptionKey('backend'))
assert isinstance(backend, str)
return backend
diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py
index 774dc5a..686a336 100644
--- a/mesonbuild/mconf.py
+++ b/mesonbuild/mconf.py
@@ -17,8 +17,11 @@ from . import coredata, environment, mesonlib, build, mintro, mlog
from .ast import AstIDGenerator
import typing as T
+from .mesonlib import MachineChoice, OptionKey
+
if T.TYPE_CHECKING:
import argparse
+ from .coredata import UserOption
def add_arguments(parser: 'argparse.ArgumentParser') -> None:
coredata.register_builtin_arguments(parser)
@@ -26,7 +29,6 @@ def add_arguments(parser: 'argparse.ArgumentParser') -> None:
parser.add_argument('--clearcache', action='store_true', default=False,
help='Clear cached state (e.g. found dependencies)')
-
def make_lower_case(val: T.Any) -> T.Union[str, T.List[T.Any]]: # T.Any because of recursion...
if isinstance(val, bool):
return str(val).lower()
@@ -52,8 +54,8 @@ class Conf:
self.choices_col = []
self.descr_col = []
self.has_choices = False
- self.all_subprojects = set()
- self.yielding_options = set()
+ self.all_subprojects: T.Set[str] = set()
+ self.yielding_options: T.Set[OptionKey] = set()
if os.path.isdir(os.path.join(self.build_dir, 'meson-private')):
self.build = build.load(self.build_dir)
@@ -101,20 +103,20 @@ class Conf:
else:
print('{0:{width[0]}} {1:{width[1]}} {3}'.format(*line, width=col_widths))
- def split_options_per_subproject(self, options):
- result = {}
+ def split_options_per_subproject(self, options: 'coredata.KeyedOptionDictType') -> T.Dict[str, T.Dict[str, 'UserOption']]:
+ result: T.Dict[str, T.Dict[str, 'UserOption']] = {}
for k, o in options.items():
- subproject = ''
- if ':' in k:
- subproject, optname = k.split(':')
- if o.yielding and optname in options:
+ subproject = k.subproject
+ if k.subproject:
+ k = k.as_root()
+ if o.yielding and k in options:
self.yielding_options.add(k)
self.all_subprojects.add(subproject)
- result.setdefault(subproject, {})[k] = o
+ result.setdefault(subproject, {})[str(k)] = o
return result
- def _add_line(self, name, value, choices, descr):
- self.name_col.append(' ' * self.print_margin + name)
+ def _add_line(self, name: OptionKey, value, choices, descr) -> None:
+ self.name_col.append(' ' * self.print_margin + str(name))
self.value_col.append(value)
self.choices_col.append(choices)
self.descr_col.append(descr)
@@ -163,7 +165,7 @@ class Conf:
self._add_line(section + ':', '', '', '')
self.print_margin = 2
- def print_options(self, title, options):
+ def print_options(self, title: str, options: 'coredata.KeyedOptionDictType') -> None:
if not options:
return
if title:
@@ -188,33 +190,34 @@ class Conf:
if not self.default_values_only:
print(' Build dir ', self.build_dir)
- 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]
-
- dir_options = {k: o for k, o in self.coredata.builtins.items() if k in dir_option_names}
- test_options = {k: o for k, o in self.coredata.builtins.items() if k in test_option_names}
- core_options = {k: o for k, o in self.coredata.builtins.items() if k in core_option_names}
-
- core_options = self.split_options_per_subproject(core_options)
- host_compiler_options = self.split_options_per_subproject(
- dict(self.coredata.flatten_lang_iterator(
- self.coredata.compiler_options.host.items())))
- build_compiler_options = self.split_options_per_subproject(
- dict(self.coredata.flatten_lang_iterator(
- (self.coredata.insert_build_prefix(k), o)
- for k, o in self.coredata.compiler_options.build.items())))
- project_options = self.split_options_per_subproject(self.coredata.user_options)
+ dir_option_names = set(coredata.BUILTIN_DIR_OPTIONS)
+ test_option_names = {OptionKey('errorlogs'),
+ OptionKey('stdsplit')}
+
+ dir_options: 'coredata.KeyedOptionDictType' = {}
+ test_options: 'coredata.KeyedOptionDictType' = {}
+ core_options: 'coredata.KeyedOptionDictType' = {}
+ for k, v in self.coredata.options.items():
+ if k in dir_option_names:
+ dir_options[k] = v
+ elif k in test_option_names:
+ test_options[k] = v
+ elif k.is_builtin():
+ core_options[k] = v
+
+ host_core_options = self.split_options_per_subproject({k: v for k, v in core_options.items() if k.machine is MachineChoice.HOST})
+ build_core_options = self.split_options_per_subproject({k: v for k, v in core_options.items() if k.machine is MachineChoice.BUILD})
+ host_compiler_options = self.split_options_per_subproject({k: v for k, v in self.coredata.options.items() if k.is_compiler() and k.machine is MachineChoice.HOST})
+ build_compiler_options = self.split_options_per_subproject({k: v for k, v in self.coredata.options.items() if k.is_compiler() and k.machine is MachineChoice.BUILD})
+ project_options = self.split_options_per_subproject({k: v for k, v in self.coredata.options.items() if k.is_project()})
show_build_options = self.default_values_only or self.build.environment.is_cross_build()
self.add_section('Main project options')
- self.print_options('Core options', core_options[''])
- self.print_options('', self.coredata.builtins_per_machine.host)
+ self.print_options('Core options', host_core_options[''])
if show_build_options:
- self.print_options('', {self.coredata.insert_build_prefix(k): o for k, o in self.coredata.builtins_per_machine.build.items()})
- self.print_options('Backend options', self.coredata.backend_options)
- self.print_options('Base options', self.coredata.base_options)
+ self.print_options('', build_core_options[''])
+ self.print_options('Backend options', {str(k): v for k, v in self.coredata.options.items()})
+ self.print_options('Base options', {str(k): v for k, v in self.coredata.options.items()})
self.print_options('Compiler options', host_compiler_options.get('', {}))
if show_build_options:
self.print_options('', build_compiler_options.get('', {}))
@@ -251,7 +254,7 @@ def run(options):
return 0
save = False
- if len(options.cmd_line_options) > 0:
+ if options.cmd_line_options:
c.set_options(options.cmd_line_options)
coredata.update_cmd_line_file(builddir, options)
save = True
diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py
index f73778e..2c1727b 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib.py
@@ -14,13 +14,13 @@
"""A library of random helper functionality."""
from pathlib import Path
+import enum
import sys
import stat
import time
import platform, subprocess, operator, os, shlex, shutil, re
import collections
-from enum import IntEnum
-from functools import lru_cache, wraps
+from functools import lru_cache, wraps, total_ordering
from itertools import tee, filterfalse
from tempfile import TemporaryDirectory
import typing as T
@@ -31,7 +31,7 @@ from mesonbuild import mlog
if T.TYPE_CHECKING:
from .build import ConfigurationData
- from .coredata import OptionDictType, UserOption
+ from .coredata import KeyedOptionDictType, UserOption
from .compilers.compilers import CompilerType
from .interpreterbase import ObjectHolder
@@ -347,7 +347,7 @@ def classify_unity_sources(compilers: T.Iterable['CompilerType'], sources: T.Ite
return compsrclist
-class MachineChoice(IntEnum):
+class MachineChoice(enum.IntEnum):
"""Enum class representing one of the two abstract machine names used in
most places: the build, and host, machines.
@@ -1616,7 +1616,7 @@ def relative_to_if_possible(path: Path, root: Path, resolve: bool = False) -> Pa
except ValueError:
return path
-class LibType(IntEnum):
+class LibType(enum.IntEnum):
"""Enumeration for library types."""
@@ -1738,8 +1738,13 @@ def run_once(func: T.Callable[..., _T]) -> T.Callable[..., _T]:
class OptionProxy(T.Generic[_T]):
- def __init__(self, value: _T):
+ def __init__(self, value: _T, choices: T.Optional[T.List[str]] = None):
self.value = value
+ self.choices = choices
+
+ def set_value(self, v: _T) -> None:
+ # XXX: should this be an error
+ self.value = v
class OptionOverrideProxy(collections.abc.MutableMapping):
@@ -1751,27 +1756,27 @@ class OptionOverrideProxy(collections.abc.MutableMapping):
# TODO: the typing here could be made more explicit using a TypeDict from
# python 3.8 or typing_extensions
- def __init__(self, overrides: T.Dict[str, T.Any], *options: 'OptionDictType'):
+ def __init__(self, overrides: T.Dict['OptionKey', T.Any], *options: 'KeyedOptionDictType'):
self.overrides = overrides.copy()
- self.options = {} # type: T.Dict[str, UserOption]
+ self.options: T.Dict['OptionKey', UserOption] = {}
for o in options:
self.options.update(o)
- def __getitem__(self, key: str) -> T.Union['UserOption', OptionProxy]:
+ def __getitem__(self, key: 'OptionKey') -> T.Union['UserOption', OptionProxy]:
if key in self.options:
opt = self.options[key]
if key in self.overrides:
- return OptionProxy(opt.validate_value(self.overrides[key]))
+ return OptionProxy(opt.validate_value(self.overrides[key]), getattr(opt, 'choices', None))
return opt
raise KeyError('Option not found', key)
- def __setitem__(self, key: str, value: T.Union['UserOption', OptionProxy]) -> None:
+ def __setitem__(self, key: 'OptionKey', value: T.Union['UserOption', OptionProxy]) -> None:
self.overrides[key] = value.value
- def __delitem__(self, key: str) -> None:
+ def __delitem__(self, key: 'OptionKey') -> None:
del self.overrides[key]
- def __iter__(self) -> T.Iterator[str]:
+ def __iter__(self) -> T.Iterator['OptionKey']:
return iter(self.options)
def __len__(self) -> int:
@@ -1779,3 +1784,244 @@ class OptionOverrideProxy(collections.abc.MutableMapping):
def copy(self) -> 'OptionOverrideProxy':
return OptionOverrideProxy(self.overrides.copy(), self.options.copy())
+
+
+class OptionType(enum.Enum):
+
+ """Enum used to specify what kind of argument a thing is."""
+
+ BUILTIN = 0
+ BASE = 1
+ COMPILER = 2
+ PROJECT = 3
+ BACKEND = 4
+
+# This is copied from coredata. There is no way to share this, because this
+# is used in the OptionKey constructor, and the coredata lists are
+# OptionKeys...
+_BUILTIN_NAMES = {
+ 'prefix',
+ 'bindir',
+ 'datadir',
+ 'includedir',
+ 'infodir',
+ 'libdir',
+ 'libexecdir',
+ 'localedir',
+ 'localstatedir',
+ 'mandir',
+ 'sbindir',
+ 'sharedstatedir',
+ 'sysconfdir',
+ 'auto_features',
+ 'backend',
+ 'buildtype',
+ 'debug',
+ 'default_library',
+ 'errorlogs',
+ 'install_umask',
+ 'layout',
+ 'optimization',
+ 'stdsplit',
+ 'strip',
+ 'unity',
+ 'unity_size',
+ 'warning_level',
+ 'werror',
+ 'wrap_mode',
+ 'force_fallback_for',
+ 'pkg_config_path',
+ 'cmake_prefix_path',
+}
+
+
+def _classify_argument(key: 'OptionKey') -> OptionType:
+ """Classify arguments into groups so we know which dict to assign them to."""
+
+ if key.name.startswith('b_'):
+ assert key.machine is MachineChoice.HOST, str(key)
+ return OptionType.BASE
+ elif key.lang is not None:
+ return OptionType.COMPILER
+ elif key.name in _BUILTIN_NAMES:
+ return OptionType.BUILTIN
+ elif key.name.startswith('backend_'):
+ assert key.machine is MachineChoice.HOST, str(key)
+ return OptionType.BACKEND
+ else:
+ assert key.machine is MachineChoice.HOST, str(key)
+ return OptionType.PROJECT
+
+
+@total_ordering
+class OptionKey:
+
+ """Represents an option key in the various option dictionaries.
+
+ This provides a flexible, powerful way to map option names from their
+ external form (things like subproject:build.option) to something that
+ internally easier to reason about and produce.
+ """
+
+ __slots__ = ['name', 'subproject', 'machine', 'lang', '_hash', 'type']
+
+ name: str
+ subproject: str
+ machine: MachineChoice
+ lang: T.Optional[str]
+ _hash: int
+ type: OptionType
+
+ def __init__(self, name: str, subproject: str = '',
+ machine: MachineChoice = MachineChoice.HOST,
+ lang: T.Optional[str] = None, _type: T.Optional[OptionType] = None):
+ # the _type option to the constructor is kinda private. We want to be
+ # able tos ave the state and avoid the lookup function when
+ # pickling/unpickling, but we need to be able to calculate it when
+ # constructing a new OptionKey
+ object.__setattr__(self, 'name', name)
+ object.__setattr__(self, 'subproject', subproject)
+ object.__setattr__(self, 'machine', machine)
+ object.__setattr__(self, 'lang', lang)
+ object.__setattr__(self, '_hash', hash((name, subproject, machine, lang)))
+ if _type is None:
+ _type = _classify_argument(self)
+ object.__setattr__(self, 'type', _type)
+
+ def __setattr__(self, key: str, value: T.Any) -> None:
+ raise AttributeError('OptionKey instances do not support mutation.')
+
+ def __getstate__(self) -> T.Dict[str, T.Any]:
+ return {
+ 'name': self.name,
+ 'subproject': self.subproject,
+ 'machine': self.machine,
+ 'lang': self.lang,
+ '_type': self.type,
+ }
+
+ def __setstate__(self, state: T.Dict[str, T.Any]) -> None:
+ """De-serialize the state of a pickle.
+
+ This is very clever. __init__ is not a constructor, it's an
+ initializer, therefore it's safe to call more than once. We create a
+ state in the custom __getstate__ method, which is valid to pass
+ splatted to the initializer.
+ """
+ # Mypy doesn't like this, because it's so clever.
+ self.__init__(**state) # type: ignore
+
+ def __hash__(self) -> int:
+ return self._hash
+
+ def __eq__(self, other: object) -> bool:
+ if isinstance(other, OptionKey):
+ return (
+ self.name == other.name and
+ self.subproject == other.subproject and
+ self.machine is other.machine and
+ self.lang == other.lang)
+ return NotImplemented
+
+ def __lt__(self, other: object) -> bool:
+ if isinstance(other, OptionKey):
+ return (
+ self.name < other.name and
+ self.subproject < other.subproject and
+ self.machine < other.machine and
+ self.lang < other.lang)
+ return NotImplemented
+
+ def __str__(self) -> str:
+ out = self.name
+ if self.lang:
+ out = f'{self.lang}_{out}'
+ if self.machine is MachineChoice.BUILD:
+ out = f'build.{out}'
+ if self.subproject:
+ out = f'{self.subproject}:{out}'
+ return out
+
+ def __repr__(self) -> str:
+ return f'OptionKey({repr(self.name)}, {repr(self.subproject)}, {repr(self.machine)}, {repr(self.lang)})'
+
+ @classmethod
+ def from_string(cls, raw: str) -> 'OptionKey':
+ """Parse the raw command line format into a three part tuple.
+
+ This takes strings like `mysubproject:build.myoption` and Creates an
+ OptionKey out of them.
+ """
+
+ try:
+ subproject, raw2 = raw.split(':')
+ except ValueError:
+ subproject, raw2 = '', raw
+
+ if raw2.startswith('build.'):
+ raw3 = raw2.lstrip('build.')
+ for_machine = MachineChoice.BUILD
+ else:
+ raw3 = raw2
+ for_machine = MachineChoice.HOST
+
+ from .compilers import all_languages
+ if any(raw3.startswith(f'{l}_') for l in all_languages):
+ lang, opt = raw3.split('_', 1)
+ else:
+ lang, opt = None, raw3
+ assert ':' not in opt
+ assert 'build.' not in opt
+
+ return cls(opt, subproject, for_machine, lang)
+
+ def evolve(self, name: T.Optional[str] = None, subproject: T.Optional[str] = None,
+ machine: T.Optional[MachineChoice] = None, lang: T.Optional[str] = '') -> 'OptionKey':
+ """Create a new copy of this key, but with alterted members.
+
+ For example:
+ >>> a = OptionKey('foo', '', MachineChoice.Host)
+ >>> b = OptionKey('foo', 'bar', MachineChoice.Host)
+ >>> b == a.evolve(subproject='bar')
+ True
+ """
+ # We have to be a little clever with lang here, because lang is valid
+ # as None, for non-compiler options
+ return OptionKey(
+ name if name is not None else self.name,
+ subproject if subproject is not None else self.subproject,
+ machine if machine is not None else self.machine,
+ lang if lang != '' else self.lang,
+ )
+
+ def as_root(self) -> 'OptionKey':
+ """Convenience method for key.evolve(subproject='')."""
+ return self.evolve(subproject='')
+
+ def as_build(self) -> 'OptionKey':
+ """Convenience method for key.evolve(machine=MachinceChoice.BUILD)."""
+ return self.evolve(machine=MachineChoice.BUILD)
+
+ def as_host(self) -> 'OptionKey':
+ """Convenience method for key.evolve(machine=MachinceChoice.HOST)."""
+ return self.evolve(machine=MachineChoice.HOST)
+
+ def is_backend(self) -> bool:
+ """Convenience method to check if this is a backend option."""
+ return self.type is OptionType.BACKEND
+
+ def is_builtin(self) -> bool:
+ """Convenience method to check if this is a builtin option."""
+ return self.type is OptionType.BUILTIN
+
+ def is_compiler(self) -> bool:
+ """Convenience method to check if this is a builtin option."""
+ return self.type is OptionType.COMPILER
+
+ def is_project(self) -> bool:
+ """Convenience method to check if this is a project option."""
+ return self.type is OptionType.PROJECT
+
+ def is_base(self) -> bool:
+ """Convenience method to check if this is a base option."""
+ return self.type is OptionType.BASE \ No newline at end of file
diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py
index f6262c3..52f4ac0 100644
--- a/mesonbuild/mintro.py
+++ b/mesonbuild/mintro.py
@@ -21,6 +21,7 @@ project files and don't need this info."""
import collections
import json
+from mesonbuild.compilers import d
from . import build, coredata as cdata
from . import mesonlib
from .ast import IntrospectionInterpreter, build_target_functions, AstConditionLevel, AstIDGenerator, AstIndentationGenerator, AstJSONPrinter
@@ -33,6 +34,8 @@ import typing as T
import os
import argparse
+from .mesonlib import OptionKey
+
def get_meson_info_file(info_dir: str) -> str:
return os.path.join(info_dir, 'meson-info.json')
@@ -167,8 +170,8 @@ def list_targets_from_source(intr: IntrospectionInterpreter) -> T.List[T.Dict[st
return tlist
-def list_targets(builddata: build.Build, installdata: backends.InstallData, backend: backends.Backend) -> T.List[T.Dict[str, T.Union[bool, str, T.List[T.Union[str, T.Dict[str, T.Union[str, T.List[str], bool]]]]]]]:
- tlist = [] # type: T.List[T.Dict[str, T.Union[bool, str, T.List[T.Union[str, T.Dict[str, T.Union[str, T.List[str], bool]]]]]]]
+def list_targets(builddata: build.Build, installdata: backends.InstallData, backend: backends.Backend) -> T.List[T.Any]:
+ tlist = [] # type: T.List[T.Any]
build_dir = builddata.environment.get_build_dir()
src_dir = builddata.environment.get_source_dir()
@@ -197,8 +200,8 @@ def list_targets(builddata: build.Build, installdata: backends.InstallData, back
if installdata and target.should_install():
t['installed'] = True
- t['install_filename'] = [install_lookuptable.get(x, [None]) for x in target.get_outputs()]
- t['install_filename'] = [x for sublist in t['install_filename'] for x in sublist] # flatten the list
+ ifn = [install_lookuptable.get(x, [None]) for x in target.get_outputs()]
+ t['install_filename'] = [x for sublist in ifn for x in sublist] # flatten the list
else:
t['installed'] = False
tlist.append(t)
@@ -210,30 +213,30 @@ 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 = 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]
-
- dir_options = {k: o for k, o in coredata.builtins.items() if k in dir_option_names}
- test_options = {k: o for k, o in coredata.builtins.items() if k in test_option_names}
- core_options = {k: o for k, o in coredata.builtins.items() if k in core_option_names}
-
- if subprojects:
- # Add per subproject built-in options
- sub_core_options = {}
- for sub in subprojects:
- for k, o in core_options.items():
- if o.yielding:
- continue
- sub_core_options[sub + ':' + k] = o
- core_options.update(sub_core_options)
-
- def add_keys(options: 'cdata.OptionDictType', section: str, machine: str = 'any') -> None:
- for key in sorted(options.keys()):
- opt = options[key]
- optdict = {'name': key, 'value': opt.value, 'section': section, 'machine': machine}
+ subprojects = subprojects or []
+
+ dir_option_names = set(cdata.BUILTIN_DIR_OPTIONS)
+ test_option_names = {OptionKey('errorlogs'),
+ OptionKey('stdsplit')}
+
+ dir_options: 'cdata.KeyedOptionDictType' = {}
+ test_options: 'cdata.KeyedOptionDictType' = {}
+ core_options: 'cdata.KeyedOptionDictType' = {}
+ for k, v in coredata.options.items():
+ if k in dir_option_names:
+ dir_options[k] = v
+ elif k in test_option_names:
+ test_options[k] = v
+ elif k.is_builtin():
+ core_options[k] = v
+ if not v.yielding:
+ for s in subprojects:
+ core_options[k.evolve(subproject=s)] = v
+
+ def add_keys(options: 'cdata.KeyedOptionDictType', section: str) -> None:
+ for key, opt in sorted(options.items()):
+ optdict = {'name': str(key), 'value': opt.value, 'section': section,
+ 'machine': key.machine.get_lower_case_name() if coredata.is_per_machine_option(key) else 'any'}
if isinstance(opt, cdata.UserStringOption):
typestr = 'string'
elif isinstance(opt, cdata.UserBooleanOption):
@@ -252,27 +255,14 @@ def list_buildoptions(coredata: cdata.CoreData, subprojects: T.Optional[T.List[s
optlist.append(optdict)
add_keys(core_options, 'core')
- add_keys(coredata.builtins_per_machine.host, 'core', machine='host')
- add_keys(
- {'build.' + k: o for k, o in coredata.builtins_per_machine.build.items()},
- 'core',
- machine='build',
- )
- add_keys(coredata.backend_options, 'backend')
- add_keys(coredata.base_options, 'base')
- add_keys(
- dict(coredata.flatten_lang_iterator(coredata.compiler_options.host.items())),
- 'compiler',
- machine='host',
- )
- tmp_dict = dict(coredata.flatten_lang_iterator(coredata.compiler_options.build.items())) # type: T.Dict[str, cdata.UserOption]
+ add_keys({k: v for k, v in coredata.options.items() if k.is_backend()}, 'backend')
+ add_keys({k: v for k, v in coredata.options.items() if k.is_base()}, 'base')
add_keys(
- {'build.' + k: o for k, o in tmp_dict.items()},
+ {k: v for k, v in sorted(coredata.options.items(), key=lambda i: i[0].machine) if k.is_compiler()},
'compiler',
- machine='build',
)
add_keys(dir_options, 'directory')
- add_keys(coredata.user_options, 'user')
+ add_keys({k: v for k, v in coredata.options.items() if k.is_project()}, 'user')
add_keys(test_options, 'test')
return optlist
diff --git a/mesonbuild/modules/cmake.py b/mesonbuild/modules/cmake.py
index 35c85a7..f6afaf3 100644
--- a/mesonbuild/modules/cmake.py
+++ b/mesonbuild/modules/cmake.py
@@ -269,7 +269,7 @@ class CmakeModule(ExtensionModule):
pkgroot = kwargs.get('install_dir', None)
if pkgroot is None:
- pkgroot = os.path.join(state.environment.coredata.get_builtin_option('libdir'), 'cmake', name)
+ pkgroot = os.path.join(state.environment.coredata.get_option(mesonlib.OptionKey('libdir')), 'cmake', name)
if not isinstance(pkgroot, str):
raise mesonlib.MesonException('Install_dir must be a string.')
@@ -342,7 +342,7 @@ class CmakeModule(ExtensionModule):
(ofile_path, ofile_fname) = os.path.split(os.path.join(state.subdir, '{}Config.cmake'.format(name)))
ofile_abs = os.path.join(state.environment.build_dir, ofile_path, ofile_fname)
- install_dir = kwargs.get('install_dir', os.path.join(state.environment.coredata.get_builtin_option('libdir'), 'cmake', name))
+ install_dir = kwargs.get('install_dir', os.path.join(state.environment.coredata.get_option(mesonlib.OptionKey('libdir')), 'cmake', name))
if not isinstance(install_dir, str):
raise mesonlib.MesonException('"install_dir" must be a string.')
@@ -352,7 +352,7 @@ class CmakeModule(ExtensionModule):
if not isinstance(conf, ConfigurationDataHolder):
raise mesonlib.MesonException('Argument "configuration" is not of type configuration_data')
- prefix = state.environment.coredata.get_builtin_option('prefix')
+ prefix = state.environment.coredata.get_option(mesonlib.OptionKey('prefix'))
abs_install_dir = install_dir
if not os.path.isabs(abs_install_dir):
abs_install_dir = os.path.join(prefix, install_dir)
diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py
index 547aff1..21570bd 100644
--- a/mesonbuild/modules/gnome.py
+++ b/mesonbuild/modules/gnome.py
@@ -19,6 +19,7 @@ import os
import copy
import subprocess
import functools
+import typing as T
from .. import build
from .. import mlog
@@ -35,6 +36,9 @@ from ..mesonlib import (
from ..dependencies import Dependency, PkgConfigDependency, InternalDependency, ExternalProgram
from ..interpreterbase import noKwargs, permittedKwargs, FeatureNew, FeatureNewKwargs, FeatureDeprecatedKwargs
+if T.TYPE_CHECKING:
+ from ..compilers import Compiler
+
# gresource compilation is broken due to the way
# the resource compiler and Ninja clash about it
#
@@ -208,7 +212,7 @@ class GnomeModule(ExtensionModule):
if install_header:
h_kwargs['install'] = install_header
h_kwargs['install_dir'] = kwargs.get('install_dir',
- state.environment.coredata.get_builtin_option('includedir'))
+ state.environment.coredata.get_option(mesonlib.OptionKey('includedir')))
target_h = GResourceHeaderTarget(args[0] + '_h', state.subdir, state.subproject, h_kwargs)
rv = [target_c, target_h]
return ModuleReturnValue(rv, rv)
@@ -574,8 +578,8 @@ class GnomeModule(ExtensionModule):
return ret
- def _get_girtargets_langs_compilers(self, girtargets):
- ret = []
+ def _get_girtargets_langs_compilers(self, girtargets: T.List[GirTarget]) -> T.List[T.Tuple[str, 'Compiler']]:
+ ret: T.List[T.Tuple[str, 'Compiler']] = []
for girtarget in girtargets:
for lang, compiler in girtarget.compilers.items():
# XXX: Can you use g-i with any other language?
@@ -598,7 +602,7 @@ class GnomeModule(ExtensionModule):
ret += girtarget.get_include_dirs()
return ret
- def _get_langs_compilers_flags(self, state, langs_compilers):
+ def _get_langs_compilers_flags(self, state, langs_compilers: T.List[T.Tuple[str, 'Compiler']]):
cflags = []
internal_ldflags = []
external_ldflags = []
@@ -608,8 +612,8 @@ class GnomeModule(ExtensionModule):
cflags += state.global_args[lang]
if state.project_args.get(lang):
cflags += state.project_args[lang]
- if 'b_sanitize' in compiler.base_options:
- sanitize = state.environment.coredata.base_options['b_sanitize'].value
+ if mesonlib.OptionKey('b_sanitize') in compiler.base_options:
+ sanitize = state.environment.coredata.options[mesonlib.OptionKey('b_sanitize')].value
cflags += compiler.sanitizer_compile_args(sanitize)
sanitize = sanitize.split(',')
# These must be first in ldflags
@@ -1171,7 +1175,7 @@ class GnomeModule(ExtensionModule):
targets = []
install_header = kwargs.get('install_header', False)
- install_dir = kwargs.get('install_dir', state.environment.coredata.get_builtin_option('includedir'))
+ install_dir = kwargs.get('install_dir', state.environment.coredata.get_option(mesonlib.OptionKey('includedir')))
output = namebase + '.c'
# Added in https://gitlab.gnome.org/GNOME/glib/commit/e4d68c7b3e8b01ab1a4231bf6da21d045cb5a816 (2.55.2)
@@ -1328,7 +1332,7 @@ class GnomeModule(ExtensionModule):
custom_kwargs['install'] = install_header
if 'install_dir' not in custom_kwargs:
custom_kwargs['install_dir'] = \
- state.environment.coredata.get_builtin_option('includedir')
+ state.environment.coredata.get_option(mesonlib.OptionKey('includedir'))
h_target = self._make_mkenum_custom_target(state, h_sources,
h_output, h_cmd,
custom_kwargs)
@@ -1357,7 +1361,7 @@ class GnomeModule(ExtensionModule):
custom_kwargs['install'] = install_header
if 'install_dir' not in custom_kwargs:
custom_kwargs['install_dir'] = \
- state.environment.coredata.get_builtin_option('includedir')
+ state.environment.coredata.get_option(mesonlib.OptionKey('includedir'))
target = self._make_mkenum_custom_target(state, sources, basename,
generic_cmd, custom_kwargs)
return ModuleReturnValue(target, [target])
@@ -1687,7 +1691,7 @@ G_END_DECLS'''
'depends': vapi_depends,
}
install_dir = kwargs.get('install_dir',
- os.path.join(state.environment.coredata.get_builtin_option('datadir'),
+ os.path.join(state.environment.coredata.get_option(mesonlib.OptionKey('datadir')),
'vala', 'vapi'))
if kwargs.get('install'):
custom_kwargs['install'] = kwargs['install']
diff --git a/mesonbuild/modules/hotdoc.py b/mesonbuild/modules/hotdoc.py
index 5c04e27..ee756e7 100644
--- a/mesonbuild/modules/hotdoc.py
+++ b/mesonbuild/modules/hotdoc.py
@@ -326,7 +326,7 @@ class HotdocTargetBuilder:
for path in self.include_paths.keys():
self.cmd.extend(['--include-path', path])
- if self.state.environment.coredata.get_builtin_option('werror', self.state.subproject):
+ if self.state.environment.coredata.get_option(mesonlib.OptionKey('werror', subproject=self.state.subproject)):
self.cmd.append('--fatal-warning')
self.generate_hotdoc_config()
diff --git a/mesonbuild/modules/i18n.py b/mesonbuild/modules/i18n.py
index 2652e7d..d48f83b 100644
--- a/mesonbuild/modules/i18n.py
+++ b/mesonbuild/modules/i18n.py
@@ -172,7 +172,7 @@ class I18nModule(ExtensionModule):
install = kwargs.get('install', True)
if install:
- install_dir = kwargs.get('install_dir', state.environment.coredata.get_builtin_option('localedir'))
+ install_dir = kwargs.get('install_dir', state.environment.coredata.get_option(mesonlib.OptionKey('localedir')))
script = state.environment.get_build_command()
args = ['--internal', 'gettext', 'install',
'--subdir=' + state.subdir,
diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py
index 7e19d71..7d347a6 100644
--- a/mesonbuild/modules/pkgconfig.py
+++ b/mesonbuild/modules/pkgconfig.py
@@ -331,10 +331,10 @@ class PkgConfigModule(ExtensionModule):
srcdir = PurePath(state.environment.get_source_dir())
else:
outdir = state.environment.scratch_dir
- prefix = PurePath(coredata.get_builtin_option('prefix'))
+ prefix = PurePath(coredata.get_option(mesonlib.OptionKey('prefix')))
# These always return paths relative to prefix
- libdir = PurePath(coredata.get_builtin_option('libdir'))
- incdir = PurePath(coredata.get_builtin_option('includedir'))
+ libdir = PurePath(coredata.get_option(mesonlib.OptionKey('libdir')))
+ incdir = PurePath(coredata.get_option(mesonlib.OptionKey('includedir')))
fname = os.path.join(outdir, pcfile)
with open(fname, 'w', encoding='utf-8') as ofile:
if not dataonly:
@@ -531,9 +531,9 @@ class PkgConfigModule(ExtensionModule):
pkgroot = kwargs.get('install_dir', default_install_dir)
if pkgroot is None:
if mesonlib.is_freebsd():
- pkgroot = os.path.join(state.environment.coredata.get_builtin_option('prefix'), 'libdata', 'pkgconfig')
+ pkgroot = os.path.join(state.environment.coredata.get_option(mesonlib.OptionKey('prefix')), 'libdata', 'pkgconfig')
else:
- pkgroot = os.path.join(state.environment.coredata.get_builtin_option('libdir'), 'pkgconfig')
+ pkgroot = os.path.join(state.environment.coredata.get_option(mesonlib.OptionKey('libdir')), 'pkgconfig')
if not isinstance(pkgroot, str):
raise mesonlib.MesonException('Install_dir must be a string.')
self.generate_pkgconfig_file(state, deps, subdirs, name, description, url,
diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py
index ff0df2f..564d181 100644
--- a/mesonbuild/modules/python.py
+++ b/mesonbuild/modules/python.py
@@ -288,7 +288,7 @@ class PythonInstallation(ExternalProgramHolder):
ExternalProgramHolder.__init__(self, python, interpreter.subproject)
self.interpreter = interpreter
self.subproject = self.interpreter.subproject
- prefix = self.interpreter.environment.coredata.get_builtin_option('prefix')
+ prefix = self.interpreter.environment.coredata.get_option(mesonlib.OptionKey('prefix'))
self.variables = info['variables']
self.paths = info['paths']
install_paths = info['install_paths']
diff --git a/mesonbuild/modules/unstable_external_project.py b/mesonbuild/modules/unstable_external_project.py
index 7bb761f..7249078 100644
--- a/mesonbuild/modules/unstable_external_project.py
+++ b/mesonbuild/modules/unstable_external_project.py
@@ -26,6 +26,7 @@ from ..interpreter import Interpreter, DependencyHolder, InstallDir
from ..compilers.compilers import cflags_mapping, cexe_mapping
from ..dependencies.base import InternalDependency, PkgConfigDependency
from ..environment import Environment
+from ..mesonlib import OptionKey
class ExternalProject(InterpreterObject):
def __init__(self,
@@ -62,9 +63,9 @@ class ExternalProject(InterpreterObject):
self.src_dir = Path(self.env.get_source_dir(), self.subdir)
self.build_dir = Path(self.env.get_build_dir(), self.subdir, 'build')
self.install_dir = Path(self.env.get_build_dir(), self.subdir, 'dist')
- self.prefix = Path(self.env.coredata.get_builtin_option('prefix'))
- self.libdir = Path(self.env.coredata.get_builtin_option('libdir'))
- self.includedir = Path(self.env.coredata.get_builtin_option('includedir'))
+ self.prefix = Path(self.env.coredata.get_option(OptionKey('prefix')))
+ self.libdir = Path(self.env.coredata.get_option(OptionKey('libdir')))
+ self.includedir = Path(self.env.coredata.get_option(OptionKey('includedir')))
# On Windows if the prefix is "c:/foo" and DESTDIR is "c:/bar", `make`
# will install files into "c:/bar/c:/foo" which is an invalid path.
diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py
index d336a13..11fe3ce 100644
--- a/mesonbuild/msetup.py
+++ b/mesonbuild/msetup.py
@@ -177,7 +177,7 @@ class MesonApp:
mlog.initialize(env.get_log_dir(), self.options.fatal_warnings)
if self.options.profile:
mlog.set_timestamp_start(time.monotonic())
- if env.coredata.builtins['backend'].value == 'xcode':
+ if env.coredata.options[mesonlib.OptionKey('backend')].value == 'xcode':
mlog.warning('xcode backend is currently unmaintained, patches welcome')
with mesonlib.BuildDirLock(self.build_dir):
self._generate(env)
diff --git a/mesonbuild/munstable_coredata.py b/mesonbuild/munstable_coredata.py
index 5463f16..0ca8f33 100644
--- a/mesonbuild/munstable_coredata.py
+++ b/mesonbuild/munstable_coredata.py
@@ -14,7 +14,7 @@
from . import coredata as cdata
-from .mesonlib import MachineChoice
+from .mesonlib import MachineChoice, OptionKey
import os.path
import pprint
@@ -59,7 +59,7 @@ def run(options):
print('')
coredata = cdata.load(options.builddir)
- backend = coredata.get_builtin_option('backend')
+ backend = coredata.get_option(OptionKey('backend'))
for k, v in sorted(coredata.__dict__.items()):
if k in ('backend_options', 'base_options', 'builtins', 'compiler_options', 'user_options'):
# use `meson configure` to view these
diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py
index 0b18f7e..dc8c3ce 100644
--- a/mesonbuild/optinterpreter.py
+++ b/mesonbuild/optinterpreter.py
@@ -137,7 +137,7 @@ option_types = {'string': string_parser,
class OptionInterpreter:
def __init__(self, subproject: str) -> None:
- self.options: T.Dict[str, coredata.UserOption] = {}
+ self.options: 'coredata.KeyedOptionDictType' = {}
self.subproject = subproject
def process(self, option_file: str) -> None:
@@ -227,8 +227,7 @@ class OptionInterpreter:
raise OptionException('Option names can only contain letters, numbers or dashes.')
if is_invalid_name(opt_name):
raise OptionException('Option name %s is reserved.' % opt_name)
- if self.subproject != '':
- opt_name = self.subproject + ':' + opt_name
+ key = mesonlib.OptionKey(opt_name, self.subproject)
if 'yield' in kwargs:
FeatureNew.single_use('option yield', '0.45.0', self.subproject)
@@ -248,4 +247,4 @@ class OptionInterpreter:
opt = option_types[opt_type](opt_name, description, kwargs)
if opt.description == '':
opt.description = opt_name
- self.options[opt_name] = opt
+ self.options[key] = opt
diff --git a/mesonbuild/rewriter.py b/mesonbuild/rewriter.py
index 857ba0e..6b7a987 100644
--- a/mesonbuild/rewriter.py
+++ b/mesonbuild/rewriter.py
@@ -464,14 +464,11 @@ class Rewriter:
cdata = self.interpreter.coredata
options = {
- **cdata.builtins,
- **cdata.builtins_per_machine.host,
- **{'build.' + k: o for k, o in cdata.builtins_per_machine.build.items()},
- **cdata.backend_options,
- **cdata.base_options,
- **(dict(cdata.flatten_lang_iterator(cdata.compiler_options.host.items()))),
- **{'build.' + k: o for k, o in cdata.flatten_lang_iterator(cdata.compiler_options.build.items())},
- **cdata.user_options,
+ **{str(k): v for k, v in cdata.options.items()},
+ **{str(k): v for k, v in cdata.options.items()},
+ **{str(k): v for k, v in cdata.options.items()},
+ **{str(k): v for k, v in cdata.options.items()},
+ **{str(k): v for k, v in cdata.options.items()},
}
for key, val in sorted(cmd['options'].items()):
diff --git a/mesonbuild/scripts/regen_checker.py b/mesonbuild/scripts/regen_checker.py
index fa98f59..1187783 100644
--- a/mesonbuild/scripts/regen_checker.py
+++ b/mesonbuild/scripts/regen_checker.py
@@ -17,6 +17,7 @@ import pickle, subprocess
import typing as T
from ..coredata import CoreData
from ..backend.vs2010backend import RegenInfo
+from ..mesonlib import OptionKey
# This could also be used for XCode.
@@ -52,7 +53,7 @@ def run(args: T.List[str]) -> int:
with open(coredata_file, 'rb') as f:
coredata = pickle.load(f)
assert isinstance(coredata, CoreData)
- backend = coredata.get_builtin_option('backend')
+ backend = coredata.get_option(OptionKey('backend'))
assert isinstance(backend, str)
regen_timestamp = os.stat(dumpfile).st_mtime
if need_regen(regeninfo, regen_timestamp):
diff --git a/run_tests.py b/run_tests.py
index 0f02636..da894c0 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -35,6 +35,7 @@ from mesonbuild import mtest
from mesonbuild import mlog
from mesonbuild.environment import Environment, detect_ninja
from mesonbuild.coredata import backendlist, version as meson_version
+from mesonbuild.mesonlib import OptionKey
NINJA_1_9_OR_NEWER = False
NINJA_CMD = None
@@ -127,7 +128,7 @@ def get_fake_env(sdir='', bdir=None, prefix='', opts=None):
if opts is None:
opts = get_fake_options(prefix)
env = Environment(sdir, bdir, opts)
- env.coredata.compiler_options.host['c']['args'] = FakeCompilerOptions()
+ env.coredata.options[OptionKey('args', lang='c')] = FakeCompilerOptions()
env.machines.host.cpu_family = 'x86_64' # Used on macOS inside find_library
return env
diff --git a/run_unittests.py b/run_unittests.py
index 4b14a87..1ff54eb 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -30,8 +30,6 @@ import functools
import io
import operator
import threading
-import urllib.error
-import urllib.request
import zipfile
import hashlib
from itertools import chain
@@ -61,7 +59,7 @@ from mesonbuild.mesonlib import (
quote_arg, relpath, is_linux, git, GIT
)
from mesonbuild.environment import detect_ninja
-from mesonbuild.mesonlib import MesonException, EnvironmentException
+from mesonbuild.mesonlib import MesonException, EnvironmentException, OptionKey
from mesonbuild.dependencies import PkgConfigDependency, ExternalProgram
import mesonbuild.dependencies.base
from mesonbuild.build import Target, ConfigurationData
@@ -265,7 +263,8 @@ def skip_if_not_base_option(feature):
def wrapped(*args, **kwargs):
env = get_fake_env()
cc = env.detect_c_compiler(MachineChoice.HOST)
- if feature not in cc.base_options:
+ key = OptionKey(feature)
+ if key not in cc.base_options:
raise unittest.SkipTest(
'{} not available with {}'.format(feature, cc.id))
return f(*args, **kwargs)
@@ -874,7 +873,7 @@ class InternalTests(unittest.TestCase):
env = get_fake_env()
compiler = env.detect_c_compiler(MachineChoice.HOST)
env.coredata.compilers.host = {'c': compiler}
- env.coredata.compiler_options.host['c']['link_args'] = FakeCompilerOptions()
+ env.coredata.options[OptionKey('link_args', lang='c')] = FakeCompilerOptions()
p1 = Path(tmpdir) / '1'
p2 = Path(tmpdir) / '2'
p1.mkdir()
@@ -1336,10 +1335,10 @@ class DataTests(unittest.TestCase):
cc = env.detect_c_compiler(MachineChoice.HOST)
cpp = env.detect_cpp_compiler(MachineChoice.HOST)
for comp in (cc, cpp):
- for opt in comp.get_options().keys():
- self.assertIn(opt, md)
+ for opt in comp.get_options():
+ self.assertIn(str(opt), md)
for opt in comp.base_options:
- self.assertIn(opt, md)
+ self.assertIn(str(opt), md)
self.assertNotIn('b_unknown', md)
@staticmethod
@@ -1392,8 +1391,8 @@ class DataTests(unittest.TestCase):
found_entries |= options
self.assertEqual(found_entries, set([
- *mesonbuild.coredata.BUILTIN_OPTIONS.keys(),
- *mesonbuild.coredata.BUILTIN_OPTIONS_PER_MACHINE.keys()
+ *[str(k) for k in mesonbuild.coredata.BUILTIN_OPTIONS],
+ *[str(k) for k in mesonbuild.coredata.BUILTIN_OPTIONS_PER_MACHINE],
]))
# Check that `buildtype` table inside `Core options` matches how
@@ -1414,10 +1413,10 @@ class DataTests(unittest.TestCase):
debug = False
else:
raise RuntimeError('Invalid debug value {!r} in row:\n{}'.format(debug, m.group()))
- env.coredata.set_builtin_option('buildtype', buildtype)
- self.assertEqual(env.coredata.builtins['buildtype'].value, buildtype)
- self.assertEqual(env.coredata.builtins['optimization'].value, opt)
- self.assertEqual(env.coredata.builtins['debug'].value, debug)
+ env.coredata.set_option(OptionKey('buildtype'), buildtype)
+ self.assertEqual(env.coredata.options[OptionKey('buildtype')].value, buildtype)
+ self.assertEqual(env.coredata.options[OptionKey('optimization')].value, opt)
+ self.assertEqual(env.coredata.options[OptionKey('debug')].value, debug)
def test_cpu_families_documented(self):
with open("docs/markdown/Reference-tables.md", encoding='utf-8') as f:
@@ -1905,11 +1904,14 @@ class AllPlatformTests(BasePlatformTests):
https://github.com/mesonbuild/meson/issues/1349
'''
testdir = os.path.join(self.common_test_dir, '88 default options')
- self.init(testdir, default_args=False)
+ self.init(testdir, default_args=False, inprocess=True)
opts = self.introspect('--buildoptions')
for opt in opts:
if opt['name'] == 'prefix':
prefix = opt['value']
+ break
+ else:
+ raise self.fail('Did not find option "prefix"')
self.assertEqual(prefix, '/absoluteprefix')
def test_do_conf_file_preserve_newlines(self):
@@ -3632,10 +3634,10 @@ class AllPlatformTests(BasePlatformTests):
def test_conflicting_d_dash_option(self):
testdir = os.path.join(self.unit_test_dir, '37 mixed command line args')
- with self.assertRaises(subprocess.CalledProcessError) as e:
+ with self.assertRaises((subprocess.CalledProcessError, RuntimeError)) as e:
self.init(testdir, extra_args=['-Dbindir=foo', '--bindir=bar'])
# Just to ensure that we caught the correct error
- self.assertIn('passed as both', e.stderr)
+ self.assertIn('as both', e.stderr)
def _test_same_option_twice(self, arg, args):
testdir = os.path.join(self.unit_test_dir, '37 mixed command line args')
@@ -3684,57 +3686,63 @@ class AllPlatformTests(BasePlatformTests):
# Verify default values when passing no args that affect the
# configuration, and as a bonus, test that --profile-self works.
- self.init(testdir, extra_args=['--profile-self'])
+ self.init(testdir, extra_args=['--profile-self', '--fatal-meson-warnings'])
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.builtins['default_library'].value, 'static')
- self.assertEqual(obj.builtins['warning_level'].value, '1')
- self.assertEqual(obj.user_options['set_sub_opt'].value, True)
- self.assertEqual(obj.user_options['subp:subp_opt'].value, 'default3')
+ self.assertEqual(obj.options[OptionKey('default_library')].value, 'static')
+ self.assertEqual(obj.options[OptionKey('warning_level')].value, '1')
+ self.assertEqual(obj.options[OptionKey('set_sub_opt')].value, True)
+ self.assertEqual(obj.options[OptionKey('subp_opt', 'subp')].value, 'default3')
self.wipe()
# warning_level is special, it's --warnlevel instead of --warning-level
# for historical reasons
- self.init(testdir, extra_args=['--warnlevel=2'])
+ self.init(testdir, extra_args=['--warnlevel=2', '--fatal-meson-warnings'])
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.builtins['warning_level'].value, '2')
+ self.assertEqual(obj.options[OptionKey('warning_level')].value, '2')
self.setconf('--warnlevel=3')
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.builtins['warning_level'].value, '3')
+ self.assertEqual(obj.options[OptionKey('warning_level')].value, '3')
self.wipe()
# But when using -D syntax, it should be 'warning_level'
- self.init(testdir, extra_args=['-Dwarning_level=2'])
+ self.init(testdir, extra_args=['-Dwarning_level=2', '--fatal-meson-warnings'])
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.builtins['warning_level'].value, '2')
+ self.assertEqual(obj.options[OptionKey('warning_level')].value, '2')
self.setconf('-Dwarning_level=3')
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.builtins['warning_level'].value, '3')
+ self.assertEqual(obj.options[OptionKey('warning_level')].value, '3')
self.wipe()
# Mixing --option and -Doption is forbidden
- with self.assertRaises(subprocess.CalledProcessError) as cm:
+ with self.assertRaises((subprocess.CalledProcessError, RuntimeError)) as cm:
self.init(testdir, extra_args=['--warnlevel=1', '-Dwarning_level=3'])
- self.assertNotEqual(0, cm.exception.returncode)
- self.assertIn('as both', cm.exception.output)
+ if isinstance(cm.exception, subprocess.CalledProcessError):
+ self.assertNotEqual(0, cm.exception.returncode)
+ self.assertIn('as both', cm.exception.output)
+ else:
+ self.assertIn('as both', str(cm.exception))
self.init(testdir)
- with self.assertRaises(subprocess.CalledProcessError) as cm:
+ with self.assertRaises((subprocess.CalledProcessError, RuntimeError)) as cm:
self.setconf(['--warnlevel=1', '-Dwarning_level=3'])
- self.assertNotEqual(0, cm.exception.returncode)
- self.assertIn('as both', cm.exception.output)
+ if isinstance(cm.exception, subprocess.CalledProcessError):
+ self.assertNotEqual(0, cm.exception.returncode)
+ self.assertIn('as both', cm.exception.output)
+ else:
+ self.assertIn('as both', str(cm.exception))
self.wipe()
# --default-library should override default value from project()
- self.init(testdir, extra_args=['--default-library=both'])
+ self.init(testdir, extra_args=['--default-library=both', '--fatal-meson-warnings'])
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.builtins['default_library'].value, 'both')
+ self.assertEqual(obj.options[OptionKey('default_library')].value, 'both')
self.setconf('--default-library=shared')
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.builtins['default_library'].value, 'shared')
+ self.assertEqual(obj.options[OptionKey('default_library')].value, 'shared')
if self.backend is Backend.ninja:
# reconfigure target works only with ninja backend
self.build('reconfigure')
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.builtins['default_library'].value, 'shared')
+ self.assertEqual(obj.options[OptionKey('default_library')].value, 'shared')
self.wipe()
# Should warn on unknown options
@@ -3743,15 +3751,22 @@ class AllPlatformTests(BasePlatformTests):
self.wipe()
# Should fail on malformed option
- with self.assertRaises(subprocess.CalledProcessError) as cm:
+ msg = "Option 'foo' must have a value separated by equals sign."
+ with self.assertRaises((subprocess.CalledProcessError, RuntimeError)) as cm:
self.init(testdir, extra_args=['-Dfoo'])
- self.assertNotEqual(0, cm.exception.returncode)
- self.assertIn('Option \'foo\' must have a value separated by equals sign.', cm.exception.output)
+ if isinstance(cm.exception, subprocess.CalledProcessError):
+ self.assertNotEqual(0, cm.exception.returncode)
+ self.assertIn(msg, cm.exception.output)
+ else:
+ self.assertIn(msg, str(cm.exception))
self.init(testdir)
- with self.assertRaises(subprocess.CalledProcessError) as cm:
+ with self.assertRaises((subprocess.CalledProcessError, RuntimeError)) as cm:
self.setconf('-Dfoo')
- self.assertNotEqual(0, cm.exception.returncode)
- self.assertIn('Option \'foo\' must have a value separated by equals sign.', cm.exception.output)
+ if isinstance(cm.exception, subprocess.CalledProcessError):
+ self.assertNotEqual(0, cm.exception.returncode)
+ self.assertIn(msg, cm.exception.output)
+ else:
+ self.assertIn(msg, str(cm.exception))
self.wipe()
# It is not an error to set wrong option for unknown subprojects or
@@ -3760,24 +3775,24 @@ class AllPlatformTests(BasePlatformTests):
self.wipe()
# Test we can set subproject option
- self.init(testdir, extra_args=['-Dsubp:subp_opt=foo'])
+ self.init(testdir, extra_args=['-Dsubp:subp_opt=foo', '--fatal-meson-warnings'])
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.user_options['subp:subp_opt'].value, 'foo')
+ self.assertEqual(obj.options[OptionKey('subp_opt', 'subp')].value, 'foo')
self.wipe()
# c_args value should be parsed with split_args
- self.init(testdir, extra_args=['-Dc_args=-Dfoo -Dbar "-Dthird=one two"'])
+ self.init(testdir, extra_args=['-Dc_args=-Dfoo -Dbar "-Dthird=one two"', '--fatal-meson-warnings'])
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.compiler_options.host['c']['args'].value, ['-Dfoo', '-Dbar', '-Dthird=one two'])
+ self.assertEqual(obj.options[OptionKey('args', lang='c')].value, ['-Dfoo', '-Dbar', '-Dthird=one two'])
self.setconf('-Dc_args="foo bar" one two')
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.compiler_options.host['c']['args'].value, ['foo bar', 'one', 'two'])
+ self.assertEqual(obj.options[OptionKey('args', lang='c')].value, ['foo bar', 'one', 'two'])
self.wipe()
- self.init(testdir, extra_args=['-Dset_percent_opt=myoption%'])
+ self.init(testdir, extra_args=['-Dset_percent_opt=myoption%', '--fatal-meson-warnings'])
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.user_options['set_percent_opt'].value, 'myoption%')
+ self.assertEqual(obj.options[OptionKey('set_percent_opt')].value, 'myoption%')
self.wipe()
# Setting a 2nd time the same option should override the first value
@@ -3785,21 +3800,22 @@ class AllPlatformTests(BasePlatformTests):
self.init(testdir, extra_args=['--bindir=foo', '--bindir=bar',
'-Dbuildtype=plain', '-Dbuildtype=release',
'-Db_sanitize=address', '-Db_sanitize=thread',
- '-Dc_args=-Dfoo', '-Dc_args=-Dbar'])
+ '-Dc_args=-Dfoo', '-Dc_args=-Dbar',
+ '-Db_lundef=false', '--fatal-meson-warnings'])
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.builtins['bindir'].value, 'bar')
- self.assertEqual(obj.builtins['buildtype'].value, 'release')
- self.assertEqual(obj.base_options['b_sanitize'].value, 'thread')
- self.assertEqual(obj.compiler_options.host['c']['args'].value, ['-Dbar'])
+ self.assertEqual(obj.options[OptionKey('bindir')].value, 'bar')
+ self.assertEqual(obj.options[OptionKey('buildtype')].value, 'release')
+ self.assertEqual(obj.options[OptionKey('b_sanitize')].value, 'thread')
+ self.assertEqual(obj.options[OptionKey('args', lang='c')].value, ['-Dbar'])
self.setconf(['--bindir=bar', '--bindir=foo',
'-Dbuildtype=release', '-Dbuildtype=plain',
'-Db_sanitize=thread', '-Db_sanitize=address',
'-Dc_args=-Dbar', '-Dc_args=-Dfoo'])
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.builtins['bindir'].value, 'foo')
- self.assertEqual(obj.builtins['buildtype'].value, 'plain')
- self.assertEqual(obj.base_options['b_sanitize'].value, 'address')
- self.assertEqual(obj.compiler_options.host['c']['args'].value, ['-Dfoo'])
+ self.assertEqual(obj.options[OptionKey('bindir')].value, 'foo')
+ self.assertEqual(obj.options[OptionKey('buildtype')].value, 'plain')
+ self.assertEqual(obj.options[OptionKey('b_sanitize')].value, 'address')
+ self.assertEqual(obj.options[OptionKey('args', lang='c')].value, ['-Dfoo'])
self.wipe()
except KeyError:
# Ignore KeyError, it happens on CI for compilers that does not
@@ -3813,25 +3829,25 @@ class AllPlatformTests(BasePlatformTests):
# Verify default values when passing no args
self.init(testdir)
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.builtins['warning_level'].value, '0')
+ self.assertEqual(obj.options[OptionKey('warning_level')].value, '0')
self.wipe()
# verify we can override w/ --warnlevel
self.init(testdir, extra_args=['--warnlevel=1'])
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.builtins['warning_level'].value, '1')
+ self.assertEqual(obj.options[OptionKey('warning_level')].value, '1')
self.setconf('--warnlevel=0')
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.builtins['warning_level'].value, '0')
+ self.assertEqual(obj.options[OptionKey('warning_level')].value, '0')
self.wipe()
# verify we can override w/ -Dwarning_level
self.init(testdir, extra_args=['-Dwarning_level=1'])
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.builtins['warning_level'].value, '1')
+ self.assertEqual(obj.options[OptionKey('warning_level')].value, '1')
self.setconf('-Dwarning_level=0')
obj = mesonbuild.coredata.load(self.builddir)
- self.assertEqual(obj.builtins['warning_level'].value, '0')
+ self.assertEqual(obj.options[OptionKey('warning_level')].value, '0')
self.wipe()
def test_feature_check_usage_subprojects(self):
@@ -4238,7 +4254,8 @@ class AllPlatformTests(BasePlatformTests):
self.init(testdir, default_args=False)
res_wb = self.introspect('--buildoptions')
self.maxDiff = None
- self.assertListEqual(res_nb, res_wb)
+ # XXX: These now generate in a different order, is that okay?
+ self.assertListEqual(sorted(res_nb, key=lambda x: x['name']), sorted(res_wb, key=lambda x: x['name']))
def test_meson_configure_from_source_does_not_crash(self):
testdir = os.path.join(self.unit_test_dir, '59 introspect buildoptions')
@@ -4489,20 +4506,20 @@ class AllPlatformTests(BasePlatformTests):
with open(introfile, 'r') as fp:
res1 = json.load(fp)
- self.setconf('-Dcpp_std=c++14')
- self.setconf('-Dbuildtype=release')
-
- for idx, i in enumerate(res1):
+ for i in res1:
if i['name'] == 'cpp_std':
- res1[idx]['value'] = 'c++14'
+ i['value'] = 'c++14'
if i['name'] == 'build.cpp_std':
- res1[idx]['value'] = 'c++14'
+ i['value'] = 'c++14'
if i['name'] == 'buildtype':
- res1[idx]['value'] = 'release'
+ i['value'] = 'release'
if i['name'] == 'optimization':
- res1[idx]['value'] = '3'
+ i['value'] = '3'
if i['name'] == 'debug':
- res1[idx]['value'] = False
+ i['value'] = False
+
+ self.setconf('-Dcpp_std=c++14')
+ self.setconf('-Dbuildtype=release')
with open(introfile, 'r') as fp:
res2 = json.load(fp)
@@ -5758,7 +5775,7 @@ class WindowsTests(BasePlatformTests):
# Verify that the `b_vscrt` option is available
env = get_fake_env()
cc = env.detect_c_compiler(MachineChoice.HOST)
- if 'b_vscrt' not in cc.base_options:
+ if OptionKey('b_vscrt') not in cc.base_options:
raise unittest.SkipTest('Compiler does not support setting the VS CRT')
# Verify that qmake is for Qt5
if not shutil.which('qmake-qt5'):
@@ -5784,7 +5801,7 @@ class WindowsTests(BasePlatformTests):
# Verify that the `b_vscrt` option is available
env = get_fake_env()
cc = env.detect_c_compiler(MachineChoice.HOST)
- if 'b_vscrt' not in cc.base_options:
+ if OptionKey('b_vscrt') not in cc.base_options:
raise unittest.SkipTest('Compiler does not support setting the VS CRT')
def sanitycheck_vscrt(vscrt):
@@ -6285,7 +6302,7 @@ class LinuxlikeTests(BasePlatformTests):
Oargs = [arg for arg in cmd if arg.startswith('-O')]
self.assertEqual(Oargs, [Oflag, '-O0'])
- def _test_stds_impl(self, testdir, compiler: 'Compiler', p: str) -> None:
+ def _test_stds_impl(self, testdir: str, compiler: 'Compiler') -> None:
has_cpp17 = (compiler.get_id() not in {'clang', 'gcc'} or
compiler.get_id() == 'clang' and _clang_at_least(compiler, '>=5.0.0', '>=9.1') or
compiler.get_id() == 'gcc' and version_compare(compiler.version, '>=5.0.0'))
@@ -6301,8 +6318,8 @@ class LinuxlikeTests(BasePlatformTests):
# Check that all the listed -std=xxx options for this compiler work just fine when used
# https://en.wikipedia.org/wiki/Xcode#Latest_versions
# https://www.gnu.org/software/gcc/projects/cxx-status.html
- for v in compiler.get_options()['std'].choices:
- lang_std = p + '_std'
+ key = OptionKey('std', lang=compiler.language)
+ for v in compiler.get_options()[key].choices:
# we do it like this to handle gnu++17,c++17 and gnu17,c17 cleanly
# thus, C++ first
if '++17' in v and not has_cpp17:
@@ -6316,8 +6333,7 @@ class LinuxlikeTests(BasePlatformTests):
continue
elif '18' in v and not has_c18:
continue
- std_opt = '{}={}'.format(lang_std, v)
- self.init(testdir, extra_args=['-D' + std_opt])
+ self.init(testdir, extra_args=[f'-D{key!s}={v}'])
cmd = self.get_compdb()[0]['command']
# c++03 and gnu++03 are not understood by ICC, don't try to look for them
skiplist = frozenset([
@@ -6329,15 +6345,15 @@ class LinuxlikeTests(BasePlatformTests):
try:
self.build()
except Exception:
- print('{} was {!r}'.format(lang_std, v))
+ print(f'{key!s} was {v!r}')
raise
self.wipe()
# Check that an invalid std option in CFLAGS/CPPFLAGS fails
# Needed because by default ICC ignores invalid options
cmd_std = '-std=FAIL'
- if p == 'c':
+ if compiler.language == 'c':
env_flag_name = 'CFLAGS'
- elif p == 'cpp':
+ elif compiler.language == 'cpp':
env_flag_name = 'CXXFLAGS'
else:
raise NotImplementedError('Language {} not defined.'.format(p))
@@ -6358,7 +6374,7 @@ class LinuxlikeTests(BasePlatformTests):
testdir = os.path.join(self.common_test_dir, '1 trivial')
env = get_fake_env(testdir, self.builddir, self.prefix)
cc = env.detect_c_compiler(MachineChoice.HOST)
- self._test_stds_impl(testdir, cc, 'c')
+ self._test_stds_impl(testdir, cc)
def test_compiler_cpp_stds(self):
'''
@@ -6368,7 +6384,7 @@ class LinuxlikeTests(BasePlatformTests):
testdir = os.path.join(self.common_test_dir, '2 cpp')
env = get_fake_env(testdir, self.builddir, self.prefix)
cpp = env.detect_cpp_compiler(MachineChoice.HOST)
- self._test_stds_impl(testdir, cpp, 'cpp')
+ self._test_stds_impl(testdir, cpp)
def test_unity_subproj(self):
testdir = os.path.join(self.common_test_dir, '43 subproject')
@@ -6890,7 +6906,7 @@ class LinuxlikeTests(BasePlatformTests):
self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'librelativepath.pc')))
env = get_fake_env(testdir, self.builddir, self.prefix)
- env.coredata.set_options({'pkg_config_path': pkg_dir}, subproject='')
+ env.coredata.set_options({OptionKey('pkg_config_path'): pkg_dir}, subproject='')
kwargs = {'required': True, 'silent': True}
relative_path_dep = PkgConfigDependency('librelativepath', env, kwargs)
self.assertTrue(relative_path_dep.found())
@@ -8442,9 +8458,13 @@ class NativeFileTests(BasePlatformTests):
testcase = os.path.join(self.common_test_dir, '224 persubproject options')
config = self.helper_create_native_file({'sub2:built-in options': {'default_library': 'shared'}})
- with self.assertRaises(subprocess.CalledProcessError) as cm:
+ with self.assertRaises((RuntimeError, subprocess.CalledProcessError)) as cm:
self.init(testcase, extra_args=['--native-file', config])
- self.assertIn(cm.exception.stdout, 'Parent should override default_library')
+ if isinstance(cm, RuntimeError):
+ check = str(cm.exception)
+ else:
+ check = cm.exception.stdout
+ self.assertIn(check, 'Parent should override default_library')
def test_builtin_options_subprojects_dont_inherits_parent_override(self):
# If the buildfile says subproject(... default_library: shared), ensure that's overwritten
@@ -9299,7 +9319,7 @@ class SubprojectsCommandTests(BasePlatformTests):
out = self._subprojects_cmd(['foreach', '--types', 'git'] + dummy_cmd)
self.assertEqual(ran_in(out), ['subprojects/sub_git'])
-def _clang_at_least(compiler, minver: str, apple_minver: T.Optional[str]) -> bool:
+def _clang_at_least(compiler: 'Compiler', minver: str, apple_minver: T.Optional[str]) -> bool:
"""
check that Clang compiler is at least a specified version, whether AppleClang or regular Clang