diff options
author | Dylan Baker <dylan@pnwbakers.com> | 2019-07-15 16:03:33 -0700 |
---|---|---|
committer | Dylan Baker <dylan@pnwbakers.com> | 2019-08-14 13:13:23 -0700 |
commit | 3fbb45016610deba863630b20e95f60d2d49a487 (patch) | |
tree | 21018d74086a93434317f8b760b33f55a769a675 | |
parent | 38d588bc6e314d50b54313ffa9454416a00f05b5 (diff) | |
download | meson-3fbb45016610deba863630b20e95f60d2d49a487.zip meson-3fbb45016610deba863630b20e95f60d2d49a487.tar.gz meson-3fbb45016610deba863630b20e95f60d2d49a487.tar.bz2 |
environment: Detect dynamic linker class
-rw-r--r-- | mesonbuild/environment.py | 194 | ||||
-rwxr-xr-x | run_unittests.py | 3 |
2 files changed, 163 insertions, 34 deletions
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 6592e5e..2630ae9 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -13,6 +13,7 @@ # limitations under the License. import os, platform, re, sys, shlex, shutil, subprocess, typing +import tempfile from . import coredata from .linkers import ArLinker, ArmarLinker, VisualStudioLinker, DLinker, CcrxLinker, IntelVisualStudioLinker @@ -38,6 +39,23 @@ from .compilers import ( is_object, is_source, ) +from .linkers import ( + AppleDynamicLinker, + ArmClangDynamicLinker, + ArmDynamicLinker, + CcrxDynamicLinker, + ClangClDynamicLinker, + DynamicLinker, + GnuDynamicLinker, + LLVMDynamicLinker, + MSVCDynamicLinker, + OptlinkDynamicLinker, + PGIDynamicLinker, + SolarisDynamicLinker, + XildAppleDynamicLinker, + XildLinuxDynamicLinker, + XilinkDynamicLinker, +) from functools import lru_cache from .compilers import ( ArmCCompiler, @@ -63,8 +81,8 @@ from .compilers import ( EmscriptenCCompiler, EmscriptenCPPCompiler, IntelCCompiler, - IntelCPPCompiler, IntelClCCompiler, + IntelCPPCompiler, IntelClCPPCompiler, IntelFortranCompiler, IntelClFortranCompiler, @@ -639,7 +657,57 @@ class Environment: errmsg += '\nRunning "{0}" gave "{1}"'.format(c, e) raise EnvironmentException(errmsg) - def _detect_c_or_cpp_compiler(self, lang, for_machine): + @staticmethod + def _guess_nix_linker(compiler: typing.List[str], for_machine: MachineChoice, *, + prefix: typing.Union[str, typing.List[str]] = '-Wl,', + extra_args: typing.Optional[typing.List[str]] = None) -> 'DynamicLinker': + """Helper for guessing what linker to use on Unix-Like OSes. + + :prefix: The prefix that the compiler uses to proxy arguments to the + linker, if required. This can be passed as a string or a list of + strings. If it is passed as a string then the arguments to be + proxied to the linker will be concatenated, if it is a list they + will be appended. This means that if a space is required (such as + with swift which wants `-Xlinker --version` and *not* + `-Xlinker=--version`) you must pass as a list. + :extra_args: Any addtional arguments rquired (such as a source file) + """ + extra_args = typing.cast(typing.List[str], extra_args or []) + if isinstance(prefix, str): + check_args = [prefix + '--version'] + extra_args + else: + check_args = prefix + ['--version'] + extra_args + _, o, e = Popen_safe(compiler + check_args) + v = search_version(o) + if o.startswith('LLD'): + linker = LLVMDynamicLinker(compiler, for_machine, 'lld', version=v) # type: DynamicLinker + # first is for apple clang, second is for real gcc + elif e.endswith('(use -v to see invocation)\n') or 'macosx_version' in e: + if isinstance(prefix, str): + _, _, e = Popen_safe(compiler + [prefix + '-v'] + extra_args) + else: + _, _, e = Popen_safe(compiler + prefix + ['-v'] + extra_args) + i = 'APPLE ld' + for line in e.split('\n'): + if 'PROJECT:ld' in line: + v = line.split('-')[1] + break + else: + v = 'unknown version' + linker = AppleDynamicLinker(compiler, for_machine, i, version=v) + elif 'GNU' in o: + if 'gold' in 'o': + i = 'GNU ld.gold' + else: + i = 'GNU ld.bfd' + linker = GnuDynamicLinker(compiler, for_machine, i, version=v) + elif 'Solaris' in e: + linker = SolarisDynamicLinker(compiler, for_machine, 'solaris', version=search_version(e)) + else: + raise MesonException('Unable to determine dynamic linker.') + return linker + + def _detect_c_or_cpp_compiler(self, lang: str, for_machine: MachineChoice) -> Compiler: popen_exceptions = {} compilers, ccache, exe_wrap = self._get_compilers(lang, for_machine) is_cross = not self.machines.matches_build_machine(for_machine) @@ -701,13 +769,18 @@ class Environment: popen_exceptions[' '.join(compiler)] = 'no pre-processor defines' continue compiler_type = self.get_gnu_compiler_type(defines) + + linker = self._guess_nix_linker(compiler, for_machine) + if guess_gcc_or_lcc == 'lcc': version = self.get_lcc_version_from_defines(defines) cls = ElbrusCCompiler if lang == 'c' else ElbrusCPPCompiler else: version = self.get_gnu_version_from_defines(defines) cls = GnuCCompiler if lang == 'c' else GnuCPPCompiler - return cls(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, defines, full_version=full_version) + return cls(ccache + compiler, version, compiler_type, + for_machine, is_cross, exe_wrap, defines, + full_version=full_version, linker=linker) if 'Emscripten' in out: cls = EmscriptenCCompiler if lang == 'c' else EmscriptenCPPCompiler @@ -730,7 +803,8 @@ class Environment: full_version = arm_ver_str compiler_type = CompilerType.ARM_WIN cls = ArmclangCCompiler if lang == 'c' else ArmclangCPPCompiler - return cls(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, full_version=full_version) + linker = ArmClangDynamicLinker(for_machine, version=version) + return cls(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, full_version=full_version, linker=linker) if 'CL.EXE COMPATIBILITY' in out: # if this is clang-cl masquerading as cl, detect it as cl, not # clang @@ -746,7 +820,8 @@ class Environment: else: target = 'unknown target' cls = ClangClCCompiler if lang == 'c' else ClangClCPPCompiler - return cls(compiler, version, for_machine, is_cross, exe_wrap, target) + linker = ClangClDynamicLinker(for_machine, version=version) + return cls(compiler, version, for_machine, is_cross, exe_wrap, target, linker=linker) if 'clang' in out: if 'Apple' in out or self.machines[for_machine].is_darwin(): compiler_type = CompilerType.CLANG_OSX @@ -755,12 +830,15 @@ class Environment: else: compiler_type = CompilerType.CLANG_STANDARD cls = ClangCCompiler if lang == 'c' else ClangCPPCompiler - return cls(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, full_version=full_version) + + linker = self._guess_nix_linker(compiler, for_machine) + return cls(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, full_version=full_version, linker=linker) if 'Intel(R) C++ Intel(R)' in err: version = search_version(err) target = 'x86' if 'IA-32' in err else 'x86_64' cls = IntelClCCompiler if lang == 'c' else IntelClCPPCompiler - return cls(compiler, version, for_machine, is_cross, exe_wrap, target) + linker = XilinkDynamicLinker(for_machine, version=version) + return cls(compiler, version, for_machine, is_cross, exe_wrap, target, linker=linker) if 'Microsoft' in out or 'Microsoft' in err: # Latest versions of Visual Studio print version # number to stderr but earlier ones print version @@ -778,8 +856,9 @@ class Environment: target = match.group(1) else: target = 'x86' + linker = MSVCDynamicLinker(for_machine, version=version) cls = VisualStudioCCompiler if lang == 'c' else VisualStudioCPPCompiler - return cls(compiler, version, for_machine, is_cross, exe_wrap, target) + return cls(compiler, version, for_machine, is_cross, exe_wrap, target, linker=linker) if 'PGI Compilers' in out: if self.machines[for_machine].is_darwin(): compiler_type = CompilerType.PGI_OSX @@ -788,25 +867,27 @@ class Environment: else: compiler_type = CompilerType.PGI_STANDARD cls = PGICCompiler if lang == 'c' else PGICPPCompiler - return cls(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap) + linker = PGIDynamicLinker(compiler, for_machine, 'pgi', version=version) + return cls(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, linker=linker) if '(ICC)' in out: if self.machines[for_machine].is_darwin(): compiler_type = CompilerType.ICC_OSX - elif self.machines[for_machine].is_windows(): - # TODO: fix ICC on Windows - compiler_type = CompilerType.ICC_WIN + l = XildAppleDynamicLinker(compiler, for_machine, 'xild', version=version) else: compiler_type = CompilerType.ICC_STANDARD + l = XildLinuxDynamicLinker(compiler, for_machine, 'xild', version=version) cls = IntelCCompiler if lang == 'c' else IntelCPPCompiler - return cls(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, full_version=full_version) + return cls(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, full_version=full_version, linker=l) if 'ARM' in out: compiler_type = CompilerType.ARM_WIN cls = ArmCCompiler if lang == 'c' else ArmCPPCompiler - return cls(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, full_version=full_version) + linker = ArmDynamicLinker(for_machine, version=version) + return cls(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, full_version=full_version, linker=linker) if 'RX Family' in out: compiler_type = CompilerType.CCRX_WIN cls = CcrxCCompiler if lang == 'c' else CcrxCPPCompiler - return cls(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, full_version=full_version) + linker = CcrxDynamicLinker(for_machine, version=version) + return cls(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, full_version=full_version, linker=linker) self._handle_exceptions(popen_exceptions, compilers) @@ -846,8 +927,8 @@ class Environment: # Luckily, the "V" also makes it very simple to extract # the full version: version = out.strip().split('V')[-1] - cls = CudaCompiler - return cls(ccache + compiler, version, for_machine, exe_wrap) + linker = self._guess_nix_linker(compiler, for_machine, prefix='-Xlinker=') + return CudaCompiler(ccache + compiler, version, for_machine, exe_wrap, linker=linker) raise EnvironmentException('Could not find suitable CUDA compiler: "' + ' '.join(compilers) + '"') def detect_fortran_compiler(self, for_machine: MachineChoice): @@ -885,22 +966,27 @@ class Environment: else: version = self.get_gnu_version_from_defines(defines) cls = GnuFortranCompiler - return cls(compiler, version, compiler_type, for_machine, is_cross, exe_wrap, defines, full_version=full_version) + linker = self._guess_nix_linker(compiler, for_machine) + return cls(compiler, version, compiler_type, for_machine, is_cross, exe_wrap, defines, full_version=full_version, linker=linker) if 'G95' in out: - return G95FortranCompiler(compiler, version, for_machine, is_cross, exe_wrap, full_version=full_version) + linker = self._guess_nix_linker(compiler, for_machine) + return G95FortranCompiler(compiler, version, for_machine, is_cross, exe_wrap, full_version=full_version, linker=linker) if 'Sun Fortran' in err: version = search_version(err) - return SunFortranCompiler(compiler, version, for_machine, is_cross, exe_wrap, full_version=full_version) + linker = self._guess_nix_linker(compiler, for_machine) + return SunFortranCompiler(compiler, version, for_machine, is_cross, exe_wrap, full_version=full_version, linker=linker) if 'Intel(R) Visual Fortran' in err: version = search_version(err) target = 'x86' if 'IA-32' in err else 'x86_64' - return IntelClFortranCompiler(compiler, version, for_machine, is_cross, target, exe_wrap) + linker = XilinkDynamicLinker(for_machine, version=version) + return IntelClFortranCompiler(compiler, version, for_machine, is_cross, target, exe_wrap, linker=linker) if 'ifort (IFORT)' in out: - return IntelFortranCompiler(compiler, version, for_machine, is_cross, exe_wrap, full_version=full_version) + linker = XildLinuxDynamicLinker(compiler, for_machine, 'xild', version=version) + return IntelFortranCompiler(compiler, version, for_machine, is_cross, exe_wrap, full_version=full_version, linker=linker) if 'PathScale EKOPath(tm)' in err: return PathScaleFortranCompiler(compiler, version, for_machine, is_cross, exe_wrap, full_version=full_version) @@ -912,16 +998,20 @@ class Environment: compiler_type = CompilerType.PGI_WIN else: compiler_type = CompilerType.PGI_STANDARD - return PGIFortranCompiler(compiler, version, compiler_type, for_machine, is_cross, exe_wrap, full_version=full_version) + linker = PGIDynamicLinker(compiler, for_machine, 'pgi', version=version) + return PGIFortranCompiler(compiler, version, compiler_type, for_machine, is_cross, exe_wrap, full_version=full_version, linker=linker) if 'flang' in out or 'clang' in out: - return FlangFortranCompiler(compiler, version, for_machine, is_cross, exe_wrap, full_version=full_version) + linker = self._guess_nix_linker(compiler, for_machine) + return FlangFortranCompiler(compiler, version, for_machine, is_cross, exe_wrap, full_version=full_version, linker=linker) if 'Open64 Compiler Suite' in err: - return Open64FortranCompiler(compiler, version, for_machine, is_cross, exe_wrap, full_version=full_version) + linker = self._guess_nix_linker(compiler, for_machine) + return Open64FortranCompiler(compiler, version, for_machine, is_cross, exe_wrap, full_version=full_version, linker=linker) if 'NAG Fortran' in err: - return NAGFortranCompiler(compiler, version, for_machine, is_cross, exe_wrap, full_version=full_version) + linker = self._guess_nix_linker(compiler, for_machine) + return NAGFortranCompiler(compiler, version, for_machine, is_cross, exe_wrap, full_version=full_version, linker=linker) self._handle_exceptions(popen_exceptions, compilers) def get_scratch_dir(self): @@ -955,7 +1045,8 @@ class Environment: compiler_type = self.get_gnu_compiler_type(defines) version = self.get_gnu_version_from_defines(defines) comp = GnuObjCCompiler if objc else GnuObjCPPCompiler - return comp(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, defines) + linker = self._guess_nix_linker(compiler, for_machine) + return comp(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, defines, linker=linker) if 'clang' in out: comp = ClangObjCCompiler if objc else ClangObjCPPCompiler if 'Apple' in out or self.machines[for_machine].is_darwin(): @@ -964,7 +1055,8 @@ class Environment: compiler_type = CompilerType.CLANG_MINGW else: compiler_type = CompilerType.CLANG_STANDARD - return comp(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap) + linker = self._guess_nix_linker(compiler, for_machine) + return comp(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, linker=linker) self._handle_exceptions(popen_exceptions, compilers) def detect_java_compiler(self, for_machine): @@ -1039,7 +1131,18 @@ class Environment: version = search_version(out) if 'rustc' in out: - return RustCompiler(compiler, version, for_machine, is_cross, exe_wrap) + # Chalk up another quirk for rust. There is no way (AFAICT) to + # figure out what linker rustc is using for a non-nightly compiler + # (On nightly you can pass -Z print-link-args). So we're going to + # hard code the linker based on the platform. + # Currenty gnu ld is used for everything except apple by + # default, and apple ld is used on mac. + # TODO: find some better way to figure this out. + if self.machines[for_machine].is_darwin(): + linker = AppleDynamicLinker([], for_machine, 'Apple ld') + else: + linker = GnuDynamicLinker([], for_machine, 'GNU ld') + return RustCompiler(compiler, version, for_machine, is_cross, exe_wrap, linker=linker) self._handle_exceptions(popen_exceptions, compilers) @@ -1080,11 +1183,31 @@ class Environment: arch = 'x86_mscoff' if 'LLVM D compiler' in out: - return compilers.LLVMDCompiler(exelist, version, for_machine, arch, full_version=full_version) + # LDC seems to require a file + m = self.machines[for_machine] + if m.is_windows() or m.is_cygwin(): + # Getting LDC on windows to give useful linker output when not + # doing real work is painfully hard. It ships with a verison of + # lld-link, so just assume that we're going to use lld-link + # with it. + _, o, _ = Popen_safe(['lld-link.exe', '--version']) + linker = ClangClDynamicLinker(for_machine, version=search_version(o)) + else: + with tempfile.NamedTemporaryFile(suffix='.d') as f: + linker = self._guess_nix_linker(exelist, for_machine, prefix='-L', extra_args=[f.name]) + return compilers.LLVMDCompiler(exelist, version, for_machine, arch, full_version=full_version, linker=linker) elif 'gdc' in out: - return compilers.GnuDCompiler(exelist, version, for_machine, arch, full_version=full_version) + linker = self._guess_nix_linker(exelist, for_machine) + return compilers.GnuDCompiler(exelist, version, for_machine, arch, full_version=full_version, linker=linker) elif 'The D Language Foundation' in out or 'Digital Mars' in out: - return compilers.DmdDCompiler(exelist, version, for_machine, arch, full_version=full_version) + # DMD seems to require a file + m = self.machines[for_machine] + if m.is_windows() or m.is_cygwin(): + linker = OptlinkDynamicLinker(for_machine, version=full_version) + else: + with tempfile.NamedTemporaryFile(suffix='.d') as f: + linker = self._guess_nix_linker(exelist, for_machine, prefix='-L', extra_args=[f.name]) + return compilers.DmdDCompiler(exelist, version, for_machine, arch, full_version=full_version, linker=linker) raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"') def detect_swift_compiler(self, for_machine): @@ -1100,7 +1223,12 @@ class Environment: raise EnvironmentException('Could not execute Swift compiler "%s"' % ' '.join(exelist)) version = search_version(err) if 'Swift' in err: - return compilers.SwiftCompiler(exelist, version, for_machine, is_cross) + # As for 5.0.1 swiftc *requires* a file to check the linker: + with tempfile.NamedTemporaryFile(suffix='.swift') as f: + linker = self._guess_nix_linker( + exelist, for_machine, prefix=['-Xlinker'], extra_args=[f.name]) + return compilers.SwiftCompiler(exelist, version, for_machine, is_cross, linker=linker) + raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"') def compiler_from_language(self, lang: str, for_machine: MachineChoice): diff --git a/run_unittests.py b/run_unittests.py index b7bcb88..09b6e00 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -6224,7 +6224,8 @@ class NativeFileTests(BasePlatformTests): @skip_if_not_language('swift') def test_swift_compiler(self): wrapper = self.helper_create_binary_wrapper( - 'swiftc', version='Swift 1.2345', outfile='stderr') + 'swiftc', version='Swift 1.2345', outfile='stderr', + extra_args={'Xlinker': 'macosx_version. PROJECT:ld - 1.2.3'}) env = get_fake_env() env.binaries.host.binaries['swift'] = wrapper compiler = env.detect_swift_compiler(MachineChoice.HOST) |