diff options
33 files changed, 1464 insertions, 926 deletions
diff --git a/docs/markdown/snippets/b_lundef_on_apple.md b/docs/markdown/snippets/b_lundef_on_apple.md new file mode 100644 index 0000000..850a412 --- /dev/null +++ b/docs/markdown/snippets/b_lundef_on_apple.md @@ -0,0 +1,5 @@ +## Meson's builtin b_lundef is now supported on macOS + +This has always been possible, but there are some addtional restrictions on +macOS (mainly do to Apple only features). With the linker internal +re-architecture this has become possible
\ No newline at end of file diff --git a/docs/markdown/snippets/split-compiler-and-linker-representations.md b/docs/markdown/snippets/split-compiler-and-linker-representations.md new file mode 100644 index 0000000..fd2e622 --- /dev/null +++ b/docs/markdown/snippets/split-compiler-and-linker-representations.md @@ -0,0 +1,6 @@ +## Compiler and dynamic linker representation split + +0.52.0 inclues a massive refactor of the representaitons of compilers to +tease apart the representations of compilers and dynamic linkers (ld). This +fixes a number of compiler/linker combinations. In particular this fixes +use GCC and vanilla clang on macOS.
\ No newline at end of file diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index eb28f1a..82e70c9 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1328,7 +1328,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, @@ -2325,9 +2326,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)) @@ -2538,7 +2540,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 eab2057..49ec8e8 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 5cd56b8..c81fe75 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 f50b77c..1ec9146 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.') @@ -293,14 +276,14 @@ class VisualStudioLikeCCompilerMixin: class VisualStudioCCompiler(VisualStudioLikeCompiler, VisualStudioLikeCCompilerMixin, CCompiler): - def __init__(self, exelist, version, for_machine: MachineChoice, is_cross, exe_wrap, target: str): - CCompiler.__init__(self, exelist, version, for_machine, is_cross, exe_wrap) + def __init__(self, exelist, version, for_machine: MachineChoice, is_cross, exe_wrap, target: str, **kwargs): + CCompiler.__init__(self, exelist, version, for_machine, is_cross, exe_wrap, **kwargs) VisualStudioLikeCompiler.__init__(self, target) self.id = 'msvc' class ClangClCCompiler(VisualStudioLikeCompiler, VisualStudioLikeCCompilerMixin, CCompiler): - def __init__(self, exelist, version, for_machine: MachineChoice, is_cross, exe_wrap, target): - CCompiler.__init__(self, exelist, version, for_machine, is_cross, exe_wrap) + def __init__(self, exelist, version, for_machine: MachineChoice, is_cross, exe_wrap, target, **kwargs): + CCompiler.__init__(self, exelist, version, for_machine, is_cross, exe_wrap, **kwargs) VisualStudioLikeCompiler.__init__(self, target) self.id = 'clang-cl' @@ -311,8 +294,8 @@ class IntelClCCompiler(IntelVisualStudioLikeCompiler, VisualStudioLikeCCompilerM __have_warned = False - def __init__(self, exelist, version, for_machine: MachineChoice, is_cross, exe_wrap, target): - CCompiler.__init__(self, exelist, version, for_machine, is_cross, exe_wrap) + def __init__(self, exelist, version, for_machine: MachineChoice, is_cross, exe_wrap, target, **kwargs): + CCompiler.__init__(self, exelist, version, for_machine, is_cross, exe_wrap, **kwargs) IntelVisualStudioLikeCompiler.__init__(self, target) def get_options(self): @@ -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 5249068..f16e447 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 5808b2e..f93db3e 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.') @@ -473,8 +462,8 @@ class CPP11AsCPP14Mixin: class VisualStudioCPPCompiler(CPP11AsCPP14Mixin, VisualStudioLikeCPPCompilerMixin, VisualStudioLikeCompiler, CPPCompiler): - def __init__(self, exelist, version, for_machine: MachineChoice, is_cross: bool, exe_wrap, target): - CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, exe_wrap) + def __init__(self, exelist, version, for_machine: MachineChoice, is_cross: bool, exe_wrap, target, **kwargs): + CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, exe_wrap, **kwargs) VisualStudioLikeCompiler.__init__(self, target) self.base_options = ['b_pch', 'b_vscrt'] # FIXME add lto, pgo and the like self.id = 'msvc' @@ -506,8 +495,8 @@ class VisualStudioCPPCompiler(CPP11AsCPP14Mixin, VisualStudioLikeCPPCompilerMixi return args class ClangClCPPCompiler(CPP11AsCPP14Mixin, VisualStudioLikeCPPCompilerMixin, VisualStudioLikeCompiler, CPPCompiler): - def __init__(self, exelist, version, for_machine: MachineChoice, is_cross, exe_wrap, target): - CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, exe_wrap) + def __init__(self, exelist, version, for_machine: MachineChoice, is_cross, exe_wrap, target, **kwargs): + CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, exe_wrap, **kwargs) VisualStudioLikeCompiler.__init__(self, target) self.id = 'clang-cl' @@ -518,8 +507,8 @@ class ClangClCPPCompiler(CPP11AsCPP14Mixin, VisualStudioLikeCPPCompilerMixin, Vi class IntelClCPPCompiler(VisualStudioLikeCPPCompilerMixin, IntelVisualStudioLikeCompiler, CPPCompiler): - def __init__(self, exelist, version, for_machine: MachineChoice, is_cross, exe_wrap, target): - CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, exe_wrap) + def __init__(self, exelist, version, for_machine: MachineChoice, is_cross, exe_wrap, target, **kwargs): + CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, exe_wrap, **kwargs) IntelVisualStudioLikeCompiler.__init__(self, target) def get_options(self): @@ -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 8069ab1..7c884c9 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 152c8ba..b6bafe7 100644 --- a/mesonbuild/compilers/cuda.py +++ b/mesonbuild/compilers/cuda.py @@ -12,19 +12,23 @@ # See the License for the specific language governing permissions and # limitations under the License. -import re, os.path +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) -from .mixins.gnu import get_gcc_soname_args + +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): + def __init__(self, exelist, version, for_machine: MachineChoice, is_cross, exe_wrapper=None, **kwargs): if not hasattr(self, 'language'): self.language = 'cuda' - super().__init__(exelist, version, for_machine) + super().__init__(exelist, version, for_machine, **kwargs) self.is_cross = is_cross self.exe_wrapper = exe_wrapper self.id = 'nvcc' @@ -48,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)) @@ -143,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: @@ -160,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 [re.sub('^-Wl,', '-Xlinker=', 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 [] @@ -195,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] @@ -212,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 @@ -242,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 1a37c63..18e3bf9 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, gnu_color_args, gnu_optimization_args +from .mixins.gnu import GnuCompiler +from .mixins.islinker import LinkerEnvVarsMixin, BasicLinkerIsCompilerMixin d_feature_args = {'gcc': {'unittest': '-funittest', 'debug': '-fdebug', @@ -62,44 +64,8 @@ dmd_optimization_args = {'0': [], 's': ['-O'], } -class DCompiler(Compiler): - mscrt_args = { - 'none': ['-mscrtlib='], - 'md': ['-mscrtlib=msvcrt'], - 'mdd': ['-mscrtlib=msvcrtd'], - 'mt': ['-mscrtlib=libcmt'], - 'mtd': ['-mscrtlib=libcmtd'], - } - - def __init__(self, exelist, version, for_machine: MachineChoice, arch, **kwargs): - self.language = 'd' - super().__init__(exelist, version, for_machine, **kwargs) - self.id = 'unknown' - self.arch = arch - - def sanity_check(self, work_dir, environment): - source_name = os.path.join(work_dir, 'sanity.d') - output_name = os.path.join(work_dir, 'dtest') - with open(source_name, 'w') as ofile: - ofile.write('''void main() { }''') - pc = subprocess.Popen(self.exelist + self.get_output_args(output_name) + self.get_target_arch_args() + [source_name], cwd=work_dir) - pc.wait() - if pc.returncode != 0: - raise EnvironmentException('D compiler %s can not compile programs.' % self.name_string()) - if subprocess.call(output_name) != 0: - raise EnvironmentException('Executables created by D compiler %s are not runnable.' % self.name_string()) - - def needs_static_linker(self): - return True - - def name_string(self): - return ' '.join(self.exelist) - def get_exelist(self): - return self.exelist - - def get_linker_exelist(self): - return self.exelist[:] +class DmdLikeCompilerMixin: def get_output_args(self, target): return ['-of=' + target] @@ -133,11 +99,6 @@ class DCompiler(Compiler): # 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'] @@ -158,21 +119,6 @@ class DCompiler(Compiler): return [] return ['-fPIC'] - def get_std_shared_lib_link_args(self): - return ['-shared'] - - def get_soname_args(self, *args): - # FIXME: Make this work for cross-compiling - 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 get_gcc_soname_args(CompilerType.GCC_STANDARD, *args) - def get_feature_args(self, kwargs, build_to_src): res = [] if 'unittest' in kwargs: @@ -266,7 +212,7 @@ class DCompiler(Compiler): 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 [] @@ -285,54 +231,6 @@ class DCompiler(Compiler): paths = paths + ':' + padding return ['-Wl,-rpath,{}'.format(paths)] - def _get_compiler_check_args(self, env, extra_args, dependencies, mode='compile'): - if callable(extra_args): - extra_args = extra_args(mode) - if extra_args is None: - extra_args = [] - elif isinstance(extra_args, str): - extra_args = [extra_args] - if dependencies is None: - dependencies = [] - elif not isinstance(dependencies, list): - dependencies = [dependencies] - # Collect compiler arguments - args = CompilerArgs(self) - for d in dependencies: - # Add compile flags needed by dependencies - args += d.get_compile_args() - if mode == 'link': - # Add link flags needed to find dependencies - args += d.get_link_args() - - if mode == 'compile': - # Add DFLAGS from the env - args += env.coredata.get_external_args(self.for_machine, self.language) - elif mode == 'link': - # Add LDFLAGS from the env - args += env.coredata.get_external_link_args(self.for_machine, self.language) - # extra_args must override all other arguments, so we add them last - args += extra_args - return args - - def compiles(self, code, env, *, extra_args=None, dependencies=None, mode='compile'): - args = self._get_compiler_check_args(env, extra_args, dependencies, mode) - - with self.cached_compile(code, env.coredata, extra_args=args, mode=mode) as p: - return p.returncode == 0, p.cached - - def has_multi_arguments(self, args, env): - return self.compiles('int i;\n', env, extra_args=args) - - def get_target_arch_args(self): - # LDC2 on Windows targets to current OS architecture, but - # it should follow the target specified by the MSVC toolchain. - if is_windows(): - if self.arch == 'x86_64': - return ['-m64'] - return ['-m32'] - return [] - @classmethod def translate_args_to_nongnu(cls, args): dcargs = [] @@ -480,6 +378,191 @@ class DCompiler(Compiler): 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 = { + 'none': ['-mscrtlib='], + 'md': ['-mscrtlib=msvcrt'], + 'mdd': ['-mscrtlib=msvcrtd'], + 'mt': ['-mscrtlib=libcmt'], + 'mtd': ['-mscrtlib=libcmtd'], + } + + def __init__(self, exelist, version, for_machine: MachineChoice, arch, **kwargs): + self.language = 'd' + super().__init__(exelist, version, for_machine, **kwargs) + self.id = 'unknown' + self.arch = arch + + def sanity_check(self, work_dir, environment): + source_name = os.path.join(work_dir, 'sanity.d') + output_name = os.path.join(work_dir, 'dtest') + with open(source_name, 'w') as ofile: + ofile.write('''void main() { }''') + pc = subprocess.Popen(self.exelist + self.get_output_args(output_name) + self.get_target_arch_args() + [source_name], cwd=work_dir) + pc.wait() + if pc.returncode != 0: + raise EnvironmentException('D compiler %s can not compile programs.' % self.name_string()) + if subprocess.call(output_name) != 0: + raise EnvironmentException('Executables created by D compiler %s are not runnable.' % self.name_string()) + + def needs_static_linker(self): + return True + + 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 [] + 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)) + + 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 _get_compiler_check_args(self, env, extra_args, dependencies, mode='compile'): + if callable(extra_args): + extra_args = extra_args(mode) + if extra_args is None: + extra_args = [] + elif isinstance(extra_args, str): + extra_args = [extra_args] + if dependencies is None: + dependencies = [] + elif not isinstance(dependencies, list): + dependencies = [dependencies] + # Collect compiler arguments + args = CompilerArgs(self) + for d in dependencies: + # Add compile flags needed by dependencies + args += d.get_compile_args() + if mode == 'link': + # Add link flags needed to find dependencies + args += d.get_link_args() + + if mode == 'compile': + # Add DFLAGS from the env + args += env.coredata.get_external_args(self.for_machine, self.language) + elif mode == 'link': + # Add LDFLAGS from the env + args += env.coredata.get_external_link_args(self.for_machine, self.language) + # extra_args must override all other arguments, so we add them last + args += extra_args + return args + + def compiles(self, code, env, *, extra_args=None, dependencies=None, mode='compile'): + args = self._get_compiler_check_args(env, extra_args, dependencies, mode) + + with self.cached_compile(code, env.coredata, extra_args=args, mode=mode) as p: + return p.returncode == 0, p.cached + + def has_multi_arguments(self, args, env): + return self.compiles('int i;\n', env, extra_args=args) + + def get_target_arch_args(self): + # LDC2 on Windows targets to current OS architecture, but + # it should follow the target specified by the MSVC toolchain. + if is_windows(): + if self.arch == 'x86_64': + return ['-m64'] + return ['-m32'] + return [] + def get_crt_compile_args(self, crt_val, buildtype): return [] @@ -489,7 +572,11 @@ class DCompiler(Compiler): def thread_link_flags(self, env): return ['-pthread'] -class GnuDCompiler(DCompiler): + def name_string(self): + return ' '.join(self.exelist) + + +class GnuDCompiler(DCompiler, GnuCompiler): def __init__(self, exelist, version, for_machine: MachineChoice, arch, **kwargs): DCompiler.__init__(self, exelist, version, for_machine, arch, **kwargs) self.id = 'gcc' @@ -507,32 +594,17 @@ class GnuDCompiler(DCompiler): def get_colorout_args(self, colortype): if self._has_color_support: - return gnu_color_args[colortype][:] + super().get_colorout_args(colortype) return [] def get_dependency_gen_args(self, outtarget, outfile): - if not self._has_deps_support: - return [] - return ['-MD', '-MQ', outtarget, '-MF', outfile] - - def get_output_args(self, target): - return ['-o', target] - - def get_linker_output_args(self, target): - return ['-o', target] - - def get_include_args(self, path, is_system): - return ['-I' + path] + if self._has_deps_support: + return super().get_dependency_gen_args(outtarget, outfile) + return [] def get_warn_args(self, level): return self.warn_args[level] - def get_werror_args(self): - return ['-Werror'] - - def get_linker_search_args(self, dirname): - return ['-L' + dirname] - def get_coverage_args(self): return [] @@ -546,13 +618,8 @@ class GnuDCompiler(DCompiler): return parameter_list - 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_optimization_args(self, optimization_level): - return gnu_optimization_args[optimization_level] -class LLVMDCompiler(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' @@ -590,7 +657,7 @@ class LLVMDCompiler(DCompiler): return ldc_optimization_args[optimization_level] -class DmdDCompiler(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 c10e2ca..30ec6fe 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -11,9 +11,11 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. + +from pathlib import Path from typing import List import subprocess, os -from pathlib import Path +import typing from .compilers import ( CompilerType, @@ -22,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 @@ -99,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 [] @@ -256,6 +252,10 @@ class IntelFortranCompiler(IntelGnuLikeCompiler, FortranCompiler): def language_stdlib_only_link_flags(self): return ['-lifcore', '-limf'] + def get_dependency_gen_args(self, outtarget: str, outfile: str) -> typing.List[str]: + return ['-gen-dep=' + outtarget, '-gen-depformat=make'] + + class IntelClFortranCompiler(IntelVisualStudioLikeCompiler, FortranCompiler): file_suffixes = ['f90', 'f', 'for', 'ftn', 'fpp'] @@ -270,8 +270,8 @@ class IntelClFortranCompiler(IntelVisualStudioLikeCompiler, FortranCompiler): 'custom': [], } - def __init__(self, exelist, for_machine: MachineChoice, version, is_cross, target: str, exe_wrapper=None): - FortranCompiler.__init__(self, exelist, for_machine, version, is_cross, exe_wrapper) + def __init__(self, exelist, for_machine: MachineChoice, version, is_cross, target: str, exe_wrapper=None, **kwargs): + FortranCompiler.__init__(self, exelist, for_machine, version, is_cross, exe_wrapper, **kwargs) IntelVisualStudioLikeCompiler.__init__(self, target) default_warn_args = ['/warn:general', '/warn:truncated_source'] diff --git a/mesonbuild/compilers/java.py b/mesonbuild/compilers/java.py index ea1fa0f..fb1a190 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 02654ce..fca3d66 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 48b5ed1..4de06fd 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 37d2424..3af1768 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 9756604..757dc65 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 7fadb50..d7e1b21 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/islinker.py b/mesonbuild/compilers/mixins/islinker.py new file mode 100644 index 0000000..4c1a476 --- /dev/null +++ b/mesonbuild/compilers/mixins/islinker.py @@ -0,0 +1,124 @@ +# Copyright 2019 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. + +"""Mixins for compilers that *are* linkers. + +While many compilers (such as gcc and clang) are used by meson to dispatch +linker commands and other (like MSVC) are not, a few (such as DMD) actually +are both the linker and compiler in one binary. This module provides mixin +classes for those cases. +""" + +import os +import shlex +import typing + +from ... import mesonlib + +if typing.TYPE_CHECKING: + from ...coredata import OptionDictType + from ...environment import Environment + + +class LinkerEnvVarsMixin: + + """Mixin reading LDFLAGS from the environment.""" + + def get_linker_args_from_envvars(self) -> typing.List[str]: + flags = os.environ.get('LDFLAGS') + if not flags: + return [] + return shlex.split(flags) + + +class BasicLinkerIsCompilerMixin: + + """Provides a baseline of methods that a linker would implement. + + In every case this provides a "no" or "empty" answer. If a compiler + implements any of these it needs a different mixin or to override that + functionality itself. + """ + + def sanitizer_link_args(self, value: str) -> typing.List[str]: + return [] + + def get_lto_link_args(self) -> typing.List[str]: + return [] + + def can_linker_accept_rsp(self) -> bool: + return mesonlib.is_windows() + + def get_linker_exelist(self) -> typing.List[str]: + return self.exelist.copy() + + def get_linker_output_args(self, output: str) -> typing.List[str]: + return [] + + def get_linker_always_args(self) -> typing.List[str]: + return [] + + def get_linker_lib_prefix(self) -> str: + return '' + + def get_option_link_args(self, options: 'OptionDictType') -> typing.List[str]: + return [] + + def has_multi_link_args(self, args: typing.List[str], env: 'Environment') -> typing.Tuple[bool, bool]: + return False, False + + def get_link_debugfile_args(self, targetfile: str) -> typing.List[str]: + return [] + + def get_std_shared_lib_link_args(self) -> typing.List[str]: + return [] + + def get_std_shared_module_args(self, options: 'OptionDictType') -> typing.List[str]: + return self.get_std_shared_lib_link_args() + + def get_link_whole_for(self, args: typing.List[str]) -> typing.List[str]: + raise mesonlib.EnvironmentException( + 'Linker {} does not support link_whole'.format(self.id)) + + def get_allow_undefined_args(self) -> typing.List[str]: + raise mesonlib.EnvironmentException( + 'Linker {} does not support allow undefined'.format(self.id)) + + def get_pie_link_args(self) -> typing.List[str]: + m = 'Linker {} does not support position-independent executable' + raise mesonlib.EnvironmentException(m.format(self.id)) + + def get_undefined_link_args(self) -> typing.List[str]: + return [] + + def get_coverage_link_args(self) -> typing.List[str]: + m = "Linker {} doesn't implement coverage data generation.".format(self.id) + raise mesonlib.EnvironmentException(m) + + def no_undefined_link_args(self) -> typing.List[str]: + return [] + + def bitcode_args(self) -> typing.List[str]: + raise mesonlib.MesonException("This linker doesn't support bitcode bundles") + + def get_soname_args(self, for_machine: 'mesonlib.MachineChoice', + prefix: str, shlib_name: str, suffix: str, soversion: str, + darwin_versions: typing.Tuple[str, str], + is_shared_module: bool) -> typing.List[str]: + raise mesonlib.MesonException("This linker doesn't support soname 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 [] diff --git a/mesonbuild/compilers/mixins/pgi.py b/mesonbuild/compilers/mixins/pgi.py index 0613e79..c13c7bc 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,19 +54,11 @@ 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_osx_compiler or self.compiler_type.is_windows_compiler: - return [] - return ['-fPIC'] + if self.compiler_type.is_linux_compiler(): + return ['-fPIC'] + return [] def openmp_flags(self) -> typing.List[str]: return ['-mp'] @@ -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 edb104d..5fe8599 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/objc.py b/mesonbuild/compilers/objc.py index b9ce0b1..7a778d7 100644 --- a/mesonbuild/compilers/objc.py +++ b/mesonbuild/compilers/objc.py @@ -23,9 +23,9 @@ from .mixins.gnu import GnuCompiler from .mixins.clang import ClangCompiler class ObjCCompiler(CLikeCompiler, Compiler): - def __init__(self, exelist, version, for_machine: MachineChoice, is_cross: bool, exe_wrap: typing.Optional[str]): + def __init__(self, exelist, version, for_machine: MachineChoice, is_cross: bool, exe_wrap: typing.Optional[str], **kwargs): self.language = 'objc' - Compiler.__init__(self, exelist, version, for_machine) + Compiler.__init__(self, exelist, version, for_machine, **kwargs) CLikeCompiler.__init__(self, is_cross, exe_wrap) def get_display_language(self): @@ -57,8 +57,8 @@ class ObjCCompiler(CLikeCompiler, Compiler): class GnuObjCCompiler(GnuCompiler, ObjCCompiler): - def __init__(self, exelist, version, compiler_type, for_machine: MachineChoice, is_cross, exe_wrapper=None, defines=None): - ObjCCompiler.__init__(self, exelist, version, for_machine, is_cross, exe_wrapper) + def __init__(self, exelist, version, compiler_type, for_machine: MachineChoice, is_cross, exe_wrapper=None, defines=None, **kwargs): + ObjCCompiler.__init__(self, exelist, version, for_machine, is_cross, exe_wrapper, **kwargs) GnuCompiler.__init__(self, compiler_type, defines) default_warn_args = ['-Wall', '-Winvalid-pch'] self.warn_args = {'0': [], @@ -68,8 +68,8 @@ class GnuObjCCompiler(GnuCompiler, ObjCCompiler): class ClangObjCCompiler(ClangCompiler, ObjCCompiler): - def __init__(self, exelist, version, compiler_type, for_machine: MachineChoice, is_cross, exe_wrapper=None): - ObjCCompiler.__init__(self, exelist, version, for_machine, is_cross, exe_wrapper) + def __init__(self, exelist, version, compiler_type, for_machine: MachineChoice, is_cross, exe_wrapper=None, **kwargs): + ObjCCompiler.__init__(self, exelist, version, for_machine, is_cross, exe_wrapper, **kwargs) ClangCompiler.__init__(self, compiler_type) default_warn_args = ['-Wall', '-Winvalid-pch'] self.warn_args = {'0': [], diff --git a/mesonbuild/compilers/objcpp.py b/mesonbuild/compilers/objcpp.py index a090bed..70a86c5 100644 --- a/mesonbuild/compilers/objcpp.py +++ b/mesonbuild/compilers/objcpp.py @@ -23,9 +23,9 @@ from .mixins.gnu import GnuCompiler from .mixins.clang import ClangCompiler class ObjCPPCompiler(CLikeCompiler, Compiler): - def __init__(self, exelist, version, for_machine: MachineChoice, is_cross: bool, exe_wrap: typing.Optional[str]): + def __init__(self, exelist, version, for_machine: MachineChoice, is_cross: bool, exe_wrap: typing.Optional[str], **kwargs): self.language = 'objcpp' - Compiler.__init__(self, exelist, version, for_machine) + Compiler.__init__(self, exelist, version, for_machine, **kwargs) CLikeCompiler.__init__(self, is_cross, exe_wrap) def get_display_language(self): @@ -58,8 +58,8 @@ class ObjCPPCompiler(CLikeCompiler, Compiler): class GnuObjCPPCompiler(GnuCompiler, ObjCPPCompiler): - def __init__(self, exelist, version, compiler_type, for_machine: MachineChoice, is_cross, exe_wrapper=None, defines=None): - ObjCPPCompiler.__init__(self, exelist, version, for_machine, is_cross, exe_wrapper) + def __init__(self, exelist, version, compiler_type, for_machine: MachineChoice, is_cross, exe_wrapper=None, defines=None, **kwargs): + ObjCPPCompiler.__init__(self, exelist, version, for_machine, is_cross, exe_wrapper, **kwargs) GnuCompiler.__init__(self, compiler_type, defines) default_warn_args = ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'] self.warn_args = {'0': [], @@ -69,8 +69,8 @@ class GnuObjCPPCompiler(GnuCompiler, ObjCPPCompiler): class ClangObjCPPCompiler(ClangCompiler, ObjCPPCompiler): - def __init__(self, exelist, version, compiler_type, for_machine: MachineChoice, is_cross, exe_wrapper=None): - ObjCPPCompiler.__init__(self, exelist, version, for_machine, is_cross, exe_wrapper) + def __init__(self, exelist, version, compiler_type, for_machine: MachineChoice, is_cross, exe_wrapper=None, **kwargs): + ObjCPPCompiler.__init__(self, exelist, version, for_machine, is_cross, exe_wrapper, **kwargs) ClangCompiler.__init__(self, compiler_type) default_warn_args = ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'] self.warn_args = {'0': [], diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index 665b3c2..479c5a7 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'], @@ -27,9 +30,9 @@ rust_optimization_args = {'0': [], } class RustCompiler(Compiler): - def __init__(self, exelist, version, for_machine: MachineChoice, is_cross, exe_wrapper=None): + def __init__(self, exelist, version, for_machine: MachineChoice, is_cross, exe_wrapper=None, **kwargs): self.language = 'rust' - super().__init__(exelist, version, for_machine) + super().__init__(exelist, version, for_machine, **kwargs) self.exe_wrapper = exe_wrapper self.id = 'rustc' self.is_cross = is_cross @@ -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 0038264..6c639fd 100644 --- a/mesonbuild/compilers/swift.py +++ b/mesonbuild/compilers/swift.py @@ -27,16 +27,13 @@ swift_optimization_args = {'0': [], } class SwiftCompiler(Compiler): - def __init__(self, exelist, version, for_machine: MachineChoice, is_cross): + def __init__(self, exelist, version, for_machine: MachineChoice, is_cross, **kwargs): self.language = 'swift' - super().__init__(exelist, version, for_machine) + super().__init__(exelist, version, for_machine, **kwargs) self.version = version 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/environment.py b/mesonbuild/environment.py index cd55ace..2630ae9 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -13,6 +13,7 @@ # limitations under the License. import os, platform, re, sys, shlex, shutil, subprocess, typing +import tempfile from . import coredata from .linkers import ArLinker, ArmarLinker, VisualStudioLinker, DLinker, CcrxLinker, IntelVisualStudioLinker @@ -38,6 +39,23 @@ from .compilers import ( is_object, is_source, ) +from .linkers import ( + AppleDynamicLinker, + ArmClangDynamicLinker, + ArmDynamicLinker, + CcrxDynamicLinker, + ClangClDynamicLinker, + DynamicLinker, + GnuDynamicLinker, + LLVMDynamicLinker, + MSVCDynamicLinker, + OptlinkDynamicLinker, + PGIDynamicLinker, + SolarisDynamicLinker, + XildAppleDynamicLinker, + XildLinuxDynamicLinker, + XilinkDynamicLinker, +) from functools import lru_cache from .compilers import ( ArmCCompiler, @@ -63,8 +81,8 @@ from .compilers import ( EmscriptenCCompiler, EmscriptenCPPCompiler, IntelCCompiler, - IntelCPPCompiler, IntelClCCompiler, + IntelCPPCompiler, IntelClCPPCompiler, IntelFortranCompiler, IntelClFortranCompiler, @@ -639,7 +657,57 @@ class Environment: errmsg += '\nRunning "{0}" gave "{1}"'.format(c, e) raise EnvironmentException(errmsg) - def _detect_c_or_cpp_compiler(self, lang, for_machine): + @staticmethod + def _guess_nix_linker(compiler: typing.List[str], for_machine: MachineChoice, *, + prefix: typing.Union[str, typing.List[str]] = '-Wl,', + extra_args: typing.Optional[typing.List[str]] = None) -> 'DynamicLinker': + """Helper for guessing what linker to use on Unix-Like OSes. + + :prefix: The prefix that the compiler uses to proxy arguments to the + linker, if required. This can be passed as a string or a list of + strings. If it is passed as a string then the arguments to be + proxied to the linker will be concatenated, if it is a list they + will be appended. This means that if a space is required (such as + with swift which wants `-Xlinker --version` and *not* + `-Xlinker=--version`) you must pass as a list. + :extra_args: Any addtional arguments rquired (such as a source file) + """ + extra_args = typing.cast(typing.List[str], extra_args or []) + if isinstance(prefix, str): + check_args = [prefix + '--version'] + extra_args + else: + check_args = prefix + ['--version'] + extra_args + _, o, e = Popen_safe(compiler + check_args) + v = search_version(o) + if o.startswith('LLD'): + linker = LLVMDynamicLinker(compiler, for_machine, 'lld', version=v) # type: DynamicLinker + # first is for apple clang, second is for real gcc + elif e.endswith('(use -v to see invocation)\n') or 'macosx_version' in e: + if isinstance(prefix, str): + _, _, e = Popen_safe(compiler + [prefix + '-v'] + extra_args) + else: + _, _, e = Popen_safe(compiler + prefix + ['-v'] + extra_args) + i = 'APPLE ld' + for line in e.split('\n'): + if 'PROJECT:ld' in line: + v = line.split('-')[1] + break + else: + v = 'unknown version' + linker = AppleDynamicLinker(compiler, for_machine, i, version=v) + elif 'GNU' in o: + if 'gold' in 'o': + i = 'GNU ld.gold' + else: + i = 'GNU ld.bfd' + linker = GnuDynamicLinker(compiler, for_machine, i, version=v) + elif 'Solaris' in e: + linker = SolarisDynamicLinker(compiler, for_machine, 'solaris', version=search_version(e)) + else: + raise MesonException('Unable to determine dynamic linker.') + return linker + + def _detect_c_or_cpp_compiler(self, lang: str, for_machine: MachineChoice) -> Compiler: popen_exceptions = {} compilers, ccache, exe_wrap = self._get_compilers(lang, for_machine) is_cross = not self.machines.matches_build_machine(for_machine) @@ -701,13 +769,18 @@ class Environment: popen_exceptions[' '.join(compiler)] = 'no pre-processor defines' continue compiler_type = self.get_gnu_compiler_type(defines) + + linker = self._guess_nix_linker(compiler, for_machine) + if guess_gcc_or_lcc == 'lcc': version = self.get_lcc_version_from_defines(defines) cls = ElbrusCCompiler if lang == 'c' else ElbrusCPPCompiler else: version = self.get_gnu_version_from_defines(defines) cls = GnuCCompiler if lang == 'c' else GnuCPPCompiler - return cls(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, defines, full_version=full_version) + return cls(ccache + compiler, version, compiler_type, + for_machine, is_cross, exe_wrap, defines, + full_version=full_version, linker=linker) if 'Emscripten' in out: cls = EmscriptenCCompiler if lang == 'c' else EmscriptenCPPCompiler @@ -730,7 +803,8 @@ class Environment: full_version = arm_ver_str compiler_type = CompilerType.ARM_WIN cls = ArmclangCCompiler if lang == 'c' else ArmclangCPPCompiler - return cls(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, full_version=full_version) + linker = ArmClangDynamicLinker(for_machine, version=version) + return cls(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, full_version=full_version, linker=linker) if 'CL.EXE COMPATIBILITY' in out: # if this is clang-cl masquerading as cl, detect it as cl, not # clang @@ -746,7 +820,8 @@ class Environment: else: target = 'unknown target' cls = ClangClCCompiler if lang == 'c' else ClangClCPPCompiler - return cls(compiler, version, for_machine, is_cross, exe_wrap, target) + linker = ClangClDynamicLinker(for_machine, version=version) + return cls(compiler, version, for_machine, is_cross, exe_wrap, target, linker=linker) if 'clang' in out: if 'Apple' in out or self.machines[for_machine].is_darwin(): compiler_type = CompilerType.CLANG_OSX @@ -755,12 +830,15 @@ class Environment: else: compiler_type = CompilerType.CLANG_STANDARD cls = ClangCCompiler if lang == 'c' else ClangCPPCompiler - return cls(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, full_version=full_version) + + linker = self._guess_nix_linker(compiler, for_machine) + return cls(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, full_version=full_version, linker=linker) if 'Intel(R) C++ Intel(R)' in err: version = search_version(err) target = 'x86' if 'IA-32' in err else 'x86_64' cls = IntelClCCompiler if lang == 'c' else IntelClCPPCompiler - return cls(compiler, version, for_machine, is_cross, exe_wrap, target) + linker = XilinkDynamicLinker(for_machine, version=version) + return cls(compiler, version, for_machine, is_cross, exe_wrap, target, linker=linker) if 'Microsoft' in out or 'Microsoft' in err: # Latest versions of Visual Studio print version # number to stderr but earlier ones print version @@ -778,8 +856,9 @@ class Environment: target = match.group(1) else: target = 'x86' + linker = MSVCDynamicLinker(for_machine, version=version) cls = VisualStudioCCompiler if lang == 'c' else VisualStudioCPPCompiler - return cls(compiler, version, for_machine, is_cross, exe_wrap, target) + return cls(compiler, version, for_machine, is_cross, exe_wrap, target, linker=linker) if 'PGI Compilers' in out: if self.machines[for_machine].is_darwin(): compiler_type = CompilerType.PGI_OSX @@ -788,25 +867,27 @@ class Environment: else: compiler_type = CompilerType.PGI_STANDARD cls = PGICCompiler if lang == 'c' else PGICPPCompiler - return cls(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap) + linker = PGIDynamicLinker(compiler, for_machine, 'pgi', version=version) + return cls(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, linker=linker) if '(ICC)' in out: if self.machines[for_machine].is_darwin(): compiler_type = CompilerType.ICC_OSX - elif self.machines[for_machine].is_windows(): - # TODO: fix ICC on Windows - compiler_type = CompilerType.ICC_WIN + l = XildAppleDynamicLinker(compiler, for_machine, 'xild', version=version) else: compiler_type = CompilerType.ICC_STANDARD + l = XildLinuxDynamicLinker(compiler, for_machine, 'xild', version=version) cls = IntelCCompiler if lang == 'c' else IntelCPPCompiler - return cls(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, full_version=full_version) + return cls(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, full_version=full_version, linker=l) if 'ARM' in out: compiler_type = CompilerType.ARM_WIN cls = ArmCCompiler if lang == 'c' else ArmCPPCompiler - return cls(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, full_version=full_version) + linker = ArmDynamicLinker(for_machine, version=version) + return cls(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, full_version=full_version, linker=linker) if 'RX Family' in out: compiler_type = CompilerType.CCRX_WIN cls = CcrxCCompiler if lang == 'c' else CcrxCPPCompiler - return cls(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, full_version=full_version) + linker = CcrxDynamicLinker(for_machine, version=version) + return cls(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, full_version=full_version, linker=linker) self._handle_exceptions(popen_exceptions, compilers) @@ -846,8 +927,8 @@ class Environment: # Luckily, the "V" also makes it very simple to extract # the full version: version = out.strip().split('V')[-1] - cls = CudaCompiler - return cls(ccache + compiler, version, for_machine, exe_wrap) + linker = self._guess_nix_linker(compiler, for_machine, prefix='-Xlinker=') + return CudaCompiler(ccache + compiler, version, for_machine, exe_wrap, linker=linker) raise EnvironmentException('Could not find suitable CUDA compiler: "' + ' '.join(compilers) + '"') def detect_fortran_compiler(self, for_machine: MachineChoice): @@ -885,22 +966,27 @@ class Environment: else: version = self.get_gnu_version_from_defines(defines) cls = GnuFortranCompiler - return cls(compiler, version, compiler_type, for_machine, is_cross, exe_wrap, defines, full_version=full_version) + linker = self._guess_nix_linker(compiler, for_machine) + return cls(compiler, version, compiler_type, for_machine, is_cross, exe_wrap, defines, full_version=full_version, linker=linker) if 'G95' in out: - return G95FortranCompiler(compiler, version, for_machine, is_cross, exe_wrap, full_version=full_version) + linker = self._guess_nix_linker(compiler, for_machine) + return G95FortranCompiler(compiler, version, for_machine, is_cross, exe_wrap, full_version=full_version, linker=linker) if 'Sun Fortran' in err: version = search_version(err) - return SunFortranCompiler(compiler, version, for_machine, is_cross, exe_wrap, full_version=full_version) + linker = self._guess_nix_linker(compiler, for_machine) + return SunFortranCompiler(compiler, version, for_machine, is_cross, exe_wrap, full_version=full_version, linker=linker) if 'Intel(R) Visual Fortran' in err: version = search_version(err) target = 'x86' if 'IA-32' in err else 'x86_64' - return IntelClFortranCompiler(compiler, version, for_machine, is_cross, target, exe_wrap) + linker = XilinkDynamicLinker(for_machine, version=version) + return IntelClFortranCompiler(compiler, version, for_machine, is_cross, target, exe_wrap, linker=linker) if 'ifort (IFORT)' in out: - return IntelFortranCompiler(compiler, version, for_machine, is_cross, exe_wrap, full_version=full_version) + linker = XildLinuxDynamicLinker(compiler, for_machine, 'xild', version=version) + return IntelFortranCompiler(compiler, version, for_machine, is_cross, exe_wrap, full_version=full_version, linker=linker) if 'PathScale EKOPath(tm)' in err: return PathScaleFortranCompiler(compiler, version, for_machine, is_cross, exe_wrap, full_version=full_version) @@ -912,54 +998,34 @@ class Environment: compiler_type = CompilerType.PGI_WIN else: compiler_type = CompilerType.PGI_STANDARD - return PGIFortranCompiler(compiler, version, compiler_type, for_machine, is_cross, exe_wrap, full_version=full_version) + linker = PGIDynamicLinker(compiler, for_machine, 'pgi', version=version) + return PGIFortranCompiler(compiler, version, compiler_type, for_machine, is_cross, exe_wrap, full_version=full_version, linker=linker) if 'flang' in out or 'clang' in out: - return FlangFortranCompiler(compiler, version, for_machine, is_cross, exe_wrap, full_version=full_version) + linker = self._guess_nix_linker(compiler, for_machine) + return FlangFortranCompiler(compiler, version, for_machine, is_cross, exe_wrap, full_version=full_version, linker=linker) if 'Open64 Compiler Suite' in err: - return Open64FortranCompiler(compiler, version, for_machine, is_cross, exe_wrap, full_version=full_version) + linker = self._guess_nix_linker(compiler, for_machine) + return Open64FortranCompiler(compiler, version, for_machine, is_cross, exe_wrap, full_version=full_version, linker=linker) if 'NAG Fortran' in err: - return NAGFortranCompiler(compiler, version, for_machine, is_cross, exe_wrap, full_version=full_version) + linker = self._guess_nix_linker(compiler, for_machine) + return NAGFortranCompiler(compiler, version, for_machine, is_cross, exe_wrap, full_version=full_version, linker=linker) self._handle_exceptions(popen_exceptions, compilers) def get_scratch_dir(self): return self.scratch_dir - def detect_objc_compiler(self, for_machine): - popen_exceptions = {} - compilers, ccache, exe_wrap = self._get_compilers('objc', for_machine) - is_cross = not self.machines.matches_build_machine(for_machine) - for compiler in compilers: - if isinstance(compiler, str): - compiler = [compiler] - arg = ['--version'] - try: - p, out, err = Popen_safe(compiler + arg) - except OSError as e: - popen_exceptions[' '.join(compiler + arg)] = e - continue - version = search_version(out) - if 'Free Software Foundation' in out or ('e2k' in out and 'lcc' in out): - defines = self.get_gnu_compiler_defines(compiler) - if not defines: - popen_exceptions[' '.join(compiler)] = 'no pre-processor defines' - continue - compiler_type = self.get_gnu_compiler_type(defines) - version = self.get_gnu_version_from_defines(defines) - return GnuObjCCompiler(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, defines) - if out.startswith('Apple LLVM') or out.startswith('Apple clang'): - return ClangObjCCompiler(ccache + compiler, version, CompilerType.CLANG_OSX, for_machine, is_cross, exe_wrap) - if 'windows' in out: - return ClangObjCCompiler(ccache + compiler, version, CompilerType.CLANG_MINGW, for_machine, is_cross, exe_wrap) - if out.startswith(('clang', 'OpenBSD clang')): - return ClangObjCCompiler(ccache + compiler, version, CompilerType.CLANG_STANDARD, for_machine, is_cross, exe_wrap) - self._handle_exceptions(popen_exceptions, compilers) + def detect_objc_compiler(self, for_machine: MachineInfo) -> 'Compiler': + return self._detect_objc_or_objcpp_compiler(for_machine, True) - def detect_objcpp_compiler(self, for_machine): + def detect_objcpp_compiler(self, for_machine: MachineInfo) -> 'Compiler': + return self._detect_objc_or_objcpp_compiler(for_machine, False) + + def _detect_objc_or_objcpp_compiler(self, for_machine: MachineInfo, objc: bool) -> 'Compiler': popen_exceptions = {} - compilers, ccache, exe_wrap = self._get_compilers('objcpp', for_machine) + compilers, ccache, exe_wrap = self._get_compilers('objc' if objc else 'objcpp', for_machine) is_cross = not self.machines.matches_build_machine(for_machine) for compiler in compilers: if isinstance(compiler, str): @@ -978,13 +1044,19 @@ class Environment: continue compiler_type = self.get_gnu_compiler_type(defines) version = self.get_gnu_version_from_defines(defines) - return GnuObjCPPCompiler(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, defines) - if out.startswith('Apple LLVM') or out.startswith('Apple clang'): - return ClangObjCPPCompiler(ccache + compiler, version, CompilerType.CLANG_OSX, for_machine, is_cross, exe_wrap) - if 'windows' in out: - return ClangObjCPPCompiler(ccache + compiler, version, CompilerType.CLANG_MINGW, for_machine, is_cross, exe_wrap) - if out.startswith(('clang', 'OpenBSD clang')): - return ClangObjCPPCompiler(ccache + compiler, version, CompilerType.CLANG_STANDARD, for_machine, is_cross, exe_wrap) + comp = GnuObjCCompiler if objc else GnuObjCPPCompiler + linker = self._guess_nix_linker(compiler, for_machine) + return comp(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, defines, linker=linker) + if 'clang' in out: + comp = ClangObjCCompiler if objc else ClangObjCPPCompiler + if 'Apple' in out or self.machines[for_machine].is_darwin(): + compiler_type = CompilerType.CLANG_OSX + elif 'windows' in out or self.machines[for_machine].is_windows(): + compiler_type = CompilerType.CLANG_MINGW + else: + compiler_type = CompilerType.CLANG_STANDARD + linker = self._guess_nix_linker(compiler, for_machine) + return comp(ccache + compiler, version, compiler_type, for_machine, is_cross, exe_wrap, linker=linker) self._handle_exceptions(popen_exceptions, compilers) def detect_java_compiler(self, for_machine): @@ -1059,7 +1131,18 @@ class Environment: version = search_version(out) if 'rustc' in out: - return RustCompiler(compiler, version, for_machine, is_cross, exe_wrap) + # Chalk up another quirk for rust. There is no way (AFAICT) to + # figure out what linker rustc is using for a non-nightly compiler + # (On nightly you can pass -Z print-link-args). So we're going to + # hard code the linker based on the platform. + # Currenty gnu ld is used for everything except apple by + # default, and apple ld is used on mac. + # TODO: find some better way to figure this out. + if self.machines[for_machine].is_darwin(): + linker = AppleDynamicLinker([], for_machine, 'Apple ld') + else: + linker = GnuDynamicLinker([], for_machine, 'GNU ld') + return RustCompiler(compiler, version, for_machine, is_cross, exe_wrap, linker=linker) self._handle_exceptions(popen_exceptions, compilers) @@ -1100,11 +1183,31 @@ class Environment: arch = 'x86_mscoff' if 'LLVM D compiler' in out: - return compilers.LLVMDCompiler(exelist, version, for_machine, arch, full_version=full_version) + # LDC seems to require a file + m = self.machines[for_machine] + if m.is_windows() or m.is_cygwin(): + # Getting LDC on windows to give useful linker output when not + # doing real work is painfully hard. It ships with a verison of + # lld-link, so just assume that we're going to use lld-link + # with it. + _, o, _ = Popen_safe(['lld-link.exe', '--version']) + linker = ClangClDynamicLinker(for_machine, version=search_version(o)) + else: + with tempfile.NamedTemporaryFile(suffix='.d') as f: + linker = self._guess_nix_linker(exelist, for_machine, prefix='-L', extra_args=[f.name]) + return compilers.LLVMDCompiler(exelist, version, for_machine, arch, full_version=full_version, linker=linker) elif 'gdc' in out: - return compilers.GnuDCompiler(exelist, version, for_machine, arch, full_version=full_version) + linker = self._guess_nix_linker(exelist, for_machine) + return compilers.GnuDCompiler(exelist, version, for_machine, arch, full_version=full_version, linker=linker) elif 'The D Language Foundation' in out or 'Digital Mars' in out: - return compilers.DmdDCompiler(exelist, version, for_machine, arch, full_version=full_version) + # DMD seems to require a file + m = self.machines[for_machine] + if m.is_windows() or m.is_cygwin(): + linker = OptlinkDynamicLinker(for_machine, version=full_version) + else: + with tempfile.NamedTemporaryFile(suffix='.d') as f: + linker = self._guess_nix_linker(exelist, for_machine, prefix='-L', extra_args=[f.name]) + return compilers.DmdDCompiler(exelist, version, for_machine, arch, full_version=full_version, linker=linker) raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"') def detect_swift_compiler(self, for_machine): @@ -1120,7 +1223,12 @@ class Environment: raise EnvironmentException('Could not execute Swift compiler "%s"' % ' '.join(exelist)) version = search_version(err) if 'Swift' in err: - return compilers.SwiftCompiler(exelist, version, for_machine, is_cross) + # As for 5.0.1 swiftc *requires* a file to check the linker: + with tempfile.NamedTemporaryFile(suffix='.swift') as f: + linker = self._guess_nix_linker( + exelist, for_machine, prefix=['-Xlinker'], extra_args=[f.name]) + return compilers.SwiftCompiler(exelist, version, for_machine, is_cross, linker=linker) + raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"') def compiler_from_language(self, lang: str, for_machine: MachineChoice): diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index c102978..0fd2f06 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -2831,12 +2831,16 @@ external dependencies (including libraries) must go to "dependencies".''') continue else: raise + if for_machine == MachineChoice.HOST or self.environment.is_cross_build(): logger_fun = mlog.log else: logger_fun = mlog.debug logger_fun(comp.get_display_language(), 'compiler for the', machine_name, 'machine:', mlog.bold(' '.join(comp.get_exelist())), comp.get_version_string()) + if comp.linker is not None: + logger_fun(comp.get_display_language(), 'linker for the', machine_name, 'machine:', + mlog.bold(comp.linker.id), comp.linker.version) self.build.ensure_static_linker(comp) langs = self.coredata.compilers[for_machine].keys() diff --git a/mesonbuild/linkers.py b/mesonbuild/linkers.py index dc9a825..a641cd0 100644 --- a/mesonbuild/linkers.py +++ b/mesonbuild/linkers.py @@ -12,6 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +import abc +import os +import shlex import typing from . import mesonlib @@ -51,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]: @@ -189,3 +193,693 @@ class CcrxLinker(StaticLinker): def get_linker_always_args(self) -> typing.List[str]: return ['-nologo', '-form=library'] + + +def prepare_rpaths(raw_rpaths: str, build_dir: str, from_dir: str) -> typing.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: typing.List[str]) -> typing.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: typing.Dict[str, typing.List[str]] + + def __init__(self, exelist: typing.List[str], for_machine: mesonlib.MachineChoice, + id_: str, *, version: str = 'unknown version'): + self.exelist = exelist + self.for_machine = for_machine + self.version = version + self.id = id_ + + def __repr__(self) -> str: + return '<{}: v{} `{}`>'.format(type(self).__name__, self.version, ' '.join(self.exelist)) + + def get_id(self) -> str: + return self.id + + def get_version_string(self) -> str: + return '({} {})'.format(self.id, self.version) + + def get_exelist(self) -> typing.List[str]: + return self.exelist.copy() + + def get_accepts_rsp(self) -> bool: + # TODO: is it really a matter of is_windows or is it for_windows? + return mesonlib.is_windows() + + def get_always_args(self) -> typing.List[str]: + return [] + + def get_lib_prefix(self) -> str: + return '' + + # XXX: is use_ldflags a compiler or a linker attribute? + + def get_args_from_envvars(self) -> typing.List[str]: + flags = os.environ.get('LDFLAGS') + if not flags: + return [] + return shlex.split(flags) + + def get_option_args(self, options: 'OptionDictType') -> typing.List[str]: + return [] + + def has_multi_arguments(self, args: typing.List[str], env: 'Environment') -> typing.Tuple[bool, bool]: + m = 'Language {} does not support has_multi_link_arguments.' + raise mesonlib.EnvironmentException(m.format(self.id)) + + def get_debugfile_args(self, targetfile: str) -> typing.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) -> typing.List[str]: + return [] + + def get_std_shared_module_args(self, options: 'OptionDictType') -> typing.List[str]: + return self.get_std_shared_lib_args() + + def get_pie_args(self) -> typing.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) -> typing.List[str]: + return [] + + def sanitizer_args(self, value: str) -> typing.List[str]: + return [] + + def get_buildtype_args(self, buildtype: str) -> typing.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) -> typing.List[str]: + return [] + + def get_link_whole_for(self, args: typing.List[str]) -> typing.List[str]: + raise mesonlib.EnvironmentException( + 'Linker {} does not support link_whole'.format(self.id)) + + def get_allow_undefined_args(self) -> typing.List[str]: + raise mesonlib.EnvironmentException( + 'Linker {} does not support allow undefined'.format(self.id)) + + def invoked_by_compiler(self) -> bool: + """True if meson uses the compiler to invoke the linker.""" + return True + + @abc.abstractmethod + def get_output_args(self, outname: str) -> typing.List[str]: + pass + + def get_coverage_args(self) -> typing.List[str]: + m = "Linker {} doesn't implement coverage data generation.".format(self.id) + raise mesonlib.EnvironmentException(m) + + @abc.abstractmethod + def get_search_args(self, dirname: str) -> typing.List[str]: + pass + + def export_dynamic_args(self, env: 'Environment') -> typing.List[str]: + return [] + + def 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 [] + + def thread_flags(self, env: 'Environment') -> typing.List[str]: + return [] + + def no_undefined_args(self) -> typing.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) -> typing.List[str]: + """Arguments to make all warnings errors.""" + return [] + + def bitcode_args(self) -> typing.List[str]: + raise mesonlib.MesonException('This linker does not support bitcode bundles') + + def get_debug_crt_args(self) -> typing.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) -> typing.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) -> typing.List[str]: + return ['-o', outname] + + def get_std_shared_lib_args(self) -> typing.List[str]: + return ['-shared'] + + def get_search_args(self, dirname: str) -> typing.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), the Intel Xild + (which comes with ICC), LLVM's lld, and other linkers like GNU-ld. + """ + + _BUILDTYPE_ARGS = { + 'plain': [], + 'debug': [], + 'debugoptimized': [], + 'release': ['-Wl,-O1'], + 'minsize': [], + 'custom': [], + } # type: typing.Dict[str, typing.List[str]] + + def get_pie_args(self) -> typing.List[str]: + return ['-pie'] + + def get_asneeded_args(self) -> typing.List[str]: + return ['-Wl,--as-needed'] + + def get_link_whole_for(self, args: typing.List[str]) -> typing.List[str]: + if not args: + return args + return ['-Wl,--whole-archive'] + args + ['-Wl,--no-whole-archive'] + + def get_allow_undefined_args(self) -> typing.List[str]: + return ['-Wl,--allow-shlib-undefined'] + + def get_lto_args(self) -> typing.List[str]: + return ['-flto'] + + def sanitizer_args(self, value: str) -> typing.List[str]: + if value == 'none': + return [] + return ['-fsanitize=' + value] + + def invoked_by_compiler(self) -> bool: + """True if meson uses the compiler to invoke the linker.""" + return True + + def get_coverage_args(self) -> typing.List[str]: + return ['--coverage'] + + def export_dynamic_args(self, env: 'Environment') -> typing.List[str]: + m = env.machines[self.for_machine] + if m.is_windows() or m.is_cygwin(): + return ['-Wl,--export-all-symbols'] + return ['-Wl,-export-dynamic'] + + def import_library_args(self, implibname: str) -> typing.List[str]: + return ['-Wl,--out-implib=' + implibname] + + def thread_flags(self, env: 'Environment') -> typing.List[str]: + if env.machines[self.for_machine].is_haiku(): + return [] + return ['-pthread'] + + def no_undefined_args(self) -> typing.List[str]: + return ['-Wl,--no-undefined'] + + def fatal_warnings(self) -> typing.List[str]: + return ['-Wl,--fatal-warnings'] + + 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]: + 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 ['-Wl,-soname,{}{}.{}{}'.format(prefix, shlib_name, suffix, sostr)] + + def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, + rpath_paths: str, build_rpath: str, + install_rpath: str) -> typing.List[str]: + m = env.machines[self.for_machine] + if m.is_windows() or m.is_cygwin(): + return [] + if not rpath_paths and not install_rpath and not build_rpath: + return [] + 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]) + # Build_rpath is used as-is (it is usually absolute). + if build_rpath != '': + all_paths.add(build_rpath) + + # 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.append('-Wl,-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.append('-Wl,-rpath,' + paths) + + # TODO: should this actually be "for solaris/sunos"? + if mesonlib.is_sunos(): + return args + + # 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.extend(['-Wl,-rpath-link,' + os.path.join(build_dir, p) for p in rpath_paths]) + + return args + + +class AppleDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): + + """Apple's ld implementation.""" + + def get_asneeded_args(self) -> typing.List[str]: + return ['-Wl,-dead_strip_dylibs'] + + def get_allow_undefined_args(self) -> typing.List[str]: + return ['-Wl,-undefined,dynamic_lookup'] + + def get_std_shared_module_args(self, options: 'OptionDictType') -> typing.List[str]: + return ['-bundle', '-Wl,-undefined,dynamic_lookup'] + + def get_link_whole_for(self, args: typing.List[str]) -> typing.List[str]: + result = [] # type: typing.List[str] + for a in args: + result.extend(['-Wl,-force_load', a]) + return result + + def no_undefined_args(self) -> typing.List[str]: + return ['-Wl,-undefined,error'] + + def get_always_args(self) -> typing.List[str]: + return ['-Wl,-headerpad_max_install_names'] + + def bitcode_args(self) -> typing.List[str]: + return ['-Wl,-bitcode_bundle'] + + def fatal_warnings(self) -> typing.List[str]: + return ['-Wl,-fatal_warnings'] + + 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]: + 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) -> typing.List[str]: + if not rpath_paths and not install_rpath and not build_rpath: + return [] + # Ensure that there is enough space for install_name_tool in-place + # editing of large RPATHs + args = ['-Wl,-headerpad_max_install_names'] + # @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) + args.extend(['-Wl,-rpath,' + rp for rp in all_paths]) + + return args + + +class GnuDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, DynamicLinker): + + """Representation of GNU ld.bfd and ld.gold.""" + + pass + + +class LLVMDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, DynamicLinker): + + """Representation of LLVM's lld (not lld-link) linker. + + This is only the posix-like linker. + """ + + pass + + +class XildLinuxDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, DynamicLinker): + + """Representation of Intel's Xild linker. + + This is only the linux-like linker which dispatches to Gnu ld. + """ + + pass + + +class XildAppleDynamicLinker(AppleDynamicLinker): + + """Representation of Intel's Xild linker. + + This is the apple linker, which dispatches to Apple's ld. + """ + + pass + + +class CcrxDynamicLinker(DynamicLinker): + + """Linker for Renesis CCrx compiler.""" + + def __init__(self, for_machine: mesonlib.MachineChoice, + *, version: str = 'unknown version'): + super().__init__(['rlink.exe'], for_machine, 'rlink', + 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) -> typing.List[str]: + return [] + + def get_output_args(self, outputname: str) -> typing.List[str]: + return ['-output=%s' % outputname] + + def get_search_args(self, dirname: str) -> typing.NoReturn: + raise EnvironmentError('rlink.exe does not have a search dir argument') + + def get_allow_undefined_args(self) -> typing.List[str]: + return [] + + 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 [] + + +class ArmDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): + + """Linker for the ARM compiler.""" + + def __init__(self, for_machine: mesonlib.MachineChoice, + *, version: str = 'unknown version'): + super().__init__(['armlink'], for_machine, 'armlink', + version=version) + + def get_accepts_rsp(self) -> bool: + return False + + def get_std_shared_lib_args(self) -> typing.NoReturn: + raise mesonlib.MesonException('The Arm Linkers do not support shared libraries') + + def get_allow_undefined_args(self) -> typing.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') -> typing.List[str]: + return ['--export_dynamic'] + + def import_library_args(self, implibname: str) -> typing.List[str]: + return ['--symdefs=' + implibname] + + +class PGIDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): + + """PGI linker.""" + + def get_allow_undefined_args(self) -> typing.List[str]: + return [] + + 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 [] + + def get_std_shared_lib_args(self) -> typing.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) -> typing.List[str]: + if env.machines[self.for_machine].is_windows(): + return ['-R' + os.path.join(build_dir, p) for p in rpath_paths] + return [] + + +class VisualStudioLikeLinkerMixin: + + _BUILDTYPE_ARGS = { + 'plain': [], + 'debug': [], + 'debugoptimized': [], + # The otherwise implicit REF and ICF linker optimisations are disabled by + # /DEBUG. REF implies ICF. + 'release': ['/OPT:REF'], + 'minsize': ['/INCREMENTAL:NO', '/OPT:REF'], + 'custom': [], + } # type: typing.Dict[str, typing.List[str]] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.machine = 'x86' + + def get_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_output_args(self, outputname: str) -> typing.List[str]: + return ['/MACHINE:' + self.machine, '/OUT:' + outputname] + + def get_always_args(self) -> typing.List[str]: + return ['/nologo'] + + def get_search_args(self, dirname: str) -> typing.List[str]: + return ['/LIBPATH:' + dirname] + + def get_std_shared_lib_args(self) -> typing.List[str]: + return ['/DLL'] + + def get_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_allow_undefined_args(self) -> typing.List[str]: + # link.exe + return ['/FORCE:UNRESOLVED'] + + 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 [] + + +class MSVCDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): + + """Microsoft's Link.exe.""" + + def __init__(self, for_machine: mesonlib.MachineChoice, + *, version: str = 'unknown version'): + super().__init__(['link.exe'], for_machine, 'link', version=version) + + +class ClangClDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): + + """Clang's lld-link.exe.""" + + def __init__(self, for_machine: mesonlib.MachineChoice, + *, version: str = 'unknown version'): + super().__init__(['lld-link.exe'], for_machine, 'lld-link', + version=version) + + +class XilinkDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): + + """Intel's Xilink.exe.""" + + def __init__(self, for_machine: mesonlib.MachineChoice, + *, version: str = 'unknown version'): + super().__init__(['xilink.exe'], for_machine, 'xilink', version=version) + + +class SolarisDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): + + """Sys-V derived linker used on Solaris and OpenSolaris.""" + + def get_link_whole_for(self, args: typing.List[str]) -> typing.List[str]: + if not args: + return args + return ['-Wl,--whole-archive'] + args + ['-Wl,--no-whole-archive'] + + def no_undefined_args(self) -> typing.List[str]: + return ['-z', 'defs'] + + def get_allow_undefined_args(self) -> typing.List[str]: + return ['-z', 'nodefs'] + + def fatal_warnings(self) -> typing.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) -> typing.List[str]: + if not rpath_paths and not install_rpath and not build_rpath: + return [] + processed_rpaths = prepare_rpaths(rpath_paths, build_dir, from_dir) + all_paths = mesonlib.OrderedSet([os.path.join('$ORIGIN', p) for p in processed_rpaths]) + if build_rpath != '': + all_paths.add(build_rpath) + + # In order to avoid relinking for RPATH removal, the binary needs to contain just + # enough space in the ELF header to hold the final installation RPATH. + paths = ':'.join(all_paths) + if len(paths) < len(install_rpath): + padding = 'X' * (len(install_rpath) - len(paths)) + if not paths: + paths = padding + else: + paths = paths + ':' + padding + return ['-Wl,-rpath,{}'.format(paths)] + + 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]: + sostr = '' if soversion is None else '.' + soversion + return ['-Wl,-soname,{}{}.{}{}'.format(prefix, shlib_name, suffix, sostr)] + + +class OptlinkDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): + + """Digital Mars dynamic linker for windows.""" + + def __init__(self, for_machine: mesonlib.MachineChoice, + *, version: str = 'unknown version'): + # Use optlink instead of link so we don't interfer with other link.exe + # implementations. + super().__init__(['optlink.exe'], for_machine, 'optlink', version=version) + + def get_allow_undefined_args(self) -> typing.List[str]: + return [] diff --git a/run_project_tests.py b/run_project_tests.py index 88d6a57..2dff885 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -636,6 +636,7 @@ def detect_tests_to_run(only: typing.List[str]) -> typing.List[typing.Tuple[str, ('fpga', 'fpga', shutil.which('yosys') is None), ('frameworks', 'frameworks', False), ('nasm', 'nasm', False), + ('wasm', 'wasm', shutil.which('emcc') is None or backend is not Backend.ninja), ] if only: diff --git a/run_unittests.py b/run_unittests.py index 26850b7..c79b4e2 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -444,7 +444,8 @@ class InternalTests(unittest.TestCase): def test_compiler_args_class_gnuld(self): cargsfunc = mesonbuild.compilers.CompilerArgs ## Test --start/end-group - gcc = mesonbuild.compilers.GnuCCompiler([], 'fake', mesonbuild.compilers.CompilerType.GCC_STANDARD, False, MachineChoice.HOST) + linker = mesonbuild.linkers.GnuDynamicLinker([], MachineChoice.HOST, 'fake') + gcc = mesonbuild.compilers.GnuCCompiler([], 'fake', mesonbuild.compilers.CompilerType.GCC_STANDARD, False, MachineChoice.HOST, linker=linker) ## Test that 'direct' append and extend works l = cargsfunc(gcc, ['-Lfoodir', '-lfoo']) self.assertEqual(l.to_native(copy=True), ['-Lfoodir', '-Wl,--start-group', '-lfoo', '-Wl,--end-group']) @@ -5032,7 +5033,7 @@ class LinuxlikeTests(BasePlatformTests): raise unittest.SkipTest('-fsanitize=address is not supported on OpenBSD') testdir = os.path.join(self.common_test_dir, '13 pch') - self.init(testdir, extra_args=['-Db_sanitize=address']) + self.init(testdir, extra_args=['-Db_sanitize=address', '-Db_lundef=false']) self.build() compdb = self.get_compdb() for i in compdb: @@ -5950,9 +5951,10 @@ class NativeFileTests(BasePlatformTests): f.write("{}='{}'\n".format(k, v)) return filename - def helper_create_binary_wrapper(self, binary, dir_=None, **kwargs): + def helper_create_binary_wrapper(self, binary, dir_=None, extra_args=None, **kwargs): """Creates a wrapper around a binary that overrides specific values.""" filename = os.path.join(dir_ or self.builddir, 'binary_wrapper{}.py'.format(self.current_wrapper)) + extra_args = extra_args or {} self.current_wrapper += 1 if is_haiku(): chbang = '#!/bin/env python3' @@ -5969,10 +5971,10 @@ class NativeFileTests(BasePlatformTests): def main(): parser = argparse.ArgumentParser() '''.format(chbang))) - for name in kwargs: + for name in chain(extra_args, kwargs): f.write(' parser.add_argument("-{0}", "--{0}", action="store_true")\n'.format(name)) f.write(' args, extra_args = parser.parse_known_args()\n') - for name, value in kwargs.items(): + for name, value in chain(extra_args.items(), kwargs.items()): f.write(' if args.{}:\n'.format(name)) f.write(' print("{}", file=sys.{})\n'.format(value, kwargs.get('outfile', 'stdout'))) f.write(' sys.exit(0)\n') @@ -6223,7 +6225,8 @@ class NativeFileTests(BasePlatformTests): @skip_if_not_language('swift') def test_swift_compiler(self): wrapper = self.helper_create_binary_wrapper( - 'swiftc', version='Swift 1.2345', outfile='stderr') + 'swiftc', version='Swift 1.2345', outfile='stderr', + extra_args={'Xlinker': 'macosx_version. PROJECT:ld - 1.2.3'}) env = get_fake_env() env.binaries.host.binaries['swift'] = wrapper compiler = env.detect_swift_compiler(MachineChoice.HOST) diff --git a/test cases/common/152 shared module resolving symbol in executable/meson.build b/test cases/common/152 shared module resolving symbol in executable/meson.build index 282a4d2..4e5188f 100644 --- a/test cases/common/152 shared module resolving symbol in executable/meson.build +++ b/test cases/common/152 shared module resolving symbol in executable/meson.build @@ -9,6 +9,11 @@ project('shared module resolving symbol in executable', 'c') # See testcase 125 for an example of the more complex portability gymnastics # required if we do not know (at link-time) what provides the symbol. +cc = meson.get_compiler('c') +if cc.get_id() == 'pgi' + error('MESON_SKIP_TEST PGI has its own unique set of macros that would need to be handled') +endif + dl = meson.get_compiler('c').find_library('dl', required: false) e = executable('prog', 'prog.c', dependencies: dl, export_dynamic: true) m = shared_module('module', 'module.c', link_with: e) diff --git a/test cases/common/185 has link arg/meson.build b/test cases/common/185 has link arg/meson.build index 10f2218..6bfbd59 100644 --- a/test cases/common/185 has link arg/meson.build +++ b/test cases/common/185 has link arg/meson.build @@ -8,15 +8,16 @@ if cc.get_argument_syntax() == 'msvc' useless = '/DEBUG' isnt_arg = '/iambroken' else - is_arg = '-Wl,-Lfoo' - useless = '-Wl,-Lbar' + is_arg = '-Wl,-L/tmp' + useless = '-Wl,-L/usr' isnt_arg = '-Wl,-iambroken' endif assert(cc.has_link_argument(is_arg), 'Arg that should have worked does not work.') -assert(not cc.has_link_argument(isnt_arg), 'Arg that should be broken is not.') - assert(cpp.has_link_argument(is_arg), 'Arg that should have worked does not work.') + +if cc.get_id() != 'pgi' +assert(not cc.has_link_argument(isnt_arg), 'Arg that should be broken is not.') assert(not cpp.has_link_argument(isnt_arg), 'Arg that should be broken is not.') assert(cc.get_supported_link_arguments([is_arg, isnt_arg, useless]) == [is_arg, useless], 'Arg filtering returned different result.') @@ -38,7 +39,9 @@ assert(l1.get(0) == is_arg, 'First supported returned wrong argument.') assert(l2.length() == 0, 'First supported did not return empty array.') assert(not cc.has_multi_link_arguments([isnt_arg, is_arg]), 'Arg that should be broken is not.') -assert(cc.has_multi_link_arguments(is_arg), 'Arg that should have worked does not work.') -assert(cc.has_multi_link_arguments([useless, is_arg]), 'Arg that should have worked does not work.') assert(not cc.has_link_argument('-Wl,-z,nodelete42'), 'Did not detect wrong -z linker argument') +endif + +assert(cc.has_multi_link_arguments(is_arg), 'Arg that should have worked does not work.') +assert(cc.has_multi_link_arguments([useless, is_arg]), 'Arg that should have worked does not work.') diff --git a/test cases/common/203 function attributes/meson.build b/test cases/common/203 function attributes/meson.build index 58ac7c8..43ed011 100644 --- a/test cases/common/203 function attributes/meson.build +++ b/test cases/common/203 function attributes/meson.build @@ -19,6 +19,10 @@ project('gcc func attributes', ['c', 'cpp']) c = meson.get_compiler('c') cpp = meson.get_compiler('cpp') +if c.get_id() == 'pgi' + error('MESON_SKIP_TEST: PGI supports its own set of features, will need a seperate list for PGI to test it.') +endif + expected_result = not ['msvc', 'clang-cl', 'intel-cl'].contains(c.get_id()) # Q: Why is ifunc not in this list or any of the below lists? |