aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2024-05-22 23:59:02 +0300
committerJussi Pakkanen <jpakkane@gmail.com>2024-05-23 21:26:45 +0300
commit5365d9a842938dc9182b52bcdb9674a434475200 (patch)
tree993785966b37b9243281ec4eebcbac669ae5d708
parent631b38577ee01d9273f7f42747762d5f8cabc15a (diff)
downloadmeson-5365d9a842938dc9182b52bcdb9674a434475200.zip
meson-5365d9a842938dc9182b52bcdb9674a434475200.tar.gz
meson-5365d9a842938dc9182b52bcdb9674a434475200.tar.bz2
Refactor option classes to their own file.
-rw-r--r--mesonbuild/ast/introspection.py6
-rw-r--r--mesonbuild/cargo/interpreter.py8
-rw-r--r--mesonbuild/compilers/c.py34
-rw-r--r--mesonbuild/compilers/compilers.py47
-rw-r--r--mesonbuild/compilers/cpp.py54
-rw-r--r--mesonbuild/compilers/cuda.py5
-rw-r--r--mesonbuild/compilers/cython.py6
-rw-r--r--mesonbuild/compilers/fortran.py4
-rw-r--r--mesonbuild/compilers/mixins/emscripten.py3
-rw-r--r--mesonbuild/compilers/objc.py3
-rw-r--r--mesonbuild/compilers/objcpp.py3
-rw-r--r--mesonbuild/compilers/rust.py4
-rw-r--r--mesonbuild/coredata.py569
-rw-r--r--mesonbuild/interpreter/compiler.py5
-rw-r--r--mesonbuild/interpreter/interpreter.py15
-rw-r--r--mesonbuild/interpreter/interpreterobjects.py26
-rw-r--r--mesonbuild/interpreter/kwargs.py4
-rw-r--r--mesonbuild/interpreter/type_checking.py2
-rw-r--r--mesonbuild/interpreterbase/helpers.py2
-rw-r--r--mesonbuild/mconf.py21
-rw-r--r--mesonbuild/mintro.py20
-rw-r--r--mesonbuild/modules/_qt.py4
-rw-r--r--mesonbuild/modules/pkgconfig.py2
-rw-r--r--mesonbuild/modules/python.py2
-rw-r--r--mesonbuild/optinterpreter.py35
-rw-r--r--mesonbuild/options.py480
-rwxr-xr-xrun_project_tests.py3
-rwxr-xr-xrun_tests.py3
-rw-r--r--test cases/unit/116 empty project/expected_mods.json3
-rw-r--r--unittests/datatests.py5
-rw-r--r--unittests/platformagnostictests.py2
31 files changed, 713 insertions, 667 deletions
diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py
index fa11feb..c7dcf73 100644
--- a/mesonbuild/ast/introspection.py
+++ b/mesonbuild/ast/introspection.py
@@ -10,7 +10,7 @@ import copy
import os
import typing as T
-from .. import compilers, environment, mesonlib, optinterpreter
+from .. import compilers, environment, mesonlib, optinterpreter, options
from .. import coredata as cdata
from ..build import Executable, Jar, SharedLibrary, SharedModule, StaticLibrary
from ..compilers import detect_compiler_for
@@ -150,8 +150,8 @@ class IntrospectionInterpreter(AstInterpreter):
def func_add_languages(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> None:
kwargs = self.flatten_kwargs(kwargs)
required = kwargs.get('required', True)
- assert isinstance(required, (bool, cdata.UserFeatureOption)), 'for mypy'
- if isinstance(required, cdata.UserFeatureOption):
+ assert isinstance(required, (bool, options.UserFeatureOption)), 'for mypy'
+ if isinstance(required, options.UserFeatureOption):
required = required.is_enabled()
if 'native' in kwargs:
native = kwargs.get('native', False)
diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py
index e1b0928..57f2bed 100644
--- a/mesonbuild/cargo/interpreter.py
+++ b/mesonbuild/cargo/interpreter.py
@@ -23,7 +23,7 @@ import typing as T
from . import builder
from . import version
from ..mesonlib import MesonException, Popen_safe, OptionKey
-from .. import coredata
+from .. import coredata, options
if T.TYPE_CHECKING:
from types import ModuleType
@@ -712,11 +712,11 @@ def interpret(subp_name: str, subdir: str, env: Environment) -> T.Tuple[mparser.
build = builder.Builder(filename)
# Generate project options
- options: T.Dict[OptionKey, coredata.UserOption] = {}
+ project_options: T.Dict[OptionKey, options.UserOption] = {}
for feature in cargo.features:
key = OptionKey(_option_name(feature), subproject=subp_name)
enabled = feature == 'default'
- options[key] = coredata.UserBooleanOption(key.name, f'Cargo {feature} feature', enabled)
+ project_options[key] = options.UserBooleanOption(key.name, f'Cargo {feature} feature', enabled)
ast = _create_project(cargo, build)
ast += [build.assign(build.function('import', [build.string('rust')]), 'rust')]
@@ -730,4 +730,4 @@ def interpret(subp_name: str, subdir: str, env: Environment) -> T.Tuple[mparser.
for crate_type in cargo.lib.crate_type:
ast.extend(_create_lib(cargo, build, crate_type))
- return build.block(ast), options
+ return build.block(ast), project_options
diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py
index 18b25d4..d0326d7 100644
--- a/mesonbuild/compilers/c.py
+++ b/mesonbuild/compilers/c.py
@@ -6,7 +6,7 @@ from __future__ import annotations
import os.path
import typing as T
-from .. import coredata
+from .. import options
from .. import mlog
from ..mesonlib import MesonException, version_compare, OptionKey
from .c_function_attributes import C_FUNC_ATTRIBUTES
@@ -96,7 +96,7 @@ class CCompiler(CLikeCompiler, Compiler):
opts = super().get_options()
key = OptionKey('std', machine=self.for_machine, lang=self.language)
opts.update({
- key: coredata.UserStdOption('C', _ALL_STDS),
+ key: options.UserStdOption('C', _ALL_STDS),
})
return opts
@@ -128,7 +128,7 @@ class _ClangCStds(CompilerMixinBase):
if version_compare(self.version, self._C23_VERSION):
stds += ['c23']
std_opt = opts[OptionKey('std', machine=self.for_machine, lang=self.language)]
- assert isinstance(std_opt, coredata.UserStdOption), 'for mypy'
+ assert isinstance(std_opt, options.UserStdOption), 'for mypy'
std_opt.set_versions(stds, gnu=True)
return opts
@@ -154,7 +154,7 @@ class ClangCCompiler(_ClangCStds, ClangCompiler, CCompiler):
if self.info.is_windows() or self.info.is_cygwin():
self.update_options(
opts,
- self.create_option(coredata.UserArrayOption,
+ self.create_option(options.UserArrayOption,
OptionKey('winlibs', machine=self.for_machine, lang=self.language),
'Standard Win libraries to link against',
gnu_winlibs),
@@ -247,7 +247,7 @@ class ArmclangCCompiler(ArmclangCompiler, CCompiler):
def get_options(self) -> 'MutableKeyedOptionDictType':
opts = CCompiler.get_options(self)
std_opt = opts[OptionKey('std', machine=self.for_machine, lang=self.language)]
- assert isinstance(std_opt, coredata.UserStdOption), 'for mypy'
+ assert isinstance(std_opt, options.UserStdOption), 'for mypy'
std_opt.set_versions(['c90', 'c99', 'c11'], gnu=True)
return opts
@@ -298,12 +298,12 @@ class GnuCCompiler(GnuCompiler, CCompiler):
stds += ['c23']
key = OptionKey('std', machine=self.for_machine, lang=self.language)
std_opt = opts[key]
- assert isinstance(std_opt, coredata.UserStdOption), 'for mypy'
+ assert isinstance(std_opt, options.UserStdOption), 'for mypy'
std_opt.set_versions(stds, gnu=True)
if self.info.is_windows() or self.info.is_cygwin():
self.update_options(
opts,
- self.create_option(coredata.UserArrayOption,
+ self.create_option(options.UserArrayOption,
key.evolve('winlibs'),
'Standard Win libraries to link against',
gnu_winlibs),
@@ -377,7 +377,7 @@ class ElbrusCCompiler(ElbrusCompiler, CCompiler):
if version_compare(self.version, '>=1.26.00'):
stds += ['c17', 'c18', 'iso9899:2017', 'iso9899:2018', 'gnu17', 'gnu18']
std_opt = opts[OptionKey('std', machine=self.for_machine, lang=self.language)]
- assert isinstance(std_opt, coredata.UserStdOption), 'for mypy'
+ assert isinstance(std_opt, options.UserStdOption), 'for mypy'
std_opt.set_versions(stds)
return opts
@@ -416,7 +416,7 @@ class IntelCCompiler(IntelGnuLikeCompiler, CCompiler):
if version_compare(self.version, '>=16.0.0'):
stds += ['c11']
std_opt = opts[OptionKey('std', machine=self.for_machine, lang=self.language)]
- assert isinstance(std_opt, coredata.UserStdOption), 'for mypy'
+ assert isinstance(std_opt, options.UserStdOption), 'for mypy'
std_opt.set_versions(stds, gnu=True)
return opts
@@ -441,7 +441,7 @@ class VisualStudioLikeCCompilerMixin(CompilerMixinBase):
return self.update_options(
super().get_options(),
self.create_option(
- coredata.UserArrayOption,
+ options.UserArrayOption,
OptionKey('winlibs', machine=self.for_machine, lang=self.language),
'Windows libs to link against.',
msvc_winlibs,
@@ -480,7 +480,7 @@ class VisualStudioCCompiler(MSVCCompiler, VisualStudioLikeCCompilerMixin, CCompi
if version_compare(self.version, self._C17_VERSION):
stds += ['c17', 'c18']
std_opt = opts[OptionKey('std', machine=self.for_machine, lang=self.language)]
- assert isinstance(std_opt, coredata.UserStdOption), 'for mypy'
+ assert isinstance(std_opt, options.UserStdOption), 'for mypy'
std_opt.set_versions(stds, gnu=True, gnu_deprecated=True)
return opts
@@ -529,7 +529,7 @@ class IntelClCCompiler(IntelVisualStudioLikeCompiler, VisualStudioLikeCCompilerM
def get_options(self) -> 'MutableKeyedOptionDictType':
opts = super().get_options()
std_opt = opts[OptionKey('std', machine=self.for_machine, lang=self.language)]
- assert isinstance(std_opt, coredata.UserStdOption), 'for mypy'
+ assert isinstance(std_opt, options.UserStdOption), 'for mypy'
std_opt.set_versions(['c89', 'c99', 'c11'])
return opts
@@ -562,7 +562,7 @@ class ArmCCompiler(ArmCompiler, CCompiler):
def get_options(self) -> 'MutableKeyedOptionDictType':
opts = CCompiler.get_options(self)
std_opt = opts[OptionKey('std', machine=self.for_machine, lang=self.language)]
- assert isinstance(std_opt, coredata.UserStdOption), 'for mypy'
+ assert isinstance(std_opt, options.UserStdOption), 'for mypy'
std_opt.set_versions(['c89', 'c99', 'c11'])
return opts
@@ -591,7 +591,7 @@ class CcrxCCompiler(CcrxCompiler, CCompiler):
def get_options(self) -> 'MutableKeyedOptionDictType':
opts = CCompiler.get_options(self)
std_opt = opts[OptionKey('std', machine=self.for_machine, lang=self.language)]
- assert isinstance(std_opt, coredata.UserStdOption), 'for mypy'
+ assert isinstance(std_opt, options.UserStdOption), 'for mypy'
std_opt.set_versions(['c89', 'c99'])
return opts
@@ -638,7 +638,7 @@ class Xc16CCompiler(Xc16Compiler, CCompiler):
def get_options(self) -> 'MutableKeyedOptionDictType':
opts = CCompiler.get_options(self)
std_opt = opts[OptionKey('std', machine=self.for_machine, lang=self.language)]
- assert isinstance(std_opt, coredata.UserStdOption), 'for mypy'
+ assert isinstance(std_opt, options.UserStdOption), 'for mypy'
std_opt.set_versions(['c89', 'c99'], gnu=True)
return opts
@@ -683,7 +683,7 @@ class CompCertCCompiler(CompCertCompiler, CCompiler):
def get_options(self) -> 'MutableKeyedOptionDictType':
opts = CCompiler.get_options(self)
std_opt = opts[OptionKey('std', machine=self.for_machine, lang=self.language)]
- assert isinstance(std_opt, coredata.UserStdOption), 'for mypy'
+ assert isinstance(std_opt, options.UserStdOption), 'for mypy'
std_opt.set_versions(['c89', 'c99'])
return opts
@@ -720,7 +720,7 @@ class TICCompiler(TICompiler, CCompiler):
def get_options(self) -> 'MutableKeyedOptionDictType':
opts = CCompiler.get_options(self)
std_opt = opts[OptionKey('std', machine=self.for_machine, lang=self.language)]
- assert isinstance(std_opt, coredata.UserStdOption), 'for mypy'
+ assert isinstance(std_opt, options.UserStdOption), 'for mypy'
std_opt.set_versions(['c89', 'c99', 'c11'])
return opts
diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py
index 673b63f..4267384 100644
--- a/mesonbuild/compilers/compilers.py
+++ b/mesonbuild/compilers/compilers.py
@@ -15,6 +15,7 @@ from functools import lru_cache
from .. import coredata
from .. import mlog
from .. import mesonlib
+from .. import options
from ..mesonlib import (
HoldableObject,
EnvironmentException, MesonException,
@@ -35,7 +36,7 @@ if T.TYPE_CHECKING:
CompilerType = T.TypeVar('CompilerType', bound='Compiler')
_T = T.TypeVar('_T')
- UserOptionType = T.TypeVar('UserOptionType', bound=coredata.UserOption)
+ UserOptionType = T.TypeVar('UserOptionType', bound=options.UserOption)
"""This file contains the data files of all compilers Meson knows
about. To support a new compiler, add its information below.
@@ -209,40 +210,40 @@ clike_debug_args: T.Dict[bool, T.List[str]] = {
MSCRT_VALS = ['none', 'md', 'mdd', 'mt', 'mtd']
@dataclass
-class BaseOption(T.Generic[coredata._T, coredata._U]):
- opt_type: T.Type[coredata._U]
+class BaseOption(T.Generic[options._T, options._U]):
+ opt_type: T.Type[options._U]
description: str
default: T.Any = None
choices: T.Any = None
- def init_option(self, name: OptionKey) -> coredata._U:
+ def init_option(self, name: OptionKey) -> options._U:
keywords = {'value': self.default}
if self.choices:
keywords['choices'] = self.choices
return self.opt_type(name.name, self.description, **keywords)
BASE_OPTIONS: T.Mapping[OptionKey, BaseOption] = {
- OptionKey('b_pch'): BaseOption(coredata.UserBooleanOption, 'Use precompiled headers', True),
- OptionKey('b_lto'): BaseOption(coredata.UserBooleanOption, 'Use link time optimization', False),
- OptionKey('b_lto_threads'): BaseOption(coredata.UserIntegerOption, 'Use multiple threads for Link Time Optimization', (None, None, 0)),
- OptionKey('b_lto_mode'): BaseOption(coredata.UserComboOption, 'Select between different LTO modes.', 'default',
+ OptionKey('b_pch'): BaseOption(options.UserBooleanOption, 'Use precompiled headers', True),
+ OptionKey('b_lto'): BaseOption(options.UserBooleanOption, 'Use link time optimization', False),
+ OptionKey('b_lto_threads'): BaseOption(options.UserIntegerOption, 'Use multiple threads for Link Time Optimization', (None, None, 0)),
+ OptionKey('b_lto_mode'): BaseOption(options.UserComboOption, 'Select between different LTO modes.', 'default',
choices=['default', 'thin']),
- OptionKey('b_thinlto_cache'): BaseOption(coredata.UserBooleanOption, 'Use LLVM ThinLTO caching for faster incremental builds', False),
- OptionKey('b_thinlto_cache_dir'): BaseOption(coredata.UserStringOption, 'Directory to store ThinLTO cache objects', ''),
- OptionKey('b_sanitize'): BaseOption(coredata.UserComboOption, 'Code sanitizer to use', 'none',
+ OptionKey('b_thinlto_cache'): BaseOption(options.UserBooleanOption, 'Use LLVM ThinLTO caching for faster incremental builds', False),
+ OptionKey('b_thinlto_cache_dir'): BaseOption(options.UserStringOption, 'Directory to store ThinLTO cache objects', ''),
+ OptionKey('b_sanitize'): BaseOption(options.UserComboOption, 'Code sanitizer to use', 'none',
choices=['none', 'address', 'thread', 'undefined', 'memory', 'leak', 'address,undefined']),
- OptionKey('b_lundef'): BaseOption(coredata.UserBooleanOption, 'Use -Wl,--no-undefined when linking', True),
- OptionKey('b_asneeded'): BaseOption(coredata.UserBooleanOption, 'Use -Wl,--as-needed when linking', True),
- OptionKey('b_pgo'): BaseOption(coredata.UserComboOption, 'Use profile guided optimization', 'off',
+ OptionKey('b_lundef'): BaseOption(options.UserBooleanOption, 'Use -Wl,--no-undefined when linking', True),
+ OptionKey('b_asneeded'): BaseOption(options.UserBooleanOption, 'Use -Wl,--as-needed when linking', True),
+ OptionKey('b_pgo'): BaseOption(options.UserComboOption, 'Use profile guided optimization', 'off',
choices=['off', 'generate', 'use']),
- OptionKey('b_coverage'): BaseOption(coredata.UserBooleanOption, 'Enable coverage tracking.', False),
- OptionKey('b_colorout'): BaseOption(coredata.UserComboOption, 'Use colored output', 'always',
+ OptionKey('b_coverage'): BaseOption(options.UserBooleanOption, 'Enable coverage tracking.', False),
+ OptionKey('b_colorout'): BaseOption(options.UserComboOption, 'Use colored output', 'always',
choices=['auto', 'always', 'never']),
- OptionKey('b_ndebug'): BaseOption(coredata.UserComboOption, 'Disable asserts', 'false', choices=['true', 'false', 'if-release']),
- OptionKey('b_staticpic'): BaseOption(coredata.UserBooleanOption, 'Build static libraries as position independent', True),
- OptionKey('b_pie'): BaseOption(coredata.UserBooleanOption, 'Build executables as position independent', False),
- OptionKey('b_bitcode'): BaseOption(coredata.UserBooleanOption, 'Generate and embed bitcode (only macOS/iOS/tvOS)', False),
- OptionKey('b_vscrt'): BaseOption(coredata.UserComboOption, 'VS run-time library type to use.', 'from_buildtype',
+ OptionKey('b_ndebug'): BaseOption(options.UserComboOption, 'Disable asserts', 'false', choices=['true', 'false', 'if-release']),
+ OptionKey('b_staticpic'): BaseOption(options.UserBooleanOption, 'Build static libraries as position independent', True),
+ OptionKey('b_pie'): BaseOption(options.UserBooleanOption, 'Build executables as position independent', False),
+ OptionKey('b_bitcode'): BaseOption(options.UserBooleanOption, 'Generate and embed bitcode (only macOS/iOS/tvOS)', False),
+ OptionKey('b_vscrt'): BaseOption(options.UserComboOption, 'VS run-time library type to use.', 'from_buildtype',
choices=MSCRT_VALS + ['from_buildtype', 'static_from_buildtype']),
}
@@ -1365,12 +1366,12 @@ def get_global_options(lang: str,
comp_options = env.options.get(comp_key, [])
link_options = env.options.get(largkey, [])
- cargs = coredata.UserArrayOption(
+ cargs = options.UserArrayOption(
f'{lang}_{argkey.name}',
description + ' compiler',
comp_options, split_args=True, allow_dups=True)
- largs = coredata.UserArrayOption(
+ largs = options.UserArrayOption(
f'{lang}_{largkey.name}',
description + ' linker',
link_options, split_args=True, allow_dups=True)
diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py
index 525c9fc..ea51823 100644
--- a/mesonbuild/compilers/cpp.py
+++ b/mesonbuild/compilers/cpp.py
@@ -8,7 +8,7 @@ import functools
import os.path
import typing as T
-from .. import coredata
+from .. import options
from .. import mlog
from ..mesonlib import MesonException, version_compare, OptionKey
@@ -174,7 +174,7 @@ class CPPCompiler(CLikeCompiler, Compiler):
opts = super().get_options()
key = OptionKey('std', machine=self.for_machine, lang=self.language)
opts.update({
- key: coredata.UserStdOption('C++', _ALL_STDS),
+ key: options.UserStdOption('C++', _ALL_STDS),
})
return opts
@@ -242,16 +242,16 @@ class ClangCPPCompiler(_StdCPPLibMixin, ClangCompiler, CPPCompiler):
key = OptionKey('key', machine=self.for_machine, lang=self.language)
self.update_options(
opts,
- self.create_option(coredata.UserComboOption,
+ self.create_option(options.UserComboOption,
key.evolve('eh'),
'C++ exception handling type.',
['none', 'default', 'a', 's', 'sc'],
'default'),
- self.create_option(coredata.UserBooleanOption,
+ self.create_option(options.UserBooleanOption,
key.evolve('rtti'),
'Enable RTTI',
True),
- self.create_option(coredata.UserBooleanOption,
+ self.create_option(options.UserBooleanOption,
key.evolve('debugstl'),
'STL debug mode',
False),
@@ -264,12 +264,12 @@ class ClangCPPCompiler(_StdCPPLibMixin, ClangCompiler, CPPCompiler):
if version_compare(self.version, self._CPP26_VERSION):
cppstd_choices.append('c++26')
std_opt = opts[key.evolve('std')]
- assert isinstance(std_opt, coredata.UserStdOption), 'for mypy'
+ assert isinstance(std_opt, options.UserStdOption), 'for mypy'
std_opt.set_versions(cppstd_choices, gnu=True)
if self.info.is_windows() or self.info.is_cygwin():
self.update_options(
opts,
- self.create_option(coredata.UserArrayOption,
+ self.create_option(options.UserArrayOption,
key.evolve('winlibs'),
'Standard Win libraries to link against',
gnu_winlibs),
@@ -393,14 +393,14 @@ class ArmclangCPPCompiler(ArmclangCompiler, CPPCompiler):
key = OptionKey('std', machine=self.for_machine, lang=self.language)
self.update_options(
opts,
- self.create_option(coredata.UserComboOption,
+ self.create_option(options.UserComboOption,
key.evolve('eh'),
'C++ exception handling type.',
['none', 'default', 'a', 's', 'sc'],
'default'),
)
std_opt = opts[key]
- assert isinstance(std_opt, coredata.UserStdOption), 'for mypy'
+ assert isinstance(std_opt, options.UserStdOption), 'for mypy'
std_opt.set_versions(['c++98', 'c++03', 'c++11', 'c++14', 'c++17'], gnu=True)
return opts
@@ -442,16 +442,16 @@ class GnuCPPCompiler(_StdCPPLibMixin, GnuCompiler, CPPCompiler):
opts = CPPCompiler.get_options(self)
self.update_options(
opts,
- self.create_option(coredata.UserComboOption,
+ self.create_option(options.UserComboOption,
key.evolve('eh'),
'C++ exception handling type.',
['none', 'default', 'a', 's', 'sc'],
'default'),
- self.create_option(coredata.UserBooleanOption,
+ self.create_option(options.UserBooleanOption,
key.evolve('rtti'),
'Enable RTTI',
True),
- self.create_option(coredata.UserBooleanOption,
+ self.create_option(options.UserBooleanOption,
key.evolve('debugstl'),
'STL debug mode',
False),
@@ -465,12 +465,12 @@ class GnuCPPCompiler(_StdCPPLibMixin, GnuCompiler, CPPCompiler):
if version_compare(self.version, '>=14.0.0'):
cppstd_choices.append('c++26')
std_opt = opts[key]
- assert isinstance(std_opt, coredata.UserStdOption), 'for mypy'
+ assert isinstance(std_opt, options.UserStdOption), 'for mypy'
std_opt.set_versions(cppstd_choices, gnu=True)
if self.info.is_windows() or self.info.is_cygwin():
self.update_options(
opts,
- self.create_option(coredata.UserArrayOption,
+ self.create_option(options.UserArrayOption,
key.evolve('winlibs'),
'Standard Win libraries to link against',
gnu_winlibs),
@@ -582,18 +582,18 @@ class ElbrusCPPCompiler(ElbrusCompiler, CPPCompiler):
key = OptionKey('std', machine=self.for_machine, lang=self.language)
self.update_options(
opts,
- self.create_option(coredata.UserComboOption,
+ self.create_option(options.UserComboOption,
key.evolve('eh'),
'C++ exception handling type.',
['none', 'default', 'a', 's', 'sc'],
'default'),
- self.create_option(coredata.UserBooleanOption,
+ self.create_option(options.UserBooleanOption,
key.evolve('debugstl'),
'STL debug mode',
False),
)
std_opt = opts[key]
- assert isinstance(std_opt, coredata.UserStdOption), 'for mypy'
+ assert isinstance(std_opt, options.UserStdOption), 'for mypy'
std_opt.set_versions(cpp_stds, gnu=True)
return opts
@@ -661,22 +661,22 @@ class IntelCPPCompiler(IntelGnuLikeCompiler, CPPCompiler):
key = OptionKey('std', machine=self.for_machine, lang=self.language)
self.update_options(
opts,
- self.create_option(coredata.UserComboOption,
+ self.create_option(options.UserComboOption,
key.evolve('eh'),
'C++ exception handling type.',
['none', 'default', 'a', 's', 'sc'],
'default'),
- self.create_option(coredata.UserBooleanOption,
+ self.create_option(options.UserBooleanOption,
key.evolve('rtti'),
'Enable RTTI',
True),
- self.create_option(coredata.UserBooleanOption,
+ self.create_option(options.UserBooleanOption,
key.evolve('debugstl'),
'STL debug mode',
False),
)
std_opt = opts[key]
- assert isinstance(std_opt, coredata.UserStdOption), 'for mypy'
+ assert isinstance(std_opt, options.UserStdOption), 'for mypy'
std_opt.set_versions(c_stds + g_stds)
return opts
@@ -734,22 +734,22 @@ class VisualStudioLikeCPPCompilerMixin(CompilerMixinBase):
key = OptionKey('std', machine=self.for_machine, lang=self.language)
self.update_options(
opts,
- self.create_option(coredata.UserComboOption,
+ self.create_option(options.UserComboOption,
key.evolve('eh'),
'C++ exception handling type.',
['none', 'default', 'a', 's', 'sc'],
'default'),
- self.create_option(coredata.UserBooleanOption,
+ self.create_option(options.UserBooleanOption,
key.evolve('rtti'),
'Enable RTTI',
True),
- self.create_option(coredata.UserArrayOption,
+ self.create_option(options.UserArrayOption,
key.evolve('winlibs'),
'Windows libs to link against.',
msvc_winlibs),
)
std_opt = opts[key]
- assert isinstance(std_opt, coredata.UserStdOption), 'for mypy'
+ assert isinstance(std_opt, options.UserStdOption), 'for mypy'
std_opt.set_versions(cpp_stds)
return opts
@@ -912,7 +912,7 @@ class ArmCPPCompiler(ArmCompiler, CPPCompiler):
def get_options(self) -> 'MutableKeyedOptionDictType':
opts = CPPCompiler.get_options(self)
std_opt = opts[OptionKey('std', machine=self.for_machine, lang=self.language)]
- assert isinstance(std_opt, coredata.UserStdOption), 'for mypy'
+ assert isinstance(std_opt, options.UserStdOption), 'for mypy'
std_opt.set_versions(['c++03', 'c++11'])
return opts
@@ -973,7 +973,7 @@ class TICPPCompiler(TICompiler, CPPCompiler):
def get_options(self) -> 'MutableKeyedOptionDictType':
opts = CPPCompiler.get_options(self)
std_opt = opts[OptionKey('std', machine=self.for_machine, lang=self.language)]
- assert isinstance(std_opt, coredata.UserStdOption), 'for mypy'
+ assert isinstance(std_opt, options.UserStdOption), 'for mypy'
std_opt.set_versions(['c++03'])
return opts
diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py
index 7231a26..8680cf0 100644
--- a/mesonbuild/compilers/cuda.py
+++ b/mesonbuild/compilers/cuda.py
@@ -9,6 +9,7 @@ import string
import typing as T
from .. import coredata
+from .. import options
from .. import mlog
from ..mesonlib import (
EnvironmentException, Popen_safe,
@@ -643,12 +644,12 @@ class CudaCompiler(Compiler):
return self.update_options(
super().get_options(),
- self.create_option(coredata.UserComboOption,
+ self.create_option(options.UserComboOption,
OptionKey('std', machine=self.for_machine, lang=self.language),
'C++ language standard to use with CUDA',
cpp_stds,
'none'),
- self.create_option(coredata.UserStringOption,
+ self.create_option(options.UserStringOption,
OptionKey('ccbindir', machine=self.for_machine, lang=self.language),
'CUDA non-default toolchain directory to use (-ccbin)',
''),
diff --git a/mesonbuild/compilers/cython.py b/mesonbuild/compilers/cython.py
index 30cec81..61e339b 100644
--- a/mesonbuild/compilers/cython.py
+++ b/mesonbuild/compilers/cython.py
@@ -6,7 +6,7 @@ from __future__ import annotations
import typing as T
-from .. import coredata
+from .. import options
from ..mesonlib import EnvironmentException, OptionKey, version_compare
from .compilers import Compiler
@@ -69,12 +69,12 @@ class CythonCompiler(Compiler):
def get_options(self) -> 'MutableKeyedOptionDictType':
return self.update_options(
super().get_options(),
- self.create_option(coredata.UserComboOption,
+ self.create_option(options.UserComboOption,
OptionKey('version', machine=self.for_machine, lang=self.language),
'Python version to target',
['2', '3'],
'3'),
- self.create_option(coredata.UserComboOption,
+ self.create_option(options.UserComboOption,
OptionKey('language', machine=self.for_machine, lang=self.language),
'Output C or C++ files',
['c', 'cpp'],
diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py
index 4282515..7b2f5d9 100644
--- a/mesonbuild/compilers/fortran.py
+++ b/mesonbuild/compilers/fortran.py
@@ -6,7 +6,7 @@ from __future__ import annotations
import typing as T
import os
-from .. import coredata
+from .. import options
from .compilers import (
clike_debug_args,
Compiler,
@@ -114,7 +114,7 @@ class FortranCompiler(CLikeCompiler, Compiler):
def get_options(self) -> 'MutableKeyedOptionDictType':
return self.update_options(
super().get_options(),
- self.create_option(coredata.UserComboOption,
+ self.create_option(options.UserComboOption,
OptionKey('std', machine=self.for_machine, lang=self.language),
'Fortran language standard to use',
['none'],
diff --git a/mesonbuild/compilers/mixins/emscripten.py b/mesonbuild/compilers/mixins/emscripten.py
index bb8a520..110dbc6 100644
--- a/mesonbuild/compilers/mixins/emscripten.py
+++ b/mesonbuild/compilers/mixins/emscripten.py
@@ -9,6 +9,7 @@ import os.path
import typing as T
from ... import coredata
+from ... import options
from ... import mesonlib
from ...mesonlib import OptionKey
from ...mesonlib import LibType
@@ -59,7 +60,7 @@ class EmscriptenMixin(Compiler):
return self.update_options(
super().get_options(),
self.create_option(
- coredata.UserIntegerOption,
+ options.UserIntegerOption,
OptionKey('thread_count', machine=self.for_machine, lang=self.language),
'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/objc.py b/mesonbuild/compilers/objc.py
index 7c19c1b..4d33ec8 100644
--- a/mesonbuild/compilers/objc.py
+++ b/mesonbuild/compilers/objc.py
@@ -6,6 +6,7 @@ from __future__ import annotations
import typing as T
from .. import coredata
+from .. import options
from ..mesonlib import OptionKey
from .compilers import Compiler
@@ -80,7 +81,7 @@ class ClangObjCCompiler(ClangCompiler, ObjCCompiler):
def get_options(self) -> 'coredata.MutableKeyedOptionDictType':
return self.update_options(
super().get_options(),
- self.create_option(coredata.UserComboOption,
+ self.create_option(options.UserComboOption,
OptionKey('std', machine=self.for_machine, lang='c'),
'C language standard to use',
['none', 'c89', 'c99', 'c11', 'c17', 'gnu89', 'gnu99', 'gnu11', 'gnu17'],
diff --git a/mesonbuild/compilers/objcpp.py b/mesonbuild/compilers/objcpp.py
index 46eaa50..e28e3ed 100644
--- a/mesonbuild/compilers/objcpp.py
+++ b/mesonbuild/compilers/objcpp.py
@@ -6,6 +6,7 @@ from __future__ import annotations
import typing as T
from .. import coredata
+from .. import options
from ..mesonlib import OptionKey
from .mixins.clike import CLikeCompiler
@@ -80,7 +81,7 @@ class ClangObjCPPCompiler(ClangCompiler, ObjCPPCompiler):
def get_options(self) -> coredata.MutableKeyedOptionDictType:
return self.update_options(
super().get_options(),
- self.create_option(coredata.UserComboOption,
+ self.create_option(options.UserComboOption,
OptionKey('std', machine=self.for_machine, lang='cpp'),
'C++ language standard to use',
['none', 'c++98', 'c++11', 'c++14', 'c++17', 'c++20', 'c++2b',
diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py
index ce10791..0f52dbb 100644
--- a/mesonbuild/compilers/rust.py
+++ b/mesonbuild/compilers/rust.py
@@ -9,7 +9,7 @@ import textwrap
import re
import typing as T
-from .. import coredata
+from .. import options
from ..mesonlib import EnvironmentException, MesonException, Popen_safe_logged, OptionKey
from .compilers import Compiler, clike_debug_args
@@ -158,7 +158,7 @@ class RustCompiler(Compiler):
# use_linker_args method instead.
def get_options(self) -> MutableKeyedOptionDictType:
- return dict((self.create_option(coredata.UserComboOption,
+ return dict((self.create_option(options.UserComboOption,
OptionKey('std', machine=self.for_machine, lang=self.language),
'Rust edition to use',
['none', '2015', '2018', '2021'],
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index 6e67587..76da0b6 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -6,7 +6,7 @@ from __future__ import annotations
import copy
-from . import mlog, mparser
+from . import mlog, mparser, options
import pickle, os, uuid
import sys
from itertools import chain
@@ -15,12 +15,10 @@ from collections import OrderedDict, abc
from dataclasses import dataclass
from .mesonlib import (
- HoldableObject, MesonBugException,
+ MesonBugException,
MesonException, EnvironmentException, MachineChoice, PerMachine,
- PerMachineDefaultable, default_libdir, default_libexecdir,
- default_prefix, default_datadir, default_includedir, default_infodir,
- default_localedir, default_mandir, default_sbindir, default_sysconfdir,
- listify_array_value, OptionKey, OptionType, stringlistify,
+ PerMachineDefaultable,
+ OptionKey, OptionType, stringlistify,
pickle_load
)
import ast
@@ -56,8 +54,8 @@ if T.TYPE_CHECKING:
cross_file: T.List[str]
native_file: T.List[str]
- OptionDictType = T.Union[T.Dict[str, 'UserOption[T.Any]'], 'OptionsView']
- MutableKeyedOptionDictType = T.Dict['OptionKey', 'UserOption[T.Any]']
+ OptionDictType = T.Union[T.Dict[str, 'options.UserOption[T.Any]'], 'OptionsView']
+ MutableKeyedOptionDictType = T.Dict['OptionKey', 'options.UserOption[T.Any]']
KeyedOptionDictType = T.Union[MutableKeyedOptionDictType, 'OptionsView']
CompilerCheckCacheKey = T.Tuple[T.Tuple[str, ...], str, FileOrString, T.Tuple[str, ...], CompileCheckMode]
# code, args
@@ -82,20 +80,11 @@ if stable_version.endswith('.99'):
stable_version_array[-2] = str(int(stable_version_array[-2]) + 1)
stable_version = '.'.join(stable_version_array)
-backendlist = ['ninja', 'vs', 'vs2010', 'vs2012', 'vs2013', 'vs2015', 'vs2017', 'vs2019', 'vs2022', 'xcode', 'none']
-genvslitelist = ['vs2022']
-buildtypelist = ['plain', 'debug', 'debugoptimized', 'release', 'minsize', 'custom']
-
-DEFAULT_YIELDING = False
-
-# Can't bind this near the class method it seems, sadly.
-_T = T.TypeVar('_T')
-
def get_genvs_default_buildtype_list() -> list[str]:
# just debug, debugoptimized, and release for now
# but this should probably be configurable through some extra option, alongside --genvslite.
- return buildtypelist[1:-2]
+ return options.buildtypelist[1:-2]
class MesonVersionMismatchException(MesonException):
@@ -108,312 +97,6 @@ class MesonVersionMismatchException(MesonException):
self.current_version = current_version
-class UserOption(T.Generic[_T], HoldableObject):
- def __init__(self, name: str, description: str, choices: T.Optional[T.Union[str, T.List[_T]]],
- yielding: bool,
- deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False):
- super().__init__()
- self.name = name
- self.choices = choices
- self.description = description
- if not isinstance(yielding, bool):
- raise MesonException('Value of "yielding" must be a boolean.')
- self.yielding = yielding
- self.deprecated = deprecated
- self.readonly = False
-
- def listify(self, value: T.Any) -> T.List[T.Any]:
- return [value]
-
- def printable_value(self) -> T.Union[str, int, bool, T.List[T.Union[str, int, bool]]]:
- assert isinstance(self.value, (str, int, bool, list))
- return self.value
-
- # Check that the input is a valid value and return the
- # "cleaned" or "native" version. For example the Boolean
- # option could take the string "true" and return True.
- def validate_value(self, value: T.Any) -> _T:
- raise RuntimeError('Derived option class did not override validate_value.')
-
- def set_value(self, newvalue: T.Any) -> bool:
- oldvalue = getattr(self, 'value', None)
- self.value = self.validate_value(newvalue)
- return self.value != oldvalue
-
-class UserStringOption(UserOption[str]):
- def __init__(self, name: str, description: str, value: T.Any, yielding: bool = DEFAULT_YIELDING,
- deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False):
- super().__init__(name, description, None, yielding, deprecated)
- self.set_value(value)
-
- def validate_value(self, value: T.Any) -> str:
- if not isinstance(value, str):
- raise MesonException(f'The value of option "{self.name}" is "{value}", which is not a string.')
- return value
-
-class UserBooleanOption(UserOption[bool]):
- def __init__(self, name: str, description: str, value: bool, yielding: bool = DEFAULT_YIELDING,
- deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False):
- super().__init__(name, description, [True, False], yielding, deprecated)
- self.set_value(value)
-
- def __bool__(self) -> bool:
- return self.value
-
- def validate_value(self, value: T.Any) -> bool:
- if isinstance(value, bool):
- return value
- if not isinstance(value, str):
- raise MesonException(f'Option "{self.name}" value {value} cannot be converted to a boolean')
- if value.lower() == 'true':
- return True
- if value.lower() == 'false':
- return False
- raise MesonException(f'Option "{self.name}" value {value} is not boolean (true or false).')
-
-class UserIntegerOption(UserOption[int]):
- def __init__(self, name: str, description: str, value: T.Any, yielding: bool = DEFAULT_YIELDING,
- deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False):
- min_value, max_value, default_value = value
- self.min_value = min_value
- self.max_value = max_value
- c: T.List[str] = []
- if min_value is not None:
- c.append('>=' + str(min_value))
- if max_value is not None:
- c.append('<=' + str(max_value))
- choices = ', '.join(c)
- super().__init__(name, description, choices, yielding, deprecated)
- self.set_value(default_value)
-
- def validate_value(self, value: T.Any) -> int:
- if isinstance(value, str):
- value = self.toint(value)
- if not isinstance(value, int):
- raise MesonException(f'Value {value!r} for option "{self.name}" is not an integer.')
- if self.min_value is not None and value < self.min_value:
- raise MesonException(f'Value {value} for option "{self.name}" is less than minimum value {self.min_value}.')
- if self.max_value is not None and value > self.max_value:
- raise MesonException(f'Value {value} for option "{self.name}" is more than maximum value {self.max_value}.')
- return value
-
- def toint(self, valuestring: str) -> int:
- try:
- return int(valuestring)
- except ValueError:
- raise MesonException(f'Value string "{valuestring}" for option "{self.name}" is not convertible to an integer.')
-
-class OctalInt(int):
- # NinjaBackend.get_user_option_args uses str() to converts it to a command line option
- # UserUmaskOption.toint() uses int(str, 8) to convert it to an integer
- # So we need to use oct instead of dec here if we do not want values to be misinterpreted.
- def __str__(self) -> str:
- return oct(int(self))
-
-class UserUmaskOption(UserIntegerOption, UserOption[T.Union[str, OctalInt]]):
- def __init__(self, name: str, description: str, value: T.Any, yielding: bool = DEFAULT_YIELDING,
- deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False):
- super().__init__(name, description, (0, 0o777, value), yielding, deprecated)
- self.choices = ['preserve', '0000-0777']
-
- def printable_value(self) -> str:
- if self.value == 'preserve':
- return self.value
- return format(self.value, '04o')
-
- def validate_value(self, value: T.Any) -> T.Union[str, OctalInt]:
- if value == 'preserve':
- return 'preserve'
- return OctalInt(super().validate_value(value))
-
- def toint(self, valuestring: T.Union[str, OctalInt]) -> int:
- try:
- return int(valuestring, 8)
- except ValueError as e:
- raise MesonException(f'Invalid mode for option "{self.name}" {e}')
-
-class UserComboOption(UserOption[str]):
- def __init__(self, name: str, description: str, choices: T.List[str], value: T.Any,
- yielding: bool = DEFAULT_YIELDING,
- deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False):
- super().__init__(name, description, choices, yielding, deprecated)
- if not isinstance(self.choices, list):
- raise MesonException(f'Combo choices for option "{self.name}" must be an array.')
- for i in self.choices:
- if not isinstance(i, str):
- raise MesonException(f'Combo choice elements for option "{self.name}" must be strings.')
- self.set_value(value)
-
- def validate_value(self, value: T.Any) -> str:
- if value not in self.choices:
- if isinstance(value, bool):
- _type = 'boolean'
- elif isinstance(value, (int, float)):
- _type = 'number'
- else:
- _type = 'string'
- optionsstring = ', '.join([f'"{item}"' for item in self.choices])
- raise MesonException('Value "{}" (of type "{}") for option "{}" is not one of the choices.'
- ' Possible choices are (as string): {}.'.format(
- value, _type, self.name, optionsstring))
- return value
-
-class UserArrayOption(UserOption[T.List[str]]):
- def __init__(self, name: str, description: str, value: T.Union[str, T.List[str]],
- split_args: bool = False,
- allow_dups: bool = False, yielding: bool = DEFAULT_YIELDING,
- choices: T.Optional[T.List[str]] = None,
- deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False):
- super().__init__(name, description, choices if choices is not None else [], yielding, deprecated)
- self.split_args = split_args
- self.allow_dups = allow_dups
- self.set_value(value)
-
- def listify(self, value: T.Any) -> T.List[T.Any]:
- try:
- return listify_array_value(value, self.split_args)
- except MesonException as e:
- raise MesonException(f'error in option "{self.name}": {e!s}')
-
- def validate_value(self, value: T.Union[str, T.List[str]]) -> T.List[str]:
- newvalue = self.listify(value)
-
- if not self.allow_dups and len(set(newvalue)) != len(newvalue):
- msg = 'Duplicated values in array option is deprecated. ' \
- 'This will become a hard error in the future.'
- mlog.deprecation(msg)
- for i in newvalue:
- if not isinstance(i, str):
- raise MesonException(f'String array element "{newvalue!s}" for option "{self.name}" is not a string.')
- if self.choices:
- bad = [x for x in newvalue if x not in self.choices]
- if bad:
- raise MesonException('Value{} "{}" for option "{}" {} not in allowed choices: "{}"'.format(
- '' if len(bad) == 1 else 's',
- ', '.join(bad),
- self.name,
- 'is' if len(bad) == 1 else 'are',
- ', '.join(self.choices))
- )
- return newvalue
-
- def extend_value(self, value: T.Union[str, T.List[str]]) -> None:
- """Extend the value with an additional value."""
- new = self.validate_value(value)
- self.set_value(self.value + new)
-
-
-class UserFeatureOption(UserComboOption):
- static_choices = ['enabled', 'disabled', 'auto']
-
- def __init__(self, name: str, description: str, value: T.Any, yielding: bool = DEFAULT_YIELDING,
- deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False):
- super().__init__(name, description, self.static_choices, value, yielding, deprecated)
- self.name: T.Optional[str] = None # TODO: Refactor options to all store their name
-
- def is_enabled(self) -> bool:
- return self.value == 'enabled'
-
- def is_disabled(self) -> bool:
- return self.value == 'disabled'
-
- def is_auto(self) -> bool:
- return self.value == 'auto'
-
-class UserStdOption(UserComboOption):
- '''
- UserOption specific to c_std and cpp_std options. User can set a list of
- STDs in preference order and it selects the first one supported by current
- compiler.
-
- For historical reasons, some compilers (msvc) allowed setting a GNU std and
- silently fell back to C std. This is now deprecated. Projects that support
- both GNU and MSVC compilers should set e.g. c_std=gnu11,c11.
-
- This is not using self.deprecated mechanism we already have for project
- options because we want to print a warning if ALL values are deprecated, not
- if SOME values are deprecated.
- '''
- def __init__(self, lang: str, all_stds: T.List[str]) -> None:
- self.lang = lang.lower()
- self.all_stds = ['none'] + all_stds
- # Map a deprecated std to its replacement. e.g. gnu11 -> c11.
- self.deprecated_stds: T.Dict[str, str] = {}
- opt_name = 'cpp_std' if lang == 'c++' else f'{lang}_std'
- super().__init__(opt_name, f'{lang} language standard to use', ['none'], 'none')
-
- def set_versions(self, versions: T.List[str], gnu: bool = False, gnu_deprecated: bool = False) -> None:
- assert all(std in self.all_stds for std in versions)
- self.choices += versions
- if gnu:
- gnu_stds_map = {f'gnu{std[1:]}': std for std in versions}
- if gnu_deprecated:
- self.deprecated_stds.update(gnu_stds_map)
- else:
- self.choices += gnu_stds_map.keys()
-
- def validate_value(self, value: T.Union[str, T.List[str]]) -> str:
- try:
- candidates = listify_array_value(value)
- except MesonException as e:
- raise MesonException(f'error in option "{self.name}": {e!s}')
- unknown = ','.join(std for std in candidates if std not in self.all_stds)
- if unknown:
- raise MesonException(f'Unknown option "{self.name}" value {unknown}. Possible values are {self.all_stds}.')
- # Check first if any of the candidates are not deprecated
- for std in candidates:
- if std in self.choices:
- return std
- # Fallback to a deprecated std if any
- for std in candidates:
- newstd = self.deprecated_stds.get(std)
- if newstd is not None:
- mlog.deprecation(
- f'None of the values {candidates} are supported by the {self.lang} compiler.\n' +
- f'However, the deprecated {std} std currently falls back to {newstd}.\n' +
- 'This will be an error in the future.\n' +
- 'If the project supports both GNU and MSVC compilers, a value such as\n' +
- '"c_std=gnu11,c11" specifies that GNU is preferred but it can safely fallback to plain c11.')
- return newstd
- raise MesonException(f'None of values {candidates} are supported by the {self.lang.upper()} compiler. ' +
- f'Possible values for option "{self.name}" are {self.choices}')
-
-@dataclass
-class OptionsView(abc.Mapping):
- '''A view on an options dictionary for a given subproject and with overrides.
- '''
-
- # TODO: the typing here could be made more explicit using a TypeDict from
- # python 3.8 or typing_extensions
- options: KeyedOptionDictType
- subproject: T.Optional[str] = None
- overrides: T.Optional[T.Mapping[OptionKey, T.Union[str, int, bool, T.List[str]]]] = None
-
- def __getitem__(self, key: OptionKey) -> UserOption:
- # FIXME: This is fundamentally the same algorithm than interpreter.get_option_internal().
- # We should try to share the code somehow.
- key = key.evolve(subproject=self.subproject)
- if not key.is_project():
- opt = self.options.get(key)
- if opt is None or opt.yielding:
- opt = self.options[key.as_root()]
- else:
- opt = self.options[key]
- if opt.yielding:
- opt = self.options.get(key.as_root(), opt)
- if self.overrides:
- override_value = self.overrides.get(key.as_root())
- if override_value is not None:
- opt = copy.copy(opt)
- opt.set_value(override_value)
- return opt
-
- def __iter__(self) -> T.Iterator[OptionKey]:
- return iter(self.options)
-
- def __len__(self) -> int:
- return len(self.options)
-
class DependencyCacheType(enum.Enum):
OTHER = 0
@@ -664,7 +347,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[OptionKey('libdir')].default = 'lib'
+ options.BUILTIN_OPTIONS[OptionKey('libdir')].default = 'lib'
def sanitize_prefix(self, prefix: str) -> str:
prefix = os.path.expanduser(prefix)
@@ -698,7 +381,7 @@ class CoreData:
except TypeError:
return value
if option.name.endswith('dir') and value.is_absolute() and \
- option not in BUILTIN_DIR_NOPREFIX_OPTIONS:
+ option not in options.BUILTIN_DIR_NOPREFIX_OPTIONS:
try:
# Try to relativize the path.
value = value.relative_to(prefix)
@@ -717,15 +400,15 @@ class CoreData:
def init_builtins(self, subproject: str) -> None:
# Create builtin options with default values
- for key, opt in BUILTIN_OPTIONS.items():
+ for key, opt in options.BUILTIN_OPTIONS.items():
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():
+ for key, opt in options.BUILTIN_OPTIONS_PER_MACHINE.items():
self.add_builtin_option(self.options, key.evolve(subproject=subproject, machine=for_machine), opt)
@staticmethod
def add_builtin_option(opts_map: 'MutableKeyedOptionDictType', key: OptionKey,
- opt: 'BuiltinOption') -> None:
+ opt: 'options.BuiltinOption') -> None:
if key.subproject:
if opt.yielding:
# This option is global and not per-subproject
@@ -733,17 +416,17 @@ class CoreData:
value = opts_map[key.as_root()].value
else:
value = None
- opts_map[key] = opt.init_option(key, value, default_prefix())
+ opts_map[key] = opt.init_option(key, value, options.default_prefix())
def init_backend_options(self, backend_name: str) -> None:
if backend_name == 'ninja':
- self.options[OptionKey('backend_max_links')] = UserIntegerOption(
+ self.options[OptionKey('backend_max_links')] = options.UserIntegerOption(
'backend_max_links',
'Maximum number of linker processes to run or 0 for no '
'limit',
(0, None, 0))
elif backend_name.startswith('vs'):
- self.options[OptionKey('backend_startup_project')] = UserStringOption(
+ self.options[OptionKey('backend_startup_project')] = options.UserStringOption(
'backend_startup_project',
'Default project to execute in Visual Studio',
'')
@@ -881,7 +564,7 @@ class CoreData:
@staticmethod
def is_per_machine_option(optname: OptionKey) -> bool:
- if optname.as_host() in BUILTIN_OPTIONS_PER_MACHINE:
+ if optname.as_host() in options.BUILTIN_OPTIONS_PER_MACHINE:
return True
return optname.lang is not None
@@ -930,7 +613,7 @@ class CoreData:
def copy_build_options_from_regular_ones(self) -> bool:
dirty = False
assert not self.is_cross_build()
- for k in BUILTIN_OPTIONS_PER_MACHINE:
+ for k in options.BUILTIN_OPTIONS_PER_MACHINE:
o = self.options[k]
dirty |= self.options[k.as_build()].set_value(o.value)
for bk, bv in self.options.items():
@@ -944,21 +627,21 @@ class CoreData:
return dirty
- def set_options(self, options: T.Dict[OptionKey, T.Any], subproject: str = '', first_invocation: bool = False) -> bool:
+ def set_options(self, opts_to_set: T.Dict[OptionKey, T.Any], subproject: str = '', first_invocation: bool = False) -> bool:
dirty = False
if not self.is_cross_build():
- options = {k: v for k, v in options.items() if k.machine is not MachineChoice.BUILD}
+ opts_to_set = {k: v for k, v in opts_to_set.items() if k.machine is not MachineChoice.BUILD}
# Set prefix first because it's needed to sanitize other options
pfk = OptionKey('prefix')
- if pfk in options:
- prefix = self.sanitize_prefix(options[pfk])
+ if pfk in opts_to_set:
+ prefix = self.sanitize_prefix(opts_to_set[pfk])
dirty |= self.options[OptionKey('prefix')].set_value(prefix)
- for key in BUILTIN_DIR_NOPREFIX_OPTIONS:
- if key not in options:
- dirty |= self.options[key].set_value(BUILTIN_OPTIONS[key].prefixed_default(key, prefix))
+ for key in options.BUILTIN_DIR_NOPREFIX_OPTIONS:
+ if key not in opts_to_set:
+ dirty |= self.options[key].set_value(options.BUILTIN_OPTIONS[key].prefixed_default(key, prefix))
unknown_options: T.List[OptionKey] = []
- for k, v in options.items():
+ for k, v in opts_to_set.items():
if k == pfk:
continue
elif k in self.options:
@@ -1255,9 +938,9 @@ def save(obj: CoreData, build_dir: str) -> str:
def register_builtin_arguments(parser: argparse.ArgumentParser) -> None:
- for n, b in BUILTIN_OPTIONS.items():
+ for n, b in options.BUILTIN_OPTIONS.items():
b.add_to_argparse(str(n), parser, '')
- for n, b in BUILTIN_OPTIONS_PER_MACHINE.items():
+ for n, b in options.BUILTIN_OPTIONS_PER_MACHINE.items():
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",
@@ -1281,185 +964,55 @@ def parse_cmd_line_options(args: SharedCMDOptions) -> None:
# Merge builtin options set with --option into the dict.
for key in chain(
- BUILTIN_OPTIONS.keys(),
- (k.as_build() for k in BUILTIN_OPTIONS_PER_MACHINE.keys()),
- BUILTIN_OPTIONS_PER_MACHINE.keys(),
+ options.BUILTIN_OPTIONS.keys(),
+ (k.as_build() for k in options.BUILTIN_OPTIONS_PER_MACHINE.keys()),
+ options.BUILTIN_OPTIONS_PER_MACHINE.keys(),
):
name = str(key)
value = getattr(args, name, None)
if value is not None:
if key in args.cmd_line_options:
- cmdline_name = BuiltinOption.argparse_name_to_arg(name)
+ cmdline_name = options.BuiltinOption.argparse_name_to_arg(name)
raise MesonException(
f'Got argument {name} as both -D{name} and {cmdline_name}. Pick one.')
args.cmd_line_options[key] = value
delattr(args, name)
+@dataclass
+class OptionsView(abc.Mapping):
+ '''A view on an options dictionary for a given subproject and with overrides.
+ '''
-_U = T.TypeVar('_U', bound=UserOption[_T])
-
-class BuiltinOption(T.Generic[_T, _U]):
-
- """Class for a builtin option type.
-
- There are some cases that are not fully supported yet.
- """
-
- def __init__(self, opt_type: T.Type[_U], description: str, default: T.Any, yielding: bool = True, *,
- choices: T.Any = None, readonly: bool = False):
- self.opt_type = opt_type
- self.description = description
- self.default = default
- self.choices = choices
- self.yielding = yielding
- self.readonly = readonly
-
- 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)
- keywords = {'yielding': self.yielding, 'value': value}
- if self.choices:
- keywords['choices'] = self.choices
- o = self.opt_type(name.name, self.description, **keywords)
- o.readonly = self.readonly
- return o
-
- def _argparse_action(self) -> T.Optional[str]:
- # If the type is a boolean, the presence of the argument in --foo form
- # is to enable it. Disabling happens by using -Dfoo=false, which is
- # parsed under `args.projectoptions` and does not hit this codepath.
- if isinstance(self.default, bool):
- return 'store_true'
- return None
-
- def _argparse_choices(self) -> T.Any:
- if self.opt_type is UserBooleanOption:
- return [True, False]
- elif self.opt_type is UserFeatureOption:
- return UserFeatureOption.static_choices
- return self.choices
+ # TODO: the typing here could be made more explicit using a TypeDict from
+ # python 3.8 or typing_extensions
+ original_options: KeyedOptionDictType
+ subproject: T.Optional[str] = None
+ overrides: T.Optional[T.Mapping[OptionKey, T.Union[str, int, bool, T.List[str]]]] = None
- @staticmethod
- def argparse_name_to_arg(name: str) -> str:
- if name == 'warning_level':
- return '--warnlevel'
+ def __getitem__(self, key: OptionKey) -> options.UserOption:
+ # FIXME: This is fundamentally the same algorithm than interpreter.get_option_internal().
+ # We should try to share the code somehow.
+ key = key.evolve(subproject=self.subproject)
+ if not key.is_project():
+ opt = self.original_options.get(key)
+ if opt is None or opt.yielding:
+ opt = self.original_options[key.as_root()]
else:
- return '--' + name.replace('_', '-')
-
- 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]
- except KeyError:
- pass
- return self.default
+ opt = self.original_options[key]
+ if opt.yielding:
+ opt = self.original_options.get(key.as_root(), opt)
+ if self.overrides:
+ override_value = self.overrides.get(key.as_root())
+ if override_value is not None:
+ opt = copy.copy(opt)
+ opt.set_value(override_value)
+ return opt
- def add_to_argparse(self, name: str, parser: argparse.ArgumentParser, help_suffix: str) -> None:
- kwargs = OrderedDict()
+ def __iter__(self) -> T.Iterator[OptionKey]:
+ return iter(self.original_options)
- c = self._argparse_choices()
- b = self._argparse_action()
- h = self.description
- if not b:
- h = '{} (default: {}).'.format(h.rstrip('.'), self.prefixed_default(name))
- else:
- kwargs['action'] = b
- if c and not b:
- kwargs['choices'] = c
- kwargs['default'] = argparse.SUPPRESS
- kwargs['dest'] = 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
-# Also update mesonlib._BUILTIN_NAMES. See the comment there for why this is required.
-# Please also update completion scripts in $MESONSRC/data/shell-completions/
-BUILTIN_DIR_OPTIONS: T.Dict['OptionKey', 'BuiltinOption'] = OrderedDict([
- (OptionKey('prefix'), BuiltinOption(UserStringOption, 'Installation prefix', default_prefix())),
- (OptionKey('bindir'), BuiltinOption(UserStringOption, 'Executable directory', 'bin')),
- (OptionKey('datadir'), BuiltinOption(UserStringOption, 'Data file directory', default_datadir())),
- (OptionKey('includedir'), BuiltinOption(UserStringOption, 'Header file directory', default_includedir())),
- (OptionKey('infodir'), BuiltinOption(UserStringOption, 'Info page directory', default_infodir())),
- (OptionKey('libdir'), BuiltinOption(UserStringOption, 'Library directory', default_libdir())),
- (OptionKey('licensedir'), BuiltinOption(UserStringOption, 'Licenses directory', '')),
- (OptionKey('libexecdir'), BuiltinOption(UserStringOption, 'Library executable directory', default_libexecdir())),
- (OptionKey('localedir'), BuiltinOption(UserStringOption, 'Locale data directory', default_localedir())),
- (OptionKey('localstatedir'), BuiltinOption(UserStringOption, 'Localstate data directory', 'var')),
- (OptionKey('mandir'), BuiltinOption(UserStringOption, 'Manual page directory', default_mandir())),
- (OptionKey('sbindir'), BuiltinOption(UserStringOption, 'System executable directory', default_sbindir())),
- (OptionKey('sharedstatedir'), BuiltinOption(UserStringOption, 'Architecture-independent data directory', 'com')),
- (OptionKey('sysconfdir'), BuiltinOption(UserStringOption, 'Sysconf data directory', default_sysconfdir())),
-])
-
-BUILTIN_CORE_OPTIONS: T.Dict['OptionKey', 'BuiltinOption'] = OrderedDict([
- (OptionKey('auto_features'), BuiltinOption(UserFeatureOption, "Override value of all 'auto' features", 'auto')),
- (OptionKey('backend'), BuiltinOption(UserComboOption, 'Backend to use', 'ninja', choices=backendlist,
- readonly=True)),
- (OptionKey('genvslite'),
- BuiltinOption(
- UserComboOption,
- 'Setup multiple buildtype-suffixed ninja-backend build directories, '
- 'and a [builddir]_vs containing a Visual Studio meta-backend with multiple configurations that calls into them',
- 'vs2022',
- choices=genvslitelist)
- ),
- (OptionKey('buildtype'), BuiltinOption(UserComboOption, 'Build type to use', 'debug',
- choices=buildtypelist)),
- (OptionKey('debug'), BuiltinOption(UserBooleanOption, 'Enable debug symbols and other information', 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=['plain', '0', 'g', '1', '2', '3', 's'])),
- (OptionKey('prefer_static'), BuiltinOption(UserBooleanOption, 'Whether to try static linking before shared linking', False)),
- (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', 'everything'], 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', 'nopromote'])),
- (OptionKey('force_fallback_for'), BuiltinOption(UserArrayOption, 'Force fallback for those subprojects', [])),
- (OptionKey('vsenv'), BuiltinOption(UserBooleanOption, 'Activate Visual Studio environment', False, readonly=True)),
-
- # Pkgconfig module
- (OptionKey('relocatable', module='pkgconfig'),
- BuiltinOption(UserBooleanOption, 'Generate pkgconfig files as relocatable', False)),
-
- # Python module
- (OptionKey('bytecompile', module='python'),
- BuiltinOption(UserIntegerOption, 'Whether to compile bytecode', (-1, 2, 0))),
- (OptionKey('install_env', module='python'),
- BuiltinOption(UserComboOption, 'Which python environment to install to', 'prefix', choices=['auto', 'prefix', 'system', 'venv'])),
- (OptionKey('platlibdir', module='python'),
- BuiltinOption(UserStringOption, 'Directory for site-specific, platform-specific files.', '')),
- (OptionKey('purelibdir', module='python'),
- BuiltinOption(UserStringOption, 'Directory for site-specific, non-platform-specific files.', '')),
- (OptionKey('allow_limited_api', module='python'),
- BuiltinOption(UserBooleanOption, 'Whether to allow use of the Python Limited API', True)),
-])
-
-BUILTIN_OPTIONS = OrderedDict(chain(BUILTIN_DIR_OPTIONS.items(), BUILTIN_CORE_OPTIONS.items()))
-
-BUILTIN_OPTIONS_PER_MACHINE: T.Dict['OptionKey', 'BuiltinOption'] = 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: 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'},
- OptionKey('platlibdir', module='python'): {},
- OptionKey('purelibdir', module='python'): {},
-}
+ def __len__(self) -> int:
+ return len(self.original_options)
FORBIDDEN_TARGET_NAMES = frozenset({
'clean',
diff --git a/mesonbuild/interpreter/compiler.py b/mesonbuild/interpreter/compiler.py
index 50a850a..359e8e7 100644
--- a/mesonbuild/interpreter/compiler.py
+++ b/mesonbuild/interpreter/compiler.py
@@ -13,6 +13,7 @@ import typing as T
from .. import build
from .. import coredata
from .. import dependencies
+from .. import options
from .. import mesonlib
from .. import mlog
from ..compilers import SUFFIX_TO_LANG, RunResult
@@ -89,7 +90,7 @@ if T.TYPE_CHECKING:
header_include_directories: T.List[build.IncludeDirs]
header_no_builtin_args: bool
header_prefix: str
- header_required: T.Union[bool, coredata.UserFeatureOption]
+ header_required: T.Union[bool, options.UserFeatureOption]
class PreprocessKW(TypedDict):
output: str
@@ -685,7 +686,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
@typed_pos_args('compiler.find_library', str)
@typed_kwargs(
'compiler.find_library',
- KwargInfo('required', (bool, coredata.UserFeatureOption), default=True),
+ KwargInfo('required', (bool, options.UserFeatureOption), default=True),
KwargInfo('has_headers', ContainerTypeInfo(list, str), listify=True, default=[], since='0.50.0'),
KwargInfo('static', (bool, NoneType), since='0.51.0'),
KwargInfo('disabler', bool, default=False, since='0.49.0'),
diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py
index 50780ba..eb6783c 100644
--- a/mesonbuild/interpreter/interpreter.py
+++ b/mesonbuild/interpreter/interpreter.py
@@ -11,6 +11,7 @@ from .. import environment
from .. import coredata
from .. import dependencies
from .. import mlog
+from .. import options
from .. import build
from .. import optinterpreter
from .. import compilers
@@ -163,7 +164,7 @@ class Summary:
elif isinstance(i, Disabler):
FeatureNew.single_use('disabler in summary', '0.64.0', subproject)
formatted_values.append(mlog.red('NO'))
- elif isinstance(i, coredata.UserOption):
+ elif isinstance(i, options.UserOption):
FeatureNew.single_use('feature option in summary', '0.58.0', subproject)
formatted_values.append(i.printable_value())
else:
@@ -450,7 +451,7 @@ class Interpreter(InterpreterBase, HoldableObject):
build.StructuredSources: OBJ.StructuredSourcesHolder,
compilers.RunResult: compilerOBJ.TryRunResultHolder,
dependencies.ExternalLibrary: OBJ.ExternalLibraryHolder,
- coredata.UserFeatureOption: OBJ.FeatureOptionHolder,
+ options.UserFeatureOption: OBJ.FeatureOptionHolder,
envconfig.MachineInfo: OBJ.MachineHolder,
build.ConfigurationData: OBJ.ConfigurationDataHolder,
})
@@ -1047,7 +1048,7 @@ class Interpreter(InterpreterBase, HoldableObject):
# FIXME: Are there other files used by cargo interpreter?
[os.path.join(subdir, 'Cargo.toml')])
- def get_option_internal(self, optname: str) -> coredata.UserOption:
+ def get_option_internal(self, optname: str) -> options.UserOption:
key = OptionKey.from_string(optname).evolve(subproject=self.subproject)
if not key.is_project():
@@ -1056,7 +1057,7 @@ class Interpreter(InterpreterBase, HoldableObject):
if v is None or v.yielding:
v = opts.get(key.as_root())
if v is not None:
- assert isinstance(v, coredata.UserOption), 'for mypy'
+ assert isinstance(v, options.UserOption), 'for mypy'
return v
try:
@@ -1085,7 +1086,7 @@ class Interpreter(InterpreterBase, HoldableObject):
@typed_pos_args('get_option', str)
@noKwargs
def func_get_option(self, nodes: mparser.BaseNode, args: T.Tuple[str],
- kwargs: 'TYPE_kwargs') -> T.Union[coredata.UserOption, 'TYPE_var']:
+ kwargs: 'TYPE_kwargs') -> T.Union[options.UserOption, 'TYPE_var']:
optname = args[0]
if ':' in optname:
raise InterpreterException('Having a colon in option name is forbidden, '
@@ -1096,10 +1097,10 @@ class Interpreter(InterpreterBase, HoldableObject):
raise InterpreterException(f'Invalid option name {optname!r}')
opt = self.get_option_internal(optname)
- if isinstance(opt, coredata.UserFeatureOption):
+ if isinstance(opt, options.UserFeatureOption):
opt.name = optname
return opt
- elif isinstance(opt, coredata.UserOption):
+ elif isinstance(opt, options.UserOption):
if isinstance(opt.value, str):
return P_OBJ.OptionString(opt.value, f'{{{optname}}}')
return opt.value
diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py
index 8cd9a2b..adec0d5 100644
--- a/mesonbuild/interpreter/interpreterobjects.py
+++ b/mesonbuild/interpreter/interpreterobjects.py
@@ -8,7 +8,7 @@ import textwrap
from pathlib import Path, PurePath
from .. import mesonlib
-from .. import coredata
+from .. import options
from .. import build
from .. import mlog
@@ -52,7 +52,7 @@ def extract_required_kwarg(kwargs: 'kwargs.ExtractRequired',
disabled = False
required = False
feature: T.Optional[str] = None
- if isinstance(val, coredata.UserFeatureOption):
+ if isinstance(val, options.UserFeatureOption):
if not feature_check:
feature_check = FeatureNew('User option "feature"', '0.47.0')
feature_check.use(subproject)
@@ -85,12 +85,12 @@ def extract_search_dirs(kwargs: 'kwargs.ExtractSearchDirs') -> T.List[str]:
raise InvalidCode(f'Search directory {d} is not an absolute path.')
return [str(s) for s in search_dirs]
-class FeatureOptionHolder(ObjectHolder[coredata.UserFeatureOption]):
- def __init__(self, option: coredata.UserFeatureOption, interpreter: 'Interpreter'):
+class FeatureOptionHolder(ObjectHolder[options.UserFeatureOption]):
+ def __init__(self, option: options.UserFeatureOption, interpreter: 'Interpreter'):
super().__init__(option, interpreter)
if option and option.is_auto():
# TODO: we need to cast here because options is not a TypedDict
- auto = T.cast('coredata.UserFeatureOption', self.env.coredata.options[OptionKey('auto_features')])
+ auto = T.cast('options.UserFeatureOption', self.env.coredata.options[OptionKey('auto_features')])
self.held_object = copy.copy(auto)
self.held_object.name = option.name
self.methods.update({'enabled': self.enabled_method,
@@ -108,12 +108,12 @@ class FeatureOptionHolder(ObjectHolder[coredata.UserFeatureOption]):
def value(self) -> str:
return 'disabled' if not self.held_object else self.held_object.value
- def as_disabled(self) -> coredata.UserFeatureOption:
+ def as_disabled(self) -> options.UserFeatureOption:
disabled = copy.deepcopy(self.held_object)
disabled.value = 'disabled'
return disabled
- def as_enabled(self) -> coredata.UserFeatureOption:
+ def as_enabled(self) -> options.UserFeatureOption:
enabled = copy.deepcopy(self.held_object)
enabled.value = 'enabled'
return enabled
@@ -139,7 +139,7 @@ class FeatureOptionHolder(ObjectHolder[coredata.UserFeatureOption]):
def auto_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
return self.value == 'auto'
- def _disable_if(self, condition: bool, message: T.Optional[str]) -> coredata.UserFeatureOption:
+ def _disable_if(self, condition: bool, message: T.Optional[str]) -> options.UserFeatureOption:
if not condition:
return copy.deepcopy(self.held_object)
@@ -156,7 +156,7 @@ class FeatureOptionHolder(ObjectHolder[coredata.UserFeatureOption]):
'feature_option.require',
_ERROR_MSG_KW,
)
- def require_method(self, args: T.Tuple[bool], kwargs: 'kwargs.FeatureOptionRequire') -> coredata.UserFeatureOption:
+ def require_method(self, args: T.Tuple[bool], kwargs: 'kwargs.FeatureOptionRequire') -> options.UserFeatureOption:
return self._disable_if(not args[0], kwargs['error_message'])
@FeatureNew('feature_option.disable_if()', '1.1.0')
@@ -165,7 +165,7 @@ class FeatureOptionHolder(ObjectHolder[coredata.UserFeatureOption]):
'feature_option.disable_if',
_ERROR_MSG_KW,
)
- def disable_if_method(self, args: T.Tuple[bool], kwargs: 'kwargs.FeatureOptionRequire') -> coredata.UserFeatureOption:
+ def disable_if_method(self, args: T.Tuple[bool], kwargs: 'kwargs.FeatureOptionRequire') -> options.UserFeatureOption:
return self._disable_if(args[0], kwargs['error_message'])
@FeatureNew('feature_option.enable_if()', '1.1.0')
@@ -174,7 +174,7 @@ class FeatureOptionHolder(ObjectHolder[coredata.UserFeatureOption]):
'feature_option.enable_if',
_ERROR_MSG_KW,
)
- def enable_if_method(self, args: T.Tuple[bool], kwargs: 'kwargs.FeatureOptionRequire') -> coredata.UserFeatureOption:
+ def enable_if_method(self, args: T.Tuple[bool], kwargs: 'kwargs.FeatureOptionRequire') -> options.UserFeatureOption:
if not args[0]:
return copy.deepcopy(self.held_object)
@@ -188,13 +188,13 @@ class FeatureOptionHolder(ObjectHolder[coredata.UserFeatureOption]):
@FeatureNew('feature_option.disable_auto_if()', '0.59.0')
@noKwargs
@typed_pos_args('feature_option.disable_auto_if', bool)
- def disable_auto_if_method(self, args: T.Tuple[bool], kwargs: TYPE_kwargs) -> coredata.UserFeatureOption:
+ def disable_auto_if_method(self, args: T.Tuple[bool], kwargs: TYPE_kwargs) -> options.UserFeatureOption:
return copy.deepcopy(self.held_object) if self.value != 'auto' or not args[0] else self.as_disabled()
@FeatureNew('feature_option.enable_auto_if()', '1.1.0')
@noKwargs
@typed_pos_args('feature_option.enable_auto_if', bool)
- def enable_auto_if_method(self, args: T.Tuple[bool], kwargs: TYPE_kwargs) -> coredata.UserFeatureOption:
+ def enable_auto_if_method(self, args: T.Tuple[bool], kwargs: TYPE_kwargs) -> options.UserFeatureOption:
return self.as_enabled() if self.value == 'auto' and args[0] else copy.deepcopy(self.held_object)
diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py
index 17f7876..85779bc 100644
--- a/mesonbuild/interpreter/kwargs.py
+++ b/mesonbuild/interpreter/kwargs.py
@@ -10,7 +10,7 @@ import typing as T
from typing_extensions import TypedDict, Literal, Protocol, NotRequired
from .. import build
-from .. import coredata
+from .. import options
from ..compilers import Compiler
from ..dependencies.base import Dependency
from ..mesonlib import EnvironmentVariables, MachineChoice, File, FileMode, FileOrString, OptionKey
@@ -73,7 +73,7 @@ class ExtractRequired(TypedDict):
a boolean or a feature option should inherit it's arguments from this class.
"""
- required: T.Union[bool, coredata.UserFeatureOption]
+ required: T.Union[bool, options.UserFeatureOption]
class ExtractSearchDirs(TypedDict):
diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py
index 9b7e35c..2856136 100644
--- a/mesonbuild/interpreter/type_checking.py
+++ b/mesonbuild/interpreter/type_checking.py
@@ -11,7 +11,7 @@ from .. import compilers
from ..build import (CustomTarget, BuildTarget,
CustomTargetIndex, ExtractedObjects, GeneratedList, IncludeDirs,
BothLibraries, SharedLibrary, StaticLibrary, Jar, Executable, StructuredSources)
-from ..coredata import UserFeatureOption
+from ..options import UserFeatureOption
from ..dependencies import Dependency, InternalDependency
from ..interpreterbase.decorators import KwargInfo, ContainerTypeInfo
from ..mesonlib import (File, FileMode, MachineChoice, listify, has_path_sep,
diff --git a/mesonbuild/interpreterbase/helpers.py b/mesonbuild/interpreterbase/helpers.py
index 3942f2c..0b04362 100644
--- a/mesonbuild/interpreterbase/helpers.py
+++ b/mesonbuild/interpreterbase/helpers.py
@@ -5,7 +5,7 @@ from __future__ import annotations
from .. import mesonlib, mparser
from .exceptions import InterpreterException, InvalidArguments
-from ..coredata import UserOption
+from ..options import UserOption
import collections.abc
diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py
index 2cef24f..48359de 100644
--- a/mesonbuild/mconf.py
+++ b/mesonbuild/mconf.py
@@ -14,6 +14,7 @@ import collections
from . import build
from . import coredata
+from . import options
from . import environment
from . import mesonlib
from . import mintro
@@ -83,12 +84,12 @@ class Conf:
# if the option file has been updated, reload it
# This cannot handle options for a new subproject that has not yet
# been configured.
- for sub, options in self.coredata.options_files.items():
- if options is not None and os.path.exists(options[0]):
- opfile = options[0]
+ for sub, conf_options in self.coredata.options_files.items():
+ if conf_options is not None and os.path.exists(conf_options[0]):
+ opfile = conf_options[0]
with open(opfile, 'rb') as f:
ophash = hashlib.sha1(f.read()).hexdigest()
- if ophash != options[1]:
+ if ophash != conf_options[1]:
oi = OptionInterpreter(sub)
oi.process(opfile)
self.coredata.update_project_options(oi.options, sub)
@@ -223,18 +224,18 @@ class Conf:
self._add_line(mlog.normal_yellow(section + ':'), '', '', '')
self.print_margin = 2
- def print_options(self, title: str, options: 'coredata.KeyedOptionDictType') -> None:
- if not options:
+ def print_options(self, title: str, opts: 'coredata.KeyedOptionDictType') -> None:
+ if not opts:
return
if title:
self.add_title(title)
- auto = T.cast('coredata.UserFeatureOption', self.coredata.options[OptionKey('auto_features')])
- for k, o in sorted(options.items()):
+ auto = T.cast('options.UserFeatureOption', self.coredata.options[OptionKey('auto_features')])
+ for k, o in sorted(opts.items()):
printable_value = o.printable_value()
root = k.as_root()
if o.yielding and k.subproject and root in self.coredata.options:
printable_value = '<inherited from main project>'
- if isinstance(o, coredata.UserFeatureOption) and o.is_auto():
+ if isinstance(o, options.UserFeatureOption) and o.is_auto():
printable_value = auto.printable_value()
self.add_option(str(root), o.description, printable_value, o.choices)
@@ -255,7 +256,7 @@ class Conf:
if not self.default_values_only:
mlog.log(' Build dir ', self.build_dir)
- dir_option_names = set(coredata.BUILTIN_DIR_OPTIONS)
+ dir_option_names = set(options.BUILTIN_DIR_OPTIONS)
test_option_names = {OptionKey('errorlogs'),
OptionKey('stdsplit')}
diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py
index bdbb59e..a5ce72a 100644
--- a/mesonbuild/mintro.py
+++ b/mesonbuild/mintro.py
@@ -19,7 +19,7 @@ from pathlib import Path, PurePath
import sys
import typing as T
-from . import build, mesonlib, coredata as cdata
+from . import build, mesonlib, options, coredata as cdata
from .ast import IntrospectionInterpreter, BUILD_TARGET_FUNCTIONS, AstConditionLevel, AstIDGenerator, AstIndentationGenerator, AstJSONPrinter
from .backend import backends
from .dependencies import Dependency
@@ -88,7 +88,7 @@ def add_arguments(parser: argparse.ArgumentParser) -> None:
flag = '--' + key.replace('_', '-')
parser.add_argument(flag, action='store_true', dest=key, default=False, help=val.desc)
- parser.add_argument('--backend', choices=sorted(cdata.backendlist), dest='backend', default='ninja',
+ parser.add_argument('--backend', choices=sorted(options.backendlist), dest='backend', default='ninja',
help='The backend to use for the --buildoptions introspection.')
parser.add_argument('-a', '--all', action='store_true', dest='all', default=False,
help='Print all available information.')
@@ -284,7 +284,7 @@ def list_buildoptions(coredata: cdata.CoreData, subprojects: T.Optional[T.List[s
optlist: T.List[T.Dict[str, T.Union[str, bool, int, T.List[str]]]] = []
subprojects = subprojects or []
- dir_option_names = set(cdata.BUILTIN_DIR_OPTIONS)
+ dir_option_names = set(options.BUILTIN_DIR_OPTIONS)
test_option_names = {OptionKey('errorlogs'),
OptionKey('stdsplit')}
@@ -302,20 +302,20 @@ def list_buildoptions(coredata: cdata.CoreData, subprojects: T.Optional[T.List[s
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()):
+ def add_keys(opts: 'cdata.KeyedOptionDictType', section: str) -> None:
+ for key, opt in sorted(opts.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):
+ if isinstance(opt, options.UserStringOption):
typestr = 'string'
- elif isinstance(opt, cdata.UserBooleanOption):
+ elif isinstance(opt, options.UserBooleanOption):
typestr = 'boolean'
- elif isinstance(opt, cdata.UserComboOption):
+ elif isinstance(opt, options.UserComboOption):
optdict['choices'] = opt.choices
typestr = 'combo'
- elif isinstance(opt, cdata.UserIntegerOption):
+ elif isinstance(opt, options.UserIntegerOption):
typestr = 'integer'
- elif isinstance(opt, cdata.UserArrayOption):
+ elif isinstance(opt, options.UserArrayOption):
typestr = 'array'
if opt.choices:
optdict['choices'] = opt.choices
diff --git a/mesonbuild/modules/_qt.py b/mesonbuild/modules/_qt.py
index 7effa1f..ebb8a39 100644
--- a/mesonbuild/modules/_qt.py
+++ b/mesonbuild/modules/_qt.py
@@ -11,7 +11,7 @@ import xml.etree.ElementTree as ET
from . import ModuleReturnValue, ExtensionModule
from .. import build
-from .. import coredata
+from .. import options
from .. import mlog
from ..dependencies import find_external_dependency, Dependency, ExternalLibrary, InternalDependency
from ..mesonlib import MesonException, File, version_compare, Popen_safe
@@ -256,7 +256,7 @@ class QtBaseModule(ExtensionModule):
@noPosargs
@typed_kwargs(
'qt.has_tools',
- KwargInfo('required', (bool, coredata.UserFeatureOption), default=False),
+ KwargInfo('required', (bool, options.UserFeatureOption), default=False),
KwargInfo('method', str, default='auto'),
)
def has_tools(self, state: 'ModuleState', args: T.Tuple, kwargs: 'HasToolKwArgs') -> bool:
diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py
index ebe0d92..1a73070 100644
--- a/mesonbuild/modules/pkgconfig.py
+++ b/mesonbuild/modules/pkgconfig.py
@@ -14,7 +14,7 @@ from .. import build
from .. import dependencies
from .. import mesonlib
from .. import mlog
-from ..coredata import BUILTIN_DIR_OPTIONS
+from ..options import BUILTIN_DIR_OPTIONS
from ..dependencies.pkgconfig import PkgConfigDependency, PkgConfigInterface
from ..interpreter.type_checking import D_MODULE_VERSIONS_KW, INSTALL_DIR_KW, VARIABLES_KW, NoneType
from ..interpreterbase import FeatureNew, FeatureDeprecated
diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py
index 59b5050..d195a3f 100644
--- a/mesonbuild/modules/python.py
+++ b/mesonbuild/modules/python.py
@@ -9,7 +9,7 @@ import typing as T
from . import ExtensionModule, ModuleInfo
from .. import mesonlib
from .. import mlog
-from ..coredata import UserFeatureOption
+from ..options import UserFeatureOption
from ..build import known_shmod_kwargs, CustomTarget, CustomTargetIndex, BuildTarget, GeneratedList, StructuredSources, ExtractedObjects, SharedModule
from ..dependencies import NotFoundDependency
from ..dependencies.detect import get_dep_identifier, find_external_dependency
diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py
index 599da65..ffa46cd 100644
--- a/mesonbuild/optinterpreter.py
+++ b/mesonbuild/optinterpreter.py
@@ -7,6 +7,7 @@ import re
import typing as T
from . import coredata
+from . import options
from . import mesonlib
from . import mparser
from . import mlog
@@ -66,7 +67,7 @@ class OptionInterpreter:
def __init__(self, subproject: 'SubProject') -> None:
self.options: 'coredata.MutableKeyedOptionDictType' = {}
self.subproject = subproject
- self.option_types: T.Dict[str, T.Callable[..., coredata.UserOption]] = {
+ self.option_types: T.Dict[str, T.Callable[..., options.UserOption]] = {
'string': self.string_parser,
'boolean': self.boolean_parser,
'combo': self.combo_parser,
@@ -179,7 +180,7 @@ class OptionInterpreter:
since='0.60.0',
since_values={str: '0.63.0'},
),
- KwargInfo('yield', bool, default=coredata.DEFAULT_YIELDING, since='0.45.0'),
+ KwargInfo('yield', bool, default=options.DEFAULT_YIELDING, since='0.45.0'),
allow_unknown=True,
)
@typed_pos_args('option', str)
@@ -208,8 +209,8 @@ class OptionInterpreter:
'string option',
KwargInfo('value', str, default=''),
)
- def string_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECATED_ARGS], kwargs: StringArgs) -> coredata.UserOption:
- return coredata.UserStringOption(name, description, kwargs['value'], *args)
+ def string_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECATED_ARGS], kwargs: StringArgs) -> options.UserOption:
+ return options.UserStringOption(name, description, kwargs['value'], *args)
@typed_kwargs(
'boolean option',
@@ -221,20 +222,20 @@ class OptionInterpreter:
deprecated_values={str: ('1.1.0', 'use a boolean, not a string')},
),
)
- def boolean_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECATED_ARGS], kwargs: BooleanArgs) -> coredata.UserOption:
- return coredata.UserBooleanOption(name, description, kwargs['value'], *args)
+ def boolean_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECATED_ARGS], kwargs: BooleanArgs) -> options.UserOption:
+ return options.UserBooleanOption(name, description, kwargs['value'], *args)
@typed_kwargs(
'combo option',
KwargInfo('value', (str, NoneType)),
KwargInfo('choices', ContainerTypeInfo(list, str, allow_empty=False), required=True),
)
- def combo_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECATED_ARGS], kwargs: ComboArgs) -> coredata.UserOption:
+ def combo_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECATED_ARGS], kwargs: ComboArgs) -> options.UserOption:
choices = kwargs['choices']
value = kwargs['value']
if value is None:
value = kwargs['choices'][0]
- return coredata.UserComboOption(name, description, choices, value, *args)
+ return options.UserComboOption(name, description, choices, value, *args)
@typed_kwargs(
'integer option',
@@ -248,17 +249,17 @@ class OptionInterpreter:
KwargInfo('min', (int, NoneType)),
KwargInfo('max', (int, NoneType)),
)
- def integer_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECATED_ARGS], kwargs: IntegerArgs) -> coredata.UserOption:
+ def integer_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECATED_ARGS], kwargs: IntegerArgs) -> options.UserOption:
value = kwargs['value']
inttuple = (kwargs['min'], kwargs['max'], value)
- return coredata.UserIntegerOption(name, description, inttuple, *args)
+ return options.UserIntegerOption(name, description, inttuple, *args)
@typed_kwargs(
'string array option',
KwargInfo('value', (ContainerTypeInfo(list, str), str, NoneType)),
KwargInfo('choices', ContainerTypeInfo(list, str), default=[]),
)
- def string_array_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECATED_ARGS], kwargs: StringArrayArgs) -> coredata.UserOption:
+ def string_array_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECATED_ARGS], kwargs: StringArrayArgs) -> options.UserOption:
choices = kwargs['choices']
value = kwargs['value'] if kwargs['value'] is not None else choices
if isinstance(value, str):
@@ -266,14 +267,14 @@ class OptionInterpreter:
FeatureDeprecated('String value for array option', '1.3.0').use(self.subproject)
else:
raise mesonlib.MesonException('Value does not define an array: ' + value)
- return coredata.UserArrayOption(name, description, value,
- choices=choices,
- yielding=args[0],
- deprecated=args[1])
+ return options.UserArrayOption(name, description, value,
+ choices=choices,
+ yielding=args[0],
+ deprecated=args[1])
@typed_kwargs(
'feature option',
KwargInfo('value', str, default='auto', validator=in_set_validator({'auto', 'enabled', 'disabled'})),
)
- def feature_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECATED_ARGS], kwargs: FeatureArgs) -> coredata.UserOption:
- return coredata.UserFeatureOption(name, description, kwargs['value'], *args)
+ def feature_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECATED_ARGS], kwargs: FeatureArgs) -> options.UserOption:
+ return options.UserFeatureOption(name, description, kwargs['value'], *args)
diff --git a/mesonbuild/options.py b/mesonbuild/options.py
new file mode 100644
index 0000000..2699dd5
--- /dev/null
+++ b/mesonbuild/options.py
@@ -0,0 +1,480 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright 2013-2024 Contributors to the The Meson project
+
+from collections import OrderedDict
+from itertools import chain
+import argparse
+
+from .mesonlib import (
+ HoldableObject,
+ OptionKey,
+ default_prefix,
+ default_datadir,
+ default_includedir,
+ default_infodir,
+ default_libdir,
+ default_libexecdir,
+ default_localedir,
+ default_mandir,
+ default_sbindir,
+ default_sysconfdir,
+ MesonException,
+ listify_array_value,
+)
+
+from . import mlog
+
+import typing as T
+
+DEFAULT_YIELDING = False
+
+# Can't bind this near the class method it seems, sadly.
+_T = T.TypeVar('_T')
+
+backendlist = ['ninja', 'vs', 'vs2010', 'vs2012', 'vs2013', 'vs2015', 'vs2017', 'vs2019', 'vs2022', 'xcode', 'none']
+genvslitelist = ['vs2022']
+buildtypelist = ['plain', 'debug', 'debugoptimized', 'release', 'minsize', 'custom']
+
+
+class UserOption(T.Generic[_T], HoldableObject):
+ def __init__(self, name: str, description: str, choices: T.Optional[T.Union[str, T.List[_T]]],
+ yielding: bool,
+ deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False):
+ super().__init__()
+ self.name = name
+ self.choices = choices
+ self.description = description
+ if not isinstance(yielding, bool):
+ raise MesonException('Value of "yielding" must be a boolean.')
+ self.yielding = yielding
+ self.deprecated = deprecated
+ self.readonly = False
+
+ def listify(self, value: T.Any) -> T.List[T.Any]:
+ return [value]
+
+ def printable_value(self) -> T.Union[str, int, bool, T.List[T.Union[str, int, bool]]]:
+ assert isinstance(self.value, (str, int, bool, list))
+ return self.value
+
+ # Check that the input is a valid value and return the
+ # "cleaned" or "native" version. For example the Boolean
+ # option could take the string "true" and return True.
+ def validate_value(self, value: T.Any) -> _T:
+ raise RuntimeError('Derived option class did not override validate_value.')
+
+ def set_value(self, newvalue: T.Any) -> bool:
+ oldvalue = getattr(self, 'value', None)
+ self.value = self.validate_value(newvalue)
+ return self.value != oldvalue
+
+_U = T.TypeVar('_U', bound=UserOption[_T])
+
+
+class UserStringOption(UserOption[str]):
+ def __init__(self, name: str, description: str, value: T.Any, yielding: bool = DEFAULT_YIELDING,
+ deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False):
+ super().__init__(name, description, None, yielding, deprecated)
+ self.set_value(value)
+
+ def validate_value(self, value: T.Any) -> str:
+ if not isinstance(value, str):
+ raise MesonException(f'The value of option "{self.name}" is "{value}", which is not a string.')
+ return value
+
+class UserBooleanOption(UserOption[bool]):
+ def __init__(self, name: str, description: str, value: bool, yielding: bool = DEFAULT_YIELDING,
+ deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False):
+ super().__init__(name, description, [True, False], yielding, deprecated)
+ self.set_value(value)
+
+ def __bool__(self) -> bool:
+ return self.value
+
+ def validate_value(self, value: T.Any) -> bool:
+ if isinstance(value, bool):
+ return value
+ if not isinstance(value, str):
+ raise MesonException(f'Option "{self.name}" value {value} cannot be converted to a boolean')
+ if value.lower() == 'true':
+ return True
+ if value.lower() == 'false':
+ return False
+ raise MesonException(f'Option "{self.name}" value {value} is not boolean (true or false).')
+
+class UserIntegerOption(UserOption[int]):
+ def __init__(self, name: str, description: str, value: T.Any, yielding: bool = DEFAULT_YIELDING,
+ deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False):
+ min_value, max_value, default_value = value
+ self.min_value = min_value
+ self.max_value = max_value
+ c: T.List[str] = []
+ if min_value is not None:
+ c.append('>=' + str(min_value))
+ if max_value is not None:
+ c.append('<=' + str(max_value))
+ choices = ', '.join(c)
+ super().__init__(name, description, choices, yielding, deprecated)
+ self.set_value(default_value)
+
+ def validate_value(self, value: T.Any) -> int:
+ if isinstance(value, str):
+ value = self.toint(value)
+ if not isinstance(value, int):
+ raise MesonException(f'Value {value!r} for option "{self.name}" is not an integer.')
+ if self.min_value is not None and value < self.min_value:
+ raise MesonException(f'Value {value} for option "{self.name}" is less than minimum value {self.min_value}.')
+ if self.max_value is not None and value > self.max_value:
+ raise MesonException(f'Value {value} for option "{self.name}" is more than maximum value {self.max_value}.')
+ return value
+
+ def toint(self, valuestring: str) -> int:
+ try:
+ return int(valuestring)
+ except ValueError:
+ raise MesonException(f'Value string "{valuestring}" for option "{self.name}" is not convertible to an integer.')
+
+class OctalInt(int):
+ # NinjaBackend.get_user_option_args uses str() to converts it to a command line option
+ # UserUmaskOption.toint() uses int(str, 8) to convert it to an integer
+ # So we need to use oct instead of dec here if we do not want values to be misinterpreted.
+ def __str__(self) -> str:
+ return oct(int(self))
+
+class UserUmaskOption(UserIntegerOption, UserOption[T.Union[str, OctalInt]]):
+ def __init__(self, name: str, description: str, value: T.Any, yielding: bool = DEFAULT_YIELDING,
+ deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False):
+ super().__init__(name, description, (0, 0o777, value), yielding, deprecated)
+ self.choices = ['preserve', '0000-0777']
+
+ def printable_value(self) -> str:
+ if self.value == 'preserve':
+ return self.value
+ return format(self.value, '04o')
+
+ def validate_value(self, value: T.Any) -> T.Union[str, OctalInt]:
+ if value == 'preserve':
+ return 'preserve'
+ return OctalInt(super().validate_value(value))
+
+ def toint(self, valuestring: T.Union[str, OctalInt]) -> int:
+ try:
+ return int(valuestring, 8)
+ except ValueError as e:
+ raise MesonException(f'Invalid mode for option "{self.name}" {e}')
+
+class UserComboOption(UserOption[str]):
+ def __init__(self, name: str, description: str, choices: T.List[str], value: T.Any,
+ yielding: bool = DEFAULT_YIELDING,
+ deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False):
+ super().__init__(name, description, choices, yielding, deprecated)
+ if not isinstance(self.choices, list):
+ raise MesonException(f'Combo choices for option "{self.name}" must be an array.')
+ for i in self.choices:
+ if not isinstance(i, str):
+ raise MesonException(f'Combo choice elements for option "{self.name}" must be strings.')
+ self.set_value(value)
+
+ def validate_value(self, value: T.Any) -> str:
+ if value not in self.choices:
+ if isinstance(value, bool):
+ _type = 'boolean'
+ elif isinstance(value, (int, float)):
+ _type = 'number'
+ else:
+ _type = 'string'
+ optionsstring = ', '.join([f'"{item}"' for item in self.choices])
+ raise MesonException('Value "{}" (of type "{}") for option "{}" is not one of the choices.'
+ ' Possible choices are (as string): {}.'.format(
+ value, _type, self.name, optionsstring))
+ return value
+
+class UserArrayOption(UserOption[T.List[str]]):
+ def __init__(self, name: str, description: str, value: T.Union[str, T.List[str]],
+ split_args: bool = False,
+ allow_dups: bool = False, yielding: bool = DEFAULT_YIELDING,
+ choices: T.Optional[T.List[str]] = None,
+ deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False):
+ super().__init__(name, description, choices if choices is not None else [], yielding, deprecated)
+ self.split_args = split_args
+ self.allow_dups = allow_dups
+ self.set_value(value)
+
+ def listify(self, value: T.Any) -> T.List[T.Any]:
+ try:
+ return listify_array_value(value, self.split_args)
+ except MesonException as e:
+ raise MesonException(f'error in option "{self.name}": {e!s}')
+
+ def validate_value(self, value: T.Union[str, T.List[str]]) -> T.List[str]:
+ newvalue = self.listify(value)
+
+ if not self.allow_dups and len(set(newvalue)) != len(newvalue):
+ msg = 'Duplicated values in array option is deprecated. ' \
+ 'This will become a hard error in the future.'
+ mlog.deprecation(msg)
+ for i in newvalue:
+ if not isinstance(i, str):
+ raise MesonException(f'String array element "{newvalue!s}" for option "{self.name}" is not a string.')
+ if self.choices:
+ bad = [x for x in newvalue if x not in self.choices]
+ if bad:
+ raise MesonException('Value{} "{}" for option "{}" {} not in allowed choices: "{}"'.format(
+ '' if len(bad) == 1 else 's',
+ ', '.join(bad),
+ self.name,
+ 'is' if len(bad) == 1 else 'are',
+ ', '.join(self.choices))
+ )
+ return newvalue
+
+ def extend_value(self, value: T.Union[str, T.List[str]]) -> None:
+ """Extend the value with an additional value."""
+ new = self.validate_value(value)
+ self.set_value(self.value + new)
+
+
+class UserFeatureOption(UserComboOption):
+ static_choices = ['enabled', 'disabled', 'auto']
+
+ def __init__(self, name: str, description: str, value: T.Any, yielding: bool = DEFAULT_YIELDING,
+ deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False):
+ super().__init__(name, description, self.static_choices, value, yielding, deprecated)
+ self.name: T.Optional[str] = None # TODO: Refactor options to all store their name
+
+ def is_enabled(self) -> bool:
+ return self.value == 'enabled'
+
+ def is_disabled(self) -> bool:
+ return self.value == 'disabled'
+
+ def is_auto(self) -> bool:
+ return self.value == 'auto'
+
+class UserStdOption(UserComboOption):
+ '''
+ UserOption specific to c_std and cpp_std options. User can set a list of
+ STDs in preference order and it selects the first one supported by current
+ compiler.
+
+ For historical reasons, some compilers (msvc) allowed setting a GNU std and
+ silently fell back to C std. This is now deprecated. Projects that support
+ both GNU and MSVC compilers should set e.g. c_std=gnu11,c11.
+
+ This is not using self.deprecated mechanism we already have for project
+ options because we want to print a warning if ALL values are deprecated, not
+ if SOME values are deprecated.
+ '''
+ def __init__(self, lang: str, all_stds: T.List[str]) -> None:
+ self.lang = lang.lower()
+ self.all_stds = ['none'] + all_stds
+ # Map a deprecated std to its replacement. e.g. gnu11 -> c11.
+ self.deprecated_stds: T.Dict[str, str] = {}
+ opt_name = 'cpp_std' if lang == 'c++' else f'{lang}_std'
+ super().__init__(opt_name, f'{lang} language standard to use', ['none'], 'none')
+
+ def set_versions(self, versions: T.List[str], gnu: bool = False, gnu_deprecated: bool = False) -> None:
+ assert all(std in self.all_stds for std in versions)
+ self.choices += versions
+ if gnu:
+ gnu_stds_map = {f'gnu{std[1:]}': std for std in versions}
+ if gnu_deprecated:
+ self.deprecated_stds.update(gnu_stds_map)
+ else:
+ self.choices += gnu_stds_map.keys()
+
+ def validate_value(self, value: T.Union[str, T.List[str]]) -> str:
+ try:
+ candidates = listify_array_value(value)
+ except MesonException as e:
+ raise MesonException(f'error in option "{self.name}": {e!s}')
+ unknown = ','.join(std for std in candidates if std not in self.all_stds)
+ if unknown:
+ raise MesonException(f'Unknown option "{self.name}" value {unknown}. Possible values are {self.all_stds}.')
+ # Check first if any of the candidates are not deprecated
+ for std in candidates:
+ if std in self.choices:
+ return std
+ # Fallback to a deprecated std if any
+ for std in candidates:
+ newstd = self.deprecated_stds.get(std)
+ if newstd is not None:
+ mlog.deprecation(
+ f'None of the values {candidates} are supported by the {self.lang} compiler.\n' +
+ f'However, the deprecated {std} std currently falls back to {newstd}.\n' +
+ 'This will be an error in the future.\n' +
+ 'If the project supports both GNU and MSVC compilers, a value such as\n' +
+ '"c_std=gnu11,c11" specifies that GNU is preferred but it can safely fallback to plain c11.')
+ return newstd
+ raise MesonException(f'None of values {candidates} are supported by the {self.lang.upper()} compiler. ' +
+ f'Possible values for option "{self.name}" are {self.choices}')
+
+
+class BuiltinOption(T.Generic[_T, _U]):
+
+ """Class for a builtin option type.
+
+ There are some cases that are not fully supported yet.
+ """
+
+ def __init__(self, opt_type: T.Type[_U], description: str, default: T.Any, yielding: bool = True, *,
+ choices: T.Any = None, readonly: bool = False):
+ self.opt_type = opt_type
+ self.description = description
+ self.default = default
+ self.choices = choices
+ self.yielding = yielding
+ self.readonly = readonly
+
+ 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)
+ keywords = {'yielding': self.yielding, 'value': value}
+ if self.choices:
+ keywords['choices'] = self.choices
+ o = self.opt_type(name.name, self.description, **keywords)
+ o.readonly = self.readonly
+ return o
+
+ def _argparse_action(self) -> T.Optional[str]:
+ # If the type is a boolean, the presence of the argument in --foo form
+ # is to enable it. Disabling happens by using -Dfoo=false, which is
+ # parsed under `args.projectoptions` and does not hit this codepath.
+ if isinstance(self.default, bool):
+ return 'store_true'
+ return None
+
+ def _argparse_choices(self) -> T.Any:
+ if self.opt_type is UserBooleanOption:
+ return [True, False]
+ elif self.opt_type is UserFeatureOption:
+ return UserFeatureOption.static_choices
+ return self.choices
+
+ @staticmethod
+ def argparse_name_to_arg(name: str) -> str:
+ if name == 'warning_level':
+ return '--warnlevel'
+ else:
+ return '--' + name.replace('_', '-')
+
+ 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]
+ except KeyError:
+ pass
+ return self.default
+
+ def add_to_argparse(self, name: str, parser: argparse.ArgumentParser, help_suffix: str) -> None:
+ kwargs = OrderedDict()
+
+ c = self._argparse_choices()
+ b = self._argparse_action()
+ h = self.description
+ if not b:
+ h = '{} (default: {}).'.format(h.rstrip('.'), self.prefixed_default(name))
+ else:
+ kwargs['action'] = b
+ if c and not b:
+ kwargs['choices'] = c
+ kwargs['default'] = argparse.SUPPRESS
+ kwargs['dest'] = 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
+# Also update mesonlib._BUILTIN_NAMES. See the comment there for why this is required.
+# Please also update completion scripts in $MESONSRC/data/shell-completions/
+BUILTIN_DIR_OPTIONS: T.Dict['OptionKey', 'BuiltinOption'] = OrderedDict([
+ (OptionKey('prefix'), BuiltinOption(UserStringOption, 'Installation prefix', default_prefix())),
+ (OptionKey('bindir'), BuiltinOption(UserStringOption, 'Executable directory', 'bin')),
+ (OptionKey('datadir'), BuiltinOption(UserStringOption, 'Data file directory', default_datadir())),
+ (OptionKey('includedir'), BuiltinOption(UserStringOption, 'Header file directory', default_includedir())),
+ (OptionKey('infodir'), BuiltinOption(UserStringOption, 'Info page directory', default_infodir())),
+ (OptionKey('libdir'), BuiltinOption(UserStringOption, 'Library directory', default_libdir())),
+ (OptionKey('licensedir'), BuiltinOption(UserStringOption, 'Licenses directory', '')),
+ (OptionKey('libexecdir'), BuiltinOption(UserStringOption, 'Library executable directory', default_libexecdir())),
+ (OptionKey('localedir'), BuiltinOption(UserStringOption, 'Locale data directory', default_localedir())),
+ (OptionKey('localstatedir'), BuiltinOption(UserStringOption, 'Localstate data directory', 'var')),
+ (OptionKey('mandir'), BuiltinOption(UserStringOption, 'Manual page directory', default_mandir())),
+ (OptionKey('sbindir'), BuiltinOption(UserStringOption, 'System executable directory', default_sbindir())),
+ (OptionKey('sharedstatedir'), BuiltinOption(UserStringOption, 'Architecture-independent data directory', 'com')),
+ (OptionKey('sysconfdir'), BuiltinOption(UserStringOption, 'Sysconf data directory', default_sysconfdir())),
+])
+
+BUILTIN_CORE_OPTIONS: T.Dict['OptionKey', 'BuiltinOption'] = OrderedDict([
+ (OptionKey('auto_features'), BuiltinOption(UserFeatureOption, "Override value of all 'auto' features", 'auto')),
+ (OptionKey('backend'), BuiltinOption(UserComboOption, 'Backend to use', 'ninja', choices=backendlist,
+ readonly=True)),
+ (OptionKey('genvslite'),
+ BuiltinOption(
+ UserComboOption,
+ 'Setup multiple buildtype-suffixed ninja-backend build directories, '
+ 'and a [builddir]_vs containing a Visual Studio meta-backend with multiple configurations that calls into them',
+ 'vs2022',
+ choices=genvslitelist)
+ ),
+ (OptionKey('buildtype'), BuiltinOption(UserComboOption, 'Build type to use', 'debug',
+ choices=buildtypelist)),
+ (OptionKey('debug'), BuiltinOption(UserBooleanOption, 'Enable debug symbols and other information', 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=['plain', '0', 'g', '1', '2', '3', 's'])),
+ (OptionKey('prefer_static'), BuiltinOption(UserBooleanOption, 'Whether to try static linking before shared linking', False)),
+ (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', 'everything'], 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', 'nopromote'])),
+ (OptionKey('force_fallback_for'), BuiltinOption(UserArrayOption, 'Force fallback for those subprojects', [])),
+ (OptionKey('vsenv'), BuiltinOption(UserBooleanOption, 'Activate Visual Studio environment', False, readonly=True)),
+
+ # Pkgconfig module
+ (OptionKey('relocatable', module='pkgconfig'),
+ BuiltinOption(UserBooleanOption, 'Generate pkgconfig files as relocatable', False)),
+
+ # Python module
+ (OptionKey('bytecompile', module='python'),
+ BuiltinOption(UserIntegerOption, 'Whether to compile bytecode', (-1, 2, 0))),
+ (OptionKey('install_env', module='python'),
+ BuiltinOption(UserComboOption, 'Which python environment to install to', 'prefix', choices=['auto', 'prefix', 'system', 'venv'])),
+ (OptionKey('platlibdir', module='python'),
+ BuiltinOption(UserStringOption, 'Directory for site-specific, platform-specific files.', '')),
+ (OptionKey('purelibdir', module='python'),
+ BuiltinOption(UserStringOption, 'Directory for site-specific, non-platform-specific files.', '')),
+ (OptionKey('allow_limited_api', module='python'),
+ BuiltinOption(UserBooleanOption, 'Whether to allow use of the Python Limited API', True)),
+])
+
+BUILTIN_OPTIONS = OrderedDict(chain(BUILTIN_DIR_OPTIONS.items(), BUILTIN_CORE_OPTIONS.items()))
+
+BUILTIN_OPTIONS_PER_MACHINE: T.Dict['OptionKey', 'BuiltinOption'] = 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: 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'},
+ OptionKey('platlibdir', module='python'): {},
+ OptionKey('purelibdir', module='python'): {},
+}
+
+
+class OptionStore:
+ def __init__(self):
+ # This class will hold all options for a given build directory
+ self.dummy = None
diff --git a/run_project_tests.py b/run_project_tests.py
index 23561d9..974273f 100755
--- a/run_project_tests.py
+++ b/run_project_tests.py
@@ -41,7 +41,8 @@ from mesonbuild.compilers import compiler_from_language
from mesonbuild.build import ConfigurationData
from mesonbuild.mesonlib import MachineChoice, Popen_safe, TemporaryDirectoryWinProof, setup_vsenv
from mesonbuild.mlog import blue, bold, cyan, green, red, yellow, normal_green
-from mesonbuild.coredata import backendlist, version as meson_version
+from mesonbuild.coredata import version as meson_version
+from mesonbuild.options import backendlist
from mesonbuild.modules.python import PythonExternalProgram
from run_tests import (
get_fake_options, run_configure, get_meson_script, get_backend_commands,
diff --git a/run_tests.py b/run_tests.py
index 6d33dd9..63eb62c 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -33,7 +33,8 @@ from mesonbuild import mesonmain
from mesonbuild import mtest
from mesonbuild import mlog
from mesonbuild.environment import Environment, detect_ninja, detect_machine_info
-from mesonbuild.coredata import backendlist, version as meson_version
+from mesonbuild.coredata import version as meson_version
+from mesonbuild.options import backendlist
from mesonbuild.mesonlib import OptionKey, setup_vsenv
if T.TYPE_CHECKING:
diff --git a/test cases/unit/116 empty project/expected_mods.json b/test cases/unit/116 empty project/expected_mods.json
index 7463bcb..19f56a5 100644
--- a/test cases/unit/116 empty project/expected_mods.json
+++ b/test cases/unit/116 empty project/expected_mods.json
@@ -225,6 +225,7 @@
"mesonbuild.mparser",
"mesonbuild.msetup",
"mesonbuild.optinterpreter",
+ "mesonbuild.options",
"mesonbuild.programs",
"mesonbuild.scripts",
"mesonbuild.scripts.meson_exe",
@@ -237,6 +238,6 @@
"mesonbuild.wrap",
"mesonbuild.wrap.wrap"
],
- "count": 68
+ "count": 69
}
}
diff --git a/unittests/datatests.py b/unittests/datatests.py
index b14bbac..19664e3 100644
--- a/unittests/datatests.py
+++ b/unittests/datatests.py
@@ -14,6 +14,7 @@ import mesonbuild.dependencies.factory
import mesonbuild.envconfig
import mesonbuild.environment
import mesonbuild.coredata
+import mesonbuild.options
import mesonbuild.modules.gnome
from mesonbuild.interpreter import Interpreter
from mesonbuild.ast import AstInterpreter
@@ -139,8 +140,8 @@ class DataTests(unittest.TestCase):
found_entries |= options
self.assertEqual(found_entries, {
- *(str(k.evolve(module=None)) for k in mesonbuild.coredata.BUILTIN_OPTIONS),
- *(str(k.evolve(module=None)) for k in mesonbuild.coredata.BUILTIN_OPTIONS_PER_MACHINE),
+ *(str(k.evolve(module=None)) for k in mesonbuild.options.BUILTIN_OPTIONS),
+ *(str(k.evolve(module=None)) for k in mesonbuild.options.BUILTIN_OPTIONS_PER_MACHINE),
})
# Check that `buildtype` table inside `Core options` matches how
diff --git a/unittests/platformagnostictests.py b/unittests/platformagnostictests.py
index ffc4b47..33a789b 100644
--- a/unittests/platformagnostictests.py
+++ b/unittests/platformagnostictests.py
@@ -274,7 +274,7 @@ class PlatformAgnosticTests(BasePlatformTests):
expected = json.load(f)['meson']['modules']
self.assertEqual(data['modules'], expected)
- self.assertEqual(data['count'], 68)
+ self.assertEqual(data['count'], 69)
def test_meson_package_cache_dir(self):
# Copy testdir into temporary directory to not pollute meson source tree.