Skip to content

Commit 4ad792e

Browse files
committed
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, a63739d 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).
1 parent b56a319 commit 4ad792e

File tree

2 files changed

+77
-21
lines changed

2 files changed

+77
-21
lines changed

mesonbuild/compilers/detect.py

Lines changed: 70 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ def sanitize(p: T.Optional[str]) -> T.Optional[str]:
340340
guess_gcc_or_lcc = None
341341

342342
if guess_gcc_or_lcc:
343-
defines = _get_gnu_compiler_defines(compiler)
343+
defines = _get_gnu_compiler_defines(compiler, lang)
344344
if not defines:
345345
popen_exceptions[join_args(compiler)] = 'no pre-processor defines'
346346
continue
@@ -449,7 +449,7 @@ def sanitize(p: T.Optional[str]) -> T.Optional[str]:
449449
if 'clang' in out or 'Clang' in out:
450450
linker = None
451451

452-
defines = _get_clang_compiler_defines(compiler)
452+
defines = _get_clang_compiler_defines(compiler, lang)
453453

454454
# Even if the for_machine is darwin, we could be using vanilla
455455
# clang.
@@ -676,7 +676,7 @@ def detect_fortran_compiler(env: 'Environment', for_machine: MachineChoice) -> C
676676
guess_gcc_or_lcc = 'lcc'
677677

