aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam James <sam@gentoo.org>2024-06-03 09:43:20 +0100
committerSam James <sam@gentoo.org>2024-06-24 00:36:06 +0100
commit4ad792e158b6059eb847dd0562aff9bd7029981f (patch)
tree66a0ccd94a98788e229737fce4c71a3a8c60b982
parentb56a3198b4e8e8a6738dc372bddc66225abc20f9 (diff)
downloadmeson-4ad792e158b6059eb847dd0562aff9bd7029981f.zip
meson-4ad792e158b6059eb847dd0562aff9bd7029981f.tar.gz
meson-4ad792e158b6059eb847dd0562aff9bd7029981f.tar.bz2
compilers: detect: fix pre-processor scraping by defining language
_get_gnu_compiler_defines and _get_clang_compiler_defines were broken by not defining the language they used. Neither GCC nor Clang infer the language based on the driver name which means `self.defines` isn't populated correctly in compilers/cpp.py. e.g. ``` $ echo "" | g++ -E -dM - | grep -i cplus $ echo "" | g++ -x c++ -E -dM - | grep -i cplus #define __cplusplus 201703L ``` Fix that by passing '-cpp -x LANGUAGE' as a first pass. If it fails, try again without '-cpp -x LANGUAGE' as before, as its portability isn't certain. We do '-cpp' because during testing, I found Fortran needs this, although per below, I had to drop Fortran in the end and leave it to the fallback (existing) path. Without this change, a63739d394dd77314270f5a46f79171a8c544e77 is only partially effective. It works if the system has injected Clang options via /etc/clang configuration files, but not by e.g. patching the driver (or for GCC there too). Unfortunately, we have to wimp out for Fortran and fallback to the old method because you need the language standard (e.g. -x f95).
-rw-r--r--mesonbuild/compilers/detect.py91
-rw-r--r--mesonbuild/compilers/mixins/clang.py7
2 files changed, 77 insertions, 21 deletions
diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py
index f16e40f..255812c 100644
--- a/mesonbuild/compilers/detect.py
+++ b/mesonbuild/compilers/detect.py
@@ -340,7 +340,7 @@ def _detect_c_or_cpp_compiler(env: 'Environment', lang: str, for_machine: Machin
guess_gcc_or_lcc = None
if guess_gcc_or_lcc:
- defines = _get_gnu_compiler_defines(compiler)
+ defines = _get_gnu_compiler_defines(compiler, lang)
if not defines:
popen_exceptions[join_args(compiler)] = 'no pre-processor defines'
continue
@@ -449,7 +449,7 @@ def _detect_c_or_cpp_compiler(env: 'Environment', lang: str, for_machine: Machin
if 'clang' in out or 'Clang' in out:
linker = None
- defines = _get_clang_compiler_defines(compiler)
+ defines = _get_clang_compiler_defines(compiler, lang)
# Even if the for_machine is darwin, we could be using vanilla
# clang.
@@ -676,7 +676,7 @@ def detect_fortran_compiler(env: 'Environment', for_machine: MachineChoice) -> C
guess_gcc_or_lcc = 'lcc'
if guess_gcc_or_lcc:
- defines = _get_gnu_compiler_defines(compiler)
+ defines = _get_gnu_compiler_defines(compiler, 'fortran')
if not defines:
popen_exceptions[join_args(compiler)] = 'no pre-processor defines'
continue
@@ -843,7 +843,7 @@ def _detect_objc_or_objcpp_compiler(env: 'Environment', lang: str, for_machine:
continue
version = search_version(out)
if 'Free Software Foundation' in out:
- defines = _get_gnu_compiler_defines(compiler)
+ defines = _get_gnu_compiler_defines(compiler, lang)
if not defines:
popen_exceptions[join_args(compiler)] = 'no pre-processor defines'
continue
@@ -855,7 +855,7 @@ def _detect_objc_or_objcpp_compiler(env: 'Environment', lang: str, for_machine:
defines, linker=linker)
if 'clang' in out:
linker = None
- defines = _get_clang_compiler_defines(compiler)
+ defines = _get_clang_compiler_defines(compiler, lang)
if not defines:
popen_exceptions[join_args(compiler)] = 'no pre-processor defines'
continue
@@ -1329,19 +1329,43 @@ def detect_masm_compiler(env: 'Environment', for_machine: MachineChoice) -> Comp
# GNU/Clang defines and version
# =============================
-def _get_gnu_compiler_defines(compiler: T.List[str]) -> T.Dict[str, str]:
+def _get_gnu_compiler_defines(compiler: T.List[str], lang: str) -> T.Dict[str, str]:
"""
Get the list of GCC pre-processor defines
"""
+ from .mixins.gnu import _LANG_MAP as gnu_LANG_MAP
+
+ def _try_obtain_compiler_defines(args: T.List[str]) -> str:
+ mlog.debug(f'Running command: {join_args(args)}')
+ p, output, error = Popen_safe(compiler + args, write='', stdin=subprocess.PIPE)
+ if p.returncode != 0:
+ raise EnvironmentException('Unable to get gcc pre-processor defines:\n'
+ f'Compiler stdout:\n{output}\n-----\n'
+ f'Compiler stderr:\n{error}\n-----\n')
+ return output
+
# Arguments to output compiler pre-processor defines to stdout
# gcc, g++, and gfortran all support these arguments
- args = compiler + ['-E', '-dM', '-']
- mlog.debug(f'Running command: {join_args(args)}')
- p, output, error = Popen_safe(args, write='', stdin=subprocess.PIPE)
- if p.returncode != 0:
- raise EnvironmentException('Unable to detect gcc pre-processor defines:\n'
- f'Compiler stdout:\n{output}\n-----\n'
- f'Compiler stderr:\n{error}\n-----\n')
+ baseline_test_args = ['-E', '-dM', '-']
+ try:
+ # We assume that when _get_gnu_compiler_defines is called, it's
+ # close enough to a GCCish compiler so we reuse the _LANG_MAP
+ # from the GCC mixin. This isn't a dangerous assumption because
+ # we fallback if the detection fails anyway.
+
+ # We might not have a match for Fortran, so fallback to detection
+ # based on the driver.
+ lang = gnu_LANG_MAP[lang]
+
+ # The compiler may not infer the target language based on the driver name
+ # so first, try with '-cpp -x lang', then fallback without given it's less
+ # portable. We try with '-cpp' as GCC needs it for Fortran at least, and
+ # it seems to do no harm.
+ output = _try_obtain_compiler_defines(['-cpp', '-x', lang] + baseline_test_args)
+ except (EnvironmentException, KeyError):
+ mlog.debug(f'pre-processor extraction using -cpp -x {lang} failed, falling back w/o lang')
+ output = _try_obtain_compiler_defines(baseline_test_args)
+
# Parse several lines of the type:
# `#define ___SOME_DEF some_value`
# and extract `___SOME_DEF`
@@ -1358,17 +1382,42 @@ def _get_gnu_compiler_defines(compiler: T.List[str]) -> T.Dict[str, str]:
defines[rest[0]] = rest[1]
return defines
-def _get_clang_compiler_defines(compiler: T.List[str]) -> T.Dict[str, str]:
+def _get_clang_compiler_defines(compiler: T.List[str], lang: str) -> T.Dict[str, str]:
"""
Get the list of Clang pre-processor defines
"""
- args = compiler + ['-E', '-dM', '-']
- mlog.debug(f'Running command: {join_args(args)}')
- p, output, error = Popen_safe(args, write='', stdin=subprocess.PIPE)
- if p.returncode != 0:
- raise EnvironmentException('Unable to get clang pre-processor defines:\n'
- f'Compiler stdout:\n{output}\n-----\n'
- f'Compiler stderr:\n{error}\n-----\n')
+ from .mixins.clang import _LANG_MAP as clang_LANG_MAP
+
+ def _try_obtain_compiler_defines(args: T.List[str]) -> str:
+ mlog.debug(f'Running command: {join_args(args)}')
+ p, output, error = Popen_safe(compiler + args, write='', stdin=subprocess.PIPE)
+ if p.returncode != 0:
+ raise EnvironmentException('Unable to get clang pre-processor defines:\n'
+ f'Compiler stdout:\n{output}\n-----\n'
+ f'Compiler stderr:\n{error}\n-----\n')
+ return output
+
+ # Arguments to output compiler pre-processor defines to stdout
+ baseline_test_args = ['-E', '-dM', '-']
+ try:
+ # We assume that when _get_clang_compiler_defines is called, it's
+ # close enough to a Clangish compiler so we reuse the _LANG_MAP
+ # from the Clang mixin. This isn't a dangerous assumption because
+ # we fallback if the detection fails anyway.
+
+ # We might not have a match for Fortran, so fallback to detection
+ # based on the driver.
+ lang = clang_LANG_MAP[lang]
+
+ # The compiler may not infer the target language based on the driver name
+ # so first, try with '-cpp -x lang', then fallback without given it's less
+ # portable. We try with '-cpp' as GCC needs it for Fortran at least, and
+ # it seems to do no harm.
+ output = _try_obtain_compiler_defines(['-cpp', '-x', lang] + baseline_test_args)
+ except (EnvironmentException, KeyError):
+ mlog.debug(f'pre-processor extraction using -cpp -x {lang} failed, falling back w/o lang')
+ output = _try_obtain_compiler_defines(baseline_test_args)
+
defines: T.Dict[str, str] = {}
for line in output.split('\n'):
if not line:
diff --git a/mesonbuild/compilers/mixins/clang.py b/mesonbuild/compilers/mixins/clang.py
index a9bf566..de9d8f8 100644
--- a/mesonbuild/compilers/mixins/clang.py
+++ b/mesonbuild/compilers/mixins/clang.py
@@ -36,6 +36,13 @@ clang_optimization_args: T.Dict[str, T.List[str]] = {
's': ['-Oz'],
}
+_LANG_MAP = {
+ 'c': 'c',
+ 'cpp': 'c++',
+ 'objc': 'objective-c',
+ 'objcpp': 'objective-c++',
+}
+
class ClangCompiler(GnuLikeCompiler):
id = 'clang'