Merge pull request #6207 from dcbaker/linker-option

Add a way to select the dynamic linker meson uses
pull/6330/head
Jussi Pakkanen 5 years ago committed by GitHub
commit 17dd9e5bff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      ci/azure-steps.yml
  2. 7
      docs/markdown/Cross-compilation.md
  3. 1
      docs/markdown/Native-environments.md
  4. 25
      docs/markdown/howtox.md
  5. 17
      docs/markdown/snippets/linker_override.md
  6. 9
      mesonbuild/backend/ninjabackend.py
  7. 6
      mesonbuild/compilers/compilers.py
  8. 4
      mesonbuild/compilers/mixins/gnu.py
  9. 4
      mesonbuild/compilers/mixins/visualstudio.py
  10. 6
      mesonbuild/compilers/rust.py
  11. 2
      mesonbuild/envconfig.py
  12. 257
      mesonbuild/environment.py
  13. 69
      mesonbuild/linkers.py
  14. 9
      run_project_tests.py
  15. 89
      run_unittests.py

@ -156,9 +156,10 @@ steps:
echo "" echo ""
echo "Locating cl, rc:" echo "Locating cl, rc, link:"
where.exe cl where.exe cl
where.exe rc where.exe rc
where.exe link
echo "" echo ""
echo "=== Start running tests ===" echo "=== Start running tests ==="

@ -97,6 +97,7 @@ this:
[binaries] [binaries]
c = '/usr/bin/i586-mingw32msvc-gcc' c = '/usr/bin/i586-mingw32msvc-gcc'
cpp = '/usr/bin/i586-mingw32msvc-g++' cpp = '/usr/bin/i586-mingw32msvc-g++'
ld = 'gold'
ar = '/usr/i586-mingw32msvc/bin/ar' ar = '/usr/i586-mingw32msvc/bin/ar'
strip = '/usr/i586-mingw32msvc/bin/strip' strip = '/usr/i586-mingw32msvc/bin/strip'
pkgconfig = '/usr/bin/i586-mingw32msvc-pkg-config' pkgconfig = '/usr/bin/i586-mingw32msvc-pkg-config'
@ -112,6 +113,12 @@ of a wrapper, these lines are all you need to write. Meson will
automatically use the given wrapper when it needs to run host automatically use the given wrapper when it needs to run host
binaries. This happens e.g. when running the project's test suite. binaries. This happens e.g. when running the project's test suite.
ld is special because it is compiler specific. For compilers like gcc and
clang which are used to invoke the linker this is a value to pass to their
"choose the linker" argument (-fuse-ld= in this case). For compilers like
MSVC and Clang-Cl, this is the path to a linker for meson to invoke, such as
`link.exe` or `lld-link.exe`. Support for ls is *new in 0.53.0*
The next section lists properties of the cross compiler and its target The next section lists properties of the cross compiler and its target
system, and thus properties of host system of what we're building. It system, and thus properties of host system of what we're building. It
looks like this: looks like this:

@ -40,6 +40,7 @@ like `llvm-config`
c = '/usr/local/bin/clang' c = '/usr/local/bin/clang'
cpp = '/usr/local/bin/clang++' cpp = '/usr/local/bin/clang++'
rust = '/usr/local/bin/rust' rust = '/usr/local/bin/rust'
ld = 'gold'
llvm-config = '/usr/local/llvm-svn/bin/llvm-config' llvm-config = '/usr/local/llvm-svn/bin/llvm-config'
``` ```

@ -23,8 +23,29 @@ compilation is done by setting `CC` to point to the cross compiler
that Meson supports natively the case where you compile helper tools that Meson supports natively the case where you compile helper tools
(such as code generators) and use the results during the (such as code generators) and use the results during the
build. Because of this Meson needs to know both the native and the build. Because of this Meson needs to know both the native and the
cross compiler. The former is set via the environment variables and cross compiler. The former is set via the environment variables or
the latter via the cross file only. native-files and the latter via the cross file only.
## Set dynamic linker
```console
$ CC=clang LD=lld meson <options>
```
or
```console
$ CC=clang-cl LD=link meson <options>
```
Like the compiler, the linker is selected via the LD environment variable, or
through the `ld` entry in a native or cross file. You must be aware of
whehter you're using a compiler that invokes the linker itself (most
compilers including GCC and Clang) or a linker that is invoked directly (when
using MSVC or compilers that act like it, including Clang-Cl). With the
former `ld` or `LD` should be the value to pass to the compiler's special
argument (such as `-fuse-ld` with clang and gcc), with the latter it should
be an exectuable, such as `lld-link.exe`.
## Set default C/C++ language version ## Set default C/C++ language version

@ -0,0 +1,17 @@
## Generic Overrider for Dynamic Linker selection
Previous to meson 0.52.0 you set the dynamic linker using compiler specific
flags passed via language flags and hoped things worked out. In meson 0.52.0
meson started detecting the linker and making intelligent decisions about
using it. Unfortunately this broke choosing a non-default linker.
Now there is a generic mechanism for doing this, you may use the LD
environment variable (with normal meson environment variable rules), or add
the following to a cross or native file:
```ini
[binaries]
ld = 'gold'
```
And meson will select the linker if possible.

@ -1291,6 +1291,15 @@ int dummy;
else: else:
raise InvalidArguments('Unknown target type for rustc.') raise InvalidArguments('Unknown target type for rustc.')
args.append(cratetype) args.append(cratetype)
# If we're dynamically linking, add those arguments
#
# Rust is super annoying, calling -C link-arg foo does not work, it has
# to be -C link-arg=foo
if cratetype in {'bin', 'dylib'}:
for a in rustc.linker.get_always_args():
args += ['-C', 'link-arg={}'.format(a)]
args += ['--crate-name', target.name] args += ['--crate-name', target.name]
args += rustc.get_buildtype_args(self.get_option_for_target('buildtype', target)) args += rustc.get_buildtype_args(self.get_option_for_target('buildtype', target))
args += rustc.get_debug_args(self.get_option_for_target('debug', target)) args += rustc.get_debug_args(self.get_option_for_target('debug', target))

@ -1192,6 +1192,12 @@ class Compiler:
def get_dependency_link_args(self, dep): def get_dependency_link_args(self, dep):
return dep.get_link_args() return dep.get_link_args()
@classmethod
def use_linker_args(cls, linker: str) -> typing.List[str]:
"""Get a list of arguments to pass to the compiler to set the linker.
"""
return []
def get_largefile_args(compiler): def get_largefile_args(compiler):
''' '''

@ -299,6 +299,10 @@ class GnuLikeCompiler(metaclass=abc.ABCMeta):
return ['-isystem' + path] return ['-isystem' + path]
return ['-I' + path] return ['-I' + path]
@classmethod
def use_linker_args(cls, linker: str) -> typing.List[str]:
return ['-fuse-ld={}'.format(linker)]
class GnuCompiler(GnuLikeCompiler): class GnuCompiler(GnuLikeCompiler):
""" """

@ -381,3 +381,7 @@ class VisualStudioLikeCompiler(metaclass=abc.ABCMeta):
def get_argument_syntax(self) -> str: def get_argument_syntax(self) -> str:
return 'msvc' return 'msvc'
@classmethod
def use_linker_args(cls, linker: str) -> typing.List[str]:
return []

