diff options
author | Daniel Mensinger <daniel@mensinger-ka.de> | 2021-06-03 21:10:19 +0200 |
---|---|---|
committer | Daniel Mensinger <daniel@mensinger-ka.de> | 2021-06-25 19:34:48 +0200 |
commit | 0f237b8d1d84e4906c758bbef44d81e93207f8e4 (patch) | |
tree | dbcc035687e2a85bad3cbb3e2152edc22fdc5d88 /mesonbuild/linkers.py | |
parent | d3d95d39beebb42894dbcf7cad58ef1fba4bbe2f (diff) | |
download | meson-0f237b8d1d84e4906c758bbef44d81e93207f8e4.zip meson-0f237b8d1d84e4906c758bbef44d81e93207f8e4.tar.gz meson-0f237b8d1d84e4906c758bbef44d81e93207f8e4.tar.bz2 |
linkers: move linkers.py into the linkers package
Diffstat (limited to 'mesonbuild/linkers.py')
-rw-r--r-- | mesonbuild/linkers.py | 1429 |
1 files changed, 0 insertions, 1429 deletions
diff --git a/mesonbuild/linkers.py b/mesonbuild/linkers.py deleted file mode 100644 index 7b938ac..0000000 --- a/mesonbuild/linkers.py +++ /dev/null @@ -1,1429 +0,0 @@ -# Copyright 2012-2017 The Meson development team - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import abc -import enum -import os -import typing as T - -from . import mesonlib -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) -> T.List[str]: - return [] - - def get_buildtype_linker_args(self, buildtype: str) -> T.List[str]: - return [] - - def get_output_args(self, target: str) -> T.List[str]: - return[] - - def get_coverage_link_args(self) -> T.List[str]: - return [] - - def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, - rpath_paths: str, build_rpath: str, - install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: - return ([], set()) - - def thread_link_flags(self, env: 'Environment') -> T.List[str]: - return [] - - def openmp_flags(self) -> T.List[str]: - return [] - - def get_option_link_args(self, options: '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 mesonlib.EnvironmentException(f'{self.id} does not implemnt 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 import VisualStudioCCompiler - return VisualStudioCCompiler.unix_args_to_native(args) - - @classmethod - def native_args_to_unix(cls, args: T.List[str]) -> T.List[str]: - from .compilers import VisualStudioCCompiler - return VisualStudioCCompiler.native_args_to_unix(args) - - 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 ArLinker(StaticLinker): - - def __init__(self, exelist: T.List[str]): - super().__init__(exelist) - self.id = 'ar' - pc, stdo = mesonlib.Popen_safe(self.exelist + ['-h'])[0:2] - # Enable deterministic builds if they are available. - if '[D]' in stdo: - self.std_args = ['csrD'] - else: - self.std_args = ['csr'] - self.can_rsp = '@<' in stdo - - def can_linker_accept_rsp(self) -> bool: - return self.can_rsp - - def get_std_link_args(self) -> T.List[str]: - return self.std_args - - def get_output_args(self, target: str) -> T.List[str]: - return [target] - - def rsp_file_syntax(self) -> RSPFileSyntax: - return RSPFileSyntax.GCC - - -class ArmarLinker(ArLinker): # lgtm [py/missing-call-to-init] - - def __init__(self, exelist: T.List[str]): - StaticLinker.__init__(self, exelist) - self.id = 'armar' - self.std_args = ['-csr'] - - def can_linker_accept_rsp(self) -> bool: - # armar can't accept arguments using the @rsp syntax - return False - - -class DLinker(StaticLinker): - def __init__(self, exelist: T.List[str], arch: str, *, 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) -> 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 C2000Linker(StaticLinker): - - def __init__(self, exelist: T.List[str]): - super().__init__(exelist) - self.id = 'ar2000' - - def can_linker_accept_rsp(self) -> bool: - return False - - def get_output_args(self, target: str) -> T.List[str]: - return [f'{target}'] - - def get_linker_always_args(self) -> T.List[str]: - return ['-r'] - - -class AIXArLinker(ArLinker): - - def __init__(self, exelist: T.List[str]): - StaticLinker.__init__(self, exelist) - self.id = 'aixar' - self.std_args = ['-csr', '-Xany'] - - def can_linker_accept_rsp(self) -> bool: - # AIXAr can't accept arguments using the @rsp syntax - return False - - -def prepare_rpaths(raw_rpaths: str, build_dir: str, from_dir: str) -> T.List[str]: - # The rpaths we write must be relative if they point to the build dir, - # because otherwise they have different length depending on the build - # directory. This breaks reproducible builds. - internal_format_rpaths = [evaluate_rpath(p, build_dir, from_dir) for p in raw_rpaths] - ordered_rpaths = order_rpaths(internal_format_rpaths) - return ordered_rpaths - - -def order_rpaths(rpath_list: T.List[str]) -> T.List[str]: - # We want rpaths that point inside our build dir to always override - # those pointing to other places in the file system. This is so built - # binaries prefer our libraries to the ones that may lie somewhere - # in the file system, such as /lib/x86_64-linux-gnu. - # - # The correct thing to do here would be C++'s std::stable_partition. - # Python standard library does not have it, so replicate it with - # sort, which is guaranteed to be stable. - return sorted(rpath_list, key=os.path.isabs) - - -def evaluate_rpath(p: str, build_dir: str, from_dir: str) -> str: - if p == from_dir: - return '' # relpath errors out in this case - elif os.path.isabs(p): - return p # These can be outside of build dir. - else: - return os.path.relpath(os.path.join(build_dir, p), os.path.join(build_dir, from_dir)) - -class 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]: - m = 'Language {} does not support has_multi_link_arguments.' - raise mesonlib.EnvironmentException(m.format(self.id)) - - def get_debugfile_name(self, targetfile: str) -> str: - '''Name of debug file written out (see below)''' - return None - - def get_debugfile_args(self, targetfile: str) -> T.List[str]: - """Some compilers (MSVC) write debug into a separate file. - - This method takes the target object path and returns a list of - commands to append to the linker invocation to control where that - file is written. - """ - return [] - - def get_std_shared_lib_args(self) -> T.List[str]: - return [] - - def get_std_shared_module_args(self, options: '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. - m = 'Linker {} does not support position-independent executable' - raise mesonlib.EnvironmentException(m.format(self.id)) - - def get_lto_args(self) -> T.List[str]: - return [] - - def sanitizer_args(self, value: str) -> T.List[str]: - return [] - - def get_buildtype_args(self, buildtype: str) -> T.List[str]: - # We can override these in children by just overriding the - # _BUILDTYPE_ARGS value. - return self._BUILDTYPE_ARGS[buildtype] - - def get_asneeded_args(self) -> T.List[str]: - return [] - - def get_link_whole_for(self, args: T.List[str]) -> T.List[str]: - raise mesonlib.EnvironmentException( - f'Linker {self.id} does not support link_whole') - - def get_allow_undefined_args(self) -> T.List[str]: - raise mesonlib.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]: - m = f"Linker {self.id} doesn't implement coverage data generation." - raise mesonlib.EnvironmentException(m) - - @abc.abstractmethod - def get_search_args(self, dirname: str) -> T.List[str]: - pass - - def export_dynamic_args(self, env: 'Environment') -> T.List[str]: - return [] - - def import_library_args(self, implibname: str) -> T.List[str]: - """The name of the outputted import library. - - This implementation is used only on Windows by compilers that use GNU ld - """ - return [] - - def thread_flags(self, env: 'Environment') -> T.List[str]: - return [] - - def no_undefined_args(self) -> T.List[str]: - """Arguments to error if there are any undefined symbols at link time. - - This is the inverse of get_allow_undefined_args(). - - TODO: A future cleanup might merge this and - get_allow_undefined_args() into a single method taking a - boolean - """ - return [] - - def fatal_warnings(self) -> T.List[str]: - """Arguments to make all warnings errors.""" - return [] - - def headerpad_args(self) -> T.List[str]: - # Only used by the Apple linker - return [] - - def 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 mesonlib.MesonException('This linker does not support bitcode bundles') - - def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, - rpath_paths: str, build_rpath: str, - install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: - return ([], set()) - - def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str, - suffix: str, soversion: str, darwin_versions: T.Tuple[str, str], - is_shared_module: bool) -> T.List[str]: - return [] - - -class PosixDynamicLinkerMixin: - - """Mixin class for POSIX-ish linkers. - - This is obviously a pretty small subset of the linker interface, but - enough dynamic linkers that meson supports are POSIX-like but not - GNU-like that it makes sense to split this out. - """ - - def get_output_args(self, outname: str) -> T.List[str]: - return ['-o', outname] - - def get_std_shared_lib_args(self) -> T.List[str]: - return ['-shared'] - - def get_search_args(self, dirname: str) -> T.List[str]: - return ['-L' + dirname] - - -class GnuLikeDynamicLinkerMixin: - - """Mixin class for dynamic linkers that provides gnu-like interface. - - This acts as a base for the GNU linkers (bfd and gold), LLVM's lld, and - other linkers like GNU-ld. - """ - - 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]] - - def get_buildtype_args(self, buildtype: str) -> T.List[str]: - # We can override these in children by just overriding the - # _BUILDTYPE_ARGS value. - return mesonlib.listify([self._apply_prefix(a) for a in self._BUILDTYPE_ARGS[buildtype]]) - - def get_pie_args(self) -> T.List[str]: - return ['-pie'] - - def get_asneeded_args(self) -> T.List[str]: - return self._apply_prefix('--as-needed') - - def get_link_whole_for(self, args: T.List[str]) -> T.List[str]: - if not args: - return args - return self._apply_prefix('--whole-archive') + args + self._apply_prefix('--no-whole-archive') - - def get_allow_undefined_args(self) -> T.List[str]: - return self._apply_prefix('--allow-shlib-undefined') - - def get_lto_args(self) -> T.List[str]: - return ['-flto'] - - def sanitizer_args(self, value: str) -> T.List[str]: - if value == 'none': - return [] - return ['-fsanitize=' + value] - - def get_coverage_args(self) -> T.List[str]: - return ['--coverage'] - - def export_dynamic_args(self, env: 'Environment') -> T.List[str]: - m = env.machines[self.for_machine] - if m.is_windows() or m.is_cygwin(): - return self._apply_prefix('--export-all-symbols') - return self._apply_prefix('-export-dynamic') - - def import_library_args(self, implibname: str) -> T.List[str]: - return self._apply_prefix('--out-implib=' + implibname) - - def thread_flags(self, env: 'Environment') -> T.List[str]: - if env.machines[self.for_machine].is_haiku(): - return [] - return ['-pthread'] - - def no_undefined_args(self) -> T.List[str]: - return self._apply_prefix('--no-undefined') - - def fatal_warnings(self) -> T.List[str]: - return self._apply_prefix('--fatal-warnings') - - def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str, - suffix: str, soversion: str, darwin_versions: T.Tuple[str, str], - is_shared_module: bool) -> T.List[str]: - m = env.machines[self.for_machine] - if m.is_windows() or m.is_cygwin(): - # For PE/COFF the soname argument has no effect - return [] - sostr = '' if soversion is None else '.' + soversion - return self._apply_prefix(f'-soname,{prefix}{shlib_name}.{suffix}{sostr}') - - def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, - rpath_paths: str, build_rpath: str, - install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: - m = env.machines[self.for_machine] - if m.is_windows() or m.is_cygwin(): - return ([], set()) - if not rpath_paths and not install_rpath and not build_rpath: - return ([], set()) - args = [] - origin_placeholder = '$ORIGIN' - processed_rpaths = prepare_rpaths(rpath_paths, build_dir, from_dir) - # Need to deduplicate rpaths, as macOS's install_name_tool - # is *very* allergic to duplicate -delete_rpath arguments - # when calling depfixer on installation. - all_paths = mesonlib.OrderedSet([os.path.join(origin_placeholder, p) for p in processed_rpaths]) - rpath_dirs_to_remove = set() - for p in all_paths: - rpath_dirs_to_remove.add(p.encode('utf8')) - # Build_rpath is used as-is (it is usually absolute). - if build_rpath != '': - all_paths.add(build_rpath) - for p in build_rpath.split(':'): - rpath_dirs_to_remove.add(p.encode('utf8')) - - # TODO: should this actually be "for (dragonfly|open)bsd"? - if mesonlib.is_dragonflybsd() or mesonlib.is_openbsd(): - # This argument instructs the compiler to record the value of - # ORIGIN in the .dynamic section of the elf. On Linux this is done - # by default, but is not on dragonfly/openbsd for some reason. Without this - # $ORIGIN in the runtime path will be undefined and any binaries - # linked against local libraries will fail to resolve them. - args.extend(self._apply_prefix('-z,origin')) - - # In order to avoid relinking for RPATH removal, the binary needs to contain just - # enough space in the ELF header to hold the final installation RPATH. - paths = ':'.join(all_paths) - if len(paths) < len(install_rpath): - padding = 'X' * (len(install_rpath) - len(paths)) - if not paths: - paths = padding - else: - paths = paths + ':' + padding - args.extend(self._apply_prefix('-rpath,' + paths)) - - # TODO: should this actually be "for solaris/sunos"? - if mesonlib.is_sunos(): - return (args, rpath_dirs_to_remove) - - # Rpaths to use while linking must be absolute. These are not - # written to the binary. Needed only with GNU ld: - # https://sourceware.org/bugzilla/show_bug.cgi?id=16936 - # Not needed on Windows or other platforms that don't use RPATH - # https://github.com/mesonbuild/meson/issues/1897 - # - # In addition, this linker option tends to be quite long and some - # compilers have trouble dealing with it. That's why we will include - # one option per folder, like this: - # - # -Wl,-rpath-link,/path/to/folder1 -Wl,-rpath,/path/to/folder2 ... - # - # ...instead of just one single looooong option, like this: - # - # -Wl,-rpath-link,/path/to/folder1:/path/to/folder2:... - for p in rpath_paths: - args.extend(self._apply_prefix('-rpath-link,' + os.path.join(build_dir, p))) - - return (args, rpath_dirs_to_remove) - - def get_win_subsystem_args(self, value: str) -> T.List[str]: - if 'windows' in value: - args = ['--subsystem,windows'] - elif 'console' in value: - args = ['--subsystem,console'] - else: - raise mesonlib.MesonException(f'Only "windows" and "console" are supported for win_subsystem with MinGW, not "{value}".') - if ',' in value: - args[-1] = args[-1] + ':' + value.split(',')[1] - - 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], - is_shared_module: bool) -> T.List[str]: - if is_shared_module: - return [] - install_name = ['@rpath/', prefix, shlib_name] - if soversion is not None: - install_name.append('.' + soversion) - install_name.append('.dylib') - args = ['-install_name', ''.join(install_name)] - if darwin_versions: - args.extend(['-compatibility_version', darwin_versions[0], - '-current_version', darwin_versions[1]]) - return args - - def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, - rpath_paths: str, build_rpath: str, - install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: - if not rpath_paths and not install_rpath and not build_rpath: - return ([], set()) - args = [] - # @loader_path is the equivalent of $ORIGIN on macOS - # https://stackoverflow.com/q/26280738 - origin_placeholder = '@loader_path' - processed_rpaths = prepare_rpaths(rpath_paths, build_dir, from_dir) - all_paths = mesonlib.OrderedSet([os.path.join(origin_placeholder, p) for p in processed_rpaths]) - if build_rpath != '': - all_paths.add(build_rpath) - for rp in all_paths: - args.extend(self._apply_prefix('-rpath,' + rp)) - - return (args, set()) - - -class GnuDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, DynamicLinker): - - """Representation of GNU ld.bfd and ld.gold.""" - - def get_accepts_rsp(self) -> bool: - return True - - -class GnuGoldDynamicLinker(GnuDynamicLinker): - - id = 'ld.gold' - - -class GnuBFDDynamicLinker(GnuDynamicLinker): - - id = 'ld.bfd' - - -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 + self._apply_prefix('--allow-shlib-undefined')) - self.has_allow_shlib_undefined = not ('unknown argument: --allow-shlib-undefined' in e) - - def get_allow_undefined_args(self) -> T.List[str]: - if self.has_allow_shlib_undefined: - return self._apply_prefix('--allow-shlib-undefined') - return [] - - -class WASMDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, DynamicLinker): - - """Emscripten's wasm-ld.""" - - id = 'ld.wasm' - - def get_allow_undefined_args(self) -> T.List[str]: - return ['-s', 'ERROR_ON_UNDEFINED_SYMBOLS=0'] - - def no_undefined_args(self) -> T.List[str]: - return ['-s', 'ERROR_ON_UNDEFINED_SYMBOLS=1'] - - def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str, - suffix: str, soversion: str, darwin_versions: T.Tuple[str, str], - is_shared_module: bool) -> T.List[str]: - raise mesonlib.MesonException(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: 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], - is_shared_module: bool) -> 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.exe'], for_machine, '', [], - version=version) - - def get_link_whole_for(self, args: T.List[str]) -> T.List[str]: - if not args: - return args - return self._apply_prefix('--start-group') + args + self._apply_prefix('--end-group') - - def get_accepts_rsp(self) -> bool: - return False - - def get_lib_prefix(self) -> str: - return '' - - def get_std_shared_lib_args(self) -> T.List[str]: - return [] - - def get_output_args(self, outputname: str) -> T.List[str]: - return [f'-o{outputname}'] - - def get_search_args(self, dirname: str) -> 'T.NoReturn': - raise OSError('xc16-gcc.exe does not have a search dir argument') - - def get_allow_undefined_args(self) -> T.List[str]: - return [] - - def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str, - suffix: str, soversion: str, darwin_versions: T.Tuple[str, str], - is_shared_module: bool) -> T.List[str]: - return [] - - def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, - rpath_paths: str, build_rpath: str, - install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: - return ([], set()) - -class 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], - is_shared_module: bool) -> T.List[str]: - raise mesonlib.MesonException(f'{self.id} does not support shared libraries.') - - def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, - rpath_paths: str, build_rpath: str, - install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: - return ([], set()) - -class C2000DynamicLinker(DynamicLinker): - - """Linker for Texas Instruments C2000 compiler.""" - - id = 'cl2000' - - def __init__(self, for_machine: mesonlib.MachineChoice, - *, version: str = 'unknown version'): - super().__init__(['cl2000.exe'], for_machine, '', [], - version=version) - - def get_link_whole_for(self, args: T.List[str]) -> T.List[str]: - if not args: - return args - return self._apply_prefix('--start-group') + args + self._apply_prefix('--end-group') - - def get_accepts_rsp(self) -> bool: - return False - - def get_lib_prefix(self) -> str: - return '-l=' - - def get_std_shared_lib_args(self) -> T.List[str]: - return [] - - def get_output_args(self, outputname: str) -> T.List[str]: - return ['-z', f'--output_file={outputname}'] - - def get_search_args(self, dirname: str) -> 'T.NoReturn': - raise OSError('cl2000.exe does not have a search dir argument') - - def get_allow_undefined_args(self) -> T.List[str]: - return [] - - def get_always_args(self) -> T.List[str]: - return [] - - -class ArmDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): - - """Linker for the ARM compiler.""" - - 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 mesonlib.MesonException('The Arm Linkers do not support shared libraries') - - def get_allow_undefined_args(self) -> T.List[str]: - return [] - - -class ArmClangDynamicLinker(ArmDynamicLinker): - - """Linker used with ARM's clang fork. - - The interface is similar enough to the old ARM ld that it inherits and - extends a few things as needed. - """ - - def export_dynamic_args(self, env: 'Environment') -> T.List[str]: - return ['--export_dynamic'] - - def import_library_args(self, implibname: str) -> T.List[str]: - return ['--symdefs=' + implibname] - -class QualcommLLVMDynamicLinker(LLVMDynamicLinker): - - """ARM Linker from Snapdragon LLVM ARM Compiler.""" - - id = 'ld.qcld' - - -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], - is_shared_module: bool) -> T.List[str]: - return [] - - def get_std_shared_lib_args(self) -> T.List[str]: - # PGI -shared is Linux only. - if mesonlib.is_windows(): - return ['-Bdynamic', '-Mmakedll'] - elif mesonlib.is_linux(): - return ['-shared'] - return [] - - def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, - rpath_paths: str, build_rpath: str, - install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: - if not env.machines[self.for_machine].is_windows(): - return (['-R' + os.path.join(build_dir, p) for p in rpath_paths], set()) - return ([], set()) - -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) -> 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], - is_shared_module: bool) -> T.List[str]: - return [] - - def import_library_args(self, implibname: str) -> T.List[str]: - """The command to generate the import library.""" - return self._apply_prefix(['/IMPLIB:' + implibname]) - - 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()}']) - - -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) - - -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: 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], - is_shared_module: bool) -> 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 build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, - rpath_paths: str, build_rpath: str, - install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: - 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 interfer 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().split('V')[-1] - - def get_accepts_rsp(self) -> bool: - # nvcc does not support response files - return False - - def get_lib_prefix(self) -> str: - if not mesonlib.is_windows(): - return '' - # nvcc doesn't recognize Meson's default .a extension for static libraries on - # Windows and passes it to cl as an object file, resulting in 'warning D9024 : - # unrecognized source file type 'xxx.a', object file assumed'. - # - # nvcc's --library= option doesn't help: it takes the library name without the - # extension and assumes that the extension on Windows is .lib; prefixing the - # library with -Xlinker= seems to work. - from .compilers import CudaCompiler - return CudaCompiler.LINKER_PREFIX - - def fatal_warnings(self) -> T.List[str]: - return ['--warning-as-error'] - - def get_allow_undefined_args(self) -> T.List[str]: - return [] - - def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str, - suffix: str, soversion: str, darwin_versions: T.Tuple[str, str], - is_shared_module: bool) -> T.List[str]: - return [] |