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).
This commit is contained in:
parent
b56a3198b4
commit
4ad792e158
|
@ -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…
Reference in New Issue