@ -32,7 +32,7 @@ rust_optimization_args = {'0': [],
class RustCompiler(Compiler): class RustCompiler(Compiler):
LINKER_PREFIX = '-Wl,' # rustc doesn't invoke the compiler itself, it doesn't need a LINKER_PREFIX
def __init__(self, exelist, version, for_machine: MachineChoice, def __init__(self, exelist, version, for_machine: MachineChoice,
is_cross, info: 'MachineInfo', exe_wrapper=None, **kwargs): is_cross, info: 'MachineInfo', exe_wrapper=None, **kwargs):
@ -109,3 +109,7 @@ class RustCompiler(Compiler):
def get_std_exe_link_args(self): def get_std_exe_link_args(self):
return [] return []
# Rust does not have a use_linker_args because it dispatches to a gcc-like
# C compiler for dynamic linking, as such we invoke the C compiler's
# use_linker_args method instead.

@ -309,7 +309,9 @@ class BinaryTable(HasEnvVarFallback):
'strip': 'STRIP', 'strip': 'STRIP',
'ar': 'AR', 'ar': 'AR',
'windres': 'WINDRES', 'windres': 'WINDRES',
'ld': 'LD',
# Other tools
'cmake': 'CMAKE', 'cmake': 'CMAKE',
'qmake': 'QMAKE', 'qmake': 'QMAKE',
'pkgconfig': 'PKG_CONFIG', 'pkgconfig': 'PKG_CONFIG',

@ -53,10 +53,9 @@ from .linkers import (
PGIDynamicLinker, PGIDynamicLinker,
PGIStaticLinker, PGIStaticLinker,
SolarisDynamicLinker, SolarisDynamicLinker,
XildAppleDynamicLinker,
XildLinuxDynamicLinker,
XilinkDynamicLinker, XilinkDynamicLinker,
CudaLinker, CudaLinker,
VisualStudioLikeLinkerMixin,
) )
from functools import lru_cache from functools import lru_cache
from .compilers import ( from .compilers import (
@ -733,55 +732,88 @@ class Environment:
errmsg += '\nRunning "{0}" gave "{1}"'.format(c, e) errmsg += '\nRunning "{0}" gave "{1}"'.format(c, e)
raise EnvironmentException(errmsg) raise EnvironmentException(errmsg)
@staticmethod def _guess_win_linker(self, compiler: typing.List[str], comp_class: Compiler,
def _guess_win_linker(compiler: typing.List[str], for_machine: MachineChoice, for_machine: MachineChoice, *,
prefix: typing.Union[str, typing.List[str]]) -> 'DynamicLinker': use_linker_prefix: bool = True) -> 'DynamicLinker':
# Explicitly pass logo here so that we can get the version of link.exe # Explicitly pass logo here so that we can get the version of link.exe
if isinstance(prefix, str): if not use_linker_prefix or comp_class.LINKER_PREFIX is None:
check_args = [prefix + '/logo', prefix + '--version'] check_args = ['/logo', '--version']
else: elif isinstance(comp_class.LINKER_PREFIX, str):
check_args = prefix + ['/logo'] + prefix + ['--version'] check_args = [comp_class.LINKER_PREFIX + '/logo', comp_class.LINKER_PREFIX + '--version']
elif isinstance(comp_class.LINKER_PREFIX, list):
check_args = comp_class.LINKER_PREFIX + ['/logo'] + comp_class.LINKER_PREFIX + ['--version']
override = [] # type: typing.List[str]
value = self.binaries[for_machine].lookup_entry('ld')
if value is not None:
override = comp_class.use_linker_args(value[0])
check_args += override
p, o, _ = Popen_safe(compiler + check_args) p, o, _ = Popen_safe(compiler + check_args)
if o.startswith('LLD'): if o.startswith('LLD'):
if '(compatible with GNU linkers)' in o: if '(compatible with GNU linkers)' in o:
return LLVMDynamicLinker(compiler, for_machine, 'lld', prefix, version=search_version(o)) return LLVMDynamicLinker(
else: compiler, for_machine, 'lld', comp_class.LINKER_PREFIX,
return ClangClDynamicLinker(for_machine, exelist=compiler, prefix=prefix, version=search_version(o)) override, version=search_version(o))
elif o.startswith('Microsoft'):
match = re.search(r'.*(X86|X64|ARM|ARM64).*', o) if value is not None:
compiler = value
p, o, e = Popen_safe(compiler + check_args)
if o.startswith('LLD'):
return ClangClDynamicLinker(
for_machine, [],
prefix=comp_class.LINKER_PREFIX if use_linker_prefix else [],
exelist=compiler, version=search_version(o))
elif 'OPTLINK' in o:
# Opltink's stdout *may* beging with a \r character.
return OptlinkDynamicLinker(for_machine, version=search_version(o))
elif o.startswith('Microsoft') or e.startswith('Microsoft'):
out = o or e
match = re.search(r'.*(X86|X64|ARM|ARM64).*', out)
if match: if match:
target = str(match.group(1)) target = str(match.group(1))
else: else:
target = 'x86' target = 'x86'
return MSVCDynamicLinker(
for_machine, machine=target, exelist=compiler, prefix=prefix,
version=search_version(o))
raise MesonException('Cannot guess dynamic linker')
@staticmethod return MSVCDynamicLinker(
def _guess_nix_linker(compiler: typing.List[str], for_machine: MachineChoice, for_machine, [], machine=target, exelist=compiler,
prefix: typing.Union[str, typing.List[str]], *, prefix=comp_class.LINKER_PREFIX if use_linker_prefix else [],
version=search_version(out))
elif 'GNU coreutils' in o:
raise EnvironmentException(
"Found GNU link.exe instead of MSVC link.exe. This link.exe "
"is not a linker. You may need to reorder entries to your "
"%PATH% variable to resolve this.")
raise EnvironmentException('Unable to determine dynamic linker')
def _guess_nix_linker(self, compiler: typing.List[str], comp_class: typing.Type[Compiler],
for_machine: MachineChoice, *,
extra_args: typing.Optional[typing.List[str]] = None) -> 'DynamicLinker': extra_args: typing.Optional[typing.List[str]] = None) -> 'DynamicLinker':
"""Helper for guessing what linker to use on Unix-Like OSes. """Helper for guessing what linker to use on Unix-Like OSes.
:prefix: The prefix that the compiler uses to proxy arguments to the :compiler: Invocation to use to get linker
linker, if required. This can be passed as a string or a list of :comp_class: The Compiler Type (uninstantiated)
strings. If it is passed as a string then the arguments to be :for_machine: which machine this linker targets
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 additional arguments required (such as a source file) :extra_args: Any additional arguments required (such as a source file)
""" """
extra_args = typing.cast(typing.List[str], extra_args or []) extra_args = typing.cast(typing.List[str], extra_args or [])
if isinstance(prefix, str):
check_args = [prefix + '--version'] + extra_args if isinstance(comp_class.LINKER_PREFIX, str):
check_args = [comp_class.LINKER_PREFIX + '--version'] + extra_args
else: else:
check_args = prefix + ['--version'] + extra_args check_args = comp_class.LINKER_PREFIX + ['--version'] + extra_args
override = [] # type: typing.List[str]
value = self.binaries[for_machine].lookup_entry('ld')
if value is not None:
override = comp_class.use_linker_args(value[0])
check_args += override
_, o, e = Popen_safe(compiler + check_args) _, o, e = Popen_safe(compiler + check_args)
v = search_version(o) v = search_version(o)
if o.startswith('LLD'): if o.startswith('LLD'):
linker = LLVMDynamicLinker(compiler, for_machine, 'lld', prefix, version=v) # type: DynamicLinker linker = LLVMDynamicLinker(compiler, for_machine, 'lld', comp_class.LINKER_PREFIX, override, version=v) # type: DynamicLinker
elif e.startswith('lld-link: '): elif e.startswith('lld-link: '):
# Toolchain wrapper got in the way; this happens with e.g. https://github.com/mstorsjo/llvm-mingw # Toolchain wrapper got in the way; this happens with e.g. https://github.com/mstorsjo/llvm-mingw
# Let's try to extract the linker invocation command to grab the version. # Let's try to extract the linker invocation command to grab the version.
@ -797,13 +829,13 @@ class Environment:
_, o, e = Popen_safe([linker_cmd, '--version']) _, o, e = Popen_safe([linker_cmd, '--version'])
v = search_version(o) v = search_version(o)
linker = LLVMDynamicLinker(compiler, for_machine, 'lld', prefix, version=v) linker = LLVMDynamicLinker(compiler, for_machine, 'lld', comp_class.LINKER_PREFIX, override, version=v)
# first is for apple clang, second is for real gcc # first is for apple clang, second is for real gcc
elif e.endswith('(use -v to see invocation)\n') or 'macosx_version' in e: elif e.endswith('(use -v to see invocation)\n') or 'macosx_version' in e:
if isinstance(prefix, str): if isinstance(comp_class.LINKER_PREFIX, str):
_, _, e = Popen_safe(compiler + [prefix + '-v'] + extra_args) _, _, e = Popen_safe(compiler + [comp_class.LINKER_PREFIX + '-v'] + extra_args)
else: else:
_, _, e = Popen_safe(compiler + prefix + ['-v'] + extra_args) _, _, e = Popen_safe(compiler + comp_class.LINKER_PREFIX + ['-v'] + extra_args)
i = 'APPLE ld' i = 'APPLE ld'
for line in e.split('\n'): for line in e.split('\n'):
if 'PROJECT:ld' in line: if 'PROJECT:ld' in line:
@ -811,19 +843,19 @@ class Environment:
break break
else: else:
v = 'unknown version' v = 'unknown version'
linker = AppleDynamicLinker(compiler, for_machine, i, prefix, version=v) linker = AppleDynamicLinker(compiler, for_machine, i, comp_class.LINKER_PREFIX, override, version=v)
elif 'GNU' in o: elif 'GNU' in o:
if 'gold' in o: if 'gold' in o:
i = 'GNU ld.gold' i = 'GNU ld.gold'
else: else:
i = 'GNU ld.bfd' i = 'GNU ld.bfd'
linker = GnuDynamicLinker(compiler, for_machine, i, prefix, version=v) linker = GnuDynamicLinker(compiler, for_machine, i, comp_class.LINKER_PREFIX, override, version=v)
elif 'Solaris' in e or 'Solaris' in o: elif 'Solaris' in e or 'Solaris' in o:
linker = SolarisDynamicLinker( linker = SolarisDynamicLinker(
compiler, for_machine, 'solaris', prefix, compiler, for_machine, 'solaris', comp_class.LINKER_PREFIX, override,
version=search_version(e)) version=search_version(e))
else: else:
raise MesonException('Unable to determine dynamic linker.') raise EnvironmentException('Unable to determine dynamic linker')
return linker return linker
def _detect_c_or_cpp_compiler(self, lang: str, for_machine: MachineChoice) -> Compiler: def _detect_c_or_cpp_compiler(self, lang: str, for_machine: MachineChoice) -> Compiler:
@ -898,7 +930,7 @@ class Environment:
version = self.get_gnu_version_from_defines(defines) version = self.get_gnu_version_from_defines(defines)
cls = GnuCCompiler if lang == 'c' else GnuCPPCompiler cls = GnuCCompiler if lang == 'c' else GnuCPPCompiler
linker = self._guess_nix_linker(compiler, for_machine, cls.LINKER_PREFIX) linker = self._guess_nix_linker(compiler, cls, for_machine)
return cls( return cls(
ccache + compiler, version, for_machine, is_cross, ccache + compiler, version, for_machine, is_cross,
info, exe_wrap, defines, full_version=full_version, info, exe_wrap, defines, full_version=full_version,
@ -925,7 +957,7 @@ class Environment:
version = search_version(arm_ver_str) version = search_version(arm_ver_str)
full_version = arm_ver_str full_version = arm_ver_str
cls = ArmclangCCompiler if lang == 'c' else ArmclangCPPCompiler cls = ArmclangCCompiler if lang == 'c' else ArmclangCPPCompiler
linker = ArmClangDynamicLinker(for_machine, version=version) linker = ArmClangDynamicLinker(for_machine, [], version=version)
return cls( return cls(
ccache + compiler, version, for_machine, is_cross, info, ccache + compiler, version, for_machine, is_cross, info,
exe_wrap, full_version=full_version, linker=linker) exe_wrap, full_version=full_version, linker=linker)
@ -944,7 +976,7 @@ class Environment:
else: else:
target = 'unknown target' target = 'unknown target'
cls = ClangClCCompiler if lang == 'c' else ClangClCPPCompiler cls = ClangClCCompiler if lang == 'c' else ClangClCPPCompiler
linker = ClangClDynamicLinker(for_machine, version=version) linker = self._guess_win_linker(['lld-link'], cls, for_machine)
return cls( return cls(
compiler, version, for_machine, is_cross, info, exe_wrap, compiler, version, for_machine, is_cross, info, exe_wrap,
target, linker=linker) target, linker=linker)
@ -963,11 +995,11 @@ class Environment:
# style ld, but for clang on "real" windows we'll use # style ld, but for clang on "real" windows we'll use
# either link.exe or lld-link.exe # either link.exe or lld-link.exe
try: try:
linker = self._guess_win_linker(compiler, for_machine, cls.LINKER_PREFIX) linker = self._guess_win_linker(compiler, cls, for_machine)
except MesonException: except MesonException:
pass pass
if linker is None: if linker is None:
linker = self._guess_nix_linker(compiler, for_machine, cls.LINKER_PREFIX) linker = self._guess_nix_linker(compiler, cls, for_machine)
return cls( return cls(
ccache + compiler, version, for_machine, is_cross, info, ccache + compiler, version, for_machine, is_cross, info,
@ -999,23 +1031,23 @@ class Environment:
else: else:
m = 'Failed to detect MSVC compiler target architecture: \'cl /?\' output is\n{}' m = 'Failed to detect MSVC compiler target architecture: \'cl /?\' output is\n{}'
raise EnvironmentException(m.format(cl_signature)) raise EnvironmentException(m.format(cl_signature))
linker = MSVCDynamicLinker(for_machine, version=version)
cls = VisualStudioCCompiler if lang == 'c' else VisualStudioCPPCompiler cls = VisualStudioCCompiler if lang == 'c' else VisualStudioCPPCompiler
linker = self._guess_win_linker(['link'], cls, for_machine)
return cls( return cls(
compiler, version, for_machine, is_cross, info, exe_wrap, compiler, version, for_machine, is_cross, info, exe_wrap,
target, linker=linker) target, linker=linker)
if 'PGI Compilers' in out: if 'PGI Compilers' in out:
cls = PGICCompiler if lang == 'c' else PGICPPCompiler cls = PGICCompiler if lang == 'c' else PGICPPCompiler
linker = PGIDynamicLinker(compiler, for_machine, 'pgi', cls.LINKER_PREFIX, version=version) linker = PGIDynamicLinker(compiler, for_machine, 'pgi', cls.LINKER_PREFIX, [], version=version)
return cls( return cls(
ccache + compiler, version, for_machine, is_cross, ccache + compiler, version, for_machine, is_cross,
info, exe_wrap, linker=linker) info, exe_wrap, linker=linker)
if '(ICC)' in out: if '(ICC)' in out:
cls = IntelCCompiler if lang == 'c' else IntelCPPCompiler cls = IntelCCompiler if lang == 'c' else IntelCPPCompiler
if self.machines[for_machine].is_darwin(): if self.machines[for_machine].is_darwin():
l = XildAppleDynamicLinker(compiler, for_machine, 'xild', cls.LINKER_PREFIX, version=version) l = AppleDynamicLinker(compiler, for_machine, 'APPLE ld', cls.LINKER_PREFIX, [], version=version)
else: else:
l = XildLinuxDynamicLinker(compiler, for_machine, 'xild', cls.LINKER_PREFIX, version=version) l = self._guess_nix_linker(compiler, cls, for_machine)
return cls( return cls(
ccache + compiler, version, for_machine, is_cross, info, ccache + compiler, version, for_machine, is_cross, info,
exe_wrap, full_version=full_version, linker=l) exe_wrap, full_version=full_version, linker=l)
@ -1113,7 +1145,7 @@ class Environment:
version = self.get_gnu_version_from_defines(defines) version = self.get_gnu_version_from_defines(defines)
cls = GnuFortranCompiler cls = GnuFortranCompiler
linker = self._guess_nix_linker( linker = self._guess_nix_linker(
compiler, for_machine, cls.LINKER_PREFIX) compiler, cls, for_machine)
return cls( return cls(
compiler, version, for_machine, is_cross, info, compiler, version, for_machine, is_cross, info,
exe_wrap, defines, full_version=full_version, exe_wrap, defines, full_version=full_version,
@ -1121,7 +1153,7 @@ class Environment:
if 'G95' in out: if 'G95' in out:
linker = self._guess_nix_linker( linker = self._guess_nix_linker(
compiler, for_machine, cls.LINKER_PREFIX) compiler, cls, for_machine)
return G95FortranCompiler( return G95FortranCompiler(
compiler, version, for_machine, is_cross, info, compiler, version, for_machine, is_cross, info,
exe_wrap, full_version=full_version, linker=linker) exe_wrap, full_version=full_version, linker=linker)
@ -1129,7 +1161,7 @@ class Environment:
if 'Sun Fortran' in err: if 'Sun Fortran' in err:
version = search_version(err) version = search_version(err)
linker = self._guess_nix_linker( linker = self._guess_nix_linker(
compiler, for_machine, cls.LINKER_PREFIX) compiler, cls, for_machine)
return SunFortranCompiler( return SunFortranCompiler(
compiler, version, for_machine, is_cross, info, compiler, version, for_machine, is_cross, info,
exe_wrap, full_version=full_version, linker=linker) exe_wrap, full_version=full_version, linker=linker)
@ -1137,14 +1169,13 @@ class Environment:
if 'Intel(R) Visual Fortran' in err: if 'Intel(R) Visual Fortran' in err:
version = search_version(err) version = search_version(err)
target = 'x86' if 'IA-32' in err else 'x86_64' target = 'x86' if 'IA-32' in err else 'x86_64'
linker = XilinkDynamicLinker(for_machine, version=version) linker = XilinkDynamicLinker(for_machine, [], version=version)
return IntelClFortranCompiler( return IntelClFortranCompiler(
compiler, version, for_machine, is_cross, target, compiler, version, for_machine, is_cross, target,
info, exe_wrap, linker=linker) info, exe_wrap, linker=linker)
if 'ifort (IFORT)' in out: if 'ifort (IFORT)' in out:
linker = XildLinuxDynamicLinker( linker = self._guess_nix_linker(compiler, IntelFortranCompiler, for_machine)
compiler, for_machine, 'xild', IntelFortranCompiler.LINKER_PREFIX, version=version)
return IntelFortranCompiler( return IntelFortranCompiler(
compiler, version, for_machine, is_cross, info, compiler, version, for_machine, is_cross, info,
exe_wrap, full_version=full_version, linker=linker) exe_wrap, full_version=full_version, linker=linker)
@ -1164,21 +1195,21 @@ class Environment:
if 'flang' in out or 'clang' in out: if 'flang' in out or 'clang' in out:
linker = self._guess_nix_linker( linker = self._guess_nix_linker(
compiler, for_machine, FlangFortranCompiler.LINKER_PREFIX) compiler, FlangFortranCompiler, for_machine)
return FlangFortranCompiler( return FlangFortranCompiler(
compiler, version, for_machine, is_cross, info, compiler, version, for_machine, is_cross, info,
exe_wrap, full_version=full_version, linker=linker) exe_wrap, full_version=full_version, linker=linker)
if 'Open64 Compiler Suite' in err: if 'Open64 Compiler Suite' in err:
linker = self._guess_nix_linker( linker = self._guess_nix_linker(
compiler, for_machine, Open64FortranCompiler.LINKER_PREFIX) compiler, Open64FortranCompiler, for_machine)
return Open64FortranCompiler( return Open64FortranCompiler(
compiler, version, for_machine, is_cross, info, compiler, version, for_machine, is_cross, info,
exe_wrap, full_version=full_version, linker=linker) exe_wrap, full_version=full_version, linker=linker)
if 'NAG Fortran' in err: if 'NAG Fortran' in err:
linker = self._guess_nix_linker( linker = self._guess_nix_linker(
compiler, for_machine, NAGFortranCompiler.LINKER_PREFIX) compiler, NAGFortranCompiler, for_machine)
return NAGFortranCompiler( return NAGFortranCompiler(
compiler, version, for_machine, is_cross, info, compiler, version, for_machine, is_cross, info,
exe_wrap, full_version=full_version, linker=linker) exe_wrap, full_version=full_version, linker=linker)
@ -1217,7 +1248,7 @@ class Environment:
continue continue
version = self.get_gnu_version_from_defines(defines) version = self.get_gnu_version_from_defines(defines)
comp = GnuObjCCompiler if objc else GnuObjCPPCompiler comp = GnuObjCCompiler if objc else GnuObjCPPCompiler
linker = self._guess_nix_linker(compiler, for_machine, comp.LINKER_PREFIX) linker = self._guess_nix_linker(compiler, comp, for_machine)
return comp( return comp(
ccache + compiler, version, for_machine, is_cross, info, ccache + compiler, version, for_machine, is_cross, info,
exe_wrap, defines, linker=linker) exe_wrap, defines, linker=linker)
@ -1227,13 +1258,13 @@ class Environment:
if 'windows' in out or self.machines[for_machine].is_windows(): if 'windows' in out or self.machines[for_machine].is_windows():
# If we're in a MINGW context this actually will use a gnu style ld # If we're in a MINGW context this actually will use a gnu style ld
try: try:
linker = self._guess_win_linker(compiler, for_machine, comp.LINKER_PREFIX) linker = self._guess_win_linker(compiler, comp, for_machine)
except MesonException: except MesonException:
pass pass
if not linker: if not linker:
linker = self._guess_nix_linker( linker = self._guess_nix_linker(
compiler, for_machine, comp.LINKER_PREFIX) compiler, comp, for_machine)
return comp( return comp(
ccache + compiler, version, for_machine, ccache + compiler, version, for_machine,
is_cross, info, exe_wrap, linker=linker) is_cross, info, exe_wrap, linker=linker)
@ -1303,6 +1334,10 @@ class Environment:
compilers, ccache, exe_wrap = self._get_compilers('rust', for_machine) compilers, ccache, exe_wrap = self._get_compilers('rust', for_machine)
is_cross = not self.machines.matches_build_machine(for_machine) is_cross = not self.machines.matches_build_machine(for_machine)
info = self.machines[for_machine] info = self.machines[for_machine]
cc = self.detect_c_compiler(for_machine)
is_link_exe = isinstance(cc.linker, VisualStudioLikeLinkerMixin)
for compiler in compilers: for compiler in compilers:
if isinstance(compiler, str): if isinstance(compiler, str):
compiler = [compiler] compiler = [compiler]
@ -1316,19 +1351,42 @@ class Environment:
version = search_version(out) version = search_version(out)
if 'rustc' in out: if 'rustc' in out:
# Chalk up another quirk for rust. There is no way (AFAICT) to # On Linux and mac rustc will invoke gcc (clang for mac
# figure out what linker rustc is using for a non-nightly compiler # presumably) and it can do this windows, for dynamic linking.
# (On nightly you can pass -Z print-link-args). So we're going to # this means the easiest way to C compiler for dynamic linking.
# hard code the linker based on the platform. # figure out what linker to use is to just get the value of the
# Currently gnu ld is used for everything except apple by # C compiler and use that as the basis of the rust linker.
# default, and apple ld is used on mac. # However, there are two things we need to change, if CC is not
# TODO: find some better way to figure this out. # the default use that, and second add the necessary arguments
if self.machines[for_machine].is_darwin(): # to rust to use -fuse-ld
linker = AppleDynamicLinker(
[], for_machine, 'Apple ld', RustCompiler.LINKER_PREFIX) extra_args = {}
always_args = []
if is_link_exe:
compiler.extend(['-C', 'linker={}'.format(cc.linker.exelist[0])])
extra_args['direct'] = True
extra_args['machine'] = cc.linker.machine
elif not ((info.is_darwin() and isinstance(cc, AppleClangCCompiler)) or
isinstance(cc, GnuCCompiler)):
c = cc.exelist[1] if cc.exelist[0].endswith('ccache') else cc.exelist[0]
compiler.extend(['-C', 'linker={}'.format(c)])
value = self.binaries[for_machine].lookup_entry('ld')
if value is not None:
for a in cc.use_linker_args(value[0]):
always_args.extend(['-C', 'link-arg={}'.format(a)])
# This trickery with type() gets us the class of the linker
# so we can initialize a new copy for the Rust Compiler
if is_link_exe:
linker = type(cc.linker)(for_machine, always_args, exelist=cc.linker.exelist,
version=cc.linker.version, **extra_args)
else: else:
linker = GnuDynamicLinker( linker = type(cc.linker)(compiler, for_machine, cc.linker.id, cc.LINKER_PREFIX,
[], for_machine, 'GNU ld', RustCompiler.LINKER_PREFIX) always_args=always_args, version=cc.linker.version,
**extra_args)
return RustCompiler( return RustCompiler(
compiler, version, for_machine, is_cross, info, exe_wrap, compiler, version, for_machine, is_cross, info, exe_wrap,
linker=linker) linker=linker)
@ -1339,10 +1397,11 @@ class Environment:
info = self.machines[for_machine] info = self.machines[for_machine]
# Detect the target architecture, required for proper architecture handling on Windows. # Detect the target architecture, required for proper architecture handling on Windows.
c_compiler = {} # MSVC compiler is required for correct platform detection.
is_msvc = mesonlib.is_windows() and 'VCINSTALLDIR' in os.environ c_compiler = {'c': self.detect_c_compiler(for_machine)}
if is_msvc: is_msvc = isinstance(c_compiler['c'], VisualStudioCCompiler)
c_compiler = {'c': self.detect_c_compiler(for_machine)} # MSVC compiler is required for correct platform detection. if not is_msvc:
c_compiler = {}
arch = detect_cpu_family(c_compiler) arch = detect_cpu_family(c_compiler)
if is_msvc and arch == 'x86': if is_msvc and arch == 'x86':
@ -1372,25 +1431,22 @@ class Environment:
if 'LLVM D compiler' in out: if 'LLVM D compiler' in out:
# LDC seems to require a file # LDC seems to require a file
m = self.machines[for_machine] if info.is_windows() or info.is_cygwin():
if m.is_windows() or m.is_cygwin(): # Getting LDC on windows to give useful linker output when
if is_msvc: # not doing real work is painfully hard. It ships with a
linker = MSVCDynamicLinker(for_machine, version=version) # version of lld-link, so unless we think the user wants
else: # link.exe, just assume that we're going to use lld-link
# Getting LDC on windows to give useful linker output when not # with it.
# doing real work is painfully hard. It ships with a version of linker = self._guess_win_linker(
# lld-link, so just assume that we're going to use lld-link ['link' if is_msvc else 'lld-link'],
# with it. compilers.LLVMDCompiler, for_machine, use_linker_prefix=False)
_, o, _ = Popen_safe(['lld-link.exe', '--version'])
linker = ClangClDynamicLinker(for_machine, version=search_version(o))
else: else:
with tempfile.NamedTemporaryFile(suffix='.d') as f: with tempfile.NamedTemporaryFile(suffix='.d') as f:
# LDC writes an object file to the current working directory. # LDC writes an object file to the current working directory.
# Clean it up. # Clean it up.
objectfile = os.path.basename(f.name)[:-1] + 'o' objectfile = os.path.basename(f.name)[:-1] + 'o'
linker = self._guess_nix_linker( linker = self._guess_nix_linker(
exelist, for_machine, exelist, compilers.LLVMDCompiler, for_machine,
compilers.LLVMDCompiler.LINKER_PREFIX,
extra_args=[f.name]) extra_args=[f.name])
try: try:
os.unlink(objectfile) os.unlink(objectfile)
@ -1401,27 +1457,25 @@ class Environment:
exelist, version, for_machine, info, arch, exelist, version, for_machine, info, arch,
full_version=full_version, linker=linker) full_version=full_version, linker=linker)
elif 'gdc' in out: elif 'gdc' in out:
linker = self._guess_nix_linker(exelist, for_machine, compilers.GnuDCompiler.LINKER_PREFIX) linker = self._guess_nix_linker(exelist, compilers.GnuDCompiler, for_machine)
return compilers.GnuDCompiler( return compilers.GnuDCompiler(
exelist, version, for_machine, info, arch, is_cross, exe_wrap, exelist, version, for_machine, info, arch, is_cross, exe_wrap,
full_version=full_version, linker=linker) full_version=full_version, linker=linker)
elif 'The D Language Foundation' in out or 'Digital Mars' in out: elif 'The D Language Foundation' in out or 'Digital Mars' in out:
# DMD seems to require a file # DMD seems to require a file
m = self.machines[for_machine] if info.is_windows() or info.is_cygwin():
if m.is_windows() or m.is_cygwin():
if is_msvc: if is_msvc:
linker = MSVCDynamicLinker(for_machine, version=version) linker_cmd = ['link']
elif arch == 'x86': elif arch == 'x86':
linker = OptlinkDynamicLinker(for_machine, version=full_version) linker_cmd = ['optlink']
else: else:
# DMD ships with lld-link linker_cmd = ['lld-link']
_, o, _ = Popen_safe(['lld-link.exe', '--version']) linker = self._guess_win_linker(linker_cmd, compilers.DmdDCompiler, for_machine,
linker = ClangClDynamicLinker(for_machine, version=search_version(o)) use_linker_prefix=False)
else: else:
with tempfile.NamedTemporaryFile(suffix='.d') as f: with tempfile.NamedTemporaryFile(suffix='.d') as f:
linker = self._guess_nix_linker( linker = self._guess_nix_linker(
exelist, for_machine, exelist, compilers.DmdDCompiler, for_machine,
compilers.LLVMDCompiler.LINKER_PREFIX,
extra_args=[f.name]) extra_args=[f.name])
return compilers.DmdDCompiler( return compilers.DmdDCompiler(
exelist, version, for_machine, info, arch, exelist, version, for_machine, info, arch,
@ -1447,8 +1501,7 @@ class Environment:
# As for 5.0.1 swiftc *requires* a file to check the linker: # As for 5.0.1 swiftc *requires* a file to check the linker:
with tempfile.NamedTemporaryFile(suffix='.swift') as f: with tempfile.NamedTemporaryFile(suffix='.swift') as f:
linker = self._guess_nix_linker( linker = self._guess_nix_linker(
exelist, for_machine, exelist, compilers.SwiftCompiler, for_machine,
compilers.SwiftCompiler.LINKER_PREFIX,
extra_args=[f.name]) extra_args=[f.name])
return compilers.SwiftCompiler( return compilers.SwiftCompiler(
exelist, version, for_machine, info, is_cross, linker=linker) exelist, version, for_machine, info, is_cross, linker=linker)

@ -247,17 +247,21 @@ class DynamicLinker(metaclass=abc.ABCMeta):
} # type: typing.Dict[str, typing.List[str]] } # type: typing.Dict[str, typing.List[str]]
def _apply_prefix(self, arg: str) -> typing.List[str]: def _apply_prefix(self, arg: str) -> typing.List[str]:
if isinstance(self.prefix_arg, str): if self.prefix_arg is None:
return [arg]
elif isinstance(self.prefix_arg, str):
return [self.prefix_arg + arg] return [self.prefix_arg + arg]
return self.prefix_arg + [arg] return self.prefix_arg + [arg]
def __init__(self, exelist: typing.List[str], for_machine: mesonlib.MachineChoice, def __init__(self, exelist: typing.List[str], for_machine: mesonlib.MachineChoice,
id_: str, prefix_arg: typing.Union[str, typing.List[str]], *, version: str = 'unknown version'): id_: str, prefix_arg: typing.Union[str, typing.List[str]],
always_args: typing.List[str], *, version: str = 'unknown version'):
self.exelist = exelist self.exelist = exelist
self.for_machine = for_machine self.for_machine = for_machine
self.version = version self.version = version
self.id = id_ self.id = id_
self.prefix_arg = prefix_arg self.prefix_arg = prefix_arg
self.always_args = always_args
def __repr__(self) -> str: def __repr__(self) -> str:
return '<{}: v{} `{}`>'.format(type(self).__name__, self.version, ' '.join(self.exelist)) return '<{}: v{} `{}`>'.format(type(self).__name__, self.version, ' '.join(self.exelist))
@ -276,7 +280,7 @@ class DynamicLinker(metaclass=abc.ABCMeta):
return mesonlib.is_windows() return mesonlib.is_windows()
def get_always_args(self) -> typing.List[str]: def get_always_args(self) -> typing.List[str]:
return [] return self.always_args.copy()
def get_lib_prefix(self) -> str: def get_lib_prefix(self) -> str:
return '' return ''
@ -395,6 +399,11 @@ class DynamicLinker(metaclass=abc.ABCMeta):
install_rpath: str) -> typing.List[str]: install_rpath: str) -> typing.List[str]:
return [] return []
def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str,
suffix: str, soversion: str, darwin_versions: typing.Tuple[str, str],
is_shared_module: bool) -> typing.List[str]:
return []
class PosixDynamicLinkerMixin: class PosixDynamicLinkerMixin:
@ -419,8 +428,8 @@ class GnuLikeDynamicLinkerMixin:
"""Mixin class for dynamic linkers that provides gnu-like interface. """Mixin class for dynamic linkers that provides gnu-like interface.
This acts as a base for the GNU linkers (bfd and gold), the Intel Xild This acts as a base for the GNU linkers (bfd and gold), LLVM's lld, and
(which comes with ICC), LLVM's lld, and other linkers like GNU-ld. other linkers like GNU-ld.
""" """
_BUILDTYPE_ARGS = { _BUILDTYPE_ARGS = {
@ -595,7 +604,7 @@ class AppleDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker):
return self._apply_prefix('-undefined,error') return self._apply_prefix('-undefined,error')
def get_always_args(self) -> typing.List[str]: def get_always_args(self) -> typing.List[str]:
return self._apply_prefix('-headerpad_max_install_names') return self._apply_prefix('-headerpad_max_install_names') + super().get_always_args()
def bitcode_args(self) -> typing.List[str]: def bitcode_args(self) -> typing.List[str]:
return self._apply_prefix('-bitcode_bundle') return self._apply_prefix('-bitcode_bundle')
@ -656,26 +665,6 @@ class LLVMDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, Dyna
pass pass
class XildLinuxDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, DynamicLinker):
"""Representation of Intel's Xild linker.
This is only the linux-like linker which dispatches to Gnu ld.
"""
pass
class XildAppleDynamicLinker(AppleDynamicLinker):
"""Representation of Intel's Xild linker.
This is the apple linker, which dispatches to Apple's ld.
"""
pass
class CcrxDynamicLinker(DynamicLinker): class CcrxDynamicLinker(DynamicLinker):
"""Linker for Renesis CCrx compiler.""" """Linker for Renesis CCrx compiler."""
@ -715,7 +704,7 @@ class ArmDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker):
def __init__(self, for_machine: mesonlib.MachineChoice, def __init__(self, for_machine: mesonlib.MachineChoice,
*, version: str = 'unknown version'): *, version: str = 'unknown version'):
super().__init__(['armlink'], for_machine, 'armlink', '', super().__init__(['armlink'], for_machine, 'armlink', '', [],
version=version) version=version)
def get_accepts_rsp(self) -> bool: def get_accepts_rsp(self) -> bool:
@ -802,7 +791,7 @@ class VisualStudioLikeLinkerMixin:
self.machine = machine self.machine = machine
def invoked_by_compiler(self) -> bool: def invoked_by_compiler(self) -> bool:
return self.direct return not self.direct
def get_debug_crt_args(self) -> typing.List[str]: def get_debug_crt_args(self) -> typing.List[str]:
"""Arguments needed to select a debug crt for the linker. """Arguments needed to select a debug crt for the linker.
@ -818,7 +807,7 @@ class VisualStudioLikeLinkerMixin:
return self._apply_prefix('/MACHINE:' + self.machine) + self._apply_prefix('/OUT:' + outputname) return self._apply_prefix('/MACHINE:' + self.machine) + self._apply_prefix('/OUT:' + outputname)
def get_always_args(self) -> typing.List[str]: def get_always_args(self) -> typing.List[str]:
return self._apply_prefix('/nologo') return self._apply_prefix('/nologo') + super().get_always_args()
def get_search_args(self, dirname: str) -> typing.List[str]: def get_search_args(self, dirname: str) -> typing.List[str]:
return self._apply_prefix('/LIBPATH:' + dirname) return self._apply_prefix('/LIBPATH:' + dirname)
@ -853,33 +842,35 @@ class MSVCDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker):
"""Microsoft's Link.exe.""" """Microsoft's Link.exe."""
def __init__(self, for_machine: mesonlib.MachineChoice, *, def __init__(self, for_machine: mesonlib.MachineChoice, always_args: typing.List[str], *,
exelist: typing.Optional[typing.List[str]] = None, exelist: typing.Optional[typing.List[str]] = None,
prefix: typing.Union[str, typing.List[str]] = '', prefix: typing.Union[str, typing.List[str]] = '',
machine: str = 'x86', version: str = 'unknown version'): machine: str = 'x86', version: str = 'unknown version',
direct: bool = True):
super().__init__(exelist or ['link.exe'], for_machine, 'link', super().__init__(exelist or ['link.exe'], for_machine, 'link',
prefix, machine=machine, version=version) prefix, always_args, machine=machine, version=version, direct=direct)
class ClangClDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): class ClangClDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker):
"""Clang's lld-link.exe.""" """Clang's lld-link.exe."""
def __init__(self, for_machine: mesonlib.MachineChoice, *, def __init__(self, for_machine: mesonlib.MachineChoice, always_args: typing.List[str], *,
exelist: typing.Optional[typing.List[str]] = None, exelist: typing.Optional[typing.List[str]] = None,
prefix: typing.Union[str, typing.List[str]] = '', prefix: typing.Union[str, typing.List[str]] = '',
version: str = 'unknown version'): machine: str = 'x86', version: str = 'unknown version',
super().__init__(exelist or ['lld-link.exe'], for_machine, direct: bool = True):
'lld-link', prefix, version=version) super().__init__(exelist or ['lld-link.exe'], for_machine, 'lld-link',
prefix, always_args, machine=machine, version=version, direct=direct)
class XilinkDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): class XilinkDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker):
"""Intel's Xilink.exe.""" """Intel's Xilink.exe."""
def __init__(self, for_machine: mesonlib.MachineChoice, def __init__(self, for_machine: mesonlib.MachineChoice, always_args: typing.List[str],
*, version: str = 'unknown version'): *, version: str = 'unknown version'):
super().__init__(['xilink.exe'], for_machine, 'xilink', '', version=version) super().__init__(['xilink.exe'], for_machine, 'xilink', '', always_args, version=version)
class SolarisDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): class SolarisDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker):
@ -936,7 +927,7 @@ class OptlinkDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker):
*, version: str = 'unknown version'): *, version: str = 'unknown version'):
# Use optlink instead of link so we don't interfer with other link.exe # Use optlink instead of link so we don't interfer with other link.exe
# implementations. # implementations.
super().__init__(['optlink.exe'], for_machine, 'optlink', prefix_arg='', version=version) super().__init__(['optlink.exe'], for_machine, 'optlink', '', [], version=version)
def get_allow_undefined_args(self) -> typing.List[str]: def get_allow_undefined_args(self) -> typing.List[str]:
return [] return []

