1# Copyright 2012-2021 The Meson development team
2
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6
7#     http://www.apache.org/licenses/LICENSE-2.0
8
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15from ..mesonlib import (
16    MachineChoice, MesonException, EnvironmentException,
17    search_version, is_windows, Popen_safe, windows_proof_rm,
18)
19from ..envconfig import BinaryTable
20from .. import mlog
21
22from ..linkers import (
23    guess_win_linker,
24    guess_nix_linker,
25    AIXArLinker,
26    ArLinker,
27    ArmarLinker,
28    ArmClangDynamicLinker,
29    ArmDynamicLinker,
30    CcrxLinker,
31    CcrxDynamicLinker,
32    CompCertLinker,
33    CompCertDynamicLinker,
34    C2000Linker,
35    C2000DynamicLinker,
36    DLinker,
37    NAGDynamicLinker,
38    NvidiaHPC_DynamicLinker,
39    PGIDynamicLinker,
40    PGIStaticLinker,
41    StaticLinker,
42    Xc16Linker,
43    Xc16DynamicLinker,
44    XilinkDynamicLinker,
45    CudaLinker,
46    IntelVisualStudioLinker,
47    VisualStudioLinker,
48    VisualStudioLikeLinkerMixin,
49    WASMDynamicLinker,
50)
51from .compilers import Compiler
52from .c import (
53    CCompiler,
54    AppleClangCCompiler,
55    ArmCCompiler,
56    ArmclangCCompiler,
57    ClangCCompiler,
58    ClangClCCompiler,
59    GnuCCompiler,
60    ElbrusCCompiler,
61    EmscriptenCCompiler,
62    IntelCCompiler,
63    IntelClCCompiler,
64    NvidiaHPC_CCompiler,
65    PGICCompiler,
66    CcrxCCompiler,
67    Xc16CCompiler,
68    CompCertCCompiler,
69    C2000CCompiler,
70    VisualStudioCCompiler,
71)
72from .cpp import (
73    CPPCompiler,
74    AppleClangCPPCompiler,
75    ArmCPPCompiler,
76    ArmclangCPPCompiler,
77    ClangCPPCompiler,
78    ClangClCPPCompiler,
79    GnuCPPCompiler,
80    ElbrusCPPCompiler,
81    EmscriptenCPPCompiler,
82    IntelCPPCompiler,
83    IntelClCPPCompiler,
84    NvidiaHPC_CPPCompiler,
85    PGICPPCompiler,
86    CcrxCPPCompiler,
87    C2000CPPCompiler,
88    VisualStudioCPPCompiler,
89)
90from .cs import MonoCompiler, VisualStudioCsCompiler
91from .d import (
92    DCompiler,
93    DmdDCompiler,
94    GnuDCompiler,
95    LLVMDCompiler,
96)
97from .cuda import CudaCompiler
98from .fortran import (
99    FortranCompiler,
100    G95FortranCompiler,
101    GnuFortranCompiler,
102    ElbrusFortranCompiler,
103    FlangFortranCompiler,
104    IntelFortranCompiler,
105    IntelClFortranCompiler,
106    NAGFortranCompiler,
107    Open64FortranCompiler,
108    PathScaleFortranCompiler,
109    NvidiaHPC_FortranCompiler,
110    PGIFortranCompiler,
111    SunFortranCompiler,
112)
113from .java import JavaCompiler
114from .objc import (
115    ObjCCompiler,
116    AppleClangObjCCompiler,
117    ClangObjCCompiler,
118    GnuObjCCompiler,
119)
120from .objcpp import (
121    ObjCPPCompiler,
122    AppleClangObjCPPCompiler,
123    ClangObjCPPCompiler,
124    GnuObjCPPCompiler,
125)
126from .cython import CythonCompiler
127from .rust import RustCompiler, ClippyRustCompiler
128from .swift import SwiftCompiler
129from .vala import ValaCompiler
130from .mixins.visualstudio import VisualStudioLikeCompiler
131from .mixins.gnu import GnuCompiler
132from .mixins.clang import ClangCompiler
133
134import subprocess
135import platform
136import re
137import shutil
138import tempfile
139import os
140import typing as T
141
142if T.TYPE_CHECKING:
143    from ..environment import Environment
144    from ..programs import ExternalProgram
145
146
147# Default compilers and linkers
148# =============================
149
150defaults: T.Dict[str, T.List[str]] = {}
151
152# List of potential compilers.
153if is_windows():
154    # Intel C and C++ compiler is icl on Windows, but icc and icpc elsewhere.
155    # Search for icl before cl, since Intel "helpfully" provides a
156    # cl.exe that returns *exactly the same thing* that microsofts
157    # cl.exe does, and if icl is present, it's almost certainly what
158    # you want.
159    defaults['c'] = ['icl', 'cl', 'cc', 'gcc', 'clang', 'clang-cl', 'pgcc']
160    # There is currently no pgc++ for Windows, only for  Mac and Linux.
161    defaults['cpp'] = ['icl', 'cl', 'c++', 'g++', 'clang++', 'clang-cl']
162    defaults['fortran'] = ['ifort', 'gfortran', 'flang', 'pgfortran', 'g95']
163    # Clang and clang++ are valid, but currently unsupported.
164    defaults['objc'] = ['cc', 'gcc']
165    defaults['objcpp'] = ['c++', 'g++']
166    defaults['cs'] = ['csc', 'mcs']
167else:
168    if platform.machine().lower() == 'e2k':
169        defaults['c'] = ['cc', 'gcc', 'lcc', 'clang']
170        defaults['cpp'] = ['c++', 'g++', 'l++', 'clang++']
171        defaults['objc'] = ['clang']
172        defaults['objcpp'] = ['clang++']
173    else:
174        defaults['c'] = ['cc', 'gcc', 'clang', 'nvc', 'pgcc', 'icc']
175        defaults['cpp'] = ['c++', 'g++', 'clang++', 'nvc++', 'pgc++', 'icpc']
176        defaults['objc'] = ['cc', 'gcc', 'clang']
177        defaults['objcpp'] = ['c++', 'g++', 'clang++']
178    defaults['fortran'] = ['gfortran', 'flang', 'nvfortran', 'pgfortran', 'ifort', 'g95']
179    defaults['cs'] = ['mcs', 'csc']
180defaults['d'] = ['ldc2', 'ldc', 'gdc', 'dmd']
181defaults['java'] = ['javac']
182defaults['cuda'] = ['nvcc']
183defaults['rust'] = ['rustc']
184defaults['swift'] = ['swiftc']
185defaults['vala'] = ['valac']
186defaults['cython'] = ['cython']
187defaults['static_linker'] = ['ar', 'gar']
188defaults['strip'] = ['strip']
189defaults['vs_static_linker'] = ['lib']
190defaults['clang_cl_static_linker'] = ['llvm-lib']
191defaults['cuda_static_linker'] = ['nvlink']
192defaults['gcc_static_linker'] = ['gcc-ar']
193defaults['clang_static_linker'] = ['llvm-ar']
194
195
196def compiler_from_language(env: 'Environment', lang: str, for_machine: MachineChoice) -> T.Optional[Compiler]:
197    lang_map: T.Dict[str, T.Callable[['Environment', MachineChoice], Compiler]] = {
198        'c': detect_c_compiler,
199        'cpp': detect_cpp_compiler,
200        'objc': detect_objc_compiler,
201        'cuda': detect_cuda_compiler,
202        'objcpp': detect_objcpp_compiler,
203        'java': detect_java_compiler,
204        'cs': detect_cs_compiler,
205        'vala': detect_vala_compiler,
206        'd': detect_d_compiler,
207        'rust': detect_rust_compiler,
208        'fortran': detect_fortran_compiler,
209        'swift': detect_swift_compiler,
210        'cython': detect_cython_compiler,
211    }
212    return lang_map[lang](env, for_machine) if lang in lang_map else None
213
214def detect_compiler_for(env: 'Environment', lang: str, for_machine: MachineChoice) -> T.Optional[Compiler]:
215    comp = compiler_from_language(env, lang, for_machine)
216    if comp is not None:
217        assert comp.for_machine == for_machine
218        env.coredata.process_new_compiler(lang, comp, env)
219    return comp
220
221
222# Helpers
223# =======
224
225def _get_compilers(env: 'Environment', lang: str, for_machine: MachineChoice) -> T.Tuple[T.List[T.List[str]], T.List[str], T.Optional['ExternalProgram']]:
226    '''
227    The list of compilers is detected in the exact same way for
228    C, C++, ObjC, ObjC++, Fortran, CS so consolidate it here.
229    '''
230    value = env.lookup_binary_entry(for_machine, lang)
231    if value is not None:
232        comp, ccache = BinaryTable.parse_entry(value)
233        # Return value has to be a list of compiler 'choices'
234        compilers = [comp]
235    else:
236        if not env.machines.matches_build_machine(for_machine):
237            raise EnvironmentException(f'{lang!r} compiler binary not defined in cross or native file')
238        compilers = [[x] for x in defaults[lang]]
239        ccache = BinaryTable.detect_ccache()
240
241    if env.machines.matches_build_machine(for_machine):
242        exe_wrap: T.Optional[ExternalProgram] = None
243    else:
244        exe_wrap = env.get_exe_wrapper()
245
246    return compilers, ccache, exe_wrap
247
248def _handle_exceptions(
249        exceptions: T.Mapping[str, T.Union[Exception, str]],
250        binaries: T.List[T.List[str]],
251        bintype: str = 'compiler'
252    ) -> T.NoReturn:
253    errmsg = f'Unknown {bintype}(s): {binaries}'
254    if exceptions:
255        errmsg += '\nThe following exception(s) were encountered:'
256        for c, e in exceptions.items():
257            errmsg += f'\nRunning "{c}" gave "{e}"'
258    raise EnvironmentException(errmsg)
259
260
261# Linker specific
262# ===============
263
264def detect_static_linker(env: 'Environment', compiler: Compiler) -> StaticLinker:
265    linker = env.lookup_binary_entry(compiler.for_machine, 'ar')
266    if linker is not None:
267        linkers = [linker]
268    else:
269        default_linkers = [[l] for l in defaults['static_linker']]
270        if isinstance(compiler, CudaCompiler):
271            linkers = [defaults['cuda_static_linker']] + default_linkers
272        elif isinstance(compiler, VisualStudioLikeCompiler):
273            linkers = [defaults['vs_static_linker'], defaults['clang_cl_static_linker']]
274        elif isinstance(compiler, GnuCompiler):
275            # Use gcc-ar if available; needed for LTO
276            linkers = [defaults['gcc_static_linker']] + default_linkers
277        elif isinstance(compiler, ClangCompiler):
278            # Use llvm-ar if available; needed for LTO
279            linkers = [defaults['clang_static_linker']] + default_linkers
280        elif isinstance(compiler, DCompiler):
281            # Prefer static linkers over linkers used by D compilers
282            if is_windows():
283                linkers = [defaults['vs_static_linker'], defaults['clang_cl_static_linker'], compiler.get_linker_exelist()]
284            else:
285                linkers = default_linkers
286        elif isinstance(compiler, IntelClCCompiler):
287            # Intel has it's own linker that acts like microsoft's lib
288            linkers = [['xilib']]
289        elif isinstance(compiler, (PGICCompiler, PGIFortranCompiler)) and is_windows():
290            linkers = [['ar']]  # For PGI on Windows, "ar" is just a wrapper calling link/lib.
291        else:
292            linkers = default_linkers
293    popen_exceptions = {}
294    for linker in linkers:
295        if not {'lib', 'lib.exe', 'llvm-lib', 'llvm-lib.exe', 'xilib', 'xilib.exe'}.isdisjoint(linker):
296            arg = '/?'
297        elif not {'ar2000', 'ar2000.exe'}.isdisjoint(linker):
298            arg = '?'
299        else:
300            arg = '--version'
301        try:
302            p, out, err = Popen_safe(linker + [arg])
303        except OSError as e:
304            popen_exceptions[' '.join(linker + [arg])] = e
305            continue
306        if "xilib: executing 'lib'" in err:
307            return IntelVisualStudioLinker(linker, getattr(compiler, 'machine', None))
308        if '/OUT:' in out.upper() or '/OUT:' in err.upper():
309            return VisualStudioLinker(linker, getattr(compiler, 'machine', None))
310        if 'ar-Error-Unknown switch: --version' in err:
311            return PGIStaticLinker(linker)
312        if p.returncode == 0 and ('armar' in linker or 'armar.exe' in linker):
313            return ArmarLinker(linker)
314        if 'DMD32 D Compiler' in out or 'DMD64 D Compiler' in out:
315            assert isinstance(compiler, DCompiler)
316            return DLinker(linker, compiler.arch)
317        if 'LDC - the LLVM D compiler' in out:
318            assert isinstance(compiler, DCompiler)
319            return DLinker(linker, compiler.arch, rsp_syntax=compiler.rsp_file_syntax())
320        if 'GDC' in out and ' based on D ' in out:
321            assert isinstance(compiler, DCompiler)
322            return DLinker(linker, compiler.arch)
323        if err.startswith('Renesas') and ('rlink' in linker or 'rlink.exe' in linker):
324            return CcrxLinker(linker)
325        if out.startswith('GNU ar') and ('xc16-ar' in linker or 'xc16-ar.exe' in linker):
326            return Xc16Linker(linker)
327        if out.startswith('TMS320C2000') and ('ar2000' in linker or 'ar2000.exe' in linker):
328            return C2000Linker(linker)
329        if out.startswith('The CompCert'):
330            return CompCertLinker(linker)
331        if p.returncode == 0:
332            return ArLinker(linker)
333        if p.returncode == 1 and err.startswith('usage'): # OSX
334            return ArLinker(linker)
335        if p.returncode == 1 and err.startswith('Usage'): # AIX
336            return AIXArLinker(linker)
337        if p.returncode == 1 and err.startswith('ar: bad option: --'): # Solaris
338            return ArLinker(linker)
339    _handle_exceptions(popen_exceptions, linkers, 'linker')
340
341
342# Compilers
343# =========
344
345
346def _detect_c_or_cpp_compiler(env: 'Environment', lang: str, for_machine: MachineChoice, *, override_compiler: T.Optional[T.List[str]] = None) -> Compiler:
347    """Shared implementation for finding the C or C++ compiler to use.
348
349    the override_compiler option is provided to allow compilers which use
350    the compiler (GCC or Clang usually) as their shared linker, to find
351    the linker they need.
352    """
353    popen_exceptions: T.Dict[str, T.Union[Exception, str]] = {}
354    compilers, ccache, exe_wrap = _get_compilers(env, lang, for_machine)
355    if override_compiler is not None:
356        compilers = [override_compiler]
357    is_cross = env.is_cross_build(for_machine)
358    info = env.machines[for_machine]
359    cls: T.Union[T.Type[CCompiler], T.Type[CPPCompiler]]
360
361    for compiler in compilers:
362        if isinstance(compiler, str):
363            compiler = [compiler]
364        compiler_name = os.path.basename(compiler[0])
365
366        if any(os.path.basename(x) in {'cl', 'cl.exe', 'clang-cl', 'clang-cl.exe'} for x in compiler):
367            # Watcom C provides it's own cl.exe clone that mimics an older
368            # version of Microsoft's compiler. Since Watcom's cl.exe is
369            # just a wrapper, we skip using it if we detect its presence
370            # so as not to confuse Meson when configuring for MSVC.
371            #
372            # Additionally the help text of Watcom's cl.exe is paged, and
373            # the binary will not exit without human intervention. In
374            # practice, Meson will block waiting for Watcom's cl.exe to
375            # exit, which requires user input and thus will never exit.
376            if 'WATCOM' in os.environ:
377                def sanitize(p: str) -> str:
378                    return os.path.normcase(os.path.abspath(p))
379
380                watcom_cls = [sanitize(os.path.join(os.environ['WATCOM'], 'BINNT', 'cl')),
381                                sanitize(os.path.join(os.environ['WATCOM'], 'BINNT', 'cl.exe')),
382                                sanitize(os.path.join(os.environ['WATCOM'], 'BINNT64', 'cl')),
383                                sanitize(os.path.join(os.environ['WATCOM'], 'BINNT64', 'cl.exe')),]
384                found_cl = sanitize(shutil.which('cl'))
385                if found_cl in watcom_cls:
386                    mlog.debug('Skipping unsupported cl.exe clone at:', found_cl)
387                    continue
388            arg = '/?'
389        elif 'armcc' in compiler_name:
390            arg = '--vsn'
391        elif 'ccrx' in compiler_name:
392            arg = '-v'
393        elif 'xc16' in compiler_name:
394            arg = '--version'
395        elif 'ccomp' in compiler_name:
396            arg = '-version'
397        elif 'cl2000' in compiler_name:
398            arg = '-version'
399        elif compiler_name in {'icl', 'icl.exe'}:
400            # if you pass anything to icl you get stuck in a pager
401            arg = ''
402        else:
403            arg = '--version'
404
405        try:
406            p, out, err = Popen_safe(compiler + [arg])
407        except OSError as e:
408            popen_exceptions[' '.join(compiler + [arg])] = e
409            continue
410
411        if 'ccrx' in compiler_name:
412            out = err
413
414        full_version = out.split('\n', 1)[0]
415        version = search_version(out)
416
417        guess_gcc_or_lcc: T.Optional[str] = None
418        if 'Free Software Foundation' in out or 'xt-' in out:
419            guess_gcc_or_lcc = 'gcc'
420        if 'e2k' in out and 'lcc' in out:
421            guess_gcc_or_lcc = 'lcc'
422        if 'Microchip Technology' in out:
423            # this output has "Free Software Foundation" in its version
424            guess_gcc_or_lcc = None
425
426        if guess_gcc_or_lcc:
427            defines = _get_gnu_compiler_defines(compiler)
428            if not defines:
429                popen_exceptions[' '.join(compiler)] = 'no pre-processor defines'
430                continue
431
432            if guess_gcc_or_lcc == 'lcc':
433                version = _get_lcc_version_from_defines(defines)
434                cls = ElbrusCCompiler if lang == 'c' else ElbrusCPPCompiler
435            else:
436                version = _get_gnu_version_from_defines(defines)
437                cls = GnuCCompiler if lang == 'c' else GnuCPPCompiler
438
439            linker = guess_nix_linker(env, compiler, cls, for_machine)
440
441            return cls(
442                ccache + compiler, version, for_machine, is_cross,
443                info, exe_wrap, defines=defines, full_version=full_version,
444                linker=linker)
445
446        if 'Emscripten' in out:
447            cls = EmscriptenCCompiler if lang == 'c' else EmscriptenCPPCompiler
448            env.coredata.add_lang_args(cls.language, cls, for_machine, env)
449
450            # emcc requires a file input in order to pass arguments to the
451            # linker. It'll exit with an error code, but still print the
452            # linker version. Old emcc versions ignore -Wl,--version completely,
453            # however. We'll report "unknown version" in that case.
454            with tempfile.NamedTemporaryFile(suffix='.c') as f:
455                cmd = compiler + [cls.LINKER_PREFIX + "--version", f.name]
456                _, o, _ = Popen_safe(cmd)
457
458            linker = WASMDynamicLinker(
459                compiler, for_machine, cls.LINKER_PREFIX,
460                [], version=search_version(o))
461            return cls(
462                ccache + compiler, version, for_machine, is_cross, info,
463                exe_wrap, linker=linker, full_version=full_version)
464
465        if 'armclang' in out:
466            # The compiler version is not present in the first line of output,
467            # instead it is present in second line, startswith 'Component:'.
468            # So, searching for the 'Component' in out although we know it is
469            # present in second line, as we are not sure about the
470            # output format in future versions
471            arm_ver_match = re.search('.*Component.*', out)
472            if arm_ver_match is None:
473                popen_exceptions[' '.join(compiler)] = 'version string not found'
474                continue
475            arm_ver_str = arm_ver_match.group(0)
476            # Override previous values
477            version = search_version(arm_ver_str)
478            full_version = arm_ver_str
479            cls = ArmclangCCompiler if lang == 'c' else ArmclangCPPCompiler
480            linker = ArmClangDynamicLinker(for_machine, version=version)
481            env.coredata.add_lang_args(cls.language, cls, for_machine, env)
482            return cls(
483                ccache + compiler, version, for_machine, is_cross, info,
484                exe_wrap, full_version=full_version, linker=linker)
485        if 'CL.EXE COMPATIBILITY' in out:
486            # if this is clang-cl masquerading as cl, detect it as cl, not
487            # clang
488            arg = '--version'
489            try:
490                p, out, err = Popen_safe(compiler + [arg])
491            except OSError as e:
492                popen_exceptions[' '.join(compiler + [arg])] = e
493            version = search_version(out)
494            match = re.search('^Target: (.*?)-', out, re.MULTILINE)
495            if match:
496                target = match.group(1)
497            else:
498                target = 'unknown target'
499            cls = ClangClCCompiler if lang == 'c' else ClangClCPPCompiler
500            linker = guess_win_linker(env, ['lld-link'], cls, for_machine)
501            return cls(
502                compiler, version, for_machine, is_cross, info, target,
503                exe_wrap, linker=linker)
504        if 'clang' in out or 'Clang' in out:
505            linker = None
506
507            defines = _get_clang_compiler_defines(compiler)
508
509            # Even if the for_machine is darwin, we could be using vanilla
510            # clang.
511            if 'Apple' in out:
512                cls = AppleClangCCompiler if lang == 'c' else AppleClangCPPCompiler
513            else:
514                cls = ClangCCompiler if lang == 'c' else ClangCPPCompiler
515
516            if 'windows' in out or env.machines[for_machine].is_windows():
517                # If we're in a MINGW context this actually will use a gnu
518                # style ld, but for clang on "real" windows we'll use
519                # either link.exe or lld-link.exe
520                try:
521                    linker = guess_win_linker(env, compiler, cls, for_machine, invoked_directly=False)
522                except MesonException:
523                    pass
524            if linker is None:
525                linker = guess_nix_linker(env, compiler, cls, for_machine)
526
527            return cls(
528                ccache + compiler, version, for_machine, is_cross, info,
529                exe_wrap, defines=defines, full_version=full_version, linker=linker)
530
531        if 'Intel(R) C++ Intel(R)' in err:
532            version = search_version(err)
533            target = 'x86' if 'IA-32' in err else 'x86_64'
534            cls = IntelClCCompiler if lang == 'c' else IntelClCPPCompiler
535            env.coredata.add_lang_args(cls.language, cls, for_machine, env)
536            linker = XilinkDynamicLinker(for_machine, [], version=version)
537            return cls(
538                compiler, version, for_machine, is_cross, info, target,
539                exe_wrap, linker=linker)
540        if 'Microsoft' in out or 'Microsoft' in err:
541            # Latest versions of Visual Studio print version
542            # number to stderr but earlier ones print version
543            # on stdout.  Why? Lord only knows.
544            # Check both outputs to figure out version.
545            for lookat in [err, out]:
546                version = search_version(lookat)
547                if version != 'unknown version':
548                    break
549            else:
550                raise EnvironmentException(f'Failed to detect MSVC compiler version: stderr was\n{err!r}')
551            cl_signature = lookat.split('\n')[0]
552            match = re.search(r'.*(x86|x64|ARM|ARM64)([^_A-Za-z0-9]|$)', cl_signature)
553            if match:
554                target = match.group(1)
555            else:
556                m = f'Failed to detect MSVC compiler target architecture: \'cl /?\' output is\n{cl_signature}'
557                raise EnvironmentException(m)
558            cls = VisualStudioCCompiler if lang == 'c' else VisualStudioCPPCompiler
559            linker = guess_win_linker(env, ['link'], cls, for_machine)
560            return cls(
561                compiler, version, for_machine, is_cross, info, target,
562                exe_wrap, full_version=cl_signature, linker=linker)
563        if 'PGI Compilers' in out:
564            cls = PGICCompiler if lang == 'c' else PGICPPCompiler
565            env.coredata.add_lang_args(cls.language, cls, for_machine, env)
566            linker = PGIDynamicLinker(compiler, for_machine, cls.LINKER_PREFIX, [], version=version)
567            return cls(
568                ccache + compiler, version, for_machine, is_cross,
569                info, exe_wrap, linker=linker)
570        if 'NVIDIA Compilers and Tools' in out:
571            cls = NvidiaHPC_CCompiler if lang == 'c' else NvidiaHPC_CPPCompiler
572            env.coredata.add_lang_args(cls.language, cls, for_machine, env)
573            linker = NvidiaHPC_DynamicLinker(compiler, for_machine, cls.LINKER_PREFIX, [], version=version)
574            return cls(
575                ccache + compiler, version, for_machine, is_cross,
576                info, exe_wrap, linker=linker)
577        if '(ICC)' in out:
578            cls = IntelCCompiler if lang == 'c' else IntelCPPCompiler
579            l = guess_nix_linker(env, compiler, cls, for_machine)
580            return cls(
581                ccache + compiler, version, for_machine, is_cross, info,
582                exe_wrap, full_version=full_version, linker=l)
583        if 'ARM' in out:
584            cls = ArmCCompiler if lang == 'c' else ArmCPPCompiler
585            env.coredata.add_lang_args(cls.language, cls, for_machine, env)
586            linker = ArmDynamicLinker(for_machine, version=version)
587            return cls(
588                ccache + compiler, version, for_machine, is_cross,
589                info, exe_wrap, full_version=full_version, linker=linker)
590        if 'RX Family' in out:
591            cls = CcrxCCompiler if lang == 'c' else CcrxCPPCompiler
592            env.coredata.add_lang_args(cls.language, cls, for_machine, env)
593            linker = CcrxDynamicLinker(for_machine, version=version)
594            return cls(
595                ccache + compiler, version, for_machine, is_cross, info,
596                exe_wrap, full_version=full_version, linker=linker)
597
598        if 'Microchip Technology' in out:
599            cls = Xc16CCompiler if lang == 'c' else Xc16CCompiler
600            env.coredata.add_lang_args(cls.language, cls, for_machine, env)
601            linker = Xc16DynamicLinker(for_machine, version=version)
602            return cls(
603                ccache + compiler, version, for_machine, is_cross, info,
604                exe_wrap, full_version=full_version, linker=linker)
605
606        if 'CompCert' in out:
607            cls = CompCertCCompiler
608            env.coredata.add_lang_args(cls.language, cls, for_machine, env)
609            linker = CompCertDynamicLinker(for_machine, version=version)
610            return cls(
611                ccache + compiler, version, for_machine, is_cross, info,
612                exe_wrap, full_version=full_version, linker=linker)
613
614        if 'TMS320C2000 C/C++' in out:
615            cls = C2000CCompiler if lang == 'c' else C2000CPPCompiler
616            env.coredata.add_lang_args(cls.language, cls, for_machine, env)
617            linker = C2000DynamicLinker(compiler, for_machine, version=version)
618            return cls(
619                ccache + compiler, version, for_machine, is_cross, info,
620                exe_wrap, full_version=full_version, linker=linker)
621
622    _handle_exceptions(popen_exceptions, compilers)
623    raise EnvironmentException(f'Unknown compiler {compilers}')
624
625def detect_c_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
626    return _detect_c_or_cpp_compiler(env, 'c', for_machine)
627
628def detect_cpp_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
629    return _detect_c_or_cpp_compiler(env, 'cpp', for_machine)
630
631def detect_cuda_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
632    popen_exceptions = {}
633    is_cross = env.is_cross_build(for_machine)
634    compilers, ccache, exe_wrap = _get_compilers(env, 'cuda', for_machine)
635    info = env.machines[for_machine]
636    for compiler in compilers:
637        arg = '--version'
638        try:
639            p, out, err = Popen_safe(compiler + [arg])
640        except OSError as e:
641            popen_exceptions[' '.join(compiler + [arg])] = e
642            continue
643        # Example nvcc printout:
644        #
645        #     nvcc: NVIDIA (R) Cuda compiler driver
646        #     Copyright (c) 2005-2018 NVIDIA Corporation
647        #     Built on Sat_Aug_25_21:08:01_CDT_2018
648        #     Cuda compilation tools, release 10.0, V10.0.130
649        #
650        # search_version() first finds the "10.0" after "release",
651        # rather than the more precise "10.0.130" after "V".
652        # The patch version number is occasionally important; For
653        # instance, on Linux,
654        #    - CUDA Toolkit 8.0.44 requires NVIDIA Driver 367.48
655        #    - CUDA Toolkit 8.0.61 requires NVIDIA Driver 375.26
656        # Luckily, the "V" also makes it very simple to extract
657        # the full version:
658        version = out.strip().split('V')[-1]
659        cpp_compiler = detect_cpp_compiler(env, for_machine)
660        cls = CudaCompiler
661        env.coredata.add_lang_args(cls.language, cls, for_machine, env)
662        linker = CudaLinker(compiler, for_machine, CudaCompiler.LINKER_PREFIX, [], version=CudaLinker.parse_version())
663        return cls(ccache + compiler, version, for_machine, is_cross, exe_wrap, host_compiler=cpp_compiler, info=info, linker=linker)
664    raise EnvironmentException(f'Could not find suitable CUDA compiler: "{"; ".join([" ".join(c) for c in compilers])}"')
665
666def detect_fortran_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
667    popen_exceptions: T.Dict[str, T.Union[Exception, str]] = {}
668    compilers, ccache, exe_wrap = _get_compilers(env, 'fortran', for_machine)
669    is_cross = env.is_cross_build(for_machine)
670    info = env.machines[for_machine]
671    cls: T.Type[FortranCompiler]
672    for compiler in compilers:
673        for arg in ['--version', '-V']:
674            try:
675                p, out, err = Popen_safe(compiler + [arg])
676            except OSError as e:
677                popen_exceptions[' '.join(compiler + [arg])] = e
678                continue
679
680            version = search_version(out)
681            full_version = out.split('\n', 1)[0]
682
683            guess_gcc_or_lcc: T.Optional[str] = None
684            if 'GNU Fortran' in out:
685                guess_gcc_or_lcc = 'gcc'
686            if 'e2k' in out and 'lcc' in out:
687                guess_gcc_or_lcc = 'lcc'
688
689            if guess_gcc_or_lcc:
690                defines = _get_gnu_compiler_defines(compiler)
691                if not defines:
692                    popen_exceptions[' '.join(compiler)] = 'no pre-processor defines'
693                    continue
694                if guess_gcc_or_lcc == 'lcc':
695                    version = _get_lcc_version_from_defines(defines)
696                    cls = ElbrusFortranCompiler
697                    linker = guess_nix_linker(env, compiler, cls, for_machine)
698                    return cls(
699                        compiler, version, for_machine, is_cross, info,
700                        exe_wrap, defines, full_version=full_version, linker=linker)
701                else:
702                    version = _get_gnu_version_from_defines(defines)
703                    cls = GnuFortranCompiler
704                    linker = guess_nix_linker(env, compiler, cls, for_machine)
705                    return cls(
706                        compiler, version, for_machine, is_cross, info,
707                        exe_wrap, defines, full_version=full_version, linker=linker)
708
709            if 'G95' in out:
710                cls = G95FortranCompiler
711                linker = guess_nix_linker(env, compiler, cls, for_machine)
712                return G95FortranCompiler(
713                    compiler, version, for_machine, is_cross, info,
714                    exe_wrap, full_version=full_version, linker=linker)
715
716            if 'Sun Fortran' in err:
717                version = search_version(err)
718                cls = SunFortranCompiler
719                linker = guess_nix_linker(env, compiler, cls, for_machine)
720                return SunFortranCompiler(
721                    compiler, version, for_machine, is_cross, info,
722                    exe_wrap, full_version=full_version, linker=linker)
723
724            if 'Intel(R) Visual Fortran' in err or 'Intel(R) Fortran' in err:
725                version = search_version(err)
726                target = 'x86' if 'IA-32' in err else 'x86_64'
727                cls = IntelClFortranCompiler
728                env.coredata.add_lang_args(cls.language, cls, for_machine, env)
729                linker = XilinkDynamicLinker(for_machine, [], version=version)
730                return cls(
731                    compiler, version, for_machine, is_cross, info,
732                    target, exe_wrap, linker=linker)
733
734            if 'ifort (IFORT)' in out:
735                linker = guess_nix_linker(env, compiler, IntelFortranCompiler, for_machine)
736                return IntelFortranCompiler(
737                    compiler, version, for_machine, is_cross, info,
738                    exe_wrap, full_version=full_version, linker=linker)
739
740            if 'PathScale EKOPath(tm)' in err:
741                return PathScaleFortranCompiler(
742                    compiler, version, for_machine, is_cross, info,
743                    exe_wrap, full_version=full_version)
744
745            if 'PGI Compilers' in out:
746                cls = PGIFortranCompiler
747                env.coredata.add_lang_args(cls.language, cls, for_machine, env)
748                linker = PGIDynamicLinker(compiler, for_machine,
749                                            cls.LINKER_PREFIX, [], version=version)
750                return cls(
751                    compiler, version, for_machine, is_cross, info, exe_wrap,
752                    full_version=full_version, linker=linker)
753
754            if 'NVIDIA Compilers and Tools' in out:
755                cls = NvidiaHPC_FortranCompiler
756                env.coredata.add_lang_args(cls.language, cls, for_machine, env)
757                linker = PGIDynamicLinker(compiler, for_machine,
758                                            cls.LINKER_PREFIX, [], version=version)
759                return cls(
760                    compiler, version, for_machine, is_cross, info, exe_wrap,
761                    full_version=full_version, linker=linker)
762
763            if 'flang' in out or 'clang' in out:
764                linker = guess_nix_linker(env,
765                    compiler, FlangFortranCompiler, for_machine)
766                return FlangFortranCompiler(
767                    compiler, version, for_machine, is_cross, info,
768                    exe_wrap, full_version=full_version, linker=linker)
769
770            if 'Open64 Compiler Suite' in err:
771                linker = guess_nix_linker(env,
772                    compiler, Open64FortranCompiler, for_machine)
773                return Open64FortranCompiler(
774                    compiler, version, for_machine, is_cross, info,
775                    exe_wrap, full_version=full_version, linker=linker)
776
777            if 'NAG Fortran' in err:
778                full_version = err.split('\n', 1)[0]
779                version = full_version.split()[-1]
780                cls = NAGFortranCompiler
781                env.coredata.add_lang_args(cls.language, cls, for_machine, env)
782                linker = NAGDynamicLinker(
783                    compiler, for_machine, cls.LINKER_PREFIX, [],
784                    version=version)
785                return cls(
786                    compiler, version, for_machine, is_cross, info,
787                    exe_wrap, full_version=full_version, linker=linker)
788
789    _handle_exceptions(popen_exceptions, compilers)
790    raise EnvironmentException('Unreachable code (exception to make mypy happy)')
791
792def detect_objc_compiler(env: 'Environment', for_machine: MachineChoice) -> 'Compiler':
793    return _detect_objc_or_objcpp_compiler(env, for_machine, True)
794
795def detect_objcpp_compiler(env: 'Environment', for_machine: MachineChoice) -> 'Compiler':
796    return _detect_objc_or_objcpp_compiler(env, for_machine, False)
797
798def _detect_objc_or_objcpp_compiler(env: 'Environment', for_machine: MachineChoice, objc: bool) -> 'Compiler':
799    popen_exceptions: T.Dict[str, T.Union[Exception, str]] = {}
800    compilers, ccache, exe_wrap = _get_compilers(env, 'objc' if objc else 'objcpp', for_machine)
801    is_cross = env.is_cross_build(for_machine)
802    info = env.machines[for_machine]
803    comp: T.Union[T.Type[ObjCCompiler], T.Type[ObjCPPCompiler]]
804
805    for compiler in compilers:
806        arg = ['--version']
807        try:
808            p, out, err = Popen_safe(compiler + arg)
809        except OSError as e:
810            popen_exceptions[' '.join(compiler + arg)] = e
811            continue
812        version = search_version(out)
813        if 'Free Software Foundation' in out:
814            defines = _get_gnu_compiler_defines(compiler)
815            if not defines:
816                popen_exceptions[' '.join(compiler)] = 'no pre-processor defines'
817                continue
818            version = _get_gnu_version_from_defines(defines)
819            comp = GnuObjCCompiler if objc else GnuObjCPPCompiler
820            linker = guess_nix_linker(env, compiler, comp, for_machine)
821            return comp(
822                ccache + compiler, version, for_machine, is_cross, info,
823                exe_wrap, defines, linker=linker)
824        if 'clang' in out:
825            linker = None
826            defines = _get_clang_compiler_defines(compiler)
827            if not defines:
828                popen_exceptions[' '.join(compiler)] = 'no pre-processor defines'
829                continue
830            if 'Apple' in out:
831                comp = AppleClangObjCCompiler if objc else AppleClangObjCPPCompiler
832            else:
833                comp = ClangObjCCompiler if objc else ClangObjCPPCompiler
834            if 'windows' in out or env.machines[for_machine].is_windows():
835                # If we're in a MINGW context this actually will use a gnu style ld
836                try:
837                    linker = guess_win_linker(env, compiler, comp, for_machine)
838                except MesonException:
839                    pass
840
841            if not linker:
842                linker = guess_nix_linker(env, compiler, comp, for_machine)
843            return comp(
844                ccache + compiler, version, for_machine,
845                is_cross, info, exe_wrap, linker=linker, defines=defines)
846    _handle_exceptions(popen_exceptions, compilers)
847    raise EnvironmentException('Unreachable code (exception to make mypy happy)')
848
849def detect_java_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
850    exelist = env.lookup_binary_entry(for_machine, 'java')
851    info = env.machines[for_machine]
852    if exelist is None:
853        # TODO support fallback
854        exelist = [defaults['java'][0]]
855
856    try:
857        p, out, err = Popen_safe(exelist + ['-version'])
858    except OSError:
859        raise EnvironmentException('Could not execute Java compiler "{}"'.format(' '.join(exelist)))
860    if 'javac' in out or 'javac' in err:
861        version = search_version(err if 'javac' in err else out)
862        if not version or version == 'unknown version':
863            parts = (err if 'javac' in err else out).split()
864            if len(parts) > 1:
865                version = parts[1]
866        comp_class = JavaCompiler
867        env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)
868        return comp_class(exelist, version, for_machine, info)
869    raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
870
871def detect_cs_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
872    compilers, ccache, exe_wrap = _get_compilers(env, 'cs', for_machine)
873    popen_exceptions = {}
874    info = env.machines[for_machine]
875    for comp in compilers:
876        try:
877            p, out, err = Popen_safe(comp + ['--version'])
878        except OSError as e:
879            popen_exceptions[' '.join(comp + ['--version'])] = e
880            continue
881
882        version = search_version(out)
883        cls: T.Union[T.Type[MonoCompiler], T.Type[VisualStudioCsCompiler]]
884        if 'Mono' in out:
885            cls = MonoCompiler
886        elif "Visual C#" in out:
887            cls = VisualStudioCsCompiler
888        else:
889            continue
890        env.coredata.add_lang_args(cls.language, cls, for_machine, env)
891        return cls(comp, version, for_machine, info)
892
893    _handle_exceptions(popen_exceptions, compilers)
894    raise EnvironmentException('Unreachable code (exception to make mypy happy)')
895
896def detect_cython_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
897    """Search for a cython compiler."""
898    compilers, _, _ = _get_compilers(env, 'cython', for_machine)
899    is_cross = env.is_cross_build(for_machine)
900    info = env.machines[for_machine]
901
902    popen_exceptions: T.Dict[str, Exception] = {}
903    for comp in compilers:
904        try:
905            err = Popen_safe(comp + ['-V'])[2]
906        except OSError as e:
907            popen_exceptions[' '.join(comp + ['-V'])] = e
908            continue
909
910        version = search_version(err)
911        if 'Cython' in err:
912            comp_class = CythonCompiler
913            env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)
914            return comp_class(comp, version, for_machine, info, is_cross=is_cross)
915    _handle_exceptions(popen_exceptions, compilers)
916    raise EnvironmentException('Unreachable code (exception to make mypy happy)')
917
918def detect_vala_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
919    exelist = env.lookup_binary_entry(for_machine, 'vala')
920    is_cross = env.is_cross_build(for_machine)
921    info = env.machines[for_machine]
922    if exelist is None:
923        # TODO support fallback
924        exelist = [defaults['vala'][0]]
925
926    try:
927        p, out = Popen_safe(exelist + ['--version'])[0:2]
928    except OSError:
929        raise EnvironmentException('Could not execute Vala compiler "{}"'.format(' '.join(exelist)))
930    version = search_version(out)
931    if 'Vala' in out:
932        comp_class = ValaCompiler
933        env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)
934        return comp_class(exelist, version, for_machine, is_cross, info)
935    raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
936
937def detect_rust_compiler(env: 'Environment', for_machine: MachineChoice) -> RustCompiler:
938    popen_exceptions = {}  # type: T.Dict[str, Exception]
939    compilers, _, exe_wrap = _get_compilers(env, 'rust', for_machine)
940    is_cross = env.is_cross_build(for_machine)
941    info = env.machines[for_machine]
942
943    cc = detect_c_compiler(env, for_machine)
944    is_link_exe = isinstance(cc.linker, VisualStudioLikeLinkerMixin)
945    override = env.lookup_binary_entry(for_machine, 'rust_ld')
946
947    for compiler in compilers:
948        arg = ['--version']
949        try:
950            out = Popen_safe(compiler + arg)[1]
951        except OSError as e:
952            popen_exceptions[' '.join(compiler + arg)] = e
953            continue
954
955        version = search_version(out)
956        cls: T.Type[RustCompiler] = RustCompiler
957
958        # Clippy is a wrapper around rustc, but it doesn't have rustc in it's
959        # output. We can otherwise treat it as rustc.
960        if 'clippy' in out:
961            out = 'rustc'
962            cls = ClippyRustCompiler
963
964        if 'rustc' in out:
965            # On Linux and mac rustc will invoke gcc (clang for mac
966            # presumably) and it can do this windows, for dynamic linking.
967            # this means the easiest way to C compiler for dynamic linking.
968            # figure out what linker to use is to just get the value of the
969            # C compiler and use that as the basis of the rust linker.
970            # However, there are two things we need to change, if CC is not
971            # the default use that, and second add the necessary arguments
972            # to rust to use -fuse-ld
973
974            if any(a.startswith('linker=') for a in compiler):
975                mlog.warning(
976                    'Please do not put -C linker= in your compiler '
977                    'command, set rust_ld=command in your cross file '
978                    'or use the RUST_LD environment variable, otherwise meson '
979                    'will override your selection.')
980
981            compiler = compiler.copy()  # avoid mutating the original list
982
983            if override is None:
984                extra_args: T.Dict[str, T.Union[str, bool]] = {}
985                always_args: T.List[str] = []
986                if is_link_exe:
987                    compiler.extend(cls.use_linker_args(cc.linker.exelist[0]))
988                    extra_args['direct'] = True
989                    extra_args['machine'] = cc.linker.machine
990                else:
991                    exelist = cc.linker.exelist + cc.linker.get_always_args()
992                    if 'ccache' in exelist[0]:
993                        del exelist[0]
994                    c = exelist.pop(0)
995                    compiler.extend(cls.use_linker_args(c))
996
997                    # Also ensure that we pass any extra arguments to the linker
998                    for l in exelist:
999                        compiler.extend(['-C', f'link-arg={l}'])
1000
1001                # This trickery with type() gets us the class of the linker
1002                # so we can initialize a new copy for the Rust Compiler
1003                # TODO rewrite this without type: ignore
1004                if is_link_exe:
1005                    linker = type(cc.linker)(for_machine, always_args, exelist=cc.linker.exelist,   # type: ignore
1006                                                version=cc.linker.version, **extra_args)            # type: ignore
1007                else:
1008                    linker = type(cc.linker)(compiler, for_machine, cc.LINKER_PREFIX,
1009                                                always_args=always_args, version=cc.linker.version,
1010                                                **extra_args) # type: ignore
1011            elif 'link' in override[0]:
1012                linker = guess_win_linker(env,
1013                    override, cls, for_machine, use_linker_prefix=False)
1014                # rustc takes linker arguments without a prefix, and
1015                # inserts the correct prefix itself.
1016                assert isinstance(linker, VisualStudioLikeLinkerMixin)
1017                linker.direct = True
1018                compiler.extend(cls.use_linker_args(linker.exelist[0]))
1019            else:
1020                # On linux and macos rust will invoke the c compiler for
1021                # linking, on windows it will use lld-link or link.exe.
1022                # we will simply ask for the C compiler that corresponds to
1023                # it, and use that.
1024                cc = _detect_c_or_cpp_compiler(env, 'c', for_machine, override_compiler=override)
1025                linker = cc.linker
1026
1027                # Of course, we're not going to use any of that, we just
1028                # need it to get the proper arguments to pass to rustc
1029                c = linker.exelist[1] if linker.exelist[0].endswith('ccache') else linker.exelist[0]
1030                compiler.extend(cls.use_linker_args(c))
1031
1032            env.coredata.add_lang_args(cls.language, cls, for_machine, env)
1033            return cls(
1034                compiler, version, for_machine, is_cross, info, exe_wrap,
1035                linker=linker)
1036
1037    _handle_exceptions(popen_exceptions, compilers)
1038    raise EnvironmentException('Unreachable code (exception to make mypy happy)')
1039
1040def detect_d_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
1041    info = env.machines[for_machine]
1042
1043    # Detect the target architecture, required for proper architecture handling on Windows.
1044    # MSVC compiler is required for correct platform detection.
1045    c_compiler = {'c': detect_c_compiler(env, for_machine)}
1046    is_msvc = isinstance(c_compiler['c'], VisualStudioCCompiler)
1047    if not is_msvc:
1048        c_compiler = {}
1049
1050    # Import here to avoid circular imports
1051    from ..environment import detect_cpu_family
1052    arch = detect_cpu_family(c_compiler)
1053    if is_msvc and arch == 'x86':
1054        arch = 'x86_mscoff'
1055
1056    popen_exceptions = {}
1057    is_cross = env.is_cross_build(for_machine)
1058    compilers, ccache, exe_wrap = _get_compilers(env, 'd', for_machine)
1059    for exelist in compilers:
1060        # Search for a D compiler.
1061        # We prefer LDC over GDC unless overridden with the DC
1062        # environment variable because LDC has a much more
1063        # up to date language version at time (2016).
1064        if os.path.basename(exelist[-1]).startswith(('ldmd', 'gdmd')):
1065            raise EnvironmentException(
1066                f'Meson does not support {exelist[-1]} as it is only a DMD frontend for another compiler.'
1067                'Please provide a valid value for DC or unset it so that Meson can resolve the compiler by itself.')
1068        try:
1069            p, out = Popen_safe(exelist + ['--version'])[0:2]
1070        except OSError as e:
1071            popen_exceptions[' '.join(exelist + ['--version'])] = e
1072            continue
1073        version = search_version(out)
1074        full_version = out.split('\n', 1)[0]
1075
1076        if 'LLVM D compiler' in out:
1077            # LDC seems to require a file
1078            # We cannot use NamedTemproraryFile on windows, its documented
1079            # to not work for our uses. So, just use mkstemp and only have
1080            # one path for simplicity.
1081            o, f = tempfile.mkstemp('.d')
1082            os.close(o)
1083
1084            try:
1085                if info.is_windows() or info.is_cygwin():
1086                    objfile = os.path.basename(f)[:-1] + 'obj'
1087                    linker = guess_win_linker(env,
1088                        exelist,
1089                        LLVMDCompiler, for_machine,
1090                        use_linker_prefix=True, invoked_directly=False,
1091                        extra_args=[f])
1092                else:
1093                    # LDC writes an object file to the current working directory.
1094                    # Clean it up.
1095                    objfile = os.path.basename(f)[:-1] + 'o'
1096                    linker = guess_nix_linker(env,
1097                        exelist, LLVMDCompiler, for_machine,
1098                        extra_args=[f])
1099            finally:
1100                windows_proof_rm(f)
1101                windows_proof_rm(objfile)
1102
1103            return LLVMDCompiler(
1104                exelist, version, for_machine, info, arch,
1105                full_version=full_version, linker=linker, version_output=out)
1106        elif 'gdc' in out:
1107            linker = guess_nix_linker(env, exelist, GnuDCompiler, for_machine)
1108            return GnuDCompiler(
1109                exelist, version, for_machine, info, arch,
1110                exe_wrapper=exe_wrap, is_cross=is_cross,
1111                full_version=full_version, linker=linker)
1112        elif 'The D Language Foundation' in out or 'Digital Mars' in out:
1113            # DMD seems to require a file
1114            # We cannot use NamedTemproraryFile on windows, its documented
1115            # to not work for our uses. So, just use mkstemp and only have
1116            # one path for simplicity.
1117            o, f = tempfile.mkstemp('.d')
1118            os.close(o)
1119
1120            # DMD as different detection logic for x86 and x86_64
1121            arch_arg = '-m64' if arch == 'x86_64' else '-m32'
1122
1123            try:
1124                if info.is_windows() or info.is_cygwin():
1125                    objfile = os.path.basename(f)[:-1] + 'obj'
1126                    linker = guess_win_linker(env,
1127                        exelist, DmdDCompiler, for_machine,
1128                        invoked_directly=False, extra_args=[f, arch_arg])
1129                else:
1130                    objfile = os.path.basename(f)[:-1] + 'o'
1131                    linker = guess_nix_linker(env,
1132                        exelist, DmdDCompiler, for_machine,
1133                        extra_args=[f, arch_arg])
1134            finally:
1135                windows_proof_rm(f)
1136                windows_proof_rm(objfile)
1137
1138            return DmdDCompiler(
1139                exelist, version, for_machine, info, arch,
1140                full_version=full_version, linker=linker)
1141        raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
1142
1143    _handle_exceptions(popen_exceptions, compilers)
1144    raise EnvironmentException('Unreachable code (exception to make mypy happy)')
1145
1146def detect_swift_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
1147    exelist = env.lookup_binary_entry(for_machine, 'swift')
1148    is_cross = env.is_cross_build(for_machine)
1149    info = env.machines[for_machine]
1150    if exelist is None:
1151        # TODO support fallback
1152        exelist = [defaults['swift'][0]]
1153
1154    try:
1155        p, _, err = Popen_safe(exelist + ['-v'])
1156    except OSError:
1157        raise EnvironmentException('Could not execute Swift compiler "{}"'.format(' '.join(exelist)))
1158    version = search_version(err)
1159    if 'Swift' in err:
1160        # As for 5.0.1 swiftc *requires* a file to check the linker:
1161        with tempfile.NamedTemporaryFile(suffix='.swift') as f:
1162            linker = guess_nix_linker(env,
1163                exelist, SwiftCompiler, for_machine,
1164                extra_args=[f.name])
1165        return SwiftCompiler(
1166            exelist, version, for_machine, is_cross, info, linker=linker)
1167
1168    raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
1169
1170
1171# GNU/Clang defines and version
1172# =============================
1173
1174def _get_gnu_compiler_defines(compiler: T.List[str]) -> T.Dict[str, str]:
1175    """
1176    Detect GNU compiler platform type (Apple, MinGW, Unix)
1177    """
1178    # Arguments to output compiler pre-processor defines to stdout
1179    # gcc, g++, and gfortran all support these arguments
1180    args = compiler + ['-E', '-dM', '-']
1181    p, output, error = Popen_safe(args, write='', stdin=subprocess.PIPE)
1182    if p.returncode != 0:
1183        raise EnvironmentException('Unable to detect GNU compiler type:\n' + output + error)
1184    # Parse several lines of the type:
1185    # `#define ___SOME_DEF some_value`
1186    # and extract `___SOME_DEF`
1187    defines: T.Dict[str, str] = {}
1188    for line in output.split('\n'):
1189        if not line:
1190            continue
1191        d, *rest = line.split(' ', 2)
1192        if d != '#define':
1193            continue
1194        if len(rest) == 1:
1195            defines[rest[0]] = ''
1196        if len(rest) == 2:
1197            defines[rest[0]] = rest[1]
1198    return defines
1199
1200def _get_clang_compiler_defines(compiler: T.List[str]) -> T.Dict[str, str]:
1201    """
1202    Get the list of Clang pre-processor defines
1203    """
1204    args = compiler + ['-E', '-dM', '-']
1205    p, output, error = Popen_safe(args, write='', stdin=subprocess.PIPE)
1206    if p.returncode != 0:
1207        raise EnvironmentException('Unable to get clang pre-processor defines:\n' + output + error)
1208    defines: T.Dict[str, str] = {}
1209    for line in output.split('\n'):
1210        if not line:
1211            continue
1212        d, *rest = line.split(' ', 2)
1213        if d != '#define':
1214            continue
1215        if len(rest) == 1:
1216            defines[rest[0]] = ''
1217        if len(rest) == 2:
1218            defines[rest[0]] = rest[1]
1219    return defines
1220
1221def _get_gnu_version_from_defines(defines: T.Dict[str, str]) -> str:
1222    dot = '.'
1223    major = defines.get('__GNUC__', '0')
1224    minor = defines.get('__GNUC_MINOR__', '0')
1225    patch = defines.get('__GNUC_PATCHLEVEL__', '0')
1226    return dot.join((major, minor, patch))
1227
1228def _get_lcc_version_from_defines(defines: T.Dict[str, str]) -> str:
1229    dot = '.'
1230    generation_and_major = defines.get('__LCC__', '100')
1231    generation = generation_and_major[:1]
1232    major = generation_and_major[1:]
1233    minor = defines.get('__LCC_MINOR__', '0')
1234    return dot.join((generation, major, minor))
1235