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