diff options
Diffstat (limited to 'mesonbuild/linkers/linkers.py')
-rw-r--r-- | mesonbuild/linkers/linkers.py | 1556 |
1 files changed, 1556 insertions, 0 deletions
diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py new file mode 100644 index 0000000..5799caf --- /dev/null +++ b/mesonbuild/linkers/linkers.py @@ -0,0 +1,1556 @@ +# Copyright 2012-2022 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. +from __future__ import annotations + +import abc +import enum +import os +import typing as T + +from .. import mesonlib +from ..mesonlib import EnvironmentException, MesonException +from ..arglist import CompilerArgs + +if T.TYPE_CHECKING: + from ..coredata import KeyedOptionDictType + from ..environment import Environment + from ..mesonlib import MachineChoice + + +@enum.unique +class RSPFileSyntax(enum.Enum): + + """Which RSP file syntax the compiler supports.""" + + MSVC = enum.auto() + GCC = enum.auto() + + +class StaticLinker: + + id: str + + def __init__(self, exelist: T.List[str]): + self.exelist = exelist + + def compiler_args(self, args: T.Optional[T.Iterable[str]] = None) -> CompilerArgs: + return CompilerArgs(self, args) + + 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: 'KeyedOptionDictType') -> 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, env: 'Environment', is_thin: bool) -> 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: T.Tuple[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: 'KeyedOptionDictType') -> 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 [] + + def rsp_file_syntax(self) -> RSPFileSyntax: + """The format of the RSP file that this compiler supports. + + If `self.can_linker_accept_rsp()` returns True, then this needs to + be implemented + """ + assert not self.can_linker_accept_rsp(), f'{self.id} linker accepts RSP, but doesn\' provide a supported format, this is a bug' + raise EnvironmentException(f'{self.id} does not implement rsp format, this shouldn\'t be called') + + +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.c 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.c import VisualStudioCCompiler + return VisualStudioCCompiler.native_args_to_unix(args) + + def rsp_file_syntax(self) -> RSPFileSyntax: + return RSPFileSyntax.MSVC + + +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 ArLikeLinker(StaticLinker): + # POSIX requires supporting the dash, GNU permits omitting it + std_args = ['-csr'] + + def can_linker_accept_rsp(self) -> bool: + # armar / AIX can't accept arguments using the @rsp syntax + # in fact, only the 'ar' id can + return False + + def get_std_link_args(self, env: 'Environment', is_thin: bool) -> T.List[str]: + return self.std_args + + def get_output_args(self, target: str) -> T.List[str]: + return [target] + + def rsp_file_syntax(self) -> RSPFileSyntax: + return RSPFileSyntax.GCC + + +class ArLinker(ArLikeLinker): + id = 'ar' + + def __init__(self, for_machine: mesonlib.MachineChoice, exelist: T.List[str]): + super().__init__(exelist) + stdo = mesonlib.Popen_safe(self.exelist + ['-h'])[1] + # Enable deterministic builds if they are available. + stdargs = 'csr' + thinargs = '' + if '[D]' in stdo: + stdargs += 'D' + if '[T]' in stdo: + thinargs = 'T' + self.std_args = [stdargs] + self.std_thin_args = [stdargs + thinargs] + self.can_rsp = '@<' in stdo + self.for_machine = for_machine + + def can_linker_accept_rsp(self) -> bool: + return self.can_rsp + + def get_std_link_args(self, env: 'Environment', is_thin: bool) -> T.List[str]: + # Thin archives are a GNU extension not supported by the system linkers + # on Mac OS X, Solaris, or illumos, so don't build them on those OSes. + # OS X ld rejects with: "file built for unknown-unsupported file format" + # illumos/Solaris ld rejects with: "unknown file type" + if is_thin and not env.machines[self.for_machine].is_darwin() \ + and not env.machines[self.for_machine].is_sunos(): + return self.std_thin_args + else: + return self.std_args + + +class AppleArLinker(ArLinker): + + # mostly this is used to determine that we need to call ranlib + + id = 'applear' + + +class ArmarLinker(ArLikeLinker): + id = 'armar' + + +class DLinker(StaticLinker): + def __init__(self, exelist: T.List[str], arch: str, *, rsp_syntax: RSPFileSyntax = RSPFileSyntax.GCC): + super().__init__(exelist) + self.id = exelist[0] + self.arch = arch + self.__rsp_syntax = rsp_syntax + + def get_std_link_args(self, env: 'Environment', is_thin: bool) -> 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 [] + + def rsp_file_syntax(self) -> RSPFileSyntax: + return self.__rsp_syntax + + +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 [f'-output={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 [f'{target}'] + + def get_linker_always_args(self) -> T.List[str]: + return ['rcs'] + +class CompCertLinker(StaticLinker): + + def __init__(self, exelist: T.List[str]): + super().__init__(exelist) + self.id = 'ccomp' + + def can_linker_accept_rsp(self) -> bool: + return False + + def get_output_args(self, target: str) -> T.List[str]: + return [f'-o{target}'] + + +class TILinker(StaticLinker): + + def __init__(self, exelist: T.List[str]): + super().__init__(exelist) + self.id = 'ti-ar' + + def can_linker_accept_rsp(self) -> bool: + return False + + def get_output_args(self, target: str) -> T.List[str]: + return [f'{target}'] + + def get_linker_always_args(self) -> T.List[str]: + return ['-r'] + + +class C2000Linker(TILinker): + # Required for backwards compat with projects created before ti-cgt support existed + id = 'ar2000' + + +class AIXArLinker(ArLikeLinker): + id = 'aixar' + std_args = ['-csr', '-Xany'] + + +def prepare_rpaths(raw_rpaths: T.Tuple[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 DynamicLinker(metaclass=abc.ABCMeta): + + """Base class for dynamic linkers.""" + + _BUILDTYPE_ARGS = { + 'plain': [], + 'debug': [], + 'debugoptimized': [], + 'release': [], + 'minsize': [], + 'custom': [], + } # type: T.Dict[str, T.List[str]] + + @abc.abstractproperty + def id(self) -> str: + pass + + 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, 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.prefix_arg = prefix_arg + self.always_args = always_args + self.machine = None # type: T.Optional[str] + + 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 f'({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 rsp_file_syntax(self) -> RSPFileSyntax: + """The format of the RSP file that this compiler supports. + + If `self.can_linker_accept_rsp()` returns True, then this needs to + be implemented + """ + return RSPFileSyntax.GCC + + 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: 'KeyedOptionDictType') -> T.List[str]: + return [] + + def has_multi_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]: + raise EnvironmentException(f'Language {self.id} does not support has_multi_link_arguments.') + + 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: 'KeyedOptionDictType') -> 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. + raise EnvironmentException(f'Linker {self.id} does not support position-independent executable') + + def get_lto_args(self) -> T.List[str]: + return [] + + def get_thinlto_cache_args(self, path: str) -> 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 EnvironmentException( + f'Linker {self.id} does not support link_whole') + + def get_allow_undefined_args(self) -> T.List[str]: + raise EnvironmentException( + f'Linker {self.id} does not support allow undefined') + + @abc.abstractmethod + def get_output_args(self, outname: str) -> T.List[str]: + pass + + def get_coverage_args(self) -> T.List[str]: + raise EnvironmentException(f"Linker {self.id} doesn't implement coverage data generation.") + + @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 get_gui_app_args(self, value: bool) -> T.List[str]: + # Only used by VisualStudioLikeLinkers + return [] + + def get_win_subsystem_args(self, value: str) -> T.List[str]: + # Only used if supported by the dynamic linker and + # only when targeting Windows + return [] + + def bitcode_args(self) -> T.List[str]: + raise MesonException('This linker does not support bitcode bundles') + + def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, + rpath_paths: T.Tuple[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]) -> 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. + """ + + if T.TYPE_CHECKING: + for_machine = MachineChoice.HOST + def _apply_prefix(self, arg: T.Union[str, T.List[str]]) -> T.List[str]: ... + + _BUILDTYPE_ARGS = { + 'plain': [], + 'debug': [], + 'debugoptimized': [], + 'release': ['-O1'], + 'minsize': [], + 'custom': [], + } # type: T.Dict[str, T.List[str]] + + _SUBSYSTEMS = { + "native": "1", + "windows": "windows", + "console": "console", + "posix": "7", + "efi_application": "10", + "efi_boot_service_driver": "11", + "efi_runtime_driver": "12", + "efi_rom": "13", + "boot_application": "16", + } # type: T.Dict[str, 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]) -> 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(f'-soname,{prefix}{shlib_name}.{suffix}{sostr}') + + def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, + rpath_paths: T.Tuple[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) + + def get_win_subsystem_args(self, value: str) -> T.List[str]: + # MinGW only directly supports a couple of the possible + # PE application types. The raw integer works as an argument + # as well, and is always accepted, so we manually map the + # other types here. List of all types: + # https://github.com/wine-mirror/wine/blob/3ded60bd1654dc689d24a23305f4a93acce3a6f2/include/winnt.h#L2492-L2507 + versionsuffix = None + if ',' in value: + value, versionsuffix = value.split(',', 1) + newvalue = self._SUBSYSTEMS.get(value) + if newvalue is not None: + if versionsuffix is not None: + newvalue += f':{versionsuffix}' + args = [f'--subsystem,{newvalue}'] + else: + raise mesonlib.MesonBugException(f'win_subsystem: {value!r} not handled in MinGW linker. This should not be possible.') + + return self._apply_prefix(args) + + +class AppleDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): + + """Apple's ld implementation.""" + + id = 'ld64' + + 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: 'KeyedOptionDictType') -> T.List[str]: + return ['-bundle'] + self._apply_prefix('-undefined,dynamic_lookup') + + def get_pie_args(self) -> T.List[str]: + return [] + + 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]) -> T.List[str]: + 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: T.Tuple[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()) + + def get_thinlto_cache_args(self, path: str) -> T.List[str]: + return ["-Wl,-cache_path_lto," + path] + + +class LLVMLD64DynamicLinker(AppleDynamicLinker): + + id = 'ld64.lld' + + +class GnuDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, DynamicLinker): + + """Representation of GNU ld.bfd and ld.gold.""" + + def get_accepts_rsp(self) -> bool: + return True + + +class GnuGoldDynamicLinker(GnuDynamicLinker): + + id = 'ld.gold' + + def get_thinlto_cache_args(self, path: str) -> T.List[str]: + return ['-Wl,-plugin-opt,cache-dir=' + path] + + +class GnuBFDDynamicLinker(GnuDynamicLinker): + + id = 'ld.bfd' + + +class MoldDynamicLinker(GnuDynamicLinker): + + id = 'ld.mold' + + def get_thinlto_cache_args(self, path: str) -> T.List[str]: + return ['-Wl,--thinlto-cache-dir=' + path] + + +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. + """ + + id = 'ld.lld' + + def __init__(self, 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'): + super().__init__(exelist, for_machine, prefix_arg, always_args, version=version) + + # Some targets don't seem to support this argument (windows, wasm, ...) + _, _, e = mesonlib.Popen_safe(self.exelist + always_args + self._apply_prefix('--allow-shlib-undefined')) + # Versions < 9 do not have a quoted argument + self.has_allow_shlib_undefined = ('unknown argument: --allow-shlib-undefined' not in e) and ("unknown argument: '--allow-shlib-undefined'" not 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 [] + + def get_thinlto_cache_args(self, path: str) -> T.List[str]: + return ['-Wl,--thinlto-cache-dir=' + path] + + def get_win_subsystem_args(self, value: str) -> T.List[str]: + # lld does not support a numeric subsystem value + version = None + if ',' in value: + value, version = value.split(',', 1) + if value in self._SUBSYSTEMS: + if version is not None: + value += f':{version}' + return self._apply_prefix([f'--subsystem,{value}']) + else: + raise mesonlib.MesonBugException(f'win_subsystem: {value} not handled in lld linker. This should not be possible.') + + +class WASMDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, DynamicLinker): + + """Emscripten's wasm-ld.""" + + id = 'ld.wasm' + + def get_allow_undefined_args(self) -> T.List[str]: + return ['-sERROR_ON_UNDEFINED_SYMBOLS=0'] + + def no_undefined_args(self) -> T.List[str]: + return ['-sERROR_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]) -> T.List[str]: + raise MesonException(f'{self.id} does not support shared libraries.') + + def get_asneeded_args(self) -> T.List[str]: + return [] + + def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, + rpath_paths: T.Tuple[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.""" + + id = 'rlink' + + def __init__(self, for_machine: mesonlib.MachineChoice, + *, version: str = 'unknown version'): + super().__init__(['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 [f'-output={outputname}'] + + def get_search_args(self, dirname: str) -> 'T.NoReturn': + raise OSError('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]) -> T.List[str]: + return [] + + +class Xc16DynamicLinker(DynamicLinker): + + """Linker for Microchip XC16 compiler.""" + + id = 'xc16-gcc' + + def __init__(self, for_machine: mesonlib.MachineChoice, + *, version: str = 'unknown version'): + super().__init__(['xc16-gcc'], 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 [f'-o{outputname}'] + + def get_search_args(self, dirname: str) -> 'T.NoReturn': + raise OSError('xc16-gcc 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]) -> T.List[str]: + return [] + + def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, + rpath_paths: T.Tuple[str, ...], build_rpath: str, + install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: + return ([], set()) + +class CompCertDynamicLinker(DynamicLinker): + + """Linker for CompCert C compiler.""" + + id = 'ccomp' + + def __init__(self, for_machine: mesonlib.MachineChoice, + *, version: str = 'unknown version'): + super().__init__(['ccomp'], 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('-Wl,--whole-archive') + args + self._apply_prefix('-Wl,--no-whole-archive') + + 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 [f'-o{outputname}'] + + def get_search_args(self, dirname: str) -> T.List[str]: + return [f'-L{dirname}'] + + 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]) -> T.List[str]: + raise MesonException(f'{self.id} does not support shared libraries.') + + def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, + rpath_paths: T.Tuple[str, ...], build_rpath: str, + install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: + return ([], set()) + +class TIDynamicLinker(DynamicLinker): + + """Linker for Texas Instruments compiler family.""" + + id = 'ti' + + def __init__(self, exelist: T.List[str], for_machine: mesonlib.MachineChoice, + *, version: str = 'unknown version'): + super().__init__(exelist, 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', f'--output_file={outputname}'] + + def get_search_args(self, dirname: str) -> 'T.NoReturn': + raise OSError('TI compilers do 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 C2000DynamicLinker(TIDynamicLinker): + # Required for backwards compat with projects created before ti-cgt support existed + id = 'cl2000' + + +class ArmDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): + + """Linker for the ARM compiler.""" + + id = 'armlink' + + def __init__(self, for_machine: mesonlib.MachineChoice, + *, version: str = 'unknown version'): + super().__init__(['armlink'], for_machine, '', [], + version=version) + + def get_accepts_rsp(self) -> bool: + return False + + def get_std_shared_lib_args(self) -> 'T.NoReturn': + raise 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 QualcommLLVMDynamicLinker(LLVMDynamicLinker): + + """ARM Linker from Snapdragon LLVM ARM Compiler.""" + + id = 'ld.qcld' + + +class NAGDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): + + """NAG Fortran linker, ld via gcc indirection. + + Using nagfor -Wl,foo passes option foo to a backend gcc invocation. + (This linking gathers the correct objects needed from the nagfor runtime + system.) + To pass gcc -Wl,foo options (i.e., to ld) one must apply indirection + again: nagfor -Wl,-Wl,,foo + """ + + id = 'nag' + + def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, + rpath_paths: T.Tuple[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 = [] + origin_placeholder = '$ORIGIN' + 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('-Wl,-Wl,,-rpath,,' + rp)) + + return (args, set()) + + def get_allow_undefined_args(self) -> T.List[str]: + return [] + + def get_std_shared_lib_args(self) -> T.List[str]: + from ..compilers.fortran import NAGFortranCompiler + return NAGFortranCompiler.get_nagfor_quiet(self.version) + ['-Wl,-shared'] + + +class PGIDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): + + """PGI linker.""" + + id = 'pgi' + + 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]) -> 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: T.Tuple[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()) + +NvidiaHPC_DynamicLinker = PGIDynamicLinker + + +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, env: 'Environment', is_thin: bool) -> T.List[str]: + return self.std_args + + def get_output_args(self, target: str) -> T.List[str]: + return [target] + +NvidiaHPC_StaticLinker = PGIStaticLinker + + +class VisualStudioLikeLinkerMixin: + + """Mixin class for for dynamic linkers that act like Microsoft's link.exe.""" + + if T.TYPE_CHECKING: + for_machine = MachineChoice.HOST + def _apply_prefix(self, arg: T.Union[str, T.List[str]]) -> T.List[str]: ... + + _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, 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', direct: bool = True, machine: str = 'x86'): + # There's no way I can find to make mypy understand what's going on here + super().__init__(exelist, for_machine, prefix_arg, always_args, version=version) # type: ignore + self.machine = machine + self.direct = direct + + 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_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]: + parent = super().get_always_args() # type: ignore + return self._apply_prefix('/nologo') + T.cast('T.List[str]', parent) + + 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]) -> 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]) + + def rsp_file_syntax(self) -> RSPFileSyntax: + return RSPFileSyntax.MSVC + + +class MSVCDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): + + """Microsoft's Link.exe.""" + + id = 'link' + + 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__(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() + + def get_gui_app_args(self, value: bool) -> T.List[str]: + return self.get_win_subsystem_args("windows" if value else "console") + + def get_win_subsystem_args(self, value: str) -> T.List[str]: + return self._apply_prefix([f'/SUBSYSTEM:{value.upper()}']) + + +class ClangClDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): + + """Clang's lld-link.exe.""" + + id = 'lld-link' + + 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__(exelist or ['lld-link.exe'], for_machine, + prefix, always_args, machine=machine, version=version, direct=direct) + + def get_output_args(self, outputname: str) -> T.List[str]: + # If we're being driven indirectly by clang just skip /MACHINE + # as clang's target triple will handle the machine selection + if self.machine is None: + return self._apply_prefix([f"/OUT:{outputname}"]) + + return super().get_output_args(outputname) + + def get_gui_app_args(self, value: bool) -> T.List[str]: + return self.get_win_subsystem_args("windows" if value else "console") + + def get_win_subsystem_args(self, value: str) -> T.List[str]: + return self._apply_prefix([f'/SUBSYSTEM:{value.upper()}']) + + def get_thinlto_cache_args(self, path: str) -> T.List[str]: + return ["/lldltocache:" + path] + + +class XilinkDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): + + """Intel's Xilink.exe.""" + + id = 'xilink' + + 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__(['xilink.exe'], for_machine, '', always_args, version=version) + + def get_gui_app_args(self, value: bool) -> T.List[str]: + return self.get_win_subsystem_args("windows" if value else "console") + + def get_win_subsystem_args(self, value: str) -> T.List[str]: + return self._apply_prefix([f'/SUBSYSTEM:{value.upper()}']) + + +class SolarisDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): + + """Sys-V derived linker used on Solaris and OpenSolaris.""" + + id = 'ld.solaris' + + 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_pie_args(self) -> T.List[str]: + # Available in Solaris 11.2 and later + pc, stdo, stde = mesonlib.Popen_safe(self.exelist + self._apply_prefix('-zhelp')) + for line in (stdo + stde).split('\n'): + if '-z type' in line: + if 'pie' in line: + return ['-z', 'type=pie'] + break + return [] + + def get_asneeded_args(self) -> T.List[str]: + return self._apply_prefix(['-z', 'ignore']) + + 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: T.Tuple[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]) + rpath_dirs_to_remove = set() + for p in all_paths: + rpath_dirs_to_remove.add(p.encode('utf8')) + if build_rpath != '': + all_paths.add(build_rpath) + for p in build_rpath.split(':'): + rpath_dirs_to_remove.add(p.encode('utf8')) + + # 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(f'-rpath,{paths}'), rpath_dirs_to_remove) + + def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str, + suffix: str, soversion: str, darwin_versions: T.Tuple[str, str]) -> T.List[str]: + sostr = '' if soversion is None else '.' + soversion + return self._apply_prefix(f'-soname,{prefix}{shlib_name}.{suffix}{sostr}') + + +class AIXDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): + + """Sys-V derived linker used on AIX""" + + id = 'ld.aix' + + def get_always_args(self) -> T.List[str]: + return self._apply_prefix(['-bnoipath', '-bbigtoc']) + super().get_always_args() + + def no_undefined_args(self) -> T.List[str]: + return self._apply_prefix(['-bernotok']) + + def get_allow_undefined_args(self) -> T.List[str]: + return self._apply_prefix(['-berok']) + + def get_link_whole_for(self, args: T.List[str]) -> T.List[str]: + # AIX's linker always links the whole archive: "The ld command + # processes all input files in the same manner, whether they are + # archives or not." + return args + + def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, + rpath_paths: T.Tuple[str, ...], build_rpath: str, + install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: + all_paths = mesonlib.OrderedSet() # type: mesonlib.OrderedSet[str] + # install_rpath first, followed by other paths, and the system path last + if install_rpath != '': + all_paths.add(install_rpath) + if build_rpath != '': + all_paths.add(build_rpath) + for p in rpath_paths: + all_paths.add(os.path.join(build_dir, p)) + # We should consider allowing the $LIBPATH environment variable + # to override sys_path. + sys_path = env.get_compiler_system_dirs(self.for_machine) + if len(sys_path) == 0: + # get_compiler_system_dirs doesn't support our compiler. + # Use the default system library path + all_paths.update(['/usr/lib', '/lib']) + else: + # Include the compiler's default library paths, but filter out paths that don't exist + for p in sys_path: + if os.path.isdir(p): + all_paths.add(p) + return (self._apply_prefix('-blibpath:' + ':'.join(all_paths)), set()) + + def thread_flags(self, env: 'Environment') -> T.List[str]: + return ['-pthread'] + + +class OptlinkDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): + + """Digital Mars dynamic linker for windows.""" + + id = 'optlink' + + def __init__(self, exelist: T.List[str], for_machine: mesonlib.MachineChoice, + *, version: str = 'unknown version'): + # Use optlink instead of link so we don't interfere with other link.exe + # implementations. + super().__init__(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)""" + + id = 'nvlink' + + @staticmethod + def parse_version() -> str: + 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().rsplit('V', maxsplit=1)[-1] + + def get_accepts_rsp(self) -> bool: + # nvcc does not support response files + return False + + def get_lib_prefix(self) -> str: + # 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. + # + # On Linux, we have to use rely on -Xlinker= too, since nvcc/nvlink chokes on + # versioned shared libraries: + # + # nvcc fatal : Don't know what to do with 'subprojects/foo/libbar.so.0.1.2' + # + from ..compilers.cuda 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]) -> T.List[str]: + return [] |