The Meson Build System
http://mesonbuild.com/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1202 lines
42 KiB
1202 lines
42 KiB
# Copyright 2012-2017 The Meson development team |
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
|
# you may not use this file except in compliance with the License. |
|
# You may obtain a copy of the License at |
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0 |
|
|
|
# Unless required by applicable law or agreed to in writing, software |
|
# distributed under the License is distributed on an "AS IS" BASIS, |
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
# See the License for the specific language governing permissions and |
|
# limitations under the License. |
|
|
|
import abc |
|
import os |
|
import typing as T |
|
|
|
from . import mesonlib |
|
from .envconfig import get_env_var |
|
|
|
if T.TYPE_CHECKING: |
|
from .coredata import OptionDictType |
|
from .environment import Environment |
|
|
|
|
|
class StaticLinker: |
|
|
|
def __init__(self, exelist: T.List[str]): |
|
self.exelist = exelist |
|
|
|
def can_linker_accept_rsp(self) -> bool: |
|
""" |
|
Determines whether the linker can accept arguments using the @rsp syntax. |
|
""" |
|
return mesonlib.is_windows() |
|
|
|
def get_base_link_args(self, options: 'OptionDictType') -> T.List[str]: |
|
"""Like compilers.get_base_link_args, but for the static linker.""" |
|
return [] |
|
|
|
def get_exelist(self) -> T.List[str]: |
|
return self.exelist.copy() |
|
|
|
def get_std_link_args(self) -> T.List[str]: |
|
return [] |
|
|
|
def get_buildtype_linker_args(self, buildtype: str) -> T.List[str]: |
|
return [] |
|
|
|
def get_output_args(self, target: str) -> T.List[str]: |
|
return[] |
|
|
|
def get_coverage_link_args(self) -> T.List[str]: |
|
return [] |
|
|
|
def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, |
|
rpath_paths: str, build_rpath: str, |
|
install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: |
|
return ([], set()) |
|
|
|
def thread_link_flags(self, env: 'Environment') -> T.List[str]: |
|
return [] |
|
|
|
def openmp_flags(self) -> T.List[str]: |
|
return [] |
|
|
|
def get_option_link_args(self, options: 'OptionDictType') -> T.List[str]: |
|
return [] |
|
|
|
@classmethod |
|
def unix_args_to_native(cls, args: T.List[str]) -> T.List[str]: |
|
return args[:] |
|
|
|
@classmethod |
|
def native_args_to_unix(cls, args: T.List[str]) -> T.List[str]: |
|
return args[:] |
|
|
|
def get_link_debugfile_name(self, targetfile: str) -> str: |
|
return None |
|
|
|
def get_link_debugfile_args(self, targetfile: str) -> T.List[str]: |
|
# Static libraries do not have PDB files |
|
return [] |
|
|
|
def get_always_args(self) -> T.List[str]: |
|
return [] |
|
|
|
def get_linker_always_args(self) -> T.List[str]: |
|
return [] |
|
|
|
|
|
class VisualStudioLikeLinker: |
|
always_args = ['/NOLOGO'] |
|
|
|
def __init__(self, machine: str): |
|
self.machine = machine |
|
|
|
def get_always_args(self) -> T.List[str]: |
|
return self.always_args.copy() |
|
|
|
def get_linker_always_args(self) -> T.List[str]: |
|
return self.always_args.copy() |
|
|
|
def get_output_args(self, target: str) -> T.List[str]: |
|
args = [] # type: T.List[str] |
|
if self.machine: |
|
args += ['/MACHINE:' + self.machine] |
|
args += ['/OUT:' + target] |
|
return args |
|
|
|
@classmethod |
|
def unix_args_to_native(cls, args: T.List[str]) -> T.List[str]: |
|
from .compilers import VisualStudioCCompiler |
|
return VisualStudioCCompiler.unix_args_to_native(args) |
|
|
|
@classmethod |
|
def native_args_to_unix(cls, args: T.List[str]) -> T.List[str]: |
|
from .compilers import VisualStudioCCompiler |
|
return VisualStudioCCompiler.native_args_to_unix(args) |
|
|
|
|
|
class VisualStudioLinker(VisualStudioLikeLinker, StaticLinker): |
|
|
|
"""Microsoft's lib static linker.""" |
|
|
|
def __init__(self, exelist: T.List[str], machine: str): |
|
StaticLinker.__init__(self, exelist) |
|
VisualStudioLikeLinker.__init__(self, machine) |
|
|
|
|
|
class IntelVisualStudioLinker(VisualStudioLikeLinker, StaticLinker): |
|
|
|
"""Intel's xilib static linker.""" |
|
|
|
def __init__(self, exelist: T.List[str], machine: str): |
|
StaticLinker.__init__(self, exelist) |
|
VisualStudioLikeLinker.__init__(self, machine) |
|
|
|
|
|
class ArLinker(StaticLinker): |
|
|
|
def __init__(self, exelist: T.List[str]): |
|
super().__init__(exelist) |
|
self.id = 'ar' |
|
pc, stdo = mesonlib.Popen_safe(self.exelist + ['-h'])[0:2] |
|
# Enable deterministic builds if they are available. |
|
if '[D]' in stdo: |
|
self.std_args = ['csrD'] |
|
else: |
|
self.std_args = ['csr'] |
|
self.can_rsp = '@<' in stdo |
|
|
|
def can_linker_accept_rsp(self) -> bool: |
|
return self.can_rsp |
|
|
|
def get_std_link_args(self) -> T.List[str]: |
|
return self.std_args |
|
|
|
def get_output_args(self, target: str) -> T.List[str]: |
|
return [target] |
|
|
|
|
|
class ArmarLinker(ArLinker): # lgtm [py/missing-call-to-init] |
|
|
|
def __init__(self, exelist: T.List[str]): |
|
StaticLinker.__init__(self, exelist) |
|
self.id = 'armar' |
|
self.std_args = ['-csr'] |
|
|
|
def can_linker_accept_rsp(self) -> bool: |
|
# armar can't accept arguments using the @rsp syntax |
|
return False |
|
|
|
|
|
class DLinker(StaticLinker): |
|
def __init__(self, exelist: T.List[str], arch: str): |
|
super().__init__(exelist) |
|
self.id = exelist[0] |
|
self.arch = arch |
|
|
|
def get_std_link_args(self) -> T.List[str]: |
|
return ['-lib'] |
|
|
|
def get_output_args(self, target: str) -> T.List[str]: |
|
return ['-of=' + target] |
|
|
|
def get_linker_always_args(self) -> T.List[str]: |
|
if mesonlib.is_windows(): |
|
if self.arch == 'x86_64': |
|
return ['-m64'] |
|
elif self.arch == 'x86_mscoff' and self.id == 'dmd': |
|
return ['-m32mscoff'] |
|
return ['-m32'] |
|
return [] |
|
|
|
|
|
class CcrxLinker(StaticLinker): |
|
|
|
def __init__(self, exelist: T.List[str]): |
|
super().__init__(exelist) |
|
self.id = 'rlink' |
|
|
|
def can_linker_accept_rsp(self) -> bool: |
|
return False |
|
|
|
def get_output_args(self, target: str) -> T.List[str]: |
|
return ['-output={}'.format(target)] |
|
|
|
def get_linker_always_args(self) -> T.List[str]: |
|
return ['-nologo', '-form=library'] |
|
|
|
|
|
class Xc16Linker(StaticLinker): |
|
|
|
def __init__(self, exelist: T.List[str]): |
|
super().__init__(exelist) |
|
self.id = 'xc16-ar' |
|
|
|
def can_linker_accept_rsp(self) -> bool: |
|
return False |
|
|
|
def get_output_args(self, target: str) -> T.List[str]: |
|
return ['{}'.format(target)] |
|
|
|
def get_linker_always_args(self) -> T.List[str]: |
|
return ['rcs'] |
|
|
|
|
|
class C2000Linker(StaticLinker): |
|
|
|
def __init__(self, exelist: T.List[str]): |
|
super().__init__(exelist) |
|
self.id = 'ar2000' |
|
|
|
def can_linker_accept_rsp(self) -> bool: |
|
return False |
|
|
|
def get_output_args(self, target: str) -> T.List[str]: |
|
return ['{}'.format(target)] |
|
|
|
def get_linker_always_args(self) -> T.List[str]: |
|
return ['-r'] |
|
|
|
|
|
def prepare_rpaths(raw_rpaths: str, build_dir: str, from_dir: str) -> T.List[str]: |
|
# The rpaths we write must be relative if they point to the build dir, |
|
# because otherwise they have different length depending on the build |
|
# directory. This breaks reproducible builds. |
|
internal_format_rpaths = [evaluate_rpath(p, build_dir, from_dir) for p in raw_rpaths] |
|
ordered_rpaths = order_rpaths(internal_format_rpaths) |
|
return ordered_rpaths |
|
|
|
|
|
def order_rpaths(rpath_list: T.List[str]) -> T.List[str]: |
|
# We want rpaths that point inside our build dir to always override |
|
# those pointing to other places in the file system. This is so built |
|
# binaries prefer our libraries to the ones that may lie somewhere |
|
# in the file system, such as /lib/x86_64-linux-gnu. |
|
# |
|
# The correct thing to do here would be C++'s std::stable_partition. |
|
# Python standard library does not have it, so replicate it with |
|
# sort, which is guaranteed to be stable. |
|
return sorted(rpath_list, key=os.path.isabs) |
|
|
|
|
|
def evaluate_rpath(p: str, build_dir: str, from_dir: str) -> str: |
|
if p == from_dir: |
|
return '' # relpath errors out in this case |
|
elif os.path.isabs(p): |
|
return p # These can be outside of build dir. |
|
else: |
|
return os.path.relpath(os.path.join(build_dir, p), os.path.join(build_dir, from_dir)) |
|
|
|
|
|
class LinkerEnvVarsMixin(metaclass=abc.ABCMeta): |
|
|
|
"""Mixin reading LDFLAGS from the environment.""" |
|
|
|
@staticmethod |
|
def get_args_from_envvars(for_machine: mesonlib.MachineChoice, |
|
is_cross: bool) -> T.List[str]: |
|
raw_value = get_env_var(for_machine, is_cross, 'LDFLAGS') |
|
if raw_value is not None: |
|
return mesonlib.split_args(raw_value) |
|
else: |
|
return [] |
|
|
|
class DynamicLinker(LinkerEnvVarsMixin, metaclass=abc.ABCMeta): |
|
|
|
"""Base class for dynamic linkers.""" |
|
|
|
_BUILDTYPE_ARGS = { |
|
'plain': [], |
|
'debug': [], |
|
'debugoptimized': [], |
|
'release': [], |
|
'minsize': [], |
|
'custom': [], |
|
} # type: T.Dict[str, T.List[str]] |
|
|
|
def _apply_prefix(self, arg: T.Union[str, T.List[str]]) -> T.List[str]: |
|
args = [arg] if isinstance(arg, str) else arg |
|
if self.prefix_arg is None: |
|
return args |
|
elif isinstance(self.prefix_arg, str): |
|
return [self.prefix_arg + arg for arg in args] |
|
ret = [] |
|
for arg in args: |
|
ret += self.prefix_arg + [arg] |
|
return ret |
|
|
|
def __init__(self, id_: str, exelist: T.List[str], |
|
for_machine: mesonlib.MachineChoice, prefix_arg: T.Union[str, T.List[str]], |
|
always_args: T.List[str], *, version: str = 'unknown version'): |
|
self.exelist = exelist |
|
self.for_machine = for_machine |
|
self.version = version |
|
self.id = id_ |
|
self.prefix_arg = prefix_arg |
|
self.always_args = always_args |
|
|
|
def __repr__(self) -> str: |
|
return '<{}: v{} `{}`>'.format(type(self).__name__, self.version, ' '.join(self.exelist)) |
|
|
|
def get_id(self) -> str: |
|
return self.id |
|
|
|
def get_version_string(self) -> str: |
|
return '({} {})'.format(self.id, self.version) |
|
|
|
def get_exelist(self) -> T.List[str]: |
|
return self.exelist.copy() |
|
|
|
def get_accepts_rsp(self) -> bool: |
|
# rsp files are only used when building on Windows because we want to |
|
# avoid issues with quoting and max argument length |
|
return mesonlib.is_windows() |
|
|
|
def get_always_args(self) -> T.List[str]: |
|
return self.always_args.copy() |
|
|
|
def get_lib_prefix(self) -> str: |
|
return '' |
|
|
|
# XXX: is use_ldflags a compiler or a linker attribute? |
|
|
|
def get_option_args(self, options: 'OptionDictType') -> T.List[str]: |
|
return [] |
|
|
|
def has_multi_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]: |
|
m = 'Language {} does not support has_multi_link_arguments.' |
|
raise mesonlib.EnvironmentException(m.format(self.id)) |
|
|
|
def get_debugfile_name(self, targetfile: str) -> str: |
|
'''Name of debug file written out (see below)''' |
|
return None |
|
|
|
def get_debugfile_args(self, targetfile: str) -> T.List[str]: |
|
"""Some compilers (MSVC) write debug into a separate file. |
|
|
|
This method takes the target object path and returns a list of |
|
commands to append to the linker invocation to control where that |
|
file is written. |
|
""" |
|
return [] |
|
|
|
def get_std_shared_lib_args(self) -> T.List[str]: |
|
return [] |
|
|
|
def get_std_shared_module_args(self, options: 'OptionDictType') -> T.List[str]: |
|
return self.get_std_shared_lib_args() |
|
|
|
def get_pie_args(self) -> T.List[str]: |
|
# TODO: this really needs to take a boolean and return the args to |
|
# disable pie, otherwise it only acts to enable pie if pie *isn't* the |
|
# default. |
|
m = 'Linker {} does not support position-independent executable' |
|
raise mesonlib.EnvironmentException(m.format(self.id)) |
|
|
|
def get_lto_args(self) -> T.List[str]: |
|
return [] |
|
|
|
def sanitizer_args(self, value: str) -> T.List[str]: |
|
return [] |
|
|
|
def get_buildtype_args(self, buildtype: str) -> T.List[str]: |
|
# We can override these in children by just overriding the |
|
# _BUILDTYPE_ARGS value. |
|
return self._BUILDTYPE_ARGS[buildtype] |
|
|
|
def get_asneeded_args(self) -> T.List[str]: |
|
return [] |
|
|
|
def get_link_whole_for(self, args: T.List[str]) -> T.List[str]: |
|
raise mesonlib.EnvironmentException( |
|
'Linker {} does not support link_whole'.format(self.id)) |
|
|
|
def get_allow_undefined_args(self) -> T.List[str]: |
|
raise mesonlib.EnvironmentException( |
|
'Linker {} does not support allow undefined'.format(self.id)) |
|
|
|
@abc.abstractmethod |
|
def get_output_args(self, outname: str) -> T.List[str]: |
|
pass |
|
|
|
def get_coverage_args(self) -> T.List[str]: |
|
m = "Linker {} doesn't implement coverage data generation.".format(self.id) |
|
raise mesonlib.EnvironmentException(m) |
|
|
|
@abc.abstractmethod |
|
def get_search_args(self, dirname: str) -> T.List[str]: |
|
pass |
|
|
|
def export_dynamic_args(self, env: 'Environment') -> T.List[str]: |
|
return [] |
|
|
|
def import_library_args(self, implibname: str) -> T.List[str]: |
|
"""The name of the outputted import library. |
|
|
|
This implementation is used only on Windows by compilers that use GNU ld |
|
""" |
|
return [] |
|
|
|
def thread_flags(self, env: 'Environment') -> T.List[str]: |
|
return [] |
|
|
|
def no_undefined_args(self) -> T.List[str]: |
|
"""Arguments to error if there are any undefined symbols at link time. |
|
|
|
This is the inverse of get_allow_undefined_args(). |
|
|
|
TODO: A future cleanup might merge this and |
|
get_allow_undefined_args() into a single method taking a |
|
boolean |
|
""" |
|
return [] |
|
|
|
def fatal_warnings(self) -> T.List[str]: |
|
"""Arguments to make all warnings errors.""" |
|
return [] |
|
|
|
def headerpad_args(self) -> T.List[str]: |
|
# Only used by the Apple linker |
|
return [] |
|
|
|
def bitcode_args(self) -> T.List[str]: |
|
raise mesonlib.MesonException('This linker does not support bitcode bundles') |
|
|
|
def get_debug_crt_args(self) -> T.List[str]: |
|
return [] |
|
|
|
def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, |
|
rpath_paths: str, build_rpath: str, |
|
install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: |
|
return ([], set()) |
|
|
|
def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str, |
|
suffix: str, soversion: str, darwin_versions: T.Tuple[str, str], |
|
is_shared_module: bool) -> T.List[str]: |
|
return [] |
|
|
|
|
|
class PosixDynamicLinkerMixin: |
|
|
|
"""Mixin class for POSIX-ish linkers. |
|
|
|
This is obviously a pretty small subset of the linker interface, but |
|
enough dynamic linkers that meson supports are POSIX-like but not |
|
GNU-like that it makes sense to split this out. |
|
""" |
|
|
|
def get_output_args(self, outname: str) -> T.List[str]: |
|
return ['-o', outname] |
|
|
|
def get_std_shared_lib_args(self) -> T.List[str]: |
|
return ['-shared'] |
|
|
|
def get_search_args(self, dirname: str) -> T.List[str]: |
|
return ['-L' + dirname] |
|
|
|
|
|
class GnuLikeDynamicLinkerMixin: |
|
|
|
"""Mixin class for dynamic linkers that provides gnu-like interface. |
|
|
|
This acts as a base for the GNU linkers (bfd and gold), LLVM's lld, and |
|
other linkers like GNU-ld. |
|
""" |
|
|
|
_BUILDTYPE_ARGS = { |
|
'plain': [], |
|
'debug': [], |
|
'debugoptimized': [], |
|
'release': ['-O1'], |
|
'minsize': [], |
|
'custom': [], |
|
} # type: T.Dict[str, T.List[str]] |
|
|
|
def get_buildtype_args(self, buildtype: str) -> T.List[str]: |
|
# We can override these in children by just overriding the |
|
# _BUILDTYPE_ARGS value. |
|
return mesonlib.listify([self._apply_prefix(a) for a in self._BUILDTYPE_ARGS[buildtype]]) |
|
|
|
def get_pie_args(self) -> T.List[str]: |
|
return ['-pie'] |
|
|
|
def get_asneeded_args(self) -> T.List[str]: |
|
return self._apply_prefix('--as-needed') |
|
|
|
def get_link_whole_for(self, args: T.List[str]) -> T.List[str]: |
|
if not args: |
|
return args |
|
return self._apply_prefix('--whole-archive') + args + self._apply_prefix('--no-whole-archive') |
|
|
|
def get_allow_undefined_args(self) -> T.List[str]: |
|
return self._apply_prefix('--allow-shlib-undefined') |
|
|
|
def get_lto_args(self) -> T.List[str]: |
|
return ['-flto'] |
|
|
|
def sanitizer_args(self, value: str) -> T.List[str]: |
|
if value == 'none': |
|
return [] |
|
return ['-fsanitize=' + value] |
|
|
|
def get_coverage_args(self) -> T.List[str]: |
|
return ['--coverage'] |
|
|
|
def export_dynamic_args(self, env: 'Environment') -> T.List[str]: |
|
m = env.machines[self.for_machine] |
|
if m.is_windows() or m.is_cygwin(): |
|
return self._apply_prefix('--export-all-symbols') |
|
return self._apply_prefix('-export-dynamic') |
|
|
|
def import_library_args(self, implibname: str) -> T.List[str]: |
|
return self._apply_prefix('--out-implib=' + implibname) |
|
|
|
def thread_flags(self, env: 'Environment') -> T.List[str]: |
|
if env.machines[self.for_machine].is_haiku(): |
|
return [] |
|
return ['-pthread'] |
|
|
|
def no_undefined_args(self) -> T.List[str]: |
|
return self._apply_prefix('--no-undefined') |
|
|
|
def fatal_warnings(self) -> T.List[str]: |
|
return self._apply_prefix('--fatal-warnings') |
|
|
|
def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str, |
|
suffix: str, soversion: str, darwin_versions: T.Tuple[str, str], |
|
is_shared_module: bool) -> T.List[str]: |
|
m = env.machines[self.for_machine] |
|
if m.is_windows() or m.is_cygwin(): |
|
# For PE/COFF the soname argument has no effect |
|
return [] |
|
sostr = '' if soversion is None else '.' + soversion |
|
return self._apply_prefix('-soname,{}{}.{}{}'.format(prefix, shlib_name, suffix, sostr)) |
|
|
|
def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, |
|
rpath_paths: str, build_rpath: str, |
|
install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: |
|
m = env.machines[self.for_machine] |
|
if m.is_windows() or m.is_cygwin(): |
|
return ([], set()) |
|
if not rpath_paths and not install_rpath and not build_rpath: |
|
return ([], set()) |
|
args = [] |
|
origin_placeholder = '$ORIGIN' |
|
processed_rpaths = prepare_rpaths(rpath_paths, build_dir, from_dir) |
|
# Need to deduplicate rpaths, as macOS's install_name_tool |
|
# is *very* allergic to duplicate -delete_rpath arguments |
|
# when calling depfixer on installation. |
|
all_paths = mesonlib.OrderedSet([os.path.join(origin_placeholder, p) for p in processed_rpaths]) |
|
rpath_dirs_to_remove = set() |
|
for p in all_paths: |
|
rpath_dirs_to_remove.add(p.encode('utf8')) |
|
# Build_rpath is used as-is (it is usually absolute). |
|
if build_rpath != '': |
|
all_paths.add(build_rpath) |
|
for p in build_rpath.split(':'): |
|
rpath_dirs_to_remove.add(p.encode('utf8')) |
|
|
|
# TODO: should this actually be "for (dragonfly|open)bsd"? |
|
if mesonlib.is_dragonflybsd() or mesonlib.is_openbsd(): |
|
# This argument instructs the compiler to record the value of |
|
# ORIGIN in the .dynamic section of the elf. On Linux this is done |
|
# by default, but is not on dragonfly/openbsd for some reason. Without this |
|
# $ORIGIN in the runtime path will be undefined and any binaries |
|
# linked against local libraries will fail to resolve them. |
|
args.extend(self._apply_prefix('-z,origin')) |
|
|
|
# In order to avoid relinking for RPATH removal, the binary needs to contain just |
|
# enough space in the ELF header to hold the final installation RPATH. |
|
paths = ':'.join(all_paths) |
|
if len(paths) < len(install_rpath): |
|
padding = 'X' * (len(install_rpath) - len(paths)) |
|
if not paths: |
|
paths = padding |
|
else: |
|
paths = paths + ':' + padding |
|
args.extend(self._apply_prefix('-rpath,' + paths)) |
|
|
|
# TODO: should this actually be "for solaris/sunos"? |
|
if mesonlib.is_sunos(): |
|
return (args, rpath_dirs_to_remove) |
|
|
|
# Rpaths to use while linking must be absolute. These are not |
|
# written to the binary. Needed only with GNU ld: |
|
# https://sourceware.org/bugzilla/show_bug.cgi?id=16936 |
|
# Not needed on Windows or other platforms that don't use RPATH |
|
# https://github.com/mesonbuild/meson/issues/1897 |
|
# |
|
# In addition, this linker option tends to be quite long and some |
|
# compilers have trouble dealing with it. That's why we will include |
|
# one option per folder, like this: |
|
# |
|
# -Wl,-rpath-link,/path/to/folder1 -Wl,-rpath,/path/to/folder2 ... |
|
# |
|
# ...instead of just one single looooong option, like this: |
|
# |
|
# -Wl,-rpath-link,/path/to/folder1:/path/to/folder2:... |
|
for p in rpath_paths: |
|
args.extend(self._apply_prefix('-rpath-link,' + os.path.join(build_dir, p))) |
|
|
|
return (args, rpath_dirs_to_remove) |
|
|
|
|
|
class AppleDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): |
|
|
|
"""Apple's ld implementation.""" |
|
|
|
def __init__(self, *args, **kwargs): |
|
super().__init__('ld64', *args, **kwargs) |
|
|
|
def get_asneeded_args(self) -> T.List[str]: |
|
return self._apply_prefix('-dead_strip_dylibs') |
|
|
|
def get_allow_undefined_args(self) -> T.List[str]: |
|
return self._apply_prefix('-undefined,dynamic_lookup') |
|
|
|
def get_std_shared_module_args(self, options: 'OptionDictType') -> T.List[str]: |
|
return ['-bundle'] + self._apply_prefix('-undefined,dynamic_lookup') |
|
|
|
def get_pie_args(self) -> T.List[str]: |
|
return ['-pie'] |
|
|
|
def get_link_whole_for(self, args: T.List[str]) -> T.List[str]: |
|
result = [] # type: T.List[str] |
|
for a in args: |
|
result.extend(self._apply_prefix('-force_load')) |
|
result.append(a) |
|
return result |
|
|
|
def get_coverage_args(self) -> T.List[str]: |
|
return ['--coverage'] |
|
|
|
def sanitizer_args(self, value: str) -> T.List[str]: |
|
if value == 'none': |
|
return [] |
|
return ['-fsanitize=' + value] |
|
|
|
def no_undefined_args(self) -> T.List[str]: |
|
return self._apply_prefix('-undefined,error') |
|
|
|
def headerpad_args(self) -> T.List[str]: |
|
return self._apply_prefix('-headerpad_max_install_names') |
|
|
|
def bitcode_args(self) -> T.List[str]: |
|
return self._apply_prefix('-bitcode_bundle') |
|
|
|
def fatal_warnings(self) -> T.List[str]: |
|
return self._apply_prefix('-fatal_warnings') |
|
|
|
def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str, |
|
suffix: str, soversion: str, darwin_versions: T.Tuple[str, str], |
|
is_shared_module: bool) -> T.List[str]: |
|
if is_shared_module: |
|
return [] |
|
install_name = ['@rpath/', prefix, shlib_name] |
|
if soversion is not None: |
|
install_name.append('.' + soversion) |
|
install_name.append('.dylib') |
|
args = ['-install_name', ''.join(install_name)] |
|
if darwin_versions: |
|
args.extend(['-compatibility_version', darwin_versions[0], |
|
'-current_version', darwin_versions[1]]) |
|
return args |
|
|
|
def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, |
|
rpath_paths: str, build_rpath: str, |
|
install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: |
|
if not rpath_paths and not install_rpath and not build_rpath: |
|
return ([], set()) |
|
args = [] |
|
# @loader_path is the equivalent of $ORIGIN on macOS |
|
# https://stackoverflow.com/q/26280738 |
|
origin_placeholder = '@loader_path' |
|
processed_rpaths = prepare_rpaths(rpath_paths, build_dir, from_dir) |
|
all_paths = mesonlib.OrderedSet([os.path.join(origin_placeholder, p) for p in processed_rpaths]) |
|
if build_rpath != '': |
|
all_paths.add(build_rpath) |
|
for rp in all_paths: |
|
args.extend(self._apply_prefix('-rpath,' + rp)) |
|
|
|
return (args, set()) |
|
|
|
|
|
class GnuDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, DynamicLinker): |
|
|
|
"""Representation of GNU ld.bfd and ld.gold.""" |
|
|
|
def get_accepts_rsp(self) -> bool: |
|
return True; |
|
|
|
|
|
class GnuGoldDynamicLinker(GnuDynamicLinker): |
|
|
|
def __init__(self, *args, **kwargs): |
|
super().__init__('ld.gold', *args, **kwargs) |
|
|
|
|
|
class GnuBFDDynamicLinker(GnuDynamicLinker): |
|
|
|
def __init__(self, *args, **kwargs): |
|
super().__init__('ld.bfd', *args, **kwargs) |
|
|
|
|
|
class LLVMDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, DynamicLinker): |
|
|
|
"""Representation of LLVM's ld.lld linker. |
|
|
|
This is only the gnu-like linker, not the apple like or link.exe like |
|
linkers. |
|
""" |
|
|
|
def __init__(self, *args, **kwargs): |
|
super().__init__('ld.lld', *args, **kwargs) |
|
|
|
# Some targets don't seem to support this argument (windows, wasm, ...) |
|
_, _, e = mesonlib.Popen_safe(self.exelist + self._apply_prefix('--allow-shlib-undefined')) |
|
self.has_allow_shlib_undefined = not ('unknown argument: --allow-shlib-undefined' in e) |
|
|
|
def get_allow_undefined_args(self) -> T.List[str]: |
|
if self.has_allow_shlib_undefined: |
|
return self._apply_prefix('--allow-shlib-undefined') |
|
return [] |
|
|
|
|
|
class WASMDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, DynamicLinker): |
|
|
|
"""Emscripten's wasm-ld.""" |
|
|
|
def __init__(self, *args, **kwargs): |
|
super().__init__('ld.wasm', *args, **kwargs) |
|
|
|
def thread_link_flags(self, env: 'Environment') -> T.List[str]: |
|
args = ['-s', 'USE_PTHREADS=1'] |
|
count = env.coredata.compiler_options[self.for_machine]['{}_thread_count'.format(self.language)].value # type: int |
|
if count: |
|
args.extend(['-s', 'PTHREAD_POOL_SIZE={}'.format(count)]) |
|
return args |
|
|
|
def get_allow_undefined_args(self) -> T.List[str]: |
|
return ['-s', 'ERROR_ON_UNDEFINED_SYMBOLS=0'] |
|
|
|
def no_undefined_args(self) -> T.List[str]: |
|
return ['-s', 'ERROR_ON_UNDEFINED_SYMBOLS=1'] |
|
|
|
def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str, |
|
suffix: str, soversion: str, darwin_versions: T.Tuple[str, str], |
|
is_shared_module: bool) -> T.List[str]: |
|
raise mesonlib.MesonException('{} does not support shared libraries.'.format(self.id)) |
|
|
|
def get_asneeded_args(self) -> T.List[str]: |
|
return [] |
|
|
|
def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, |
|
rpath_paths: str, build_rpath: str, |
|
install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: |
|
return ([], set()) |
|
|
|
|
|
class CcrxDynamicLinker(DynamicLinker): |
|
|
|
"""Linker for Renesis CCrx compiler.""" |
|
|
|
def __init__(self, for_machine: mesonlib.MachineChoice, |
|
*, version: str = 'unknown version'): |
|
super().__init__('rlink', ['rlink.exe'], for_machine, '', [], |
|
version=version) |
|
|
|
def get_accepts_rsp(self) -> bool: |
|
return False |
|
|
|
def get_lib_prefix(self) -> str: |
|
return '-lib=' |
|
|
|
def get_std_shared_lib_args(self) -> T.List[str]: |
|
return [] |
|
|
|
def get_output_args(self, outputname: str) -> T.List[str]: |
|
return ['-output={}'.format(outputname)] |
|
|
|
def get_search_args(self, dirname: str) -> 'T.NoReturn': |
|
raise EnvironmentError('rlink.exe does not have a search dir argument') |
|
|
|
def get_allow_undefined_args(self) -> T.List[str]: |
|
return [] |
|
|
|
def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str, |
|
suffix: str, soversion: str, darwin_versions: T.Tuple[str, str], |
|
is_shared_module: bool) -> T.List[str]: |
|
return [] |
|
|
|
|
|
class Xc16DynamicLinker(DynamicLinker): |
|
|
|
"""Linker for Microchip XC16 compiler.""" |
|
|
|
def __init__(self, for_machine: mesonlib.MachineChoice, |
|
*, version: str = 'unknown version'): |
|
super().__init__('xc16-gcc', ['xc16-gcc.exe'], for_machine, '', [], |
|
version=version) |
|
|
|
def get_link_whole_for(self, args: T.List[str]) -> T.List[str]: |
|
if not args: |
|
return args |
|
return self._apply_prefix('--start-group') + args + self._apply_prefix('--end-group') |
|
|
|
def get_accepts_rsp(self) -> bool: |
|
return False |
|
|
|
def get_lib_prefix(self) -> str: |
|
return '' |
|
|
|
def get_std_shared_lib_args(self) -> T.List[str]: |
|
return [] |
|
|
|
def get_output_args(self, outputname: str) -> T.List[str]: |
|
return ['-o{}'.format(outputname)] |
|
|
|
def get_search_args(self, dirname: str) -> 'T.NoReturn': |
|
raise EnvironmentError('xc16-gcc.exe does not have a search dir argument') |
|
|
|
def get_allow_undefined_args(self) -> T.List[str]: |
|
return [] |
|
|
|
def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str, |
|
suffix: str, soversion: str, darwin_versions: T.Tuple[str, str], |
|
is_shared_module: bool) -> T.List[str]: |
|
return [] |
|
|
|
def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, |
|
rpath_paths: str, build_rpath: str, |
|
install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: |
|
return ([], set()) |
|
|
|
|
|
class C2000DynamicLinker(DynamicLinker): |
|
|
|
"""Linker for Texas Instruments C2000 compiler.""" |
|
|
|
def __init__(self, for_machine: mesonlib.MachineChoice, |
|
*, version: str = 'unknown version'): |
|
super().__init__('cl2000', ['cl2000.exe'], for_machine, '', [], |
|
version=version) |
|
|
|
def get_link_whole_for(self, args: T.List[str]) -> T.List[str]: |
|
if not args: |
|
return args |
|
return self._apply_prefix('--start-group') + args + self._apply_prefix('--end-group') |
|
|
|
def get_accepts_rsp(self) -> bool: |
|
return False |
|
|
|
def get_lib_prefix(self) -> str: |
|
return '-l=' |
|
|
|
def get_std_shared_lib_args(self) -> T.List[str]: |
|
return [] |
|
|
|
def get_output_args(self, outputname: str) -> T.List[str]: |
|
return ['-z', '--output_file={}'.format(outputname)] |
|
|
|
def get_search_args(self, dirname: str) -> 'T.NoReturn': |
|
raise EnvironmentError('cl2000.exe does not have a search dir argument') |
|
|
|
def get_allow_undefined_args(self) -> T.List[str]: |
|
return [] |
|
|
|
def get_always_args(self) -> T.List[str]: |
|
return [] |
|
|
|
|
|
class ArmDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): |
|
|
|
"""Linker for the ARM compiler.""" |
|
|
|
def __init__(self, for_machine: mesonlib.MachineChoice, |
|
*, version: str = 'unknown version'): |
|
super().__init__('armlink', ['armlink'], for_machine, '', [], |
|
version=version) |
|
|
|
def get_accepts_rsp(self) -> bool: |
|
return False |
|
|
|
def get_std_shared_lib_args(self) -> 'T.NoReturn': |
|
raise mesonlib.MesonException('The Arm Linkers do not support shared libraries') |
|
|
|
def get_allow_undefined_args(self) -> T.List[str]: |
|
return [] |
|
|
|
|
|
class ArmClangDynamicLinker(ArmDynamicLinker): |
|
|
|
"""Linker used with ARM's clang fork. |
|
|
|
The interface is similar enough to the old ARM ld that it inherits and |
|
extends a few things as needed. |
|
""" |
|
|
|
def export_dynamic_args(self, env: 'Environment') -> T.List[str]: |
|
return ['--export_dynamic'] |
|
|
|
def import_library_args(self, implibname: str) -> T.List[str]: |
|
return ['--symdefs=' + implibname] |
|
|
|
|
|
class PGIDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): |
|
|
|
"""PGI linker.""" |
|
|
|
def __init__(self, *args, **kwargs): |
|
super().__init__('pgi', *args, **kwargs) |
|
|
|
def get_allow_undefined_args(self) -> T.List[str]: |
|
return [] |
|
|
|
def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str, |
|
suffix: str, soversion: str, darwin_versions: T.Tuple[str, str], |
|
is_shared_module: bool) -> T.List[str]: |
|
return [] |
|
|
|
def get_std_shared_lib_args(self) -> T.List[str]: |
|
# PGI -shared is Linux only. |
|
if mesonlib.is_windows(): |
|
return ['-Bdynamic', '-Mmakedll'] |
|
elif mesonlib.is_linux(): |
|
return ['-shared'] |
|
return [] |
|
|
|
def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, |
|
rpath_paths: str, build_rpath: str, |
|
install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: |
|
if not env.machines[self.for_machine].is_windows(): |
|
return (['-R' + os.path.join(build_dir, p) for p in rpath_paths], set()) |
|
return ([], set()) |
|
|
|
|
|
class PGIStaticLinker(StaticLinker): |
|
def __init__(self, exelist: T.List[str]): |
|
super().__init__(exelist) |
|
self.id = 'ar' |
|
self.std_args = ['-r'] |
|
|
|
def get_std_link_args(self) -> T.List[str]: |
|
return self.std_args |
|
|
|
def get_output_args(self, target: str) -> T.List[str]: |
|
return [target] |
|
|
|
|
|
class VisualStudioLikeLinkerMixin: |
|
|
|
_BUILDTYPE_ARGS = { |
|
'plain': [], |
|
'debug': [], |
|
'debugoptimized': [], |
|
# The otherwise implicit REF and ICF linker optimisations are disabled by |
|
# /DEBUG. REF implies ICF. |
|
'release': ['/OPT:REF'], |
|
'minsize': ['/INCREMENTAL:NO', '/OPT:REF'], |
|
'custom': [], |
|
} # type: T.Dict[str, T.List[str]] |
|
|
|
def __init__(self, *args, direct: bool = True, machine: str = 'x86', **kwargs): |
|
super().__init__(*args, **kwargs) |
|
self.machine = machine |
|
|
|
def get_buildtype_args(self, buildtype: str) -> T.List[str]: |
|
return mesonlib.listify([self._apply_prefix(a) for a in self._BUILDTYPE_ARGS[buildtype]]) |
|
|
|
def invoked_by_compiler(self) -> bool: |
|
return not self.direct |
|
|
|
def get_debug_crt_args(self) -> T.List[str]: |
|
"""Arguments needed to select a debug crt for the linker. |
|
|
|
Sometimes we need to manually select the CRT (C runtime) to use with |
|
MSVC. One example is when trying to link with static libraries since |
|
MSVC won't auto-select a CRT for us in that case and will error out |
|
asking us to select one. |
|
""" |
|
return self._apply_prefix('/MDd') |
|
|
|
def get_output_args(self, outputname: str) -> T.List[str]: |
|
return self._apply_prefix(['/MACHINE:' + self.machine, '/OUT:' + outputname]) |
|
|
|
def get_always_args(self) -> T.List[str]: |
|
return self._apply_prefix('/nologo') + super().get_always_args() |
|
|
|
def get_search_args(self, dirname: str) -> T.List[str]: |
|
return self._apply_prefix('/LIBPATH:' + dirname) |
|
|
|
def get_std_shared_lib_args(self) -> T.List[str]: |
|
return self._apply_prefix('/DLL') |
|
|
|
def get_debugfile_name(self, targetfile: str) -> str: |
|
basename = targetfile.rsplit('.', maxsplit=1)[0] |
|
return basename + '.pdb' |
|
|
|
def get_debugfile_args(self, targetfile: str) -> T.List[str]: |
|
return self._apply_prefix(['/DEBUG', '/PDB:' + self.get_debugfile_name(targetfile)]) |
|
|
|
def get_link_whole_for(self, args: T.List[str]) -> T.List[str]: |
|
# Only since VS2015 |
|
args = mesonlib.listify(args) |
|
l = [] # T.List[str] |
|
for a in args: |
|
l.extend(self._apply_prefix('/WHOLEARCHIVE:' + a)) |
|
return l |
|
|
|
def get_allow_undefined_args(self) -> T.List[str]: |
|
return [] |
|
|
|
def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str, |
|
suffix: str, soversion: str, darwin_versions: T.Tuple[str, str], |
|
is_shared_module: bool) -> T.List[str]: |
|
return [] |
|
|
|
def import_library_args(self, implibname: str) -> T.List[str]: |
|
"""The command to generate the import library.""" |
|
return self._apply_prefix(['/IMPLIB:' + implibname]) |
|
|
|
|
|
class MSVCDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): |
|
|
|
"""Microsoft's Link.exe.""" |
|
|
|
def __init__(self, for_machine: mesonlib.MachineChoice, always_args: T.List[str], *, |
|
exelist: T.Optional[T.List[str]] = None, |
|
prefix: T.Union[str, T.List[str]] = '', |
|
machine: str = 'x86', version: str = 'unknown version', |
|
direct: bool = True): |
|
super().__init__('link', exelist or ['link.exe'], for_machine, |
|
prefix, always_args, machine=machine, version=version, direct=direct) |
|
|
|
def get_always_args(self) -> T.List[str]: |
|
return self._apply_prefix(['/nologo', '/release']) + super().get_always_args() |
|
|
|
|
|
class ClangClDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): |
|
|
|
"""Clang's lld-link.exe.""" |
|
|
|
def __init__(self, for_machine: mesonlib.MachineChoice, always_args: T.List[str], *, |
|
exelist: T.Optional[T.List[str]] = None, |
|
prefix: T.Union[str, T.List[str]] = '', |
|
machine: str = 'x86', version: str = 'unknown version', |
|
direct: bool = True): |
|
super().__init__('lld-link', exelist or ['lld-link.exe'], for_machine, |
|
prefix, always_args, machine=machine, version=version, direct=direct) |
|
|
|
|
|
class XilinkDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): |
|
|
|
"""Intel's Xilink.exe.""" |
|
|
|
def __init__(self, for_machine: mesonlib.MachineChoice, always_args: T.List[str], |
|
*, version: str = 'unknown version'): |
|
super().__init__('xilink', ['xilink.exe'], for_machine, '', always_args, version=version) |
|
|
|
|
|
class SolarisDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): |
|
|
|
"""Sys-V derived linker used on Solaris and OpenSolaris.""" |
|
|
|
def __init__(self, *args, **kwargs): |
|
super().__init__('ld.solaris', *args, **kwargs) |
|
|
|
def get_link_whole_for(self, args: T.List[str]) -> T.List[str]: |
|
if not args: |
|
return args |
|
return self._apply_prefix('--whole-archive') + args + self._apply_prefix('--no-whole-archive') |
|
|
|
def no_undefined_args(self) -> T.List[str]: |
|
return ['-z', 'defs'] |
|
|
|
def get_allow_undefined_args(self) -> T.List[str]: |
|
return ['-z', 'nodefs'] |
|
|
|
def fatal_warnings(self) -> T.List[str]: |
|
return ['-z', 'fatal-warnings'] |
|
|
|
def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, |
|
rpath_paths: str, build_rpath: str, |
|
install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: |
|
if not rpath_paths and not install_rpath and not build_rpath: |
|
return ([], set()) |
|
processed_rpaths = prepare_rpaths(rpath_paths, build_dir, from_dir) |
|
all_paths = mesonlib.OrderedSet([os.path.join('$ORIGIN', p) for p in processed_rpaths]) |
|
if build_rpath != '': |
|
all_paths.add(build_rpath) |
|
|
|
# In order to avoid relinking for RPATH removal, the binary needs to contain just |
|
# enough space in the ELF header to hold the final installation RPATH. |
|
paths = ':'.join(all_paths) |
|
if len(paths) < len(install_rpath): |
|
padding = 'X' * (len(install_rpath) - len(paths)) |
|
if not paths: |
|
paths = padding |
|
else: |
|
paths = paths + ':' + padding |
|
return (self._apply_prefix('-rpath,{}'.format(paths)), set()) |
|
|
|
def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str, |
|
suffix: str, soversion: str, darwin_versions: T.Tuple[str, str], |
|
is_shared_module: bool) -> T.List[str]: |
|
sostr = '' if soversion is None else '.' + soversion |
|
return self._apply_prefix('-soname,{}{}.{}{}'.format(prefix, shlib_name, suffix, sostr)) |
|
|
|
|
|
class OptlinkDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): |
|
|
|
"""Digital Mars dynamic linker for windows.""" |
|
|
|
def __init__(self, exelist: T.List[str], for_machine: mesonlib.MachineChoice, |
|
*, version: str = 'unknown version'): |
|
# Use optlink instead of link so we don't interfer with other link.exe |
|
# implementations. |
|
super().__init__('optlink', exelist, for_machine, '', [], version=version) |
|
|
|
def get_allow_undefined_args(self) -> T.List[str]: |
|
return [] |
|
|
|
def get_debugfile_args(self, targetfile: str) -> T.List[str]: |
|
# Optlink does not generate pdb files. |
|
return [] |
|
|
|
def get_always_args(self) -> T.List[str]: |
|
return [] |
|
|
|
|
|
class CudaLinker(PosixDynamicLinkerMixin, DynamicLinker): |
|
"""Cuda linker (nvlink)""" |
|
|
|
def __init__(self, *args, **kwargs): |
|
super().__init__('nvlink', *args, **kwargs) |
|
|
|
@staticmethod |
|
def parse_version(): |
|
version_cmd = ['nvlink', '--version'] |
|
try: |
|
_, out, _ = mesonlib.Popen_safe(version_cmd) |
|
except OSError: |
|
return 'unknown version' |
|
# Output example: |
|
# nvlink: NVIDIA (R) Cuda linker |
|
# Copyright (c) 2005-2018 NVIDIA Corporation |
|
# Built on Sun_Sep_30_21:09:22_CDT_2018 |
|
# Cuda compilation tools, release 10.0, V10.0.166 |
|
# we need the most verbose version output. Luckily starting with V |
|
return out.strip().split('V')[-1] |
|
|
|
def get_accepts_rsp(self) -> bool: |
|
# nvcc does not support response files |
|
return False |
|
|
|
def get_lib_prefix(self) -> str: |
|
if not mesonlib.is_windows(): |
|
return '' |
|
# nvcc doesn't recognize Meson's default .a extension for static libraries on |
|
# Windows and passes it to cl as an object file, resulting in 'warning D9024 : |
|
# unrecognized source file type 'xxx.a', object file assumed'. |
|
# |
|
# nvcc's --library= option doesn't help: it takes the library name without the |
|
# extension and assumes that the extension on Windows is .lib; prefixing the |
|
# library with -Xlinker= seems to work. |
|
from .compilers import CudaCompiler |
|
return CudaCompiler.LINKER_PREFIX |
|
|
|
def fatal_warnings(self) -> T.List[str]: |
|
return ['--warning-as-error'] |
|
|
|
def get_allow_undefined_args(self) -> T.List[str]: |
|
return [] |
|
|
|
def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str, |
|
suffix: str, soversion: str, darwin_versions: T.Tuple[str, str], |
|
is_shared_module: bool) -> T.List[str]: |
|
return []
|
|
|