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
0.48
Dylan Baker 6 years ago committed by Nirbheek Chauhan
parent 008a87e19d
commit 7c19bd5539
  1. 83
      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),

Loading…
Cancel
Save