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, a63739d394 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).
pull/13289/head
Sam James 5 months ago
parent b56a3198b4
commit 4ad792e158
No known key found for this signature in database
GPG Key ID: 738409F520DF9190
  1. 91
      mesonbuild/compilers/detect.py
  2. 7
      mesonbuild/compilers/mixins/clang.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:

@ -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'

Loading…
Cancel
Save