From 71db6b04a31707674ad776be1cf22f667056d56b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 4 Dec 2020 16:09:10 -0800 Subject: use OptionKey for builtin and base options I would have prefered to do these seperatately, but they are combined in some cases, so it was much easier to convert them together. this eliminates the builtins_per_machine dict, as it's duplicated with the OptionKey's machine parameter. --- mesonbuild/backend/backends.py | 29 ++-- mesonbuild/backend/ninjabackend.py | 39 ++--- mesonbuild/backend/vs2010backend.py | 8 +- mesonbuild/build.py | 35 ++-- mesonbuild/cmake/executor.py | 4 +- mesonbuild/cmake/interpreter.py | 2 +- mesonbuild/compilers/compilers.py | 106 ++++++------ mesonbuild/compilers/cpp.py | 2 +- mesonbuild/compilers/d.py | 12 +- mesonbuild/compilers/mixins/arm.py | 7 +- mesonbuild/compilers/mixins/clang.py | 7 +- mesonbuild/compilers/mixins/clike.py | 7 +- mesonbuild/compilers/mixins/elbrus.py | 6 +- mesonbuild/compilers/mixins/emscripten.py | 2 +- mesonbuild/compilers/mixins/gnu.py | 14 +- mesonbuild/compilers/mixins/intel.py | 5 +- mesonbuild/compilers/mixins/pgi.py | 3 +- mesonbuild/compilers/mixins/visualstudio.py | 2 +- mesonbuild/compilers/rust.py | 4 +- mesonbuild/compilers/vala.py | 4 +- mesonbuild/coredata.py | 244 ++++++++++++++-------------- mesonbuild/dependencies/base.py | 11 +- mesonbuild/dependencies/boost.py | 4 +- mesonbuild/dependencies/ui.py | 4 +- mesonbuild/interpreter.py | 43 ++--- mesonbuild/mconf.py | 60 +++---- mesonbuild/mesonlib.py | 78 +++++++-- mesonbuild/mintro.py | 38 ++--- mesonbuild/modules/gnome.py | 14 +- mesonbuild/msetup.py | 2 +- mesonbuild/rewriter.py | 6 +- run_unittests.py | 73 +++++---- 32 files changed, 444 insertions(+), 431 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 4c35253..b6d084f 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -32,7 +32,7 @@ from .. import mlog from ..compilers import languages_using_ldflags from ..mesonlib import ( File, MachineChoice, MesonException, OrderedSet, OptionOverrideProxy, - classify_unity_sources, unholder + classify_unity_sources, unholder, OptionKey ) if T.TYPE_CHECKING: @@ -211,21 +211,21 @@ class Backend: def get_target_filename_abs(self, target): return os.path.join(self.environment.get_build_dir(), self.get_target_filename(target)) - def get_base_options_for_target(self, target): + def get_base_options_for_target(self, target: build.BuildTarget) -> OptionOverrideProxy: return OptionOverrideProxy(target.option_overrides_base, self.environment.coredata.builtins, - self.environment.coredata.base_options) + {k: v for k, v in self.environment.coredata.base_options.items()}) def get_compiler_options_for_target(self, target: build.BuildTarget) -> OptionOverrideProxy: comp_reg = self.environment.coredata.compiler_options comp_override = target.option_overrides_compiler return OptionOverrideProxy(comp_override, comp_reg) - def get_option_for_target(self, option_name, target): + def get_option_for_target(self, option_name: 'OptionKey', target: build.BuildTarget): if option_name in target.option_overrides_base: override = target.option_overrides_base[option_name] return self.environment.coredata.validate_option_value(option_name, override) - return self.environment.coredata.get_builtin_option(option_name, target.subproject) + return self.environment.coredata.get_builtin_option(str(option_name), target.subproject) def get_target_filename_for_linking(self, target): # On some platforms (msvc for instance), the file that is used for @@ -299,7 +299,7 @@ class Backend: abs_files = [] result = [] compsrcs = classify_unity_sources(target.compilers.values(), unity_src) - unity_size = self.get_option_for_target('unity_size', target) + unity_size = self.get_option_for_target(OptionKey('unity_size'), target) def init_language_file(suffix, unity_file_number): unity_src = self.get_unity_source_file(target, suffix, unity_file_number) @@ -620,7 +620,8 @@ class Backend: if self.is_unity(extobj.target): compsrcs = classify_unity_sources(extobj.target.compilers.values(), sources) sources = [] - unity_size = self.get_option_for_target('unity_size', extobj.target) + unity_size = self.get_option_for_target(OptionKey('unity_size'), extobj.target) + for comp, srcs in compsrcs.items(): for i in range(len(srcs) // unity_size + 1): osrc = self.get_unity_source_file(extobj.target, @@ -689,20 +690,20 @@ class Backend: if no_warn_args: commands += compiler.get_no_warn_args() else: - commands += compiler.get_warn_args(self.get_option_for_target('warning_level', target)) + commands += compiler.get_warn_args(self.get_option_for_target(OptionKey('warning_level'), target)) # Add -Werror if werror=true is set in the build options set on the # command-line or default_options inside project(). This only sets the # action to be done for warnings if/when they are emitted, so it's ok # to set it after get_no_warn_args() or get_warn_args(). - if self.get_option_for_target('werror', target): + if self.get_option_for_target(OptionKey('werror'), target): commands += compiler.get_werror_args() # Add compile args for c_* or cpp_* build options set on the # command-line or default_options inside project(). commands += compiler.get_option_compile_args(copt_proxy) # Add buildtype args: optimization level, debugging, etc. - commands += compiler.get_buildtype_args(self.get_option_for_target('buildtype', target)) - commands += compiler.get_optimization_args(self.get_option_for_target('optimization', target)) - commands += compiler.get_debug_args(self.get_option_for_target('debug', target)) + commands += compiler.get_buildtype_args(self.get_option_for_target(OptionKey('buildtype'), target)) + commands += compiler.get_optimization_args(self.get_option_for_target(OptionKey('optimization'), target)) + commands += compiler.get_debug_args(self.get_option_for_target(OptionKey('debug'), target)) # MSVC debug builds have /ZI argument by default and /Zi is added with debug flag # /ZI needs to be removed in that case to avoid cl's warning to that effect (D9025 : overriding '/ZI' with '/Zi') if ('/ZI' in commands) and ('/Zi' in commands): @@ -1021,7 +1022,7 @@ class Backend: return libs def is_unity(self, target): - optval = self.get_option_for_target('unity', target) + optval = self.get_option_for_target(OptionKey('unity'), target) if optval == 'on' or (optval == 'subprojects' and target.subproject != ''): return True return False @@ -1227,7 +1228,7 @@ class Backend: # # TODO: Create GNUStrip/AppleStrip/etc. hierarchy for more # fine-grained stripping of static archives. - should_strip = not isinstance(t, build.StaticLibrary) and self.get_option_for_target('strip', t) + should_strip = not isinstance(t, build.StaticLibrary) and self.get_option_for_target(OptionKey('strip'), t) # Install primary build output (library/executable/jar, etc) # Done separately because of strip/aliases/rpath if outdirs[0] is not False: diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index a4cdefd..4bc76ab 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -537,8 +537,9 @@ int dummy; self.add_build_comment(NinjaComment('Install rules')) self.generate_install() self.generate_dist() - if 'b_coverage' in self.environment.coredata.base_options and \ - self.environment.coredata.base_options['b_coverage'].value: + key = OptionKey('b_coverage') + if (key in self.environment.coredata.base_options and + self.environment.coredata.base_options[key].value): self.add_build_comment(NinjaComment('Coverage rules')) self.generate_coverage_rules() self.add_build_comment(NinjaComment('Suffix')) @@ -814,7 +815,7 @@ int dummy; source2object[s] = o obj_list.append(o) - use_pch = self.environment.coredata.base_options.get('b_pch', False) + use_pch = self.environment.coredata.base_options.get(OptionKey('b_pch')) if use_pch and target.has_pch(): pch_objects = self.generate_pch(target, header_deps=header_deps) else: @@ -1297,8 +1298,8 @@ int dummy; args.append(a) return args, deps - def generate_cs_target(self, target): - buildtype = self.get_option_for_target('buildtype', target) + def generate_cs_target(self, target: build.BuildTarget): + buildtype = self.get_option_for_target(OptionKey('buildtype'), target) fname = target.get_filename() outname_rel = os.path.join(self.get_target_dir(target), fname) src_list = target.get_sources() @@ -1307,8 +1308,8 @@ int dummy; deps = [] commands = compiler.compiler_args(target.extra_args.get('cs', [])) commands += compiler.get_buildtype_args(buildtype) - commands += compiler.get_optimization_args(self.get_option_for_target('optimization', target)) - commands += compiler.get_debug_args(self.get_option_for_target('debug', target)) + commands += compiler.get_optimization_args(self.get_option_for_target(OptionKey('optimization'), target)) + commands += compiler.get_debug_args(self.get_option_for_target(OptionKey('debug'), target)) if isinstance(target, build.Executable): commands.append('-target:exe') elif isinstance(target, build.SharedLibrary): @@ -1349,7 +1350,7 @@ int dummy; def determine_single_java_compile_args(self, target, compiler): args = [] - args += compiler.get_buildtype_args(self.get_option_for_target('buildtype', target)) + args += compiler.get_buildtype_args(self.get_option_for_target(OptionKey('buildtype'), target)) args += self.build.get_global_args(compiler, target.for_machine) args += self.build.get_project_args(compiler, target.subproject, target.for_machine) args += target.get_java_args() @@ -1512,7 +1513,7 @@ int dummy; valac_outputs.append(vala_c_file) args = self.generate_basic_compiler_args(target, valac) - args += valac.get_colorout_args(self.environment.coredata.base_options.get('b_colorout').value) + args += valac.get_colorout_args(self.environment.coredata.base_options.get(OptionKey('b_colorout')).value) # Tell Valac to output everything in our private directory. Sadly this # means it will also preserve the directory components of Vala sources # found inside the build tree (generated sources). @@ -1619,9 +1620,9 @@ int dummy; opt_proxy = self.get_compiler_options_for_target(target) args += ['--crate-name', target.name] - args += rustc.get_buildtype_args(self.get_option_for_target('buildtype', target)) - args += rustc.get_debug_args(self.get_option_for_target('debug', target)) - args += rustc.get_optimization_args(self.get_option_for_target('optimization', target)) + args += rustc.get_buildtype_args(self.get_option_for_target(OptionKey('buildtype'), target)) + args += rustc.get_debug_args(self.get_option_for_target(OptionKey('debug'), target)) + args += rustc.get_optimization_args(self.get_option_for_target(OptionKey('optimization'), target)) args += rustc.get_option_compile_args(opt_proxy) args += self.build.get_global_args(rustc, target.for_machine) args += self.build.get_project_args(rustc, target.subproject, target.for_machine) @@ -1772,8 +1773,8 @@ int dummy; raise InvalidArguments('Swift target {} contains a non-swift source file.'.format(target.get_basename())) os.makedirs(self.get_target_private_dir_abs(target), exist_ok=True) compile_args = swiftc.get_compile_only_args() - compile_args += swiftc.get_optimization_args(self.get_option_for_target('optimization', target)) - compile_args += swiftc.get_debug_args(self.get_option_for_target('debug', target)) + compile_args += swiftc.get_optimization_args(self.get_option_for_target(OptionKey('optimization'), target)) + compile_args += swiftc.get_debug_args(self.get_option_for_target(OptionKey('debug'), target)) compile_args += swiftc.get_module_args(module_name) compile_args += self.build.get_project_args(swiftc, target.subproject, target.for_machine) compile_args += self.build.get_global_args(swiftc, target.for_machine) @@ -2498,7 +2499,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) commands += self.get_compile_debugfile_args(compiler, target, rel_obj) # PCH handling - if self.environment.coredata.base_options.get('b_pch', False): + if self.environment.coredata.base_options.get(OptionKey('b_pch')): commands += self.get_pch_include_args(compiler, target) pchlist = target.get_pch(compiler.language) else: @@ -2869,9 +2870,9 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) # Add things like /NOLOGO; usually can't be overridden commands += linker.get_linker_always_args() # Add buildtype linker args: optimization level, etc. - commands += linker.get_buildtype_linker_args(self.get_option_for_target('buildtype', target)) + commands += linker.get_buildtype_linker_args(self.get_option_for_target(OptionKey('buildtype'), target)) # Add /DEBUG and the pdb filename when using MSVC - if self.get_option_for_target('debug', target): + if self.get_option_for_target(OptionKey('debug'), target): commands += self.get_link_debugfile_args(linker, target, outname) debugfile = self.get_link_debugfile_name(linker, target, outname) if debugfile is not None: @@ -3155,8 +3156,8 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) if ctlist: elem.add_dep(self.generate_custom_target_clean(ctlist)) - if 'b_coverage' in self.environment.coredata.base_options and \ - self.environment.coredata.base_options['b_coverage'].value: + if OptionKey('b_coverage') in self.environment.coredata.base_options and \ + self.environment.coredata.base_options[OptionKey('b_coverage')].value: self.generate_gcov_clean() elem.add_dep('clean-gcda') elem.add_dep('clean-gcno') diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 8af83e6..ed84828 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -785,7 +785,7 @@ class Vs2010Backend(backends.Backend): build_args += compiler.get_optimization_args(self.optimization) build_args += compiler.get_debug_args(self.debug) buildtype_link_args = compiler.get_buildtype_linker_args(self.buildtype) - vscrt_type = self.environment.coredata.base_options['b_vscrt'] + vscrt_type = self.environment.coredata.base_options[OptionKey('b_vscrt')] project_name = target.name target_name = target.name root = ET.Element('Project', {'DefaultTargets': "Build", @@ -1048,9 +1048,9 @@ class Vs2010Backend(backends.Backend): ET.SubElement(clconf, 'PreprocessorDefinitions').text = ';'.join(target_defines) ET.SubElement(clconf, 'FunctionLevelLinking').text = 'true' # Warning level - warning_level = self.get_option_for_target('warning_level', target) + warning_level = self.get_option_for_target(OptionKey('warning_level'), target) ET.SubElement(clconf, 'WarningLevel').text = 'Level' + str(1 + int(warning_level)) - if self.get_option_for_target('werror', target): + if self.get_option_for_target(OptionKey('werror'), target): ET.SubElement(clconf, 'TreatWarningAsError').text = 'true' # Optimization flags o_flags = split_o_flags_args(build_args) @@ -1075,7 +1075,7 @@ class Vs2010Backend(backends.Backend): ET.SubElement(clconf, 'FavorSizeOrSpeed').text = 'Speed' # Note: SuppressStartupBanner is /NOLOGO and is 'true' by default pch_sources = {} - if self.environment.coredata.base_options.get('b_pch', False): + if self.environment.coredata.base_options.get(OptionKey('b_pch')): for lang in ['c', 'cpp']: pch = target.get_pch(lang) if not pch: diff --git a/mesonbuild/build.py b/mesonbuild/build.py index d61ca73..2916976 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -404,7 +404,7 @@ class EnvironmentVariables: return env class Target: - def __init__(self, name, subdir, subproject, build_by_default, for_machine: MachineChoice): + def __init__(self, name, subdir, subproject, build_by_default: bool, for_machine: MachineChoice): if has_path_sep(name): # Fix failing test 53 when this becomes an error. mlog.warning('''Target "{}" has a path separator in its name. @@ -417,7 +417,7 @@ a hard error in the future.'''.format(name)) self.for_machine = for_machine self.install = False self.build_always_stale = False - self.option_overrides_base: 'OptionDictType' = {} + self.option_overrides_base: T.Dict[OptionKey, str] = {} self.option_overrides_compiler: 'KeyedOptionDictType' = {} self.extra_files = [] # type: T.List[File] if not hasattr(self, 'typename'): @@ -499,7 +499,7 @@ a hard error in the future.'''.format(name)) return self.construct_id_from_path( self.subdir, self.name, self.type_suffix()) - def process_kwargs_base(self, kwargs): + def process_kwargs_base(self, kwargs: T.Dict[str, T.Any]) -> None: if 'build_by_default' in kwargs: self.build_by_default = kwargs['build_by_default'] if not isinstance(self.build_by_default, bool): @@ -512,23 +512,22 @@ a hard error in the future.'''.format(name)) option_overrides = self.parse_overrides(kwargs) for k, v in option_overrides.items(): - if '_' in k: - key = OptionKey.from_string(k) - if key.lang: - self.option_overrides_compiler[key.evolve(machine=self.for_machine)] = v - continue + if k.lang: + self.option_overrides_compiler[k.evolve(machine=self.for_machine)] = v + continue self.option_overrides_base[k] = v - def parse_overrides(self, kwargs) -> dict: - result = {} + @staticmethod + def parse_overrides(kwargs: T.Dict[str, T.Any]) -> T.Dict[OptionKey, str]: + result: T.Dict[OptionKey, str] = {} overrides = stringlistify(kwargs.get('override_options', [])) for o in overrides: if '=' not in o: raise InvalidArguments('Overrides must be of form "key=value"') k, v = o.split('=', 1) - k = k.strip() + key = OptionKey.from_string(k.strip()) v = v.strip() - result[k] = v + result[key] = v return result def is_linkable_target(self) -> bool: @@ -1066,17 +1065,18 @@ This will become a hard error in a future Meson release.''') raise InvalidArguments('Invalid value for win_subsystem: {}.'.format(value)) return value - def _extract_pic_pie(self, kwargs, arg, environment, option): + def _extract_pic_pie(self, kwargs, arg: str, environment, option: str): # Check if we have -fPIC, -fpic, -fPIE, or -fpie in cflags all_flags = self.extra_args['c'] + self.extra_args['cpp'] if '-f' + arg.lower() in all_flags or '-f' + arg.upper() in all_flags: mlog.warning("Use the '{}' kwarg instead of passing '{}' manually to {!r}".format(arg, '-f' + arg, self.name)) return True + k = OptionKey(option) if arg in kwargs: val = kwargs[arg] - elif option in environment.coredata.base_options: - val = environment.coredata.base_options[option].value + elif k in environment.coredata.base_options: + val = environment.coredata.base_options[k].value else: val = False @@ -1597,8 +1597,9 @@ class Executable(BuildTarget): def __init__(self, name: str, subdir: str, subproject: str, for_machine: MachineChoice, sources: T.List[File], objects, environment: environment.Environment, kwargs): self.typename = 'executable' - if 'pie' not in kwargs and 'b_pie' in environment.coredata.base_options: - kwargs['pie'] = environment.coredata.base_options['b_pie'].value + key = OptionKey('b_pie') + if 'pie' not in kwargs and key in environment.coredata.base_options: + kwargs['pie'] = environment.coredata.base_options[key].value super().__init__(name, subdir, subproject, for_machine, sources, objects, environment, kwargs) # Unless overridden, executables have no suffix or prefix. Except on # Windows and with C#/Mono executables where the suffix is 'exe' diff --git a/mesonbuild/cmake/executor.py b/mesonbuild/cmake/executor.py index 19971e3..21dfd356 100644 --- a/mesonbuild/cmake/executor.py +++ b/mesonbuild/cmake/executor.py @@ -23,7 +23,7 @@ import re import os from .. import mlog -from ..mesonlib import PerMachine, Popen_safe, version_compare, MachineChoice, is_windows +from ..mesonlib import PerMachine, Popen_safe, version_compare, MachineChoice, is_windows, OptionKey from ..envconfig import get_env_var if T.TYPE_CHECKING: @@ -62,7 +62,7 @@ class CMakeExecutor: self.cmakebin = None return - self.prefix_paths = self.environment.coredata.builtins_per_machine[self.for_machine]['cmake_prefix_path'].value + self.prefix_paths = self.environment.coredata.builtins[OptionKey('cmake_prefix_path', machine=self.for_machine)].value env_pref_path_raw = get_env_var( self.for_machine, self.environment.is_cross_build(), diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py index 5931fd1..5d2dd7f 100644 --- a/mesonbuild/cmake/interpreter.py +++ b/mesonbuild/cmake/interpreter.py @@ -578,7 +578,7 @@ class ConverterTarget: @lru_cache(maxsize=None) def _all_lang_stds(self, lang: str) -> T.List[str]: try: - res = self.env.coredata.compiler_options[OptionKey('std', machine=MachineChoice.BUILD, lang=lang)].choices # type: ignore + res = self.env.coredata.compiler_options[OptionKey('std', machine=MachineChoice.BUILD, lang=lang)].choices except KeyError: return [] diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 40bb9e6..234ce06 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -265,36 +265,32 @@ cuda_debug_args = {False: [], clike_debug_args = {False: [], True: ['-g']} # type: T.Dict[bool, T.List[str]] -base_options = {'b_pch': coredata.UserBooleanOption('Use precompiled headers', True), - 'b_lto': coredata.UserBooleanOption('Use link time optimization', False), - 'b_sanitize': coredata.UserComboOption('Code sanitizer to use', - ['none', 'address', 'thread', 'undefined', 'memory', 'address,undefined'], - 'none'), - 'b_lundef': coredata.UserBooleanOption('Use -Wl,--no-undefined when linking', True), - 'b_asneeded': coredata.UserBooleanOption('Use -Wl,--as-needed when linking', True), - 'b_pgo': coredata.UserComboOption('Use profile guided optimization', - ['off', 'generate', 'use'], - 'off'), - 'b_coverage': coredata.UserBooleanOption('Enable coverage tracking.', - False), - 'b_colorout': coredata.UserComboOption('Use colored output', - ['auto', 'always', 'never'], - 'always'), - 'b_ndebug': coredata.UserComboOption('Disable asserts', - ['true', 'false', 'if-release'], 'false'), - 'b_staticpic': coredata.UserBooleanOption('Build static libraries as position independent', - True), - 'b_pie': coredata.UserBooleanOption('Build executables as position independent', - False), - 'b_bitcode': coredata.UserBooleanOption('Generate and embed bitcode (only macOS/iOS/tvOS)', - False), - 'b_vscrt': coredata.UserComboOption('VS run-time library type to use.', - ['none', 'md', 'mdd', 'mt', 'mtd', 'from_buildtype', 'static_from_buildtype'], - 'from_buildtype'), - } # type: OptionDictType - -def option_enabled(boptions: T.List[str], options: 'OptionDictType', - option: str) -> bool: +base_options: 'KeyedOptionDictType' = { + OptionKey('b_pch'): coredata.UserBooleanOption('Use precompiled headers', True), + OptionKey('b_lto'): coredata.UserBooleanOption('Use link time optimization', False), + OptionKey('b_sanitize'): coredata.UserComboOption('Code sanitizer to use', + ['none', 'address', 'thread', 'undefined', 'memory', 'address,undefined'], + 'none'), + OptionKey('b_lundef'): coredata.UserBooleanOption('Use -Wl,--no-undefined when linking', True), + OptionKey('b_asneeded'): coredata.UserBooleanOption('Use -Wl,--as-needed when linking', True), + OptionKey('b_pgo'): coredata.UserComboOption('Use profile guided optimization', + ['off', 'generate', 'use'], + 'off'), + OptionKey('b_coverage'): coredata.UserBooleanOption('Enable coverage tracking.', False), + OptionKey('b_colorout'): coredata.UserComboOption('Use colored output', + ['auto', 'always', 'never'], + 'always'), + OptionKey('b_ndebug'): coredata.UserComboOption('Disable asserts', ['true', 'false', 'if-release'], 'false'), + OptionKey('b_staticpic'): coredata.UserBooleanOption('Build static libraries as position independent', True), + OptionKey('b_pie'): coredata.UserBooleanOption('Build executables as position independent', False), + OptionKey('b_bitcode'): coredata.UserBooleanOption('Generate and embed bitcode (only macOS/iOS/tvOS)', False), + OptionKey('b_vscrt'): coredata.UserComboOption('VS run-time library type to use.', + ['none', 'md', 'mdd', 'mt', 'mtd', 'from_buildtype', 'static_from_buildtype'], + 'from_buildtype'), +} + +def option_enabled(boptions: T.Set[OptionKey], options: 'KeyedOptionDictType', + option: OptionKey) -> bool: try: if option not in boptions: return False @@ -304,23 +300,23 @@ def option_enabled(boptions: T.List[str], options: 'OptionDictType', except KeyError: return False -def get_base_compile_args(options: 'OptionDictType', compiler: 'Compiler') -> T.List[str]: +def get_base_compile_args(options: 'KeyedOptionDictType', compiler: 'Compiler') -> T.List[str]: args = [] # type T.List[str] try: - if options['b_lto'].value: + if options[OptionKey('b_lto')].value: args.extend(compiler.get_lto_compile_args()) except KeyError: pass try: - args += compiler.get_colorout_args(options['b_colorout'].value) + args += compiler.get_colorout_args(options[OptionKey('b_colorout')].value) except KeyError: pass try: - args += compiler.sanitizer_compile_args(options['b_sanitize'].value) + args += compiler.sanitizer_compile_args(options[OptionKey('b_sanitize')].value) except KeyError: pass try: - pgo_val = options['b_pgo'].value + pgo_val = options[OptionKey('b_pgo')].value if pgo_val == 'generate': args.extend(compiler.get_profile_generate_args()) elif pgo_val == 'use': @@ -328,23 +324,23 @@ def get_base_compile_args(options: 'OptionDictType', compiler: 'Compiler') -> T. except KeyError: pass try: - if options['b_coverage'].value: + if options[OptionKey('b_coverage')].value: args += compiler.get_coverage_args() except KeyError: pass try: - if (options['b_ndebug'].value == 'true' or - (options['b_ndebug'].value == 'if-release' and - options['buildtype'].value in {'release', 'plain'})): + if (options[OptionKey('b_ndebug')].value == 'true' or + (options[OptionKey('b_ndebug')].value == 'if-release' and + options[OptionKey('buildtype')].value in {'release', 'plain'})): args += compiler.get_disable_assert_args() except KeyError: pass # This does not need a try...except - if option_enabled(compiler.base_options, options, 'b_bitcode'): + if option_enabled(compiler.base_options, options, OptionKey('b_bitcode')): args.append('-fembed-bitcode') try: - crt_val = options['b_vscrt'].value - buildtype = options['buildtype'].value + crt_val = options[OptionKey('b_vscrt')].value + buildtype = options[OptionKey('buildtype')].value try: args += compiler.get_crt_compile_args(crt_val, buildtype) except AttributeError: @@ -353,20 +349,20 @@ def get_base_compile_args(options: 'OptionDictType', compiler: 'Compiler') -> T. pass return args -def get_base_link_args(options: 'OptionDictType', linker: 'Compiler', +def get_base_link_args(options: 'KeyedOptionDictType', linker: 'Compiler', is_shared_module: bool) -> T.List[str]: args = [] # type: T.List[str] try: - if options['b_lto'].value: + if options[OptionKey('b_lto')].value: args.extend(linker.get_lto_link_args()) except KeyError: pass try: - args += linker.sanitizer_link_args(options['b_sanitize'].value) + args += linker.sanitizer_link_args(options[OptionKey('b_sanitize')].value) except KeyError: pass try: - pgo_val = options['b_pgo'].value + pgo_val = options[OptionKey('b_pgo')].value if pgo_val == 'generate': args.extend(linker.get_profile_generate_args()) elif pgo_val == 'use': @@ -374,13 +370,13 @@ def get_base_link_args(options: 'OptionDictType', linker: 'Compiler', except KeyError: pass try: - if options['b_coverage'].value: + if options[OptionKey('b_coverage')].value: args += linker.get_coverage_link_args() except KeyError: pass - as_needed = option_enabled(linker.base_options, options, 'b_asneeded') - bitcode = option_enabled(linker.base_options, options, 'b_bitcode') + as_needed = option_enabled(linker.base_options, options, OptionKey('b_asneeded')) + bitcode = option_enabled(linker.base_options, options, OptionKey('b_bitcode')) # Shared modules cannot be built with bitcode_bundle because # -bitcode_bundle is incompatible with -undefined and -bundle if bitcode and not is_shared_module: @@ -394,14 +390,14 @@ def get_base_link_args(options: 'OptionDictType', linker: 'Compiler', if not bitcode: args.extend(linker.headerpad_args()) if (not is_shared_module and - option_enabled(linker.base_options, options, 'b_lundef')): + option_enabled(linker.base_options, options, OptionKey('b_lundef'))): args.extend(linker.no_undefined_link_args()) else: args.extend(linker.get_allow_undefined_link_args()) try: - crt_val = options['b_vscrt'].value - buildtype = options['buildtype'].value + crt_val = options[OptionKey('b_vscrt')].value + buildtype = options[OptionKey('buildtype')].value try: args += linker.get_crt_link_args(crt_val, buildtype) except AttributeError: @@ -477,7 +473,7 @@ class Compiler(metaclass=abc.ABCMeta): self.version = version self.full_version = full_version self.for_machine = for_machine - self.base_options = [] # type: T.List[str] + self.base_options: T.Set[OptionKey] = set() self.linker = linker self.info = info self.is_cross = is_cross @@ -1248,14 +1244,14 @@ def get_global_options(lang: str, description = 'Extra arguments passed to the {}'.format(lang) argkey = OptionKey('args', lang=lang, machine=for_machine) largkey = argkey.evolve('link_args') - opts = { + opts: 'KeyedOptionDictType' = { argkey: coredata.UserArrayOption( description + ' compiler', [], split_args=True, user_input=True, allow_dups=True), largkey: coredata.UserArrayOption( description + ' linker', [], split_args=True, user_input=True, allow_dups=True), - } # type: OptionDictType + } # Get from env vars. compile_args, link_args = get_args_from_envvars( diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index b94beb6..2e94e48 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -664,7 +664,7 @@ class VisualStudioCPPCompiler(CPP11AsCPP14Mixin, VisualStudioLikeCPPCompilerMixi CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, info, exe_wrapper, linker=linker, full_version=full_version) MSVCCompiler.__init__(self, target) - self.base_options = ['b_pch', 'b_vscrt', 'b_ndebug'] # FIXME add lto, pgo and the like + self.base_options = {OptionKey(o) for o in ['b_pch', 'b_vscrt', 'b_ndebug']} # FIXME add lto, pgo and the like self.id = 'msvc' def get_options(self) -> 'KeyedOptionDictType': diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py index ca6de38..eac2aa7 100644 --- a/mesonbuild/compilers/d.py +++ b/mesonbuild/compilers/d.py @@ -18,7 +18,7 @@ import subprocess import typing as T from ..mesonlib import ( - EnvironmentException, MachineChoice, version_compare, + EnvironmentException, MachineChoice, version_compare, OptionKey, ) from ..arglist import CompilerArgs @@ -653,8 +653,10 @@ class GnuDCompiler(GnuCompiler, DCompiler): '1': default_warn_args, '2': default_warn_args + ['-Wextra'], '3': default_warn_args + ['-Wextra', '-Wpedantic']} - self.base_options = ['b_colorout', 'b_sanitize', 'b_staticpic', - 'b_vscrt', 'b_coverage', 'b_pgo', 'b_ndebug'] + self.base_options = { + OptionKey(o) for o in [ + 'b_colorout', 'b_sanitize', 'b_staticpic', 'b_vscrt', + 'b_coverage', 'b_pgo', 'b_ndebug']} self._has_color_support = version_compare(self.version, '>=4.9') # dependencies were implemented before, but broken - support was fixed in GCC 7.1+ @@ -724,7 +726,7 @@ class LLVMDCompiler(DmdLikeCompilerMixin, DCompiler): full_version=full_version, is_cross=is_cross) DmdLikeCompilerMixin.__init__(self, dmd_frontend_version=find_ldc_dmd_frontend_version(version_output)) self.id = 'llvm' - self.base_options = ['b_coverage', 'b_colorout', 'b_vscrt', 'b_ndebug'] + self.base_options = {OptionKey(o) for o in ['b_coverage', 'b_colorout', 'b_vscrt', 'b_ndebug']} def get_colorout_args(self, colortype: str) -> T.List[str]: if colortype == 'always': @@ -782,7 +784,7 @@ class DmdDCompiler(DmdLikeCompilerMixin, DCompiler): full_version=full_version, is_cross=is_cross) DmdLikeCompilerMixin.__init__(self, version) self.id = 'dmd' - self.base_options = ['b_coverage', 'b_colorout', 'b_vscrt', 'b_ndebug'] + self.base_options = {OptionKey(o) for o in ['b_coverage', 'b_colorout', 'b_vscrt', 'b_ndebug']} def get_colorout_args(self, colortype: str) -> T.List[str]: if colortype == 'always': diff --git a/mesonbuild/compilers/mixins/arm.py b/mesonbuild/compilers/mixins/arm.py index ee7d337..beb5fd5 100644 --- a/mesonbuild/compilers/mixins/arm.py +++ b/mesonbuild/compilers/mixins/arm.py @@ -19,6 +19,7 @@ import typing as T from ... import mesonlib from ...linkers import ArmClangDynamicLinker +from ...mesonlib import OptionKey from ..compilers import clike_debug_args from .clang import clang_color_args @@ -145,8 +146,10 @@ class ArmclangCompiler(Compiler): if not mesonlib.version_compare(self.version, '==' + self.linker.version): raise mesonlib.EnvironmentException('armlink version does not match with compiler version') self.id = 'armclang' - self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage', - 'b_ndebug', 'b_staticpic', 'b_colorout'] + self.base_options = { + OptionKey(o) for o in + ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage', + 'b_ndebug', 'b_staticpic', 'b_colorout']} # Assembly self.can_compile_suffixes.add('s') diff --git a/mesonbuild/compilers/mixins/clang.py b/mesonbuild/compilers/mixins/clang.py index 2e50577..fcb2225 100644 --- a/mesonbuild/compilers/mixins/clang.py +++ b/mesonbuild/compilers/mixins/clang.py @@ -20,6 +20,7 @@ import typing as T from ... import mesonlib from ...linkers import AppleDynamicLinker +from ...mesonlib import OptionKey from ..compilers import CompileCheckMode from .gnu import GnuLikeCompiler @@ -48,11 +49,11 @@ class ClangCompiler(GnuLikeCompiler): super().__init__() self.id = 'clang' self.defines = defines or {} - self.base_options.append('b_colorout') + self.base_options.add(OptionKey('b_colorout')) # TODO: this really should be part of the linker base_options, but # linkers don't have base_options. if isinstance(self.linker, AppleDynamicLinker): - self.base_options.append('b_bitcode') + self.base_options.add(OptionKey('b_bitcode')) # All Clang backends can also do LLVM IR self.can_compile_suffixes.add('ll') @@ -108,7 +109,7 @@ class ClangCompiler(GnuLikeCompiler): else: # Shouldn't work, but it'll be checked explicitly in the OpenMP dependency. return [] - + @classmethod def use_linker_args(cls, linker: str) -> T.List[str]: # Clang additionally can use a linker specified as a path, which GCC diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index dca09ea..0f30f55 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -35,6 +35,7 @@ from ... import mesonlib from ... import mlog from ...linkers import GnuLikeDynamicLinkerMixin, SolarisDynamicLinker, CompCertDynamicLinker from ...mesonlib import LibType +from ...coredata import OptionKey from .. import compilers from ..compilers import CompileCheckMode from .visualstudio import VisualStudioLikeCompiler @@ -393,14 +394,16 @@ class CLikeCompiler(Compiler): # linking with static libraries since MSVC won't select a CRT for # us in that case and will error out asking us to pick one. try: - crt_val = env.coredata.base_options['b_vscrt'].value - buildtype = env.coredata.builtins['buildtype'].value + crt_val = env.coredata.base_options[OptionKey('b_vscrt')].value + buildtype = env.coredata.builtins[OptionKey('buildtype')].value cargs += self.get_crt_compile_args(crt_val, buildtype) except (KeyError, AttributeError): pass # Add CFLAGS/CXXFLAGS/OBJCFLAGS/OBJCXXFLAGS and CPPFLAGS from the env sys_args = env.coredata.get_external_args(self.for_machine, self.language) + if isinstance(sys_args, str): + sys_args = [sys_args] # Apparently it is a thing to inject linker flags both # via CFLAGS _and_ LDFLAGS, even though the former are # also used during linking. These flags can break diff --git a/mesonbuild/compilers/mixins/elbrus.py b/mesonbuild/compilers/mixins/elbrus.py index 2ea3599..16f6210 100644 --- a/mesonbuild/compilers/mixins/elbrus.py +++ b/mesonbuild/compilers/mixins/elbrus.py @@ -21,7 +21,7 @@ import re from .gnu import GnuLikeCompiler from .gnu import gnu_optimization_args -from ...mesonlib import Popen_safe +from ...mesonlib import Popen_safe, OptionKey if T.TYPE_CHECKING: from ...environment import Environment @@ -34,9 +34,7 @@ class ElbrusCompiler(GnuLikeCompiler): def __init__(self) -> None: super().__init__() self.id = 'lcc' - self.base_options = ['b_pgo', 'b_coverage', - 'b_ndebug', 'b_staticpic', - 'b_lundef', 'b_asneeded'] + self.base_options = {OptionKey(o) for o in ['b_pgo', 'b_coverage', 'b_ndebug', 'b_staticpic', 'b_lundef', 'b_asneeded']} # FIXME: use _build_wrapper to call this so that linker flags from the env # get applied diff --git a/mesonbuild/compilers/mixins/emscripten.py b/mesonbuild/compilers/mixins/emscripten.py index b480de3..537ae92 100644 --- a/mesonbuild/compilers/mixins/emscripten.py +++ b/mesonbuild/compilers/mixins/emscripten.py @@ -51,7 +51,7 @@ class EmscriptenMixin(Compiler): def thread_link_flags(self, env: 'Environment') -> T.List[str]: args = ['-s', 'USE_PTHREADS=1'] - count: int = env.coredata.compiler_options[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 if count: args.extend(['-s', 'PTHREAD_POOL_SIZE={}'.format(count)]) return args diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py index 3d43162..95bcd7c 100644 --- a/mesonbuild/compilers/mixins/gnu.py +++ b/mesonbuild/compilers/mixins/gnu.py @@ -24,6 +24,7 @@ import typing as T from ... import mesonlib from ... import mlog +from ...mesonlib import OptionKey if T.TYPE_CHECKING: from ...environment import Environment @@ -146,14 +147,15 @@ class GnuLikeCompiler(Compiler, metaclass=abc.ABCMeta): LINKER_PREFIX = '-Wl,' def __init__(self) -> None: - self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_coverage', - 'b_ndebug', 'b_staticpic', 'b_pie'] + self.base_options = { + OptionKey(o) for o in ['b_pch', 'b_lto', 'b_pgo', 'b_coverage', + 'b_ndebug', 'b_staticpic', 'b_pie']} if not (self.info.is_windows() or self.info.is_cygwin() or self.info.is_openbsd()): - self.base_options.append('b_lundef') + self.base_options.add(OptionKey('b_lundef')) if not self.info.is_windows() or self.info.is_cygwin(): - self.base_options.append('b_asneeded') + self.base_options.add(OptionKey('b_asneeded')) if not self.info.is_hurd(): - self.base_options.append('b_sanitize') + self.base_options.add(OptionKey('b_sanitize')) # All GCC-like backends can do assembly self.can_compile_suffixes.add('s') @@ -328,7 +330,7 @@ class GnuCompiler(GnuLikeCompiler): super().__init__() self.id = 'gcc' self.defines = defines or {} - self.base_options.append('b_colorout') + self.base_options.add(OptionKey('b_colorout')) def get_colorout_args(self, colortype: str) -> T.List[str]: if mesonlib.version_compare(self.version, '>=4.9.0'): diff --git a/mesonbuild/compilers/mixins/intel.py b/mesonbuild/compilers/mixins/intel.py index 442e8c7..5bca254 100644 --- a/mesonbuild/compilers/mixins/intel.py +++ b/mesonbuild/compilers/mixins/intel.py @@ -79,8 +79,9 @@ class IntelGnuLikeCompiler(GnuLikeCompiler): # It does have IPO, which serves much the same purpose as LOT, but # there is an unfortunate rule for using IPO (you can't control the # name of the output file) which break assumptions meson makes - self.base_options = ['b_pch', 'b_lundef', 'b_asneeded', 'b_pgo', - 'b_coverage', 'b_ndebug', 'b_staticpic', 'b_pie'] + self.base_options = {mesonlib.OptionKey(o) for o in [ + 'b_pch', 'b_lundef', 'b_asneeded', 'b_pgo', 'b_coverage', + 'b_ndebug', 'b_staticpic', 'b_pie']} self.id = 'intel' self.lang_header = 'none' diff --git a/mesonbuild/compilers/mixins/pgi.py b/mesonbuild/compilers/mixins/pgi.py index 61dee8d..8461574 100644 --- a/mesonbuild/compilers/mixins/pgi.py +++ b/mesonbuild/compilers/mixins/pgi.py @@ -19,6 +19,7 @@ import os from pathlib import Path from ..compilers import clike_debug_args, clike_optimization_args +from ...mesonlib import OptionKey if T.TYPE_CHECKING: from ...environment import Environment @@ -43,7 +44,7 @@ pgi_buildtype_args = { class PGICompiler(Compiler): def __init__(self) -> None: - self.base_options = ['b_pch'] + self.base_options = {OptionKey('b_pch')} self.id = 'pgi' default_warn_args = ['-Minform=inform'] diff --git a/mesonbuild/compilers/mixins/visualstudio.py b/mesonbuild/compilers/mixins/visualstudio.py index c38d59a..92f4fcd 100644 --- a/mesonbuild/compilers/mixins/visualstudio.py +++ b/mesonbuild/compilers/mixins/visualstudio.py @@ -129,7 +129,7 @@ class VisualStudioLikeCompiler(Compiler, metaclass=abc.ABCMeta): INVOKES_LINKER = False def __init__(self, target: str): - self.base_options = ['b_pch', 'b_ndebug', 'b_vscrt'] # FIXME add lto, pgo and the like + self.base_options = {mesonlib.OptionKey(o) for o in ['b_pch', 'b_ndebug', 'b_vscrt']} # FIXME add lto, pgo and the like self.target = target self.is_64 = ('x64' in target) or ('x86_64' in target) # do some canonicalization of target machine diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index 8a1acc7..fd58819 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -55,9 +55,9 @@ class RustCompiler(Compiler): linker=linker) self.exe_wrapper = exe_wrapper self.id = 'rustc' - self.base_options.append('b_colorout') + self.base_options.add(OptionKey('b_colorout')) if 'link' in self.linker.id: - self.base_options.append('b_vscrt') + self.base_options.add(OptionKey('b_vscrt')) def needs_static_linker(self) -> bool: return False diff --git a/mesonbuild/compilers/vala.py b/mesonbuild/compilers/vala.py index 14971d4..80e91f6 100644 --- a/mesonbuild/compilers/vala.py +++ b/mesonbuild/compilers/vala.py @@ -16,7 +16,7 @@ import os.path import typing as T from .. import mlog -from ..mesonlib import EnvironmentException, MachineChoice, version_compare +from ..mesonlib import EnvironmentException, MachineChoice, version_compare, OptionKey from .compilers import Compiler, LibType @@ -33,7 +33,7 @@ class ValaCompiler(Compiler): super().__init__(exelist, version, for_machine, info, is_cross=is_cross) self.version = version self.id = 'valac' - self.base_options = ['b_colorout'] + self.base_options = {OptionKey('b_colorout')} def needs_static_linker(self) -> bool: return False # Because compiles into C. diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index f09c398..6d6583f 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -21,7 +21,7 @@ from collections import OrderedDict, defaultdict from .mesonlib import ( MesonException, EnvironmentException, MachineChoice, PerMachine, default_libdir, default_libexecdir, default_prefix, split_args, - OptionKey, + OptionKey, OptionType, ) from .wrap import WrapMode import ast @@ -301,16 +301,17 @@ class DependencyCache: successfully lookup by providing a simple get/put interface. """ - def __init__(self, builtins_per_machine: PerMachine[T.Dict[str, UserOption[T.Any]]], for_machine: MachineChoice): + def __init__(self, builtins: 'KeyedOptionDictType', for_machine: MachineChoice): self.__cache = OrderedDict() # type: T.MutableMapping[CacheKeyType, DependencySubCache] - self.__builtins_per_machine = builtins_per_machine - self.__for_machine = for_machine + self.__builtins = builtins + self.__pkg_conf_key = OptionKey('pkg_config_path', machine=for_machine) + self.__cmake_key = OptionKey('cmake_prefix_path', machine=for_machine) def __calculate_subkey(self, type_: DependencyCacheType) -> T.Tuple[T.Any, ...]: if type_ is DependencyCacheType.PKG_CONFIG: - return tuple(self.__builtins_per_machine[self.__for_machine]['pkg_config_path'].value) + return tuple(self.__builtins[self.__pkg_conf_key].value) elif type_ is DependencyCacheType.CMAKE: - return tuple(self.__builtins_per_machine[self.__for_machine]['cmake_prefix_path'].value) + return tuple(self.__builtins[self.__cmake_key].value) assert type_ is DependencyCacheType.OTHER, 'Someone forgot to update subkey calculations for a new type' return tuple() @@ -384,17 +385,16 @@ class CoreData: self.meson_command = meson_command self.target_guids = {} self.version = version - self.builtins = {} # type: OptionDictType - self.builtins_per_machine: PerMachine['OptionDictType'] = PerMachine({}, {}) + self.builtins: 'KeyedOptionDictType' = {} self.backend_options: 'KeyedOptionDictType' = {} self.user_options: 'KeyedOptionDictType' = {} self.compiler_options: 'KeyedOptionDictType' = {} - self.base_options = {} # type: OptionDictType + self.base_options: 'KeyedOptionDictType' = {} self.cross_files = self.__load_config_files(options, scratch_dir, 'cross') self.compilers = PerMachine(OrderedDict(), OrderedDict()) # type: PerMachine[T.Dict[str, Compiler]] - build_cache = DependencyCache(self.builtins_per_machine, MachineChoice.BUILD) - host_cache = DependencyCache(self.builtins_per_machine, MachineChoice.BUILD) + build_cache = DependencyCache(self.builtins, MachineChoice.BUILD) + host_cache = DependencyCache(self.builtins, MachineChoice.BUILD) self.deps = PerMachine(build_cache, host_cache) # type: PerMachine[DependencyCache] self.compiler_check_cache = OrderedDict() # type: T.Dict[CompilerCheckCacheKey, compiler.CompileResult] @@ -466,7 +466,7 @@ class CoreData: # getting the "system default" is always wrong on multiarch # platforms as it gets a value like lib/x86_64-linux-gnu. if self.cross_files: - BUILTIN_OPTIONS['libdir'].default = 'lib' + BUILTIN_OPTIONS[OptionKey('libdir')].default = 'lib' def sanitize_prefix(self, prefix): prefix = os.path.expanduser(prefix) @@ -486,7 +486,7 @@ class CoreData: prefix = prefix[:-1] return prefix - def sanitize_dir_option_value(self, prefix: str, option: str, value: T.Any) -> T.Any: + def sanitize_dir_option_value(self, prefix: str, option: OptionKey, value: T.Any) -> T.Any: ''' If the option is an installation directory option and the value is an absolute path, check that it resides within prefix and return the value @@ -501,13 +501,13 @@ class CoreData: value = PurePath(value) except TypeError: return value - if option.endswith('dir') and value.is_absolute() and \ - option not in builtin_dir_noprefix_options: + if option.name.endswith('dir') and value.is_absolute() and \ + option not in BULITIN_DIR_NOPREFIX_OPTIONS: # Value must be a subdir of the prefix # commonpath will always return a path in the native format, so we # must use pathlib.PurePath to do the same conversion before # comparing. - msg = ('The value of the {!r} option is \'{!s}\' which must be a ' + msg = ('The value of the \'{!s}\' option is \'{!s}\' which must be a ' 'subdir of the prefix {!r}.\nNote that if you pass a ' 'relative path, it is assumed to be a subdir of prefix.') # os.path.commonpath doesn't understand case-insensitive filesystems, @@ -520,25 +520,25 @@ class CoreData: raise MesonException(msg.format(option, value, prefix)) return value.as_posix() - def init_builtins(self, subproject: str): + def init_builtins(self, subproject: str) -> None: # Create builtin options with default values for key, opt in BUILTIN_OPTIONS.items(): - self.add_builtin_option(self.builtins, key, opt, subproject) + self.add_builtin_option(self.builtins, key.evolve(subproject=subproject), opt) for for_machine in iter(MachineChoice): for key, opt in BUILTIN_OPTIONS_PER_MACHINE.items(): - self.add_builtin_option(self.builtins_per_machine[for_machine], key, opt, subproject) + self.add_builtin_option(self.builtins, key.evolve(subproject=subproject, machine=for_machine), opt) - def add_builtin_option(self, opts_map, key, opt, subproject): - if subproject: + @staticmethod + def add_builtin_option(opts_map: 'KeyedOptionDictType', key: OptionKey, + opt: 'BuiltinOption') -> None: + if key.subproject: if opt.yielding: # This option is global and not per-subproject return - optname = subproject + ':' + key - value = opts_map[key].value + value = opts_map[key.as_root()].value else: - optname = key value = None - opts_map[optname] = opt.init_option(key, value, default_prefix()) + opts_map[key] = opt.init_option(key, value, default_prefix()) def init_backend_options(self, backend_name: str) -> None: if backend_name == 'ninja': @@ -552,45 +552,43 @@ class CoreData: '') def get_builtin_option(self, optname: str, subproject: str = '') -> T.Union[str, int, bool]: - raw_optname = optname - if subproject: - optname = subproject + ':' + optname + key = OptionKey.from_string(optname).evolve(subproject=subproject) for opts in self._get_all_builtin_options(): - v = opts.get(optname) + v = opts.get(str(key)) if v is None or v.yielding: - v = opts.get(raw_optname) + v = opts.get(str(key.as_root())) if v is None: continue - if raw_optname == 'wrap_mode': + if key.name == 'wrap_mode': return WrapMode.from_string(v.value) return v.value - raise RuntimeError('Tried to get unknown builtin option %s.' % raw_optname) + raise RuntimeError(f'Tried to get unknown builtin option {key.name}.') - def _try_set_builtin_option(self, optname, value): + def _try_set_builtin_option(self, key: OptionKey, value) -> bool: for opts in self._get_all_builtin_options(): - opt = opts.get(optname) + opt = opts.get(str(key)) if opt is None: continue - if optname == 'prefix': + if key.name == 'prefix': value = self.sanitize_prefix(value) else: - prefix = self.builtins['prefix'].value - value = self.sanitize_dir_option_value(prefix, optname, value) + prefix = self.builtins[OptionKey('prefix')].value + value = self.sanitize_dir_option_value(prefix, key, value) break else: return False opt.set_value(value) # Make sure that buildtype matches other settings. - if optname == 'buildtype': + if key.name == 'buildtype': self.set_others_from_buildtype(value) else: self.set_buildtype_from_others() return True - def set_builtin_option(self, optname, value): + def set_builtin_option(self, optname: OptionKey, value) -> None: res = self._try_set_builtin_option(optname, value) if not res: - raise RuntimeError('Tried to set unknown builtin option %s.' % optname) + raise RuntimeError(f'Tried to set unknown builtin option {str(optname)}') def set_others_from_buildtype(self, value): if value == 'plain': @@ -611,12 +609,12 @@ class CoreData: else: assert(value == 'custom') return - self.builtins['optimization'].set_value(opt) - self.builtins['debug'].set_value(debug) + self.builtins[OptionKey('optimization')].set_value(opt) + self.builtins[OptionKey('debug')].set_value(debug) def set_buildtype_from_others(self): - opt = self.builtins['optimization'].value - debug = self.builtins['debug'].value + opt = self.builtins[OptionKey('optimization')].value + debug = self.builtins[OptionKey('debug')].value if opt == '0' and not debug: mode = 'plain' elif opt == '0' and debug: @@ -629,7 +627,7 @@ class CoreData: mode = 'minsize' else: mode = 'custom' - self.builtins['buildtype'].set_value(mode) + self.builtins[OptionKey('buildtype')].set_value(mode) @classmethod def get_prefixed_options_per_machine( @@ -666,19 +664,18 @@ class CoreData: yield {str(k): v for k, v in self.backend_options.items()} yield {str(k): v for k, v in self.user_options.items()} yield {str(k): v for k, v in self.compiler_options.items()} - yield self.base_options + yield {str(k): v for k, v in self.base_options.items()} def _get_all_builtin_options(self) -> T.Iterable[T.Dict[str, UserOption]]: - yield dict(self.get_prefixed_options_per_machine(self.builtins_per_machine)) - yield self.builtins + yield {str(k): v for k, v in self.builtins.items()} def get_all_options(self) -> T.Iterable[T.Dict[str, UserOption]]: yield from self._get_all_nonbuiltin_options() yield from self._get_all_builtin_options() - def validate_option_value(self, option_name, override_value): + def validate_option_value(self, option_name: OptionKey, override_value): for opts in self.get_all_options(): - opt = opts.get(option_name) + opt = opts.get(str(option_name)) if opt is not None: try: return opt.validate_value(override_value) @@ -718,10 +715,11 @@ class CoreData: return False return len(self.cross_files) > 0 - def copy_build_options_from_regular_ones(self): + def copy_build_options_from_regular_ones(self) -> None: assert not self.is_cross_build() - for k, o in self.builtins_per_machine.host.items(): - self.builtins_per_machine.build[k].set_value(o.value) + for k in BUILTIN_OPTIONS_PER_MACHINE: + o = self.builtins[k] + self.builtins[k.as_build()].set_value(o.value) for bk, bv in self.compiler_options.items(): if bk.machine is MachineChoice.BUILD: hk = bk.as_host() @@ -738,8 +736,8 @@ class CoreData: pfk = OptionKey('prefix') if pfk in options: prefix = self.sanitize_prefix(options[pfk]) - self.builtins['prefix'].set_value(prefix) - for key in builtin_dir_noprefix_options: + self.builtins[OptionKey('prefix')].set_value(prefix) + for key in BULITIN_DIR_NOPREFIX_OPTIONS: if key not in options: self.builtins[key].set_value(BUILTIN_OPTIONS[key].prefixed_default(key, prefix)) @@ -747,7 +745,7 @@ class CoreData: for k, v in options.items(): if k == pfk: continue - if self._try_set_builtin_option(str(k), v): + if self._try_set_builtin_option(k, v): continue for opts in self._get_all_nonbuiltin_options(): tgt = opts.get(str(k)) @@ -783,15 +781,13 @@ class CoreData: # to know which backend we'll use). options: T.MutableMapping[OptionKey, T.Any] = OrderedDict() - from . import optinterpreter for k, v in chain(default_options.items(), env.options.items()): # Subproject: skip options for other subprojects if k.subproject and k.subproject != subproject: continue # Skip base, compiler, and backend options, they are handled when # adding languages and setting backend. - if (k.name not in self.builtins and k.name not in self.builtins_per_machine[k.machine] and - optinterpreter.is_invalid_name(str(k), log=False)): + if k.type in {OptionType.COMPILER, OptionType.BACKEND, OptionType.BASE}: continue options[k] = v @@ -818,20 +814,19 @@ class CoreData: self.compilers[comp.for_machine][lang] = comp self.add_compiler_options(comp.get_options(), lang, comp.for_machine, env) - enabled_opts = [] - for optname in comp.base_options: - if optname in self.base_options: + enabled_opts: T.List[OptionKey] = [] + for key in comp.base_options: + if key in self.base_options: continue - oobj = compilers.base_options[optname] - key = OptionKey(optname, machine=comp.for_machine) + oobj = compilers.base_options[key] if key in env.options: oobj.set_value(env.options[key]) - enabled_opts.append(optname) - self.base_options[optname] = oobj + enabled_opts.append(key) + self.base_options[key] = oobj self.emit_base_options_warnings(enabled_opts) - def emit_base_options_warnings(self, enabled_opts: list): - if 'b_bitcode' in 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) @@ -1007,10 +1002,10 @@ def save(obj: CoreData, build_dir: str) -> str: def register_builtin_arguments(parser: argparse.ArgumentParser) -> None: for n, b in BUILTIN_OPTIONS.items(): - b.add_to_argparse(n, parser, '', '') + b.add_to_argparse(str(n), parser, '') for n, b in BUILTIN_OPTIONS_PER_MACHINE.items(): - b.add_to_argparse(n, parser, '', ' (just for host machine)') - b.add_to_argparse(n, parser, 'build.', ' (just for build machine)') + b.add_to_argparse(str(n), parser, ' (just for host machine)') + b.add_to_argparse(str(n.as_build()), parser, ' (just for build machine)') parser.add_argument('-D', action='append', dest='projectoptions', default=[], metavar="option", help='Set the value of an option, can be used several times to set multiple options.') @@ -1031,14 +1026,14 @@ def parse_cmd_line_options(args: argparse.Namespace) -> None: args.cmd_line_options = create_options_dict(args.projectoptions) # Merge builtin options set with --option into the dict. - for name in chain( + for key in chain( BUILTIN_OPTIONS.keys(), - ('build.' + k for k in BUILTIN_OPTIONS_PER_MACHINE.keys()), + (k.as_build() for k in BUILTIN_OPTIONS_PER_MACHINE.keys()), BUILTIN_OPTIONS_PER_MACHINE.keys(), ): + name = str(key) value = getattr(args, name, None) if value is not None: - key = OptionKey.from_string(name) if key in args.cmd_line_options: cmdline_name = BuiltinOption.argparse_name_to_arg(name) raise MesonException( @@ -1064,7 +1059,7 @@ class BuiltinOption(T.Generic[_T, _U]): self.choices = choices self.yielding = yielding - def init_option(self, name: str, value: T.Optional[T.Any], prefix: str) -> _U: + def init_option(self, name: 'OptionKey', value: T.Optional[T.Any], prefix: str) -> _U: """Create an instance of opt_type and return it.""" if value is None: value = self.prefixed_default(name, prefix) @@ -1095,16 +1090,16 @@ class BuiltinOption(T.Generic[_T, _U]): else: return '--' + name.replace('_', '-') - def prefixed_default(self, name: str, prefix: str = '') -> T.Any: + def prefixed_default(self, name: 'OptionKey', prefix: str = '') -> T.Any: if self.opt_type in [UserComboOption, UserIntegerOption]: return self.default try: - return builtin_dir_noprefix_options[name][prefix] + return BULITIN_DIR_NOPREFIX_OPTIONS[name][prefix] except KeyError: pass return self.default - def add_to_argparse(self, name: str, parser: argparse.ArgumentParser, prefix: str, help_suffix: str) -> None: + def add_to_argparse(self, name: str, parser: argparse.ArgumentParser, help_suffix: str) -> None: kwargs = OrderedDict() c = self._argparse_choices() @@ -1117,64 +1112,65 @@ class BuiltinOption(T.Generic[_T, _U]): if c and not b: kwargs['choices'] = c kwargs['default'] = argparse.SUPPRESS - kwargs['dest'] = prefix + name + kwargs['dest'] = name - cmdline_name = self.argparse_name_to_arg(prefix + name) + cmdline_name = self.argparse_name_to_arg(name) parser.add_argument(cmdline_name, help=h + help_suffix, **kwargs) # Update `docs/markdown/Builtin-options.md` after changing the options below -BUILTIN_DIR_OPTIONS = OrderedDict([ - ('prefix', BuiltinOption(UserStringOption, 'Installation prefix', default_prefix())), - ('bindir', BuiltinOption(UserStringOption, 'Executable directory', 'bin')), - ('datadir', BuiltinOption(UserStringOption, 'Data file directory', 'share')), - ('includedir', BuiltinOption(UserStringOption, 'Header file directory', 'include')), - ('infodir', BuiltinOption(UserStringOption, 'Info page directory', 'share/info')), - ('libdir', BuiltinOption(UserStringOption, 'Library directory', default_libdir())), - ('libexecdir', BuiltinOption(UserStringOption, 'Library executable directory', default_libexecdir())), - ('localedir', BuiltinOption(UserStringOption, 'Locale data directory', 'share/locale')), - ('localstatedir', BuiltinOption(UserStringOption, 'Localstate data directory', 'var')), - ('mandir', BuiltinOption(UserStringOption, 'Manual page directory', 'share/man')), - ('sbindir', BuiltinOption(UserStringOption, 'System executable directory', 'sbin')), - ('sharedstatedir', BuiltinOption(UserStringOption, 'Architecture-independent data directory', 'com')), - ('sysconfdir', BuiltinOption(UserStringOption, 'Sysconf data directory', 'etc')), -]) # type: OptionDictType - -BUILTIN_CORE_OPTIONS = OrderedDict([ - ('auto_features', BuiltinOption(UserFeatureOption, "Override value of all 'auto' features", 'auto')), - ('backend', BuiltinOption(UserComboOption, 'Backend to use', 'ninja', choices=backendlist)), - ('buildtype', BuiltinOption(UserComboOption, 'Build type to use', 'debug', - choices=['plain', 'debug', 'debugoptimized', 'release', 'minsize', 'custom'])), - ('debug', BuiltinOption(UserBooleanOption, 'Debug', True)), - ('default_library', BuiltinOption(UserComboOption, 'Default library type', 'shared', choices=['shared', 'static', 'both'], - yielding=False)), - ('errorlogs', BuiltinOption(UserBooleanOption, "Whether to print the logs from failing tests", True)), - ('install_umask', BuiltinOption(UserUmaskOption, 'Default umask to apply on permissions of installed files', '022')), - ('layout', BuiltinOption(UserComboOption, 'Build directory layout', 'mirror', choices=['mirror', 'flat'])), - ('optimization', BuiltinOption(UserComboOption, 'Optimization level', '0', choices=['0', 'g', '1', '2', '3', 's'])), - ('stdsplit', BuiltinOption(UserBooleanOption, 'Split stdout and stderr in test logs', True)), - ('strip', BuiltinOption(UserBooleanOption, 'Strip targets on install', False)), - ('unity', BuiltinOption(UserComboOption, 'Unity build', 'off', choices=['on', 'off', 'subprojects'])), - ('unity_size', BuiltinOption(UserIntegerOption, 'Unity block size', (2, None, 4))), - ('warning_level', BuiltinOption(UserComboOption, 'Compiler warning level to use', '1', choices=['0', '1', '2', '3'], yielding=False)), - ('werror', BuiltinOption(UserBooleanOption, 'Treat warnings as errors', False, yielding=False)), - ('wrap_mode', BuiltinOption(UserComboOption, 'Wrap mode', 'default', choices=['default', 'nofallback', 'nodownload', 'forcefallback'])), - ('force_fallback_for', BuiltinOption(UserArrayOption, 'Force fallback for those subprojects', [])), -]) # type: OptionDictType +# Also update mesonlib._BUILTIN_NAMES. See the comment there for why this is required. +BUILTIN_DIR_OPTIONS: 'KeyedOptionDictType' = OrderedDict([ + (OptionKey('prefix'), BuiltinOption(UserStringOption, 'Installation prefix', default_prefix())), + (OptionKey('bindir'), BuiltinOption(UserStringOption, 'Executable directory', 'bin')), + (OptionKey('datadir'), BuiltinOption(UserStringOption, 'Data file directory', 'share')), + (OptionKey('includedir'), BuiltinOption(UserStringOption, 'Header file directory', 'include')), + (OptionKey('infodir'), BuiltinOption(UserStringOption, 'Info page directory', 'share/info')), + (OptionKey('libdir'), BuiltinOption(UserStringOption, 'Library directory', default_libdir())), + (OptionKey('libexecdir'), BuiltinOption(UserStringOption, 'Library executable directory', default_libexecdir())), + (OptionKey('localedir'), BuiltinOption(UserStringOption, 'Locale data directory', 'share/locale')), + (OptionKey('localstatedir'), BuiltinOption(UserStringOption, 'Localstate data directory', 'var')), + (OptionKey('mandir'), BuiltinOption(UserStringOption, 'Manual page directory', 'share/man')), + (OptionKey('sbindir'), BuiltinOption(UserStringOption, 'System executable directory', 'sbin')), + (OptionKey('sharedstatedir'), BuiltinOption(UserStringOption, 'Architecture-independent data directory', 'com')), + (OptionKey('sysconfdir'), BuiltinOption(UserStringOption, 'Sysconf data directory', 'etc')), +]) + +BUILTIN_CORE_OPTIONS: 'KeyedOptionDictType' = OrderedDict([ + (OptionKey('auto_features'), BuiltinOption(UserFeatureOption, "Override value of all 'auto' features", 'auto')), + (OptionKey('backend'), BuiltinOption(UserComboOption, 'Backend to use', 'ninja', choices=backendlist)), + (OptionKey('buildtype'), BuiltinOption(UserComboOption, 'Build type to use', 'debug', + choices=['plain', 'debug', 'debugoptimized', 'release', 'minsize', 'custom'])), + (OptionKey('debug'), BuiltinOption(UserBooleanOption, 'Debug', True)), + (OptionKey('default_library'), BuiltinOption(UserComboOption, 'Default library type', 'shared', choices=['shared', 'static', 'both'], + yielding=False)), + (OptionKey('errorlogs'), BuiltinOption(UserBooleanOption, "Whether to print the logs from failing tests", True)), + (OptionKey('install_umask'), BuiltinOption(UserUmaskOption, 'Default umask to apply on permissions of installed files', '022')), + (OptionKey('layout'), BuiltinOption(UserComboOption, 'Build directory layout', 'mirror', choices=['mirror', 'flat'])), + (OptionKey('optimization'), BuiltinOption(UserComboOption, 'Optimization level', '0', choices=['0', 'g', '1', '2', '3', 's'])), + (OptionKey('stdsplit'), BuiltinOption(UserBooleanOption, 'Split stdout and stderr in test logs', True)), + (OptionKey('strip'), BuiltinOption(UserBooleanOption, 'Strip targets on install', False)), + (OptionKey('unity'), BuiltinOption(UserComboOption, 'Unity build', 'off', choices=['on', 'off', 'subprojects'])), + (OptionKey('unity_size'), BuiltinOption(UserIntegerOption, 'Unity block size', (2, None, 4))), + (OptionKey('warning_level'), BuiltinOption(UserComboOption, 'Compiler warning level to use', '1', choices=['0', '1', '2', '3'], yielding=False)), + (OptionKey('werror'), BuiltinOption(UserBooleanOption, 'Treat warnings as errors', False, yielding=False)), + (OptionKey('wrap_mode'), BuiltinOption(UserComboOption, 'Wrap mode', 'default', choices=['default', 'nofallback', 'nodownload', 'forcefallback'])), + (OptionKey('force_fallback_for'), BuiltinOption(UserArrayOption, 'Force fallback for those subprojects', [])), +]) BUILTIN_OPTIONS = OrderedDict(chain(BUILTIN_DIR_OPTIONS.items(), BUILTIN_CORE_OPTIONS.items())) -BUILTIN_OPTIONS_PER_MACHINE = OrderedDict([ - ('pkg_config_path', BuiltinOption(UserArrayOption, 'List of additional paths for pkg-config to search', [])), - ('cmake_prefix_path', BuiltinOption(UserArrayOption, 'List of additional prefixes for cmake to search', [])), +BUILTIN_OPTIONS_PER_MACHINE: 'KeyedOptionDictType' = OrderedDict([ + (OptionKey('pkg_config_path'), BuiltinOption(UserArrayOption, 'List of additional paths for pkg-config to search', [])), + (OptionKey('cmake_prefix_path'), BuiltinOption(UserArrayOption, 'List of additional prefixes for cmake to search', [])), ]) # Special prefix-dependent defaults for installation directories that reside in # a path outside of the prefix in FHS and common usage. -builtin_dir_noprefix_options = { - 'sysconfdir': {'/usr': '/etc'}, - 'localstatedir': {'/usr': '/var', '/usr/local': '/var/local'}, - 'sharedstatedir': {'/usr': '/var/lib', '/usr/local': '/var/local/lib'}, +BULITIN_DIR_NOPREFIX_OPTIONS: T.Dict[OptionKey, T.Dict[str, str]] = { + OptionKey('sysconfdir'): {'/usr': '/etc'}, + OptionKey('localstatedir'): {'/usr': '/var', '/usr/local': '/var/local'}, + OptionKey('sharedstatedir'): {'/usr': '/var/lib', '/usr/local': '/var/local/lib'}, } FORBIDDEN_TARGET_NAMES = {'clean': None, diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 6f568d3..7b8045d 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -37,7 +37,7 @@ from ..environment import Environment, MachineInfo from ..cmake import CMakeExecutor, CMakeTraceParser, CMakeException, CMakeToolchain, CMakeExecScope, check_cmake_args from ..mesonlib import MachineChoice, MesonException, OrderedSet, PerMachine from ..mesonlib import Popen_safe, version_compare_many, version_compare, listify, stringlistify, extract_as_list, split_args -from ..mesonlib import Version, LibType +from ..mesonlib import Version, LibType, OptionKey from ..mesondata import mesondata if T.TYPE_CHECKING: @@ -656,8 +656,9 @@ class PkgConfigDependency(ExternalDependency): return rc, out, err @staticmethod - def setup_env(env, environment, for_machine, extra_path=None): - extra_paths = environment.coredata.builtins_per_machine[for_machine]['pkg_config_path'].value + def setup_env(env: T.MutableMapping[str, str], environment: 'Environment', for_machine: MachineChoice, + extra_path: T.Optional[str] = None) -> None: + extra_paths: T.List[str] = environment.coredata.builtins[OptionKey('pkg_config_path', machine=for_machine)].value if extra_path: extra_paths.append(extra_path) sysroot = environment.properties[for_machine].get_sys_root() @@ -1484,9 +1485,9 @@ class CMakeDependency(ExternalDependency): cfgs = [x for x in tgt.properties['IMPORTED_CONFIGURATIONS'] if x] cfg = cfgs[0] - if 'b_vscrt' in self.env.coredata.base_options: + if OptionKey('b_vscrt') in self.env.coredata.base_options: is_debug = self.env.coredata.get_builtin_option('buildtype') == 'debug' - if self.env.coredata.base_options['b_vscrt'].value in ('mdd', 'mtd'): + if self.env.coredata.base_options[OptionKey('b_vscrt')].value in {'mdd', 'mtd'}: is_debug = True else: is_debug = self.env.coredata.get_builtin_option('debug') diff --git a/mesonbuild/dependencies/boost.py b/mesonbuild/dependencies/boost.py index 370fa72..e2e4789 100644 --- a/mesonbuild/dependencies/boost.py +++ b/mesonbuild/dependencies/boost.py @@ -616,8 +616,8 @@ class BoostDependency(ExternalDependency): # MSVC is very picky with the library tags vscrt = '' try: - crt_val = self.env.coredata.base_options['b_vscrt'].value - buildtype = self.env.coredata.builtins['buildtype'].value + crt_val = self.env.coredata.base_options[mesonlib.OptionKey('b_vscrt')].value + buildtype = self.env.coredata.builtins[mesonlib.OptionKey('buildtype')].value vscrt = self.clib_compiler.get_crt_compile_args(crt_val, buildtype)[0] except (KeyError, IndexError, AttributeError): pass diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py index baf8e94..942f69a 100644 --- a/mesonbuild/dependencies/ui.py +++ b/mesonbuild/dependencies/ui.py @@ -383,8 +383,8 @@ class QtBaseDependency(ExternalDependency): # Use the buildtype by default, but look at the b_vscrt option if the # compiler supports it. is_debug = self.env.coredata.get_builtin_option('buildtype') == 'debug' - if 'b_vscrt' in self.env.coredata.base_options: - if self.env.coredata.base_options['b_vscrt'].value in ('mdd', 'mtd'): + if mesonlib.OptionKey('b_vscrt') in self.env.coredata.base_options: + if self.env.coredata.base_options[mesonlib.OptionKey('b_vscrt')].value in {'mdd', 'mtd'}: is_debug = True modules_lib_suffix = self._get_modules_lib_suffix(is_debug) diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index b4336eb..1329ecf 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -11,7 +11,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import pdb from . import mparser from . import environment from . import coredata @@ -22,7 +21,7 @@ from . import optinterpreter from . import compilers from .wrap import wrap, WrapMode from . import mesonlib -from .mesonlib import FileMode, MachineChoice, Popen_safe, listify, extract_as_list, has_path_sep, unholder +from .mesonlib import FileMode, MachineChoice, OptionKey, Popen_safe, listify, extract_as_list, has_path_sep, unholder from .dependencies import ExternalProgram from .dependencies import InternalDependency, Dependency, NotFoundDependency, DependencyException from .depfile import DepFile @@ -83,7 +82,7 @@ class FeatureOptionHolder(InterpreterObject, ObjectHolder): InterpreterObject.__init__(self) ObjectHolder.__init__(self, option) if option.is_auto(): - self.held_object = env.coredata.builtins['auto_features'] + self.held_object = env.coredata.builtins[OptionKey('auto_features')] self.name = name self.methods.update({'enabled': self.enabled_method, 'disabled': self.disabled_method, @@ -3010,7 +3009,7 @@ external dependencies (including libraries) must go to "dependencies".''') def _do_subproject_cmake(self, subp_name, subdir, subdir_abs, default_options, kwargs): with mlog.nested(): new_build = self.build.copy() - prefix = self.coredata.builtins['prefix'].value + prefix = self.coredata.builtins[OptionKey('prefix')].value from .modules.cmake import CMakeSubprojectOptions options = kwargs.get('options', CMakeSubprojectOptions()) @@ -3052,23 +3051,10 @@ external dependencies (including libraries) must go to "dependencies".''') return result def get_option_internal(self, optname: str): - raw_optname = optname - if self.is_subproject(): - optname = self.subproject + ':' + optname - - - for opts in [ - self.coredata.base_options, compilers.base_options, self.coredata.builtins, - dict(self.coredata.get_prefixed_options_per_machine(self.coredata.builtins_per_machine)), - ]: - v = opts.get(optname) - if v is None or v.yielding: - v = opts.get(raw_optname) - if v is not None: - return v + # TODO: this optname may be a compiler option + key = OptionKey.from_string(optname).evolve(subproject=self.subproject) - key = mesonlib.OptionKey.from_string(optname) - for opts in [self.coredata.compiler_options]: + for opts in [self.coredata.builtins, self.coredata.base_options, compilers.base_options, self.coredata.compiler_options]: v = opts.get(key) if v is None or v.yielding: v = opts.get(key.as_root()) @@ -3090,7 +3076,7 @@ external dependencies (including libraries) must go to "dependencies".''') mlog.warning('Option {0!r} of type {1!r} in subproject {2!r} cannot yield ' 'to parent option of type {3!r}, ignoring parent value. ' 'Use -D{2}:{0}=value to set the value for this option manually' - '.'.format(raw_optname, opt_type, self.subproject, popt_type), + '.'.format(optname, opt_type, self.subproject, popt_type), location=self.current_node) return opt except KeyError: @@ -4786,15 +4772,15 @@ different subdirectory. break def check_clang_asan_lundef(self) -> None: - if 'b_lundef' not in self.coredata.base_options: + if OptionKey('b_lundef') not in self.coredata.base_options: return - if 'b_sanitize' not in self.coredata.base_options: + if OptionKey('b_sanitize') not in self.coredata.base_options: return - if (self.coredata.base_options['b_lundef'].value and - self.coredata.base_options['b_sanitize'].value != 'none'): + if (self.coredata.base_options[OptionKey('b_lundef')].value and + self.coredata.base_options[OptionKey('b_sanitize')].value != 'none'): mlog.warning('''Trying to use {} sanitizer on Clang with b_lundef. This will probably not work. -Try setting b_lundef to false instead.'''.format(self.coredata.base_options['b_sanitize'].value), +Try setting b_lundef to false instead.'''.format(self.coredata.base_options[OptionKey('b_sanitize')].value), location=self.current_node) def evaluate_subproject_info(self, path_from_source_root, subproject_dir): @@ -4889,10 +4875,11 @@ Try setting b_lundef to false instead.'''.format(self.coredata.base_options['b_s # Check if user forces non-PIC static library. pic = True + key = OptionKey('b_staticpic') if 'pic' in kwargs: pic = kwargs['pic'] - elif 'b_staticpic' in self.environment.coredata.base_options: - pic = self.environment.coredata.base_options['b_staticpic'].value + elif key in self.environment.coredata.base_options: + pic = self.environment.coredata.base_options[key].value if pic: # Exclude sources from args and kwargs to avoid building them twice diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index 70d6806..d6584d5 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -17,12 +17,11 @@ from . import coredata, environment, mesonlib, build, mintro, mlog from .ast import AstIDGenerator import typing as T -from .mesonlib import MachineChoice +from .mesonlib import MachineChoice, OptionKey if T.TYPE_CHECKING: import argparse from .coredata import UserOption - from .mesonlib import OptionKey def add_arguments(parser: 'argparse.ArgumentParser') -> None: coredata.register_builtin_arguments(parser) @@ -30,7 +29,6 @@ def add_arguments(parser: 'argparse.ArgumentParser') -> None: parser.add_argument('--clearcache', action='store_true', default=False, help='Clear cached state (e.g. found dependencies)') - def make_lower_case(val: T.Any) -> T.Union[str, T.List[T.Any]]: # T.Any because of recursion... if isinstance(val, bool): return str(val).lower() @@ -39,12 +37,6 @@ def make_lower_case(val: T.Any) -> T.Union[str, T.List[T.Any]]: # T.Any because else: return str(val) -def insert_build_prefix(k: str) -> str: - idx = k.find(':') - if idx < 0: - return 'build.' + k - return k[:idx + 1] + 'build.' + k[idx + 1:] - class ConfException(mesonlib.MesonException): pass @@ -62,8 +54,8 @@ class Conf: self.choices_col = [] self.descr_col = [] self.has_choices = False - self.all_subprojects = set() - self.yielding_options = set() + self.all_subprojects: T.Set[str] = set() + self.yielding_options: T.Set[OptionKey] = set() if os.path.isdir(os.path.join(self.build_dir, 'meson-private')): self.build = build.load(self.build_dir) @@ -111,20 +103,8 @@ class Conf: else: print('{0:{width[0]}} {1:{width[1]}} {3}'.format(*line, width=col_widths)) - def split_options_per_subproject(self, options: T.Dict[str, 'UserOption']) -> T.Dict[str, T.Dict[str, 'UserOption']]: - result = {} - for k, o in options.items(): - subproject = '' - if ':' in k: - subproject, optname = k.split(':') - if o.yielding and optname in options: - self.yielding_options.add(k) - self.all_subprojects.add(subproject) - result.setdefault(subproject, {})[k] = o - return result - - def split_options_per_subproject2(self, options: 'coredata.KeyedOptionDictType') -> T.Dict[str, T.Dict[str, 'UserOption']]: - result = {} + def split_options_per_subproject(self, options: 'coredata.KeyedOptionDictType') -> T.Dict[str, T.Dict[str, 'UserOption']]: + result: T.Dict[str, T.Dict[str, 'UserOption']] = {} for k, o in options.items(): subproject = k.subproject if k.subproject: @@ -135,8 +115,8 @@ class Conf: result.setdefault(subproject, {})[str(k)] = o return result - def _add_line(self, name, value, choices, descr): - self.name_col.append(' ' * self.print_margin + name) + def _add_line(self, name: OptionKey, value, choices, descr) -> None: + self.name_col.append(' ' * self.print_margin + str(name)) self.value_col.append(value) self.choices_col.append(choices) self.descr_col.append(descr) @@ -185,7 +165,7 @@ class Conf: self._add_line(section + ':', '', '', '') self.print_margin = 2 - def print_options(self, title: str, options: 'coredata.OptionDictType') -> None: + def print_options(self, title: str, options: 'coredata.KeyedOptionDictType') -> None: if not options: return if title: @@ -210,28 +190,28 @@ class Conf: if not self.default_values_only: print(' Build dir ', self.build_dir) - dir_option_names = list(coredata.BUILTIN_DIR_OPTIONS) - test_option_names = ['errorlogs', - 'stdsplit'] - core_option_names = [k for k in self.coredata.builtins if k not in dir_option_names + test_option_names] + dir_option_names = set(coredata.BUILTIN_DIR_OPTIONS) + test_option_names = {OptionKey('errorlogs'), + OptionKey('stdsplit')} + core_option_names = [k for k in self.coredata.builtins if k not in dir_option_names | test_option_names] dir_options = {k: o for k, o in self.coredata.builtins.items() if k in dir_option_names} test_options = {k: o for k, o in self.coredata.builtins.items() if k in test_option_names} core_options = {k: o for k, o in self.coredata.builtins.items() if k in core_option_names} - core_options = self.split_options_per_subproject(core_options) - host_compiler_options = self.split_options_per_subproject2({k: v for k, v in self.coredata.compiler_options.items() if k.machine is MachineChoice.HOST}) - build_compiler_options = self.split_options_per_subproject2({k: v for k, v in self.coredata.compiler_options.items() if k.machine is MachineChoice.BUILD}) - project_options = self.split_options_per_subproject2(self.coredata.user_options) + host_core_options = self.split_options_per_subproject({k: v for k, v in self.coredata.builtins.items() if k.machine is MachineChoice.HOST}) + build_core_options = self.split_options_per_subproject({k: v for k, v in self.coredata.builtins.items() if k.machine is MachineChoice.BUILD}) + host_compiler_options = self.split_options_per_subproject({k: v for k, v in self.coredata.compiler_options.items() if k.machine is MachineChoice.HOST}) + build_compiler_options = self.split_options_per_subproject({k: v for k, v in self.coredata.compiler_options.items() if k.machine is MachineChoice.BUILD}) + project_options = self.split_options_per_subproject(self.coredata.user_options) show_build_options = self.default_values_only or self.build.environment.is_cross_build() self.add_section('Main project options') - self.print_options('Core options', core_options['']) - self.print_options('', self.coredata.builtins_per_machine.host) + self.print_options('Core options', host_core_options['']) if show_build_options: - self.print_options('', {insert_build_prefix(k): o for k, o in self.coredata.builtins_per_machine.build.items()}) + self.print_options('', build_core_options['']) self.print_options('Backend options', {str(k): v for k, v in self.coredata.backend_options.items()}) - self.print_options('Base options', self.coredata.base_options) + self.print_options('Base options', {str(k): v for k, v in self.coredata.base_options.items()}) self.print_options('Compiler options', host_compiler_options.get('', {})) if show_build_options: self.print_options('', build_compiler_options.get('', {})) diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py index 8252f79..07cc3a1 100644 --- a/mesonbuild/mesonlib.py +++ b/mesonbuild/mesonlib.py @@ -32,7 +32,7 @@ from mesonbuild import mlog if T.TYPE_CHECKING: from .build import ConfigurationData - from .coredata import OptionDictType, UserOption + from .coredata import KeyedOptionDictType, UserOption from .compilers.compilers import CompilerType from .interpreterbase import ObjectHolder @@ -1743,6 +1743,10 @@ class OptionProxy(T.Generic[_T]): self.value = value self.choices = choices + def set_value(self, v: _T) -> None: + # XXX: should this be an error + self.value = v + class OptionOverrideProxy(collections.abc.MutableMapping): @@ -1753,13 +1757,13 @@ class OptionOverrideProxy(collections.abc.MutableMapping): # TODO: the typing here could be made more explicit using a TypeDict from # python 3.8 or typing_extensions - def __init__(self, overrides: T.Dict[str, T.Any], *options: 'OptionDictType'): + def __init__(self, overrides: T.Dict['OptionKey', T.Any], *options: 'KeyedOptionDictType'): self.overrides = overrides.copy() - self.options = {} # type: T.Dict[str, UserOption] + self.options: T.Dict['OptionKey', UserOption] = {} for o in options: self.options.update(o) - def __getitem__(self, key: str) -> T.Union['UserOption', OptionProxy]: + def __getitem__(self, key: 'OptionKey') -> T.Union['UserOption', OptionProxy]: if key in self.options: opt = self.options[key] if key in self.overrides: @@ -1767,13 +1771,13 @@ class OptionOverrideProxy(collections.abc.MutableMapping): return opt raise KeyError('Option not found', key) - def __setitem__(self, key: str, value: T.Union['UserOption', OptionProxy]) -> None: + def __setitem__(self, key: 'OptionKey', value: T.Union['UserOption', OptionProxy]) -> None: self.overrides[key] = value.value - def __delitem__(self, key: str) -> None: + def __delitem__(self, key: 'OptionKey') -> None: del self.overrides[key] - def __iter__(self) -> T.Iterator[str]: + def __iter__(self) -> T.Iterator['OptionKey']: return iter(self.options) def __len__(self) -> int: @@ -1793,20 +1797,54 @@ class OptionType(enum.Enum): PROJECT = 3 BACKEND = 4 +# This is copied from coredata. There is no way to share this, because this +# is used in the OptionKey constructor, and the coredata lists are +# OptionKeys... +_BUILTIN_NAMES = { + 'prefix', + 'bindir', + 'datadir', + 'includedir', + 'infodir', + 'libdir', + 'libexecdir', + 'localedir', + 'localstatedir', + 'mandir', + 'sbindir', + 'sharedstatedir', + 'sysconfdir', + 'auto_features', + 'backend', + 'buildtype', + 'debug', + 'default_library', + 'errorlogs', + 'install_umask', + 'layout', + 'optimization', + 'stdsplit', + 'strip', + 'unity', + 'unity_size', + 'warning_level', + 'werror', + 'wrap_mode', + 'force_fallback_for', + 'pkg_config_path', + 'cmake_prefix_path', +} + 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: + if key.name.startswith('b_'): 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: + elif key.name in _BUILTIN_NAMES: return OptionType.BUILTIN elif key.name.startswith('backend_'): assert key.machine is MachineChoice.HOST, str(key) @@ -1868,9 +1906,10 @@ class OptionKey: 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. + splatted to the initializer. """ - self.__init__(**state) + # Mypy doesn't like this, because it's so clever. + self.__init__(**state) # type: ignore def __hash__(self) -> int: return self._hash @@ -1884,6 +1923,15 @@ class OptionKey: self.lang == other.lang) return NotImplemented + def __lt__(self, other: object) -> bool: + if isinstance(other, OptionKey): + return ( + self.name < other.name and + self.subproject < other.subproject and + self.machine < other.machine and + self.lang < other.lang) + return NotImplemented + def __str__(self) -> str: out = self.name if self.lang: diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 7b2560f..b1d2831 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -33,7 +33,7 @@ import typing as T import os import argparse -from .mesonlib import MachineChoice +from .mesonlib import MachineChoice, OptionKey def get_meson_info_file(info_dir: str) -> str: return os.path.join(info_dir, 'meson-info.json') @@ -213,24 +213,16 @@ def list_buildoptions_from_source(intr: IntrospectionInterpreter) -> T.List[T.Di def list_buildoptions(coredata: cdata.CoreData, subprojects: T.Optional[T.List[str]] = None) -> T.List[T.Dict[str, T.Union[str, bool, int, T.List[str]]]]: optlist = [] # type: T.List[T.Dict[str, T.Union[str, bool, int, T.List[str]]]] - dir_option_names = list(cdata.BUILTIN_DIR_OPTIONS) - test_option_names = ['errorlogs', - 'stdsplit'] - core_option_names = [k for k in coredata.builtins if k not in dir_option_names + test_option_names] - - dir_options = {k: o for k, o in coredata.builtins.items() if k in dir_option_names} - test_options = {k: o for k, o in coredata.builtins.items() if k in test_option_names} - core_options = {k: o for k, o in coredata.builtins.items() if k in core_option_names} - - if subprojects: - # Add per subproject built-in options - sub_core_options = {} - for sub in subprojects: - for k, o in core_options.items(): - if o.yielding: - continue - sub_core_options[sub + ':' + k] = o - core_options.update(sub_core_options) + dir_option_names = set(cdata.BUILTIN_DIR_OPTIONS) + test_option_names = {OptionKey('errorlogs'), + OptionKey('stdsplit')} + core_option_names = {k for k in coredata.builtins if k not in dir_option_names | test_option_names} + + dir_options = {str(k): o for k, o in coredata.builtins.items() if k in dir_option_names} + test_options = {str(k): o for k, o in coredata.builtins.items() if k in test_option_names} + core_options = {str(k): o for k, o in coredata.builtins.items() if k in core_option_names} + for s in subprojects or []: + core_options.update({str(k.evolve(subproject=s)): v for k, v in coredata.builtins.items() if not v.yielding}) def add_keys(options: 'cdata.OptionDictType', section: str, machine: str = 'any') -> None: for key, opt in sorted(options.items()): @@ -253,14 +245,8 @@ def list_buildoptions(coredata: cdata.CoreData, subprojects: T.Optional[T.List[s optlist.append(optdict) add_keys(core_options, 'core') - add_keys(coredata.builtins_per_machine.host, 'core', machine='host') - add_keys( - {'build.' + k: o for k, o in coredata.builtins_per_machine.build.items()}, - 'core', - machine='build', - ) add_keys({str(k): v for k, v in coredata.backend_options.items()}, 'backend') - add_keys(coredata.base_options, 'base') + add_keys({str(k): v for k, v in coredata.base_options.items()}, 'base') add_keys( {str(k): v for k, v in coredata.compiler_options.items() if k.machine is MachineChoice.HOST}, 'compiler', diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 547aff1..565fd82 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -19,6 +19,7 @@ import os import copy import subprocess import functools +import typing as T from .. import build from .. import mlog @@ -35,6 +36,9 @@ from ..mesonlib import ( from ..dependencies import Dependency, PkgConfigDependency, InternalDependency, ExternalProgram from ..interpreterbase import noKwargs, permittedKwargs, FeatureNew, FeatureNewKwargs, FeatureDeprecatedKwargs +if T.TYPE_CHECKING: + from ..compilers import Compiler + # gresource compilation is broken due to the way # the resource compiler and Ninja clash about it # @@ -574,8 +578,8 @@ class GnomeModule(ExtensionModule): return ret - def _get_girtargets_langs_compilers(self, girtargets): - ret = [] + def _get_girtargets_langs_compilers(self, girtargets: T.List[GirTarget]) -> T.List[T.Tuple[str, 'Compiler']]: + ret: T.List[T.Tuple[str, 'Compiler']] = [] for girtarget in girtargets: for lang, compiler in girtarget.compilers.items(): # XXX: Can you use g-i with any other language? @@ -598,7 +602,7 @@ class GnomeModule(ExtensionModule): ret += girtarget.get_include_dirs() return ret - def _get_langs_compilers_flags(self, state, langs_compilers): + def _get_langs_compilers_flags(self, state, langs_compilers: T.List[T.Tuple[str, 'Compiler']]): cflags = [] internal_ldflags = [] external_ldflags = [] @@ -608,8 +612,8 @@ class GnomeModule(ExtensionModule): cflags += state.global_args[lang] if state.project_args.get(lang): cflags += state.project_args[lang] - if 'b_sanitize' in compiler.base_options: - sanitize = state.environment.coredata.base_options['b_sanitize'].value + if mesonlib.OptionKey('b_sanitize') in compiler.base_options: + sanitize = state.environment.coredata.base_options[mesonlib.OptionKey('b_sanitize')].value cflags += compiler.sanitizer_compile_args(sanitize) sanitize = sanitize.split(',') # These must be first in ldflags diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index d336a13..55c9e09 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -177,7 +177,7 @@ class MesonApp: mlog.initialize(env.get_log_dir(), self.options.fatal_warnings) if self.options.profile: mlog.set_timestamp_start(time.monotonic()) - if env.coredata.builtins['backend'].value == 'xcode': + if env.coredata.builtins[mesonlib.OptionKey('backend')].value == 'xcode': mlog.warning('xcode backend is currently unmaintained, patches welcome') with mesonlib.BuildDirLock(self.build_dir): self._generate(env) diff --git a/mesonbuild/rewriter.py b/mesonbuild/rewriter.py index 776a39a..fbbdcc1 100644 --- a/mesonbuild/rewriter.py +++ b/mesonbuild/rewriter.py @@ -464,11 +464,9 @@ class Rewriter: cdata = self.interpreter.coredata options = { - **cdata.builtins, - **cdata.builtins_per_machine.host, - **{'build.' + k: o for k, o in cdata.builtins_per_machine.build.items()}, + **{str(k): v for k, v in cdata.builtins.items()}, **{str(k): v for k, v in cdata.backend_options.items()}, - **cdata.base_options, + **{str(k): v for k, v in cdata.base_options.items()}, **{str(k): v for k, v in cdata.compiler_options.items()}, **{str(k): v for k, v in cdata.user_options.items()}, } diff --git a/run_unittests.py b/run_unittests.py index 744556e..cba46c5 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -263,7 +263,8 @@ def skip_if_not_base_option(feature): def wrapped(*args, **kwargs): env = get_fake_env() cc = env.detect_c_compiler(MachineChoice.HOST) - if feature not in cc.base_options: + key = OptionKey(feature) + if key not in cc.base_options: raise unittest.SkipTest( '{} not available with {}'.format(feature, cc.id)) return f(*args, **kwargs) @@ -1390,8 +1391,8 @@ class DataTests(unittest.TestCase): found_entries |= options self.assertEqual(found_entries, set([ - *mesonbuild.coredata.BUILTIN_OPTIONS.keys(), - *mesonbuild.coredata.BUILTIN_OPTIONS_PER_MACHINE.keys() + *[str(k) for k in mesonbuild.coredata.BUILTIN_OPTIONS], + *[str(k) for k in mesonbuild.coredata.BUILTIN_OPTIONS_PER_MACHINE], ])) # Check that `buildtype` table inside `Core options` matches how @@ -1412,10 +1413,10 @@ class DataTests(unittest.TestCase): debug = False else: raise RuntimeError('Invalid debug value {!r} in row:\n{}'.format(debug, m.group())) - env.coredata.set_builtin_option('buildtype', buildtype) - self.assertEqual(env.coredata.builtins['buildtype'].value, buildtype) - self.assertEqual(env.coredata.builtins['optimization'].value, opt) - self.assertEqual(env.coredata.builtins['debug'].value, debug) + env.coredata.set_builtin_option(OptionKey('buildtype'), buildtype) + self.assertEqual(env.coredata.builtins[OptionKey('buildtype')].value, buildtype) + self.assertEqual(env.coredata.builtins[OptionKey('optimization')].value, opt) + self.assertEqual(env.coredata.builtins[OptionKey('debug')].value, debug) def test_cpu_families_documented(self): with open("docs/markdown/Reference-tables.md", encoding='utf-8') as f: @@ -1903,11 +1904,14 @@ class AllPlatformTests(BasePlatformTests): https://github.com/mesonbuild/meson/issues/1349 ''' testdir = os.path.join(self.common_test_dir, '88 default options') - self.init(testdir, default_args=False) + self.init(testdir, default_args=False, inprocess=True) opts = self.introspect('--buildoptions') for opt in opts: if opt['name'] == 'prefix': prefix = opt['value'] + break + else: + raise self.fail('Did not find option "prefix"') self.assertEqual(prefix, '/absoluteprefix') def test_do_conf_file_preserve_newlines(self): @@ -3679,35 +3683,34 @@ class AllPlatformTests(BasePlatformTests): def test_command_line(self): testdir = os.path.join(self.unit_test_dir, '34 command line') - K = OptionKey # Verify default values when passing no args that affect the # configuration, and as a bonus, test that --profile-self works. self.init(testdir, extra_args=['--profile-self', '--fatal-meson-warnings']) obj = mesonbuild.coredata.load(self.builddir) - self.assertEqual(obj.builtins['default_library'].value, 'static') - self.assertEqual(obj.builtins['warning_level'].value, '1') - self.assertEqual(obj.user_options[K('set_sub_opt')].value, True) - self.assertEqual(obj.user_options[K('subp_opt', 'subp')].value, 'default3') + self.assertEqual(obj.builtins[OptionKey('default_library')].value, 'static') + self.assertEqual(obj.builtins[OptionKey('warning_level')].value, '1') + self.assertEqual(obj.user_options[OptionKey('set_sub_opt')].value, True) + self.assertEqual(obj.user_options[OptionKey('subp_opt', 'subp')].value, 'default3') self.wipe() # warning_level is special, it's --warnlevel instead of --warning-level # for historical reasons self.init(testdir, extra_args=['--warnlevel=2', '--fatal-meson-warnings']) obj = mesonbuild.coredata.load(self.builddir) - self.assertEqual(obj.builtins['warning_level'].value, '2') + self.assertEqual(obj.builtins[OptionKey('warning_level')].value, '2') self.setconf('--warnlevel=3') obj = mesonbuild.coredata.load(self.builddir) - self.assertEqual(obj.builtins['warning_level'].value, '3') + self.assertEqual(obj.builtins[OptionKey('warning_level')].value, '3') self.wipe() # But when using -D syntax, it should be 'warning_level' self.init(testdir, extra_args=['-Dwarning_level=2', '--fatal-meson-warnings']) obj = mesonbuild.coredata.load(self.builddir) - self.assertEqual(obj.builtins['warning_level'].value, '2') + self.assertEqual(obj.builtins[OptionKey('warning_level')].value, '2') self.setconf('-Dwarning_level=3') obj = mesonbuild.coredata.load(self.builddir) - self.assertEqual(obj.builtins['warning_level'].value, '3') + self.assertEqual(obj.builtins[OptionKey('warning_level')].value, '3') self.wipe() # Mixing --option and -Doption is forbidden @@ -3731,15 +3734,15 @@ class AllPlatformTests(BasePlatformTests): # --default-library should override default value from project() self.init(testdir, extra_args=['--default-library=both', '--fatal-meson-warnings']) obj = mesonbuild.coredata.load(self.builddir) - self.assertEqual(obj.builtins['default_library'].value, 'both') + self.assertEqual(obj.builtins[OptionKey('default_library')].value, 'both') self.setconf('--default-library=shared') obj = mesonbuild.coredata.load(self.builddir) - self.assertEqual(obj.builtins['default_library'].value, 'shared') + self.assertEqual(obj.builtins[OptionKey('default_library')].value, 'shared') if self.backend is Backend.ninja: # reconfigure target works only with ninja backend self.build('reconfigure') obj = mesonbuild.coredata.load(self.builddir) - self.assertEqual(obj.builtins['default_library'].value, 'shared') + self.assertEqual(obj.builtins[OptionKey('default_library')].value, 'shared') self.wipe() # Should warn on unknown options @@ -3774,7 +3777,7 @@ class AllPlatformTests(BasePlatformTests): # Test we can set subproject option self.init(testdir, extra_args=['-Dsubp:subp_opt=foo', '--fatal-meson-warnings']) obj = mesonbuild.coredata.load(self.builddir) - self.assertEqual(obj.user_options[K('subp_opt', 'subp')].value, 'foo') + self.assertEqual(obj.user_options[OptionKey('subp_opt', 'subp')].value, 'foo') self.wipe() # c_args value should be parsed with split_args @@ -3789,7 +3792,7 @@ class AllPlatformTests(BasePlatformTests): self.init(testdir, extra_args=['-Dset_percent_opt=myoption%', '--fatal-meson-warnings']) obj = mesonbuild.coredata.load(self.builddir) - self.assertEqual(obj.user_options[K('set_percent_opt')].value, 'myoption%') + self.assertEqual(obj.user_options[OptionKey('set_percent_opt')].value, 'myoption%') self.wipe() # Setting a 2nd time the same option should override the first value @@ -3800,18 +3803,18 @@ class AllPlatformTests(BasePlatformTests): '-Dc_args=-Dfoo', '-Dc_args=-Dbar', '-Db_lundef=false', '--fatal-meson-warnings']) obj = mesonbuild.coredata.load(self.builddir) - self.assertEqual(obj.builtins['bindir'].value, 'bar') - self.assertEqual(obj.builtins['buildtype'].value, 'release') - self.assertEqual(obj.base_options['b_sanitize'].value, 'thread') + self.assertEqual(obj.builtins[OptionKey('bindir')].value, 'bar') + self.assertEqual(obj.builtins[OptionKey('buildtype')].value, 'release') + self.assertEqual(obj.base_options[OptionKey('b_sanitize')].value, 'thread') self.assertEqual(obj.compiler_options[OptionKey('args', lang='c')].value, ['-Dbar']) self.setconf(['--bindir=bar', '--bindir=foo', '-Dbuildtype=release', '-Dbuildtype=plain', '-Db_sanitize=thread', '-Db_sanitize=address', '-Dc_args=-Dbar', '-Dc_args=-Dfoo']) obj = mesonbuild.coredata.load(self.builddir) - self.assertEqual(obj.builtins['bindir'].value, 'foo') - self.assertEqual(obj.builtins['buildtype'].value, 'plain') - self.assertEqual(obj.base_options['b_sanitize'].value, 'address') + self.assertEqual(obj.builtins[OptionKey('bindir')].value, 'foo') + self.assertEqual(obj.builtins[OptionKey('buildtype')].value, 'plain') + self.assertEqual(obj.base_options[OptionKey('b_sanitize')].value, 'address') self.assertEqual(obj.compiler_options[OptionKey('args', lang='c')].value, ['-Dfoo']) self.wipe() except KeyError: @@ -3826,25 +3829,25 @@ class AllPlatformTests(BasePlatformTests): # Verify default values when passing no args self.init(testdir) obj = mesonbuild.coredata.load(self.builddir) - self.assertEqual(obj.builtins['warning_level'].value, '0') + self.assertEqual(obj.builtins[OptionKey('warning_level')].value, '0') self.wipe() # verify we can override w/ --warnlevel self.init(testdir, extra_args=['--warnlevel=1']) obj = mesonbuild.coredata.load(self.builddir) - self.assertEqual(obj.builtins['warning_level'].value, '1') + self.assertEqual(obj.builtins[OptionKey('warning_level')].value, '1') self.setconf('--warnlevel=0') obj = mesonbuild.coredata.load(self.builddir) - self.assertEqual(obj.builtins['warning_level'].value, '0') + self.assertEqual(obj.builtins[OptionKey('warning_level')].value, '0') self.wipe() # verify we can override w/ -Dwarning_level self.init(testdir, extra_args=['-Dwarning_level=1']) obj = mesonbuild.coredata.load(self.builddir) - self.assertEqual(obj.builtins['warning_level'].value, '1') + self.assertEqual(obj.builtins[OptionKey('warning_level')].value, '1') self.setconf('-Dwarning_level=0') obj = mesonbuild.coredata.load(self.builddir) - self.assertEqual(obj.builtins['warning_level'].value, '0') + self.assertEqual(obj.builtins[OptionKey('warning_level')].value, '0') self.wipe() def test_feature_check_usage_subprojects(self): @@ -5771,7 +5774,7 @@ class WindowsTests(BasePlatformTests): # Verify that the `b_vscrt` option is available env = get_fake_env() cc = env.detect_c_compiler(MachineChoice.HOST) - if 'b_vscrt' not in cc.base_options: + if OptionKey('b_vscrt') not in cc.base_options: raise unittest.SkipTest('Compiler does not support setting the VS CRT') # Verify that qmake is for Qt5 if not shutil.which('qmake-qt5'): @@ -5797,7 +5800,7 @@ class WindowsTests(BasePlatformTests): # Verify that the `b_vscrt` option is available env = get_fake_env() cc = env.detect_c_compiler(MachineChoice.HOST) - if 'b_vscrt' not in cc.base_options: + if OptionKey('b_vscrt') not in cc.base_options: raise unittest.SkipTest('Compiler does not support setting the VS CRT') def sanitycheck_vscrt(vscrt): -- cgit v1.1