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
pull/8080/head
Dylan Baker 4 years ago
parent fe973d9fc4
commit f9b19e73a5
  1. 4
      mesonbuild/ast/introspection.py
  2. 3
      mesonbuild/backend/ninjabackend.py
  3. 3
      mesonbuild/backend/vs2010backend.py
  4. 4
      mesonbuild/build.py
  5. 3
      mesonbuild/cmake/interpreter.py
  6. 3
      mesonbuild/compilers/c.py
  7. 3
      mesonbuild/compilers/compilers.py
  8. 4
      mesonbuild/compilers/cpp.py
  9. 6
      mesonbuild/compilers/cuda.py
  10. 4
      mesonbuild/compilers/fortran.py
  11. 5
      mesonbuild/compilers/mixins/emscripten.py
  12. 6
      mesonbuild/compilers/rust.py
  13. 198
      mesonbuild/coredata.py
  14. 24
      mesonbuild/environment.py
  15. 2
      mesonbuild/interpreter.py
  16. 3
      mesonbuild/mconf.py
  17. 197
      mesonbuild/mesonlib.py
  18. 3
      run_tests.py
  19. 3
      run_unittests.py

@ -19,7 +19,7 @@ from .interpreter import AstInterpreter
from .visitor import AstVisitor from .visitor import AstVisitor
from .. import compilers, environment, mesonlib, optinterpreter from .. import compilers, environment, mesonlib, optinterpreter
from .. import coredata as cdata from .. import coredata as cdata
from ..mesonlib import MachineChoice from ..mesonlib import MachineChoice, OptionKey
from ..interpreterbase import InvalidArguments, TYPE_nvar from ..interpreterbase import InvalidArguments, TYPE_nvar
from ..build import BuildTarget, Executable, Jar, SharedLibrary, SharedModule, StaticLibrary from ..build import BuildTarget, Executable, Jar, SharedLibrary, SharedModule, StaticLibrary
from ..mparser import BaseNode, ArithmeticNode, ArrayNode, ElementaryNode, IdNode, FunctionNode, StringNode from ..mparser import BaseNode, ArithmeticNode, ArrayNode, ElementaryNode, IdNode, FunctionNode, StringNode
@ -65,7 +65,7 @@ class IntrospectionInterpreter(AstInterpreter):
self.coredata = self.environment.get_coredata() self.coredata = self.environment.get_coredata()
self.option_file = os.path.join(self.source_root, self.subdir, 'meson_options.txt') self.option_file = os.path.join(self.source_root, self.subdir, 'meson_options.txt')
self.backend = backend 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.project_data = {} # type: T.Dict[str, T.Any]
self.targets = [] # type: T.List[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]] self.dependencies = [] # type: T.List[T.Dict[str, T.Any]]

@ -43,11 +43,10 @@ from ..mesonlib import (
File, LibType, MachineChoice, MesonException, OrderedSet, PerMachine, File, LibType, MachineChoice, MesonException, OrderedSet, PerMachine,
ProgressBar, quote_arg, unholder, 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 .backends import CleanTrees
from ..build import InvalidArguments from ..build import InvalidArguments
from ..interpreter import Interpreter from ..interpreter import Interpreter
from ..coredata import OptionKey
if T.TYPE_CHECKING: if T.TYPE_CHECKING:
from ..linkers import StaticLinker from ..linkers import StaticLinker

@ -28,10 +28,9 @@ from .. import mlog
from .. import compilers from .. import compilers
from ..interpreter import Interpreter from ..interpreter import Interpreter
from ..mesonlib import ( 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 ..environment import Environment, build_filename
from ..coredata import OptionKey
def autodetect_vs_version(build: T.Optional[build.Build], interpreter: T.Optional[Interpreter]): def autodetect_vs_version(build: T.Optional[build.Build], interpreter: T.Optional[Interpreter]):
vs_version = os.getenv('VisualStudioVersion', None) vs_version = os.getenv('VisualStudioVersion', None)

@ -28,7 +28,8 @@ from . import mlog
from .mesonlib import ( from .mesonlib import (
File, MesonException, MachineChoice, PerMachine, OrderedSet, listify, File, MesonException, MachineChoice, PerMachine, OrderedSet, listify,
extract_as_list, typeslistify, stringlistify, classify_unity_sources, 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 ( from .compilers import (
Compiler, all_languages, is_object, clink_langs, sort_clink, lang_suffixes, Compiler, all_languages, is_object, clink_langs, sort_clink, lang_suffixes,
@ -36,7 +37,6 @@ from .compilers import (
) )
from .linkers import StaticLinker from .linkers import StaticLinker
from .interpreterbase import FeatureNew from .interpreterbase import FeatureNew
from .coredata import OptionKey
if T.TYPE_CHECKING: if T.TYPE_CHECKING:
from .coredata import KeyedOptionDictType, OptionDictType from .coredata import KeyedOptionDictType, OptionDictType

@ -22,7 +22,7 @@ from .executor import CMakeExecutor
from .toolchain import CMakeToolchain, CMakeExecScope from .toolchain import CMakeToolchain, CMakeExecScope
from .traceparser import CMakeTraceParser, CMakeGeneratorTarget from .traceparser import CMakeTraceParser, CMakeGeneratorTarget
from .. import mlog, mesonlib 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 ..mesondata import mesondata
from ..compilers.compilers import lang_suffixes, header_suffixes, obj_suffixes, lib_suffixes, is_header from ..compilers.compilers import lang_suffixes, header_suffixes, obj_suffixes, lib_suffixes, is_header
from enum import Enum from enum import Enum
@ -31,7 +31,6 @@ from pathlib import Path
import typing as T import typing as T
import re import re
from os import environ from os import environ
from ..coredata import OptionKey
from ..mparser import ( from ..mparser import (
Token, Token,

@ -16,7 +16,7 @@ import os.path
import typing as T import typing as T
from .. import coredata 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 ..linkers import LinkerEnvVarsMixin
from .c_function_attributes import C_FUNC_ATTRIBUTES from .c_function_attributes import C_FUNC_ATTRIBUTES
from .mixins.clike import CLikeCompiler from .mixins.clike import CLikeCompiler
@ -37,7 +37,6 @@ from .compilers import (
msvc_winlibs, msvc_winlibs,
Compiler, Compiler,
) )
from ..coredata import OptionKey
if T.TYPE_CHECKING: if T.TYPE_CHECKING:
from ..coredata import KeyedOptionDictType from ..coredata import KeyedOptionDictType

@ -25,14 +25,13 @@ from .. import mesonlib
from ..linkers import LinkerEnvVarsMixin from ..linkers import LinkerEnvVarsMixin
from ..mesonlib import ( from ..mesonlib import (
EnvironmentException, MachineChoice, MesonException, EnvironmentException, MachineChoice, MesonException,
Popen_safe, split_args, LibType, TemporaryDirectoryWinProof Popen_safe, split_args, LibType, TemporaryDirectoryWinProof, OptionKey,
) )
from ..envconfig import ( from ..envconfig import (
get_env_var get_env_var
) )
from ..arglist import CompilerArgs from ..arglist import CompilerArgs
from ..coredata import OptionKey
if T.TYPE_CHECKING: if T.TYPE_CHECKING:
from ..build import BuildTarget from ..build import BuildTarget

@ -19,7 +19,7 @@ import typing as T
from .. import coredata from .. import coredata
from .. import mlog from .. import mlog
from ..mesonlib import MesonException, MachineChoice, version_compare from ..mesonlib import MesonException, MachineChoice, version_compare, OptionKey
from ..linkers import LinkerEnvVarsMixin from ..linkers import LinkerEnvVarsMixin
from .compilers import ( from .compilers import (
@ -41,8 +41,6 @@ from .mixins.elbrus import ElbrusCompiler
from .mixins.pgi import PGICompiler from .mixins.pgi import PGICompiler
from .mixins.emscripten import EmscriptenMixin from .mixins.emscripten import EmscriptenMixin
from ..coredata import OptionKey
if T.TYPE_CHECKING: if T.TYPE_CHECKING:
from ..coredata import KeyedOptionDictType from ..coredata import KeyedOptionDictType
from ..dependencies import Dependency, ExternalProgram from ..dependencies import Dependency, ExternalProgram

@ -17,9 +17,11 @@ import os.path
import typing as T import typing as T
from .. import coredata from .. import coredata
from ..coredata import OptionKey
from .. import mlog 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, from .compilers import (Compiler, cuda_buildtype_args, cuda_optimization_args,
cuda_debug_args) cuda_debug_args)

@ -17,7 +17,6 @@ import typing as T
import subprocess, os import subprocess, os
from .. import coredata from .. import coredata
from ..coredata import OptionKey
from .compilers import ( from .compilers import (
clike_debug_args, clike_debug_args,
Compiler, Compiler,
@ -32,7 +31,8 @@ from .mixins.elbrus import ElbrusCompiler
from .mixins.pgi import PGICompiler from .mixins.pgi import PGICompiler
from mesonbuild.mesonlib import ( from mesonbuild.mesonlib import (
version_compare, EnvironmentException, MesonException, MachineChoice, LibType version_compare, EnvironmentException, MesonException, MachineChoice,
LibType, OptionKey,
) )
if T.TYPE_CHECKING: if T.TYPE_CHECKING:

@ -18,6 +18,7 @@ import os.path
import typing as T import typing as T
from ... import coredata from ... import coredata
from ...mesonlib import OptionKey
if T.TYPE_CHECKING: if T.TYPE_CHECKING:
from ...environment import Environment from ...environment import Environment
@ -50,14 +51,14 @@ class EmscriptenMixin(Compiler):
def thread_link_flags(self, env: 'Environment') -> T.List[str]: def thread_link_flags(self, env: 'Environment') -> T.List[str]:
args = ['-s', 'USE_PTHREADS=1'] 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: if count:
args.extend(['-s', 'PTHREAD_POOL_SIZE={}'.format(count)]) args.extend(['-s', 'PTHREAD_POOL_SIZE={}'.format(count)])
return args return args
def get_options(self) -> 'coredata.KeyedOptionDictType': def get_options(self) -> 'coredata.KeyedOptionDictType':
opts = super().get_options() 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({ opts.update({
key: coredata.UserIntegerOption( key: coredata.UserIntegerOption(
'Number of threads to use in web assembly, set to 0 to disable', 'Number of threads to use in web assembly, set to 0 to disable',

@ -17,8 +17,10 @@ import textwrap
import typing as T import typing as T
from .. import coredata from .. import coredata
from ..coredata import OptionKey from ..mesonlib import (
from ..mesonlib import EnvironmentException, MachineChoice, MesonException, Popen_safe EnvironmentException, MachineChoice, MesonException, Popen_safe,
OptionKey,
)
from .compilers import Compiler, rust_buildtype_args, clike_debug_args from .compilers import Compiler, rust_buildtype_args, clike_debug_args
if T.TYPE_CHECKING: if T.TYPE_CHECKING:

@ -20,7 +20,8 @@ from pathlib import PurePath
from collections import OrderedDict, defaultdict from collections import OrderedDict, defaultdict
from .mesonlib import ( from .mesonlib import (
MesonException, EnvironmentException, MachineChoice, PerMachine, 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 from .wrap import WrapMode
import ast import ast
@ -49,201 +50,6 @@ default_yielding = False
_T = T.TypeVar('_T') _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): class MesonVersionMismatchException(MesonException):
'''Build directory generated with Meson version is incompatible with current version''' '''Build directory generated with Meson version is incompatible with current version'''
def __init__(self, old_version: str, current_version: str) -> None: def __init__(self, old_version: str, current_version: str) -> None:

@ -23,7 +23,7 @@ from .linkers import ArLinker, ArmarLinker, VisualStudioLinker, DLinker, CcrxLin
from . import mesonlib from . import mesonlib
from .mesonlib import ( from .mesonlib import (
MesonException, EnvironmentException, MachineChoice, Popen_safe, MesonException, EnvironmentException, MachineChoice, Popen_safe,
PerMachineDefaultable, PerThreeMachineDefaultable, split_args, quote_arg PerMachineDefaultable, PerThreeMachineDefaultable, split_args, quote_arg, OptionKey
) )
from . import mlog from . import mlog
@ -605,7 +605,7 @@ class Environment:
# #
# Note that order matters because of 'buildtype', if it is after # Note that order matters because of 'buildtype', if it is after
# 'optimization' and 'debug' keys, it override them. # '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 ## 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 # Warn if the user is using two different ways of setting build-type
# options that override each other # options that override each other
bt = coredata.OptionKey('buildtype') bt = OptionKey('buildtype')
db = coredata.OptionKey('debug') db = OptionKey('debug')
op = coredata.OptionKey('optimization') op = OptionKey('optimization')
if bt in self.options and (db in self.options or op in self.options): if bt in self.options and (db in self.options or op in self.options):
mlog.warning('Recommend using either -Dbuildtype or -Doptimization + -Ddebug. ' mlog.warning('Recommend using either -Dbuildtype or -Doptimization + -Ddebug. '
'Using both is redundant since they override each other. ' 'Using both is redundant since they override each other. '
@ -723,7 +723,7 @@ class Environment:
if paths: if paths:
mlog.deprecation('The [paths] section is deprecated, use the [built-in options] section instead.') mlog.deprecation('The [paths] section is deprecated, use the [built-in options] section instead.')
for k, v in paths.items(): 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() deprecated_properties = set()
for lang in compilers.all_languages: for lang in compilers.all_languages:
deprecated_properties.add(lang + '_args') deprecated_properties.add(lang + '_args')
@ -731,7 +731,7 @@ class Environment:
for k, v in properties.properties.copy().items(): for k, v in properties.properties.copy().items():
if k in deprecated_properties: 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)) 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] del properties.properties[k]
for section, values in config.items(): for section, values in config.items():
if ':' in section: if ':' in section:
@ -740,12 +740,12 @@ class Environment:
subproject = '' subproject = ''
if section == 'built-in options': if section == 'built-in options':
for k, v in values.items(): 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 self.options[key] = v
elif section == 'project options': elif section == 'project options':
for k, v in values.items(): for k, v in values.items():
# Project options are always for the machine machine # 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 self.options[key] = v
def set_default_options_from_env(self) -> None: 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 # FIXME: We should remember if we took the value from env to warn
# if it changes on future invocations. # if it changes on future invocations.
if self.first_invocation: if self.first_invocation:
key = coredata.OptionKey(keyname, machine=for_machine) key = OptionKey(keyname, machine=for_machine)
self.options.setdefault(key, p_list) self.options.setdefault(key, p_list)
def create_new_coredata(self, options: 'argparse.Namespace') -> None: def create_new_coredata(self, options: 'argparse.Namespace') -> None:
@ -938,7 +938,7 @@ class Environment:
elif isinstance(comp_class.LINKER_PREFIX, list): elif isinstance(comp_class.LINKER_PREFIX, list):
check_args = comp_class.LINKER_PREFIX + ['/logo'] + comp_class.LINKER_PREFIX + ['--version'] 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] override = [] # type: T.List[str]
value = self.lookup_binary_entry(for_machine, comp_class.language + '_ld') 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) self.coredata.add_lang_args(comp_class.language, comp_class, for_machine, self)
extra_args = extra_args or [] 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): if isinstance(comp_class.LINKER_PREFIX, str):
check_args = [comp_class.LINKER_PREFIX + '--version'] + extra_args check_args = [comp_class.LINKER_PREFIX + '--version'] + extra_args

@ -3067,7 +3067,7 @@ external dependencies (including libraries) must go to "dependencies".''')
if v is not None: if v is not None:
return v return v
key = coredata.OptionKey.from_string(optname) key = mesonlib.OptionKey.from_string(optname)
for opts in [self.coredata.compiler_options]: for opts in [self.coredata.compiler_options]:
v = opts.get(key) v = opts.get(key)
if v is None or v.yielding: if v is None or v.yielding:

@ -21,7 +21,8 @@ from .mesonlib import MachineChoice
if T.TYPE_CHECKING: if T.TYPE_CHECKING:
import argparse import argparse
from .coredata import OptionKey, UserOption from .coredata import UserOption
from .mesonlib import OptionKey
def add_arguments(parser: 'argparse.ArgumentParser') -> None: def add_arguments(parser: 'argparse.ArgumentParser') -> None:
coredata.register_builtin_arguments(parser) coredata.register_builtin_arguments(parser)

@ -14,6 +14,7 @@
"""A library of random helper functionality.""" """A library of random helper functionality."""
from pathlib import Path from pathlib import Path
import enum
import sys import sys
import stat import stat
import time import time
@ -1780,3 +1781,199 @@ class OptionOverrideProxy(collections.abc.MutableMapping):
def copy(self) -> 'OptionOverrideProxy': def copy(self) -> 'OptionOverrideProxy':
return OptionOverrideProxy(self.overrides.copy(), self.options.copy()) 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

@ -34,7 +34,8 @@ from mesonbuild import mesonmain
from mesonbuild import mtest from mesonbuild import mtest
from mesonbuild import mlog from mesonbuild import mlog
from mesonbuild.environment import Environment, detect_ninja 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_1_9_OR_NEWER = False
NINJA_CMD = None NINJA_CMD = None

@ -59,12 +59,11 @@ from mesonbuild.mesonlib import (
quote_arg, relpath, is_linux, git, GIT quote_arg, relpath, is_linux, git, GIT
) )
from mesonbuild.environment import detect_ninja 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 from mesonbuild.dependencies import PkgConfigDependency, ExternalProgram
import mesonbuild.dependencies.base import mesonbuild.dependencies.base
from mesonbuild.build import Target, ConfigurationData from mesonbuild.build import Target, ConfigurationData
import mesonbuild.modules.pkgconfig import mesonbuild.modules.pkgconfig
from mesonbuild.coredata import OptionKey
from mesonbuild.mtest import TAPParser, TestResult from mesonbuild.mtest import TAPParser, TestResult

Loading…
Cancel
Save