678678
if guess_gcc_or_lcc:
679-
defines = _get_gnu_compiler_defines(compiler)
679+
defines = _get_gnu_compiler_defines(compiler, 'fortran')
680680
if not defines:
681681
popen_exceptions[join_args(compiler)] = 'no pre-processor defines'
682682
continue
@@ -843,7 +843,7 @@ def _detect_objc_or_objcpp_compiler(env: 'Environment', lang: str, for_machine:
843843
continue
844844
version = search_version(out)
845845
if 'Free Software Foundation' in out:
846-
defines = _get_gnu_compiler_defines(compiler)
846+
defines = _get_gnu_compiler_defines(compiler, lang)
847847
if not defines:
848848
popen_exceptions[join_args(compiler)] = 'no pre-processor defines'
849849
continue
@@ -855,7 +855,7 @@ def _detect_objc_or_objcpp_compiler(env: 'Environment', lang: str, for_machine:
855855
defines, linker=linker)
856856
if 'clang' in out:
857857
linker = None
858-
defines = _get_clang_compiler_defines(compiler)
858+
defines = _get_clang_compiler_defines(compiler, lang)
859859
if not defines:
860860
popen_exceptions[join_args(compiler)] = 'no pre-processor defines'
861861
continue
@@ -1329,19 +1329,43 @@ def detect_masm_compiler(env: 'Environment', for_machine: MachineChoice) -> Comp
13291329
# GNU/Clang defines and version
13301330
# =============================
13311331

1332-
def _get_gnu_compiler_defines(compiler: T.List[str]) -> T.Dict[str, str]:
1332+
def _get_gnu_compiler_defines(compiler: T.List[str], lang: str) -> T.Dict[str, str]:
13331333
"""
13341334
Get the list of GCC pre-processor defines
13351335
"""
1336+
from .mixins.gnu import _LANG_MAP as gnu_LANG_MAP
1337+
1338+
def _try_obtain_compiler_defines(args: T.List[str]) -> str:
1339+
mlog.debug(f'Running command: {join_args(args)}')
1340+
p, output, error = Popen_safe(compiler + args, write='', stdin=subprocess.PIPE)
1341+
if p.returncode != 0:
1342+
raise EnvironmentException('Unable to get gcc pre-processor defines:\n'
1343+
f'Compiler stdout:\n{output}\n-----\n'
1344+
f'Compiler stderr:\n{error}\n-----\n')
1345+
return output
1346+
13361347
# Arguments to output compiler pre-processor defines to stdout
13371348
# gcc, g++, and gfortran all support these arguments
1338-
args = compiler + ['-E', '-dM', '-']
1339-
mlog.debug(f'Running command: {join_args(args)}')
1340-
p, output, error = Popen_safe(args, write='', stdin=subprocess.PIPE)
1341-
if p.returncode != 0:
1342-
raise EnvironmentException('Unable to detect gcc pre-processor defines:\n'
1343-
f'Compiler stdout:\n{output}\n-----\n'
1344-
f'Compiler stderr:\n{error}\n-----\n')
1349+
baseline_test_args = ['-E', '-dM', '-']
1350+
try:
1351+
# We assume that when _get_gnu_compiler_defines is called, it's
1352+
# close enough to a GCCish compiler so we reuse the _LANG_MAP
1353+
# from the GCC mixin. This isn't a dangerous assumption because
1354+
# we fallback if the detection fails anyway.
1355+
1356+
# We might not have a match for Fortran, so fallback to detection
1357+
# based on the driver.
1358+
lang = gnu_LANG_MAP[lang]
1359+
1360+
# The compiler may not infer the target language based on the driver name
1361+
# so first, try with '-cpp -x lang', then fallback without given it's less
1362+
# portable. We try with '-cpp' as GCC needs it for Fortran at least, and
1363+
# it seems to do no harm.
1364+
output = _try_obtain_compiler_defines(['-cpp', '-x', lang] + baseline_test_args)
1365+
except (EnvironmentException, KeyError):
1366+
mlog.debug(f'pre-processor extraction using -cpp -x {lang} failed, falling back w/o lang')
1367+
output = _try_obtain_compiler_defines(baseline_test_args)
1368+
13451369
# Parse several lines of the type:
13461370
# `#define ___SOME_DEF some_value`
13471371
# and extract `___SOME_DEF`
@@ -1358,17 +1382,42 @@ def _get_gnu_compiler_defines(compiler: T.List[str]) -> T.Dict[str, str]:
13581382
defines[rest[0]] = rest[1]
13591383
return defines
13601384

1361-
def _get_clang_compiler_defines(compiler: T.List[str]) -> T.Dict[str, str]:
1385+
def _get_clang_compiler_defines(compiler: T.List[str], lang: str) -> T.Dict[str, str]:
13621386
"""
13631387
Get the list of Clang pre-processor defines
13641388
"""
1365-
args = compiler + ['-E', '-dM', '-']
1366-
mlog.debug(f'Running command: {join_args(args)}')
1367-
p, output, error = Popen_safe(args, write='', stdin=subprocess.PIPE)
1368-
if p.returncode != 0:
1369-
raise EnvironmentException('Unable to get clang pre-processor defines:\n'
1370-
f'Compiler stdout:\n{output}\n-----\n'
1371-
f'Compiler stderr:\n{error}\n-----\n')
1389+
from .mixins.clang import _LANG_MAP as clang_LANG_MAP
1390+
1391+
def _try_obtain_compiler_defines(args: T.List[str]) -> str:
1392+
mlog.debug(f'Running command: {join_args(args)}')
1393+
p, output, error = Popen_safe(compiler + args, write='', stdin=subprocess.PIPE)
1394+
if p.returncode != 0:
1395+
raise EnvironmentException('Unable to get clang pre-processor defines:\n'
1396+
f'Compiler stdout:\n{output}\n-----\n'
1397+
f'Compiler stderr:\n{error}\n-----\n')
1398+
return output
1399+
1400+
# Arguments to output compiler pre-processor defines to stdout
1401+
baseline_test_args = ['-E', '-dM', '-']
1402+
try:
1403+
# We assume that when _get_clang_compiler_defines is called, it's
1404+
# close enough to a Clangish compiler so we reuse the _LANG_MAP
1405+
# from the Clang mixin. This isn't a dangerous assumption because
1406+
# we fallback if the detection fails anyway.
1407+
1408+
# We might not have a match for Fortran, so fallback to detection
1409+
# based on the driver.
1410+
lang = clang_LANG_MAP[lang]
1411+
1412+
# The compiler may not infer the target language based on the driver name
1413+
# so first, try with '-cpp -x lang', then fallback without given it's less
1414+
# portable. We try with '-cpp' as GCC needs it for Fortran at least, and
1415+
# it seems to do no harm.
1416+
output = _try_obtain_compiler_defines(['-cpp', '-x', lang] + baseline_test_args)
1417+
except (EnvironmentException, KeyError):
1418+
mlog.debug(f'pre-processor extraction using -cpp -x {lang} failed, falling back w/o lang')
1419+
output = _try_obtain_compiler_defines(baseline_test_args)
1420+
13721421
defines: T.Dict[str, str] = {}
13731422
for line in output.split('\n'):
13741423
if not line:

mesonbuild/compilers/mixins/clang.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@
3636
's': ['-Oz'],
3737
}
3838

39+
_LANG_MAP = {
40+
'c': 'c',
41+
'cpp': 'c++',
42+
'objc': 'objective-c',
43+
'objcpp': 'objective-c++',
44+
}
45+
3946
class ClangCompiler(GnuLikeCompiler):
4047

4148
id = 'clang'

0 commit comments

Comments
 (0)