aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mesonbuild/ast/introspection.py4
-rw-r--r--mesonbuild/backend/ninjabackend.py3
-rw-r--r--mesonbuild/backend/vs2010backend.py3
-rw-r--r--mesonbuild/build.py4
-rw-r--r--mesonbuild/cmake/interpreter.py3
-rw-r--r--mesonbuild/compilers/c.py3
-rw-r--r--mesonbuild/compilers/compilers.py3
-rw-r--r--mesonbuild/compilers/cpp.py4
-rw-r--r--mesonbuild/compilers/cuda.py6
-rw-r--r--mesonbuild/compilers/fortran.py4
-rw-r--r--mesonbuild/compilers/mixins/emscripten.py5
-rw-r--r--mesonbuild/compilers/rust.py6
-rw-r--r--mesonbuild/coredata.py198
-rw-r--r--mesonbuild/environment.py24
-rw-r--r--mesonbuild/interpreter.py2
-rw-r--r--mesonbuild/mconf.py3
-rw-r--r--mesonbuild/mesonlib.py197
-rwxr-xr-xrun_tests.py3
-rwxr-xr-xrun_unittests.py3
19 files changed, 240 insertions, 238 deletions
diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py
index 030ca8b..83b6843 100644
--- a/mesonbuild/ast/introspection.py
+++ b/mesonbuild/ast/introspection.py
@@ -19,7 +19,7 @@ from .interpreter import AstInterpreter
from .visitor import AstVisitor
from .. import compilers, environment, mesonlib, optinterpreter
from .. import coredata as cdata
-from ..mesonlib import MachineChoice
+from ..mesonlib import MachineChoice, OptionKey
from ..interpreterbase import InvalidArguments, TYPE_nvar
from ..build import BuildTarget, Executable, Jar, SharedLibrary, SharedModule, StaticLibrary
from ..mparser import BaseNode, ArithmeticNode, ArrayNode, ElementaryNode, IdNode, FunctionNode, StringNode
@@ -65,7 +65,7 @@ class IntrospectionInterpreter(AstInterpreter):
self.coredata = self.environment.get_coredata()
self.option_file = os.path.join(self.source_root, self.subdir, 'meson_options.txt')
self.backend = backend
- self.default_options = {cdata.OptionKey('backend'): self.backend}
+ self.default_options = {OptionKey('backend'): self.backend}
self.project_data = {} # type: T.Dict[str, T.Any]
self.targets = [] # type: T.List[T.Dict[str, T.Any]]
self.dependencies = [] # type: T.List[T.Dict[str, T.Any]]
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index 39e0645..a4cdefd 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -43,11 +43,10 @@ from ..mesonlib import (
File, LibType, MachineChoice, MesonException, OrderedSet, PerMachine,
ProgressBar, quote_arg, unholder,
)
-from ..mesonlib import get_compiler_for_source, has_path_sep
+from ..mesonlib import get_compiler_for_source, has_path_sep, OptionKey
from .backends import CleanTrees
from ..build import InvalidArguments
from ..interpreter import Interpreter
-from ..coredata import OptionKey
if T.TYPE_CHECKING:
from ..linkers import StaticLinker
diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py
index f893e8f..8af83e6 100644
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -28,10 +28,9 @@ from .. import mlog
from .. import compilers
from ..interpreter import Interpreter
from ..mesonlib import (
- MesonException, File, python_command, replace_if_different
+ MesonException, File, python_command, replace_if_different, OptionKey,
)
from ..environment import Environment, build_filename
-from ..coredata import OptionKey
def autodetect_vs_version(build: T.Optional[build.Build], interpreter: T.Optional[Interpreter]):
vs_version = os.getenv('VisualStudioVersion', None)
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 3b7e10d..d61ca73 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -28,7 +28,8 @@ from . import mlog
from .mesonlib import (
File, MesonException, MachineChoice, PerMachine, OrderedSet, listify,
extract_as_list, typeslistify, stringlistify, classify_unity_sources,
- get_filenames_templates_dict, substitute_values, has_path_sep, unholder
+ get_filenames_templates_dict, substitute_values, has_path_sep, unholder,
+ OptionKey,
)
from .compilers import (
Compiler, all_languages, is_object, clink_langs, sort_clink, lang_suffixes,
@@ -36,7 +37,6 @@ from .compilers import (
)
from .linkers import StaticLinker
from .interpreterbase import FeatureNew
-from .coredata import OptionKey
if T.TYPE_CHECKING:
from .coredata import KeyedOptionDictType, OptionDictType
diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py
index 6125f15..5931fd1 100644
--- a/mesonbuild/cmake/interpreter.py
+++ b/mesonbuild/cmake/interpreter.py
@@ -22,7 +22,7 @@ from .executor import CMakeExecutor
from .toolchain import CMakeToolchain, CMakeExecScope
from .traceparser import CMakeTraceParser, CMakeGeneratorTarget
from .. import mlog, mesonlib
-from ..mesonlib import MachineChoice, OrderedSet, version_compare, path_is_in_root, relative_to_if_possible
+from ..mesonlib import MachineChoice, OrderedSet, version_compare, path_is_in_root, relative_to_if_possible, OptionKey
from ..mesondata import mesondata
from ..compilers.compilers import lang_suffixes, header_suffixes, obj_suffixes, lib_suffixes, is_header
from enum import Enum
@@ -31,7 +31,6 @@ from pathlib import Path
import typing as T
import re
from os import environ
-from ..coredata import OptionKey
from ..mparser import (
Token,
diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py
index fe5f465..311e65a 100644
--- a/mesonbuild/compilers/c.py
+++ b/mesonbuild/compilers/c.py
@@ -16,7 +16,7 @@ import os.path
import typing as T
from .. import coredata
-from ..mesonlib import MachineChoice, MesonException, mlog, version_compare
+from ..mesonlib import MachineChoice, MesonException, mlog, version_compare, OptionKey
from ..linkers import LinkerEnvVarsMixin
from .c_function_attributes import C_FUNC_ATTRIBUTES
from .mixins.clike import CLikeCompiler
@@ -37,7 +37,6 @@ from .compilers import (
msvc_winlibs,
Compiler,
)
-from ..coredata import OptionKey
if T.TYPE_CHECKING:
from ..coredata import KeyedOptionDictType
diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py
index acf3823..40bb9e6 100644
--- a/mesonbuild/compilers/compilers.py
+++ b/mesonbuild/compilers/compilers.py
@@ -25,14 +25,13 @@ from .. import mesonlib
from ..linkers import LinkerEnvVarsMixin
from ..mesonlib import (
EnvironmentException, MachineChoice, MesonException,
- Popen_safe, split_args, LibType, TemporaryDirectoryWinProof
+ Popen_safe, split_args, LibType, TemporaryDirectoryWinProof, OptionKey,
)
from ..envconfig import (
get_env_var
)
from ..arglist import CompilerArgs
-from ..coredata import OptionKey
if T.TYPE_CHECKING:
from ..build import BuildTarget
diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py
index 578e362..b94beb6 100644
--- a/mesonbuild/compilers/cpp.py
+++ b/mesonbuild/compilers/cpp.py
@@ -19,7 +19,7 @@ import typing as T
from .. import coredata
from .. import mlog
-from ..mesonlib import MesonException, MachineChoice, version_compare
+from ..mesonlib import MesonException, MachineChoice, version_compare, OptionKey
from ..linkers import LinkerEnvVarsMixin
from .compilers import (
@@ -41,8 +41,6 @@ from .mixins.elbrus import ElbrusCompiler
from .mixins.pgi import PGICompiler
from .mixins.emscripten import EmscriptenMixin
-from ..coredata import OptionKey
-
if T.TYPE_CHECKING:
from ..coredata import KeyedOptionDictType
from ..dependencies import Dependency, ExternalProgram
diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py
index da1e002..7fa3e4f 100644
--- a/mesonbuild/compilers/cuda.py
+++ b/mesonbuild/compilers/cuda.py
@@ -17,9 +17,11 @@ import os.path
import typing as T
from .. import coredata
-from ..coredata import OptionKey
from .. import mlog
-from ..mesonlib import EnvironmentException, MachineChoice, Popen_safe, OptionOverrideProxy, is_windows, LibType
+from ..mesonlib import (
+ EnvironmentException, MachineChoice, Popen_safe, OptionOverrideProxy,
+ is_windows, LibType, OptionKey,
+)
from .compilers import (Compiler, cuda_buildtype_args, cuda_optimization_args,
cuda_debug_args)
diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py
index edd7911..d65d585 100644
--- a/mesonbuild/compilers/fortran.py
+++ b/mesonbuild/compilers/fortran.py
@@ -17,7 +17,6 @@ import typing as T
import subprocess, os
from .. import coredata
-from ..coredata import OptionKey
from .compilers import (
clike_debug_args,
Compiler,
@@ -32,7 +31,8 @@ from .mixins.elbrus import ElbrusCompiler
from .mixins.pgi import PGICompiler
from mesonbuild.mesonlib import (
- version_compare, EnvironmentException, MesonException, MachineChoice, LibType
+ version_compare, EnvironmentException, MesonException, MachineChoice,
+ LibType, OptionKey,
)
if T.TYPE_CHECKING:
diff --git a/mesonbuild/compilers/mixins/emscripten.py b/mesonbuild/compilers/mixins/emscripten.py
index aacea15..b480de3 100644
--- a/mesonbuild/compilers/mixins/emscripten.py
+++ b/mesonbuild/compilers/mixins/emscripten.py
@@ -18,6 +18,7 @@ import os.path
import typing as T
from ... import coredata
+from ...mesonlib import OptionKey
if T.TYPE_CHECKING:
from ...environment import Environment
@@ -50,14 +51,14 @@ class EmscriptenMixin(Compiler):
def thread_link_flags(self, env: 'Environment') -> T.List[str]:
args = ['-s', 'USE_PTHREADS=1']
- count: int = env.coredata.compiler_options[coredata.OptionKey('thread_count', lang=self.language, machine=self.for_machine)].value # type: ignore
+ count: int = env.coredata.compiler_options[OptionKey('thread_count', lang=self.language, machine=self.for_machine)].value # type: ignore
if count:
args.extend(['-s', 'PTHREAD_POOL_SIZE={}'.format(count)])
return args
def get_options(self) -> 'coredata.KeyedOptionDictType':
opts = super().get_options()
- key = coredata.OptionKey('thread_count', machine=self.for_machine, lang=self.language)
+ key = OptionKey('thread_count', machine=self.for_machine, lang=self.language)
opts.update({
key: coredata.UserIntegerOption(
'Number of threads to use in web assembly, set to 0 to disable',
diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py
index cf1af0a..8a1acc7 100644
--- a/mesonbuild/compilers/rust.py
+++ b/mesonbuild/compilers/rust.py
@@ -17,8 +17,10 @@ import textwrap
import typing as T
from .. import coredata
-from ..coredata import OptionKey
-from ..mesonlib import EnvironmentException, MachineChoice, MesonException, Popen_safe
+from ..mesonlib import (
+ EnvironmentException, MachineChoice, MesonException, Popen_safe,
+ OptionKey,
+)
from .compilers import Compiler, rust_buildtype_args, clike_debug_args
if T.TYPE_CHECKING:
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index 97f5fff..f09c398 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -20,7 +20,8 @@ from pathlib import PurePath
from collections import OrderedDict, defaultdict
from .mesonlib import (
MesonException, EnvironmentException, MachineChoice, PerMachine,
- default_libdir, default_libexecdir, default_prefix, split_args
+ default_libdir, default_libexecdir, default_prefix, split_args,
+ OptionKey,
)
from .wrap import WrapMode
import ast
@@ -49,201 +50,6 @@ default_yielding = False
_T = T.TypeVar('_T')
-class OptionType(enum.Enum):
-
- """Enum used to specify what kind of argument a thing is."""
-
- BUILTIN = 0
- BASE = 1
- COMPILER = 2
- PROJECT = 3
- BACKEND = 4
-
-
-def classify_argument(key: 'OptionKey') -> OptionType:
- """Classify arguments into groups so we know which dict to assign them to."""
-
- from .compilers import base_options
- all_builtins = set(BUILTIN_OPTIONS) | set(BUILTIN_OPTIONS_PER_MACHINE) | set(builtin_dir_noprefix_options)
-
- if key.name in base_options:
- assert key.machine is MachineChoice.HOST, str(key)
- return OptionType.BASE
- elif key.lang is not None:
- return OptionType.COMPILER
- elif key.name in all_builtins:
- return OptionType.BUILTIN
- elif key.name.startswith('backend_'):
- assert key.machine is MachineChoice.HOST, str(key)
- return OptionType.BACKEND
- else:
- assert key.machine is MachineChoice.HOST, str(key)
- return OptionType.PROJECT
-
-
-class OptionKey:
-
- """Represents an option key in the various option dictionaries.
-
- This provides a flexible, powerful way to map option names from their
- external form (things like subproject:build.option) to something that
- internally easier to reason about and produce.
- """
-
- __slots__ = ['name', 'subproject', 'machine', 'lang', '_hash', 'type']
-
- name: str
- subproject: str
- machine: MachineChoice
- lang: T.Optional[str]
- _hash: int
- type: OptionType
-
- def __init__(self, name: str, subproject: str = '',
- machine: MachineChoice = MachineChoice.HOST,
- lang: T.Optional[str] = None, _type: T.Optional[OptionType] = None):
- # the _type option to the constructor is kinda private. We want to be
- # able tos ave the state and avoid the lookup function when
- # pickling/unpickling, but we need to be able to calculate it when
- # constructing a new OptionKey
- object.__setattr__(self, 'name', name)
- object.__setattr__(self, 'subproject', subproject)
- object.__setattr__(self, 'machine', machine)
- object.__setattr__(self, 'lang', lang)
- object.__setattr__(self, '_hash', hash((name, subproject, machine, lang)))
- if _type is None:
- _type = classify_argument(self)
- object.__setattr__(self, 'type', _type)
-
- def __setattr__(self, key: str, value: T.Any) -> None:
- raise AttributeError('OptionKey instances do not support mutation.')
-
- def __getstate__(self) -> T.Dict[str, T.Any]:
- return {
- 'name': self.name,
- 'subproject': self.subproject,
- 'machine': self.machine,
- 'lang': self.lang,
- '_type': self.type,
- }
-
- def __setstate__(self, state: T.Dict[str, T.Any]) -> None:
- """De-serialize the state of a pickle.
-
- This is very clever. __init__ is not a constructor, it's an
- initializer, therefore it's safe to call more than once. We create a
- state in the custom __getstate__ method, which is valid to pass
- unsplatted to the initializer.
- """
- self.__init__(**state)
-
- def __hash__(self) -> int:
- return self._hash
-
- def __eq__(self, other: object) -> bool:
- if isinstance(other, OptionKey):
- return (
- self.name == other.name and
- self.subproject == other.subproject and
- self.machine is other.machine and
- self.lang == other.lang)
- return NotImplemented
-
- def __str__(self) -> str:
- out = self.name
- if self.lang:
- out = f'{self.lang}_{out}'
- if self.machine is MachineChoice.BUILD:
- out = f'build.{out}'
- if self.subproject:
- out = f'{self.subproject}:{out}'
- return out
-
- def __repr__(self) -> str:
- return f'OptionKey({repr(self.name)}, {repr(self.subproject)}, {repr(self.machine)}, {repr(self.lang)})'
-
- @classmethod
- def from_string(cls, raw: str) -> 'OptionKey':
- """Parse the raw command line format into a three part tuple.
-
- This takes strings like `mysubproject:build.myoption` and Creates an
- OptionKey out of them.
- """
-
- try:
- subproject, raw2 = raw.split(':')
- except ValueError:
- subproject, raw2 = '', raw
-
- if raw2.startswith('build.'):
- raw3 = raw2.lstrip('build.')
- for_machine = MachineChoice.BUILD
- else:
- raw3 = raw2
- for_machine = MachineChoice.HOST
-
- from .compilers import all_languages
- if any(raw3.startswith(f'{l}_') for l in all_languages):
- lang, opt = raw3.split('_', 1)
- else:
- lang, opt = None, raw3
- assert ':' not in opt
- assert 'build.' not in opt
-
- return cls(opt, subproject, for_machine, lang)
-
- def evolve(self, name: T.Optional[str] = None, subproject: T.Optional[str] = None,
- machine: T.Optional[MachineChoice] = None, lang: T.Optional[str] = '') -> 'OptionKey':
- """Create a new copy of this key, but with alterted members.
-
- For example:
- >>> a = OptionKey('foo', '', MachineChoice.Host)
- >>> b = OptionKey('foo', 'bar', MachineChoice.Host)
- >>> b == a.evolve(subproject='bar')
- True
- """
- # We have to be a little clever with lang here, because lang is valid
- # as None, for non-compiler options
- return OptionKey(
- name if name is not None else self.name,
- subproject if subproject is not None else self.subproject,
- machine if machine is not None else self.machine,
- lang if lang != '' else self.lang,
- )
-
- def as_root(self) -> 'OptionKey':
- """Convenience method for key.evolve(subproject='')."""
- return self.evolve(subproject='')
-
- def as_build(self) -> 'OptionKey':
- """Convenience method for key.evolve(machine=MachinceChoice.BUILD)."""
- return self.evolve(machine=MachineChoice.BUILD)
-
- def as_host(self) -> 'OptionKey':
- """Convenience method for key.evolve(machine=MachinceChoice.HOST)."""
- return self.evolve(machine=MachineChoice.HOST)
-
- def is_backend(self) -> bool:
- """Convenience method to check if this is a backend option."""
- return self.type is OptionType.BACKEND
-
- def is_builtin(self) -> bool:
- """Convenience method to check if this is a builtin option."""
- return self.type is OptionType.BUILTIN
-
- def is_compiler(self) -> bool:
- """Convenience method to check if this is a builtin option."""
- return self.type is OptionType.COMPILER
-
- def is_project(self) -> bool:
- """Convenience method to check if this is a project option."""
- return self.type is OptionType.PROJECT
-
- def is_base(self) -> bool:
- """Convenience method to check if this is a base option."""
- return self.type is OptionType.BASE
-
-
class MesonVersionMismatchException(MesonException):
'''Build directory generated with Meson version is incompatible with current version'''
def __init__(self, old_version: str, current_version: str) -> None:
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index 3120279..3ea078a 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -23,7 +23,7 @@ from .linkers import ArLinker, ArmarLinker, VisualStudioLinker, DLinker, CcrxLin
from . import mesonlib
from .mesonlib import (
MesonException, EnvironmentException, MachineChoice, Popen_safe,
- PerMachineDefaultable, PerThreeMachineDefaultable, split_args, quote_arg
+ PerMachineDefaultable, PerThreeMachineDefaultable, split_args, quote_arg, OptionKey
)
from . import mlog
@@ -605,7 +605,7 @@ class Environment:
#
# Note that order matters because of 'buildtype', if it is after
# 'optimization' and 'debug' keys, it override them.
- self.options: T.MutableMapping[coredata.OptionKey, str] = collections.OrderedDict()
+ self.options: T.MutableMapping[OptionKey, str] = collections.OrderedDict()
## Read in native file(s) to override build machine configuration
@@ -655,9 +655,9 @@ class Environment:
# Warn if the user is using two different ways of setting build-type
# options that override each other
- bt = coredata.OptionKey('buildtype')
- db = coredata.OptionKey('debug')
- op = coredata.OptionKey('optimization')
+ bt = OptionKey('buildtype')
+ db = OptionKey('debug')
+ op = OptionKey('optimization')
if bt in self.options and (db in self.options or op in self.options):
mlog.warning('Recommend using either -Dbuildtype or -Doptimization + -Ddebug. '
'Using both is redundant since they override each other. '
@@ -723,7 +723,7 @@ class Environment:
if paths:
mlog.deprecation('The [paths] section is deprecated, use the [built-in options] section instead.')
for k, v in paths.items():
- self.options[coredata.OptionKey.from_string(k).evolve(machine=machine)] = v
+ self.options[OptionKey.from_string(k).evolve(machine=machine)] = v
deprecated_properties = set()
for lang in compilers.all_languages:
deprecated_properties.add(lang + '_args')
@@ -731,7 +731,7 @@ class Environment:
for k, v in properties.properties.copy().items():
if k in deprecated_properties:
mlog.deprecation('{} in the [properties] section of the machine file is deprecated, use the [built-in options] section.'.format(k))
- self.options[coredata.OptionKey.from_string(k).evolve(machine=machine)] = v
+ self.options[OptionKey.from_string(k).evolve(machine=machine)] = v
del properties.properties[k]
for section, values in config.items():
if ':' in section:
@@ -740,12 +740,12 @@ class Environment:
subproject = ''
if section == 'built-in options':
for k, v in values.items():
- key = coredata.OptionKey.from_string(k).evolve(subproject=subproject, machine=machine)
+ key = OptionKey.from_string(k).evolve(subproject=subproject, machine=machine)
self.options[key] = v
elif section == 'project options':
for k, v in values.items():
# Project options are always for the machine machine
- key = coredata.OptionKey.from_string(k).evolve(subproject=subproject)
+ key = OptionKey.from_string(k).evolve(subproject=subproject)
self.options[key] = v
def set_default_options_from_env(self) -> None:
@@ -763,7 +763,7 @@ class Environment:
# FIXME: We should remember if we took the value from env to warn
# if it changes on future invocations.
if self.first_invocation:
- key = coredata.OptionKey(keyname, machine=for_machine)
+ key = OptionKey(keyname, machine=for_machine)
self.options.setdefault(key, p_list)
def create_new_coredata(self, options: 'argparse.Namespace') -> None:
@@ -938,7 +938,7 @@ class Environment:
elif isinstance(comp_class.LINKER_PREFIX, list):
check_args = comp_class.LINKER_PREFIX + ['/logo'] + comp_class.LINKER_PREFIX + ['--version']
- check_args += self.coredata.compiler_options[coredata.OptionKey('args', lang=comp_class.language, machine=for_machine)].value
+ check_args += self.coredata.compiler_options[OptionKey('args', lang=comp_class.language, machine=for_machine)].value
override = [] # type: T.List[str]
value = self.lookup_binary_entry(for_machine, comp_class.language + '_ld')
@@ -1004,7 +1004,7 @@ class Environment:
"""
self.coredata.add_lang_args(comp_class.language, comp_class, for_machine, self)
extra_args = extra_args or []
- extra_args += self.coredata.compiler_options[coredata.OptionKey('args', lang=comp_class.language, machine=for_machine)].value
+ extra_args += self.coredata.compiler_options[OptionKey('args', lang=comp_class.language, machine=for_machine)].value
if isinstance(comp_class.LINKER_PREFIX, str):
check_args = [comp_class.LINKER_PREFIX + '--version'] + extra_args
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index 6aba3e7..b4336eb 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -3067,7 +3067,7 @@ external dependencies (including libraries) must go to "dependencies".''')
if v is not None:
return v
- key = coredata.OptionKey.from_string(optname)
+ key = mesonlib.OptionKey.from_string(optname)
for opts in [self.coredata.compiler_options]:
v = opts.get(key)
if v is None or v.yielding:
diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py
index c14d701..70d6806 100644
--- a/mesonbuild/mconf.py
+++ b/mesonbuild/mconf.py
@@ -21,7 +21,8 @@ from .mesonlib import MachineChoice
if T.TYPE_CHECKING:
import argparse
- from .coredata import OptionKey, UserOption
+ from .coredata import UserOption
+ from .mesonlib import OptionKey
def add_arguments(parser: 'argparse.ArgumentParser') -> None:
coredata.register_builtin_arguments(parser)
diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py
index 805e679..8252f79 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib.py
@@ -14,6 +14,7 @@
"""A library of random helper functionality."""
from pathlib import Path
+import enum
import sys
import stat
import time
@@ -1780,3 +1781,199 @@ class OptionOverrideProxy(collections.abc.MutableMapping):
def copy(self) -> 'OptionOverrideProxy':
return OptionOverrideProxy(self.overrides.copy(), self.options.copy())
+
+
+class OptionType(enum.Enum):
+
+ """Enum used to specify what kind of argument a thing is."""
+
+ BUILTIN = 0
+ BASE = 1
+ COMPILER = 2
+ PROJECT = 3
+ BACKEND = 4
+
+
+def _classify_argument(key: 'OptionKey') -> OptionType:
+ """Classify arguments into groups so we know which dict to assign them to."""
+
+ from .compilers import base_options
+ from .coredata import BUILTIN_OPTIONS, BUILTIN_OPTIONS_PER_MACHINE, builtin_dir_noprefix_options
+ all_builtins = set(BUILTIN_OPTIONS) | set(BUILTIN_OPTIONS_PER_MACHINE) | set(builtin_dir_noprefix_options)
+
+ if key.name in base_options:
+ assert key.machine is MachineChoice.HOST, str(key)
+ return OptionType.BASE
+ elif key.lang is not None:
+ return OptionType.COMPILER
+ elif key.name in all_builtins:
+ return OptionType.BUILTIN
+ elif key.name.startswith('backend_'):
+ assert key.machine is MachineChoice.HOST, str(key)
+ return OptionType.BACKEND
+ else:
+ assert key.machine is MachineChoice.HOST, str(key)
+ return OptionType.PROJECT
+
+
+class OptionKey:
+
+ """Represents an option key in the various option dictionaries.
+
+ This provides a flexible, powerful way to map option names from their
+ external form (things like subproject:build.option) to something that
+ internally easier to reason about and produce.
+ """
+
+ __slots__ = ['name', 'subproject', 'machine', 'lang', '_hash', 'type']
+
+ name: str
+ subproject: str
+ machine: MachineChoice
+ lang: T.Optional[str]
+ _hash: int
+ type: OptionType
+
+ def __init__(self, name: str, subproject: str = '',
+ machine: MachineChoice = MachineChoice.HOST,
+ lang: T.Optional[str] = None, _type: T.Optional[OptionType] = None):
+ # the _type option to the constructor is kinda private. We want to be
+ # able tos ave the state and avoid the lookup function when
+ # pickling/unpickling, but we need to be able to calculate it when
+ # constructing a new OptionKey
+ object.__setattr__(self, 'name', name)
+ object.__setattr__(self, 'subproject', subproject)
+ object.__setattr__(self, 'machine', machine)
+ object.__setattr__(self, 'lang', lang)
+ object.__setattr__(self, '_hash', hash((name, subproject, machine, lang)))
+ if _type is None:
+ _type = _classify_argument(self)
+ object.__setattr__(self, 'type', _type)
+
+ def __setattr__(self, key: str, value: T.Any) -> None:
+ raise AttributeError('OptionKey instances do not support mutation.')
+
+ def __getstate__(self) -> T.Dict[str, T.Any]:
+ return {
+ 'name': self.name,
+ 'subproject': self.subproject,
+ 'machine': self.machine,
+ 'lang': self.lang,
+ '_type': self.type,
+ }
+
+ def __setstate__(self, state: T.Dict[str, T.Any]) -> None:
+ """De-serialize the state of a pickle.
+
+ This is very clever. __init__ is not a constructor, it's an
+ initializer, therefore it's safe to call more than once. We create a
+ state in the custom __getstate__ method, which is valid to pass
+ unsplatted to the initializer.
+ """
+ self.__init__(**state)
+
+ def __hash__(self) -> int:
+ return self._hash
+
+ def __eq__(self, other: object) -> bool:
+ if isinstance(other, OptionKey):
+ return (
+ self.name == other.name and
+ self.subproject == other.subproject and
+ self.machine is other.machine and
+ self.lang == other.lang)
+ return NotImplemented
+
+ def __str__(self) -> str:
+ out = self.name
+ if self.lang:
+ out = f'{self.lang}_{out}'
+ if self.machine is MachineChoice.BUILD:
+ out = f'build.{out}'
+ if self.subproject:
+ out = f'{self.subproject}:{out}'
+ return out
+
+ def __repr__(self) -> str:
+ return f'OptionKey({repr(self.name)}, {repr(self.subproject)}, {repr(self.machine)}, {repr(self.lang)})'
+
+ @classmethod
+ def from_string(cls, raw: str) -> 'OptionKey':
+ """Parse the raw command line format into a three part tuple.
+
+ This takes strings like `mysubproject:build.myoption` and Creates an
+ OptionKey out of them.
+ """
+
+ try:
+ subproject, raw2 = raw.split(':')
+ except ValueError:
+ subproject, raw2 = '', raw
+
+ if raw2.startswith('build.'):
+ raw3 = raw2.lstrip('build.')
+ for_machine = MachineChoice.BUILD
+ else:
+ raw3 = raw2
+ for_machine = MachineChoice.HOST
+
+ from .compilers import all_languages
+ if any(raw3.startswith(f'{l}_') for l in all_languages):
+ lang, opt = raw3.split('_', 1)
+ else:
+ lang, opt = None, raw3
+ assert ':' not in opt
+ assert 'build.' not in opt
+
+ return cls(opt, subproject, for_machine, lang)
+
+ def evolve(self, name: T.Optional[str] = None, subproject: T.Optional[str] = None,
+ machine: T.Optional[MachineChoice] = None, lang: T.Optional[str] = '') -> 'OptionKey':
+ """Create a new copy of this key, but with alterted members.
+
+ For example:
+ >>> a = OptionKey('foo', '', MachineChoice.Host)
+ >>> b = OptionKey('foo', 'bar', MachineChoice.Host)
+ >>> b == a.evolve(subproject='bar')
+ True
+ """
+ # We have to be a little clever with lang here, because lang is valid
+ # as None, for non-compiler options
+ return OptionKey(
+ name if name is not None else self.name,
+ subproject if subproject is not None else self.subproject,
+ machine if machine is not None else self.machine,
+ lang if lang != '' else self.lang,
+ )
+
+ def as_root(self) -> 'OptionKey':
+ """Convenience method for key.evolve(subproject='')."""
+ return self.evolve(subproject='')
+
+ def as_build(self) -> 'OptionKey':
+ """Convenience method for key.evolve(machine=MachinceChoice.BUILD)."""
+ return self.evolve(machine=MachineChoice.BUILD)
+
+ def as_host(self) -> 'OptionKey':
+ """Convenience method for key.evolve(machine=MachinceChoice.HOST)."""
+ return self.evolve(machine=MachineChoice.HOST)
+
+ def is_backend(self) -> bool:
+ """Convenience method to check if this is a backend option."""
+ return self.type is OptionType.BACKEND
+
+ def is_builtin(self) -> bool:
+ """Convenience method to check if this is a builtin option."""
+ return self.type is OptionType.BUILTIN
+
+ def is_compiler(self) -> bool:
+ """Convenience method to check if this is a builtin option."""
+ return self.type is OptionType.COMPILER
+
+ def is_project(self) -> bool:
+ """Convenience method to check if this is a project option."""
+ return self.type is OptionType.PROJECT
+
+ def is_base(self) -> bool:
+ """Convenience method to check if this is a base option."""
+ return self.type is OptionType.BASE \ No newline at end of file
diff --git a/run_tests.py b/run_tests.py
index 15e643a..ec58ac0 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -34,7 +34,8 @@ from mesonbuild import mesonmain
from mesonbuild import mtest
from mesonbuild import mlog
from mesonbuild.environment import Environment, detect_ninja
-from mesonbuild.coredata import backendlist, version as meson_version, OptionKey
+from mesonbuild.coredata import backendlist, version as meson_version
+from mesonbuild.mesonlib import OptionKey
NINJA_1_9_OR_NEWER = False
NINJA_CMD = None
diff --git a/run_unittests.py b/run_unittests.py
index 0236909..744556e 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -59,12 +59,11 @@ from mesonbuild.mesonlib import (
quote_arg, relpath, is_linux, git, GIT
)
from mesonbuild.environment import detect_ninja
-from mesonbuild.mesonlib import MesonException, EnvironmentException
+from mesonbuild.mesonlib import MesonException, EnvironmentException, OptionKey
from mesonbuild.dependencies import PkgConfigDependency, ExternalProgram
import mesonbuild.dependencies.base
from mesonbuild.build import Target, ConfigurationData
import mesonbuild.modules.pkgconfig
-from mesonbuild.coredata import OptionKey
from mesonbuild.mtest import TAPParser, TestResult