compilers: Make GCC detection more robust on all platforms

Fixes https://github.com/mesonbuild/meson/issues/650

Also adds new has_define and get_define internal API for querying
pre-processor defines provided by GNU compilers.
pull/814/head
Nirbheek Chauhan 8 years ago
parent ac8c8c2ba8
commit a001fa0fb6
  1. 44
      mesonbuild/compilers.py
  2. 102
      mesonbuild/environment.py

@ -341,6 +341,12 @@ class Compiler():
def get_exelist(self): def get_exelist(self):
return self.exelist[:] return self.exelist[:]
def get_define(self, *args, **kwargs):
raise EnvironmentException('%s does not support get_define.' % self.id)
def has_define(self, *args, **kwargs):
raise EnvironmentException('%s does not support has_define.' % self.id)
def get_always_args(self): def get_always_args(self):
return [] return []
@ -1880,9 +1886,10 @@ def get_gcc_soname_args(gcc_type, shlib_name, path, soversion):
class GnuCompiler: class GnuCompiler:
# Functionality that is common to all GNU family compilers. # Functionality that is common to all GNU family compilers.
def __init__(self, gcc_type): def __init__(self, gcc_type, defines):
self.id = 'gcc' self.id = 'gcc'
self.gcc_type = gcc_type self.gcc_type = gcc_type
self.defines = defines or {}
self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage', self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage',
'b_colorout', 'b_ndebug'] 'b_colorout', 'b_ndebug']
if self.gcc_type != GCC_OSX: if self.gcc_type != GCC_OSX:
@ -1902,6 +1909,13 @@ class GnuCompiler:
args[args.index('-Wpedantic')] = '-pedantic' args[args.index('-Wpedantic')] = '-pedantic'
return args return args
def has_define(self, define):
return define in self.defines
def get_define(self, define):
if define in self.defines:
return defines[define]
def get_pic_args(self): def get_pic_args(self):
if self.gcc_type == GCC_MINGW: if self.gcc_type == GCC_MINGW:
return [] # On Window gcc defaults to fpic being always on. return [] # On Window gcc defaults to fpic being always on.
@ -1926,9 +1940,9 @@ class GnuCompiler:
return get_gcc_soname_args(self.gcc_type, shlib_name, path, soversion) return get_gcc_soname_args(self.gcc_type, shlib_name, path, soversion)
class GnuCCompiler(GnuCompiler, CCompiler): class GnuCCompiler(GnuCompiler, CCompiler):
def __init__(self, exelist, version, gcc_type, is_cross, exe_wrapper=None): def __init__(self, exelist, version, gcc_type, is_cross, exe_wrapper=None, defines=None):
CCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) CCompiler.__init__(self, exelist, version, is_cross, exe_wrapper)
GnuCompiler.__init__(self, gcc_type) GnuCompiler.__init__(self, gcc_type, defines)
# Gcc can do asm, too. # Gcc can do asm, too.
self.can_compile_suffixes.add('s') self.can_compile_suffixes.add('s')
self.warn_args = {'1': ['-Wall', '-Winvalid-pch'], self.warn_args = {'1': ['-Wall', '-Winvalid-pch'],
@ -1960,9 +1974,9 @@ class GnuCCompiler(GnuCompiler, CCompiler):
class GnuCPPCompiler(GnuCompiler, CPPCompiler): class GnuCPPCompiler(GnuCompiler, CPPCompiler):
def __init__(self, exelist, version, gcc_type, is_cross, exe_wrap): def __init__(self, exelist, version, gcc_type, is_cross, exe_wrap, defines):
CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrap) CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrap)
GnuCompiler.__init__(self, gcc_type) GnuCompiler.__init__(self, gcc_type, defines)
self.warn_args = {'1': ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'], self.warn_args = {'1': ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'],
'2': ['-Wall', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor'], '2': ['-Wall', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor'],
'3': ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor']} '3': ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor']}
@ -1998,22 +2012,22 @@ class GnuCPPCompiler(GnuCompiler, CPPCompiler):
class GnuObjCCompiler(GnuCompiler,ObjCCompiler): class GnuObjCCompiler(GnuCompiler,ObjCCompiler):
def __init__(self, exelist, version, is_cross, exe_wrapper=None): def __init__(self, exelist, version, is_cross, exe_wrapper=None, defines=None):
ObjCCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) ObjCCompiler.__init__(self, exelist, version, is_cross, exe_wrapper)
# Not really correct, but GNU objc is only used on non-OSX non-win. File a bug # Not really correct, but GNU objc is only used on non-OSX non-win. File a bug
# if this breaks your use case. # if this breaks your use case.
GnuCompiler.__init__(self, GCC_STANDARD) GnuCompiler.__init__(self, GCC_STANDARD, defines)
self.warn_args = {'1': ['-Wall', '-Winvalid-pch'], self.warn_args = {'1': ['-Wall', '-Winvalid-pch'],
'2': ['-Wall', '-Wextra', '-Winvalid-pch'], '2': ['-Wall', '-Wextra', '-Winvalid-pch'],
'3' : ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch']} '3' : ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch']}
class GnuObjCPPCompiler(GnuCompiler, ObjCPPCompiler): class GnuObjCPPCompiler(GnuCompiler, ObjCPPCompiler):
def __init__(self, exelist, version, is_cross, exe_wrapper=None): def __init__(self, exelist, version, is_cross, exe_wrapper=None, defines=None):
ObjCCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) ObjCPPCompiler.__init__(self, exelist, version, is_cross, exe_wrapper)
# Not really correct, but GNU objc is only used on non-OSX non-win. File a bug # Not really correct, but GNU objc is only used on non-OSX non-win. File a bug
# if this breaks your use case. # if this breaks your use case.
GnuCompiler.__init__(self, GCC_STANDARD) GnuCompiler.__init__(self, GCC_STANDARD, defines)
self.warn_args = {'1': ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'], self.warn_args = {'1': ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'],
'2': ['-Wall', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor'], '2': ['-Wall', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor'],
'3' : ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor']} '3' : ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor']}
@ -2226,11 +2240,19 @@ end program prog
class GnuFortranCompiler(FortranCompiler): class GnuFortranCompiler(FortranCompiler):
def __init__(self, exelist, version, gcc_type, is_cross, exe_wrapper=None): def __init__(self, exelist, version, gcc_type, is_cross, exe_wrapper=None, defines=None):
super().__init__(exelist, version, is_cross, exe_wrapper=None) super().__init__(exelist, version, is_cross, exe_wrapper=None)
self.gcc_type = gcc_type self.gcc_type = gcc_type
self.defines = defines or {}
self.id = 'gcc' self.id = 'gcc'
def has_define(self, define):
return define in self.defines
def get_define(self, define):
if define in self.defines:
return defines[define]
def get_always_args(self): def get_always_args(self):
return ['-pipe'] return ['-pipe']

