diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 978c16999..62bdd9286 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1327,7 +1327,8 @@ int dummy; self.get_target_dir(target)) else: target_slashname_workaround_dir = self.get_target_dir(target) - rpath_args = rustc.build_rpath_args(self.environment.get_build_dir(), + rpath_args = rustc.build_rpath_args(self.environment, + self.environment.get_build_dir(), target_slashname_workaround_dir, self.determine_rpath_dirs(target), target.build_rpath, @@ -2324,9 +2325,10 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) # All shared libraries are PIC commands += linker.get_pic_args() # Add -Wl,-soname arguments on Linux, -install_name on OS X - commands += linker.get_soname_args(target.prefix, target.name, target.suffix, - target.soversion, target.darwin_versions, - isinstance(target, build.SharedModule)) + commands += linker.get_soname_args( + self.environment, target.prefix, target.name, target.suffix, + target.soversion, target.darwin_versions, + isinstance(target, build.SharedModule)) # This is only visited when building for Windows using either GCC or Visual Studio if target.vs_module_defs and hasattr(linker, 'gen_vs_module_defs_args'): commands += linker.gen_vs_module_defs_args(target.vs_module_defs.rel_to_builddir(self.build_to_src)) @@ -2537,7 +2539,8 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) self.get_target_dir(target)) else: target_slashname_workaround_dir = self.get_target_dir(target) - commands += linker.build_rpath_args(self.environment.get_build_dir(), + commands += linker.build_rpath_args(self.environment, + self.environment.get_build_dir(), target_slashname_workaround_dir, self.determine_rpath_dirs(target), target.build_rpath, diff --git a/mesonbuild/build.py b/mesonbuild/build.py index b77995ac2..2f3767094 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -28,7 +28,7 @@ from .mesonlib import ( extract_as_list, typeslistify, stringlistify, classify_unity_sources, get_filenames_templates_dict, substitute_values, has_path_sep, ) -from .compilers import Compiler, is_object, clink_langs, sort_clink, lang_suffixes, get_macos_dylib_install_name +from .compilers import Compiler, is_object, clink_langs, sort_clink, lang_suffixes from .linkers import StaticLinker from .interpreterbase import FeatureNew @@ -96,8 +96,12 @@ known_stlib_kwargs = known_build_target_kwargs | {'pic'} known_jar_kwargs = known_exe_kwargs | {'main_class'} @lru_cache(maxsize=None) -def get_target_macos_dylib_install_name(ld): - return get_macos_dylib_install_name(ld.prefix, ld.name, ld.suffix, ld.soversion) +def get_target_macos_dylib_install_name(ld) -> str: + name = ['@rpath/', ld.prefix, ld.name] + if ld.soversion is not None: + name.append('.' + ld.soversion) + name.append('.dylib') + return ''.join(name) class InvalidArguments(MesonException): pass diff --git a/mesonbuild/compilers/__init__.py b/mesonbuild/compilers/__init__.py index 5cd56b871..c81fe752a 100644 --- a/mesonbuild/compilers/__init__.py +++ b/mesonbuild/compilers/__init__.py @@ -23,7 +23,6 @@ __all__ = [ 'clink_langs', 'c_suffixes', 'cpp_suffixes', - 'get_macos_dylib_install_name', 'get_base_compile_args', 'get_base_link_args', 'is_assembly', @@ -185,6 +184,6 @@ from .rust import RustCompiler from .swift import SwiftCompiler from .vala import ValaCompiler from .mixins.visualstudio import VisualStudioLikeCompiler -from .mixins.gnu import GnuCompiler, get_macos_dylib_install_name +from .mixins.gnu import GnuCompiler from .mixins.intel import IntelGnuLikeCompiler, IntelVisualStudioLikeCompiler from .mixins.clang import ClangCompiler diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 589857ee1..1ec914650 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -27,6 +27,7 @@ from .mixins.intel import IntelGnuLikeCompiler, IntelVisualStudioLikeCompiler from .mixins.clang import ClangCompiler from .mixins.elbrus import ElbrusCompiler from .mixins.pgi import PGICompiler +from .mixins.islinker import BasicLinkerIsCompilerMixin, LinkerEnvVarsMixin from .compilers import ( gnu_winlibs, msvc_winlibs, @@ -112,14 +113,8 @@ class ClangCCompiler(ClangCompiler, CCompiler): def get_option_link_args(self, options): return [] - def get_linker_always_args(self): - basic = super().get_linker_always_args() - if self.compiler_type.is_osx_compiler: - return basic + ['-Wl,-headerpad_max_install_names'] - return basic - -class EmscriptenCCompiler(ClangCCompiler): +class EmscriptenCCompiler(LinkerEnvVarsMixin, BasicLinkerIsCompilerMixin, ClangCCompiler): def __init__(self, exelist, version, compiler_type, for_machine: MachineChoice, is_cross, exe_wrapper=None, **kwargs): if not is_cross: raise MesonException('Emscripten compiler can only be used for cross compilation.') @@ -129,18 +124,6 @@ class EmscriptenCCompiler(ClangCCompiler): def get_option_link_args(self, options): return [] - def get_linker_always_args(self): - return [] - - def get_asneeded_args(self): - return [] - - def get_lundef_args(self): - return [] - - def build_rpath_args(self, *args, **kwargs): - return [] - def get_soname_args(self, *args, **kwargs): raise MesonException('Emscripten does not support shared libraries.') @@ -388,9 +371,6 @@ class CcrxCCompiler(CcrxCompiler, CCompiler): def get_output_args(self, target): return ['-output=obj=%s' % target] - def get_linker_output_args(self, outputname): - return ['-output=%s' % outputname] - def get_werror_args(self): return ['-change_message=error'] diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 52490687a..f16e447f1 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -13,9 +13,10 @@ # limitations under the License. import contextlib, enum, os.path, re, tempfile, shlex -from typing import Optional, Tuple, List +import typing +from typing import List, Optional, Tuple -from ..linkers import StaticLinker +from ..linkers import StaticLinker, GnuLikeDynamicLinkerMixin from .. import coredata from .. import mlog from .. import mesonlib @@ -27,6 +28,11 @@ from ..envconfig import ( Properties, ) +if typing.TYPE_CHECKING: + from ..coredata import OptionDictType + from ..environment import Environment + from ..linkers import DynamicLinker # noqa: F401 + """This file contains the data files of all compilers Meson knows about. To support a new compiler, add its information below. Also add corresponding autodetection code in environment.py.""" @@ -335,18 +341,26 @@ def get_base_link_args(options, linker, is_shared_module): args += linker.get_coverage_link_args() except KeyError: pass - # These do not need a try...except - if not is_shared_module and option_enabled(linker.base_options, options, 'b_lundef'): - args.append('-Wl,--no-undefined') + as_needed = option_enabled(linker.base_options, options, 'b_asneeded') bitcode = option_enabled(linker.base_options, options, 'b_bitcode') # Shared modules cannot be built with bitcode_bundle because # -bitcode_bundle is incompatible with -undefined and -bundle if bitcode and not is_shared_module: - args.append('-Wl,-bitcode_bundle') + args.extend(linker.bitcode_args()) elif as_needed: # -Wl,-dead_strip_dylibs is incompatible with bitcode args.extend(linker.get_asneeded_args()) + + # Apple's ld (the only one that supports bitcode) does not like any + # -undefined arguments at all, so don't pass these when using bitcode + if not bitcode: + if (not is_shared_module and + option_enabled(linker.base_options, options, 'b_lundef')): + args.extend(linker.no_undefined_link_args()) + else: + args.extend(linker.get_allow_undefined_link_args()) + try: crt_val = options['b_vscrt'].value buildtype = options['buildtype'].value @@ -358,30 +372,6 @@ def get_base_link_args(options, linker, is_shared_module): pass return args -def prepare_rpaths(raw_rpaths, build_dir, from_dir): - 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): - # 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, build_dir, from_dir): - 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 CrossNoRunException(MesonException): pass @@ -529,7 +519,7 @@ class CompilerArgs(list): return True return False - def to_native(self, copy=False): + def to_native(self, copy: bool = False) -> typing.List[str]: # Check if we need to add --start/end-group for circular dependencies # between static libraries, and for recursively searching for symbols # needed by static libraries that are provided by object files or @@ -538,8 +528,12 @@ class CompilerArgs(list): new = self.copy() else: new = self - if get_compiler_uses_gnuld(self.compiler): - global soregex + # This covers all ld.bfd, ld.gold, ld.gold, and xild on Linux, which + # all act like (or are) gnu ld + # TODO: this could probably be added to the DynamicLinker instead + if (hasattr(self.compiler, 'linker') and + self.compiler.linker is not None and + isinstance(self.compiler.linker, GnuLikeDynamicLinkerMixin)): group_start = -1 group_end = -1 for i, each in enumerate(new): @@ -656,7 +650,8 @@ class Compiler: # manually searched. internal_libs = () - def __init__(self, exelist, version, for_machine: MachineChoice, **kwargs): + def __init__(self, exelist, version, for_machine: MachineChoice, + linker: typing.Optional['DynamicLinker'] = None, **kwargs): if isinstance(exelist, str): self.exelist = [exelist] elif isinstance(exelist, list): @@ -676,6 +671,7 @@ class Compiler: self.full_version = None self.for_machine = for_machine self.base_options = [] + self.linker = linker def __repr__(self): repr_str = "<{0}: v{1} `{2}`>" @@ -729,6 +725,12 @@ class Compiler: def get_exelist(self): return self.exelist[:] + def get_linker_exelist(self) -> typing.List[str]: + return self.linker.get_exelist() + + def get_linker_output_args(self, outputname: str) -> typing.List[str]: + return self.linker.get_output_args(outputname) + def get_builtin_define(self, *args, **kwargs): raise EnvironmentException('%s does not support get_builtin_define.' % self.id) @@ -738,17 +740,17 @@ class Compiler: def get_always_args(self): return [] - def can_linker_accept_rsp(self): + def can_linker_accept_rsp(self) -> bool: """ Determines whether the linker can accept arguments using the @rsp syntax. """ - return mesonlib.is_windows() + return self.linker.get_accepts_rsp() def get_linker_always_args(self): - return [] + return self.linker.get_always_args() def get_linker_lib_prefix(self): - return '' + return self.linker.get_lib_prefix() def gen_import_library_args(self, implibname): """ @@ -769,7 +771,10 @@ class Compiler: """ return self.get_language() in languages_using_ldflags - def get_args_from_envvars(self): + def get_linker_args_from_envvars(self) -> typing.List[str]: + return self.linker.get_args_from_envvars() + + def get_args_from_envvars(self) -> typing.Tuple[typing.List[str], typing.List[str]]: """ Returns a tuple of (compile_flags, link_flags) for the specified language from the inherited environment @@ -781,15 +786,13 @@ class Compiler: mlog.debug('No {} in the environment, not changing global flags.'.format(var)) lang = self.get_language() - compiler_is_linker = False - if hasattr(self, 'get_linker_exelist'): - compiler_is_linker = (self.get_exelist() == self.get_linker_exelist()) + compiler_is_linker = self.linker is not None and self.linker.invoked_by_compiler() if lang not in cflags_mapping: return [], [] - compile_flags = [] - link_flags = [] + compile_flags = [] # type: typing.List[str] + link_flags = [] # type: typing.List[str] env_compile_flags = os.environ.get(cflags_mapping[lang]) log_var(cflags_mapping[lang], env_compile_flags) @@ -798,12 +801,11 @@ class Compiler: # Link flags (same for all languages) if self.use_ldflags(): - env_link_flags = os.environ.get('LDFLAGS') + env_link_flags = self.get_linker_args_from_envvars() else: - env_link_flags = None + env_link_flags = [] log_var('LDFLAGS', env_link_flags) - if env_link_flags is not None: - link_flags += shlex.split(env_link_flags) + link_flags += env_link_flags if compiler_is_linker: # When the compiler is used as a wrapper around the linker (such as # with GCC and Clang), the compile flags can be needed while linking @@ -861,8 +863,8 @@ class Compiler: def get_option_compile_args(self, options): return [] - def get_option_link_args(self, options): - return [] + def get_option_link_args(self, options: 'OptionDictType') -> typing.List[str]: + return self.linker.get_option_args(options) def check_header(self, *args, **kwargs) -> Tuple[bool, bool]: raise EnvironmentException('Language %s does not support header checks.' % self.get_display_language()) @@ -910,10 +912,8 @@ class Compiler: 'Language {} does not support has_multi_arguments.'.format( self.get_display_language())) - def has_multi_link_arguments(self, args, env) -> Tuple[bool, bool]: - raise EnvironmentException( - 'Language {} does not support has_multi_link_arguments.'.format( - self.get_display_language())) + def has_multi_link_arguments(self, args: typing.List[str], env: 'Environment') -> Tuple[bool, bool]: + return self.linker.has_multi_arguments(args, env) def _get_compile_output(self, dirname, mode): # In pre-processor mode, the output is sent to stdout and discarded @@ -1026,19 +1026,23 @@ class Compiler: def get_compile_debugfile_args(self, rel_obj, **kwargs): return [] - def get_link_debugfile_args(self, rel_obj): - return [] + def get_link_debugfile_args(self, targetfile: str) -> typing.List[str]: + return self.linker.get_debugfile_args(targetfile) - def get_std_shared_lib_link_args(self): - return [] + def get_std_shared_lib_link_args(self) -> typing.List[str]: + return self.linker.get_std_shared_lib_args() + + def get_std_shared_module_link_args(self, options: 'OptionDictType') -> typing.List[str]: + return self.linker.get_std_shared_module_args(options) + + def get_link_whole_for(self, args: typing.List[str]) -> typing.List[str]: + return self.linker.get_link_whole_for(args) - def get_std_shared_module_link_args(self, options): - return self.get_std_shared_lib_link_args() + def get_allow_undefined_link_args(self) -> typing.List[str]: + return self.linker.get_allow_undefined_args() - def get_link_whole_for(self, args): - if isinstance(args, list) and not args: - return [] - raise EnvironmentException('Language %s does not support linking whole archives.' % self.get_display_language()) + def no_undefined_link_args(self) -> typing.List[str]: + return self.linker.no_undefined_args() # Compiler arguments needed to enable the given instruction set. # May be [] meaning nothing needed or None meaning the given set @@ -1046,77 +1050,11 @@ class Compiler: def get_instruction_set_args(self, instruction_set): return None - def build_unix_rpath_args(self, build_dir, from_dir, rpath_paths, build_rpath, install_rpath): - if not rpath_paths and not install_rpath and not build_rpath: - return [] - args = [] - if get_compiler_is_osx_compiler(self): - # Ensure that there is enough space for install_name_tool in-place editing of large RPATHs - args.append('-Wl,-headerpad_max_install_names') - # @loader_path is the equivalent of $ORIGIN on macOS - # https://stackoverflow.com/q/26280738 - origin_placeholder = '@loader_path' - else: - origin_placeholder = '$ORIGIN' - # 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. - 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 = OrderedSet([os.path.join(origin_placeholder, p) for p in processed_rpaths]) - # Build_rpath is used as-is (it is usually absolute). - if build_rpath != '': - all_paths.add(build_rpath) - - 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.append('-Wl,-z,origin') - - if get_compiler_is_osx_compiler(self): - # macOS does not support colon-separated strings in LC_RPATH, - # hence we have to pass each path component individually - args += ['-Wl,-rpath,' + rp for rp in all_paths] - else: - # 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.append('-Wl,-rpath,' + paths) - - if mesonlib.is_sunos(): - return args - - if get_compiler_is_linuxlike(self): - # 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:... - - args += ['-Wl,-rpath-link,' + os.path.join(build_dir, p) for p in rpath_paths] - - return args + def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, + rpath_paths: str, build_rpath: str, + install_rpath: str) -> typing.List[str]: + return self.linker.build_rpath_args( + env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath) def thread_flags(self, env): return [] @@ -1125,10 +1063,6 @@ class Compiler: raise EnvironmentException('Language %s does not support OpenMP flags.' % self.get_display_language()) def language_stdlib_only_link_flags(self): - # The linker flags needed to link the standard library of the current - # language in. This is needed in cases where you e.g. combine D and C++ - # and both of which need to link their runtime library in or otherwise - # building fails with undefined symbols. return [] def gnu_symbol_visibility_args(self, vistype): @@ -1149,9 +1083,8 @@ class Compiler: m = 'Language {} does not support position-independent executable' raise EnvironmentException(m.format(self.get_display_language())) - def get_pie_link_args(self): - m = 'Language {} does not support position-independent executable' - raise EnvironmentException(m.format(self.get_display_language())) + def get_pie_link_args(self) -> typing.List[str]: + return self.linker.get_pie_args() def get_argument_syntax(self): """Returns the argument family type. @@ -1172,11 +1105,8 @@ class Compiler: raise EnvironmentException( '%s does not support get_profile_use_args ' % self.get_id()) - def get_undefined_link_args(self): - ''' - Get args for allowing undefined symbols when linking to a shared library - ''' - return [] + def get_undefined_link_args(self) -> typing.List[str]: + return self.linker.get_undefined_link_args() def remove_linkerlike_args(self, args): return [x for x in args if not x.startswith('-Wl')] @@ -1185,13 +1115,32 @@ class Compiler: return [] def get_lto_link_args(self) -> List[str]: - return [] + return self.linker.get_lto_args() def sanitizer_compile_args(self, value: str) -> List[str]: return [] def sanitizer_link_args(self, value: str) -> List[str]: - return [] + return self.linker.sanitizer_args(value) + + def get_asneeded_args(self) -> typing.List[str]: + return self.linker.get_asneeded_args() + + def bitcode_args(self) -> typing.List[str]: + return self.linker.bitcode_args() + + def get_linker_debug_crt_args(self) -> typing.List[str]: + return self.linker.get_debug_crt_args() + + def get_buildtype_linker_args(self, buildtype: str) -> typing.List[str]: + return self.linker.get_buildtype_args(buildtype) + + def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str, + suffix: str, soversion: str, darwin_versions: typing.Tuple[str, str], + is_shared_module: bool) -> typing.List[str]: + return self.linker.get_soname_args( + env, prefix, shlib_name, suffix, soversion, + darwin_versions, is_shared_module) @enum.unique @@ -1235,23 +1184,6 @@ def get_compiler_is_linuxlike(compiler): compiler_type = getattr(compiler, 'compiler_type', None) return compiler_type and compiler_type.is_standard_compiler -def get_compiler_is_osx_compiler(compiler): - compiler_type = getattr(compiler, 'compiler_type', None) - return compiler_type and compiler_type.is_osx_compiler - -def get_compiler_uses_gnuld(c): - # FIXME: Perhaps we should detect the linker in the environment? - # FIXME: Assumes that *BSD use GNU ld, but they might start using lld soon - compiler_type = getattr(c, 'compiler_type', None) - return compiler_type in { - CompilerType.GCC_STANDARD, - CompilerType.GCC_MINGW, - CompilerType.GCC_CYGWIN, - CompilerType.CLANG_STANDARD, - CompilerType.CLANG_MINGW, - CompilerType.ICC_STANDARD, - } - def get_largefile_args(compiler): ''' Enable transparent large-file-support for 32-bit UNIX systems diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index f942114b1..f93db3ebe 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -36,6 +36,7 @@ from .mixins.intel import IntelGnuLikeCompiler, IntelVisualStudioLikeCompiler from .mixins.clang import ClangCompiler from .mixins.elbrus import ElbrusCompiler from .mixins.pgi import PGICompiler +from .mixins.islinker import BasicLinkerIsCompilerMixin, LinkerEnvVarsMixin def non_msvc_eh_options(eh, args): @@ -183,7 +184,7 @@ class ClangCPPCompiler(ClangCompiler, CPPCompiler): return ['-lstdc++'] -class EmscriptenCPPCompiler(ClangCPPCompiler): +class EmscriptenCPPCompiler(LinkerEnvVarsMixin, BasicLinkerIsCompilerMixin, ClangCPPCompiler): def __init__(self, exelist, version, compiler_type, for_machine: MachineChoice, is_cross, exe_wrapper=None, **kwargs): if not is_cross: raise MesonException('Emscripten compiler can only be used for cross compilation.') @@ -200,18 +201,6 @@ class EmscriptenCPPCompiler(ClangCPPCompiler): def get_option_link_args(self, options): return [] - def get_linker_always_args(self): - return [] - - def get_asneeded_args(self): - return [] - - def get_lundef_args(self): - return [] - - def build_rpath_args(self, *args, **kwargs): - return [] - def get_soname_args(self, *args, **kwargs): raise MesonException('Emscripten does not support shared libraries.') @@ -574,9 +563,6 @@ class CcrxCPPCompiler(CcrxCompiler, CPPCompiler): def get_output_args(self, target): return ['-output=obj=%s' % target] - def get_linker_output_args(self, outputname): - return ['-output=%s' % outputname] - def get_option_link_args(self, options): return [] diff --git a/mesonbuild/compilers/cs.py b/mesonbuild/compilers/cs.py index 8069ab19e..7c884c9d0 100644 --- a/mesonbuild/compilers/cs.py +++ b/mesonbuild/compilers/cs.py @@ -18,6 +18,7 @@ from ..mesonlib import EnvironmentException from ..mesonlib import is_windows from .compilers import Compiler, MachineChoice, mono_buildtype_args +from .mixins.islinker import BasicLinkerIsCompilerMixin cs_optimization_args = {'0': [], 'g': [], @@ -27,7 +28,7 @@ cs_optimization_args = {'0': [], 's': ['-optimize+'], } -class CsCompiler(Compiler): +class CsCompiler(BasicLinkerIsCompilerMixin, Compiler): def __init__(self, exelist, version, for_machine: MachineChoice, comp_id, runner=None): self.language = 'cs' super().__init__(exelist, version, for_machine) @@ -50,18 +51,12 @@ class CsCompiler(Compiler): def get_link_args(self, fname): return ['-r:' + fname] - def get_soname_args(self, *args): - return [] - def get_werror_args(self): return ['-warnaserror'] def split_shlib_to_parts(self, fname): return None, fname - def build_rpath_args(self, build_dir, from_dir, rpath_paths, build_rpath, install_rpath): - return [] - def get_dependency_gen_args(self, outtarget, outfile): return [] @@ -71,15 +66,9 @@ class CsCompiler(Compiler): def get_compile_only_args(self): return [] - def get_linker_output_args(self, outputname): - return [] - def get_coverage_args(self): return [] - def get_coverage_link_args(self): - return [] - def get_std_exe_link_args(self): return [] @@ -142,6 +131,7 @@ class CsCompiler(Compiler): def get_optimization_args(self, optimization_level): return cs_optimization_args[optimization_level] + class MonoCompiler(CsCompiler): def __init__(self, exelist, version, for_machine: MachineChoice): super().__init__(exelist, version, for_machine, 'mono', diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py index a121264b2..b6bafe767 100644 --- a/mesonbuild/compilers/cuda.py +++ b/mesonbuild/compilers/cuda.py @@ -13,12 +13,17 @@ # limitations under the License. import os.path +import typing from .. import mlog from ..mesonlib import EnvironmentException, MachineChoice, Popen_safe from .compilers import (Compiler, cuda_buildtype_args, cuda_optimization_args, cuda_debug_args, CompilerType) +if typing.TYPE_CHECKING: + from ..environment import Environment # noqa: F401 + + class CudaCompiler(Compiler): def __init__(self, exelist, version, for_machine: MachineChoice, is_cross, exe_wrapper=None, **kwargs): if not hasattr(self, 'language'): @@ -47,7 +52,7 @@ class CudaCompiler(Compiler): return [] def thread_link_flags(self, environment): - return ['-Xcompiler=-pthread'] + return self._cook_link_args(super().thread_link_flags()) def sanity_check(self, work_dir, environment): mlog.debug('Sanity testing ' + self.get_display_language() + ' compiler:', ' '.join(self.exelist)) @@ -142,9 +147,6 @@ class CudaCompiler(Compiler): else: mlog.debug('cudaGetDeviceCount() returned ' + stde) - def get_compiler_check_args(self): - return super().get_compiler_check_args() + [] - def has_header_symbol(self, hname, symbol, prefix, env, extra_args=None, dependencies=None): result, cached = super().has_header_symbol(hname, symbol, prefix, env, extra_args, dependencies) if result: @@ -159,22 +161,24 @@ class CudaCompiler(Compiler): return self.compiles(t.format(**fargs), env, extra_args, dependencies) @staticmethod - def _cook_link_args(args): + def _cook_link_args(args: typing.List[str]) -> typing.List[str]: """ Converts GNU-style arguments -Wl,-arg,-arg to NVCC-style arguments -Xlinker=-arg,-arg """ - return [arg.replace('-Wl', '-Xlinker=', 1) if arg.startswith('-Wl') else arg for arg in args] - - def get_output_args(self, target): - return ['-o', target] + cooked = [] # type: typing.List[str] + for arg in args: + if arg.startswith('-Wl,'): + arg = arg.replace('-Wl,', '-Xlinker=', 1) + arg = arg.replace(' ', '\\') + cooked.append(arg) + return cooked def name_string(self): return ' '.join(self.exelist) def get_soname_args(self, *args): - rawargs = get_gcc_soname_args(CompilerType.GCC_STANDARD, *args) - return self._cook_link_args(rawargs) + return self._cook_link_args(super().get_soname_args(*args)) def get_dependency_gen_args(self, outtarget, outfile): return [] @@ -194,12 +198,6 @@ class CudaCompiler(Compiler): def get_werror_args(self): return ['-Werror=cross-execution-space-call,deprecated-declarations,reorder'] - def get_linker_exelist(self): - return self.exelist[:] - - def get_linker_output_args(self, outputname): - return ['-o', outputname] - def get_warn_args(self, level): return self.warn_args[level] @@ -211,27 +209,17 @@ class CudaCompiler(Compiler): path = '.' return ['-I' + path] - def get_std_shared_lib_link_args(self): - return ['-shared'] - def depfile_for_object(self, objfile): return objfile + '.' + self.get_depfile_suffix() def get_depfile_suffix(self): return 'd' - def get_buildtype_linker_args(self, buildtype): - return [] - - def get_std_exe_link_args(self): - return [] - - def build_rpath_args(self, build_dir, from_dir, rpath_paths, build_rpath, install_rpath): - rawargs = self.build_unix_rpath_args(build_dir, from_dir, rpath_paths, build_rpath, install_rpath) - return self._cook_link_args(rawargs) - - def get_linker_search_args(self, dirname): - return ['-L' + dirname] + def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, + rpath_paths: str, build_rpath: str, + install_rpath: str) -> typing.List[str]: + return self._cook_link_args(super().build_rpath_args( + env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath)) def linker_to_compiler_args(self, args): return args @@ -241,3 +229,9 @@ class CudaCompiler(Compiler): def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): return [] + + def get_output_args(self, target: str) -> typing.List[str]: + return ['-o', target] + + def get_std_exe_link_args(self) -> typing.List[str]: + return [] diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py index 27ca6fc6d..18e3bf9b7 100644 --- a/mesonbuild/compilers/d.py +++ b/mesonbuild/compilers/d.py @@ -13,6 +13,7 @@ # limitations under the License. import os.path, subprocess +import typing from ..mesonlib import ( EnvironmentException, MachineChoice, version_compare, is_windows, is_osx @@ -27,7 +28,8 @@ from .compilers import ( Compiler, CompilerArgs, ) -from .mixins.gnu import get_gcc_soname_args, GnuCompiler +from .mixins.gnu import GnuCompiler +from .mixins.islinker import LinkerEnvVarsMixin, BasicLinkerIsCompilerMixin d_feature_args = {'gcc': {'unittest': '-funittest', 'debug': '-fdebug', @@ -65,9 +67,6 @@ dmd_optimization_args = {'0': [], class DmdLikeCompilerMixin: - def get_linker_exelist(self): - return self.get_exelist() - def get_output_args(self, target): return ['-of=' + target] @@ -100,11 +99,6 @@ class DmdLikeCompilerMixin: # DMD and LDC does not currently return Makefile-compatible dependency info. return [] - def get_linker_search_args(self, dirname): - # -L is recognized as "add this to the search path" by the linker, - # while the compiler recognizes it as "pass to linker". - return ['-Wl,-L' + dirname] - def get_coverage_args(self): return ['-cov'] @@ -114,22 +108,111 @@ class DmdLikeCompilerMixin: def get_compile_only_args(self): return ['-c'] - def get_soname_args(self, *args): - # FIXME: Make this work for cross-compiling + def depfile_for_object(self, objfile): + return objfile + '.' + self.get_depfile_suffix() + + def get_depfile_suffix(self): + return 'deps' + + def get_pic_args(self): if is_windows(): return [] - elif is_osx(): - soname_args = get_gcc_soname_args(CompilerType.GCC_OSX, *args) - if soname_args: - return ['-Wl,' + ','.join(soname_args)] - return [] + return ['-fPIC'] + + def get_feature_args(self, kwargs, build_to_src): + res = [] + if 'unittest' in kwargs: + unittest = kwargs.pop('unittest') + unittest_arg = d_feature_args[self.id]['unittest'] + if not unittest_arg: + raise EnvironmentException('D compiler %s does not support the "unittest" feature.' % self.name_string()) + if unittest: + res.append(unittest_arg) + + if 'debug' in kwargs: + debug_level = -1 + debugs = kwargs.pop('debug') + if not isinstance(debugs, list): + debugs = [debugs] + + debug_arg = d_feature_args[self.id]['debug'] + if not debug_arg: + raise EnvironmentException('D compiler %s does not support conditional debug identifiers.' % self.name_string()) + + # Parse all debug identifiers and the largest debug level identifier + for d in debugs: + if isinstance(d, int): + if d > debug_level: + debug_level = d + elif isinstance(d, str) and d.isdigit(): + if int(d) > debug_level: + debug_level = int(d) + else: + res.append('{0}={1}'.format(debug_arg, d)) - return get_gcc_soname_args(CompilerType.GCC_STANDARD, *args) + if debug_level >= 0: + res.append('{0}={1}'.format(debug_arg, debug_level)) + + if 'versions' in kwargs: + version_level = -1 + versions = kwargs.pop('versions') + if not isinstance(versions, list): + versions = [versions] + + version_arg = d_feature_args[self.id]['version'] + if not version_arg: + raise EnvironmentException('D compiler %s does not support conditional version identifiers.' % self.name_string()) + + # Parse all version identifiers and the largest version level identifier + for v in versions: + if isinstance(v, int): + if v > version_level: + version_level = v + elif isinstance(v, str) and v.isdigit(): + if int(v) > version_level: + version_level = int(v) + else: + res.append('{0}={1}'.format(version_arg, v)) + + if version_level >= 0: + res.append('{0}={1}'.format(version_arg, version_level)) + + if 'import_dirs' in kwargs: + import_dirs = kwargs.pop('import_dirs') + if not isinstance(import_dirs, list): + import_dirs = [import_dirs] + + import_dir_arg = d_feature_args[self.id]['import_dir'] + if not import_dir_arg: + raise EnvironmentException('D compiler %s does not support the "string import directories" feature.' % self.name_string()) + for idir_obj in import_dirs: + basedir = idir_obj.get_curdir() + for idir in idir_obj.get_incdirs(): + # Avoid superfluous '/.' at the end of paths when d is '.' + if idir not in ('', '.'): + expdir = os.path.join(basedir, idir) + else: + expdir = basedir + srctreedir = os.path.join(build_to_src, expdir) + res.append('{0}{1}'.format(import_dir_arg, srctreedir)) + + if kwargs: + raise EnvironmentException('Unknown D compiler feature(s) selected: %s' % ', '.join(kwargs.keys())) + + return res + + def get_buildtype_linker_args(self, buildtype): + if buildtype != 'plain': + return self.get_target_arch_args() + return [] + + def get_std_exe_link_args(self): + return [] def gen_import_library_args(self, implibname): return ['-Wl,--out-implib=' + implibname] - def build_rpath_args(self, build_dir, from_dir, rpath_paths, build_rpath, install_rpath): + def build_rpath_args(self, env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath): if is_windows(): return [] @@ -295,6 +378,11 @@ class DmdLikeCompilerMixin: assert(buildtype == 'custom') raise EnvironmentException('Requested C runtime based on buildtype, but buildtype is "custom".') + def get_soname_args(self, *args, **kwargs) -> typing.List[str]: + # LDC and DMD actually do use a linker, but they proxy all of that with + # their own arguments + return Compiler.get_soname_args(self, *args, **kwargs) + class DCompiler(Compiler): mscrt_args = { @@ -337,9 +425,6 @@ class DCompiler(Compiler): return [] return ['-fPIC'] - def get_std_shared_lib_link_args(self): - return ['-shared'] - def get_feature_args(self, kwargs, build_to_src): res = [] if 'unittest' in kwargs: @@ -534,7 +619,7 @@ class GnuDCompiler(DCompiler, GnuCompiler): return parameter_list -class LLVMDCompiler(DmdLikeCompilerMixin, DCompiler): +class LLVMDCompiler(DmdLikeCompilerMixin, LinkerEnvVarsMixin, BasicLinkerIsCompilerMixin, DCompiler): def __init__(self, exelist, version, for_machine: MachineChoice, arch, **kwargs): DCompiler.__init__(self, exelist, version, for_machine, arch, **kwargs) self.id = 'llvm' @@ -572,7 +657,7 @@ class LLVMDCompiler(DmdLikeCompilerMixin, DCompiler): return ldc_optimization_args[optimization_level] -class DmdDCompiler(DmdLikeCompilerMixin, DCompiler): +class DmdDCompiler(DmdLikeCompilerMixin, LinkerEnvVarsMixin, BasicLinkerIsCompilerMixin, DCompiler): def __init__(self, exelist, version, for_machine: MachineChoice, arch, **kwargs): DCompiler.__init__(self, exelist, version, for_machine, arch, **kwargs) self.id = 'dmd' diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index 48c324f41..30ec6fea2 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -24,8 +24,7 @@ from .compilers import ( ) from .mixins.clike import CLikeCompiler from .mixins.gnu import ( - GnuCompiler, apple_buildtype_linker_args, gnulike_buildtype_args, - gnulike_buildtype_linker_args, gnu_optimization_args, + GnuCompiler, gnulike_buildtype_args, gnu_optimization_args, ) from .mixins.intel import IntelGnuLikeCompiler, IntelVisualStudioLikeCompiler from .mixins.clang import ClangCompiler @@ -101,11 +100,6 @@ class FortranCompiler(CLikeCompiler, Compiler): def get_debug_args(self, is_debug): return clike_debug_args[is_debug] - def get_buildtype_linker_args(self, buildtype): - if is_osx(): - return apple_buildtype_linker_args[buildtype] - return gnulike_buildtype_linker_args[buildtype] - def get_dependency_gen_args(self, outtarget, outfile): return [] diff --git a/mesonbuild/compilers/java.py b/mesonbuild/compilers/java.py index ea1fa0f76..fb1a1905c 100644 --- a/mesonbuild/compilers/java.py +++ b/mesonbuild/compilers/java.py @@ -17,8 +17,9 @@ import os.path, shutil, subprocess from ..mesonlib import EnvironmentException, MachineChoice from .compilers import Compiler, java_buildtype_args +from .mixins.islinker import BasicLinkerIsCompilerMixin -class JavaCompiler(Compiler): +class JavaCompiler(BasicLinkerIsCompilerMixin, Compiler): def __init__(self, exelist, version, for_machine: MachineChoice): self.language = 'java' super().__init__(exelist, version, for_machine) @@ -26,24 +27,15 @@ class JavaCompiler(Compiler): self.is_cross = False self.javarunner = 'java' - def get_soname_args(self, *args): - return [] - def get_werror_args(self): return ['-Werror'] def split_shlib_to_parts(self, fname): return None, fname - def build_rpath_args(self, build_dir, from_dir, rpath_paths, build_rpath, install_rpath): - return [] - def get_dependency_gen_args(self, outtarget, outfile): return [] - def get_linker_exelist(self): - return self.exelist[:] - def get_compile_only_args(self): return [] @@ -52,15 +44,9 @@ class JavaCompiler(Compiler): subdir = './' return ['-d', subdir, '-s', subdir] - def get_linker_output_args(self, outputname): - return [] - def get_coverage_args(self): return [] - def get_coverage_link_args(self): - return [] - def get_std_exe_link_args(self): return [] diff --git a/mesonbuild/compilers/mixins/arm.py b/mesonbuild/compilers/mixins/arm.py index 02654cebd..fca3d66e5 100644 --- a/mesonbuild/compilers/mixins/arm.py +++ b/mesonbuild/compilers/mixins/arm.py @@ -35,15 +35,6 @@ arm_buildtype_args = { 'custom': [], } # type: typing.Dict[str, typing.List[str]] -arm_buildtype_linker_args = { - 'plain': [], - 'debug': [], - 'debugoptimized': [], - 'release': [], - 'minsize': [], - 'custom': [], -} # type: typing.Dict[str, typing.List[str]] - arm_optimization_args = { '0': ['-O0'], 'g': ['-g'], @@ -87,8 +78,6 @@ class ArmCompiler: # Assembly self.can_compile_suffixes.add('s') - def can_linker_accept_rsp(self) -> bool: - return False def get_pic_args(self) -> typing.List[str]: # FIXME: Add /ropi, /rwpi, /fpic etc. qualifiers to --apcs @@ -97,9 +86,6 @@ class ArmCompiler: def get_buildtype_args(self, buildtype: str) -> typing.List[str]: return arm_buildtype_args[buildtype] - def get_buildtype_linker_args(self, buildtype: str) -> typing.List[str]: - return arm_buildtype_linker_args[buildtype] - # Override CCompiler.get_always_args def get_always_args(self) -> typing.List[str]: return [] @@ -108,10 +94,6 @@ class ArmCompiler: def get_dependency_gen_args(self, outtarget: str, outfile: str) -> typing.List[str]: return [] - # Override CCompiler.get_std_shared_lib_link_args - def get_std_shared_lib_link_args(self) -> typing.List[str]: - return [] - def get_pch_use_args(self, pch_dir: str, header: str) -> typing.List[str]: # FIXME: Add required arguments # NOTE from armcc user guide: @@ -130,19 +112,9 @@ class ArmCompiler: def thread_flags(self, env: 'Environment') -> typing.List[str]: return [] - def thread_link_flags(self, env: 'Environment') -> typing.List[str]: - return [] - - def get_linker_exelist(self) -> typing.List[str]: - args = ['armlink'] - return args - def get_coverage_args(self) -> typing.List[str]: return [] - def get_coverage_link_args(self) -> typing.List[str]: - return [] - def get_optimization_args(self, optimization_level: str) -> typing.List[str]: return arm_optimization_args[optimization_level] @@ -191,9 +163,6 @@ class ArmclangCompiler: # Assembly self.can_compile_suffixes.update('s') - def can_linker_accept_rsp(self) -> bool: - return False - def get_pic_args(self) -> typing.List[str]: # PIC support is not enabled by default for ARM, # if users want to use it, they need to add the required arguments explicitly @@ -205,13 +174,6 @@ class ArmclangCompiler: def get_buildtype_args(self, buildtype: str) -> typing.List[str]: return armclang_buildtype_args[buildtype] - def get_buildtype_linker_args(self, buildtype: str) -> typing.List[str]: - return arm_buildtype_linker_args[buildtype] - - # Override CCompiler.get_std_shared_lib_link_args - def get_std_shared_lib_link_args(self) -> typing.List[str]: - return [] - def get_pch_suffix(self) -> str: return 'gch' @@ -225,34 +187,12 @@ class ArmclangCompiler: def get_dependency_gen_args(self, outtarget: str, outfile: str) -> typing.List[str]: return [] - # Override CCompiler.build_rpath_args - def build_rpath_args(self, build_dir: str, from_dir: str, rpath_paths: str, - build_rpath: str, install_rpath: str) -> typing.List[str]: - return [] - - def get_linker_exelist(self) -> typing.List[str]: - return [self.linker_exe] - def get_optimization_args(self, optimization_level: str) -> typing.List[str]: return armclang_optimization_args[optimization_level] def get_debug_args(self, is_debug: bool) -> typing.List[str]: return clike_debug_args[is_debug] - def gen_export_dynamic_link_args(self, env: 'Environment') -> typing.List[str]: - """ - The args for export dynamic - """ - return ['--export_dynamic'] - - def gen_import_library_args(self, implibname: str) -> typing.List[str]: - """ - The args of the outputted import library - - ArmLinker's symdefs output can be used as implib - """ - return ['--symdefs=' + implibname] - def compute_parameters_with_absolute_paths(self, parameter_list: typing.List[str], build_dir: str) -> typing.List[str]: for idx, i in enumerate(parameter_list): if i[:2] == '-I' or i[:2] == '-L': diff --git a/mesonbuild/compilers/mixins/ccrx.py b/mesonbuild/compilers/mixins/ccrx.py index 0e435e3a5..d15411142 100644 --- a/mesonbuild/compilers/mixins/ccrx.py +++ b/mesonbuild/compilers/mixins/ccrx.py @@ -32,15 +32,6 @@ ccrx_buildtype_args = { 'custom': [], } # type: typing.Dict[str, typing.List[str]] -ccrx_buildtype_linker_args = { - 'plain': [], - 'debug': [], - 'debugoptimized': [], - 'release': [], - 'minsize': [], - 'custom': [], -} # type: typing.Dict[str, typing.List[str]] - ccrx_optimization_args = { '0': ['-optimize=0'], 'g': ['-optimize=0'], @@ -60,14 +51,6 @@ class CcrxCompiler: def __init__(self, compiler_type: 'CompilerType'): if not self.is_cross: raise EnvironmentException('ccrx supports only cross-compilation.') - # Check whether 'rlink.exe' is available in path - self.linker_exe = 'rlink.exe' - args = '--version' - try: - p, stdo, stderr = Popen_safe(self.linker_exe, args) - except OSError as e: - err_msg = 'Unknown linker\nRunning "{0}" gave \n"{1}"'.format(' '.join([self.linker_exe] + [args]), e) - raise EnvironmentException(err_msg) self.id = 'ccrx' self.compiler_type = compiler_type # Assembly @@ -78,9 +61,6 @@ class CcrxCompiler: '2': default_warn_args + [], '3': default_warn_args + []} - def can_linker_accept_rsp(self) -> bool: - return False - def get_pic_args(self) -> typing.List[str]: # PIC support is not enabled by default for CCRX, # if users want to use it, they need to add the required arguments explicitly @@ -89,13 +69,6 @@ class CcrxCompiler: def get_buildtype_args(self, buildtype: str) -> typing.List[str]: return ccrx_buildtype_args[buildtype] - def get_buildtype_linker_args(self, buildtype: str) -> typing.List[str]: - return ccrx_buildtype_linker_args[buildtype] - - # Override CCompiler.get_std_shared_lib_link_args - def get_std_shared_lib_link_args(self) -> typing.List[str]: - return [] - def get_pch_suffix(self) -> str: return 'pch' @@ -106,28 +79,12 @@ class CcrxCompiler: def get_dependency_gen_args(self, outtarget: str, outfile: str) -> typing.List[str]: return [] - # Override CCompiler.build_rpath_args - def build_rpath_args(self, build_dir: str, from_dir: str, rpath_paths: str, build_rpath: str, install_rpath: str) -> typing.List[str]: - return [] - def thread_flags(self, env: 'Environment') -> typing.List[str]: return [] - def thread_link_flags(self, env: 'Environment') -> typing.List[str]: - return [] - - def get_linker_exelist(self) -> typing.List[str]: - return [self.linker_exe] - - def get_linker_lib_prefix(self) -> str: - return '-lib=' - def get_coverage_args(self) -> typing.List[str]: return [] - def get_coverage_link_args(self) -> typing.List[str]: - return [] - def get_optimization_args(self, optimization_level: str) -> typing.List[str]: return ccrx_optimization_args[optimization_level] diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index 37d2424cd..3af176829 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -61,9 +61,6 @@ class CLikeCompiler: else: self.exe_wrapper = exe_wrapper.get_command() - # Set to None until we actually need to check this - self.has_fatal_warnings_link_arg = None - def needs_static_linker(self): return True # When compiling static libraries, so yes. @@ -73,13 +70,6 @@ class CLikeCompiler: ''' return ['-pipe'] + compilers.get_largefile_args(self) - def get_linker_debug_crt_args(self): - """ - Arguments needed to select a debug crt for the linker - This is only needed for MSVC - """ - return [] - def get_no_stdinc_args(self): return ['-nostdinc'] @@ -93,22 +83,9 @@ class CLikeCompiler: # Almost every compiler uses this for disabling warnings return ['-w'] - def get_soname_args(self, *args): - return [] - def split_shlib_to_parts(self, fname): return None, fname - # The default behavior is this, override in MSVC - @functools.lru_cache(maxsize=None) - def build_rpath_args(self, build_dir, from_dir, rpath_paths, build_rpath, install_rpath): - if self.compiler_type.is_windows_compiler: - return [] - return self.build_unix_rpath_args(build_dir, from_dir, rpath_paths, build_rpath, install_rpath) - - def get_dependency_gen_args(self, outtarget, outfile): - return ['-MD', '-MQ', outtarget, '-MF', outfile] - def depfile_for_object(self, objfile): return objfile + '.' + self.get_depfile_suffix() @@ -118,9 +95,6 @@ class CLikeCompiler: def get_exelist(self): return self.exelist[:] - def get_linker_exelist(self): - return self.exelist[:] - def get_preprocess_only_args(self): return ['-E', '-P'] @@ -140,19 +114,17 @@ class CLikeCompiler: def get_output_args(self, target): return ['-o', target] - def get_linker_output_args(self, outputname): - return ['-o', outputname] - def get_coverage_args(self): return ['--coverage'] - def get_coverage_link_args(self): - return ['--coverage'] + def get_coverage_link_args(self) -> typing.List[str]: + return self.linker.get_coverage_args() def get_werror_args(self): return ['-Werror'] def get_std_exe_link_args(self): + # TODO: is this a linker property? return [] def get_include_args(self, path, is_system): @@ -162,9 +134,6 @@ class CLikeCompiler: return ['-isystem', path] return ['-I' + path] - def get_std_shared_lib_link_args(self): - return ['-shared'] - def get_compiler_dirs(self, env: 'Environment', name: str) -> typing.List[str]: ''' Get dirs from the compiler, either `libraries:` or `programs:` @@ -222,27 +191,16 @@ class CLikeCompiler: return os.path.basename(header_name) + '.' + self.get_pch_suffix() def get_linker_search_args(self, dirname: str) -> typing.List[str]: - return ['-L' + dirname] + return self.linker.get_search_args(dirname) def get_default_include_dirs(self): return [] - def gen_export_dynamic_link_args(self, env) -> typing.List[str]: - m = env.machines[self.for_machine] - if m.is_windows() or m.is_cygwin(): - return ['-Wl,--export-all-symbols'] - elif env.machines[self.for_machine].is_darwin(): - return [] - else: - return ['-Wl,-export-dynamic'] + def gen_export_dynamic_link_args(self, env: 'Environment') -> typing.List[str]: + return self.linker.export_dynamic_args(env) def gen_import_library_args(self, implibname: str) -> typing.List[str]: - """ - The name of the outputted import library - - This implementation is used only on Windows by compilers that use GNU ld - """ - return ['-Wl,--out-implib=' + implibname] + return self.linker.import_library_args(implibname) def sanity_check_impl(self, work_dir, environment, sname, code): mlog.debug('Sanity testing ' + self.get_display_language() + ' compiler:', ' '.join(self.exelist)) @@ -1104,11 +1062,8 @@ class CLikeCompiler: return [] return ['-pthread'] - def thread_link_flags(self, env): - host_m = env.machines[self.for_machine] - if host_m.is_haiku() or host_m.is_darwin(): - return [] - return ['-pthread'] + def thread_link_flags(self, env: 'Environment') -> typing.List[str]: + return self.linker.thread_flags(env) def linker_to_compiler_args(self, args): return args @@ -1139,14 +1094,7 @@ class CLikeCompiler: # First time we check for link flags we need to first check if we have # --fatal-warnings, otherwise some linker checks could give some # false positive. - fatal_warnings_args = ['-Wl,--fatal-warnings'] - if self.has_fatal_warnings_link_arg is None: - self.has_fatal_warnings_link_arg = False - self.has_fatal_warnings_link_arg = self.has_multi_link_arguments(fatal_warnings_args, env)[0] - - if self.has_fatal_warnings_link_arg: - args = fatal_warnings_args + args - + args = self.linker.fatal_warnings() + args args = self.linker_to_compiler_args(args) code = 'int main() { return 0; }' return self.has_arguments(args, env, code, mode='link') diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py index 9756604c6..757dc652f 100644 --- a/mesonbuild/compilers/mixins/gnu.py +++ b/mesonbuild/compilers/mixins/gnu.py @@ -46,24 +46,6 @@ gnulike_buildtype_args = { 'custom': [], } # type: typing.Dict[str, typing.List[str]] -apple_buildtype_linker_args = { - 'plain': [], - 'debug': [], - 'debugoptimized': [], - 'release': [], - 'minsize': [], - 'custom': [], -} # type: typing.Dict[str, typing.List[str]] - -gnulike_buildtype_linker_args = { - 'plain': [], - 'debug': [], - 'debugoptimized': [], - 'release': ['-Wl,-O1'], - 'minsize': [], - 'custom': [], -} # type: typing.Dict[str, typing.List[str]] - gnu_optimization_args = { '0': [], 'g': ['-Og'], @@ -102,35 +84,6 @@ gnu_color_args = { } # type: typing.Dict[str, typing.List[str]] -def get_macos_dylib_install_name(prefix: str, shlib_name: str, suffix: str, soversion: str) -> str: - install_name = prefix + shlib_name - if soversion is not None: - install_name += '.' + soversion - install_name += '.dylib' - return '@rpath/' + install_name - - -def get_gcc_soname_args(compiler_type: 'CompilerType', prefix: str, - shlib_name: str, suffix: str, soversion: str, darwin_versions: - typing.Tuple[str, str], is_shared_module: bool) -> typing.List[str]: - if compiler_type.is_standard_compiler: - sostr = '' if soversion is None else '.' + soversion - return ['-Wl,-soname,%s%s.%s%s' % (prefix, shlib_name, suffix, sostr)] - elif compiler_type.is_windows_compiler: - # For PE/COFF the soname argument has no effect with GNU LD - return [] - elif compiler_type.is_osx_compiler: - if is_shared_module: - return [] - name = get_macos_dylib_install_name(prefix, shlib_name, suffix, soversion) - args = ['-install_name', name] - if darwin_versions: - args += ['-compatibility_version', darwin_versions[0], '-current_version', darwin_versions[1]] - return args - else: - raise RuntimeError('Not implemented yet.') - - # TODO: The result from calling compiler should be cached. So that calling this # function multiple times don't add latency. def gnulike_default_include_dirs(compiler: typing.List[str], lang: str) -> typing.List[str]: @@ -179,25 +132,13 @@ class GnuLikeCompiler(metaclass=abc.ABCMeta): self.compiler_type = compiler_type self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage', 'b_ndebug', 'b_staticpic', 'b_pie'] - if (not self.compiler_type.is_osx_compiler and - not self.compiler_type.is_windows_compiler and - not mesonlib.is_openbsd()): + if not (self.compiler_type.is_windows_compiler or mesonlib.is_openbsd()): self.base_options.append('b_lundef') if not self.compiler_type.is_windows_compiler: self.base_options.append('b_asneeded') # All GCC-like backends can do assembly self.can_compile_suffixes.add('s') - def get_asneeded_args(self) -> typing.List[str]: - # GNU ld cannot be installed on macOS - # https://github.com/Homebrew/homebrew-core/issues/17794#issuecomment-328174395 - # Hence, we don't need to differentiate between OS and ld - # for the sake of adding as-needed support - if self.compiler_type.is_osx_compiler: - return ['-Wl,-dead_strip_dylibs'] - else: - return ['-Wl,--as-needed'] - def get_pic_args(self) -> typing.List[str]: if self.compiler_type.is_osx_compiler or self.compiler_type.is_windows_compiler: return [] # On Window and OS X, pic is always on. @@ -206,9 +147,6 @@ class GnuLikeCompiler(metaclass=abc.ABCMeta): def get_pie_args(self) -> typing.List[str]: return ['-fPIE'] - def get_pie_link_args(self) -> typing.List[str]: - return ['-pie'] - def get_buildtype_args(self, buildtype: str) -> typing.List[str]: return gnulike_buildtype_args[buildtype] @@ -219,11 +157,6 @@ class GnuLikeCompiler(metaclass=abc.ABCMeta): def get_debug_args(self, is_debug: bool) -> typing.List[str]: return clike_debug_args[is_debug] - def get_buildtype_linker_args(self, buildtype: str) -> typing.List[str]: - if self.compiler_type.is_osx_compiler: - return apple_buildtype_linker_args[buildtype] - return gnulike_buildtype_linker_args[buildtype] - @abc.abstractmethod def get_pch_suffix(self) -> str: raise NotImplementedError("get_pch_suffix not implemented") @@ -231,27 +164,6 @@ class GnuLikeCompiler(metaclass=abc.ABCMeta): def split_shlib_to_parts(self, fname: str) -> typing.Tuple[str, str]: return os.path.dirname(fname), fname - # We're doing argument proxying here, I don't think there's anyway to - # accurately model this without copying the real signature - def get_soname_args(self, *args: typing.Any) -> typing.List[str]: - return get_gcc_soname_args(self.compiler_type, *args) - - def get_std_shared_lib_link_args(self) -> typing.List[str]: - return ['-shared'] - - def get_std_shared_module_link_args(self, options: typing.Dict[str, 'UserOption[typing.Any]']) -> typing.List[str]: - if self.compiler_type.is_osx_compiler: - return ['-bundle', '-Wl,-undefined,dynamic_lookup'] - return ['-shared'] - - def get_link_whole_for(self, args: typing.List[str]) -> typing.List[str]: - if self.compiler_type.is_osx_compiler: - result = [] # type: typing.List[str] - for a in args: - result += ['-Wl,-force_load', a] - return result - return ['-Wl,--whole-archive'] + args + ['-Wl,--no-whole-archive'] - def get_instruction_set_args(self, instruction_set: str) -> typing.Optional[typing.List[str]]: return gnulike_instruction_set_args.get(instruction_set, None) @@ -284,19 +196,6 @@ class GnuLikeCompiler(metaclass=abc.ABCMeta): def get_profile_use_args(self) -> typing.List[str]: return ['-fprofile-use', '-fprofile-correction'] - def get_allow_undefined_link_args(self) -> typing.List[str]: - if self.compiler_type.is_osx_compiler: - # Apple ld - return ['-Wl,-undefined,dynamic_lookup'] - elif self.compiler_type.is_windows_compiler: - # For PE/COFF this is impossible - return [] - elif mesonlib.is_sunos(): - return [] - else: - # GNU ld and LLVM lld - return ['-Wl,--allow-shlib-undefined'] - def get_gui_app_args(self, value: bool) -> typing.List[str]: if self.compiler_type.is_windows_compiler: return ['-mwindows' if value else '-mconsole'] @@ -369,9 +268,6 @@ class GnuLikeCompiler(metaclass=abc.ABCMeta): def get_lto_compile_args(self) -> typing.List[str]: return ['-flto'] - def get_lto_link_args(self) -> typing.List[str]: - return ['-flto'] - def sanitizer_compile_args(self, value: str) -> typing.List[str]: if value == 'none': return [] @@ -380,10 +276,19 @@ class GnuLikeCompiler(metaclass=abc.ABCMeta): args.append('-fno-omit-frame-pointer') return args - def sanitizer_link_args(self, value: str) -> typing.List[str]: - if value == 'none': - return [] - return ['-fsanitize=' + value] + def get_output_args(self, target: str) -> typing.List[str]: + return ['-o', target] + + def get_dependency_gen_args(self, outtarget, outfile): + return ['-MD', '-MQ', outtarget, '-MF', outfile] + + def get_compile_only_args(self) -> typing.List[str]: + return ['-c'] + + def get_include_args(self, path: str, is_system: bool) -> typing.List[str]: + if is_system: + return ['-isystem' + path] + return ['-I' + path] class GnuCompiler(GnuLikeCompiler): diff --git a/mesonbuild/compilers/mixins/intel.py b/mesonbuild/compilers/mixins/intel.py index 7fadb50eb..d7e1b21cc 100644 --- a/mesonbuild/compilers/mixins/intel.py +++ b/mesonbuild/compilers/mixins/intel.py @@ -133,8 +133,5 @@ class IntelVisualStudioLikeCompiler(VisualStudioLikeCompiler): version = int(v1 + v2) return self._calculate_toolset_version(version) - def get_linker_exelist(self) -> typing.List[str]: - return ['xilink'] - def openmp_flags(self) -> typing.List[str]: return ['/Qopenmp'] diff --git a/mesonbuild/compilers/mixins/pgi.py b/mesonbuild/compilers/mixins/pgi.py index ff2e9bf08..c13c7bc20 100644 --- a/mesonbuild/compilers/mixins/pgi.py +++ b/mesonbuild/compilers/mixins/pgi.py @@ -33,17 +33,7 @@ pgi_buildtype_args = { } # type: typing.Dict[str, typing.List[str]] -pgi_buildtype_linker_args = { - 'plain': [], - 'debug': [], - 'debugoptimized': [], - 'release': [], - 'minsize': [], - 'custom': [], -} # type: typing.Dict[str, typing.List[str]] - - -class PGICompiler(): +class PGICompiler: def __init__(self, compiler_type: 'CompilerType'): self.base_options = ['b_pch'] self.id = 'pgi' @@ -64,14 +54,6 @@ class PGICompiler(): def gen_import_library_args(self, implibname: str) -> typing.List[str]: return [] - def get_std_shared_lib_link_args(self) -> typing.List[str]: - # PGI -shared is Linux only. - if self.compiler_type.is_windows_compiler: - return ['-Bdynamic', '-Mmakedll'] - elif not self.compiler_type.is_osx_compiler: - return ['-shared'] - return [] - def get_pic_args(self) -> typing.List[str]: # PGI -fPIC is Linux only. if self.compiler_type.is_linux_compiler(): @@ -84,9 +66,6 @@ class PGICompiler(): def get_buildtype_args(self, buildtype: str) -> typing.List[str]: return pgi_buildtype_args[buildtype] - def get_buildtype_linker_args(self, buildtype: str) -> typing.List[str]: - return pgi_buildtype_linker_args[buildtype] - def get_optimization_args(self, optimization_level: str) -> typing.List[str]: return clike_optimization_args[optimization_level] @@ -99,9 +78,6 @@ class PGICompiler(): parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:])) return parameter_list - def get_allow_undefined_link_args(self) -> typing.List[str]: - return [] - def get_dependency_gen_args(self, outtarget: str, outfile: str) -> typing.List[str]: return [] diff --git a/mesonbuild/compilers/mixins/visualstudio.py b/mesonbuild/compilers/mixins/visualstudio.py index edb104d0c..5fe85996d 100644 --- a/mesonbuild/compilers/mixins/visualstudio.py +++ b/mesonbuild/compilers/mixins/visualstudio.py @@ -61,17 +61,6 @@ msvc_buildtype_args = { 'custom': [], } # type: typing.Dict[str, typing.List[str]] -msvc_buildtype_linker_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: typing.Dict[str, typing.List[str]] - msvc_optimization_args = { '0': [], 'g': ['/O0'], @@ -133,31 +122,18 @@ class VisualStudioLikeCompiler(metaclass=abc.ABCMeta): self.machine = 'x86' else: self.machine = target + self.linker.machine = self.machine # Override CCompiler.get_always_args def get_always_args(self) -> typing.List[str]: return self.always_args - def get_linker_debug_crt_args(self) -> typing.List[str]: - """ - Arguments needed to select a debug crt for the linker - - Sometimes we need to manually select the CRT (C runtime) to use with - MSVC. One example is when trying to link with static libraries since - MSVC won't auto-select a CRT for us in that case and will error out - asking us to select one. - """ - return ['/MDd'] - def get_buildtype_args(self, buildtype: str) -> typing.List[str]: args = msvc_buildtype_args[buildtype] if self.id == 'msvc' and mesonlib.version_compare(self.version, '<18.0'): args = [arg for arg in args if arg != '/Gw'] return args - def get_buildtype_linker_args(self, buildtype: str) -> typing.List[str]: - return msvc_buildtype_linker_args[buildtype] - def get_pch_suffix(self) -> str: return 'pch' @@ -197,23 +173,6 @@ class VisualStudioLikeCompiler(metaclass=abc.ABCMeta): def get_dependency_gen_args(self, outtarget: str, outfile: str) -> typing.List[str]: return [] - def get_linker_exelist(self) -> typing.List[str]: - # FIXME, should have same path as compiler. - # FIXME, should be controllable via cross-file. - if self.id == 'clang-cl': - return ['lld-link'] - else: - return ['link'] - - def get_linker_always_args(self) -> typing.List[str]: - return ['/nologo'] - - def get_linker_output_args(self, outputname: str) -> typing.List[str]: - return ['/MACHINE:' + self.machine, '/OUT:' + outputname] - - def get_linker_search_args(self, dirname: str) -> typing.List[str]: - return ['/LIBPATH:' + dirname] - def linker_to_compiler_args(self, args: typing.List[str]) -> typing.List[str]: return ['/link'] + args @@ -228,12 +187,6 @@ class VisualStudioLikeCompiler(metaclass=abc.ABCMeta): def get_pic_args(self) -> typing.List[str]: return [] # PIC is handled by the loader on Windows - def gen_export_dynamic_link_args(self, env: 'Environment') -> typing.List[str]: - return [] # Not applicable with MSVC - - def get_std_shared_lib_link_args(self) -> typing.List[str]: - return ['/DLL'] - def gen_vs_module_defs_args(self, defsfile: str) -> typing.List[str]: if not isinstance(defsfile, str): raise RuntimeError('Module definitions file should be str') @@ -249,9 +202,6 @@ class VisualStudioLikeCompiler(metaclass=abc.ABCMeta): "The name of the outputted import library" return ['/IMPLIB:' + implibname] - def build_rpath_args(self, build_dir: str, from_dir: str, rpath_paths: str, build_rpath: str, install_rpath: str) -> typing.List[str]: - return [] - def openmp_flags(self) -> typing.List[str]: return ['/openmp'] @@ -259,9 +209,6 @@ class VisualStudioLikeCompiler(metaclass=abc.ABCMeta): def thread_flags(self, env: 'Environment') -> typing.List[str]: return [] - def thread_link_flags(self, env: 'Environment') -> typing.List[str]: - return [] - @classmethod def unix_args_to_native(cls, args: typing.List[str]) -> typing.List[str]: result = [] @@ -331,16 +278,6 @@ class VisualStudioLikeCompiler(metaclass=abc.ABCMeta): args = ['/FS'] + args return args - def get_link_debugfile_args(self, targetfile: str) -> typing.List[str]: - pdbarr = targetfile.split('.')[:-1] - pdbarr += ['pdb'] - return ['/DEBUG', '/PDB:' + '.'.join(pdbarr)] - - def get_link_whole_for(self, args: typing.List[str]) -> typing.List[str]: - # Only since VS2015 - args = mesonlib.listify(args) - return ['/WHOLEARCHIVE:' + x for x in args] - def get_instruction_set_args(self, instruction_set: str) -> typing.Optional[typing.List[str]]: if self.is_64: return vs64_instruction_set_args.get(instruction_set, None) @@ -418,7 +355,3 @@ class VisualStudioLikeCompiler(metaclass=abc.ABCMeta): def get_argument_syntax(self) -> str: return 'msvc' - - def get_allow_undefined_link_args(self) -> typing.List[str]: - # link.exe - return ['/FORCE:UNRESOLVED'] diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index c482e77ea..cecbe6473 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -13,11 +13,14 @@ # limitations under the License. import subprocess, os.path +import typing from ..mesonlib import EnvironmentException, MachineChoice, Popen_safe - from .compilers import Compiler, rust_buildtype_args, clike_debug_args +if typing.TYPE_CHECKING: + from ..environment import Environment # noqa: F401 + rust_optimization_args = {'0': [], 'g': ['-C', '--opt-level=0'], '1': ['-C', '--opt-level=1'], @@ -77,9 +80,6 @@ class RustCompiler(Compiler): def get_buildtype_args(self, buildtype): return rust_buildtype_args[buildtype] - def build_rpath_args(self, build_dir, from_dir, rpath_paths, build_rpath, install_rpath): - return self.build_unix_rpath_args(build_dir, from_dir, rpath_paths, build_rpath, install_rpath) - def get_sysroot(self): cmd = self.exelist + ['--print', 'sysroot'] p, stdo, stde = Popen_safe(cmd) @@ -102,8 +102,5 @@ class RustCompiler(Compiler): return parameter_list - def get_buildtype_linker_args(self, build_type): - return [] - def get_std_exe_link_args(self): return [] diff --git a/mesonbuild/compilers/swift.py b/mesonbuild/compilers/swift.py index b979d2d33..6c639fd88 100644 --- a/mesonbuild/compilers/swift.py +++ b/mesonbuild/compilers/swift.py @@ -34,9 +34,6 @@ class SwiftCompiler(Compiler): self.id = 'llvm' self.is_cross = is_cross - def get_linker_exelist(self): - return self.exelist[:] - def name_string(self): return ' '.join(self.exelist) @@ -58,9 +55,6 @@ class SwiftCompiler(Compiler): def get_output_args(self, target): return ['-o', target] - def get_linker_output_args(self, target): - return ['-o', target] - def get_header_import_args(self, headername): return ['-import-objc-header', headername] @@ -70,9 +64,6 @@ class SwiftCompiler(Compiler): def get_buildtype_args(self, buildtype): return swift_buildtype_args[buildtype] - def get_buildtype_linker_args(self, buildtype): - return [] - def get_std_exe_link_args(self): return ['-emit-executable'] @@ -82,9 +73,6 @@ class SwiftCompiler(Compiler): def get_mod_gen_args(self): return ['-emit-module'] - def build_rpath_args(self, *args): - return [] # FIXME - def get_include_args(self, dirname): return ['-I' + dirname] diff --git a/mesonbuild/linkers.py b/mesonbuild/linkers.py index c80720d21..a641cd007 100644 --- a/mesonbuild/linkers.py +++ b/mesonbuild/linkers.py @@ -54,8 +54,9 @@ class StaticLinker: def get_coverage_link_args(self) -> typing.List[str]: return [] - def build_rpath_args(self, build_dir: str, from_dir: str, rpath_paths: str, - build_rpath: str, install_rpath: str) -> typing.List[str]: + def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, + rpath_paths: str, build_rpath: str, + install_rpath: str) -> typing.List[str]: return [] def thread_link_flags(self, env: 'Environment') -> typing.List[str]: