aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/coredata.py
diff options
context:
space:
mode:
Diffstat (limited to 'mesonbuild/coredata.py')
-rw-r--r--mesonbuild/coredata.py491
1 files changed, 135 insertions, 356 deletions
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index 36191d9..9a4139b 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: Apache-2.0
-# Copyright 2013-2024 The Meson development team
-# Copyright © 2023-2024 Intel Corporation
+# Copyright 2013-2025 The Meson development team
+# Copyright © 2023-2025 Intel Corporation
from __future__ import annotations
@@ -9,39 +9,40 @@ import copy
from . import mlog, options
import pickle, os, uuid
import sys
+from functools import lru_cache
from itertools import chain
-from pathlib import PurePath
-from collections import OrderedDict, abc
-from dataclasses import dataclass
+from collections import OrderedDict
+import textwrap
from .mesonlib import (
- MesonBugException,
MesonException, MachineChoice, PerMachine,
PerMachineDefaultable,
- OptionKey, OptionType, stringlistify,
+ default_prefix,
+ stringlistify,
pickle_load
)
+from .options import OptionKey
+
from .machinefile import CmdLineFileParser
import ast
-import argparse
import enum
import shlex
import typing as T
if T.TYPE_CHECKING:
+ import argparse
from typing_extensions import Protocol
- from typing import Any
from . import dependencies
from .compilers.compilers import Compiler, CompileResult, RunResult, CompileCheckMode
from .dependencies.detect import TV_DepID
- from .environment import Environment
from .mesonlib import FileOrString
from .cmake.traceparser import CMakeCacheEntry
from .interpreterbase import SubProject
- from .options import UserOption
+ from .options import ElementaryOptionValues, MutableKeyedOptionDictType
+ from .build import BuildTarget
class SharedCMDOptions(Protocol):
@@ -58,9 +59,7 @@ if T.TYPE_CHECKING:
cross_file: T.List[str]
native_file: T.List[str]
- OptionDictType = T.Union[T.Dict[str, 'options.UserOption[T.Any]'], 'OptionsView']
- MutableKeyedOptionDictType = T.Dict['OptionKey', 'options.UserOption[T.Any]']
- KeyedOptionDictType = T.Union['options.OptionStore', 'OptionsView']
+ OptionDictType = T.Dict[str, options.AnyOptionType]
CompilerCheckCacheKey = T.Tuple[T.Tuple[str, ...], str, FileOrString, T.Tuple[str, ...], CompileCheckMode]
# code, args
RunCheckCacheKey = T.Tuple[str, T.Tuple[str, ...]]
@@ -72,7 +71,7 @@ if T.TYPE_CHECKING:
#
# Pip requires that RCs are named like this: '0.1.0.rc1'
# But the corresponding Git tag needs to be '0.1.0rc1'
-version = '1.4.99'
+version = '1.8.99'
# The next stable version when we are in dev. This is used to allow projects to
# require meson version >=1.2.0 when using 1.1.99. FeatureNew won't warn when
@@ -144,16 +143,16 @@ class DependencyCache:
successfully lookup by providing a simple get/put interface.
"""
- def __init__(self, builtins: 'KeyedOptionDictType', for_machine: MachineChoice):
+ def __init__(self, builtins: options.OptionStore, for_machine: MachineChoice):
self.__cache: T.MutableMapping[TV_DepID, DependencySubCache] = OrderedDict()
self.__builtins = builtins
- self.__pkg_conf_key = OptionKey('pkg_config_path', machine=for_machine)
- self.__cmake_key = OptionKey('cmake_prefix_path', machine=for_machine)
+ self.__pkg_conf_key = options.OptionKey('pkg_config_path')
+ self.__cmake_key = options.OptionKey('cmake_prefix_path')
def __calculate_subkey(self, type_: DependencyCacheType) -> T.Tuple[str, ...]:
data: T.Dict[DependencyCacheType, T.List[str]] = {
- DependencyCacheType.PKG_CONFIG: stringlistify(self.__builtins.get_value(self.__pkg_conf_key)),
- DependencyCacheType.CMAKE: stringlistify(self.__builtins.get_value(self.__cmake_key)),
+ DependencyCacheType.PKG_CONFIG: stringlistify(self.__builtins.get_value_for(self.__pkg_conf_key)),
+ DependencyCacheType.CMAKE: stringlistify(self.__builtins.get_value_for(self.__cmake_key)),
DependencyCacheType.OTHER: [],
}
assert type_ in data, 'Someone forgot to update subkey calculations for a new type'
@@ -248,6 +247,7 @@ class CoreData:
'default': '8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942',
'c': '8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942',
'cpp': '8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942',
+ 'masm': '8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942',
'test': '3AC096D0-A1C2-E12C-1390-A8335801FDAB',
'directory': '2150E333-8FDC-42A3-9474-1A3956D46DE8',
}
@@ -257,9 +257,9 @@ class CoreData:
self.meson_command = meson_command
self.target_guids = {}
self.version = version
- self.optstore = options.OptionStore()
self.cross_files = self.__load_config_files(cmd_options, scratch_dir, 'cross')
self.compilers: PerMachine[T.Dict[str, Compiler]] = PerMachine(OrderedDict(), OrderedDict())
+ self.optstore = options.OptionStore(self.is_cross_build())
# Stores the (name, hash) of the options file, The name will be either
# "meson_options.txt" or "meson.options".
@@ -286,7 +286,7 @@ class CoreData:
# Only to print a warning if it changes between Meson invocations.
self.config_files = self.__load_config_files(cmd_options, scratch_dir, 'native')
self.builtin_options_libdir_cross_fixup()
- self.init_builtins('')
+ self.init_builtins()
@staticmethod
def __load_config_files(cmd_options: SharedCMDOptions, scratch_dir: str, ftype: str) -> T.List[str]:
@@ -315,15 +315,15 @@ class CoreData:
# in this case we've been passed some kind of pipe, copy
# the contents of that file into the meson private (scratch)
# directory so that it can be re-read when wiping/reconfiguring
- copy = os.path.join(scratch_dir, f'{uuid.uuid4()}.{ftype}.ini')
+ fcopy = os.path.join(scratch_dir, f'{uuid.uuid4()}.{ftype}.ini')
with open(f, encoding='utf-8') as rf:
- with open(copy, 'w', encoding='utf-8') as wf:
+ with open(fcopy, 'w', encoding='utf-8') as wf:
wf.write(rf.read())
- real.append(copy)
+ real.append(fcopy)
# Also replace the command line argument, as the pipe
# probably won't exist on reconfigure
- filenames[i] = copy
+ filenames[i] = fcopy
continue
if sys.platform != 'win32':
paths = [
@@ -353,74 +353,33 @@ class CoreData:
if self.cross_files:
options.BUILTIN_OPTIONS[OptionKey('libdir')].default = 'lib'
- def sanitize_prefix(self, prefix: str) -> str:
- prefix = os.path.expanduser(prefix)
- if not os.path.isabs(prefix):
- raise MesonException(f'prefix value {prefix!r} must be an absolute path')
- if prefix.endswith('/') or prefix.endswith('\\'):
- # On Windows we need to preserve the trailing slash if the
- # string is of type 'C:\' because 'C:' is not an absolute path.
- if len(prefix) == 3 and prefix[1] == ':':
- pass
- # If prefix is a single character, preserve it since it is
- # the root directory.
- elif len(prefix) == 1:
- pass
- else:
- prefix = prefix[:-1]
- return prefix
-
- def sanitize_dir_option_value(self, prefix: str, option: OptionKey, value: T.Any) -> T.Any:
- '''
- If the option is an installation directory option, the value is an
- absolute path and resides within prefix, return the value
- as a path relative to the prefix. Otherwise, return it as is.
-
- This way everyone can do f.ex, get_option('libdir') and usually get
- the library directory relative to prefix, even though it really
- should not be relied upon.
- '''
- try:
- value = PurePath(value)
- except TypeError:
- return value
- if option.name.endswith('dir') and value.is_absolute() and \
- option not in options.BUILTIN_DIR_NOPREFIX_OPTIONS:
- try:
- # Try to relativize the path.
- value = value.relative_to(prefix)
- except ValueError:
- # Path is not relative, let’s keep it as is.
- pass
- if '..' in value.parts:
- raise MesonException(
- f'The value of the \'{option}\' option is \'{value}\' but '
- 'directory options are not allowed to contain \'..\'.\n'
- f'If you need a path outside of the {prefix!r} prefix, '
- 'please use an absolute path.'
- )
- # .as_posix() keeps the posix-like file separators Meson uses.
- return value.as_posix()
-
- def init_builtins(self, subproject: str) -> None:
+ def init_builtins(self) -> None:
# Create builtin options with default values
for key, opt in options.BUILTIN_OPTIONS.items():
- self.add_builtin_option(self.optstore, key.evolve(subproject=subproject), opt)
+ self.add_builtin_option(self.optstore, key, opt)
for for_machine in iter(MachineChoice):
for key, opt in options.BUILTIN_OPTIONS_PER_MACHINE.items():
- self.add_builtin_option(self.optstore, key.evolve(subproject=subproject, machine=for_machine), opt)
+ self.add_builtin_option(self.optstore, key.evolve(machine=for_machine), opt)
@staticmethod
- def add_builtin_option(opts_map: 'MutableKeyedOptionDictType', key: OptionKey,
- opt: 'options.BuiltinOption') -> None:
+ def add_builtin_option(optstore: options.OptionStore, key: OptionKey,
+ opt: options.AnyOptionType) -> None:
+ # Create a copy of the object, as we're going to mutate it
+ opt = copy.copy(opt)
if key.subproject:
if opt.yielding:
# This option is global and not per-subproject
return
- value = opts_map.get_value(key.as_root())
else:
- value = None
- opts_map.add_system_option(key, opt.init_option(key, value, options.default_prefix()))
+ new_value = options.argparse_prefixed_default(
+ opt, key, default_prefix())
+ opt.set_value(new_value)
+
+ modulename = key.get_module_prefix()
+ if modulename:
+ optstore.add_module_option(modulename, key, opt)
+ else:
+ optstore.add_system_option(key, opt)
def init_backend_options(self, backend_name: str) -> None:
if backend_name == 'ninja':
@@ -428,75 +387,44 @@ class CoreData:
'backend_max_links',
'Maximum number of linker processes to run or 0 for no '
'limit',
- (0, None, 0)))
+ 0,
+ min_value=0))
elif backend_name.startswith('vs'):
self.optstore.add_system_option('backend_startup_project', options.UserStringOption(
'backend_startup_project',
'Default project to execute in Visual Studio',
''))
- def get_option(self, key: OptionKey) -> T.Union[T.List[str], str, int, bool]:
- try:
- v = self.optstore.get_value(key)
- return v
- except KeyError:
- pass
-
- try:
- v = self.optstore.get_value_object(key.as_root())
- if v.yielding:
- return v.value
- except KeyError:
- pass
-
- raise MesonException(f'Tried to get unknown builtin option {str(key)}')
+ def get_option_for_target(self, target: 'BuildTarget', key: T.Union[str, OptionKey]) -> ElementaryOptionValues:
+ if isinstance(key, str):
+ assert ':' not in key
+ newkey = OptionKey(key, target.subproject)
+ else:
+ newkey = key
+ if newkey.subproject != target.subproject:
+ # FIXME: this should be an error. The caller needs to ensure that
+ # key and target have the same subproject for consistency.
+ # Now just do this to get things going.
+ newkey = newkey.evolve(subproject=target.subproject)
+ (option_object, value) = self.optstore.get_value_object_and_value_for(newkey)
+ override = target.get_override(newkey.name)
+ if override is not None:
+ return option_object.validate_value(override)
+ return value
+
+ def set_from_configure_command(self, options: SharedCMDOptions) -> bool:
+ unset_opts = getattr(options, 'unset_opts', [])
+ all_D = options.projectoptions[:]
+ for key, valstr in options.cmd_line_options.items():
+ all_D.append(f'{key!s}={valstr}')
+ return self.optstore.set_from_configure_command(all_D, unset_opts)
def set_option(self, key: OptionKey, value, first_invocation: bool = False) -> bool:
dirty = False
- if key.is_builtin():
- if key.name == 'prefix':
- value = self.sanitize_prefix(value)
- else:
- prefix = self.optstore.get_value('prefix')
- value = self.sanitize_dir_option_value(prefix, key, value)
-
try:
- opt = self.optstore.get_value_object(key)
+ changed = self.optstore.set_option(key, value, first_invocation)
except KeyError:
raise MesonException(f'Tried to set unknown builtin option {str(key)}')
-
- if opt.deprecated is True:
- mlog.deprecation(f'Option {key.name!r} is deprecated')
- elif isinstance(opt.deprecated, list):
- for v in opt.listify(value):
- if v in opt.deprecated:
- mlog.deprecation(f'Option {key.name!r} value {v!r} is deprecated')
- elif isinstance(opt.deprecated, dict):
- def replace(v):
- newvalue = opt.deprecated.get(v)
- if newvalue is not None:
- mlog.deprecation(f'Option {key.name!r} value {v!r} is replaced by {newvalue!r}')
- return newvalue
- return v
- newvalue = [replace(v) for v in opt.listify(value)]
- value = ','.join(newvalue)
- elif isinstance(opt.deprecated, str):
- # Option is deprecated and replaced by another. Note that a project
- # option could be replaced by a built-in or module option, which is
- # why we use OptionKey.from_string(newname) instead of
- # key.evolve(newname). We set the value on both the old and new names,
- # assuming they accept the same value. That could for example be
- # achieved by adding the values from old option as deprecated on the
- # new option, for example in the case of boolean option is replaced
- # by a feature option with a different name.
- newname = opt.deprecated
- newkey = OptionKey.from_string(newname).evolve(subproject=key.subproject)
- mlog.deprecation(f'Option {key.name!r} is replaced by {newname!r}')
- dirty |= self.set_option(newkey, value, first_invocation)
-
- changed = opt.set_value(value)
- if changed and opt.readonly and not first_invocation:
- raise MesonException(f'Tried modify read only option {str(key)!r}')
dirty |= changed
if key.name == 'buildtype':
@@ -512,7 +440,7 @@ class CoreData:
def get_nondefault_buildtype_args(self) -> T.List[T.Union[T.Tuple[str, str, str], T.Tuple[str, bool, bool]]]:
result: T.List[T.Union[T.Tuple[str, str, str], T.Tuple[str, bool, bool]]] = []
- value = self.optstore.get_value('buildtype')
+ value = self.optstore.get_value_for('buildtype')
if value == 'plain':
opt = 'plain'
debug = False
@@ -531,8 +459,8 @@ class CoreData:
else:
assert value == 'custom'
return []
- actual_opt = self.optstore.get_value('optimization')
- actual_debug = self.optstore.get_value('debug')
+ actual_opt = self.optstore.get_value_for('optimization')
+ actual_debug = self.optstore.get_value_for('debug')
if actual_opt != opt:
result.append(('optimization', actual_opt, opt))
if actual_debug != debug:
@@ -561,67 +489,36 @@ class CoreData:
assert value == 'custom'
return False
- dirty |= self.optstore.set_value('optimization', opt)
- dirty |= self.optstore.set_value('debug', debug)
+ dirty |= self.optstore.set_option(OptionKey('optimization'), opt)
+ dirty |= self.optstore.set_option(OptionKey('debug'), debug)
return dirty
- @staticmethod
- def is_per_machine_option(optname: OptionKey) -> bool:
- if optname.as_host() in options.BUILTIN_OPTIONS_PER_MACHINE:
- return True
- return optname.lang is not None
-
def get_external_args(self, for_machine: MachineChoice, lang: str) -> T.List[str]:
# mypy cannot analyze type of OptionKey
- key = OptionKey('args', machine=for_machine, lang=lang)
+ key = OptionKey(f'{lang}_args', machine=for_machine)
return T.cast('T.List[str]', self.optstore.get_value(key))
+ @lru_cache(maxsize=None)
def get_external_link_args(self, for_machine: MachineChoice, lang: str) -> T.List[str]:
# mypy cannot analyze type of OptionKey
- key = OptionKey('link_args', machine=for_machine, lang=lang)
- return T.cast('T.List[str]', self.optstore.get_value(key))
-
- def update_project_options(self, project_options: 'MutableKeyedOptionDictType', subproject: SubProject) -> None:
- for key, value in project_options.items():
- if not key.is_project():
- continue
- if key not in self.optstore:
- self.optstore.add_project_option(key, value)
- continue
- if key.subproject != subproject:
- raise MesonBugException(f'Tried to set an option for subproject {key.subproject} from {subproject}!')
-
- oldval = self.optstore.get_value_object(key)
- if type(oldval) is not type(value):
- self.optstore.set_value(key, value.value)
- elif oldval.choices != value.choices:
- # If the choices have changed, use the new value, but attempt
- # to keep the old options. If they are not valid keep the new
- # defaults but warn.
- self.optstore.set_value_object(key, value)
- try:
- value.set_value(oldval.value)
- except MesonException:
- mlog.warning(f'Old value(s) of {key} are no longer valid, resetting to default ({value.value}).',
- fatal=False)
-
- # Find any extranious keys for this project and remove them
- for key in self.optstore.keys() - project_options.keys():
- if key.is_project() and key.subproject == subproject:
- self.optstore.remove(key)
+ linkkey = OptionKey(f'{lang}_link_args', machine=for_machine)
+ return T.cast('T.List[str]', self.optstore.get_value_for(linkkey))
def is_cross_build(self, when_building_for: MachineChoice = MachineChoice.HOST) -> bool:
if when_building_for == MachineChoice.BUILD:
return False
return len(self.cross_files) > 0
- def copy_build_options_from_regular_ones(self) -> bool:
+ def copy_build_options_from_regular_ones(self, shut_up_pylint: bool = True) -> bool:
+ # FIXME, needs cross compilation support.
+ if shut_up_pylint:
+ return False
dirty = False
assert not self.is_cross_build()
for k in options.BUILTIN_OPTIONS_PER_MACHINE:
- o = self.optstore.get_value_object(k)
- dirty |= self.optstore.set_value(k.as_build(), o.value)
+ o = self.optstore.get_value_object_for(k.name)
+ dirty |= self.optstore.set_option(k, o.value, True)
for bk, bv in self.optstore.items():
if bk.machine is MachineChoice.BUILD:
hk = bk.as_host()
@@ -640,131 +537,72 @@ class CoreData:
# Set prefix first because it's needed to sanitize other options
pfk = OptionKey('prefix')
if pfk in opts_to_set:
- prefix = self.sanitize_prefix(opts_to_set[pfk])
- dirty |= self.optstore.set_value('prefix', prefix)
+ prefix = self.optstore.sanitize_prefix(opts_to_set[pfk])
for key in options.BUILTIN_DIR_NOPREFIX_OPTIONS:
if key not in opts_to_set:
- dirty |= self.optstore.set_value(key, options.BUILTIN_OPTIONS[key].prefixed_default(key, prefix))
+ val = options.BUILTIN_OPTIONS[key].prefixed_default(key, prefix)
+ dirty |= self.optstore.set_option(key, val)
unknown_options: T.List[OptionKey] = []
for k, v in opts_to_set.items():
if k == pfk:
continue
- elif k in self.optstore:
+ elif k.evolve(subproject=None) in self.optstore:
dirty |= self.set_option(k, v, first_invocation)
- elif k.machine != MachineChoice.BUILD and k.type != OptionType.COMPILER:
+ elif k.machine != MachineChoice.BUILD and not self.optstore.is_compiler_option(k):
unknown_options.append(k)
if unknown_options:
- unknown_options_str = ', '.join(sorted(str(s) for s in unknown_options))
- sub = f'In subproject {subproject}: ' if subproject else ''
- raise MesonException(f'{sub}Unknown options: "{unknown_options_str}"')
+ if subproject:
+ # The subproject may have top-level options that should be used
+ # when it is not a subproject. Ignore those for now. With option
+ # refactor they will get per-subproject values.
+ really_unknown = []
+ for uo in unknown_options:
+ topkey = uo.as_root()
+ if topkey not in self.optstore:
+ really_unknown.append(uo)
+ unknown_options = really_unknown
+ if unknown_options:
+ unknown_options_str = ', '.join(sorted(str(s) for s in unknown_options))
+ sub = f'In subproject {subproject}: ' if subproject else ''
+ raise MesonException(f'{sub}Unknown options: "{unknown_options_str}"')
if not self.is_cross_build():
dirty |= self.copy_build_options_from_regular_ones()
return dirty
- def set_default_options(self, default_options: T.MutableMapping[OptionKey, str], subproject: str, env: 'Environment') -> None:
- from .compilers import base_options
-
- # Main project can set default options on subprojects, but subprojects
- # can only set default options on themselves.
- # Preserve order: if env.options has 'buildtype' it must come after
- # 'optimization' if it is in default_options.
- options: T.MutableMapping[OptionKey, T.Any] = OrderedDict()
- for k, v in default_options.items():
- if not subproject or k.subproject == subproject:
- options[k] = v
- options.update(env.options)
- env.options = options
-
- # Create a subset of options, keeping only project and builtin
- # options for this subproject.
- # Language and backend specific options will be set later when adding
- # languages and setting the backend (builtin options must be set first
- # to know which backend we'll use).
- options = OrderedDict()
-
- for k, v in env.options.items():
- # If this is a subproject, don't use other subproject options
- if k.subproject and k.subproject != subproject:
- continue
- # If the option is a builtin and is yielding then it's not allowed per subproject.
- #
- # Always test this using the HOST machine, as many builtin options
- # are not valid for the BUILD machine, but the yielding value does
- # not differ between them even when they are valid for both.
- if subproject and k.is_builtin() and self.optstore.get_value_object(k.evolve(subproject='', machine=MachineChoice.HOST)).yielding:
- continue
- # Skip base, compiler, and backend options, they are handled when
- # adding languages and setting backend.
- if k.type in {OptionType.COMPILER, OptionType.BACKEND}:
- continue
- if k.type == OptionType.BASE and k.as_root() in base_options:
- # set_options will report unknown base options
- continue
- options[k] = v
-
- self.set_options(options, subproject=subproject, first_invocation=env.first_invocation)
-
- def add_compiler_options(self, c_options: MutableKeyedOptionDictType, lang: str, for_machine: MachineChoice,
- env: Environment, subproject: str) -> None:
+ def add_compiler_options(self, c_options: MutableKeyedOptionDictType, lang: str, for_machine: MachineChoice) -> None:
for k, o in c_options.items():
- value = env.options.get(k)
- if value is not None:
- o.set_value(value)
- if not subproject:
- self.optstore.set_value_object(k, o) # override compiler option on reconfigure
- self.optstore.setdefault(k, o)
+ assert k.subproject is None and k.machine is for_machine
+ if lang == 'objc' and k.name == 'c_std':
+ # For objective C, always fall back to c_std.
+ self.optstore.add_compiler_option('c', k, o)
+ elif lang == 'objcpp' and k.name == 'cpp_std':
+ self.optstore.add_compiler_option('cpp', k, o)
+ else:
+ self.optstore.add_compiler_option(lang, k, o)
+
+ def process_compiler_options(self, lang: str, comp: Compiler, subproject: str) -> None:
+ self.add_compiler_options(comp.get_options(), lang, comp.for_machine)
- if subproject:
- sk = k.evolve(subproject=subproject)
- value = env.options.get(sk) or value
- if value is not None:
- o.set_value(value)
- self.optstore.set_value_object(sk, o) # override compiler option on reconfigure
- self.optstore.setdefault(sk, o)
-
- def add_lang_args(self, lang: str, comp: T.Type['Compiler'],
- for_machine: MachineChoice, env: 'Environment') -> None:
- """Add global language arguments that are needed before compiler/linker detection."""
- from .compilers import compilers
- # These options are all new at this point, because the compiler is
- # responsible for adding its own options, thus calling
- # `self.optstore.update()`` is perfectly safe.
- self.optstore.update(compilers.get_global_options(lang, comp, for_machine, env))
-
- def process_compiler_options(self, lang: str, comp: Compiler, env: Environment, subproject: str) -> None:
- from . import compilers
-
- self.add_compiler_options(comp.get_options(), lang, comp.for_machine, env, subproject)
-
- enabled_opts: T.List[OptionKey] = []
for key in comp.base_options:
if subproject:
skey = key.evolve(subproject=subproject)
else:
skey = key
if skey not in self.optstore:
- self.optstore.add_system_option(skey, copy.deepcopy(compilers.base_options[key]))
- if skey in env.options:
- self.optstore.set_value(skey, env.options[skey])
- enabled_opts.append(skey)
- elif subproject and key in env.options:
- self.optstore.set_value(skey, env.options[key])
- enabled_opts.append(skey)
- if subproject and key not in self.optstore:
- self.optstore.add_system_option(key, copy.deepcopy(self.optstore.get_value_object(skey)))
- elif skey in env.options:
- self.optstore.set_value(skey, env.options[skey])
- elif subproject and key in env.options:
- self.optstore.set_value(skey, env.options[key])
- self.emit_base_options_warnings(enabled_opts)
-
- def emit_base_options_warnings(self, enabled_opts: T.List[OptionKey]) -> None:
- if OptionKey('b_bitcode') in enabled_opts:
- mlog.warning('Base option \'b_bitcode\' is enabled, which is incompatible with many linker options. Incompatible options such as \'b_asneeded\' have been disabled.', fatal=False)
- mlog.warning('Please see https://mesonbuild.com/Builtin-options.html#Notes_about_Apple_Bitcode_support for more details.', fatal=False)
+ self.optstore.add_system_option(skey, copy.deepcopy(options.COMPILER_BASE_OPTIONS[key]))
+
+ self.emit_base_options_warnings()
+
+ def emit_base_options_warnings(self) -> None:
+ bcodekey = OptionKey('b_bitcode')
+ if bcodekey in self.optstore and self.optstore.get_value(bcodekey):
+ msg = textwrap.dedent('''Base option 'b_bitcode' is enabled, which is incompatible with many linker options.
+ Incompatible options such as \'b_asneeded\' have been disabled.'
+ Please see https://mesonbuild.com/Builtin-options.html#Notes_about_Apple_Bitcode_support for more details.''')
+ mlog.warning(msg, once=True, fatal=False)
def get_cmd_line_file(build_dir: str) -> str:
return os.path.join(build_dir, 'meson-private', 'cmd_line.txt')
@@ -852,28 +690,22 @@ def save(obj: CoreData, build_dir: str) -> str:
def register_builtin_arguments(parser: argparse.ArgumentParser) -> None:
for n, b in options.BUILTIN_OPTIONS.items():
- b.add_to_argparse(str(n), parser, '')
+ options.option_to_argparse(b, n, parser, '')
for n, b in options.BUILTIN_OPTIONS_PER_MACHINE.items():
- b.add_to_argparse(str(n), parser, ' (just for host machine)')
- b.add_to_argparse(str(n.as_build()), parser, ' (just for build machine)')
+ options.option_to_argparse(b, n, parser, ' (just for host machine)')
+ options.option_to_argparse(b, n.as_build(), parser, ' (just for build machine)')
parser.add_argument('-D', action='append', dest='projectoptions', default=[], metavar="option",
help='Set the value of an option, can be used several times to set multiple options.')
-def create_options_dict(options: T.List[str], subproject: str = '') -> T.Dict[OptionKey, str]:
- result: T.OrderedDict[OptionKey, str] = OrderedDict()
- for o in options:
+def parse_cmd_line_options(args: SharedCMDOptions) -> None:
+ args.cmd_line_options = {}
+ for o in args.projectoptions:
try:
- (key, value) = o.split('=', 1)
+ keystr, value = o.split('=', 1)
except ValueError:
raise MesonException(f'Option {o!r} must have a value separated by equals sign.')
- k = OptionKey.from_string(key)
- if subproject:
- k = k.evolve(subproject=subproject)
- result[k] = value
- return result
-
-def parse_cmd_line_options(args: SharedCMDOptions) -> None:
- args.cmd_line_options = create_options_dict(args.projectoptions)
+ key = OptionKey.from_string(keystr)
+ args.cmd_line_options[key] = value
# Merge builtin options set with --option into the dict.
for key in chain(
@@ -885,65 +717,12 @@ def parse_cmd_line_options(args: SharedCMDOptions) -> None:
value = getattr(args, name, None)
if value is not None:
if key in args.cmd_line_options:
- cmdline_name = options.BuiltinOption.argparse_name_to_arg(name)
+ cmdline_name = options.argparse_name_to_arg(name)
raise MesonException(
f'Got argument {name} as both -D{name} and {cmdline_name}. Pick one.')
args.cmd_line_options[key] = value
delattr(args, name)
-@dataclass
-class OptionsView(abc.Mapping):
- '''A view on an options dictionary for a given subproject and with overrides.
- '''
-
- # TODO: the typing here could be made more explicit using a TypeDict from
- # python 3.8 or typing_extensions
- original_options: T.Union[KeyedOptionDictType, 'dict[OptionKey, UserOption[Any]]']
- subproject: T.Optional[str] = None
- overrides: T.Optional[T.Mapping[OptionKey, T.Union[str, int, bool, T.List[str]]]] = None
-
- def __getitem__(self, key: OptionKey) -> options.UserOption:
- # FIXME: This is fundamentally the same algorithm than interpreter.get_option_internal().
- # We should try to share the code somehow.
- key = key.evolve(subproject=self.subproject)
- if not key.is_project():
- opt = self.original_options.get(key)
- if opt is None or opt.yielding:
- key2 = key.as_root()
- # This hack goes away once wi start using OptionStore
- # to hold overrides.
- if isinstance(self.original_options, options.OptionStore):
- if key2 not in self.original_options:
- raise KeyError
- opt = self.original_options.get_value_object(key2)
- else:
- opt = self.original_options[key2]
- else:
- opt = self.original_options[key]
- if opt.yielding:
- opt = self.original_options.get(key.as_root(), opt)
- if self.overrides:
- override_value = self.overrides.get(key.as_root())
- if override_value is not None:
- opt = copy.copy(opt)
- opt.set_value(override_value)
- return opt
-
- def get_value(self, key: T.Union[str, OptionKey]):
- if isinstance(key, str):
- key = OptionKey(key)
- return self[key].value
-
- def set_value(self, key: T.Union[str, OptionKey], value: T.Union[str, int, bool, T.List[str]]):
- if isinstance(key, str):
- key = OptionKey(key)
- self.overrides[key] = value
-
- def __iter__(self) -> T.Iterator[OptionKey]:
- return iter(self.original_options)
-
- def __len__(self) -> int:
- return len(self.original_options)
FORBIDDEN_TARGET_NAMES = frozenset({
'clean',