diff options
author | Dylan Baker <dylan@pnwbakers.com> | 2017-04-13 14:46:36 -0700 |
---|---|---|
committer | Dylan Baker <dylan@pnwbakers.com> | 2017-05-03 14:35:38 -0700 |
commit | 4bee51655bea9c8bebd3c55414d7daf13591fb59 (patch) | |
tree | 427a07b1c87dcfc7c9ccdead7b634ff23fa9a496 | |
parent | 4334c960624f2981e536a46697e429a43fc19f36 (diff) | |
download | meson-4bee51655bea9c8bebd3c55414d7daf13591fb59.zip meson-4bee51655bea9c8bebd3c55414d7daf13591fb59.tar.gz meson-4bee51655bea9c8bebd3c55414d7daf13591fb59.tar.bz2 |
Add dependency for LLVM. Fixes #1611
This adds a depdendncy wrapper for llvm-config based on the wxwidgets
dependency. IT handles libs, version, include dir, and the llvm unique
concept of components. These components are individual pieces of the
LLVM library that may or may not be available depending on the platform.
-rw-r--r-- | docs/markdown/Release-notes-for-0.41.0.md | 4 | ||||
-rw-r--r-- | mesonbuild/dependencies.py | 127 | ||||
-rw-r--r-- | test cases/frameworks/15 llvm/meson.build | 10 | ||||
-rw-r--r-- | test cases/frameworks/15 llvm/sum.c | 76 |
4 files changed, 217 insertions, 0 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/dependencies.py b/mesonbuild/dependencies.py index 86aa795..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 @@ -1612,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', []) @@ -1676,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); +} |