diff options
author | Jussi Pakkanen <jpakkane@gmail.com> | 2017-05-04 23:21:40 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-05-04 23:21:40 +0200 |
commit | 11968382a3800f7dd6d04b3a94d4351f0950dd9a (patch) | |
tree | 3f77f862dd1605173630074f674ec38cd0959b43 | |
parent | a576791064b92bfe44c958648eee64f55e87b23d (diff) | |
parent | 4bee51655bea9c8bebd3c55414d7daf13591fb59 (diff) | |
download | meson-11968382a3800f7dd6d04b3a94d4351f0950dd9a.zip meson-11968382a3800f7dd6d04b3a94d4351f0950dd9a.tar.gz meson-11968382a3800f7dd6d04b3a94d4351f0950dd9a.tar.bz2 |
Merge pull request #1621 from dcbaker/llvm-dep
RFC: Add dependency for LLVM. Fixes #1611
-rw-r--r-- | docs/markdown/Release-notes-for-0.41.0.md | 4 | ||||
-rw-r--r-- | mesonbuild/build.py | 9 | ||||
-rw-r--r-- | mesonbuild/dependencies.py | 132 | ||||
-rw-r--r-- | test cases/frameworks/15 llvm/meson.build | 10 | ||||
-rw-r--r-- | test cases/frameworks/15 llvm/sum.c | 76 |
5 files changed, 230 insertions, 1 deletions
diff --git a/docs/markdown/Release-notes-for-0.41.0.md b/docs/markdown/Release-notes-for-0.41.0.md index a3ef384..6e00ecd 100644 --- a/docs/markdown/Release-notes-for-0.41.0.md +++ b/docs/markdown/Release-notes-for-0.41.0.md @@ -8,3 +8,7 @@ short-description: Release notes for 0.41 (preliminary) # New features Add features here as code is merged to master. + +## Dependency Handler for LLVM + +Native support for linking against LLVM using the `dependency` function. diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 7dc0bc7..06e6156 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -908,7 +908,14 @@ You probably should put it in link_with instead.''') # Pick a compiler based on the language priority-order for l in clike_langs: if l in self.compilers or l in dep_langs: - return all_compilers[l] + try: + return all_compilers[l] + except KeyError: + raise MesonException( + 'Could not get a dynamic linker for build target {!r}. ' + 'Requires a linker for language "{}", but that is not ' + 'a project language.'.format(self.name, l)) + m = 'Could not get a dynamic linker for build target {!r}' raise AssertionError(m.format(self.name)) diff --git a/mesonbuild/dependencies.py b/mesonbuild/dependencies.py index c512bf3..ef7be3a 100644 --- a/mesonbuild/dependencies.py +++ b/mesonbuild/dependencies.py @@ -22,6 +22,7 @@ import re import sys import os, stat, glob, shutil +import shlex import subprocess import sysconfig from enum import Enum @@ -356,13 +357,16 @@ class WxDependency(Dependency): def __init__(self, environment, kwargs): Dependency.__init__(self, 'wx', kwargs) self.is_found = False + # FIXME: use version instead of modversion self.modversion = 'none' if WxDependency.wx_found is None: self.check_wxconfig() if not WxDependency.wx_found: + # FIXME: this message could be printed after Dependncy found mlog.log("Neither wx-config-3.0 nor wx-config found; can't detect dependency") return + # FIXME: This should print stdout and stderr using mlog.debug p, out = Popen_safe([self.wxc, '--version'])[0:2] if p.returncode != 0: mlog.log('Dependency wxwidgets found:', mlog.red('NO')) @@ -382,10 +386,12 @@ class WxDependency(Dependency): # wx-config seems to have a cflags as well but since it requires C++, # this should be good, at least for now. p, out = Popen_safe([self.wxc, '--cxxflags'])[0:2] + # FIXME: this error should only be raised if required is true if p.returncode != 0: raise DependencyException('Could not generate cargs for wxwidgets.') self.cargs = out.split() + # FIXME: this error should only be raised if required is true p, out = Popen_safe([self.wxc, '--libs'] + self.requested_modules)[0:2] if p.returncode != 0: raise DependencyException('Could not generate libs for wxwidgets.') @@ -1607,6 +1613,131 @@ class ValgrindDependency(PkgConfigDependency): def get_link_args(self): return [] +class LLVMDependency(Dependency): + """LLVM dependency. + + LLVM uses a special tool, llvm-config, which has arguments for getting + c args, cxx args, and ldargs as well as version. + """ + + # Ordered list of llvm-config binaries to try. Start with base, then try + # newest back to oldest (3.5 is abitrary), and finally the devel version. + llvm_config_bins = [ + 'llvm-config', 'llvm-config-4.0', 'llvm-config-3.9', 'llvm-config39', + 'llvm-config-3.8', 'llvm-config38', 'llvm-config-3.7', 'llvm-config37', + 'llvm-config-3.6', 'llvm-config36', 'llvm-config-3.5', 'llvm-config35', + 'llvm-config-devel', + ] + llvmconfig = None + _llvmconfig_found = False + __best_found = None + + def __init__(self, environment, kwargs): + super().__init__('llvm-config', kwargs) + # 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. + self.language = 'cpp' + self.cargs = [] + self.libs = [] + self.modules = [] + + required = kwargs.get('required', True) + req_version = kwargs.get('version', None) + if self.llvmconfig is None: + self.check_llvmconfig(req_version) + if not self._llvmconfig_found: + if self.__best_found is not None: + mlog.log('found {!r} but need:'.format(self.version), + req_version) + else: + mlog.log("No llvm-config found; can't detect dependency") + mlog.log('Dependency LLVM found:', mlog.red('NO')) + if required: + raise DependencyException('Dependency LLVM not found') + return + + p, out, err = Popen_safe([self.llvmconfig, '--version']) + if p.returncode != 0: + mlog.debug('stdout: {}\nstderr: {}'.format(out, err)) + if required: + raise DependencyException('Dependency LLVM not found') + return + else: + self.version = out.strip() + mlog.log('Dependency LLVM found:', mlog.green('YES')) + self.is_found = True + + p, out = Popen_safe( + [self.llvmconfig, '--libs', '--ldflags', '--system-libs'])[:2] + if p.returncode != 0: + raise DependencyException('Could not generate libs for LLVM.') + self.libs = shlex.split(out) + + p, out = Popen_safe([self.llvmconfig, '--cppflags'])[:2] + if p.returncode != 0: + raise DependencyException('Could not generate includedir for LLVM.') + self.cargs = shlex.split(out) + + p, out = Popen_safe([self.llvmconfig, '--components'])[:2] + if p.returncode != 0: + raise DependencyException('Could not generate modules for LLVM.') + self.modules = shlex.split(out) + + modules = mesonlib.stringlistify(kwargs.get('modules', [])) + for mod in modules: + if mod not in self.modules: + mlog.log('LLVM module', mod, 'found:', mlog.red('NO')) + self.is_found = False + if required: + raise DependencyException( + 'Could not find required LLVM Component: {}'.format(mod)) + else: + mlog.log('LLVM module', mod, 'found:', mlog.green('YES')) + + def get_version(self): + return self.version + + def get_compile_args(self): + return self.cargs + + def get_link_args(self): + return self.libs + + @classmethod + def check_llvmconfig(cls, version_req): + """Try to find the highest version of llvm-config.""" + for llvmconfig in cls.llvm_config_bins: + try: + p, out = Popen_safe([llvmconfig, '--version'])[0:2] + out = out.strip() + if p.returncode != 0: + continue + if version_req: + if version_compare(out, version_req, strict=True): + if cls.__best_found and version_compare(out, '<={}'.format(cls.__best_found), strict=True): + continue + cls.__best_found = out + cls.llvmconfig = llvmconfig + else: + # If no specific version is requested use the first version + # found, since that should be the best. + cls.__best_found = out + cls.llvmconfig = llvmconfig + break + except (FileNotFoundError, PermissionError): + pass + if cls.__best_found: + mlog.log('Found llvm-config:', + mlog.bold(shutil.which(cls.llvmconfig)), + '({})'.format(out.strip())) + cls._llvmconfig_found = True + else: + cls.llvmconfig = False + + def need_threads(self): + return True + + def get_dep_identifier(name, kwargs): elements = [name] modlist = kwargs.get('modules', []) @@ -1671,4 +1802,5 @@ packages = {'boost': BoostDependency, 'threads': ThreadDependency, 'python3': Python3Dependency, 'valgrind': ValgrindDependency, + 'llvm': LLVMDependency, } diff --git a/test cases/frameworks/15 llvm/meson.build b/test cases/frameworks/15 llvm/meson.build new file mode 100644 index 0000000..582ff37 --- /dev/null +++ b/test cases/frameworks/15 llvm/meson.build @@ -0,0 +1,10 @@ +project('llvmtest', ['c', 'cpp'], default_options : ['c_std=c99']) + +llvm_dep = dependency( + 'llvm', + modules : ['bitwriter', 'asmprinter', 'executionengine', 'target', + 'mcjit', 'nativecodegen'], + required : true, +) + +executable('sum', 'sum.c', dependencies : llvm_dep) diff --git a/test cases/frameworks/15 llvm/sum.c b/test cases/frameworks/15 llvm/sum.c new file mode 100644 index 0000000..a93588e --- /dev/null +++ b/test cases/frameworks/15 llvm/sum.c @@ -0,0 +1,76 @@ +/** This code is public domain, and taken from + * https://github.com/paulsmith/getting-started-llvm-c-api/blob/master/sum.c + */ +/** + * LLVM equivalent of: + * + * int sum(int a, int b) { + * return a + b; + * } + */ + +#include <llvm-c/Core.h> +#include <llvm-c/ExecutionEngine.h> +#include <llvm-c/Target.h> +#include <llvm-c/Analysis.h> +#include <llvm-c/BitWriter.h> + +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> + +int main(int argc, char const *argv[]) { + LLVMModuleRef mod = LLVMModuleCreateWithName("my_module"); + + LLVMTypeRef param_types[] = { LLVMInt32Type(), LLVMInt32Type() }; + LLVMTypeRef ret_type = LLVMFunctionType(LLVMInt32Type(), param_types, 2, 0); + LLVMValueRef sum = LLVMAddFunction(mod, "sum", ret_type); + + LLVMBasicBlockRef entry = LLVMAppendBasicBlock(sum, "entry"); + + LLVMBuilderRef builder = LLVMCreateBuilder(); + LLVMPositionBuilderAtEnd(builder, entry); + LLVMValueRef tmp = LLVMBuildAdd(builder, LLVMGetParam(sum, 0), LLVMGetParam(sum, 1), "tmp"); + LLVMBuildRet(builder, tmp); + + char *error = NULL; + LLVMVerifyModule(mod, LLVMAbortProcessAction, &error); + LLVMDisposeMessage(error); + + LLVMExecutionEngineRef engine; + error = NULL; + LLVMLinkInMCJIT(); + LLVMInitializeNativeAsmPrinter(); + LLVMInitializeNativeTarget(); + if (LLVMCreateExecutionEngineForModule(&engine, mod, &error) != 0) { + fprintf(stderr, "failed to create execution engine\n"); + abort(); + } + if (error) { + fprintf(stderr, "error: %s\n", error); + LLVMDisposeMessage(error); + exit(EXIT_FAILURE); + } + + if (argc < 3) { + fprintf(stderr, "usage: %s x y\n", argv[0]); + exit(EXIT_FAILURE); + } + long long x = strtoll(argv[1], NULL, 10); + long long y = strtoll(argv[2], NULL, 10); + + LLVMGenericValueRef args[] = { + LLVMCreateGenericValueOfInt(LLVMInt32Type(), x, 0), + LLVMCreateGenericValueOfInt(LLVMInt32Type(), y, 0) + }; + LLVMGenericValueRef res = LLVMRunFunction(engine, sum, 2, args); + printf("%d\n", (int)LLVMGenericValueToInt(res, 0)); + + // Write out bitcode to file + if (LLVMWriteBitcodeToFile(mod, "sum.bc") != 0) { + fprintf(stderr, "error writing bitcode to file, skipping\n"); + } + + LLVMDisposeBuilder(builder); + LLVMDisposeExecutionEngine(engine); +} |