From ad79db8f0ac780896df29606655e8b82334e4e2b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 24 Apr 2017 11:19:04 -0700 Subject: Add some FIXME comments to wxwidgets dependency. --- mesonbuild/dependencies.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mesonbuild/dependencies.py b/mesonbuild/dependencies.py index c512bf3..86aa795 100644 --- a/mesonbuild/dependencies.py +++ b/mesonbuild/dependencies.py @@ -356,13 +356,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 +385,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.') -- cgit v1.1 From 4334c960624f2981e536a46697e429a43fc19f36 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 24 Apr 2017 16:56:36 -0700 Subject: Provide a helpful message when a language is required but not included This happens when building a C project with LLVM, which requires the C++ linker. --- mesonbuild/build.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index c8d692e..04fce80 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -909,7 +909,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)) -- cgit v1.1 From 4bee51655bea9c8bebd3c55414d7daf13591fb59 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 13 Apr 2017 14:46:36 -0700 Subject: 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. --- docs/markdown/Release-notes-for-0.41.0.md | 4 + mesonbuild/dependencies.py | 127 ++++++++++++++++++++++++++++++ test cases/frameworks/15 llvm/meson.build | 10 +++ test cases/frameworks/15 llvm/sum.c | 76 ++++++++++++++++++ 4 files changed, 217 insertions(+) create mode 100644 test cases/frameworks/15 llvm/meson.build create mode 100644 test cases/frameworks/15 llvm/sum.c 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 +#include +#include +#include +#include + +#include +#include +#include + +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); +} -- cgit v1.1