aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2017-05-04 23:21:40 +0200
committerGitHub <noreply@github.com>2017-05-04 23:21:40 +0200
commit11968382a3800f7dd6d04b3a94d4351f0950dd9a (patch)
tree3f77f862dd1605173630074f674ec38cd0959b43
parenta576791064b92bfe44c958648eee64f55e87b23d (diff)
parent4bee51655bea9c8bebd3c55414d7daf13591fb59 (diff)
downloadmeson-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.md4
-rw-r--r--mesonbuild/build.py9
-rw-r--r--mesonbuild/dependencies.py132
-rw-r--r--test cases/frameworks/15 llvm/meson.build10
-rw-r--r--test cases/frameworks/15 llvm/sum.c76
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);
+}