diff options
-rw-r--r-- | docs/markdown/snippets/llvm-static-linking.md | 8 | ||||
-rw-r--r-- | mesonbuild/dependencies/dev.py | 92 | ||||
-rw-r--r-- | test cases/frameworks/15 llvm/meson.build | 36 |
3 files changed, 108 insertions, 28 deletions
diff --git a/docs/markdown/snippets/llvm-static-linking.md b/docs/markdown/snippets/llvm-static-linking.md new file mode 100644 index 0000000..bb72a56 --- /dev/null +++ b/docs/markdown/snippets/llvm-static-linking.md @@ -0,0 +1,8 @@ +# LLVM dependency supports both dynamic and static linking + +The LLVM dependency has been improved to consistently use dynamic linking. +Previously recent version (>= 3.9) would link dynamically while older versions +would link statically. + +Now LLVM also accepts the `static` keyword to enable statically linking to LLVM +modules instead of dynamically linking. diff --git a/mesonbuild/dependencies/dev.py b/mesonbuild/dependencies/dev.py index 308ae55..2ab2847 100644 --- a/mesonbuild/dependencies/dev.py +++ b/mesonbuild/dependencies/dev.py @@ -16,6 +16,7 @@ # development purposes, such as testing, debugging, etc.. import os +import re import shlex import shutil @@ -136,8 +137,10 @@ class LLVMDependency(ExternalDependency): # It's necessary for LLVM <= 3.8 to use the C++ linker. For 3.9 and 4.0 # the C linker works fine if only using the C API. super().__init__('llvm-config', environment, 'cpp', kwargs) - self.modules = [] + self.provided_modules = [] + self.required_modules = set() self.llvmconfig = None + self.static = kwargs.get('static', False) self.__best_found = None # FIXME: Support multiple version requirements ala PkgConfigDependency req_version = kwargs.get('version', None) @@ -169,31 +172,90 @@ class LLVMDependency(ExternalDependency): # for users who want the patch version. self.version = out.strip().rstrip('svn') - p, out = Popen_safe( - [self.llvmconfig, '--libs', '--ldflags'])[:2] + p, out = Popen_safe([self.llvmconfig, '--components'])[:2] if p.returncode != 0: - raise DependencyException('Could not generate libs for LLVM.') - self.link_args = strip_system_libdirs(environment, shlex.split(out)) + raise DependencyException('Could not generate modules for LLVM.') + self.provided_modules = shlex.split(out) + + modules = stringlistify(extract_as_list(kwargs, 'modules')) + self.check_components(modules) + opt_modules = stringlistify(extract_as_list(kwargs, 'optional_modules')) + self.check_components(opt_modules, required=False) + p, out = Popen_safe([self.llvmconfig, '--cppflags'])[:2] if p.returncode != 0: raise DependencyException('Could not generate includedir for LLVM.') cargs = mesonlib.OrderedSet(shlex.split(out)) self.compile_args = list(cargs.difference(self.__cpp_blacklist)) - p, out = Popen_safe([self.llvmconfig, '--components'])[:2] + if version_compare(self.version, '>= 3.9'): + self._set_new_link_args() + else: + self._set_old_link_args() + self.link_args = strip_system_libdirs(environment, self.link_args) + + def _set_new_link_args(self): + """How to set linker args for LLVM versions >= 3.9""" + link_args = ['--link-static', '--system-libs'] if self.static else ['--link-shared'] + p, out = Popen_safe( + [self.llvmconfig, '--libs', '--ldflags'] + link_args + list(self.required_modules))[:2] if p.returncode != 0: - raise DependencyException('Could not generate modules for LLVM.') - self.modules = shlex.split(out) + raise DependencyException('Could not generate libs for LLVM.') + self.link_args = shlex.split(out) - modules = stringlistify(extract_as_list(kwargs, 'modules')) + def _set_old_link_args(self): + """Setting linker args for older versions of llvm. + + Old versions of LLVM bring an extra level of insanity with them. + llvm-config will provide the correct arguments for static linking, but + not for shared-linnking, we have to figure those out ourselves, because + of course we do. + """ + if self.static: + p, out = Popen_safe( + [self.llvmconfig, '--libs', '--ldflags', '--system-libs'] + list(self.required_modules))[:2] + if p.returncode != 0: + raise DependencyException('Could not generate libs for LLVM.') + self.link_args = shlex.split(out) + else: + # llvm-config will provide arguments for static linking, so we get + # to figure out for ourselves what to link with. We'll do that by + # checking in the directory provided by --libdir for a library + # called libLLVM-<ver>.(so|dylib|dll) + p, out = Popen_safe([self.llvmconfig, '--libdir'])[:2] + if p.returncode != 0: + raise DependencyException('Could not generate libs for LLVM.') + libdir = out.strip() + + expected_name = 'libLLVM-{}'.format(self.version) + re_name = re.compile(r'{}.(so|dll|dylib)'.format(expected_name)) + + for file_ in os.listdir(libdir): + if re_name.match(file_): + self.link_args = ['-L{}'.format(libdir), + '-l{}'.format(os.path.splitext(file_.lstrip('lib'))[0])] + break + else: + raise DependencyException( + 'Could not find a dynamically linkable library for LLVM.') + + def check_components(self, modules, required=True): + """Check for llvm components (modules in meson terms). + + The required option is whether the module is required, not whether LLVM + is required. + """ for mod in sorted(set(modules)): - if mod not in self.modules: - mlog.log('LLVM module', mod, 'found:', mlog.red('NO')) - self.is_found = False - if self.required: - raise DependencyException( - 'Could not find required LLVM Component: {}'.format(mod)) + if mod not in self.provided_modules: + mlog.log('LLVM module', mod, 'found:', mlog.red('NO'), + '(optional)' if not required else '') + if required: + self.is_found = False + if self.required: + raise DependencyException( + 'Could not find required LLVM Component: {}'.format(mod)) else: + self.required_modules.add(mod) mlog.log('LLVM module', mod, 'found:', mlog.green('YES')) def check_llvmconfig(self, version_req): diff --git a/test cases/frameworks/15 llvm/meson.build b/test cases/frameworks/15 llvm/meson.build index 468094a..eb1b8d5 100644 --- a/test cases/frameworks/15 llvm/meson.build +++ b/test cases/frameworks/15 llvm/meson.build @@ -1,21 +1,31 @@ project('llvmtest', ['c', 'cpp'], default_options : ['c_std=c99']) -llvm_dep = dependency( - 'llvm', - modules : ['bitwriter', 'asmprinter', 'executionengine', 'target', - 'mcjit', 'nativecodegen'], - required : true, -) - d = dependency('llvm', modules : 'not-found', required : false) assert(d.found() == false, 'not-found llvm module found') d = dependency('llvm', version : '<0.1', required : false) assert(d.found() == false, 'ancient llvm module found') -executable('sum', 'sum.c', dependencies : [ - llvm_dep, - dependency('zlib'), - meson.get_compiler('c').find_library('dl', required : false), - dependency('tinfo'), - ]) +d = dependency('llvm', optional_modules : 'not-found', required : false) +assert(d.found() == true, 'optional module stopped llvm from being found.') + +foreach static : [true, false] + llvm_dep = dependency( + 'llvm', + modules : ['bitwriter', 'asmprinter', 'executionengine', 'target', + 'mcjit', 'nativecodegen'], + required : true, + static : static, + ) + name = static ? 'static' : 'dynamic' + executable( + 'sum-@0@'.format(name), + 'sum.c', + dependencies : [ + llvm_dep, + dependency('zlib'), + dependency('glib-2.0'), + meson.get_compiler('c').find_library('dl', required : false), + ] + ) +endforeach |