aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/compilers/detect.py
diff options
context:
space:
mode:
Diffstat (limited to 'mesonbuild/compilers/detect.py')
-rw-r--r--mesonbuild/compilers/detect.py1209
1 files changed, 1209 insertions, 0 deletions
diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py
new file mode 100644
index 0000000..f79a61b
--- /dev/null
+++ b/mesonbuild/compilers/detect.py
@@ -0,0 +1,1209 @@
+# Copyright 2012-2021 The Meson development team
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ..mesonlib import (
+ MachineChoice, MesonException, EnvironmentException,
+ search_version, is_windows, Popen_safe, windows_proof_rm,
+)
+from ..envconfig import BinaryTable
+from .. import mlog
+
+from ..linkers import (
+ guess_win_linker,
+ guess_nix_linker,
+ AIXArLinker,
+ ArLinker,
+ ArmarLinker,
+ ArmClangDynamicLinker,
+ ArmDynamicLinker,
+ CcrxLinker,
+ CcrxDynamicLinker,
+ CompCertLinker,
+ CompCertDynamicLinker,
+ C2000Linker,
+ C2000DynamicLinker,
+ DLinker,
+ NvidiaHPC_DynamicLinker,
+ PGIDynamicLinker,
+ PGIStaticLinker,
+ StaticLinker,
+ Xc16Linker,
+ Xc16DynamicLinker,
+ XilinkDynamicLinker,
+ CudaLinker,
+ IntelVisualStudioLinker,
+ VisualStudioLinker,
+ VisualStudioLikeLinkerMixin,
+ WASMDynamicLinker,
+)
+from .compilers import Compiler
+from .c import (
+ AppleClangCCompiler,
+ ArmCCompiler,
+ ArmclangCCompiler,
+ ClangCCompiler,
+ ClangClCCompiler,
+ GnuCCompiler,
+ ElbrusCCompiler,
+ EmscriptenCCompiler,
+ IntelCCompiler,
+ IntelClCCompiler,
+ NvidiaHPC_CCompiler,
+ PGICCompiler,
+ CcrxCCompiler,
+ Xc16CCompiler,
+ CompCertCCompiler,
+ C2000CCompiler,
+ VisualStudioCCompiler,
+)
+from .cpp import (
+ AppleClangCPPCompiler,
+ ArmCPPCompiler,
+ ArmclangCPPCompiler,
+ ClangCPPCompiler,
+ ClangClCPPCompiler,
+ GnuCPPCompiler,
+ ElbrusCPPCompiler,
+ EmscriptenCPPCompiler,
+ IntelCPPCompiler,
+ IntelClCPPCompiler,
+ NvidiaHPC_CPPCompiler,
+ PGICPPCompiler,
+ CcrxCPPCompiler,
+ C2000CPPCompiler,
+ VisualStudioCPPCompiler,
+)
+from .cs import MonoCompiler, VisualStudioCsCompiler
+from .d import (
+ DCompiler,
+ DmdDCompiler,
+ GnuDCompiler,
+ LLVMDCompiler,
+)
+from .cuda import CudaCompiler
+from .fortran import (
+ G95FortranCompiler,
+ GnuFortranCompiler,
+ ElbrusFortranCompiler,
+ FlangFortranCompiler,
+ IntelFortranCompiler,
+ IntelClFortranCompiler,
+ NAGFortranCompiler,
+ Open64FortranCompiler,
+ PathScaleFortranCompiler,
+ NvidiaHPC_FortranCompiler,
+ PGIFortranCompiler,
+ SunFortranCompiler,
+)
+from .java import JavaCompiler
+from .objc import (
+ AppleClangObjCCompiler,
+ ClangObjCCompiler,
+ GnuObjCCompiler,
+)
+from .objcpp import (
+ AppleClangObjCPPCompiler,
+ ClangObjCPPCompiler,
+ GnuObjCPPCompiler,
+)
+from .cython import CythonCompiler
+from .rust import RustCompiler
+from .swift import SwiftCompiler
+from .vala import ValaCompiler
+from .mixins.visualstudio import VisualStudioLikeCompiler
+from .mixins.gnu import GnuCompiler
+from .mixins.clang import ClangCompiler
+
+import subprocess
+import platform
+import re
+import shutil
+import tempfile
+import os
+import typing as T
+
+if T.TYPE_CHECKING:
+ from ..environment import Environment
+ from ..programs import ExternalProgram
+
+
+
+# Default compilers and linkers
+# =============================
+
+defaults: T.Dict[str, T.List[str]] = {}
+
+# List of potential compilers.
+if is_windows():
+ # Intel C and C++ compiler is icl on Windows, but icc and icpc elsewhere.
+ # Search for icl before cl, since Intel "helpfully" provides a
+ # cl.exe that returns *exactly the same thing* that microsofts
+ # cl.exe does, and if icl is present, it's almost certainly what
+ # you want.
+ defaults['c'] = ['icl', 'cl', 'cc', 'gcc', 'clang', 'clang-cl', 'pgcc']
+ # There is currently no pgc++ for Windows, only for Mac and Linux.
+ defaults['cpp'] = ['icl', 'cl', 'c++', 'g++', 'clang++', 'clang-cl']
+ defaults['fortran'] = ['ifort', 'gfortran', 'flang', 'pgfortran', 'g95']
+ # Clang and clang++ are valid, but currently unsupported.
+ defaults['objc'] = ['cc', 'gcc']
+ defaults['objcpp'] = ['c++', 'g++']
+ defaults['cs'] = ['csc', 'mcs']
+else:
+ if platform.machine().lower() == 'e2k':
+ # There are no objc or objc++ compilers for Elbrus,
+ # and there's no clang which can build binaries for host.
+ defaults['c'] = ['cc', 'gcc', 'lcc']
+ defaults['cpp'] = ['c++', 'g++', 'l++']
+ defaults['objc'] = []
+ defaults['objcpp'] = []
+ else:
+ defaults['c'] = ['cc', 'gcc', 'clang', 'nvc', 'pgcc', 'icc']
+ defaults['cpp'] = ['c++', 'g++', 'clang++', 'nvc++', 'pgc++', 'icpc']
+ defaults['objc'] = ['cc', 'gcc', 'clang']
+ defaults['objcpp'] = ['c++', 'g++', 'clang++']
+ defaults['fortran'] = ['gfortran', 'flang', 'nvfortran', 'pgfortran', 'ifort', 'g95']
+ defaults['cs'] = ['mcs', 'csc']
+defaults['d'] = ['ldc2', 'ldc', 'gdc', 'dmd']
+defaults['java'] = ['javac']
+defaults['cuda'] = ['nvcc']
+defaults['rust'] = ['rustc']
+defaults['swift'] = ['swiftc']
+defaults['vala'] = ['valac']
+defaults['cython'] = ['cython']
+defaults['static_linker'] = ['ar', 'gar']
+defaults['strip'] = ['strip']
+defaults['vs_static_linker'] = ['lib']
+defaults['clang_cl_static_linker'] = ['llvm-lib']
+defaults['cuda_static_linker'] = ['nvlink']
+defaults['gcc_static_linker'] = ['gcc-ar']
+defaults['clang_static_linker'] = ['llvm-ar']
+
+
+def compiler_from_language(env: 'Environment', lang: str, for_machine: MachineChoice):
+ lang_map = {
+ 'c': detect_c_compiler,
+ 'cpp': detect_cpp_compiler,
+ 'objc': detect_objc_compiler,
+ 'cuda': detect_cuda_compiler,
+ 'objcpp': detect_objcpp_compiler,
+ 'java': detect_java_compiler,
+ 'cs': detect_cs_compiler,
+ 'vala': detect_vala_compiler,
+ 'd': detect_d_compiler,
+ 'rust': detect_rust_compiler,
+ 'fortran': detect_fortran_compiler,
+ 'swift': detect_swift_compiler,
+ 'cython': detect_cython_compiler,
+ }
+ return lang_map[lang](env, for_machine) if lang in lang_map else None
+
+def detect_compiler_for(env: 'Environment', lang: str, for_machine: MachineChoice):
+ comp = compiler_from_language(env, lang, for_machine)
+ if comp is not None:
+ assert comp.for_machine == for_machine
+ env.coredata.process_new_compiler(lang, comp, env)
+ return comp
+
+
+# Helpers
+# =======
+
+def _get_compilers(env: 'Environment', lang: str, for_machine: MachineChoice) -> T.Tuple[T.List[str], T.List[str], T.Optional['ExternalProgram']]:
+ '''
+ The list of compilers is detected in the exact same way for
+ C, C++, ObjC, ObjC++, Fortran, CS so consolidate it here.
+ '''
+ value = env.lookup_binary_entry(for_machine, lang)
+ if value is not None:
+ compilers, ccache = BinaryTable.parse_entry(value)
+ # Return value has to be a list of compiler 'choices'
+ compilers = [compilers]
+ else:
+ if not env.machines.matches_build_machine(for_machine):
+ raise EnvironmentException(f'{lang!r} compiler binary not defined in cross or native file')
+ compilers = defaults[lang]
+ ccache = BinaryTable.detect_ccache()
+
+ if env.machines.matches_build_machine(for_machine):
+ exe_wrap = None
+ else:
+ exe_wrap = env.get_exe_wrapper()
+
+ return compilers, ccache, exe_wrap
+
+def _handle_exceptions(exceptions, binaries, bintype='compiler'):
+ errmsg = f'Unknown {bintype}(s): {binaries}'
+ if exceptions:
+ errmsg += '\nThe following exception(s) were encountered:'
+ for c, e in exceptions.items():
+ errmsg += f'\nRunning "{c}" gave "{e}"'
+ raise EnvironmentException(errmsg)
+
+
+# Linker specific
+# ===============
+
+def detect_static_linker(env: 'Environment', compiler):
+ linker = env.lookup_binary_entry(compiler.for_machine, 'ar')
+ if linker is not None:
+ linkers = [linker]
+ else:
+ default_linkers = [[l] for l in defaults['static_linker']]
+ if isinstance(compiler, CudaCompiler):
+ linkers = [defaults['cuda_static_linker']] + default_linkers
+ elif isinstance(compiler, VisualStudioLikeCompiler):
+ linkers = [defaults['vs_static_linker'], defaults['clang_cl_static_linker']]
+ elif isinstance(compiler, GnuCompiler):
+ # Use gcc-ar if available; needed for LTO
+ linkers = [defaults['gcc_static_linker']] + default_linkers
+ elif isinstance(compiler, ClangCompiler):
+ # Use llvm-ar if available; needed for LTO
+ linkers = [defaults['clang_static_linker']] + default_linkers
+ elif isinstance(compiler, DCompiler):
+ # Prefer static linkers over linkers used by D compilers
+ if is_windows():
+ linkers = [defaults['vs_static_linker'], defaults['clang_cl_static_linker'], compiler.get_linker_exelist()]
+ else:
+ linkers = default_linkers
+ elif isinstance(compiler, IntelClCCompiler):
+ # Intel has it's own linker that acts like microsoft's lib
+ linkers = ['xilib']
+ elif isinstance(compiler, (PGICCompiler, PGIFortranCompiler)) and is_windows():
+ linkers = [['ar']] # For PGI on Windows, "ar" is just a wrapper calling link/lib.
+ else:
+ linkers = default_linkers
+ popen_exceptions = {}
+ for linker in linkers:
+ if not {'lib', 'lib.exe', 'llvm-lib', 'llvm-lib.exe', 'xilib', 'xilib.exe'}.isdisjoint(linker):
+ arg = '/?'
+ elif not {'ar2000', 'ar2000.exe'}.isdisjoint(linker):
+ arg = '?'
+ else:
+ arg = '--version'
+ try:
+ p, out, err = Popen_safe(linker + [arg])
+ except OSError as e:
+ popen_exceptions[' '.join(linker + [arg])] = e
+ continue
+ if "xilib: executing 'lib'" in err:
+ return IntelVisualStudioLinker(linker, getattr(compiler, 'machine', None))
+ if '/OUT:' in out.upper() or '/OUT:' in err.upper():
+ return VisualStudioLinker(linker, getattr(compiler, 'machine', None))
+ if 'ar-Error-Unknown switch: --version' in err:
+ return PGIStaticLinker(linker)
+ if p.returncode == 0 and ('armar' in linker or 'armar.exe' in linker):
+ return ArmarLinker(linker)
+ if 'DMD32 D Compiler' in out or 'DMD64 D Compiler' in out:
+ return DLinker(linker, compiler.arch)
+ if 'LDC - the LLVM D compiler' in out:
+ return DLinker(linker, compiler.arch, rsp_syntax=compiler.rsp_file_syntax())
+ if 'GDC' in out and ' based on D ' in out:
+ return DLinker(linker, compiler.arch)
+ if err.startswith('Renesas') and ('rlink' in linker or 'rlink.exe' in linker):
+ return CcrxLinker(linker)
+ if out.startswith('GNU ar') and ('xc16-ar' in linker or 'xc16-ar.exe' in linker):
+ return Xc16Linker(linker)
+ if out.startswith('TMS320C2000') and ('ar2000' in linker or 'ar2000.exe' in linker):
+ return C2000Linker(linker)
+ if out.startswith('The CompCert'):
+ return CompCertLinker(linker)
+ if p.returncode == 0:
+ return ArLinker(linker)
+ if p.returncode == 1 and err.startswith('usage'): # OSX
+ return ArLinker(linker)
+ if p.returncode == 1 and err.startswith('Usage'): # AIX
+ return AIXArLinker(linker)
+ if p.returncode == 1 and err.startswith('ar: bad option: --'): # Solaris
+ return ArLinker(linker)
+ _handle_exceptions(popen_exceptions, linkers, 'linker')
+ raise EnvironmentException('Unknown static linker "{}"'.format(' '.join(linkers)))
+
+
+
+# Compilers
+# =========
+
+
+def _detect_c_or_cpp_compiler(env: 'Environment', lang: str, for_machine: MachineChoice, *, override_compiler: T.Optional[T.List[str]] = None) -> Compiler:
+ """Shared implementation for finding the C or C++ compiler to use.
+
+ the override_compiler option is provided to allow compilers which use
+ the compiler (GCC or Clang usually) as their shared linker, to find
+ the linker they need.
+ """
+ popen_exceptions = {}
+ compilers, ccache, exe_wrap = _get_compilers(env, lang, for_machine)
+ if override_compiler is not None:
+ compilers = [override_compiler]
+ is_cross = env.is_cross_build(for_machine)
+ info = env.machines[for_machine]
+
+ for compiler in compilers:
+ if isinstance(compiler, str):
+ compiler = [compiler]
+ compiler_name = os.path.basename(compiler[0])
+
+ if not {'cl', 'cl.exe', 'clang-cl', 'clang-cl.exe'}.isdisjoint(compiler):
+ # Watcom C provides it's own cl.exe clone that mimics an older
+ # version of Microsoft's compiler. Since Watcom's cl.exe is
+ # just a wrapper, we skip using it if we detect its presence
+ # so as not to confuse Meson when configuring for MSVC.
+ #
+ # Additionally the help text of Watcom's cl.exe is paged, and
+ # the binary will not exit without human intervention. In
+ # practice, Meson will block waiting for Watcom's cl.exe to
+ # exit, which requires user input and thus will never exit.
+ if 'WATCOM' in os.environ:
+ def sanitize(p):
+ return os.path.normcase(os.path.abspath(p))
+
+ watcom_cls = [sanitize(os.path.join(os.environ['WATCOM'], 'BINNT', 'cl')),
+ sanitize(os.path.join(os.environ['WATCOM'], 'BINNT', 'cl.exe'))]
+ found_cl = sanitize(shutil.which('cl'))
+ if found_cl in watcom_cls:
+ continue
+ arg = '/?'
+ elif 'armcc' in compiler_name:
+ arg = '--vsn'
+ elif 'ccrx' in compiler_name:
+ arg = '-v'
+ elif 'xc16' in compiler_name:
+ arg = '--version'
+ elif 'ccomp' in compiler_name:
+ arg = '-version'
+ elif 'cl2000' in compiler_name:
+ arg = '-version'
+ elif compiler_name in {'icl', 'icl.exe'}:
+ # if you pass anything to icl you get stuck in a pager
+ arg = ''
+ else:
+ arg = '--version'
+
+ try:
+ p, out, err = Popen_safe(compiler + [arg])
+ except OSError as e:
+ popen_exceptions[' '.join(compiler + [arg])] = e
+ continue
+
+ if 'ccrx' in compiler_name:
+ out = err
+
+ full_version = out.split('\n', 1)[0]
+ version = search_version(out)
+
+ guess_gcc_or_lcc = False
+ if 'Free Software Foundation' in out or 'xt-' in out:
+ guess_gcc_or_lcc = 'gcc'
+ if 'e2k' in out and 'lcc' in out:
+ guess_gcc_or_lcc = 'lcc'
+ if 'Microchip Technology' in out:
+ # this output has "Free Software Foundation" in its version
+ guess_gcc_or_lcc = False
+
+ if guess_gcc_or_lcc:
+ defines = _get_gnu_compiler_defines(compiler)
+ if not defines:
+ popen_exceptions[' '.join(compiler)] = 'no pre-processor defines'
+ continue
+
+ if guess_gcc_or_lcc == 'lcc':
+ version = _get_lcc_version_from_defines(defines)
+ cls = ElbrusCCompiler if lang == 'c' else ElbrusCPPCompiler
+ else:
+ version = _get_gnu_version_from_defines(defines)
+ cls = GnuCCompiler if lang == 'c' else GnuCPPCompiler
+
+ linker = guess_nix_linker(env, compiler, cls, for_machine)
+
+ return cls(
+ ccache + compiler, version, for_machine, is_cross,
+ info, exe_wrap, defines=defines, full_version=full_version,
+ linker=linker)
+
+ if 'Emscripten' in out:
+ cls = EmscriptenCCompiler if lang == 'c' else EmscriptenCPPCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+
+ # emcc requires a file input in order to pass arguments to the
+ # linker. It'll exit with an error code, but still print the
+ # linker version. Old emcc versions ignore -Wl,--version completely,
+ # however. We'll report "unknown version" in that case.
+ with tempfile.NamedTemporaryFile(suffix='.c') as f:
+ cmd = compiler + [cls.LINKER_PREFIX + "--version", f.name]
+ _, o, _ = Popen_safe(cmd)
+
+ linker = WASMDynamicLinker(
+ compiler, for_machine, cls.LINKER_PREFIX,
+ [], version=search_version(o))
+ return cls(
+ ccache + compiler, version, for_machine, is_cross, info,
+ exe_wrap, linker=linker, full_version=full_version)
+
+ if 'armclang' in out:
+ # The compiler version is not present in the first line of output,
+ # instead it is present in second line, startswith 'Component:'.
+ # So, searching for the 'Component' in out although we know it is
+ # present in second line, as we are not sure about the
+ # output format in future versions
+ arm_ver_str = re.search('.*Component.*', out)
+ if arm_ver_str is None:
+ popen_exceptions[' '.join(compiler)] = 'version string not found'
+ continue
+ arm_ver_str = arm_ver_str.group(0)
+ # Override previous values
+ version = search_version(arm_ver_str)
+ full_version = arm_ver_str
+ cls = ArmclangCCompiler if lang == 'c' else ArmclangCPPCompiler
+ linker = ArmClangDynamicLinker(for_machine, version=version)
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ return cls(
+ ccache + compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+ if 'CL.EXE COMPATIBILITY' in out:
+ # if this is clang-cl masquerading as cl, detect it as cl, not
+ # clang
+ arg = '--version'
+ try:
+ p, out, err = Popen_safe(compiler + [arg])
+ except OSError as e:
+ popen_exceptions[' '.join(compiler + [arg])] = e
+ version = search_version(out)
+ match = re.search('^Target: (.*?)-', out, re.MULTILINE)
+ if match:
+ target = match.group(1)
+ else:
+ target = 'unknown target'
+ cls = ClangClCCompiler if lang == 'c' else ClangClCPPCompiler
+ linker = guess_win_linker(env, ['lld-link'], cls, for_machine)
+ return cls(
+ compiler, version, for_machine, is_cross, info, target,
+ exe_wrap, linker=linker)
+ if 'clang' in out or 'Clang' in out:
+ linker = None
+
+ defines = _get_clang_compiler_defines(compiler)
+
+ # Even if the for_machine is darwin, we could be using vanilla
+ # clang.
+ if 'Apple' in out:
+ cls = AppleClangCCompiler if lang == 'c' else AppleClangCPPCompiler
+ else:
+ cls = ClangCCompiler if lang == 'c' else ClangCPPCompiler
+
+ if 'windows' in out or env.machines[for_machine].is_windows():
+ # If we're in a MINGW context this actually will use a gnu
+ # style ld, but for clang on "real" windows we'll use
+ # either link.exe or lld-link.exe
+ try:
+ linker = guess_win_linker(env, compiler, cls, for_machine, invoked_directly=False)
+ except MesonException:
+ pass
+ if linker is None:
+ linker = guess_nix_linker(env, compiler, cls, for_machine)
+
+ return cls(
+ ccache + compiler, version, for_machine, is_cross, info,
+ exe_wrap, defines=defines, full_version=full_version, linker=linker)
+
+ if 'Intel(R) C++ Intel(R)' in err:
+ version = search_version(err)
+ target = 'x86' if 'IA-32' in err else 'x86_64'
+ cls = IntelClCCompiler if lang == 'c' else IntelClCPPCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = XilinkDynamicLinker(for_machine, [], version=version)
+ return cls(
+ compiler, version, for_machine, is_cross, info, target,
+ exe_wrap, linker=linker)
+ if 'Microsoft' in out or 'Microsoft' in err:
+ # Latest versions of Visual Studio print version
+ # number to stderr but earlier ones print version
+ # on stdout. Why? Lord only knows.
+ # Check both outputs to figure out version.
+ for lookat in [err, out]:
+ version = search_version(lookat)
+ if version != 'unknown version':
+ break
+ else:
+ m = 'Failed to detect MSVC compiler version: stderr was\n{!r}'
+ raise EnvironmentException(m.format(err))
+ cl_signature = lookat.split('\n')[0]
+ match = re.search(r'.*(x86|x64|ARM|ARM64)([^_A-Za-z0-9]|$)', cl_signature)
+ if match:
+ target = match.group(1)
+ else:
+ m = 'Failed to detect MSVC compiler target architecture: \'cl /?\' output is\n{}'
+ raise EnvironmentException(m.format(cl_signature))
+ cls = VisualStudioCCompiler if lang == 'c' else VisualStudioCPPCompiler
+ linker = guess_win_linker(env, ['link'], cls, for_machine)
+ return cls(
+ compiler, version, for_machine, is_cross, info, target,
+ exe_wrap, full_version=cl_signature, linker=linker)
+ if 'PGI Compilers' in out:
+ cls = PGICCompiler if lang == 'c' else PGICPPCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = PGIDynamicLinker(compiler, for_machine, cls.LINKER_PREFIX, [], version=version)
+ return cls(
+ ccache + compiler, version, for_machine, is_cross,
+ info, exe_wrap, linker=linker)
+ if 'NVIDIA Compilers and Tools' in out:
+ cls = NvidiaHPC_CCompiler if lang == 'c' else NvidiaHPC_CPPCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = NvidiaHPC_DynamicLinker(compiler, for_machine, cls.LINKER_PREFIX, [], version=version)
+ return cls(
+ ccache + compiler, version, for_machine, is_cross,
+ info, exe_wrap, linker=linker)
+ if '(ICC)' in out:
+ cls = IntelCCompiler if lang == 'c' else IntelCPPCompiler
+ l = guess_nix_linker(env, compiler, cls, for_machine)
+ return cls(
+ ccache + compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=l)
+ if 'ARM' in out:
+ cls = ArmCCompiler if lang == 'c' else ArmCPPCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = ArmDynamicLinker(for_machine, version=version)
+ return cls(
+ ccache + compiler, version, for_machine, is_cross,
+ info, exe_wrap, full_version=full_version, linker=linker)
+ if 'RX Family' in out:
+ cls = CcrxCCompiler if lang == 'c' else CcrxCPPCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = CcrxDynamicLinker(for_machine, version=version)
+ return cls(
+ ccache + compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+
+ if 'Microchip Technology' in out:
+ cls = Xc16CCompiler if lang == 'c' else Xc16CCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = Xc16DynamicLinker(for_machine, version=version)
+ return cls(
+ ccache + compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+
+ if 'CompCert' in out:
+ cls = CompCertCCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = CompCertDynamicLinker(for_machine, version=version)
+ return cls(
+ ccache + compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+
+ if 'TMS320C2000 C/C++' in out:
+ cls = C2000CCompiler if lang == 'c' else C2000CPPCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = C2000DynamicLinker(for_machine, version=version)
+ return cls(
+ ccache + compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+
+
+ _handle_exceptions(popen_exceptions, compilers)
+
+def detect_c_compiler(env: 'Environment', for_machine: MachineChoice):
+ return _detect_c_or_cpp_compiler(env, 'c', for_machine)
+
+def detect_cpp_compiler(env: 'Environment', for_machine: MachineChoice):
+ return _detect_c_or_cpp_compiler(env, 'cpp', for_machine)
+
+def detect_cuda_compiler(env: 'Environment', for_machine: MachineChoice):
+ popen_exceptions = {}
+ is_cross = env.is_cross_build(for_machine)
+ compilers, ccache, exe_wrap = _get_compilers(env, 'cuda', for_machine)
+ info = env.machines[for_machine]
+ for compiler in compilers:
+ if isinstance(compiler, str):
+ compiler = [compiler]
+ arg = '--version'
+ try:
+ p, out, err = Popen_safe(compiler + [arg])
+ except OSError as e:
+ popen_exceptions[' '.join(compiler + [arg])] = e
+ continue
+ # Example nvcc printout:
+ #
+ # nvcc: NVIDIA (R) Cuda compiler driver
+ # Copyright (c) 2005-2018 NVIDIA Corporation
+ # Built on Sat_Aug_25_21:08:01_CDT_2018
+ # Cuda compilation tools, release 10.0, V10.0.130
+ #
+ # search_version() first finds the "10.0" after "release",
+ # rather than the more precise "10.0.130" after "V".
+ # The patch version number is occasionally important; For
+ # instance, on Linux,
+ # - CUDA Toolkit 8.0.44 requires NVIDIA Driver 367.48
+ # - CUDA Toolkit 8.0.61 requires NVIDIA Driver 375.26
+ # Luckily, the "V" also makes it very simple to extract
+ # the full version:
+ version = out.strip().split('V')[-1]
+ cpp_compiler = detect_cpp_compiler(env, for_machine)
+ cls = CudaCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = CudaLinker(compiler, for_machine, CudaCompiler.LINKER_PREFIX, [], version=CudaLinker.parse_version())
+ return cls(ccache + compiler, version, for_machine, is_cross, exe_wrap, host_compiler=cpp_compiler, info=info, linker=linker)
+ raise EnvironmentException('Could not find suitable CUDA compiler: "' + ' '.join(compilers) + '"')
+
+def detect_fortran_compiler(env: 'Environment', for_machine: MachineChoice):
+ popen_exceptions = {}
+ compilers, ccache, exe_wrap = _get_compilers(env, 'fortran', for_machine)
+ is_cross = env.is_cross_build(for_machine)
+ info = env.machines[for_machine]
+ for compiler in compilers:
+ if isinstance(compiler, str):
+ compiler = [compiler]
+ for arg in ['--version', '-V']:
+ try:
+ p, out, err = Popen_safe(compiler + [arg])
+ except OSError as e:
+ popen_exceptions[' '.join(compiler + [arg])] = e
+ continue
+
+ version = search_version(out)
+ full_version = out.split('\n', 1)[0]
+
+ guess_gcc_or_lcc = False
+ if 'GNU Fortran' in out:
+ guess_gcc_or_lcc = 'gcc'
+ if 'e2k' in out and 'lcc' in out:
+ guess_gcc_or_lcc = 'lcc'
+
+ if guess_gcc_or_lcc:
+ defines = _get_gnu_compiler_defines(compiler)
+ if not defines:
+ popen_exceptions[' '.join(compiler)] = 'no pre-processor defines'
+ continue
+ if guess_gcc_or_lcc == 'lcc':
+ version = _get_lcc_version_from_defines(defines)
+ cls = ElbrusFortranCompiler
+ else:
+ version = _get_gnu_version_from_defines(defines)
+ cls = GnuFortranCompiler
+ linker = guess_nix_linker(env, compiler, cls, for_machine)
+ return cls(
+ compiler, version, for_machine, is_cross, info,
+ exe_wrap, defines, full_version=full_version,
+ linker=linker)
+
+ if 'G95' in out:
+ cls = G95FortranCompiler
+ linker = guess_nix_linker(env, compiler, cls, for_machine)
+ return G95FortranCompiler(
+ compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+
+ if 'Sun Fortran' in err:
+ version = search_version(err)
+ cls = SunFortranCompiler
+ linker = guess_nix_linker(env, compiler, cls, for_machine)
+ return SunFortranCompiler(
+ compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+
+ if 'Intel(R) Visual Fortran' in err:
+ version = search_version(err)
+ target = 'x86' if 'IA-32' in err else 'x86_64'
+ cls = IntelClFortranCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = XilinkDynamicLinker(for_machine, [], version=version)
+ return cls(
+ compiler, version, for_machine, is_cross, info,
+ target, exe_wrap, linker=linker)
+
+ if 'ifort (IFORT)' in out:
+ linker = guess_nix_linker(env, compiler, IntelFortranCompiler, for_machine)
+ return IntelFortranCompiler(
+ compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+
+ if 'PathScale EKOPath(tm)' in err:
+ return PathScaleFortranCompiler(
+ compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version)
+
+ if 'PGI Compilers' in out:
+ cls = PGIFortranCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = PGIDynamicLinker(compiler, for_machine,
+ cls.LINKER_PREFIX, [], version=version)
+ return cls(
+ compiler, version, for_machine, is_cross, info, exe_wrap,
+ full_version=full_version, linker=linker)
+
+ if 'NVIDIA Compilers and Tools' in out:
+ cls = NvidiaHPC_FortranCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = PGIDynamicLinker(compiler, for_machine,
+ cls.LINKER_PREFIX, [], version=version)
+ return cls(
+ compiler, version, for_machine, is_cross, info, exe_wrap,
+ full_version=full_version, linker=linker)
+
+ if 'flang' in out or 'clang' in out:
+ linker = guess_nix_linker(env,
+ compiler, FlangFortranCompiler, for_machine)
+ return FlangFortranCompiler(
+ compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+
+ if 'Open64 Compiler Suite' in err:
+ linker = guess_nix_linker(env,
+ compiler, Open64FortranCompiler, for_machine)
+ return Open64FortranCompiler(
+ compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+
+ if 'NAG Fortran' in err:
+ linker = guess_nix_linker(env,
+ compiler, NAGFortranCompiler, for_machine)
+ return NAGFortranCompiler(
+ compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+
+ _handle_exceptions(popen_exceptions, compilers)
+
+def detect_objc_compiler(env: 'Environment', for_machine: MachineChoice) -> 'Compiler':
+ return _detect_objc_or_objcpp_compiler(env, for_machine, True)
+
+def detect_objcpp_compiler(env: 'Environment', for_machine: MachineChoice) -> 'Compiler':
+ return _detect_objc_or_objcpp_compiler(env, for_machine, False)
+
+def _detect_objc_or_objcpp_compiler(env: 'Environment', for_machine: MachineChoice, objc: bool) -> 'Compiler':
+ popen_exceptions = {}
+ compilers, ccache, exe_wrap = _get_compilers(env, 'objc' if objc else 'objcpp', for_machine)
+ is_cross = env.is_cross_build(for_machine)
+ info = env.machines[for_machine]
+
+ for compiler in compilers:
+ if isinstance(compiler, str):
+ compiler = [compiler]
+ arg = ['--version']
+ try:
+ p, out, err = Popen_safe(compiler + arg)
+ except OSError as e:
+ popen_exceptions[' '.join(compiler + arg)] = e
+ continue
+ version = search_version(out)
+ if 'Free Software Foundation' in out:
+ defines = _get_gnu_compiler_defines(compiler)
+ if not defines:
+ popen_exceptions[' '.join(compiler)] = 'no pre-processor defines'
+ continue
+ version = _get_gnu_version_from_defines(defines)
+ comp = GnuObjCCompiler if objc else GnuObjCPPCompiler
+ linker = guess_nix_linker(env, compiler, comp, for_machine)
+ return comp(
+ ccache + compiler, version, for_machine, is_cross, info,
+ exe_wrap, defines, linker=linker)
+ if 'clang' in out:
+ linker = None
+ defines = _get_clang_compiler_defines(compiler)
+ if not defines:
+ popen_exceptions[' '.join(compiler)] = 'no pre-processor defines'
+ continue
+ if 'Apple' in out:
+ comp = AppleClangObjCCompiler if objc else AppleClangObjCPPCompiler
+ else:
+ comp = ClangObjCCompiler if objc else ClangObjCPPCompiler
+ if 'windows' in out or env.machines[for_machine].is_windows():
+ # If we're in a MINGW context this actually will use a gnu style ld
+ try:
+ linker = guess_win_linker(env, compiler, comp, for_machine)
+ except MesonException:
+ pass
+
+ if not linker:
+ linker = guess_nix_linker(env, compiler, comp, for_machine)
+ return comp(
+ ccache + compiler, version, for_machine,
+ is_cross, info, exe_wrap, linker=linker, defines=defines)
+ _handle_exceptions(popen_exceptions, compilers)
+
+def detect_java_compiler(env: 'Environment', for_machine):
+ exelist = env.lookup_binary_entry(for_machine, 'java')
+ info = env.machines[for_machine]
+ if exelist is None:
+ # TODO support fallback
+ exelist = [defaults['java'][0]]
+
+ try:
+ p, out, err = Popen_safe(exelist + ['-version'])
+ except OSError:
+ raise EnvironmentException('Could not execute Java compiler "{}"'.format(' '.join(exelist)))
+ if 'javac' in out or 'javac' in err:
+ version = search_version(err if 'javac' in err else out)
+ if not version or version == 'unknown version':
+ parts = (err if 'javac' in err else out).split()
+ if len(parts) > 1:
+ version = parts[1]
+ comp_class = JavaCompiler
+ env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)
+ return comp_class(exelist, version, for_machine, info)
+ raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
+
+def detect_cs_compiler(env: 'Environment', for_machine):
+ compilers, ccache, exe_wrap = _get_compilers(env, 'cs', for_machine)
+ popen_exceptions = {}
+ info = env.machines[for_machine]
+ for comp in compilers:
+ if not isinstance(comp, list):
+ comp = [comp]
+ try:
+ p, out, err = Popen_safe(comp + ['--version'])
+ except OSError as e:
+ popen_exceptions[' '.join(comp + ['--version'])] = e
+ continue
+
+ version = search_version(out)
+ if 'Mono' in out:
+ cls = MonoCompiler
+ elif "Visual C#" in out:
+ cls = VisualStudioCsCompiler
+ else:
+ continue
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ return cls(comp, version, for_machine, info)
+
+ _handle_exceptions(popen_exceptions, compilers)
+
+def detect_cython_compiler(env: 'Environment', for_machine: MachineChoice) -> CythonCompiler:
+ """Search for a cython compiler."""
+ compilers, _, _ = _get_compilers(env, 'cython', for_machine)
+ is_cross = env.is_cross_build(for_machine)
+ info = env.machines[for_machine]
+
+ popen_exceptions: T.Dict[str, Exception] = {}
+ for comp in compilers:
+ if isinstance(comp, str):
+ comp = [comp]
+ try:
+ err = Popen_safe(comp + ['-V'])[2]
+ except OSError as e:
+ popen_exceptions[' '.join(comp + ['-V'])] = e
+ continue
+
+ version = search_version(err)
+ if 'Cython' in err:
+ comp_class = CythonCompiler
+ env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)
+ return comp_class(comp, version, for_machine, info, is_cross=is_cross)
+ _handle_exceptions(popen_exceptions, compilers)
+
+def detect_vala_compiler(env: 'Environment', for_machine):
+ exelist = env.lookup_binary_entry(for_machine, 'vala')
+ is_cross = env.is_cross_build(for_machine)
+ info = env.machines[for_machine]
+ if exelist is None:
+ # TODO support fallback
+ exelist = [defaults['vala'][0]]
+
+ try:
+ p, out = Popen_safe(exelist + ['--version'])[0:2]
+ except OSError:
+ raise EnvironmentException('Could not execute Vala compiler "{}"'.format(' '.join(exelist)))
+ version = search_version(out)
+ if 'Vala' in out:
+ comp_class = ValaCompiler
+ env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)
+ return comp_class(exelist, version, for_machine, info, is_cross)
+ raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
+
+def detect_rust_compiler(env: 'Environment', for_machine: MachineChoice) -> RustCompiler:
+ popen_exceptions = {} # type: T.Dict[str, Exception]
+ compilers, _, exe_wrap = _get_compilers(env, 'rust', for_machine)
+ is_cross = env.is_cross_build(for_machine)
+ info = env.machines[for_machine]
+
+ cc = detect_c_compiler(env, for_machine)
+ is_link_exe = isinstance(cc.linker, VisualStudioLikeLinkerMixin)
+ override = env.lookup_binary_entry(for_machine, 'rust_ld')
+
+ for compiler in compilers:
+ if isinstance(compiler, str):
+ compiler = [compiler]
+ arg = ['--version']
+ try:
+ out = Popen_safe(compiler + arg)[1]
+ except OSError as e:
+ popen_exceptions[' '.join(compiler + arg)] = e
+ continue
+
+ version = search_version(out)
+
+ if 'rustc' in out:
+ # On Linux and mac rustc will invoke gcc (clang for mac
+ # presumably) and it can do this windows, for dynamic linking.
+ # this means the easiest way to C compiler for dynamic linking.
+ # figure out what linker to use is to just get the value of the
+ # C compiler and use that as the basis of the rust linker.
+ # However, there are two things we need to change, if CC is not
+ # the default use that, and second add the necessary arguments
+ # to rust to use -fuse-ld
+
+ if any(a.startswith('linker=') for a in compiler):
+ mlog.warning(
+ 'Please do not put -C linker= in your compiler '
+ 'command, set rust_ld=command in your cross file '
+ 'or use the RUST_LD environment variable, otherwise meson '
+ 'will override your selection.')
+
+ if override is None:
+ extra_args = {}
+ always_args = []
+ if is_link_exe:
+ compiler.extend(RustCompiler.use_linker_args(cc.linker.exelist[0]))
+ extra_args['direct'] = True
+ extra_args['machine'] = cc.linker.machine
+ else:
+ exelist = cc.linker.exelist.copy()
+ if 'ccache' in exelist[0]:
+ del exelist[0]
+ c = exelist.pop(0)
+ compiler.extend(RustCompiler.use_linker_args(c))
+
+ # Also ensure that we pass any extra arguments to the linker
+ for l in exelist:
+ compiler.extend(['-C', f'link-arg={l}'])
+
+ # This trickery with type() gets us the class of the linker
+ # so we can initialize a new copy for the Rust Compiler
+ if is_link_exe:
+ linker = type(cc.linker)(for_machine, always_args, exelist=cc.linker.exelist,
+ version=cc.linker.version, **extra_args)
+ else:
+ linker = type(cc.linker)(compiler, for_machine, cc.LINKER_PREFIX,
+ always_args=always_args, version=cc.linker.version,
+ **extra_args)
+ elif 'link' in override[0]:
+ linker = guess_win_linker(env,
+ override, RustCompiler, for_machine, use_linker_prefix=False)
+ # rustc takes linker arguments without a prefix, and
+ # inserts the correct prefix itself.
+ linker.direct = True
+ compiler.extend(RustCompiler.use_linker_args(linker.exelist[0]))
+ else:
+ # On linux and macos rust will invoke the c compiler for
+ # linking, on windows it will use lld-link or link.exe.
+ # we will simply ask for the C compiler that corresponds to
+ # it, and use that.
+ cc = _detect_c_or_cpp_compiler(env, 'c', for_machine, override_compiler=override)
+ linker = cc.linker
+
+ # Of course, we're not going to use any of that, we just
+ # need it to get the proper arguments to pass to rustc
+ c = linker.exelist[1] if linker.exelist[0].endswith('ccache') else linker.exelist[0]
+ compiler.extend(RustCompiler.use_linker_args(c))
+
+ env.coredata.add_lang_args(RustCompiler.language, RustCompiler, for_machine, env)
+ return RustCompiler(
+ compiler, version, for_machine, is_cross, info, exe_wrap,
+ linker=linker)
+
+ _handle_exceptions(popen_exceptions, compilers)
+
+def detect_d_compiler(env: 'Environment', for_machine: MachineChoice):
+ info = env.machines[for_machine]
+
+ # Detect the target architecture, required for proper architecture handling on Windows.
+ # MSVC compiler is required for correct platform detection.
+ c_compiler = {'c': detect_c_compiler(env, for_machine)}
+ is_msvc = isinstance(c_compiler['c'], VisualStudioCCompiler)
+ if not is_msvc:
+ c_compiler = {}
+
+ # Import here to avoid circular imports
+ from ..environment import detect_cpu_family
+ arch = detect_cpu_family(c_compiler)
+ if is_msvc and arch == 'x86':
+ arch = 'x86_mscoff'
+
+ popen_exceptions = {}
+ is_cross = env.is_cross_build(for_machine)
+ compilers, ccache, exe_wrap = _get_compilers(env, 'd', for_machine)
+ for exelist in compilers:
+ # Search for a D compiler.
+ # We prefer LDC over GDC unless overridden with the DC
+ # environment variable because LDC has a much more
+ # up to date language version at time (2016).
+ if not isinstance(exelist, list):
+ exelist = [exelist]
+ if os.path.basename(exelist[-1]).startswith(('ldmd', 'gdmd')):
+ raise EnvironmentException(
+ 'Meson does not support {} as it is only a DMD frontend for another compiler.'
+ 'Please provide a valid value for DC or unset it so that Meson can resolve the compiler by itself.'.format(exelist[-1]))
+ try:
+ p, out = Popen_safe(exelist + ['--version'])[0:2]
+ except OSError as e:
+ popen_exceptions[' '.join(exelist + ['--version'])] = e
+ continue
+ version = search_version(out)
+ full_version = out.split('\n', 1)[0]
+
+ if 'LLVM D compiler' in out:
+ # LDC seems to require a file
+ # We cannot use NamedTemproraryFile on windows, its documented
+ # to not work for our uses. So, just use mkstemp and only have
+ # one path for simplicity.
+ o, f = tempfile.mkstemp('.d')
+ os.close(o)
+
+ try:
+ if info.is_windows() or info.is_cygwin():
+ objfile = os.path.basename(f)[:-1] + 'obj'
+ linker = guess_win_linker(env,
+ exelist,
+ LLVMDCompiler, for_machine,
+ use_linker_prefix=True, invoked_directly=False,
+ extra_args=[f])
+ else:
+ # LDC writes an object file to the current working directory.
+ # Clean it up.
+ objfile = os.path.basename(f)[:-1] + 'o'
+ linker = guess_nix_linker(env,
+ exelist, LLVMDCompiler, for_machine,
+ extra_args=[f])
+ finally:
+ windows_proof_rm(f)
+ windows_proof_rm(objfile)
+
+ return LLVMDCompiler(
+ exelist, version, for_machine, info, arch,
+ full_version=full_version, linker=linker, version_output=out)
+ elif 'gdc' in out:
+ linker = guess_nix_linker(env, exelist, GnuDCompiler, for_machine)
+ return GnuDCompiler(
+ exelist, version, for_machine, info, arch,
+ exe_wrapper=exe_wrap, is_cross=is_cross,
+ full_version=full_version, linker=linker)
+ elif 'The D Language Foundation' in out or 'Digital Mars' in out:
+ # DMD seems to require a file
+ # We cannot use NamedTemproraryFile on windows, its documented
+ # to not work for our uses. So, just use mkstemp and only have
+ # one path for simplicity.
+ o, f = tempfile.mkstemp('.d')
+ os.close(o)
+
+ # DMD as different detection logic for x86 and x86_64
+ arch_arg = '-m64' if arch == 'x86_64' else '-m32'
+
+ try:
+ if info.is_windows() or info.is_cygwin():
+ objfile = os.path.basename(f)[:-1] + 'obj'
+ linker = guess_win_linker(env,
+ exelist, DmdDCompiler, for_machine,
+ invoked_directly=False, extra_args=[f, arch_arg])
+ else:
+ objfile = os.path.basename(f)[:-1] + 'o'
+ linker = guess_nix_linker(env,
+ exelist, DmdDCompiler, for_machine,
+ extra_args=[f, arch_arg])
+ finally:
+ windows_proof_rm(f)
+ windows_proof_rm(objfile)
+
+ return DmdDCompiler(
+ exelist, version, for_machine, info, arch,
+ full_version=full_version, linker=linker)
+ raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
+
+ _handle_exceptions(popen_exceptions, compilers)
+
+def detect_swift_compiler(env: 'Environment', for_machine):
+ exelist = env.lookup_binary_entry(for_machine, 'swift')
+ is_cross = env.is_cross_build(for_machine)
+ info = env.machines[for_machine]
+ if exelist is None:
+ # TODO support fallback
+ exelist = [defaults['swift'][0]]
+
+ try:
+ p, _, err = Popen_safe(exelist + ['-v'])
+ except OSError:
+ raise EnvironmentException('Could not execute Swift compiler "{}"'.format(' '.join(exelist)))
+ version = search_version(err)
+ if 'Swift' in err:
+ # As for 5.0.1 swiftc *requires* a file to check the linker:
+ with tempfile.NamedTemporaryFile(suffix='.swift') as f:
+ linker = guess_nix_linker(env,
+ exelist, SwiftCompiler, for_machine,
+ extra_args=[f.name])
+ return SwiftCompiler(
+ exelist, version, for_machine, info, is_cross, linker=linker)
+
+ raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
+
+
+# GNU/Clang defines and version
+# =============================
+
+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, output, error = Popen_safe(args, write='', stdin=subprocess.PIPE)
+ if p.returncode != 0:
+ raise EnvironmentException('Unable to detect GNU compiler type:\n' + output + error)
+ # 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
+
+def _get_clang_compiler_defines(compiler):
+ """
+ Get the list of Clang pre-processor defines
+ """
+ args = compiler + ['-E', '-dM', '-']
+ p, output, error = Popen_safe(args, write='', stdin=subprocess.PIPE)
+ if p.returncode != 0:
+ raise EnvironmentException('Unable to get clang pre-processor defines:\n' + output + error)
+ 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
+
+def _get_gnu_version_from_defines(defines):
+ dot = '.'
+ major = defines.get('__GNUC__', '0')
+ minor = defines.get('__GNUC_MINOR__', '0')
+ patch = defines.get('__GNUC_PATCHLEVEL__', '0')
+ return dot.join((major, minor, patch))
+
+def _get_lcc_version_from_defines(defines):
+ dot = '.'
+ generation_and_major = defines.get('__LCC__', '100')
+ generation = generation_and_major[:1]
+ major = generation_and_major[1:]
+ minor = defines.get('__LCC_MINOR__', '0')
+ return dot.join((generation, major, minor))