aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDylan Baker <dylan@pnwbakers.com>2019-07-15 16:03:33 -0700
committerDylan Baker <dylan@pnwbakers.com>2019-08-14 13:13:23 -0700
commit3fbb45016610deba863630b20e95f60d2d49a487 (patch)
tree21018d74086a93434317f8b760b33f55a769a675
parent38d588bc6e314d50b54313ffa9454416a00f05b5 (diff)
downloadmeson-3fbb45016610deba863630b20e95f60d2d49a487.zip
meson-3fbb45016610deba863630b20e95f60d2d49a487.tar.gz
meson-3fbb45016610deba863630b20e95f60d2d49a487.tar.bz2
environment: Detect dynamic linker class
-rw-r--r--mesonbuild/environment.py194
-rwxr-xr-xrun_unittests.py3
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)