Skip to content

Commit

Permalink
compilers: detect: fix pre-processor scraping by defining language
Browse files Browse the repository at this point in the history
_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 '-x LANGUAGE' as a first pass. If it fails, try
again without '-x LANGUAGE' as before, as its portability isn't
certain.

While here, make the debug & error message strings consistent between
the GCC and Clang probes.

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).
  • Loading branch information
thesamesam committed Jun 9, 2024
1 parent 374fa7f commit ef7038a
Showing 1 changed file with 53 additions and 21 deletions.
74 changes: 53 additions & 21 deletions mesonbuild/compilers/detect.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ def sanitize(p: T.Optional[str]) -> T.Optional[str]:
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
Expand Down Expand Up @@ -449,7 +449,7 @@ def sanitize(p: T.Optional[str]) -> T.Optional[str]:
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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -1329,19 +1329,34 @@ 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]:
"""
Detect GNU compiler platform type (Apple, MinGW, Unix)
"""
# TODO: could really do with _LANG_MAP here for e.g. objc
lang = lang.replace('cpp', 'c++')

def _try_obtain_compiler_defines(args):
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 GNU compiler type:\n'
f'Compiler stdout:\n{output}\n-----\n'
f'Compiler stderr:\n{error}\n-----\n')
baseline_test_args = ['-E', '-dM', '-']
try:
# The compiler may not infer the target language based on the driver name
# so first, try with -x lang, then fallback without given it's less
# portable.
output = _try_obtain_compiler_defines(['-x', lang] + baseline_test_args)
except EnvironmentException:
mlog.debug(f'pre-processor extraction using -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`
Expand All @@ -1358,17 +1373,34 @@ 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')

# TODO: could really do with _LANG_MAP here for e.g. objc
lang = lang.replace('cpp', 'c++')

def _try_obtain_compiler_defines(args):
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:
# The compiler may not infer the target language based on the driver name
# so first, try with -x lang, then fallback without given it's less
# portable.
output = _try_obtain_compiler_defines(['-x', lang] + baseline_test_args)
except EnvironmentException:
mlog.debug(f'pre-processor extraction using -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:
Expand Down

0 comments on commit ef7038a

Please sign in to comment.