| # SPDX-License-Identifier: Apache-2.0 |
| # Copyright 2012-2022 The Meson development team |
| |
| from __future__ import annotations |
| |
| import abc |
| import os |
| import typing as T |
| import re |
| |
| from .base import ArLikeLinker, RSPFileSyntax |
| 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 |
| |
| |
| 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_optimization_link_args(self, optimization_level: 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, env: Environment) -> 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) -> T.Optional[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 DynamicLinker(metaclass=abc.ABCMeta): |
| |
| """Base class for dynamic linkers.""" |
| |
| _OPTIMIZATION_ARGS: T.Dict[str, T.List[str]] = { |
| 'plain': [], |
| '0': [], |
| 'g': [], |
| '1': [], |
| '2': [], |
| '3': [], |
| 's': [], |
| } |
| |
| @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: T.List[str] = [] |
| 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: T.Optional[str] = None |
| |
| 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) -> T.Optional[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_optimization_link_args(self, optimization_level: str) -> T.List[str]: |
| # We can override these in children by just overriding the |
| # _OPTIMIZATION_ARGS value. |
| return mesonlib.listify([self._apply_prefix(a) for a in self._OPTIMIZATION_ARGS[optimization_level]]) |
| |
| 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_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, outputname: 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_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 [] |
| |
| def get_archive_name(self, filename: str) -> str: |
| #Only used by AIX. |
| return str() |
| |
| def get_command_to_archive_shlib(self) -> T.List[str]: |
| #Only used by AIX. |
| return [] |
| |
| |
| if T.TYPE_CHECKING: |
| StaticLinkerBase = StaticLinker |
| DynamicLinkerBase = DynamicLinker |
| else: |
| StaticLinkerBase = DynamicLinkerBase = object |
| |
| |
| class VisualStudioLikeLinker(StaticLinkerBase): |
| 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: 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.""" |
| |
| id = 'lib' |
| |
| 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.""" |
| |
| id = 'xilib' |
| |
| def __init__(self, exelist: T.List[str], machine: str): |
| StaticLinker.__init__(self, exelist) |
| VisualStudioLikeLinker.__init__(self, machine) |
| |
| |
| class ArLinker(ArLikeLinker, StaticLinker): |
| 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, StaticLinker): |
| 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 C6000Linker(TILinker): |
| id = 'ar6000' |
| |
| |
| class AIXArLinker(ArLikeLinker, StaticLinker): |
| id = 'aixar' |
| std_args = ['-csr', '-Xany'] |
| |
| |
| class MetrowerksStaticLinker(StaticLinker): |
| |
| def can_linker_accept_rsp(self) -> bool: |
| return True |
| |
| def get_linker_always_args(self) -> T.List[str]: |
| return ['-library'] |
| |
| def get_output_args(self, target: str) -> T.List[str]: |
| return ['-o', target] |
| |
| def rsp_file_syntax(self) -> RSPFileSyntax: |
| return RSPFileSyntax.GCC |
| |
| |
| class MetrowerksStaticLinkerARM(MetrowerksStaticLinker): |
| id = 'mwldarm' |
| |
| |
| class MetrowerksStaticLinkerEmbeddedPowerPC(MetrowerksStaticLinker): |
| id = 'mwldeppc' |
| |
| 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 PosixDynamicLinkerMixin(DynamicLinkerBase): |
| |
| """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, outputname: str) -> T.List[str]: |
| return ['-o', outputname] |
| |
| 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(DynamicLinkerBase): |
| |
| """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]: ... |
| |
| _OPTIMIZATION_ARGS: T.Dict[str, T.List[str]] = { |
| 'plain': [], |
| '0': [], |
| 'g': [], |
| '1': [], |
| '2': [], |
| '3': ['-O1'], |
| 's': [], |
| } |
| |
| _SUBSYSTEMS: T.Dict[str, str] = { |
| "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", |
| } |
| |
| 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: T.List[str] = [] |
| 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: T.Set[bytes] = 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: 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]: |
| # We used to emit -undefined,error, but starting with Xcode 15 / |
| # Sonoma, doing so triggers "ld: warning: -undefined error is |
| # deprecated". Given that "-undefined error" is documented to be the |
| # linker's default behaviour, this warning seems ill advised. However, |
| # it does create a lot of noise. As "-undefined error" is the default |
| # behaviour, the least bad way to deal with this seems to be to just |
| # not emit anything here. Of course that only works as long as nothing |
| # else injects -undefined dynamic_lookup, or such. Complain to Apple. |
| return [] |
| |
| 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: T.List[str] = [] |
| # @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] |
| |
| def export_dynamic_args(self, env: 'Environment') -> T.List[str]: |
| return self._apply_prefix('-export_dynamic') |
| |
| |
| 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, ...) |
| self.has_allow_shlib_undefined = self._supports_flag('--allow-shlib-undefined', always_args) |
| # These aren't supported by TI Arm Clang |
| self.has_as_needed = self._supports_flag('--as-needed', always_args) |
| self.has_no_undefined = self._supports_flag('--no-undefined', always_args) |
| |
| def _supports_flag(self, flag: str, always_args: T.List[str]) -> bool: |
| _, _, e = mesonlib.Popen_safe(self.exelist + always_args + self._apply_prefix(flag)) |
| return ( |
| # Versions < 9 do not have a quoted argument |
| (f'unknown argument: {flag}' not in e) and |
| (f"unknown argument: '{flag}'" not in e) and |
| # TI Arm Clang uses a different message |
| (f'invalid option: {flag}' 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_asneeded_args(self) -> T.List[str]: |
| if self.has_as_needed: |
| return self._apply_prefix('--as-needed') |
| return [] |
| |
| def no_undefined_args(self) -> T.List[str]: |
| if self.has_no_undefined: |
| return self._apply_prefix('--no-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 Renesas 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 len(args) < 2: |
| 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 len(args) < 2: |
| 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 C6000DynamicLinker(TIDynamicLinker): |
| id = 'cl6000' |
| |
| |
| 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: T.List[str] = [] |
| 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(DynamicLinkerBase): |
| |
| """Mixin class 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]: ... |
| |
| _OPTIMIZATION_ARGS: T.Dict[str, T.List[str]] = { |
| 'plain': [], |
| '0': [], |
| 'g': [], |
| '1': [], |
| '2': [], |
| # The otherwise implicit REF and ICF linker optimisations are disabled by |
| # /DEBUG. REF implies ICF. |
| '3': ['/OPT:REF'], |
| 's': ['/INCREMENTAL:NO', '/OPT:REF'], |
| } |
| |
| 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) |
| self.machine = machine |
| self.direct = direct |
| |
| 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() |
| return self._apply_prefix('/nologo') + 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: |
| return targetfile |
| |
| 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(['/release']) + super().get_always_args() |
| |
| def get_win_subsystem_args(self, value: str) -> T.List[str]: |
| return self._apply_prefix([f'/SUBSYSTEM:{value.upper()}']) |
| |
| def fatal_warnings(self) -> T.List[str]: |
| return ['-WX'] |
| |
| |
| 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_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] |
| |
| def fatal_warnings(self) -> T.List[str]: |
| return ['-WX'] |
| |
| |
| 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_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: T.Set[bytes] = 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_archive_name(self, filename: str) -> str: |
| # In AIX we allow the shared library name to have the lt_version and so_version. |
| # But the archive name must just be .a . |
| # For Example shared object can have the name libgio.so.0.7200.1 but the archive |
| # must have the name libgio.a having libgio.a (libgio.so.0.7200.1) in the |
| # archive. This regular expression is to do the same. |
| filename = re.sub('[.][a]([.]?([0-9]+))*([.]?([a-z]+))*', '.a', filename.replace('.so', '.a')) |
| return filename |
| |
| def get_command_to_archive_shlib(self) -> T.List[str]: |
| # Archive shared library object and remove the shared library object, |
| # since it already exists in the archive. |
| command = ['ar', '-q', '-v', '$out', '$in', '&&', 'rm', '-f', '$in'] |
| return command |
| |
| 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[str] = mesonlib.OrderedSet() |
| # 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_lib_dirs(self.for_machine) |
| if len(sys_path) == 0: |
| # get_compiler_system_lib_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 [] |
| |
| |
| class MetrowerksLinker(DynamicLinker): |
| |
| def __init__(self, exelist: T.List[str], for_machine: mesonlib.MachineChoice, |
| *, version: str = 'unknown version'): |
| super().__init__(exelist, for_machine, '', [], |
| version=version) |
| |
| def fatal_warnings(self) -> T.List[str]: |
| return ['-w', 'error'] |
| |
| def get_allow_undefined_args(self) -> T.List[str]: |
| return [] |
| |
| def get_accepts_rsp(self) -> bool: |
| return True |
| |
| def get_lib_prefix(self) -> str: |
| return "" |
| |
| def get_linker_always_args(self) -> T.List[str]: |
| return [] |
| |
| def get_output_args(self, outputname: str) -> T.List[str]: |
| return ['-o', outputname] |
| |
| def get_search_args(self, dirname: str) -> T.List[str]: |
| return self._apply_prefix('-L' + dirname) |
| |
| def invoked_by_compiler(self) -> bool: |
| return False |
| |
| def rsp_file_syntax(self) -> RSPFileSyntax: |
| return RSPFileSyntax.GCC |
| |
| |
| class MetrowerksLinkerARM(MetrowerksLinker): |
| id = 'mwldarm' |
| |
| |
| class MetrowerksLinkerEmbeddedPowerPC(MetrowerksLinker): |
| id = 'mwldeppc' |