@ -289,6 +289,45 @@ class Environment():
if type(oldval) != type(value): if type(oldval) != type(value):
self.coredata.user_options[name] = value self.coredata.user_options[name] = value
@staticmethod
def get_gnu_compiler_defines(compiler):
"""
Detect GNU compiler platform type (Apple, MinGW, Unix)
"""
# Arguments to output compiler pre-processor defines to stdout
# gcc, g++, and gfortran all support these arguments
args = compiler + ['-E', '-dM', '-']
p = subprocess.Popen(args, universal_newlines=True,
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
output = p.communicate('')[0]
if p.returncode != 0:
raise EnvironmentException('Unable to detect GNU compiler type:\n' + output)
# Parse several lines of the type:
# `#define ___SOME_DEF some_value`
# and extract `___SOME_DEF`
defines = {}
for line in output.split('\n'):
if not line:
continue
d, *rest = line.split(' ', 2)
if d != '#define':
continue
if len(rest) == 1:
defines[rest] = True
if len(rest) == 2:
defines[rest[0]] = rest[1]
return defines
@staticmethod
def get_gnu_compiler_type(defines):
# Detect GCC type (Apple, MinGW, Cygwin, Unix)
if '__APPLE__' in defines:
return GCC_OSX
elif '__MINGW32__' in defines or '__MINGW64__' in defines:
return GCC_MINGW
# We ignore Cygwin for now, and treat it as a standard GCC
return GCC_STANDARD
def detect_c_compiler(self, want_cross): def detect_c_compiler(self, want_cross):
evar = 'CC' evar = 'CC'
if self.is_cross_build() and want_cross: if self.is_cross_build() and want_cross:
@ -330,16 +369,13 @@ class Environment():
version = vmatch.group(0) version = vmatch.group(0)
else: else:
version = 'unknown version' version = 'unknown version'
if 'apple' in out and 'Free Software Foundation' in out: if 'Free Software Foundation' in out:
return GnuCCompiler(ccache + [compiler], version, GCC_OSX, is_cross, exe_wrap) defines = self.get_gnu_compiler_defines([compiler])
if (out.startswith('cc') or 'gcc' in out.lower()) and \ if not defines:
'Free Software Foundation' in out: popen_exceptions[compiler] = 'no pre-processor defines'
lowerout = out.lower() continue
if 'mingw' in lowerout or 'msys' in lowerout or 'mingw' in compiler.lower(): gtype = self.get_gnu_compiler_type(defines)
gtype = GCC_MINGW return GnuCCompiler(ccache + [compiler], version, gtype, is_cross, exe_wrap, defines)
else:
gtype = GCC_STANDARD
return GnuCCompiler(ccache + [compiler], version, gtype, is_cross, exe_wrap)
if 'clang' in out: if 'clang' in out:
if 'Apple' in out: if 'Apple' in out:
cltype = CLANG_OSX cltype = CLANG_OSX
@ -395,13 +431,12 @@ class Environment():
version = vmatch.group(0) version = vmatch.group(0)
if 'GNU Fortran' in out: if 'GNU Fortran' in out:
if mesonlib.is_osx(): defines = self.get_gnu_compiler_defines([compiler])
gcctype = GCC_OSX if not defines:
elif mesonlib.is_windows(): popen_exceptions[compiler] = 'no pre-processor defines'
gcctype = GCC_MINGW continue
else: gtype = self.get_gnu_compiler_type(defines)
gcctype = GCC_STANDARD return GnuFortranCompiler([compiler], version, gtype, is_cross, exe_wrap, defines)
return GnuFortranCompiler([compiler], version, gcctype, is_cross, exe_wrap)
if 'G95' in out: if 'G95' in out:
return G95FortranCompiler([compiler], version, is_cross, exe_wrap) return G95FortranCompiler([compiler], version, is_cross, exe_wrap)
@ -483,16 +518,13 @@ class Environment():
version = vmatch.group(0) version = vmatch.group(0)
else: else:
version = 'unknown version' version = 'unknown version'
if 'apple' in out and 'Free Software Foundation' in out: if 'Free Software Foundation' in out:
return GnuCPPCompiler(ccache + [compiler], version, GCC_OSX, is_cross, exe_wrap) defines = self.get_gnu_compiler_defines([compiler])
if (out.startswith('c++ ') or 'g++' in out or 'GCC' in out) and \ if not defines:
'Free Software Foundation' in out: popen_exceptions[compiler] = 'no pre-processor defines'
lowerout = out.lower() continue
if 'mingw' in lowerout or 'msys' in lowerout or 'mingw' in compiler.lower(): gtype = self.get_gnu_compiler_type(defines)
gtype = GCC_MINGW return GnuCPPCompiler(ccache + [compiler], version, gtype, is_cross, exe_wrap, defines)
else:
gtype = GCC_STANDARD
return GnuCPPCompiler(ccache + [compiler], version, gtype, is_cross, exe_wrap)
if 'clang' in out: if 'clang' in out:
if 'Apple' in out: if 'Apple' in out:
cltype = CLANG_OSX cltype = CLANG_OSX
@ -533,13 +565,11 @@ class Environment():
version = vmatch.group(0) version = vmatch.group(0)
else: else:
version = 'unknown version' version = 'unknown version'
if (out.startswith('cc ') or 'gcc' in out) and \ if 'Free Software Foundation' in out:
'Free Software Foundation' in out: defines = self.get_gnu_compiler_defines(exelist)
return GnuObjCCompiler(exelist, version, is_cross, exe_wrap) return GnuObjCCompiler(exelist, version, is_cross, exe_wrap, defines)
if out.startswith('Apple LLVM'): if out.startswith('Apple LLVM'):
return ClangObjCCompiler(exelist, version, CLANG_OSX, is_cross, exe_wrap) return ClangObjCCompiler(exelist, version, CLANG_OSX, is_cross, exe_wrap)
if 'apple' in out and 'Free Software Foundation' in out:
return GnuObjCCompiler(exelist, version, is_cross, exe_wrap)
raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"') raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
def detect_objcpp_compiler(self, want_cross): def detect_objcpp_compiler(self, want_cross):
@ -566,13 +596,11 @@ class Environment():
version = vmatch.group(0) version = vmatch.group(0)
else: else:
version = 'unknown version' version = 'unknown version'
if (out.startswith('c++ ') or out.startswith('g++')) and \ if 'Free Software Foundation' in out:
'Free Software Foundation' in out: defines = self.get_gnu_compiler_defines(exelist)
return GnuObjCPPCompiler(exelist, version, is_cross, exe_wrap) return GnuObjCPPCompiler(exelist, version, is_cross, exe_wrap, defines)
if out.startswith('Apple LLVM'): if out.startswith('Apple LLVM'):
return ClangObjCPPCompiler(exelist, version, CLANG_OSX, is_cross, exe_wrap) return ClangObjCPPCompiler(exelist, version, CLANG_OSX, is_cross, exe_wrap)
if 'apple' in out and 'Free Software Foundation' in out:
return GnuObjCPPCompiler(exelist, version, is_cross, exe_wrap)
raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"') raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
def detect_java_compiler(self): def detect_java_compiler(self):

Loading…
Cancel
Save