From f9479787a01106235e14d2a08a74352f4b1177ef Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Wed, 6 Mar 2024 14:10:08 -0500 Subject: [PATCH] fix reconfigure subproject base options --- mesonbuild/ast/introspection.py | 4 +-- mesonbuild/compilers/detect.py | 4 +-- mesonbuild/coredata.py | 44 +++++++++++++++++++-------- mesonbuild/interpreter/interpreter.py | 6 ++-- mesonbuild/modules/java.py | 2 +- run_project_tests.py | 2 +- unittests/allplatformstests.py | 14 ++++----- unittests/platformagnostictests.py | 4 ++- 8 files changed, 51 insertions(+), 29 deletions(-) diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py index f9a6e11c0..046c0a2b6 100644 --- a/mesonbuild/ast/introspection.py +++ b/mesonbuild/ast/introspection.py @@ -170,7 +170,7 @@ class IntrospectionInterpreter(AstInterpreter): lang = lang.lower() if lang not in self.coredata.compilers[for_machine]: try: - comp = detect_compiler_for(self.environment, lang, for_machine, True) + comp = detect_compiler_for(self.environment, lang, for_machine, True, self.subproject) except mesonlib.MesonException: # do we even care about introspecting this language? if required: @@ -183,7 +183,7 @@ class IntrospectionInterpreter(AstInterpreter): v = copy.copy(self.coredata.options[k]) k = k.evolve(subproject=self.subproject) options[k] = v - self.coredata.add_compiler_options(options, lang, for_machine, self.environment) + self.coredata.add_compiler_options(options, lang, for_machine, self.environment, self.subproject) def func_dependency(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> None: args = self.flatten_args(args) diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py index dbc2b4879..c117c22bb 100644 --- a/mesonbuild/compilers/detect.py +++ b/mesonbuild/compilers/detect.py @@ -101,12 +101,12 @@ def compiler_from_language(env: 'Environment', lang: str, for_machine: MachineCh } return lang_map[lang](env, for_machine) if lang in lang_map else None -def detect_compiler_for(env: 'Environment', lang: str, for_machine: MachineChoice, skip_sanity_check: bool) -> T.Optional[Compiler]: +def detect_compiler_for(env: 'Environment', lang: str, for_machine: MachineChoice, skip_sanity_check: bool, subproject: str) -> T.Optional[Compiler]: comp = compiler_from_language(env, lang, for_machine) if comp is None: return comp assert comp.for_machine == for_machine - env.coredata.process_compiler_options(lang, comp, env) + env.coredata.process_compiler_options(lang, comp, env, subproject) if not skip_sanity_check: comp.sanity_check(env.get_scratch_dir(), env) env.coredata.compilers[comp.for_machine][lang] = comp diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index f2ea87993..fa70cfc3c 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -1016,15 +1016,24 @@ class CoreData: self.set_options(options, subproject=subproject, first_invocation=env.first_invocation) - def add_compiler_options(self, options: 'MutableKeyedOptionDictType', lang: str, for_machine: MachineChoice, - env: 'Environment') -> None: + def add_compiler_options(self, options: MutableKeyedOptionDictType, lang: str, for_machine: MachineChoice, + env: Environment, subproject: str) -> None: for k, o in options.items(): value = env.options.get(k) if value is not None: o.set_value(value) - self.options[k] = o # override compiler option on reconfigure + if not subproject: + self.options[k] = o # override compiler option on reconfigure self.options.setdefault(k, o) + if subproject: + sk = k.evolve(subproject=subproject) + value = env.options.get(sk) or value + if value is not None: + o.set_value(value) + self.options[sk] = o # override compiler option on reconfigure + self.options.setdefault(sk, o) + def add_lang_args(self, lang: str, comp: T.Type['Compiler'], for_machine: MachineChoice, env: 'Environment') -> None: """Add global language arguments that are needed before compiler/linker detection.""" @@ -1034,20 +1043,31 @@ class CoreData: # `self.options.update()`` is perfectly safe. self.options.update(compilers.get_global_options(lang, comp, for_machine, env)) - def process_compiler_options(self, lang: str, comp: 'Compiler', env: 'Environment') -> None: + def process_compiler_options(self, lang: str, comp: Compiler, env: Environment, subproject: str) -> None: from . import compilers - self.add_compiler_options(comp.get_options(), lang, comp.for_machine, env) + self.add_compiler_options(comp.get_options(), lang, comp.for_machine, env, subproject) enabled_opts: T.List[OptionKey] = [] for key in comp.base_options: - if key not in self.options: - self.options[key] = copy.deepcopy(compilers.base_options[key]) - if key in env.options: - self.options[key].set_value(env.options[key]) - enabled_opts.append(key) - elif key in env.options: - self.options[key].set_value(env.options[key]) + if subproject: + skey = key.evolve(subproject=subproject) + else: + skey = key + if skey not in self.options: + self.options[skey] = copy.deepcopy(compilers.base_options[key]) + if skey in env.options: + self.options[skey].set_value(env.options[skey]) + enabled_opts.append(skey) + elif subproject and key in env.options: + self.options[skey].set_value(env.options[key]) + enabled_opts.append(skey) + if subproject and key not in self.options: + self.options[key] = copy.deepcopy(self.options[skey]) + elif skey in env.options: + self.options[skey].set_value(env.options[skey]) + elif subproject and key in env.options: + self.options[skey].set_value(env.options[key]) self.emit_base_options_warnings(enabled_opts) def emit_base_options_warnings(self, enabled_opts: T.List[OptionKey]) -> None: diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index e5a201e05..47d0d2d2d 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1506,7 +1506,7 @@ class Interpreter(InterpreterBase, HoldableObject): skip_sanity_check = self.should_skip_sanity_check(for_machine) if skip_sanity_check: mlog.log('Cross compiler sanity tests disabled via the cross file.', once=True) - comp = compilers.detect_compiler_for(self.environment, lang, for_machine, skip_sanity_check) + comp = compilers.detect_compiler_for(self.environment, lang, for_machine, skip_sanity_check, self.subproject) if comp is None: raise InvalidArguments(f'Tried to use unknown language "{lang}".') except mesonlib.MesonException: @@ -1520,7 +1520,7 @@ class Interpreter(InterpreterBase, HoldableObject): raise else: # update new values from commandline, if it applies - self.coredata.process_compiler_options(lang, comp, self.environment) + self.coredata.process_compiler_options(lang, comp, self.environment, self.subproject) # Add per-subproject compiler options. They inherit value from main project. if self.subproject: @@ -1529,7 +1529,7 @@ class Interpreter(InterpreterBase, HoldableObject): v = copy.copy(self.coredata.options[k]) k = k.evolve(subproject=self.subproject) options[k] = v - self.coredata.add_compiler_options(options, lang, for_machine, self.environment) + self.coredata.add_compiler_options(options, lang, for_machine, self.environment, self.subproject) if for_machine == MachineChoice.HOST or self.environment.is_cross_build(): logger_fun = mlog.log diff --git a/mesonbuild/modules/java.py b/mesonbuild/modules/java.py index 56611ade2..abef4acdf 100644 --- a/mesonbuild/modules/java.py +++ b/mesonbuild/modules/java.py @@ -32,7 +32,7 @@ class JavaModule(NewExtensionModule): def __get_java_compiler(self, state: ModuleState) -> Compiler: if 'java' not in state.environment.coredata.compilers[MachineChoice.BUILD]: - detect_compiler_for(state.environment, 'java', MachineChoice.BUILD, False) + detect_compiler_for(state.environment, 'java', MachineChoice.BUILD, False, state.subproject) return state.environment.coredata.compilers[MachineChoice.BUILD]['java'] @FeatureNew('java.generate_native_headers', '0.62.0') diff --git a/run_project_tests.py b/run_project_tests.py index 6fc9cb7f7..a14741364 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -981,7 +981,7 @@ def have_working_compiler(lang: str, use_tmp: bool) -> bool: return False if not compiler: return False - env.coredata.process_compiler_options(lang, compiler, env) + env.coredata.process_compiler_options(lang, compiler, env, '') try: compiler.sanity_check(env.get_scratch_dir(), env) except mesonlib.MesonException: diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 2475b17f9..0e5637587 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -944,7 +944,7 @@ class AllPlatformTests(BasePlatformTests): testdir = os.path.join("test cases/cython", '2 generated sources') env = get_fake_env(testdir, self.builddir, self.prefix) try: - detect_compiler_for(env, "cython", MachineChoice.HOST, True) + detect_compiler_for(env, "cython", MachineChoice.HOST, True, '') except EnvironmentException: raise SkipTest("Cython is not installed") self.init(testdir) @@ -969,7 +969,7 @@ class AllPlatformTests(BasePlatformTests): testdir = os.path.join("test cases/cython", '2 generated sources') env = get_fake_env(testdir, self.builddir, self.prefix) try: - cython = detect_compiler_for(env, "cython", MachineChoice.HOST, True) + cython = detect_compiler_for(env, "cython", MachineChoice.HOST, True, '') if not version_compare(cython.version, '>=0.29.33'): raise SkipTest('Cython is too old') except EnvironmentException: @@ -2355,7 +2355,7 @@ class AllPlatformTests(BasePlatformTests): env = get_fake_env() for l in ['cpp', 'cs', 'd', 'java', 'cuda', 'fortran', 'objc', 'objcpp', 'rust', 'vala']: try: - comp = detect_compiler_for(env, l, MachineChoice.HOST, True) + comp = detect_compiler_for(env, l, MachineChoice.HOST, True, '') with tempfile.TemporaryDirectory() as d: comp.sanity_check(d, env) langs.append(l) @@ -2373,7 +2373,7 @@ class AllPlatformTests(BasePlatformTests): if is_windows() and lang == 'fortran' and target_type == 'library': # non-Gfortran Windows Fortran compilers do not do shared libraries in a Fortran standard way # see "test cases/fortran/6 dynamic" - fc = detect_compiler_for(env, 'fortran', MachineChoice.HOST, True) + fc = detect_compiler_for(env, 'fortran', MachineChoice.HOST, True, '') if fc.get_id() in {'intel-cl', 'pgi'}: continue # test empty directory @@ -4433,18 +4433,18 @@ class AllPlatformTests(BasePlatformTests): env = get_fake_env() # Get the compiler so we know which compiler class to mock. - cc = detect_compiler_for(env, 'c', MachineChoice.HOST, True) + cc = detect_compiler_for(env, 'c', MachineChoice.HOST, True, '') cc_type = type(cc) # Test a compiler that acts as a linker with mock.patch.object(cc_type, 'INVOKES_LINKER', True): - cc = detect_compiler_for(env, 'c', MachineChoice.HOST, True) + cc = detect_compiler_for(env, 'c', MachineChoice.HOST, True, '') link_args = env.coredata.get_external_link_args(cc.for_machine, cc.language) self.assertEqual(sorted(link_args), sorted(['-DCFLAG', '-flto'])) # And one that doesn't with mock.patch.object(cc_type, 'INVOKES_LINKER', False): - cc = detect_compiler_for(env, 'c', MachineChoice.HOST, True) + cc = detect_compiler_for(env, 'c', MachineChoice.HOST, True, '') link_args = env.coredata.get_external_link_args(cc.for_machine, cc.language) self.assertEqual(sorted(link_args), sorted(['-flto'])) diff --git a/unittests/platformagnostictests.py b/unittests/platformagnostictests.py index a6db17341..b75a78052 100644 --- a/unittests/platformagnostictests.py +++ b/unittests/platformagnostictests.py @@ -282,9 +282,11 @@ class PlatformAgnosticTests(BasePlatformTests): self.assertIn('\nMessage: b_ndebug: true\n', out) self.assertIn('\nMessage: c_std: c89\n', out) - out = self.init(testdir, extra_args=['--reconfigure', '-Db_ndebug=if-release', '-Dsub:b_ndebug=false', '-Dc_std=c99']) + out = self.init(testdir, extra_args=['--reconfigure', '-Db_ndebug=if-release', '-Dsub:b_ndebug=false', '-Dc_std=c99', '-Dsub:c_std=c11']) self.assertIn('\nMessage: b_ndebug: if-release\n', out) self.assertIn('\nMessage: c_std: c99\n', out) + self.assertIn('\nsub| Message: b_ndebug: false\n', out) + self.assertIn('\nsub| Message: c_std: c11\n', out) def test_setup_with_unknown_option(self): testdir = os.path.join(self.common_test_dir, '1 trivial')