| # 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, |
| NAGDynamicLinker, |
| NvidiaHPC_DynamicLinker, |
| PGIDynamicLinker, |
| PGIStaticLinker, |
| StaticLinker, |
| Xc16Linker, |
| Xc16DynamicLinker, |
| XilinkDynamicLinker, |
| CudaLinker, |
| IntelVisualStudioLinker, |
| VisualStudioLinker, |
| VisualStudioLikeLinkerMixin, |
| WASMDynamicLinker, |
| ) |
| from .compilers import Compiler |
| from .c import ( |
| CCompiler, |
| AppleClangCCompiler, |
| ArmCCompiler, |
| ArmclangCCompiler, |
| ClangCCompiler, |
| ClangClCCompiler, |
| GnuCCompiler, |
| ElbrusCCompiler, |
| EmscriptenCCompiler, |
| IntelCCompiler, |
| IntelClCCompiler, |
| NvidiaHPC_CCompiler, |
| PGICCompiler, |
| CcrxCCompiler, |
| Xc16CCompiler, |
| CompCertCCompiler, |
| C2000CCompiler, |
| VisualStudioCCompiler, |
| ) |
| from .cpp import ( |
| CPPCompiler, |
| 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 ( |
| FortranCompiler, |
| G95FortranCompiler, |
| GnuFortranCompiler, |
| ElbrusFortranCompiler, |
| FlangFortranCompiler, |
| IntelFortranCompiler, |
| IntelClFortranCompiler, |
| NAGFortranCompiler, |
| Open64FortranCompiler, |
| PathScaleFortranCompiler, |
| NvidiaHPC_FortranCompiler, |
| PGIFortranCompiler, |
| SunFortranCompiler, |
| ) |
| from .java import JavaCompiler |
| from .objc import ( |
| ObjCCompiler, |
| AppleClangObjCCompiler, |
| ClangObjCCompiler, |
| GnuObjCCompiler, |
| ) |
| from .objcpp import ( |
| ObjCPPCompiler, |
| AppleClangObjCPPCompiler, |
| ClangObjCPPCompiler, |
| GnuObjCPPCompiler, |
| ) |
| from .cython import CythonCompiler |
| from .rust import RustCompiler, ClippyRustCompiler |
| 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': |
| defaults['c'] = ['cc', 'gcc', 'lcc', 'clang'] |
| defaults['cpp'] = ['c++', 'g++', 'l++', 'clang++'] |
| defaults['objc'] = ['clang'] |
| defaults['objcpp'] = ['clang++'] |
| 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) -> T.Optional[Compiler]: |
| lang_map: T.Dict[str, T.Callable[['Environment', MachineChoice], Compiler]] = { |
| '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) -> T.Optional[Compiler]: |
| 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[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: |
| comp, ccache = BinaryTable.parse_entry(value) |
| # Return value has to be a list of compiler 'choices' |
| compilers = [comp] |
| 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 = [[x] for x in defaults[lang]] |
| ccache = BinaryTable.detect_compiler_cache() |
| |
| if env.machines.matches_build_machine(for_machine): |
| exe_wrap: T.Optional[ExternalProgram] = None |
| else: |
| exe_wrap = env.get_exe_wrapper() |
| |
| return compilers, ccache, exe_wrap |
| |
| def _handle_exceptions( |
| exceptions: T.Mapping[str, T.Union[Exception, str]], |
| binaries: T.List[T.List[str]], |
| bintype: str = 'compiler' |
| ) -> T.NoReturn: |
| 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: Compiler) -> StaticLinker: |
| 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: |
| assert isinstance(compiler, DCompiler) |
| return DLinker(linker, compiler.arch) |
| if 'LDC - the LLVM D compiler' in out: |
| assert isinstance(compiler, DCompiler) |
| return DLinker(linker, compiler.arch, rsp_syntax=compiler.rsp_file_syntax()) |
| if 'GDC' in out and ' based on D ' in out: |
| assert isinstance(compiler, DCompiler) |
| 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') |
| |
| |
| # 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: T.Dict[str, T.Union[Exception, str]] = {} |
| 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] |
| cls: T.Union[T.Type[CCompiler], T.Type[CPPCompiler]] |
| |
| for compiler in compilers: |
| if isinstance(compiler, str): |
| compiler = [compiler] |
| compiler_name = os.path.basename(compiler[0]) |
| |
| if any(os.path.basename(x) in {'cl', 'cl.exe', 'clang-cl', 'clang-cl.exe'} for x in 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: str) -> str: |
| 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')), |
| sanitize(os.path.join(os.environ['WATCOM'], 'BINNT64', 'cl')), |
| sanitize(os.path.join(os.environ['WATCOM'], 'BINNT64', 'cl.exe')),] |
| found_cl = sanitize(shutil.which('cl')) |
| if found_cl in watcom_cls: |
| mlog.debug('Skipping unsupported cl.exe clone at:', found_cl) |
| 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: T.Optional[str] = None |
| 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 = None |
| |
| 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_match = re.search('.*Component.*', out) |
| if arm_ver_match is None: |
| popen_exceptions[' '.join(compiler)] = 'version string not found' |
| continue |
| arm_ver_str = arm_ver_match.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: |
| raise EnvironmentException(f'Failed to detect MSVC compiler version: stderr was\n{err!r}') |
| 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 = f'Failed to detect MSVC compiler target architecture: \'cl /?\' output is\n{cl_signature}' |
| raise EnvironmentException(m) |
| cls = VisualStudioCCompiler if lang == 'c' else VisualStudioCPPCompiler |
| linker = guess_win_linker(env, ['link'], cls, for_machine) |
| # As of this writing, CCache does not support MSVC but sccache does. |
| if 'sccache' in ccache: |
| final_compiler = ccache + compiler |
| else: |
| final_compiler = compiler |
| return cls( |
| final_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(compiler, 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) |
| raise EnvironmentException(f'Unknown compiler {compilers}') |
| |
| def detect_c_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler: |
| return _detect_c_or_cpp_compiler(env, 'c', for_machine) |
| |
| def detect_cpp_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler: |
| return _detect_c_or_cpp_compiler(env, 'cpp', for_machine) |
| |
| def detect_cuda_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler: |
| 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: |
| 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(f'Could not find suitable CUDA compiler: "{"; ".join([" ".join(c) for c in compilers])}"') |
| |
| def detect_fortran_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler: |
| popen_exceptions: T.Dict[str, T.Union[Exception, str]] = {} |
| compilers, ccache, exe_wrap = _get_compilers(env, 'fortran', for_machine) |
| is_cross = env.is_cross_build(for_machine) |
| info = env.machines[for_machine] |
| cls: T.Type[FortranCompiler] |
| for compiler in compilers: |
| 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: T.Optional[str] = None |
| 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 |
| 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) |
| 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 or 'Intel(R) 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: |
| full_version = err.split('\n', 1)[0] |
| version = full_version.split()[-1] |
| cls = NAGFortranCompiler |
| env.coredata.add_lang_args(cls.language, cls, for_machine, env) |
| linker = NAGDynamicLinker( |
| 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) |
| |
| _handle_exceptions(popen_exceptions, compilers) |
| raise EnvironmentException('Unreachable code (exception to make mypy happy)') |
| |
| 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: T.Dict[str, T.Union[Exception, str]] = {} |
| 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] |
| comp: T.Union[T.Type[ObjCCompiler], T.Type[ObjCPPCompiler]] |
| |
| for compiler in compilers: |
| 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) |
| raise EnvironmentException('Unreachable code (exception to make mypy happy)') |
| |
| def detect_java_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler: |
| 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: MachineChoice) -> Compiler: |
| compilers, ccache, exe_wrap = _get_compilers(env, 'cs', for_machine) |
| popen_exceptions = {} |
| info = env.machines[for_machine] |
| for comp in compilers: |
| try: |
| p, out, err = Popen_safe(comp + ['--version']) |
| except OSError as e: |
| popen_exceptions[' '.join(comp + ['--version'])] = e |
| continue |
| |
| version = search_version(out) |
| cls: T.Union[T.Type[MonoCompiler], T.Type[VisualStudioCsCompiler]] |
| 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) |
| raise EnvironmentException('Unreachable code (exception to make mypy happy)') |
| |
| def detect_cython_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler: |
| """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: |
| 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) |
| raise EnvironmentException('Unreachable code (exception to make mypy happy)') |
| |
| def detect_vala_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler: |
| 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, is_cross, info) |
| 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: |
| arg = ['--version'] |
| try: |
| out = Popen_safe(compiler + arg)[1] |
| except OSError as e: |
| popen_exceptions[' '.join(compiler + arg)] = e |
| continue |
| |
| version = search_version(out) |
| cls: T.Type[RustCompiler] = RustCompiler |
| |
| # Clippy is a wrapper around rustc, but it doesn't have rustc in it's |
| # output. We can otherwise treat it as rustc. |
| if 'clippy' in out: |
| out = 'rustc' |
| cls = ClippyRustCompiler |
| |
| 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.') |
| |
| compiler = compiler.copy() # avoid mutating the original list |
| |
| if override is None: |
| extra_args: T.Dict[str, T.Union[str, bool]] = {} |
| always_args: T.List[str] = [] |
| if is_link_exe: |
| compiler.extend(cls.use_linker_args(cc.linker.exelist[0])) |
| extra_args['direct'] = True |
| extra_args['machine'] = cc.linker.machine |
| else: |
| exelist = cc.linker.exelist + cc.linker.get_always_args() |
| if 'ccache' in exelist[0]: |
| del exelist[0] |
| c = exelist.pop(0) |
| compiler.extend(cls.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 |
| # TODO rewrite this without type: ignore |
| if is_link_exe: |
| linker = type(cc.linker)(for_machine, always_args, exelist=cc.linker.exelist, # type: ignore |
| version=cc.linker.version, **extra_args) # type: ignore |
| else: |
| linker = type(cc.linker)(compiler, for_machine, cc.LINKER_PREFIX, |
| always_args=always_args, version=cc.linker.version, |
| **extra_args) # type: ignore |
| elif 'link' in override[0]: |
| linker = guess_win_linker(env, |
| override, cls, for_machine, use_linker_prefix=False) |
| # rustc takes linker arguments without a prefix, and |
| # inserts the correct prefix itself. |
| assert isinstance(linker, VisualStudioLikeLinkerMixin) |
| linker.direct = True |
| compiler.extend(cls.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(cls.use_linker_args(c)) |
| |
| env.coredata.add_lang_args(cls.language, cls, for_machine, env) |
| return cls( |
| compiler, version, for_machine, is_cross, info, exe_wrap, |
| linker=linker) |
| |
| _handle_exceptions(popen_exceptions, compilers) |
| raise EnvironmentException('Unreachable code (exception to make mypy happy)') |
| |
| def detect_d_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler: |
| 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 os.path.basename(exelist[-1]).startswith(('ldmd', 'gdmd')): |
| raise EnvironmentException( |
| f'Meson does not support {exelist[-1]} 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.') |
| 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) |
| raise EnvironmentException('Unreachable code (exception to make mypy happy)') |
| |
| def detect_swift_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler: |
| 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, is_cross, info, linker=linker) |
| |
| raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"') |
| |
| |
| # GNU/Clang defines and version |
| # ============================= |
| |
| def _get_gnu_compiler_defines(compiler: T.List[str]) -> T.Dict[str, str]: |
| """ |
| 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: T.Dict[str, str] = {} |
| 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[0]] = '' |
| if len(rest) == 2: |
| defines[rest[0]] = rest[1] |
| return defines |
| |
| def _get_clang_compiler_defines(compiler: T.List[str]) -> T.Dict[str, str]: |
| """ |
| 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: T.Dict[str, str] = {} |
| 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[0]] = '' |
| if len(rest) == 2: |
| defines[rest[0]] = rest[1] |
| return defines |
| |
| def _get_gnu_version_from_defines(defines: T.Dict[str, str]) -> str: |
| 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: T.Dict[str, str]) -> str: |
| 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)) |