diff options
author | Dylan Baker <dylan@pnwbakers.com> | 2020-12-03 11:37:52 -0800 |
---|---|---|
committer | Dylan Baker <dylan@pnwbakers.com> | 2021-01-04 12:20:40 -0800 |
commit | f9b19e73a5b87a2f3c8506cf19cfd5bbc468e938 (patch) | |
tree | 62a822655f89133babd261a18adc40e3cb44a0fe | |
parent | fe973d9fc45581f20fefc41fc0b8eb0066c0129d (diff) | |
download | meson-f9b19e73a5b87a2f3c8506cf19cfd5bbc468e938.zip meson-f9b19e73a5b87a2f3c8506cf19cfd5bbc468e938.tar.gz meson-f9b19e73a5b87a2f3c8506cf19cfd5bbc468e938.tar.bz2 |
move OptionKey to mesonlib
There's starting to be a lot of things including coredata that coredata
needs to itself include. putting it in mesonlib makes more sense
-rw-r--r-- | mesonbuild/ast/introspection.py | 4 | ||||
-rw-r--r-- | mesonbuild/backend/ninjabackend.py | 3 | ||||
-rw-r--r-- | mesonbuild/backend/vs2010backend.py | 3 | ||||
-rw-r--r-- | mesonbuild/build.py | 4 | ||||
-rw-r--r-- | mesonbuild/cmake/interpreter.py | 3 | ||||
-rw-r--r-- | mesonbuild/compilers/c.py | 3 | ||||
-rw-r--r-- | mesonbuild/compilers/compilers.py | 3 | ||||
-rw-r--r-- | mesonbuild/compilers/cpp.py | 4 | ||||
-rw-r--r-- | mesonbuild/compilers/cuda.py | 6 | ||||
-rw-r--r-- | mesonbuild/compilers/fortran.py | 4 | ||||
-rw-r--r-- | mesonbuild/compilers/mixins/emscripten.py | 5 | ||||
-rw-r--r-- | mesonbuild/compilers/rust.py | 6 | ||||
-rw-r--r-- | mesonbuild/coredata.py | 198 | ||||
-rw-r--r-- | mesonbuild/environment.py | 24 | ||||
-rw-r--r-- | mesonbuild/interpreter.py | 2 | ||||
-rw-r--r-- | mesonbuild/mconf.py | 3 | ||||
-rw-r--r-- | mesonbuild/mesonlib.py | 197 | ||||
-rwxr-xr-x | run_tests.py | 3 | ||||
-rwxr-xr-x | run_unittests.py | 3 |
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 |