@ -621,14 +621,13 @@ def has_broken_rustc() -> bool:
mesonlib.windows_proof_rmtree(dirname) mesonlib.windows_proof_rmtree(dirname)
return pc.returncode != 0 return pc.returncode != 0
def should_skip_rust() -> bool: def should_skip_rust(backend: Backend) -> bool:
if not shutil.which('rustc'): if not shutil.which('rustc'):
return True return True
if backend is not Backend.ninja: if backend is not Backend.ninja:
return True return True
if mesonlib.is_windows(): if mesonlib.is_windows() and has_broken_rustc():
if has_broken_rustc(): return True
return True
return False return False
def detect_tests_to_run(only: typing.List[str]) -> typing.List[typing.Tuple[str, typing.List[Path], bool]]: def detect_tests_to_run(only: typing.List[str]) -> typing.List[typing.Tuple[str, typing.List[Path], bool]]:
@ -666,7 +665,7 @@ def detect_tests_to_run(only: typing.List[str]) -> typing.List[typing.Tuple[str,
('java', 'java', backend is not Backend.ninja or mesonlib.is_osx() or not have_java()), ('java', 'java', backend is not Backend.ninja or mesonlib.is_osx() or not have_java()),
('C#', 'csharp', skip_csharp(backend)), ('C#', 'csharp', skip_csharp(backend)),
('vala', 'vala', backend is not Backend.ninja or not shutil.which(os.environ.get('VALAC', 'valac'))), ('vala', 'vala', backend is not Backend.ninja or not shutil.which(os.environ.get('VALAC', 'valac'))),
('rust', 'rust', should_skip_rust()), ('rust', 'rust', should_skip_rust(backend)),
('d', 'd', backend is not Backend.ninja or not have_d_compiler()), ('d', 'd', backend is not Backend.ninja or not have_d_compiler()),
('objective c', 'objc', backend not in (Backend.ninja, Backend.xcode) or not have_objc_compiler()), ('objective c', 'objc', backend not in (Backend.ninja, Backend.xcode) or not have_objc_compiler()),
('objective c++', 'objcpp', backend not in (Backend.ninja, Backend.xcode) or not have_objcpp_compiler()), ('objective c++', 'objcpp', backend not in (Backend.ninja, Backend.xcode) or not have_objcpp_compiler()),

@ -291,26 +291,6 @@ def no_pkgconfig():
shutil.which = old_which shutil.which = old_which
ExternalProgram._search = old_search ExternalProgram._search = old_search
class PatchModule:
'''
Fancy monkey-patching! Whee! Can't use mock.patch because it only
patches in the local namespace.
'''
def __init__(self, func, name, impl):
self.func = func
assert(isinstance(name, str))
self.func_name = name
self.old_impl = None
self.new_impl = impl
def __enter__(self):
self.old_impl = self.func
exec('{} = self.new_impl'.format(self.func_name))
def __exit__(self, *args):
exec('{} = self.old_impl'.format(self.func_name))
class InternalTests(unittest.TestCase): class InternalTests(unittest.TestCase):
@ -442,7 +422,7 @@ class InternalTests(unittest.TestCase):
def test_compiler_args_class_gnuld(self): def test_compiler_args_class_gnuld(self):
cargsfunc = mesonbuild.compilers.CompilerArgs cargsfunc = mesonbuild.compilers.CompilerArgs
## Test --start/end-group ## Test --start/end-group
linker = mesonbuild.linkers.GnuDynamicLinker([], MachineChoice.HOST, 'fake', '-Wl,') linker = mesonbuild.linkers.GnuDynamicLinker([], MachineChoice.HOST, 'fake', '-Wl,', [])
gcc = mesonbuild.compilers.GnuCCompiler([], 'fake', False, MachineChoice.HOST, mock.Mock(), linker=linker) gcc = mesonbuild.compilers.GnuCCompiler([], 'fake', False, MachineChoice.HOST, mock.Mock(), linker=linker)
## Ensure that the fake compiler is never called by overriding the relevant function ## Ensure that the fake compiler is never called by overriding the relevant function
gcc.get_default_include_dirs = lambda: ['/usr/include', '/usr/share/include', '/usr/local/include'] gcc.get_default_include_dirs = lambda: ['/usr/include', '/usr/share/include', '/usr/local/include']
@ -471,7 +451,7 @@ class InternalTests(unittest.TestCase):
def test_compiler_args_remove_system(self): def test_compiler_args_remove_system(self):
cargsfunc = mesonbuild.compilers.CompilerArgs cargsfunc = mesonbuild.compilers.CompilerArgs
## Test --start/end-group ## Test --start/end-group
linker = mesonbuild.linkers.GnuDynamicLinker([], MachineChoice.HOST, 'fake', '-Wl,') linker = mesonbuild.linkers.GnuDynamicLinker([], MachineChoice.HOST, 'fake', '-Wl,', [])
gcc = mesonbuild.compilers.GnuCCompiler([], 'fake', False, MachineChoice.HOST, mock.Mock(), linker=linker) gcc = mesonbuild.compilers.GnuCCompiler([], 'fake', False, MachineChoice.HOST, mock.Mock(), linker=linker)
## Ensure that the fake compiler is never called by overriding the relevant function ## Ensure that the fake compiler is never called by overriding the relevant function
gcc.get_default_include_dirs = lambda: ['/usr/include', '/usr/share/include', '/usr/local/include'] gcc.get_default_include_dirs = lambda: ['/usr/include', '/usr/share/include', '/usr/local/include']
@ -2316,11 +2296,11 @@ class AllPlatformTests(BasePlatformTests):
if isinstance(cc, intel): if isinstance(cc, intel):
self.assertIsInstance(linker, ar) self.assertIsInstance(linker, ar)
if is_osx(): if is_osx():
self.assertIsInstance(cc.linker, mesonbuild.linkers.XildAppleDynamicLinker) self.assertIsInstance(cc.linker, mesonbuild.linkers.AppleDynamicLinker)
elif is_windows(): elif is_windows():
self.assertIsInstance(cc.linker, mesonbuild.linkers.XilinkDynamicLinker) self.assertIsInstance(cc.linker, mesonbuild.linkers.XilinkDynamicLinker)
else: else:
self.assertIsInstance(cc.linker, mesonbuild.linkers.XildLinuxDynamicLinker) self.assertIsInstance(cc.linker, mesonbuild.linkers.GnuDynamicLinker)
if isinstance(cc, msvc): if isinstance(cc, msvc):
self.assertTrue(is_windows()) self.assertTrue(is_windows())
self.assertIsInstance(linker, lib) self.assertIsInstance(linker, lib)
@ -4558,6 +4538,29 @@ class WindowsTests(BasePlatformTests):
self.assertTrue('prog.pdb' in files) self.assertTrue('prog.pdb' in files)
def _check_ld(self, name: str, lang: str, expected: str) -> None:
if not shutil.which(name):
raise unittest.SkipTest('Could not find {}.'.format(name))
with mock.patch.dict(os.environ, {'LD': name}):
env = get_fake_env()
try:
comp = getattr(env, 'detect_{}_compiler'.format(lang))(MachineChoice.HOST)
except EnvironmentException:
raise unittest.SkipTest('Could not find a compiler for {}'.format(lang))
self.assertEqual(comp.linker.id, expected)
def test_link_environment_variable_lld_link(self):
self._check_ld('lld-link', 'c', 'lld-link')
def test_link_environment_variable_link(self):
self._check_ld('link', 'c', 'link')
def test_link_environment_variable_optlink(self):
self._check_ld('optlink', 'c', 'optlink')
def test_link_environment_variable_rust(self):
self._check_ld('link', 'rust', 'link')
@unittest.skipUnless(is_osx(), "requires Darwin") @unittest.skipUnless(is_osx(), "requires Darwin")
class DarwinTests(BasePlatformTests): class DarwinTests(BasePlatformTests):
''' '''
@ -5785,6 +5788,44 @@ c = ['{0}']
self.build() self.build()
self.run_tests() self.run_tests()
def _check_ld(self, check: str, name: str, lang: str, expected: str) -> None:
if is_sunos():
raise unittest.SkipTest('Solaris currently cannot override the linker.')
if not shutil.which(check):
raise unittest.SkipTest('Could not find {}.'.format(check))
with mock.patch.dict(os.environ, {'LD': name}):
env = get_fake_env()
comp = getattr(env, 'detect_{}_compiler'.format(lang))(MachineChoice.HOST)
if lang != 'rust' and comp.use_linker_args('foo') == []:
raise unittest.SkipTest(
'Compiler {} does not support using alternative linkers'.format(comp.id))
self.assertEqual(comp.linker.id, expected)
def test_ld_environment_variable_bfd(self):
self._check_ld('ld.bfd', 'bfd', 'c', 'GNU ld.bfd')
def test_ld_environment_variable_gold(self):
self._check_ld('ld.gold', 'gold', 'c', 'GNU ld.gold')
def test_ld_environment_variable_lld(self):
self._check_ld('ld.lld', 'lld', 'c', 'lld')
def test_ld_environment_variable_rust(self):
self._check_ld('ld.gold', 'gold', 'rust', 'GNU ld.gold')
def test_ld_environment_variable_cpp(self):
self._check_ld('ld.gold', 'gold', 'cpp', 'GNU ld.gold')
def test_ld_environment_variable_objc(self):
self._check_ld('ld.gold', 'gold', 'objc', 'GNU ld.gold')
def test_ld_environment_variable_objcpp(self):
self._check_ld('ld.gold', 'gold', 'objcpp', 'GNU ld.gold')
def test_ld_environment_variable_fortran(self):
self._check_ld('ld.gold', 'gold', 'fortran', 'GNU ld.gold')
def should_run_cross_arm_tests(): def should_run_cross_arm_tests():
return shutil.which('arm-linux-gnueabihf-gcc') and not platform.machine().lower().startswith('arm') return shutil.which('arm-linux-gnueabihf-gcc') and not platform.machine().lower().startswith('arm')

Loading…
Cancel
Save