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 guess_gcc_or_lcc = None
if guess_gcc_or_lcc: if guess_gcc_or_lcc:
defines = _get_gnu_compiler_defines(compiler) defines = _get_gnu_compiler_defines(compiler, lang)
if not defines: if not defines:
popen_exceptions[join_args(compiler)] = 'no pre-processor defines' popen_exceptions[join_args(compiler)] = 'no pre-processor defines'
continue 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: if 'clang' in out or 'Clang' in out:
linker = None 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 # Even if the for_machine is darwin, we could be using vanilla
# clang. # clang.
@ -676,7 +676,7 @@ def detect_fortran_compiler(env: 'Environment', for_machine: MachineChoice) -> C
guess_gcc_or_lcc = 'lcc' guess_gcc_or_lcc = 'lcc'
if guess_gcc_or_lcc: if guess_gcc_or_lcc:
defines = _get_gnu_compiler_defines(compiler) defines = _get_gnu_compiler_defines(compiler, 'fortran')
if not defines: if not defines:
popen_exceptions[join_args(compiler)] = 'no pre-processor defines' popen_exceptions[join_args(compiler)] = 'no pre-processor defines'
continue continue
@ -843,7 +843,7 @@ def _detect_objc_or_objcpp_compiler(env: 'Environment', lang: str, for_machine:
continue continue
version = search_version(out) version = search_version(out)
if 'Free Software Foundation' in out: if 'Free Software Foundation' in out:
defines = _get_gnu_compiler_defines(compiler) defines = _get_gnu_compiler_defines(compiler, lang)
if not defines: if not defines:
popen_exceptions[join_args(compiler)] = 'no pre-processor defines' popen_exceptions[join_args(compiler)] = 'no pre-processor defines'
continue continue
@ -855,7 +855,7 @@ def _detect_objc_or_objcpp_compiler(env: 'Environment', lang: str, for_machine:
defines, linker=linker) defines, linker=linker)
if 'clang' in out: if 'clang' in out:
linker = None linker = None
defines = _get_clang_compiler_defines(compiler) defines = _get_clang_compiler_defines(compiler, lang)
if not defines: if not defines:
popen_exceptions[join_args(compiler)] = 'no pre-processor defines' popen_exceptions[join_args(compiler)] = 'no pre-processor defines'
continue continue
@ -1329,19 +1329,43 @@ def detect_masm_compiler(env: 'Environment', for_machine: MachineChoice) -> Comp
# GNU/Clang defines and version # 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 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 # Arguments to output compiler pre-processor defines to stdout
# gcc, g++, and gfortran all support these arguments # gcc, g++, and gfortran all support these arguments
args = compiler + ['-E', '-dM', '-'] baseline_test_args = ['-E', '-dM', '-']
mlog.debug(f'Running command: {join_args(args)}') try:
p, output, error = Popen_safe(args, write='', stdin=subprocess.PIPE) # We assume that when _get_gnu_compiler_defines is called, it's
if p.returncode != 0: # close enough to a GCCish compiler so we reuse the _LANG_MAP
raise EnvironmentException('Unable to detect gcc pre-processor defines:\n' # from the GCC mixin. This isn't a dangerous assumption because
f'Compiler stdout:\n{output}\n-----\n' # we fallback if the detection fails anyway.
f'Compiler stderr:\n{error}\n-----\n')
# 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: # Parse several lines of the type:
# `#define ___SOME_DEF some_value` # `#define ___SOME_DEF some_value`
# and extract `___SOME_DEF` # 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] defines[rest[0]] = rest[1]
return defines 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 Get the list of Clang pre-processor defines
""" """
args = compiler + ['-E', '-dM', '-'] from .mixins.clang import _LANG_MAP as clang_LANG_MAP
mlog.debug(f'Running command: {join_args(args)}')
p, output, error = Popen_safe(args, write='', stdin=subprocess.PIPE) def _try_obtain_compiler_defines(args: T.List[str]) -> str:
if p.returncode != 0: mlog.debug(f'Running command: {join_args(args)}')
raise EnvironmentException('Unable to get clang pre-processor defines:\n' p, output, error = Popen_safe(compiler + args, write='', stdin=subprocess.PIPE)
f'Compiler stdout:\n{output}\n-----\n' if p.returncode != 0:
f'Compiler stderr:\n{error}\n-----\n') 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] = {} defines: T.Dict[str, str] = {}
for line in output.split('\n'): for line in output.split('\n'):
if not line: if not line:

@ -36,6 +36,13 @@ clang_optimization_args: T.Dict[str, T.List[str]] = {
's': ['-Oz'], 's': ['-Oz'],
} }
_LANG_MAP = {
'c': 'c',
'cpp': 'c++',
'objc': 'objective-c',
'objcpp': 'objective-c++',
}
class ClangCompiler(GnuLikeCompiler): class ClangCompiler(GnuLikeCompiler):
id = 'clang' id = 'clang'

Loading…
Cancel
Save