aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/compilers/compilers.py
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2019-08-20 23:14:05 +0300
committerGitHub <noreply@github.com>2019-08-20 23:14:05 +0300
commit1ea3ddc6ad7458bbcb51704ccb5ef266c5164074 (patch)
tree62540e1b93a4844a5e5c45395a63bbfa3ec419d3 /mesonbuild/compilers/compilers.py
parent0384487fc52c445d005ca1ea5b2a1c83ce56200c (diff)
parent142012ab73bf1cfedb59f539400f0c18df055ec8 (diff)
downloadmeson-1ea3ddc6ad7458bbcb51704ccb5ef266c5164074.zip
meson-1ea3ddc6ad7458bbcb51704ccb5ef266c5164074.tar.gz
meson-1ea3ddc6ad7458bbcb51704ccb5ef266c5164074.tar.bz2
Merge pull request #5681 from dcbaker/dynamic-linker-split
split dynamic linker representations from compilers
Diffstat (limited to 'mesonbuild/compilers/compilers.py')
-rw-r--r--mesonbuild/compilers/compilers.py264
1 files changed, 98 insertions, 166 deletions
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