From 52aa24e99733c2ad5da57f436823dbe56ca2164a Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 2 Dec 2019 12:40:13 -0800 Subject: linkers: Fix Apple and VS-like linkers always_args Which would not call the super() method, thus overriding the default behavior when it should have instead extended it. --- mesonbuild/linkers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/linkers.py b/mesonbuild/linkers.py index 3251845..57760f7 100644 --- a/mesonbuild/linkers.py +++ b/mesonbuild/linkers.py @@ -595,7 +595,7 @@ class AppleDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): return self._apply_prefix('-undefined,error') def get_always_args(self) -> typing.List[str]: - return self._apply_prefix('-headerpad_max_install_names') + return self._apply_prefix('-headerpad_max_install_names') + super().get_always_args() def bitcode_args(self) -> typing.List[str]: return self._apply_prefix('-bitcode_bundle') @@ -818,7 +818,7 @@ class VisualStudioLikeLinkerMixin: return self._apply_prefix('/MACHINE:' + self.machine) + self._apply_prefix('/OUT:' + outputname) def get_always_args(self) -> typing.List[str]: - return self._apply_prefix('/nologo') + return self._apply_prefix('/nologo') + super().get_always_args() def get_search_args(self, dirname: str) -> typing.List[str]: return self._apply_prefix('/LIBPATH:' + dirname) -- cgit v1.1 From 5a83cb0d33e96f19e05b2c83dae314a35ec9d5c1 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 26 Nov 2019 15:00:32 -0800 Subject: Fix detection of D linker in MSVC environments Rather than trying to figure out if we're using MSVC based on environment variables, then trying to get the C compiler and test some attributes, get the C compiler and see if it's MSVC. This is much more reliable and we were already doing it anyway. --- mesonbuild/environment.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index d81f34f..8459d64 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -1339,10 +1339,11 @@ class Environment: info = self.machines[for_machine] # Detect the target architecture, required for proper architecture handling on Windows. - c_compiler = {} - is_msvc = mesonlib.is_windows() and 'VCINSTALLDIR' in os.environ - if is_msvc: - c_compiler = {'c': self.detect_c_compiler(for_machine)} # MSVC compiler is required for correct platform detection. + # MSVC compiler is required for correct platform detection. + c_compiler = {'c': self.detect_c_compiler(for_machine)} + is_msvc = isinstance(c_compiler['c'], VisualStudioCCompiler) + if not is_msvc: + c_compiler = {} arch = detect_cpu_family(c_compiler) if is_msvc and arch == 'x86': -- cgit v1.1 From 7658e67f92ba55e2d8e466f818293a001f34a65f Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 2 Dec 2019 12:52:42 -0800 Subject: backends/ninja: apply linker arguments when using rust This will be required to make switching the linker work with rust. --- mesonbuild/backend/ninjabackend.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 3c5cdf0..9ff6f74 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1293,6 +1293,8 @@ int dummy; else: raise InvalidArguments('Unknown target type for rustc.') args.append(cratetype) + if cratetype in {'bin', 'dylib'}: + args += rustc.linker.get_always_args() args += ['--crate-name', target.name] args += rustc.get_buildtype_args(self.get_option_for_target('buildtype', target)) args += rustc.get_debug_args(self.get_option_for_target('debug', target)) -- cgit v1.1 From ef9aeb188ea2bc7353e59916c18901cde90fa2b3 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 19 Nov 2019 09:30:46 -0800 Subject: Allow selecting the dynamic linker This uses the normal meson mechanisms, an LD environment variable or via cross/native files. Fixes: #6057 --- docs/markdown/snippets/linker_override.md | 17 +++ mesonbuild/compilers/compilers.py | 6 + mesonbuild/compilers/mixins/gnu.py | 4 + mesonbuild/compilers/mixins/visualstudio.py | 4 + mesonbuild/compilers/rust.py | 6 +- mesonbuild/envconfig.py | 2 + mesonbuild/environment.py | 206 +++++++++++++++++----------- mesonbuild/linkers.py | 28 ++-- run_unittests.py | 4 +- 9 files changed, 182 insertions(+), 95 deletions(-) create mode 100644 docs/markdown/snippets/linker_override.md diff --git a/docs/markdown/snippets/linker_override.md b/docs/markdown/snippets/linker_override.md new file mode 100644 index 0000000..21cb072 --- /dev/null +++ b/docs/markdown/snippets/linker_override.md @@ -0,0 +1,17 @@ +## Generic Overrider for Dynamic Linker selection + +Previous to meson 0.52.0 you set the dynamic linker using compiler specific +flags passed via language flags and hoped things worked out. In meson 0.52.0 +meson started detecting the linker and making intelligent decisions about +using it. Unfortunately this broke choosing a non-default linker. + +Now there is a generic mechanism for doing this, you may use the LD +environment variable (with normal meson environment variable rules), or add +the following to a cross or native file: + +```ini +[binaries] +ld = 'gold' +``` + +And meson will select the linker if possible. diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 52a2788..818c62c 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -1178,6 +1178,12 @@ class Compiler: def get_dependency_link_args(self, dep): return dep.get_link_args() + @classmethod + def use_linker_args(cls, linker: str) -> typing.List[str]: + """Get a list of arguments to pass to the compiler to set the linker. + """ + return [] + def get_largefile_args(compiler): ''' diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py index 64b0d3b..6683486 100644 --- a/mesonbuild/compilers/mixins/gnu.py +++ b/mesonbuild/compilers/mixins/gnu.py @@ -299,6 +299,10 @@ class GnuLikeCompiler(metaclass=abc.ABCMeta): return ['-isystem' + path] return ['-I' + path] + @classmethod + def use_linker_args(cls, linker: str) -> typing.List[str]: + return ['-fuse-ld={}'.format(linker)] + class GnuCompiler(GnuLikeCompiler): """ diff --git a/mesonbuild/compilers/mixins/visualstudio.py b/mesonbuild/compilers/mixins/visualstudio.py index 60b07c4..4798bdc 100644 --- a/mesonbuild/compilers/mixins/visualstudio.py +++ b/mesonbuild/compilers/mixins/visualstudio.py @@ -381,3 +381,7 @@ class VisualStudioLikeCompiler(metaclass=abc.ABCMeta): def get_argument_syntax(self) -> str: return 'msvc' + + @classmethod + def use_linker_args(cls, linker: str) -> typing.List[str]: + return [] diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index a17b697..405afea 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -32,7 +32,7 @@ rust_optimization_args = {'0': [], class RustCompiler(Compiler): - LINKER_PREFIX = '-Wl,' + # rustc doesn't invoke the compiler itself, it doesn't need a LINKER_PREFIX def __init__(self, exelist, version, for_machine: MachineChoice, is_cross, info: 'MachineInfo', exe_wrapper=None, **kwargs): @@ -109,3 +109,7 @@ class RustCompiler(Compiler): def get_std_exe_link_args(self): return [] + + # Rust does not have a use_linker_args because it dispatches to a gcc-like + # C compiler for dynamic linking, as such we invoke the C compiler's + # use_linker_args method instead. diff --git a/mesonbuild/envconfig.py b/mesonbuild/envconfig.py index 1adb08d..0f277a7 100644 --- a/mesonbuild/envconfig.py +++ b/mesonbuild/envconfig.py @@ -309,7 +309,9 @@ class BinaryTable(HasEnvVarFallback): 'strip': 'STRIP', 'ar': 'AR', 'windres': 'WINDRES', + 'ld': 'LD', + # Other tools 'cmake': 'CMAKE', 'qmake': 'QMAKE', 'pkgconfig': 'PKG_CONFIG', diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 8459d64..b286eca 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -733,55 +733,85 @@ class Environment: errmsg += '\nRunning "{0}" gave "{1}"'.format(c, e) raise EnvironmentException(errmsg) - @staticmethod - def _guess_win_linker(compiler: typing.List[str], for_machine: MachineChoice, - prefix: typing.Union[str, typing.List[str]]) -> 'DynamicLinker': + def _guess_win_linker(self, compiler: typing.List[str], comp_class: Compiler, + for_machine: MachineChoice, + prefix: typing.Union[str, typing.List[str]], + *, use_linker_prefix: bool = True) -> 'DynamicLinker': # Explicitly pass logo here so that we can get the version of link.exe - if isinstance(prefix, str): - check_args = [prefix + '/logo', prefix + '--version'] - else: - check_args = prefix + ['/logo'] + prefix + ['--version'] + if not use_linker_prefix or comp_class.LINKER_PREFIX is None: + check_args = ['/logo', '--version'] + elif isinstance(comp_class.LINKER_PREFIX, str): + check_args = [comp_class.LINKER_PREFIX + '/logo', comp_class.LINKER_PREFIX + '--version'] + elif isinstance(comp_class.LINKER_PREFIX, list): + check_args = comp_class.LINKER_PREFIX + ['/logo'] + comp_class.LINKER_PREFIX + ['--version'] + + override = [] # type: typing.List[str] + value = self.binaries[for_machine].lookup_entry('ld') + if value is not None: + override = comp_class.use_linker_args(value[0]) + check_args += override + p, o, _ = Popen_safe(compiler + check_args) if o.startswith('LLD'): if '(compatible with GNU linkers)' in o: - return LLVMDynamicLinker(compiler, for_machine, 'lld', prefix, version=search_version(o)) - else: - return ClangClDynamicLinker(for_machine, exelist=compiler, prefix=prefix, version=search_version(o)) - elif o.startswith('Microsoft'): - match = re.search(r'.*(X86|X64|ARM|ARM64).*', o) + return LLVMDynamicLinker( + compiler, for_machine, 'lld', comp_class.LINKER_PREFIX, + override, version=search_version(o)) + + if value is not None: + compiler = value + + p, o, e = Popen_safe(compiler + check_args) + if o.startswith('LLD'): + return ClangClDynamicLinker( + for_machine, [], + prefix=comp_class.LINKER_PREFIX if use_linker_prefix else [], + exelist=compiler, version=search_version(o)) + elif 'OPTLINK' in o: + # Opltink's stdout *may* beging with a \r character. + return OptlinkDynamicLinker(for_machine, version=search_version(o)) + elif o.startswith('Microsoft') or e.startswith('Microsoft'): + out = o or e + match = re.search(r'.*(X86|X64|ARM|ARM64).*', out) if match: target = str(match.group(1)) else: target = 'x86' + return MSVCDynamicLinker( - for_machine, machine=target, exelist=compiler, prefix=prefix, - version=search_version(o)) - raise MesonException('Cannot guess dynamic linker') + for_machine, [], machine=target, exelist=compiler, + prefix=comp_class.LINKER_PREFIX if use_linker_prefix else [], + version=search_version(out)) + raise EnvironmentException('Unable to determine dynamic linker') - @staticmethod - def _guess_nix_linker(compiler: typing.List[str], for_machine: MachineChoice, + def _guess_nix_linker(self, compiler: typing.List[str], comp_class: typing.Type[Compiler], + for_machine: MachineChoice, prefix: typing.Union[str, typing.List[str]], *, 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. + :compiler: Invocation to use to get linker + :comp_class: The Compiler Type (uninstantiated) + :for_machine: which machine this linker targets :extra_args: Any additional arguments required (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 + + override = [] # type: typing.List[str] + value = self.binaries[for_machine].lookup_entry('ld') + if value is not None: + override = comp_class.use_linker_args(value[0]) + check_args += override + _, o, e = Popen_safe(compiler + check_args) v = search_version(o) if o.startswith('LLD'): - linker = LLVMDynamicLinker(compiler, for_machine, 'lld', prefix, version=v) # type: DynamicLinker + linker = LLVMDynamicLinker(compiler, for_machine, 'lld', prefix, override, version=v) # type: DynamicLinker elif e.startswith('lld-link: '): # Toolchain wrapper got in the way; this happens with e.g. https://github.com/mstorsjo/llvm-mingw # Let's try to extract the linker invocation command to grab the version. @@ -797,7 +827,7 @@ class Environment: _, o, e = Popen_safe([linker_cmd, '--version']) v = search_version(o) - linker = LLVMDynamicLinker(compiler, for_machine, 'lld', prefix, version=v) + linker = LLVMDynamicLinker(compiler, for_machine, 'lld', prefix, override, version=v) # 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): @@ -811,19 +841,19 @@ class Environment: break else: v = 'unknown version' - linker = AppleDynamicLinker(compiler, for_machine, i, prefix, version=v) + linker = AppleDynamicLinker(compiler, for_machine, i, prefix, override, 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, prefix, version=v) + linker = GnuDynamicLinker(compiler, for_machine, i, prefix, override, version=v) elif 'Solaris' in e or 'Solaris' in o: linker = SolarisDynamicLinker( - compiler, for_machine, 'solaris', prefix, + compiler, for_machine, 'solaris', prefix, override, version=search_version(e)) else: - raise MesonException('Unable to determine dynamic linker.') + raise EnvironmentException('Unable to determine dynamic linker') return linker def _detect_c_or_cpp_compiler(self, lang: str, for_machine: MachineChoice) -> Compiler: @@ -898,7 +928,7 @@ class Environment: version = self.get_gnu_version_from_defines(defines) cls = GnuCCompiler if lang == 'c' else GnuCPPCompiler - linker = self._guess_nix_linker(compiler, for_machine, cls.LINKER_PREFIX) + linker = self._guess_nix_linker(compiler, cls, for_machine, cls.LINKER_PREFIX) return cls( ccache + compiler, version, for_machine, is_cross, info, exe_wrap, defines, full_version=full_version, @@ -925,7 +955,7 @@ class Environment: version = search_version(arm_ver_str) full_version = arm_ver_str cls = ArmclangCCompiler if lang == 'c' else ArmclangCPPCompiler - linker = ArmClangDynamicLinker(for_machine, version=version) + linker = ArmClangDynamicLinker(for_machine, [], version=version) return cls( ccache + compiler, version, for_machine, is_cross, info, exe_wrap, full_version=full_version, linker=linker) @@ -944,7 +974,7 @@ class Environment: else: target = 'unknown target' cls = ClangClCCompiler if lang == 'c' else ClangClCPPCompiler - linker = ClangClDynamicLinker(for_machine, version=version) + linker = self._guess_win_linker(['lld-link'], cls, for_machine) return cls( compiler, version, for_machine, is_cross, info, exe_wrap, target, linker=linker) @@ -963,11 +993,11 @@ class Environment: # style ld, but for clang on "real" windows we'll use # either link.exe or lld-link.exe try: - linker = self._guess_win_linker(compiler, for_machine, cls.LINKER_PREFIX) + linker = self._guess_win_linker(compiler, cls, for_machine, cls.LINKER_PREFIX) except MesonException: pass if linker is None: - linker = self._guess_nix_linker(compiler, for_machine, cls.LINKER_PREFIX) + linker = self._guess_nix_linker(compiler, cls, for_machine, cls.LINKER_PREFIX) return cls( ccache + compiler, version, for_machine, is_cross, info, @@ -999,23 +1029,23 @@ class Environment: else: m = 'Failed to detect MSVC compiler target architecture: \'cl /?\' output is\n{}' raise EnvironmentException(m.format(cl_signature)) - linker = MSVCDynamicLinker(for_machine, version=version) cls = VisualStudioCCompiler if lang == 'c' else VisualStudioCPPCompiler + linker = self._guess_win_linker(['link'], cls, for_machine) return cls( compiler, version, for_machine, is_cross, info, exe_wrap, target, linker=linker) if 'PGI Compilers' in out: cls = PGICCompiler if lang == 'c' else PGICPPCompiler - linker = PGIDynamicLinker(compiler, for_machine, 'pgi', cls.LINKER_PREFIX, version=version) + linker = PGIDynamicLinker(compiler, for_machine, 'pgi', cls.LINKER_PREFIX, [], version=version) return cls( ccache + compiler, version, for_machine, is_cross, info, exe_wrap, linker=linker) if '(ICC)' in out: cls = IntelCCompiler if lang == 'c' else IntelCPPCompiler if self.machines[for_machine].is_darwin(): - l = XildAppleDynamicLinker(compiler, for_machine, 'xild', cls.LINKER_PREFIX, version=version) + l = XildAppleDynamicLinker(compiler, for_machine, 'xild', cls.LINKER_PREFIX, [], version=version) else: - l = XildLinuxDynamicLinker(compiler, for_machine, 'xild', cls.LINKER_PREFIX, version=version) + l = XildLinuxDynamicLinker(compiler, for_machine, 'xild', cls.LINKER_PREFIX, [], version=version) return cls( ccache + compiler, version, for_machine, is_cross, info, exe_wrap, full_version=full_version, linker=l) @@ -1113,7 +1143,7 @@ class Environment: version = self.get_gnu_version_from_defines(defines) cls = GnuFortranCompiler linker = self._guess_nix_linker( - compiler, for_machine, cls.LINKER_PREFIX) + compiler, cls, for_machine, cls.LINKER_PREFIX) return cls( compiler, version, for_machine, is_cross, info, exe_wrap, defines, full_version=full_version, @@ -1121,7 +1151,7 @@ class Environment: if 'G95' in out: linker = self._guess_nix_linker( - compiler, for_machine, cls.LINKER_PREFIX) + compiler, cls, for_machine, cls.LINKER_PREFIX) return G95FortranCompiler( compiler, version, for_machine, is_cross, info, exe_wrap, full_version=full_version, linker=linker) @@ -1129,7 +1159,7 @@ class Environment: if 'Sun Fortran' in err: version = search_version(err) linker = self._guess_nix_linker( - compiler, for_machine, cls.LINKER_PREFIX) + compiler, cls, for_machine, cls.LINKER_PREFIX) return SunFortranCompiler( compiler, version, for_machine, is_cross, info, exe_wrap, full_version=full_version, linker=linker) @@ -1137,14 +1167,14 @@ class Environment: if 'Intel(R) Visual Fortran' in err: version = search_version(err) target = 'x86' if 'IA-32' in err else 'x86_64' - linker = XilinkDynamicLinker(for_machine, version=version) + linker = XilinkDynamicLinker(for_machine, [], version=version) return IntelClFortranCompiler( compiler, version, for_machine, is_cross, target, info, exe_wrap, linker=linker) if 'ifort (IFORT)' in out: linker = XildLinuxDynamicLinker( - compiler, for_machine, 'xild', IntelFortranCompiler.LINKER_PREFIX, version=version) + compiler, for_machine, 'xild', IntelFortranCompiler.LINKER_PREFIX, [], version=version) return IntelFortranCompiler( compiler, version, for_machine, is_cross, info, exe_wrap, full_version=full_version, linker=linker) @@ -1164,21 +1194,21 @@ class Environment: if 'flang' in out or 'clang' in out: linker = self._guess_nix_linker( - compiler, for_machine, FlangFortranCompiler.LINKER_PREFIX) + compiler, FlangFortranCompiler, for_machine, FlangFortranCompiler.LINKER_PREFIX) return FlangFortranCompiler( compiler, version, for_machine, is_cross, info, exe_wrap, full_version=full_version, linker=linker) if 'Open64 Compiler Suite' in err: linker = self._guess_nix_linker( - compiler, for_machine, Open64FortranCompiler.LINKER_PREFIX) + compiler, Open64FortranCompiler, for_machine, Open64FortranCompiler.LINKER_PREFIX) return Open64FortranCompiler( compiler, version, for_machine, is_cross, info, exe_wrap, full_version=full_version, linker=linker) if 'NAG Fortran' in err: linker = self._guess_nix_linker( - compiler, for_machine, NAGFortranCompiler.LINKER_PREFIX) + compiler, NAGFortranCompiler, for_machine, NAGFortranCompiler.LINKER_PREFIX) return NAGFortranCompiler( compiler, version, for_machine, is_cross, info, exe_wrap, full_version=full_version, linker=linker) @@ -1217,7 +1247,7 @@ class Environment: continue version = self.get_gnu_version_from_defines(defines) comp = GnuObjCCompiler if objc else GnuObjCPPCompiler - linker = self._guess_nix_linker(compiler, for_machine, comp.LINKER_PREFIX) + linker = self._guess_nix_linker(compiler, comp, for_machine, comp.LINKER_PREFIX) return comp( ccache + compiler, version, for_machine, is_cross, info, exe_wrap, defines, linker=linker) @@ -1227,13 +1257,13 @@ class Environment: if 'windows' in out or self.machines[for_machine].is_windows(): # If we're in a MINGW context this actually will use a gnu style ld try: - linker = self._guess_win_linker(compiler, for_machine, comp.LINKER_PREFIX) + linker = self._guess_win_linker(compiler, comp, for_machine, comp.LINKER_PREFIX) except MesonException: pass if not linker: linker = self._guess_nix_linker( - compiler, for_machine, comp.LINKER_PREFIX) + compiler, comp, for_machine, comp.LINKER_PREFIX) return comp( ccache + compiler, version, for_machine, is_cross, info, exe_wrap, linker=linker) @@ -1316,19 +1346,36 @@ class Environment: version = search_version(out) if 'rustc' in out: - # 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. - # Currently 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', RustCompiler.LINKER_PREFIX) + if info.is_windows() or info.is_cygwin(): + # On windows rustc invokes link.exe + linker = self._guess_win_linker(['link'], for_machine) else: - linker = GnuDynamicLinker( - [], for_machine, 'GNU ld', RustCompiler.LINKER_PREFIX) + # On Linux and mac rustc will invoke gcc (clang for mac + # presumably), for dynamic linking. this means the easiest + # way to C compiler for dynamic linking. figure out what + # linker to use !windows it to just get the value of the C + # compiler and use that as the basis of the rust linker. + # However, there are two things we need to change, if CC is + # not the default use that, and second add the necessary + # arguments to rust to use -fuse-ld + cc = self.detect_c_compiler(for_machine) + + extra_args = [] + if not ((info.is_darwin() and isinstance(cc, AppleClangCCompiler)) or + isinstance(cc, GnuCCompiler)): + c = cc.exelist[1] if cc.exelist[0].endswith('ccache') else cc.exelist[0] + extra_args.extend(['-C', 'linker={}'.format(c)]) + + value = self.binaries[for_machine].lookup_entry('ld') + if value is not None: + for a in cc.use_linker_args(value[0]): + extra_args.extend(['-C', 'link-arg={}'.format(a)]) + + # This trickery with type() gets us the class of the linker + # so we can initialize a new copy for the Rust Compiler + linker = type(cc.linker)(compiler, for_machine, cc.linker.id, cc.LINKER_PREFIX, + always_args=extra_args, version=cc.linker.version) + return RustCompiler( compiler, version, for_machine, is_cross, info, exe_wrap, linker=linker) @@ -1375,26 +1422,25 @@ class Environment: # LDC seems to require a file m = self.machines[for_machine] if m.is_windows() or m.is_cygwin(): - if is_msvc: - linker = MSVCDynamicLinker(for_machine, version=version) - else: - # Getting LDC on windows to give useful linker output when not - # doing real work is painfully hard. It ships with a version 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)) + # Getting LDC on windows to give useful linker output when + # not doing real work is painfully hard. It ships with a + # version of lld-link, so unless we think the user wants + # link.exe, just assume that we're going to use lld-link + # with it. + linker = self._guess_win_linker( + ['link' if is_msvc else 'lld-link'], + compilers.LLVMDCompiler, for_machine, use_linker_prefix=False) else: with tempfile.NamedTemporaryFile(suffix='.d') as f: linker = self._guess_nix_linker( - exelist, for_machine, + exelist, compilers.LLVMDCompiler, for_machine, compilers.LLVMDCompiler.LINKER_PREFIX, extra_args=[f.name]) return compilers.LLVMDCompiler( exelist, version, for_machine, info, arch, full_version=full_version, linker=linker) elif 'gdc' in out: - linker = self._guess_nix_linker(exelist, for_machine, compilers.GnuDCompiler.LINKER_PREFIX) + linker = self._guess_nix_linker(exelist, compilers.GnuDCompiler, for_machine, compilers.GnuDCompiler.LINKER_PREFIX) return compilers.GnuDCompiler( exelist, version, for_machine, info, arch, is_cross, exe_wrap, full_version=full_version, linker=linker) @@ -1403,17 +1449,17 @@ class Environment: m = self.machines[for_machine] if m.is_windows() or m.is_cygwin(): if is_msvc: - linker = MSVCDynamicLinker(for_machine, version=version) + linker_cmd = ['link'] elif arch == 'x86': - linker = OptlinkDynamicLinker(for_machine, version=full_version) + linker_cmd = ['optlink'] else: - # DMD ships with lld-link - _, o, _ = Popen_safe(['lld-link.exe', '--version']) - linker = ClangClDynamicLinker(for_machine, version=search_version(o)) + linker_cmd = ['lld-link'] + linker = self._guess_win_linker(linker_cmd, compilers.DmdDCompiler, for_machine, + use_linker_prefix=False) else: with tempfile.NamedTemporaryFile(suffix='.d') as f: linker = self._guess_nix_linker( - exelist, for_machine, + exelist, compilers.DmdDCompiler, for_machine, compilers.LLVMDCompiler.LINKER_PREFIX, extra_args=[f.name]) return compilers.DmdDCompiler( @@ -1440,7 +1486,7 @@ class Environment: # 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, + exelist, compilers.SwiftCompiler, for_machine, compilers.SwiftCompiler.LINKER_PREFIX, extra_args=[f.name]) return compilers.SwiftCompiler( diff --git a/mesonbuild/linkers.py b/mesonbuild/linkers.py index 57760f7..021134e 100644 --- a/mesonbuild/linkers.py +++ b/mesonbuild/linkers.py @@ -247,17 +247,21 @@ class DynamicLinker(metaclass=abc.ABCMeta): } # type: typing.Dict[str, typing.List[str]] def _apply_prefix(self, arg: str) -> typing.List[str]: - if isinstance(self.prefix_arg, str): + if self.prefix_arg is None: + return [arg] + elif isinstance(self.prefix_arg, str): return [self.prefix_arg + arg] return self.prefix_arg + [arg] def __init__(self, exelist: typing.List[str], for_machine: mesonlib.MachineChoice, - id_: str, prefix_arg: typing.Union[str, typing.List[str]], *, version: str = 'unknown version'): + id_: str, prefix_arg: typing.Union[str, typing.List[str]], + always_args: typing.List[str], *, version: str = 'unknown version'): self.exelist = exelist self.for_machine = for_machine self.version = version self.id = id_ self.prefix_arg = prefix_arg + self.always_args = always_args def __repr__(self) -> str: return '<{}: v{} `{}`>'.format(type(self).__name__, self.version, ' '.join(self.exelist)) @@ -276,7 +280,7 @@ class DynamicLinker(metaclass=abc.ABCMeta): return mesonlib.is_windows() def get_always_args(self) -> typing.List[str]: - return [] + return self.always_args.copy() def get_lib_prefix(self) -> str: return '' @@ -715,7 +719,7 @@ class ArmDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): def __init__(self, for_machine: mesonlib.MachineChoice, *, version: str = 'unknown version'): - super().__init__(['armlink'], for_machine, 'armlink', '', + super().__init__(['armlink'], for_machine, 'armlink', '', [], version=version) def get_accepts_rsp(self) -> bool: @@ -853,33 +857,33 @@ class MSVCDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): """Microsoft's Link.exe.""" - def __init__(self, for_machine: mesonlib.MachineChoice, *, + def __init__(self, for_machine: mesonlib.MachineChoice, always_args: typing.List[str], *, exelist: typing.Optional[typing.List[str]] = None, prefix: typing.Union[str, typing.List[str]] = '', machine: str = 'x86', version: str = 'unknown version'): super().__init__(exelist or ['link.exe'], for_machine, 'link', - prefix, machine=machine, version=version) + prefix, always_args, machine=machine, version=version) class ClangClDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): """Clang's lld-link.exe.""" - def __init__(self, for_machine: mesonlib.MachineChoice, *, + def __init__(self, for_machine: mesonlib.MachineChoice, always_args: typing.List[str], *, exelist: typing.Optional[typing.List[str]] = None, prefix: typing.Union[str, typing.List[str]] = '', version: str = 'unknown version'): - super().__init__(exelist or ['lld-link.exe'], for_machine, - 'lld-link', prefix, version=version) + super().__init__(exelist or ['lld-link.exe'], for_machine, 'lld-link', + prefix, always_args, version=version) class XilinkDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): """Intel's Xilink.exe.""" - def __init__(self, for_machine: mesonlib.MachineChoice, + def __init__(self, for_machine: mesonlib.MachineChoice, always_args: typing.List[str], *, version: str = 'unknown version'): - super().__init__(['xilink.exe'], for_machine, 'xilink', '', version=version) + super().__init__(['xilink.exe'], for_machine, 'xilink', '', always_args, version=version) class SolarisDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): @@ -936,7 +940,7 @@ class OptlinkDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): *, 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', prefix_arg='', version=version) + super().__init__(['optlink.exe'], for_machine, 'optlink', '', [], version=version) def get_allow_undefined_args(self) -> typing.List[str]: return [] diff --git a/run_unittests.py b/run_unittests.py index 4e56c1d..bf67729 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -445,7 +445,7 @@ class InternalTests(unittest.TestCase): def test_compiler_args_class_gnuld(self): cargsfunc = mesonbuild.compilers.CompilerArgs ## Test --start/end-group - linker = mesonbuild.linkers.GnuDynamicLinker([], MachineChoice.HOST, 'fake', '-Wl,') + linker = mesonbuild.linkers.GnuDynamicLinker([], MachineChoice.HOST, 'fake', '-Wl,', []) gcc = mesonbuild.compilers.GnuCCompiler([], 'fake', False, MachineChoice.HOST, mock.Mock(), linker=linker) ## Ensure that the fake compiler is never called by overriding the relevant function gcc.get_default_include_dirs = lambda: ['/usr/include', '/usr/share/include', '/usr/local/include'] @@ -474,7 +474,7 @@ class InternalTests(unittest.TestCase): def test_compiler_args_remove_system(self): cargsfunc = mesonbuild.compilers.CompilerArgs ## Test --start/end-group - linker = mesonbuild.linkers.GnuDynamicLinker([], MachineChoice.HOST, 'fake', '-Wl,') + linker = mesonbuild.linkers.GnuDynamicLinker([], MachineChoice.HOST, 'fake', '-Wl,', []) gcc = mesonbuild.compilers.GnuCCompiler([], 'fake', False, MachineChoice.HOST, mock.Mock(), linker=linker) ## Ensure that the fake compiler is never called by overriding the relevant function gcc.get_default_include_dirs = lambda: ['/usr/include', '/usr/share/include', '/usr/local/include'] -- cgit v1.1 From 8761ca7b7d0f7f8cc26be49329dd48b52da54d62 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 19 Nov 2019 09:41:46 -0800 Subject: environment: Remove duplicate argument from _guess_nix_linker Since we pass the whole compiler class (as a type()) we don't need to also pass it's LINKER_PREFIX attribute, we can just get it from the type we're passing. --- mesonbuild/environment.py | 59 ++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index b286eca..47fb9f1 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -734,9 +734,8 @@ class Environment: raise EnvironmentException(errmsg) def _guess_win_linker(self, compiler: typing.List[str], comp_class: Compiler, - for_machine: MachineChoice, - prefix: typing.Union[str, typing.List[str]], - *, use_linker_prefix: bool = True) -> 'DynamicLinker': + for_machine: MachineChoice, *, + use_linker_prefix: bool = True) -> 'DynamicLinker': # Explicitly pass logo here so that we can get the version of link.exe if not use_linker_prefix or comp_class.LINKER_PREFIX is None: check_args = ['/logo', '--version'] @@ -785,8 +784,7 @@ class Environment: raise EnvironmentException('Unable to determine dynamic linker') def _guess_nix_linker(self, compiler: typing.List[str], comp_class: typing.Type[Compiler], - for_machine: MachineChoice, - prefix: typing.Union[str, typing.List[str]], *, + for_machine: MachineChoice, *, extra_args: typing.Optional[typing.List[str]] = None) -> 'DynamicLinker': """Helper for guessing what linker to use on Unix-Like OSes. @@ -797,10 +795,10 @@ class Environment: """ extra_args = typing.cast(typing.List[str], extra_args or []) - if isinstance(prefix, str): - check_args = [prefix + '--version'] + extra_args + if isinstance(comp_class.LINKER_PREFIX, str): + check_args = [comp_class.LINKER_PREFIX + '--version'] + extra_args else: - check_args = prefix + ['--version'] + extra_args + check_args = comp_class.LINKER_PREFIX + ['--version'] + extra_args override = [] # type: typing.List[str] value = self.binaries[for_machine].lookup_entry('ld') @@ -811,7 +809,7 @@ class Environment: _, o, e = Popen_safe(compiler + check_args) v = search_version(o) if o.startswith('LLD'): - linker = LLVMDynamicLinker(compiler, for_machine, 'lld', prefix, override, version=v) # type: DynamicLinker + linker = LLVMDynamicLinker(compiler, for_machine, 'lld', comp_class.LINKER_PREFIX, override, version=v) # type: DynamicLinker elif e.startswith('lld-link: '): # Toolchain wrapper got in the way; this happens with e.g. https://github.com/mstorsjo/llvm-mingw # Let's try to extract the linker invocation command to grab the version. @@ -827,13 +825,13 @@ class Environment: _, o, e = Popen_safe([linker_cmd, '--version']) v = search_version(o) - linker = LLVMDynamicLinker(compiler, for_machine, 'lld', prefix, override, version=v) + linker = LLVMDynamicLinker(compiler, for_machine, 'lld', comp_class.LINKER_PREFIX, override, version=v) # 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) + if isinstance(comp_class.LINKER_PREFIX, str): + _, _, e = Popen_safe(compiler + [comp_class.LINKER_PREFIX + '-v'] + extra_args) else: - _, _, e = Popen_safe(compiler + prefix + ['-v'] + extra_args) + _, _, e = Popen_safe(compiler + comp_class.LINKER_PREFIX + ['-v'] + extra_args) i = 'APPLE ld' for line in e.split('\n'): if 'PROJECT:ld' in line: @@ -841,16 +839,16 @@ class Environment: break else: v = 'unknown version' - linker = AppleDynamicLinker(compiler, for_machine, i, prefix, override, version=v) + linker = AppleDynamicLinker(compiler, for_machine, i, comp_class.LINKER_PREFIX, override, 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, prefix, override, version=v) + linker = GnuDynamicLinker(compiler, for_machine, i, comp_class.LINKER_PREFIX, override, version=v) elif 'Solaris' in e or 'Solaris' in o: linker = SolarisDynamicLinker( - compiler, for_machine, 'solaris', prefix, override, + compiler, for_machine, 'solaris', comp_class.LINKER_PREFIX, override, version=search_version(e)) else: raise EnvironmentException('Unable to determine dynamic linker') @@ -928,7 +926,7 @@ class Environment: version = self.get_gnu_version_from_defines(defines) cls = GnuCCompiler if lang == 'c' else GnuCPPCompiler - linker = self._guess_nix_linker(compiler, cls, for_machine, cls.LINKER_PREFIX) + linker = self._guess_nix_linker(compiler, cls, for_machine) return cls( ccache + compiler, version, for_machine, is_cross, info, exe_wrap, defines, full_version=full_version, @@ -993,11 +991,11 @@ class Environment: # style ld, but for clang on "real" windows we'll use # either link.exe or lld-link.exe try: - linker = self._guess_win_linker(compiler, cls, for_machine, cls.LINKER_PREFIX) + linker = self._guess_win_linker(compiler, cls, for_machine) except MesonException: pass if linker is None: - linker = self._guess_nix_linker(compiler, cls, for_machine, cls.LINKER_PREFIX) + linker = self._guess_nix_linker(compiler, cls, for_machine) return cls( ccache + compiler, version, for_machine, is_cross, info, @@ -1143,7 +1141,7 @@ class Environment: version = self.get_gnu_version_from_defines(defines) cls = GnuFortranCompiler linker = self._guess_nix_linker( - compiler, cls, for_machine, cls.LINKER_PREFIX) + compiler, cls, for_machine) return cls( compiler, version, for_machine, is_cross, info, exe_wrap, defines, full_version=full_version, @@ -1151,7 +1149,7 @@ class Environment: if 'G95' in out: linker = self._guess_nix_linker( - compiler, cls, for_machine, cls.LINKER_PREFIX) + compiler, cls, for_machine) return G95FortranCompiler( compiler, version, for_machine, is_cross, info, exe_wrap, full_version=full_version, linker=linker) @@ -1159,7 +1157,7 @@ class Environment: if 'Sun Fortran' in err: version = search_version(err) linker = self._guess_nix_linker( - compiler, cls, for_machine, cls.LINKER_PREFIX) + compiler, cls, for_machine) return SunFortranCompiler( compiler, version, for_machine, is_cross, info, exe_wrap, full_version=full_version, linker=linker) @@ -1194,21 +1192,21 @@ class Environment: if 'flang' in out or 'clang' in out: linker = self._guess_nix_linker( - compiler, FlangFortranCompiler, for_machine, FlangFortranCompiler.LINKER_PREFIX) + compiler, FlangFortranCompiler, for_machine) return FlangFortranCompiler( compiler, version, for_machine, is_cross, info, exe_wrap, full_version=full_version, linker=linker) if 'Open64 Compiler Suite' in err: linker = self._guess_nix_linker( - compiler, Open64FortranCompiler, for_machine, Open64FortranCompiler.LINKER_PREFIX) + compiler, Open64FortranCompiler, for_machine) return Open64FortranCompiler( compiler, version, for_machine, is_cross, info, exe_wrap, full_version=full_version, linker=linker) if 'NAG Fortran' in err: linker = self._guess_nix_linker( - compiler, NAGFortranCompiler, for_machine, NAGFortranCompiler.LINKER_PREFIX) + compiler, NAGFortranCompiler, for_machine) return NAGFortranCompiler( compiler, version, for_machine, is_cross, info, exe_wrap, full_version=full_version, linker=linker) @@ -1247,7 +1245,7 @@ class Environment: continue version = self.get_gnu_version_from_defines(defines) comp = GnuObjCCompiler if objc else GnuObjCPPCompiler - linker = self._guess_nix_linker(compiler, comp, for_machine, comp.LINKER_PREFIX) + linker = self._guess_nix_linker(compiler, comp, for_machine) return comp( ccache + compiler, version, for_machine, is_cross, info, exe_wrap, defines, linker=linker) @@ -1257,13 +1255,13 @@ class Environment: if 'windows' in out or self.machines[for_machine].is_windows(): # If we're in a MINGW context this actually will use a gnu style ld try: - linker = self._guess_win_linker(compiler, comp, for_machine, comp.LINKER_PREFIX) + linker = self._guess_win_linker(compiler, comp, for_machine) except MesonException: pass if not linker: linker = self._guess_nix_linker( - compiler, comp, for_machine, comp.LINKER_PREFIX) + compiler, comp, for_machine) return comp( ccache + compiler, version, for_machine, is_cross, info, exe_wrap, linker=linker) @@ -1434,13 +1432,12 @@ class Environment: with tempfile.NamedTemporaryFile(suffix='.d') as f: linker = self._guess_nix_linker( exelist, compilers.LLVMDCompiler, for_machine, - compilers.LLVMDCompiler.LINKER_PREFIX, extra_args=[f.name]) return compilers.LLVMDCompiler( exelist, version, for_machine, info, arch, full_version=full_version, linker=linker) elif 'gdc' in out: - linker = self._guess_nix_linker(exelist, compilers.GnuDCompiler, for_machine, compilers.GnuDCompiler.LINKER_PREFIX) + linker = self._guess_nix_linker(exelist, compilers.GnuDCompiler, for_machine) return compilers.GnuDCompiler( exelist, version, for_machine, info, arch, is_cross, exe_wrap, full_version=full_version, linker=linker) @@ -1460,7 +1457,6 @@ class Environment: with tempfile.NamedTemporaryFile(suffix='.d') as f: linker = self._guess_nix_linker( exelist, compilers.DmdDCompiler, for_machine, - compilers.LLVMDCompiler.LINKER_PREFIX, extra_args=[f.name]) return compilers.DmdDCompiler( exelist, version, for_machine, info, arch, @@ -1487,7 +1483,6 @@ class Environment: with tempfile.NamedTemporaryFile(suffix='.swift') as f: linker = self._guess_nix_linker( exelist, compilers.SwiftCompiler, for_machine, - compilers.SwiftCompiler.LINKER_PREFIX, extra_args=[f.name]) return compilers.SwiftCompiler( exelist, version, for_machine, info, is_cross, linker=linker) -- cgit v1.1 From 32e0bcc516e2a6160a4884ae250e817dde3fed2a Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 20 Nov 2019 11:08:42 -0800 Subject: docs: Update docs for LD and ld entries --- docs/markdown/Cross-compilation.md | 7 +++++++ docs/markdown/Native-environments.md | 1 + docs/markdown/howtox.md | 25 +++++++++++++++++++++++-- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/docs/markdown/Cross-compilation.md b/docs/markdown/Cross-compilation.md index 43e1382..d94d487 100644 --- a/docs/markdown/Cross-compilation.md +++ b/docs/markdown/Cross-compilation.md @@ -87,6 +87,7 @@ this: [binaries] c = '/usr/bin/i586-mingw32msvc-gcc' cpp = '/usr/bin/i586-mingw32msvc-g++' +ld = 'gold' ar = '/usr/i586-mingw32msvc/bin/ar' strip = '/usr/i586-mingw32msvc/bin/strip' pkgconfig = '/usr/bin/i586-mingw32msvc-pkg-config' @@ -102,6 +103,12 @@ of a wrapper, these lines are all you need to write. Meson will automatically use the given wrapper when it needs to run host binaries. This happens e.g. when running the project's test suite. +ld is special because it is compiler specific. For compilers like gcc and +clang which are used to invoke the linker this is a value to pass to their +"choose the linker" argument (-fuse-ld= in this case). For compilers like +MSVC and Clang-Cl, this is the path to a linker for meson to invoke, such as +`link.exe` or `lld-link.exe`. Support for ls is *new in 0.53.0* + The next section lists properties of the cross compiler and its target system, and thus properties of host system of what we're building. It looks like this: diff --git a/docs/markdown/Native-environments.md b/docs/markdown/Native-environments.md index 41e678e..677f067 100644 --- a/docs/markdown/Native-environments.md +++ b/docs/markdown/Native-environments.md @@ -40,6 +40,7 @@ like `llvm-config` c = '/usr/local/bin/clang' cpp = '/usr/local/bin/clang++' rust = '/usr/local/bin/rust' +ld = 'gold' llvm-config = '/usr/local/llvm-svn/bin/llvm-config' ``` diff --git a/docs/markdown/howtox.md b/docs/markdown/howtox.md index 188f1f7..f73d6b9 100644 --- a/docs/markdown/howtox.md +++ b/docs/markdown/howtox.md @@ -23,8 +23,29 @@ compilation is done by setting `CC` to point to the cross compiler that Meson supports natively the case where you compile helper tools (such as code generators) and use the results during the build. Because of this Meson needs to know both the native and the -cross compiler. The former is set via the environment variables and -the latter via the cross file only. +cross compiler. The former is set via the environment variables or +native-files and the latter via the cross file only. + +## Set dynamic linker + +```console +$ CC=clang LD=lld meson +``` + +or + +```console +$ CC=clang-cl LD=link meson +``` + +Like the compiler, the linker is selected via the LD environment variable, or +through the `ld` entry in a native or cross file. You must be aware of +whehter you're using a compiler that invokes the linker itself (most +compilers including GCC and Clang) or a linker that is invoked directly (when +using MSVC or compilers that act like it, including Clang-Cl). With the +former `ld` or `LD` should be the value to pass to the compiler's special +argument (such as `-fuse-ld` with clang and gcc), with the latter it should +be an exectuable, such as `lld-link.exe`. ## Set default C/C++ language version -- cgit v1.1 From ab5ea9e8b60f043551a2920f70d6f3a1532d5bf8 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 26 Nov 2019 12:29:12 -0800 Subject: environment: unify platform detection logic I noticed this while I was here, it's not much, just a small cleanup to the platform detection logic. --- mesonbuild/environment.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 47fb9f1..83c6935 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -1418,8 +1418,7 @@ class Environment: if 'LLVM D compiler' in out: # LDC seems to require a file - m = self.machines[for_machine] - if m.is_windows() or m.is_cygwin(): + if info.is_windows() or info.is_cygwin(): # Getting LDC on windows to give useful linker output when # not doing real work is painfully hard. It ships with a # version of lld-link, so unless we think the user wants @@ -1443,8 +1442,7 @@ class Environment: full_version=full_version, linker=linker) elif 'The D Language Foundation' in out or 'Digital Mars' in out: # DMD seems to require a file - m = self.machines[for_machine] - if m.is_windows() or m.is_cygwin(): + if info.is_windows() or info.is_cygwin(): if is_msvc: linker_cmd = ['link'] elif arch == 'x86': -- cgit v1.1 From 0ae911d893485c4b4c5f90f9d6b5f820dc1f7672 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 26 Nov 2019 12:31:46 -0800 Subject: environment: Add a special error case for getting GNU link.exe Since I spent three days banging my head against this it seems reasonable that other people might also run into this problem. It can happen if you're trying to use microsoft's link.exe, but also have the dmd bin directory at the tail of your %PATH%, among other reasons. --- mesonbuild/environment.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 83c6935..4c07e58 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -781,6 +781,11 @@ class Environment: for_machine, [], machine=target, exelist=compiler, prefix=comp_class.LINKER_PREFIX if use_linker_prefix else [], version=search_version(out)) + elif 'GNU coreutils' in o: + raise EnvironmentException( + "Found GNU link.exe instead of MSVC link.exe. This link.exe " + "is not a linker. You may need to reorder entries to your " + "%PATH% variable to resolve this.") raise EnvironmentException('Unable to determine dynamic linker') def _guess_nix_linker(self, compiler: typing.List[str], comp_class: typing.Type[Compiler], -- cgit v1.1 From 2450cd6a9fdc51895dcce461312768a485959b7e Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 26 Nov 2019 13:49:33 -0800 Subject: azure: print where link.exe is coming from too --- ci/azure-steps.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ci/azure-steps.yml b/ci/azure-steps.yml index 2376659..e33f152 100644 --- a/ci/azure-steps.yml +++ b/ci/azure-steps.yml @@ -166,9 +166,10 @@ steps: echo "" - echo "Locating cl, rc:" + echo "Locating cl, rc, link:" where.exe cl where.exe rc + where.exe link echo "" echo "=== Start running tests ===" -- cgit v1.1 From 71e3fac5af1027f88a48c0beb78c3994cea57629 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 2 Dec 2019 13:45:28 -0800 Subject: run_unittests: remove unused PatchModule class --- run_unittests.py | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/run_unittests.py b/run_unittests.py index bf67729..74e5803 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -290,26 +290,6 @@ def no_pkgconfig(): shutil.which = old_which ExternalProgram._search = old_search -class PatchModule: - ''' - Fancy monkey-patching! Whee! Can't use mock.patch because it only - patches in the local namespace. - ''' - - def __init__(self, func, name, impl): - self.func = func - assert(isinstance(name, str)) - self.func_name = name - self.old_impl = None - self.new_impl = impl - - def __enter__(self): - self.old_impl = self.func - exec('{} = self.new_impl'.format(self.func_name)) - - def __exit__(self, *args): - exec('{} = self.old_impl'.format(self.func_name)) - class InternalTests(unittest.TestCase): @@ -5743,6 +5723,7 @@ c = ['{0}'] self.build() self.run_tests() + def should_run_cross_arm_tests(): return shutil.which('arm-linux-gnueabihf-gcc') and not platform.machine().lower().startswith('arm') -- cgit v1.1 From 8091b4c7448ca467bbfff22283beaa702533f177 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 2 Dec 2019 15:00:16 -0800 Subject: Intel: Dump worthless Xild abstractions This dumps xild on mac and linux. After a lot of reading and banging my head I've discovered we (meson) don't care about xild, xild is only useful if your invoke ld directly (not through icc/icpc) and you want to do ipo/lto/wpo. Instead just make icc report what it's actually doing, invoking ld or ld64 (for linux and mac respectively) directly. This allows us to get -fuse-ld working on linux. --- mesonbuild/environment.py | 13 ++++++------- mesonbuild/linkers.py | 29 +++++++---------------------- run_unittests.py | 4 ++-- 3 files changed, 15 insertions(+), 31 deletions(-) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 4c07e58..f3f5e9e 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -53,8 +53,6 @@ from .linkers import ( PGIDynamicLinker, PGIStaticLinker, SolarisDynamicLinker, - XildAppleDynamicLinker, - XildLinuxDynamicLinker, XilinkDynamicLinker, CudaLinker, ) @@ -1046,9 +1044,9 @@ class Environment: if '(ICC)' in out: cls = IntelCCompiler if lang == 'c' else IntelCPPCompiler if self.machines[for_machine].is_darwin(): - l = XildAppleDynamicLinker(compiler, for_machine, 'xild', cls.LINKER_PREFIX, [], version=version) + l = AppleDynamicLinker(compiler, for_machine, 'APPLE ld', cls.LINKER_PREFIX, [], version=version) else: - l = XildLinuxDynamicLinker(compiler, for_machine, 'xild', cls.LINKER_PREFIX, [], version=version) + l = self._guess_nix_linker(compiler, cls, for_machine) return cls( ccache + compiler, version, for_machine, is_cross, info, exe_wrap, full_version=full_version, linker=l) @@ -1176,8 +1174,7 @@ class Environment: info, exe_wrap, linker=linker) if 'ifort (IFORT)' in out: - linker = XildLinuxDynamicLinker( - compiler, for_machine, 'xild', IntelFortranCompiler.LINKER_PREFIX, [], version=version) + linker = self._guess_nix_linker(compiler, IntelFortranCompiler, for_machine) return IntelFortranCompiler( compiler, version, for_machine, is_cross, info, exe_wrap, full_version=full_version, linker=linker) @@ -1376,8 +1373,10 @@ class Environment: # This trickery with type() gets us the class of the linker # so we can initialize a new copy for the Rust Compiler + linker = type(cc.linker)(compiler, for_machine, cc.linker.id, cc.LINKER_PREFIX, - always_args=extra_args, version=cc.linker.version) + always_args=extra_args, version=cc.linker.version, + **extra_args) return RustCompiler( compiler, version, for_machine, is_cross, info, exe_wrap, diff --git a/mesonbuild/linkers.py b/mesonbuild/linkers.py index 021134e..1c0dfff 100644 --- a/mesonbuild/linkers.py +++ b/mesonbuild/linkers.py @@ -399,6 +399,11 @@ class DynamicLinker(metaclass=abc.ABCMeta): install_rpath: str) -> 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 PosixDynamicLinkerMixin: @@ -423,8 +428,8 @@ 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. + This acts as a base for the GNU linkers (bfd and gold), LLVM's lld, and + other linkers like GNU-ld. """ _BUILDTYPE_ARGS = { @@ -660,26 +665,6 @@ class LLVMDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, Dyna 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.""" diff --git a/run_unittests.py b/run_unittests.py index 74e5803..bf91613 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -2299,11 +2299,11 @@ class AllPlatformTests(BasePlatformTests): if isinstance(cc, intel): self.assertIsInstance(linker, ar) if is_osx(): - self.assertIsInstance(cc.linker, mesonbuild.linkers.XildAppleDynamicLinker) + self.assertIsInstance(cc.linker, mesonbuild.linkers.AppleDynamicLinker) elif is_windows(): self.assertIsInstance(cc.linker, mesonbuild.linkers.XilinkDynamicLinker) else: - self.assertIsInstance(cc.linker, mesonbuild.linkers.XildLinuxDynamicLinker) + self.assertIsInstance(cc.linker, mesonbuild.linkers.GnuDynamicLinker) if isinstance(cc, msvc): self.assertTrue(is_windows()) self.assertIsInstance(linker, lib) -- cgit v1.1 From 75c1874bb3adbe96ba2a6cab64d471e27ca712c7 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 2 Dec 2019 15:05:37 -0800 Subject: run_unittests: Add unittests for ld overriding --- run_unittests.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/run_unittests.py b/run_unittests.py index bf91613..feb3ee8 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -5723,6 +5723,43 @@ c = ['{0}'] self.build() self.run_tests() + def _check_ld(self, check: str, name: str, lang: str, expected: str) -> None: + if is_sunos(): + raise unittest.SkipTest('Solaris currently cannot override the linker.') + if not shutil.which(check): + raise unittest.SkipTest('Could not find {}.'.format(check)) + with mock.patch.dict(os.environ, {'LD': name}): + env = get_fake_env() + comp = getattr(env, 'detect_{}_compiler'.format(lang))(MachineChoice.HOST) + if lang != 'rust' and comp.use_linker_args('foo') == []: + raise unittest.SkipTest( + 'Compiler {} does not support using alternative linkers'.format(comp.id)) + self.assertEqual(comp.linker.id, expected) + + def test_ld_environment_variable_bfd(self): + self._check_ld('ld.bfd', 'bfd', 'c', 'GNU ld.bfd') + + def test_ld_environment_variable_gold(self): + self._check_ld('ld.gold', 'gold', 'c', 'GNU ld.gold') + + def test_ld_environment_variable_lld(self): + self._check_ld('ld.lld', 'lld', 'c', 'lld') + + def test_ld_environment_variable_rust(self): + self._check_ld('ld.gold', 'gold', 'rust', 'GNU ld.gold') + + def test_ld_environment_variable_cpp(self): + self._check_ld('ld.gold', 'gold', 'cpp', 'GNU ld.gold') + + def test_ld_environment_variable_objc(self): + self._check_ld('ld.gold', 'gold', 'objc', 'GNU ld.gold') + + def test_ld_environment_variable_objcpp(self): + self._check_ld('ld.gold', 'gold', 'objcpp', 'GNU ld.gold') + + def test_ld_environment_variable_fortran(self): + self._check_ld('ld.gold', 'gold', 'fortran', 'GNU ld.gold') + def should_run_cross_arm_tests(): return shutil.which('arm-linux-gnueabihf-gcc') and not platform.machine().lower().startswith('arm') -- cgit v1.1 From 39efd977141e9e4fb923d631a7006c130d5ae769 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 2 Dec 2019 15:22:59 -0800 Subject: run_project_tests: Fix skip rust which expects an argument of backend, but isn't getting one. --- run_project_tests.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/run_project_tests.py b/run_project_tests.py index dbda839..0784d34 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -616,14 +616,13 @@ def has_broken_rustc() -> bool: mesonlib.windows_proof_rmtree(dirname) return pc.returncode != 0 -def should_skip_rust() -> bool: +def should_skip_rust(backend: Backend) -> bool: if not shutil.which('rustc'): return True if backend is not Backend.ninja: return True - if mesonlib.is_windows(): - if has_broken_rustc(): - return True + if mesonlib.is_windows() and has_broken_rustc(): + return True return False def detect_tests_to_run(only: typing.List[str]) -> typing.List[typing.Tuple[str, typing.List[Path], bool]]: @@ -659,7 +658,7 @@ def detect_tests_to_run(only: typing.List[str]) -> typing.List[typing.Tuple[str, ('java', 'java', backend is not Backend.ninja or mesonlib.is_osx() or not have_java()), ('C#', 'csharp', skip_csharp(backend)), ('vala', 'vala', backend is not Backend.ninja or not shutil.which(os.environ.get('VALAC', 'valac'))), - ('rust', 'rust', should_skip_rust()), + ('rust', 'rust', should_skip_rust(backend)), ('d', 'd', backend is not Backend.ninja or not have_d_compiler()), ('objective c', 'objc', backend not in (Backend.ninja, Backend.xcode) or not have_objc_compiler()), ('objective c++', 'objcpp', backend not in (Backend.ninja, Backend.xcode) or not have_objcpp_compiler()), -- cgit v1.1 From a1bfd22120c219a9190559b1fdc4ba29d8e59783 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 3 Dec 2019 11:25:26 -0800 Subject: linkers: Correct MSVC-like linkers invoke directly flag This is returning the inverse of the correct value, which happens to work out because in general the compilers that a link.exe-like linker is paired with accepts the same arguments. --- mesonbuild/linkers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/linkers.py b/mesonbuild/linkers.py index 1c0dfff..09513e0 100644 --- a/mesonbuild/linkers.py +++ b/mesonbuild/linkers.py @@ -791,7 +791,7 @@ class VisualStudioLikeLinkerMixin: self.machine = machine def invoked_by_compiler(self) -> bool: - return self.direct + return not self.direct def get_debug_crt_args(self) -> typing.List[str]: """Arguments needed to select a debug crt for the linker. -- cgit v1.1 From d8561180061f271fde9a5d782853930d59280c06 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 3 Dec 2019 11:59:49 -0800 Subject: environment: Fix selecting the linker with rustc --- mesonbuild/backend/ninjabackend.py | 9 +++++- mesonbuild/environment.py | 65 ++++++++++++++++++++++---------------- 2 files changed, 45 insertions(+), 29 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 9ff6f74..5630212 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1293,8 +1293,15 @@ int dummy; else: raise InvalidArguments('Unknown target type for rustc.') args.append(cratetype) + + # If we're dynamically linking, add those arguments + # + # Rust is super annoying, calling -C link-arg foo does not work, it has + # to be -C link-arg=foo if cratetype in {'bin', 'dylib'}: - args += rustc.linker.get_always_args() + for a in rustc.linker.get_always_args(): + args += ['-C', 'link-arg={}'.format(a)] + args += ['--crate-name', target.name] args += rustc.get_buildtype_args(self.get_option_for_target('buildtype', target)) args += rustc.get_debug_args(self.get_option_for_target('debug', target)) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index f3f5e9e..d4c1498 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -55,6 +55,7 @@ from .linkers import ( SolarisDynamicLinker, XilinkDynamicLinker, CudaLinker, + VisualStudioLikeLinkerMixin, ) from functools import lru_cache from .compilers import ( @@ -1333,6 +1334,10 @@ class Environment: compilers, ccache, exe_wrap = self._get_compilers('rust', for_machine) is_cross = not self.machines.matches_build_machine(for_machine) info = self.machines[for_machine] + + cc = self.detect_c_compiler(for_machine) + is_link_exe = isinstance(cc.linker, VisualStudioLikeLinkerMixin) + for compiler in compilers: if isinstance(compiler, str): compiler = [compiler] @@ -1346,36 +1351,40 @@ class Environment: version = search_version(out) if 'rustc' in out: - if info.is_windows() or info.is_cygwin(): - # On windows rustc invokes link.exe - linker = self._guess_win_linker(['link'], for_machine) + # On Linux and mac rustc will invoke gcc (clang for mac + # presumably) and it can do this windows, for dynamic linking. + # this means the easiest way to C compiler for dynamic linking. + # figure out what linker to use is to just get the value of the + # C compiler and use that as the basis of the rust linker. + # However, there are two things we need to change, if CC is not + # the default use that, and second add the necessary arguments + # to rust to use -fuse-ld + + extra_args = {} + always_args = [] + if is_link_exe: + compiler.extend(['-C', 'linker={}'.format(cc.linker.exelist[0])]) + extra_args['direct'] = True + extra_args['machine'] = cc.linker.machine + elif not ((info.is_darwin() and isinstance(cc, AppleClangCCompiler)) or + isinstance(cc, GnuCCompiler)): + c = cc.exelist[1] if cc.exelist[0].endswith('ccache') else cc.exelist[0] + compiler.extend(['-C', 'linker={}'.format(c)]) + + value = self.binaries[for_machine].lookup_entry('ld') + if value is not None: + for a in cc.use_linker_args(value[0]): + always_args.extend(['-C', 'link-arg={}'.format(a)]) + + # This trickery with type() gets us the class of the linker + # so we can initialize a new copy for the Rust Compiler + + if is_link_exe: + linker = type(cc.linker)(for_machine, always_args, exelist=cc.linker.exelist, + version=cc.linker.version, **extra_args) else: - # On Linux and mac rustc will invoke gcc (clang for mac - # presumably), for dynamic linking. this means the easiest - # way to C compiler for dynamic linking. figure out what - # linker to use !windows it to just get the value of the C - # compiler and use that as the basis of the rust linker. - # However, there are two things we need to change, if CC is - # not the default use that, and second add the necessary - # arguments to rust to use -fuse-ld - cc = self.detect_c_compiler(for_machine) - - extra_args = [] - if not ((info.is_darwin() and isinstance(cc, AppleClangCCompiler)) or - isinstance(cc, GnuCCompiler)): - c = cc.exelist[1] if cc.exelist[0].endswith('ccache') else cc.exelist[0] - extra_args.extend(['-C', 'linker={}'.format(c)]) - - value = self.binaries[for_machine].lookup_entry('ld') - if value is not None: - for a in cc.use_linker_args(value[0]): - extra_args.extend(['-C', 'link-arg={}'.format(a)]) - - # This trickery with type() gets us the class of the linker - # so we can initialize a new copy for the Rust Compiler - linker = type(cc.linker)(compiler, for_machine, cc.linker.id, cc.LINKER_PREFIX, - always_args=extra_args, version=cc.linker.version, + always_args=always_args, version=cc.linker.version, **extra_args) return RustCompiler( -- cgit v1.1 From afe00f42177f865a7f1781c0ccac280b7cf0d746 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 3 Dec 2019 12:00:22 -0800 Subject: linkers: make constructor signature of VisualStudioLike linkers the same --- mesonbuild/linkers.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/mesonbuild/linkers.py b/mesonbuild/linkers.py index 09513e0..d69f688 100644 --- a/mesonbuild/linkers.py +++ b/mesonbuild/linkers.py @@ -845,9 +845,10 @@ class MSVCDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): def __init__(self, for_machine: mesonlib.MachineChoice, always_args: typing.List[str], *, exelist: typing.Optional[typing.List[str]] = None, prefix: typing.Union[str, typing.List[str]] = '', - machine: str = 'x86', version: str = 'unknown version'): + machine: str = 'x86', version: str = 'unknown version', + direct: bool = True): super().__init__(exelist or ['link.exe'], for_machine, 'link', - prefix, always_args, machine=machine, version=version) + prefix, always_args, machine=machine, version=version, direct=direct) class ClangClDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): @@ -857,9 +858,10 @@ class ClangClDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): def __init__(self, for_machine: mesonlib.MachineChoice, always_args: typing.List[str], *, exelist: typing.Optional[typing.List[str]] = None, prefix: typing.Union[str, typing.List[str]] = '', - version: str = 'unknown version'): + machine: str = 'x86', version: str = 'unknown version', + direct: bool = True): super().__init__(exelist or ['lld-link.exe'], for_machine, 'lld-link', - prefix, always_args, version=version) + prefix, always_args, machine=machine, version=version, direct=direct) class XilinkDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): -- cgit v1.1 From f8aa17d8e62e05ed264a00a5e948b3e42aa68b30 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 3 Dec 2019 12:08:39 -0800 Subject: run_unittests: Add tests for LD on windows --- run_unittests.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/run_unittests.py b/run_unittests.py index feb3ee8..dcc4dec 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -4496,6 +4496,29 @@ class WindowsTests(BasePlatformTests): self.assertTrue('prog.pdb' in files) + def _check_ld(self, name: str, lang: str, expected: str) -> None: + if not shutil.which(name): + raise unittest.SkipTest('Could not find {}.'.format(name)) + with mock.patch.dict(os.environ, {'LD': name}): + env = get_fake_env() + try: + comp = getattr(env, 'detect_{}_compiler'.format(lang))(MachineChoice.HOST) + except EnvironmentException: + raise unittest.SkipTest('Could not find a compiler for {}'.format(lang)) + self.assertEqual(comp.linker.id, expected) + + def test_link_environment_variable_lld_link(self): + self._check_ld('lld-link', 'c', 'lld-link') + + def test_link_environment_variable_link(self): + self._check_ld('link', 'c', 'link') + + def test_link_environment_variable_optlink(self): + self._check_ld('optlink', 'c', 'optlink') + + def test_link_environment_variable_rust(self): + self._check_ld('link', 'rust', 'link') + @unittest.skipUnless(is_osx(), "requires Darwin") class DarwinTests(BasePlatformTests): ''' -- cgit v1.1