From 7c19bd55391935f546d20f86924103bd3d74d1ba Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 27 Sep 2018 13:19:27 -0700 Subject: [PATCH] dependencies/LLVM: rework shared link detection to be more robust Instead of trying to hardcode which versions of which OSes are misconfiguring llvm-config, lets try to use a generic mechanism that catches most of the broken cases. If a dynamic lib is built but the tools are linked staticly (LLVM_BUILD_LLVM_DYLIB=true and LLVM_LINK_LLVM_DYLIB=false) and you don't enabled shared libs (LLVM_BUILD_SHARED_LIBS=false, which should be false), then llvm-config will mistakenly return a each static library with .(so|dll|dylib) appended, instead of -lLLVM-x.y.z. if LLVM_LINK_LLVM_DYlIB is true this works fine. There's been a bug opened in upstream since 2014 about this with no responses[1]. So, this patches looks at --shared-mode (which tells us whether llvm-config was linked staticly or dynamicly), then if the shared-mode is static it will check and see what it gets. If it gets valid results then it will go with those, if it doesn't then it will try to build a set of valid link arguments to link with libLLVM.(so|dll|dylib). [1] https://bugs.llvm.org/show_bug.cgi?id=19937 --- mesonbuild/dependencies/dev.py | 83 ++++++++++++++++++++++++++++------ 1 file changed, 69 insertions(+), 14 deletions(-) diff --git a/mesonbuild/dependencies/dev.py b/mesonbuild/dependencies/dev.py index f20b9e04c..1e7c3e8a1 100644 --- a/mesonbuild/dependencies/dev.py +++ b/mesonbuild/dependencies/dev.py @@ -16,6 +16,7 @@ # development purposes, such as testing, debugging, etc.. import functools +import glob import os import re @@ -27,6 +28,17 @@ from .base import ( ) +def get_shared_library_suffix(environment, native): + """This is only gauranteed to work for languages that compile to machine + code, not for languages like C# that use a bytecode and always end in .dll + """ + if mesonlib.for_windows(native, environment): + return '.dll' + elif mesonlib.for_darwin(native, environment): + return '.dylib' + return '.so' + + class GTestDependency(ExternalDependency): def __init__(self, environment, kwargs): super().__init__('gtest', environment, 'cpp', kwargs) @@ -235,7 +247,7 @@ class LLVMDependency(ConfigToolDependency): self.compile_args = list(cargs.difference(self.__cpp_blacklist)) if version_compare(self.version, '>= 3.9'): - self._set_new_link_args() + self._set_new_link_args(environment) else: self._set_old_link_args() self.link_args = strip_system_libdirs(environment, self.link_args) @@ -258,20 +270,63 @@ class LLVMDependency(ConfigToolDependency): new_args.append(arg) return new_args - def _set_new_link_args(self): + def __check_libfiles(self, shared): + """Use llvm-config's --libfiles to check if libraries exist.""" + mode = '--link-shared' if shared else '--link-static' + + # Set self.required to true to force an exception in get_config_value + # if the returncode != 0 + restore = self.required + self.required = True + + try: + # It doesn't matter what the stage is, the caller needs to catch + # the exception anyway. + self.link_args = self.get_config_value(['--libfiles', mode], '') + finally: + self.required = restore + + def _set_new_link_args(self, environment): """How to set linker args for LLVM versions >= 3.9""" - if (not self.static and (mesonlib.is_osx() or - ((mesonlib.is_dragonflybsd() or mesonlib.is_freebsd()) and - version_compare(self.version, '>= 4.0')))): - # llvm-config on DragonFly BSD and FreeBSD for versions 4.0, 5.0, - # and 6.0 have an error when generating arguments for shared mode - # linking, even though libLLVM.so is installed, because llvm-config - # is misconfigured at build time. - # MacOS from brew has the same problem, except that it applies to - # all versions (3.9 - 7.0) - self.link_args = self.get_config_value(['--ldflags'], 'link_args') - self.link_args.append('-lLLVM') - return + mode = self.get_config_value(['--shared-mode'], 'link_args')[0] + if not self.static and mode == 'static': + # If llvm is configured with LLVM_BUILD_LLVM_DYLIB but not with + # LLVM_LINK_LLVM_DYLIB and not LLVM_BUILD_SHARED_LIBS (which + # upstreams doesn't recomend using), then llvm-config will lie to + # you about how to do shared-linking. It wants to link to a a bunch + # of individual shared libs (which don't exist because llvm wasn't + # built with LLVM_BUILD_SHARED_LIBS. + # + # Therefore, we'll try to get the libfiles, if the return code is 0 + # or we get an empty list, then we'll try to build a working + # configuration by hand. + try: + self.__check_libfiles(True) + except DependencyException: + lib_ext = get_shared_library_suffix(environment, self.native) + libdir = self.get_config_value(['--libdir'], 'link_args')[0] + # Sort for reproducability + matches = sorted(glob.iglob(os.path.join(libdir, 'libLLVM*{}'.format(lib_ext)))) + if not matches: + if self.required: + raise + return + + self.link_args = self.get_config_value(['--ldflags'], 'link_args') + libname = os.path.basename(matches[0]).rstrip(lib_ext).lstrip('lib') + self.link_args.append('-l{}'.format(libname)) + return + elif self.static and mode == 'shared': + # If, however LLVM_BUILD_SHARED_LIBS is true # (*cough* gentoo *cough*) + # then this is correct. Building with LLVM_BUILD_SHARED_LIBS has a side + # effect, it stops the generation of static archives. Therefore we need + # to check for that and error out on static if this is the case + try: + self.__check_libfiles(False) + except DependencyException: + if self.required: + raise + link_args = ['--link-static', '--system-libs'] if self.static else ['--link-shared'] self.link_args = self.get_config_value( ['--libs', '--ldflags'] + link_args + list(self.required_modules),