From 1eaab0253bea69432d69a1eddab377fbbd9ed74d Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 25 Sep 2024 10:28:37 -0700 Subject: [PATCH] compilers: Check if GCC has support for ObjC and/or ObjC++ Since this is optional, we should not accept that GCC is a valid ObjC or G++ is a valid ObjC++ Compiler unless we've tested that they can actually do a basic compile. This requires fixing a number of tests that have broken assumptions. In some cases I've split tests where issues with one language would hide the other. It would be great if we had a competent test framework that allowed subtests to skip, unfortunately we have python's unittest instead. Because of that we can't avoid extra tests by use of subtests. --- mesonbuild/compilers/detect.py | 6 +++++- unittests/failuretests.py | 19 +++++++++++++------ unittests/machinefiletests.py | 16 +++++++++++++--- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py index 7bd48d10c320..d4ad4badeefd 100644 --- a/mesonbuild/compilers/detect.py +++ b/mesonbuild/compilers/detect.py @@ -897,9 +897,13 @@ def _detect_objc_or_objcpp_compiler(env: 'Environment', lang: str, for_machine: version = _get_gnu_version_from_defines(defines) comp = objc.GnuObjCCompiler if lang == 'objc' else objcpp.GnuObjCPPCompiler linker = guess_nix_linker(env, compiler, comp, version, for_machine) - return comp( + c = comp( ccache, compiler, version, for_machine, is_cross, info, defines, linker=linker) + if not c.compiles('int main(void) { return 0; }', env)[0]: + popen_exceptions[join_args(compiler)] = f'GCC was not built with support for {"objective-c" if lang == "objc" else "objective-c++"}' + continue + return c if 'clang' in out: linker = None defines = _get_clang_compiler_defines(compiler, lang) diff --git a/unittests/failuretests.py b/unittests/failuretests.py index 0dd6c5f650cf..18d0c5e70b47 100644 --- a/unittests/failuretests.py +++ b/unittests/failuretests.py @@ -241,19 +241,26 @@ def test_dependency_invalid_method(self): ''' self.assertMesonRaises(code, ".* is not a config-tool dependency") - def test_objc_cpp_detection(self): + def test_objc_detection(self) -> None: ''' Test that when we can't detect objc or objcpp, we fail gracefully. ''' env = get_fake_env() try: detect_objc_compiler(env, MachineChoice.HOST) + except EnvironmentException as e: + self.assertRegex(str(e), r"(Unknown compiler|GCC was not built with support)") + else: + raise unittest.SkipTest('Working objective-c Compiler found, cannot test error.') + + def test_objcpp_detection(self) -> None: + env = get_fake_env() + try: detect_objcpp_compiler(env, MachineChoice.HOST) - except EnvironmentException: - code = "add_languages('objc')\nadd_languages('objcpp')" - self.assertMesonRaises(code, "Unknown compiler") - return - raise unittest.SkipTest("objc and objcpp found, can't test detection failure") + except EnvironmentException as e: + self.assertRegex(str(e), r"(Unknown compiler|GCC was not built with support)") + else: + raise unittest.SkipTest('Working objective-c++ Compiler found, cannot test error.') def test_subproject_variables(self): ''' diff --git a/unittests/machinefiletests.py b/unittests/machinefiletests.py index 803a1797df2f..e71cd04fe3c0 100644 --- a/unittests/machinefiletests.py +++ b/unittests/machinefiletests.py @@ -23,7 +23,7 @@ import mesonbuild.environment import mesonbuild.coredata import mesonbuild.modules.gnome - +from mesonbuild import mesonlib from mesonbuild import machinefile from mesonbuild.mesonlib import ( @@ -275,7 +275,12 @@ def cb(comp): if not is_real_gnu_compiler(shutil.which('gcc')): raise SkipTest('Only one compiler found, cannot test.') return 'gcc', 'gcc' - self.helper_for_compiler('objc', cb) + try: + self.helper_for_compiler('objc', cb) + except mesonlib.EnvironmentException as e: + if 'GCC was not built with support for objective-c' in str(e): + raise unittest.SkipTest("GCC doesn't support objective-c, test cannot run") + raise @skip_if_not_language('objcpp') @skip_if_env_set('OBJCXX') @@ -288,7 +293,12 @@ def cb(comp): if not is_real_gnu_compiler(shutil.which('g++')): raise SkipTest('Only one compiler found, cannot test.') return 'g++', 'gcc' - self.helper_for_compiler('objcpp', cb) + try: + self.helper_for_compiler('objcpp', cb) + except mesonlib.EnvironmentException as e: + if 'GCC was not built with support for objective-c++' in str(e): + raise unittest.SkipTest("G++ doesn't support objective-c++, test cannot run") + raise @skip_if_not_language('d') @skip_if_env_set('DC')