blob: 1db3948c1c598c1d9823804169ca5066e43a7cb2 [file] [log] [blame]
# SPDX-License-Identifier: Apache-2.0
# Copyright 2012-2022 The Meson development team
from __future__ import annotations
from .. import mlog
from ..mesonlib import (
EnvironmentException,
Popen_safe, Popen_safe_logged, join_args, search_version
)
import re
import shlex
import typing as T
if T.TYPE_CHECKING:
from .linkers import DynamicLinker, GnuDynamicLinker
from ..environment import Environment
from ..compilers import Compiler
from ..mesonlib import MachineChoice
defaults: T.Dict[str, T.List[str]] = {}
defaults['static_linker'] = ['ar', 'gar']
defaults['vs_static_linker'] = ['lib']
defaults['clang_cl_static_linker'] = ['llvm-lib']
defaults['cuda_static_linker'] = ['nvlink']
defaults['gcc_static_linker'] = ['gcc-ar']
defaults['clang_static_linker'] = ['llvm-ar']
def __failed_to_detect_linker(compiler: T.List[str], args: T.List[str], stdout: str, stderr: str) -> 'T.NoReturn':
msg = 'Unable to detect linker for compiler `{}`\nstdout: {}\nstderr: {}'.format(
join_args(compiler + args), stdout, stderr)
raise EnvironmentException(msg)
def guess_win_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Type['Compiler'],
comp_version: str, for_machine: MachineChoice, *,
use_linker_prefix: bool = True, invoked_directly: bool = True,
extra_args: T.Optional[T.List[str]] = None) -> 'DynamicLinker':
from . import linkers
env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)
# Explicitly pass logo here so that we can get the version of link.exe
if not use_linker_prefix or comp_class.LINKER_PREFIX is None:
check_args = ['/logo', '--version']
elif isinstance(comp_class.LINKER_PREFIX, str):
check_args = [comp_class.LINKER_PREFIX + '/logo', comp_class.LINKER_PREFIX + '--version']
else: # list
check_args = comp_class.LINKER_PREFIX + ['/logo'] + comp_class.LINKER_PREFIX + ['--version']
check_args += env.coredata.get_external_link_args(for_machine, comp_class.language)
override: T.List[str] = []
value = env.lookup_binary_entry(for_machine, comp_class.language + '_ld')
if value is not None:
override = comp_class.use_linker_args(value[0], comp_version)
check_args += override
elif 'lld-link' in compiler:
override = comp_class.use_linker_args('lld-link', comp_version)
check_args += override
if extra_args is not None:
check_args.extend(extra_args)
p, o, _ = Popen_safe(compiler + check_args)
if 'LLD' in o.split('\n', maxsplit=1)[0]:
if '(compatible with GNU linkers)' in o:
return linkers.LLVMDynamicLinker(
compiler, for_machine, comp_class.LINKER_PREFIX,
override, version=search_version(o))
elif not invoked_directly:
return linkers.ClangClDynamicLinker(
for_machine, override, exelist=compiler, prefix=comp_class.LINKER_PREFIX,
version=search_version(o), direct=False, machine=None)
if value is not None and invoked_directly:
compiler = value
# We've already handled the non-direct case above
p, o, e = Popen_safe(compiler + check_args)
if 'LLD' in o.split('\n', maxsplit=1)[0]:
return linkers.ClangClDynamicLinker(
for_machine, [],
prefix=comp_class.LINKER_PREFIX if use_linker_prefix else [],
exelist=compiler, version=search_version(o), direct=invoked_directly)
elif 'OPTLINK' in o:
# Optlink's stdout *may* begin with a \r character.
return linkers.OptlinkDynamicLinker(compiler, 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:
target = str(match.group(1))
else:
target = 'x86'
return linkers.MSVCDynamicLinker(
for_machine, [], machine=target, exelist=compiler,
prefix=comp_class.LINKER_PREFIX if use_linker_prefix else [],
version=search_version(out), direct=invoked_directly)
elif 'GNU coreutils' in o:
import shutil
fullpath = shutil.which(compiler[0])
raise EnvironmentException(
f"Found GNU link.exe instead of MSVC link.exe in {fullpath}.\n"
"This link.exe is not a linker.\n"
"You may need to reorder entries to your %PATH% variable to resolve this.")
__failed_to_detect_linker(compiler, check_args, o, e)
def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Type['Compiler'],
comp_version: str, for_machine: MachineChoice, *,
extra_args: T.Optional[T.List[str]] = None) -> 'DynamicLinker':
"""Helper for guessing what linker to use on Unix-Like OSes.
:compiler: Invocation to use to get linker
:comp_class: The Compiler Type (uninstantiated)
:comp_version: The compiler version string
:for_machine: which machine this linker targets
:extra_args: Any additional arguments required (such as a source file)
"""
from . import linkers
env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)
extra_args = extra_args or []
ldflags = env.coredata.get_external_link_args(for_machine, comp_class.language)
extra_args += comp_class._unix_args_to_native(ldflags, env.machines[for_machine])
if isinstance(comp_class.LINKER_PREFIX, str):
check_args = [comp_class.LINKER_PREFIX + '--version'] + extra_args
else:
check_args = comp_class.LINKER_PREFIX + ['--version'] + extra_args
override: T.List[str] = []
value = env.lookup_binary_entry(for_machine, comp_class.language + '_ld')
if value is not None:
override = comp_class.use_linker_args(value[0], comp_version)
check_args += override
mlog.debug('-----')
p, o, e = Popen_safe_logged(compiler + check_args, msg='Detecting linker via')
v = search_version(o + e)
linker: DynamicLinker
if 'LLD' in o.split('\n', maxsplit=1)[0] or 'tiarmlnk' in e:
if isinstance(comp_class.LINKER_PREFIX, str):
cmd = compiler + override + [comp_class.LINKER_PREFIX + '-v'] + extra_args
else:
cmd = compiler + override + comp_class.LINKER_PREFIX + ['-v'] + extra_args
_, newo, newerr = Popen_safe_logged(cmd, msg='Detecting LLD linker via')
lld_cls: T.Type[DynamicLinker]
if 'ld64.lld' in newerr:
lld_cls = linkers.LLVMLD64DynamicLinker
else:
lld_cls = linkers.LLVMDynamicLinker
linker = lld_cls(
compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
elif 'Snapdragon' in e and 'LLVM' in e:
linker = linkers.QualcommLLVMDynamicLinker(
compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
elif e.startswith('lld-link: '):
# The LLD MinGW frontend didn't respond to --version before version 9.0.0,
# and produced an error message about failing to link (when no object
# files were specified), instead of printing the version number.
# Let's try to extract the linker invocation command to grab the version.
_, o, e = Popen_safe(compiler + check_args + ['-v'])
try:
linker_cmd = re.match(r'.*\n(.*?)\nlld-link: ', e, re.DOTALL).group(1)
linker_cmd = shlex.split(linker_cmd)[0]
except (AttributeError, IndexError, ValueError):
pass
else:
_, o, e = Popen_safe([linker_cmd, '--version'])
v = search_version(o)
linker = linkers.LLVMDynamicLinker(compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
# detect xtools first, bug #10805
elif 'xtools-' in o.split('\n', maxsplit=1)[0]:
xtools = o.split(' ', maxsplit=1)[0]
v = xtools.split('-', maxsplit=2)[1]
linker = linkers.AppleDynamicLinker(compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
# First might be apple clang, second is for real gcc, the third is icc.
# Note that "ld: unknown option: " sometimes instead is "ld: unknown options:".
elif e.endswith('(use -v to see invocation)\n') or 'macosx_version' in e or 'ld: unknown option' in e:
if isinstance(comp_class.LINKER_PREFIX, str):
cmd = compiler + [comp_class.LINKER_PREFIX + '-v'] + extra_args
else:
cmd = compiler + comp_class.LINKER_PREFIX + ['-v'] + extra_args
_, newo, newerr = Popen_safe_logged(cmd, msg='Detecting Apple linker via')
for line in newerr.split('\n'):
if 'PROJECT:ld' in line or 'PROJECT:dyld' in line:
v = line.split('-')[1]
break
else:
__failed_to_detect_linker(compiler, check_args, o, e)
linker = linkers.AppleDynamicLinker(compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
elif 'GNU' in o or 'GNU' in e:
gnu_cls: T.Type[GnuDynamicLinker]
# this is always the only thing on stdout, except for swift
# which may or may not redirect the linker stdout to stderr
if o.startswith('GNU gold') or e.startswith('GNU gold'):
gnu_cls = linkers.GnuGoldDynamicLinker
elif o.startswith('mold') or e.startswith('mold'):
gnu_cls = linkers.MoldDynamicLinker
else:
gnu_cls = linkers.GnuBFDDynamicLinker
linker = gnu_cls(compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
elif 'Solaris' in e or 'Solaris' in o:
for line in (o+e).split('\n'):
if 'ld: Software Generation Utilities' in line:
v = line.split(':')[2].lstrip()
break
else:
v = 'unknown version'
linker = linkers.SolarisDynamicLinker(
compiler, for_machine, comp_class.LINKER_PREFIX, override,
version=v)
elif 'ld: 0706-012 The -- flag is not recognized' in e:
if isinstance(comp_class.LINKER_PREFIX, str):
_, _, e = Popen_safe(compiler + [comp_class.LINKER_PREFIX + '-V'] + extra_args)
else:
_, _, e = Popen_safe(compiler + comp_class.LINKER_PREFIX + ['-V'] + extra_args)
linker = linkers.AIXDynamicLinker(
compiler, for_machine, comp_class.LINKER_PREFIX, override,
version=search_version(e))
else:
__failed_to_detect_linker(compiler, check_args, o, e)
return linker