diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py index 16ca1213f..08bb7f856 100644 --- a/mesonbuild/compilers.py +++ b/mesonbuild/compilers.py @@ -341,6 +341,12 @@ class Compiler(): def get_exelist(self): 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): return [] @@ -1880,9 +1886,10 @@ def get_gcc_soname_args(gcc_type, shlib_name, path, soversion): class GnuCompiler: # Functionality that is common to all GNU family compilers. - def __init__(self, gcc_type): + def __init__(self, gcc_type, defines): self.id = 'gcc' self.gcc_type = gcc_type + self.defines = defines or {} self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage', 'b_colorout', 'b_ndebug'] if self.gcc_type != GCC_OSX: @@ -1902,6 +1909,13 @@ class GnuCompiler: args[args.index('-Wpedantic')] = '-pedantic' 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): if self.gcc_type == GCC_MINGW: 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) 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) - GnuCompiler.__init__(self, gcc_type) + GnuCompiler.__init__(self, gcc_type, defines) # Gcc can do asm, too. self.can_compile_suffixes.add('s') self.warn_args = {'1': ['-Wall', '-Winvalid-pch'], @@ -1960,9 +1974,9 @@ class GnuCCompiler(GnuCompiler, CCompiler): 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) - GnuCompiler.__init__(self, gcc_type) + GnuCompiler.__init__(self, gcc_type, defines) self.warn_args = {'1': ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'], '2': ['-Wall', '-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): - 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) # Not really correct, but GNU objc is only used on non-OSX non-win. File a bug # if this breaks your use case. - GnuCompiler.__init__(self, GCC_STANDARD) + GnuCompiler.__init__(self, GCC_STANDARD, defines) self.warn_args = {'1': ['-Wall', '-Winvalid-pch'], '2': ['-Wall', '-Wextra', '-Winvalid-pch'], '3' : ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch']} class GnuObjCPPCompiler(GnuCompiler, ObjCPPCompiler): - def __init__(self, exelist, version, is_cross, exe_wrapper=None): - ObjCCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) + def __init__(self, exelist, version, is_cross, exe_wrapper=None, defines=None): + 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 # 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'], '2': ['-Wall', '-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): - 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) self.gcc_type = gcc_type + self.defines = defines or {} 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): return ['-pipe'] diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 79c09baa1..4166bda50 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -289,6 +289,45 @@ class Environment(): if type(oldval) != type(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): evar = 'CC' if self.is_cross_build() and want_cross: @@ -330,16 +369,13 @@ class Environment(): version = vmatch.group(0) else: version = 'unknown version' - if 'apple' in out and 'Free Software Foundation' in out: - return GnuCCompiler(ccache + [compiler], version, GCC_OSX, is_cross, exe_wrap) - if (out.startswith('cc') or 'gcc' in out.lower()) and \ - 'Free Software Foundation' in out: - lowerout = out.lower() - if 'mingw' in lowerout or 'msys' in lowerout or 'mingw' in compiler.lower(): - gtype = GCC_MINGW - else: - gtype = GCC_STANDARD - return GnuCCompiler(ccache + [compiler], version, gtype, is_cross, exe_wrap) + if 'Free Software Foundation' in out: + defines = self.get_gnu_compiler_defines([compiler]) + if not defines: + popen_exceptions[compiler] = 'no pre-processor defines' + continue + gtype = self.get_gnu_compiler_type(defines) + return GnuCCompiler(ccache + [compiler], version, gtype, is_cross, exe_wrap, defines) if 'clang' in out: if 'Apple' in out: cltype = CLANG_OSX @@ -395,13 +431,12 @@ class Environment(): version = vmatch.group(0) if 'GNU Fortran' in out: - if mesonlib.is_osx(): - gcctype = GCC_OSX - elif mesonlib.is_windows(): - gcctype = GCC_MINGW - else: - gcctype = GCC_STANDARD - return GnuFortranCompiler([compiler], version, gcctype, is_cross, exe_wrap) + defines = self.get_gnu_compiler_defines([compiler]) + if not defines: + popen_exceptions[compiler] = 'no pre-processor defines' + continue + gtype = self.get_gnu_compiler_type(defines) + return GnuFortranCompiler([compiler], version, gtype, is_cross, exe_wrap, defines) if 'G95' in out: return G95FortranCompiler([compiler], version, is_cross, exe_wrap) @@ -483,16 +518,13 @@ class Environment(): version = vmatch.group(0) else: version = 'unknown version' - if 'apple' in out and 'Free Software Foundation' in out: - return GnuCPPCompiler(ccache + [compiler], version, GCC_OSX, is_cross, exe_wrap) - if (out.startswith('c++ ') or 'g++' in out or 'GCC' in out) and \ - 'Free Software Foundation' in out: - lowerout = out.lower() - if 'mingw' in lowerout or 'msys' in lowerout or 'mingw' in compiler.lower(): - gtype = GCC_MINGW - else: - gtype = GCC_STANDARD - return GnuCPPCompiler(ccache + [compiler], version, gtype, is_cross, exe_wrap) + if 'Free Software Foundation' in out: + defines = self.get_gnu_compiler_defines([compiler]) + if not defines: + popen_exceptions[compiler] = 'no pre-processor defines' + continue + gtype = self.get_gnu_compiler_type(defines) + return GnuCPPCompiler(ccache + [compiler], version, gtype, is_cross, exe_wrap, defines) if 'clang' in out: if 'Apple' in out: cltype = CLANG_OSX @@ -533,13 +565,11 @@ class Environment(): version = vmatch.group(0) else: version = 'unknown version' - if (out.startswith('cc ') or 'gcc' in out) and \ - 'Free Software Foundation' in out: - return GnuObjCCompiler(exelist, version, is_cross, exe_wrap) + if 'Free Software Foundation' in out: + defines = self.get_gnu_compiler_defines(exelist) + return GnuObjCCompiler(exelist, version, is_cross, exe_wrap, defines) if out.startswith('Apple LLVM'): 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) + '"') def detect_objcpp_compiler(self, want_cross): @@ -566,13 +596,11 @@ class Environment(): version = vmatch.group(0) else: version = 'unknown version' - if (out.startswith('c++ ') or out.startswith('g++')) and \ - 'Free Software Foundation' in out: - return GnuObjCPPCompiler(exelist, version, is_cross, exe_wrap) + if 'Free Software Foundation' in out: + defines = self.get_gnu_compiler_defines(exelist) + return GnuObjCPPCompiler(exelist, version, is_cross, exe_wrap, defines) if out.startswith('Apple LLVM'): 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) + '"') def detect_java_compiler(self):