aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2016-12-07 21:49:16 +0200
committerGitHub <noreply@github.com>2016-12-07 21:49:16 +0200
commitf62f73082178644d25a480d93609e6a8866b5d74 (patch)
tree046780d0d2abf4e09e2c0f1a10e38268ff457246
parent344231d336339c0ea4c1eb072ef37ba5e15ff901 (diff)
parentf5a9b3b249efdafacbff1060999c60257c0ff72c (diff)
downloadmeson-f62f73082178644d25a480d93609e6a8866b5d74.zip
meson-f62f73082178644d25a480d93609e6a8866b5d74.tar.gz
meson-f62f73082178644d25a480d93609e6a8866b5d74.tar.bz2
Merge pull request #1126 from mesonbuild/sharedmodule
Support for shared modules
-rw-r--r--mesonbuild/backend/ninjabackend.py12
-rw-r--r--mesonbuild/backend/vs2010backend.py3
-rw-r--r--mesonbuild/backend/vs2015backend.py1
-rw-r--r--mesonbuild/backend/xcodebackend.py1
-rw-r--r--mesonbuild/build.py10
-rw-r--r--mesonbuild/compilers.py55
-rw-r--r--mesonbuild/interpreter.py14
-rw-r--r--test cases/common/125 shared module/meson.build16
-rw-r--r--test cases/common/125 shared module/module.c71
-rw-r--r--test cases/common/125 shared module/prog.c102
-rw-r--r--test cases/common/125 shared module/runtime.c19
11 files changed, 281 insertions, 23 deletions
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index cc0167d..e11491f 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -139,6 +139,7 @@ class NinjaBackend(backends.Backend):
def __init__(self, build):
super().__init__(build)
+ self.name = 'ninja'
self.ninja_filename = 'build.ninja'
self.fortran_deps = {}
self.all_outputs = {}
@@ -1931,7 +1932,8 @@ rule FORTRAN_DEP_HACK
commands += linker.get_linker_always_args()
if not isinstance(target, build.StaticLibrary):
commands += compilers.get_base_link_args(self.environment.coredata.base_options,
- linker)
+ linker,
+ isinstance(target, build.SharedModule))
commands += linker.get_buildtype_linker_args(self.environment.coredata.get_builtin_option('buildtype'))
commands += linker.get_option_link_args(self.environment.coredata.compiler_options)
commands += self.get_link_debugfile_args(linker, target, outname)
@@ -1940,13 +1942,17 @@ rule FORTRAN_DEP_HACK
if isinstance(target, build.Executable):
commands += linker.get_std_exe_link_args()
elif isinstance(target, build.SharedLibrary):
- commands += linker.get_std_shared_lib_link_args()
+ if isinstance(target, build.SharedModule):
+ commands += linker.get_std_shared_module_link_args()
+ else:
+ commands += linker.get_std_shared_lib_link_args()
commands += linker.get_pic_args()
if hasattr(target, 'soversion'):
soversion = target.soversion
else:
soversion = None
- commands += linker.get_soname_args(target.prefix, target.name, target.suffix, abspath, soversion)
+ commands += linker.get_soname_args(target.prefix, target.name, target.suffix,
+ abspath, soversion, isinstance(target, build.SharedModule))
# This is only visited when using the Visual Studio toolchain
if target.vs_module_defs and hasattr(linker, 'gen_vs_module_defs_args'):
commands += linker.gen_vs_module_defs_args(target.vs_module_defs.rel_to_builddir(self.build_to_src))
diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py
index 0f67771..d043455 100644
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -57,6 +57,7 @@ class RegenInfo():
class Vs2010Backend(backends.Backend):
def __init__(self, build):
super().__init__(build)
+ self.name = 'vs2010'
self.project_file_version = '10.0.30319.1'
self.sources_conflicts = {}
self.platform_toolset = None
@@ -782,6 +783,8 @@ class Vs2010Backend(backends.Backend):
extra_link_args += l
if not isinstance(target, build.StaticLibrary):
extra_link_args += target.link_args
+ if isinstance(target, build.SharedModule):
+ extra_link_args += compiler.get_std_shared_module_link_args()
# External deps must be last because target link libraries may depend on them.
for dep in target.get_external_deps():
extra_link_args += dep.get_link_args()
diff --git a/mesonbuild/backend/vs2015backend.py b/mesonbuild/backend/vs2015backend.py
index 8d4ff45..2c0efa7 100644
--- a/mesonbuild/backend/vs2015backend.py
+++ b/mesonbuild/backend/vs2015backend.py
@@ -19,6 +19,7 @@ from .vs2010backend import Vs2010Backend
class Vs2015Backend(Vs2010Backend):
def __init__(self, build):
super().__init__(build)
+ self.name = 'vs2015'
self.platform_toolset = 'v140'
self.vs_version = '2015'
diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py
index b157741..8133e0f 100644
--- a/mesonbuild/backend/xcodebackend.py
+++ b/mesonbuild/backend/xcodebackend.py
@@ -22,6 +22,7 @@ from ..mesonlib import MesonException
class XCodeBackend(backends.Backend):
def __init__(self, build):
super().__init__(build)
+ self.name = 'xcode'
self.project_uid = self.environment.coredata.guid.replace('-', '')[:24]
self.project_conflist = self.gen_id()
self.indent = ' '
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 462a55b..79759ee 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -1127,6 +1127,16 @@ class SharedLibrary(BuildTarget):
def type_suffix(self):
return "@sha"
+# A shared library that is meant to be used with dlopen rather than linking
+# into something else.
+class SharedModule(SharedLibrary):
+ def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs):
+ if 'version' in kwargs:
+ raise MesonException('Shared modules must not specify the version kwarg.')
+ if 'soversion' in kwargs:
+ raise MesonException('Shared modules must not specify the soversion kwarg.')
+ super().__init__(name, subdir, subproject, is_cross, sources, objects, environment, kwargs)
+
class CustomTarget:
known_kwargs = {'input' : True,
'output' : True,
diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py
index 2534a47..0f6250f 100644
--- a/mesonbuild/compilers.py
+++ b/mesonbuild/compilers.py
@@ -248,7 +248,7 @@ def get_base_compile_args(options, compiler):
pass
return args
-def get_base_link_args(options, linker):
+def get_base_link_args(options, linker, is_shared_module):
args = []
# FIXME, gcc/clang specific.
try:
@@ -269,7 +269,7 @@ def get_base_link_args(options, linker):
except KeyError:
pass
try:
- if options['b_lundef'].value:
+ if not is_shared_module and options['b_lundef'].value:
args.append('-Wl,--no-undefined')
except KeyError:
pass
@@ -486,6 +486,12 @@ class Compiler():
def get_link_debugfile_args(self, rel_obj):
return []
+ def get_std_shared_lib_link_args(self):
+ return []
+
+ def get_std_shared_module_link_args(self):
+ return self.get_std_shared_lib_link_args()
+
class CCompiler(Compiler):
def __init__(self, exelist, version, is_cross, exe_wrapper=None):
# If a child ObjC or CPP class has already set it, don't set it ourselves
@@ -526,7 +532,7 @@ class CCompiler(Compiler):
# Almost every compiler uses this for disabling warnings
return ['-w']
- def get_soname_args(self, prefix, shlib_name, suffix, path, soversion):
+ def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module):
return []
def split_shlib_to_parts(self, fname):
@@ -1173,7 +1179,7 @@ class MonoCompiler(Compiler):
def get_link_args(self, fname):
return ['-r:' + fname]
- def get_soname_args(self, prefix, shlib_name, suffix, path, soversion):
+ def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module):
return []
def get_werror_args(self):
@@ -1209,9 +1215,6 @@ class MonoCompiler(Compiler):
def get_include_args(self, path):
return []
- def get_std_shared_lib_link_args(self):
- return []
-
def get_pic_args(self):
return []
@@ -1257,7 +1260,7 @@ class JavaCompiler(Compiler):
self.id = 'unknown'
self.javarunner = 'java'
- def get_soname_args(self, prefix, shlib_name, suffix, path, soversion):
+ def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module):
return []
def get_werror_args(self):
@@ -1298,9 +1301,6 @@ class JavaCompiler(Compiler):
def get_include_args(self, path):
return []
- def get_std_shared_lib_link_args(self):
- return []
-
def get_pic_args(self):
return []
@@ -1559,7 +1559,7 @@ class DCompiler(Compiler):
def get_std_shared_lib_link_args(self):
return ['-shared']
- def get_soname_args(self, prefix, shlib_name, suffix, path, soversion):
+ def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module):
return []
def get_unittest_args(self):
@@ -1993,7 +1993,7 @@ CLANG_OSX = 1
CLANG_WIN = 2
# Possibly clang-cl?
-def get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion):
+def get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion, is_shared_module):
if soversion is None:
sostr = ''
else:
@@ -2003,6 +2003,8 @@ def get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion):
return ['-Wl,-soname,%s%s.%s%s' % (prefix, shlib_name, suffix, sostr)]
return ['-Wl,-soname,%s%s' % (shlib_name, sostr)]
elif gcc_type == GCC_OSX:
+ if is_shared_module:
+ return []
return ['-install_name', os.path.join(path, 'lib' + shlib_name + '.dylib')]
else:
raise RuntimeError('Not implemented yet.')
@@ -2038,7 +2040,7 @@ class GnuCompiler:
def get_define(self, define):
if define in self.defines:
- return defines[define]
+ return self.defines[define]
def get_pic_args(self):
if self.gcc_type in (GCC_MINGW, GCC_OSX):
@@ -2060,8 +2062,13 @@ class GnuCompiler:
def split_shlib_to_parts(self, fname):
return (os.path.split(fname)[0], fname)
- def get_soname_args(self, prefix, shlib_name, suffix, path, soversion):
- return get_gcc_soname_args(self.gcc_type, prefix, shlib_name, suffix, path, soversion)
+ def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module):
+ return get_gcc_soname_args(self.gcc_type, prefix, shlib_name, suffix, path, soversion, is_shared_module)
+
+ def get_std_shared_lib_link_args(self):
+ if self.gcc_type == GCC_OSX:
+ return ['-bundle']
+ return ['-shared']
class GnuCCompiler(GnuCompiler, CCompiler):
def __init__(self, exelist, version, gcc_type, is_cross, exe_wrapper=None, defines=None):
@@ -2097,6 +2104,9 @@ class GnuCCompiler(GnuCompiler, CCompiler):
return options['c_winlibs'].value[:]
return []
+ def get_std_shared_lib_link_args(self):
+ return ['-shared']
+
class GnuCPPCompiler(GnuCompiler, CPPCompiler):
def __init__(self, exelist, version, gcc_type, is_cross, exe_wrap, defines):
@@ -2199,7 +2209,7 @@ class ClangCompiler():
# so it might change semantics at any time.
return ['-include-pch', os.path.join (pch_dir, self.get_pch_name (header))]
- def get_soname_args(self, prefix, shlib_name, suffix, path, soversion):
+ def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module):
if self.clang_type == CLANG_STANDARD:
gcc_type = GCC_STANDARD
elif self.clang_type == CLANG_OSX:
@@ -2208,7 +2218,7 @@ class ClangCompiler():
gcc_type = GCC_MINGW
else:
raise MesonException('Unreachable code when converting clang type to gcc type.')
- return get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion)
+ return get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion, is_shared_module)
def has_argument(self, arg, env):
return super().has_argument(['-Werror=unknown-warning-option', arg], env)
@@ -2224,6 +2234,11 @@ class ClangCompiler():
extra_args.append('-Wl,-no_weak_imports')
return super().has_function(funcname, prefix, env, extra_args, dependencies)
+ def get_std_shared_module_link_args(self):
+ if self.clang_type == CLANG_OSX:
+ return ['-bundle', '-Wl,-undefined,dynamic_lookup']
+ return ['-shared']
+
class ClangCCompiler(ClangCompiler, CCompiler):
def __init__(self, exelist, version, clang_type, is_cross, exe_wrapper=None):
CCompiler.__init__(self, exelist, version, is_cross, exe_wrapper)
@@ -2348,8 +2363,8 @@ end program prog
def split_shlib_to_parts(self, fname):
return (os.path.split(fname)[0], fname)
- def get_soname_args(self, prefix, shlib_name, suffix, path, soversion):
- return get_gcc_soname_args(self.gcc_type, prefix, shlib_name, suffix, path, soversion)
+ def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module):
+ return get_gcc_soname_args(self.gcc_type, prefix, shlib_name, suffix, path, soversion, is_shared_module)
def get_dependency_gen_args(self, outtarget, outfile):
# Disabled until this is fixed:
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index ff67e0e..3d4f092 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -543,6 +543,10 @@ class SharedLibraryHolder(BuildTargetHolder):
def __init__(self, target, interp):
super().__init__(target, interp)
+class SharedModuleHolder(BuildTargetHolder):
+ def __init__(self, target, interp):
+ super().__init__(target, interp)
+
class JarHolder(BuildTargetHolder):
def __init__(self, target, interp):
super().__init__(target, interp)
@@ -988,6 +992,7 @@ class MesonMain(InterpreterObject):
'version': self.version_method,
'project_name' : self.project_name_method,
'get_cross_property': self.get_cross_property_method,
+ 'backend' : self.backend_method,
})
def add_install_script_method(self, args, kwargs):
@@ -1026,6 +1031,9 @@ class MesonMain(InterpreterObject):
return src
return os.path.join(src, sub)
+ def backend_method(self, args, kwargs):
+ return self.interpreter.backend.name
+
def source_root_method(self, args, kwargs):
return self.interpreter.environment.source_dir
@@ -1154,6 +1162,7 @@ class Interpreter(InterpreterBase):
'dependency' : self.func_dependency,
'static_library' : self.func_static_lib,
'shared_library' : self.func_shared_lib,
+ 'shared_module' : self.func_shared_module,
'library' : self.func_library,
'jar' : self.func_jar,
'build_target': self.func_build_target,
@@ -1777,6 +1786,9 @@ requirements use the version keyword argument instead.''')
def func_shared_lib(self, node, args, kwargs):
return self.build_target(node, args, kwargs, SharedLibraryHolder)
+ def func_shared_module(self, node, args, kwargs):
+ return self.build_target(node, args, kwargs, SharedModuleHolder)
+
def func_library(self, node, args, kwargs):
if self.coredata.get_builtin_option('default_library') == 'shared':
return self.func_shared_lib(node, args, kwargs)
@@ -2252,6 +2264,8 @@ requirements use the version keyword argument instead.''')
targetclass = build.Executable
elif targetholder is SharedLibraryHolder:
targetclass = build.SharedLibrary
+ elif targetholder is SharedModuleHolder:
+ targetclass = build.SharedModule
elif targetholder is StaticLibraryHolder:
targetclass = build.StaticLibrary
elif targetholder is JarHolder:
diff --git a/test cases/common/125 shared module/meson.build b/test cases/common/125 shared module/meson.build
new file mode 100644
index 0000000..ccb9c1a
--- /dev/null
+++ b/test cases/common/125 shared module/meson.build
@@ -0,0 +1,16 @@
+project('shared module', 'c')
+
+if meson.backend().startswith('vs')
+ error('MESON_SKIP_TEST for some reason /FORCE does not work in the VS backend.')
+endif
+
+dl = meson.get_compiler('c').find_library('dl', required : false)
+l = shared_library('runtime', 'runtime.c')
+# Do NOT link the module with the runtime library. This
+# is a common approach for plugins that are only used
+# with dlopen. Any symbols are resolved dynamically
+# at runtime
+m = shared_module('mymodule', 'module.c')
+e = executable('prog', 'prog.c', link_with : l, dependencies : dl)
+test('import test', e, args : [m.full_path()])
+
diff --git a/test cases/common/125 shared module/module.c b/test cases/common/125 shared module/module.c
new file mode 100644
index 0000000..56078c5
--- /dev/null
+++ b/test cases/common/125 shared module/module.c
@@ -0,0 +1,71 @@
+#if defined _WIN32 || defined __CYGWIN__
+ #define DLL_PUBLIC __declspec(dllexport)
+#else
+ #if defined __GNUC__
+ #define DLL_PUBLIC __attribute__ ((visibility("default")))
+ #else
+ #pragma message ("Compiler does not support symbol visibility.")
+ #define DLL_PUBLIC
+ #endif
+#endif
+
+#ifdef _WIN32
+
+#include <stdio.h>
+#include <windows.h>
+#include <tlhelp32.h>
+
+typedef int (*fptr) (void);
+
+/* Unlike Linux and OS X, when a library is loaded, all the symbols aren't
+ * loaded into a single namespace. You must fetch the symbol by iterating over
+ * all loaded modules. Code for finding the function from any of the loaded
+ * modules is taken from gmodule.c in glib */
+fptr find_any_f (const char *name) {
+ fptr f;
+ HANDLE snapshot;
+ MODULEENTRY32 me32;
+
+ snapshot = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, 0);
+ if (snapshot == (HANDLE) -1) {
+ printf("Could not get snapshot\n");
+ return 0;
+ }
+
+ me32.dwSize = sizeof (me32);
+
+ f = NULL;
+ if (Module32First (snapshot, &me32)) {
+ do {
+ if ((f = (fptr) GetProcAddress (me32.hModule, name)) != NULL)
+ break;
+ } while (Module32Next (snapshot, &me32));
+ }
+
+ CloseHandle (snapshot);
+ return f;
+}
+
+int DLL_PUBLIC func() {
+ fptr f;
+
+ f = find_any_f ("func_from_language_runtime");
+ if (f != NULL)
+ return f();
+ printf ("Could not find function\n");
+ return 1;
+}
+
+#else
+/*
+ * Shared modules often have references to symbols that are not defined
+ * at link time, but which will be provided from deps of the executable that
+ * dlopens it. We need to make sure that this works, i.e. that we do
+ * not pass -Wl,--no-undefined when linking modules.
+ */
+int func_from_language_runtime();
+
+int DLL_PUBLIC func(void) {
+ return func_from_language_runtime();
+}
+#endif
diff --git a/test cases/common/125 shared module/prog.c b/test cases/common/125 shared module/prog.c
new file mode 100644
index 0000000..2b63840
--- /dev/null
+++ b/test cases/common/125 shared module/prog.c
@@ -0,0 +1,102 @@
+
+#include <stdio.h>
+
+int func_from_language_runtime(void);
+typedef int (*fptr) (void);
+
+#ifdef _WIN32
+
+#include <windows.h>
+
+wchar_t*
+win32_get_last_error (void)
+{
+ wchar_t *msg = NULL;
+
+ FormatMessageW (FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_IGNORE_INSERTS
+ | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, GetLastError (), 0,
+ (LPWSTR) &msg, 0, NULL);
+ return msg;
+}
+
+int
+main (int argc, char **argv)
+{
+ HINSTANCE handle;
+ fptr importedfunc;
+ int expected, actual;
+ int ret = 1;
+
+ handle = LoadLibraryA (argv[1]);
+ if (!handle) {
+ wchar_t *msg = win32_get_last_error ();
+ printf ("Could not open %s: %S\n", argv[1], msg);
+ goto nohandle;
+ }
+
+ importedfunc = (fptr) GetProcAddress (handle, "func");
+ if (importedfunc == NULL) {
+ wchar_t *msg = win32_get_last_error ();
+ printf ("Could not find 'func': %S\n", msg);
+ goto out;
+ }
+
+ actual = importedfunc ();
+ expected = func_from_language_runtime ();
+ if (actual != expected) {
+ printf ("Got %i instead of %i\n", actual, expected);
+ goto out;
+ }
+
+ ret = 0;
+out:
+ FreeLibrary (handle);
+nohandle:
+ return ret;
+}
+
+#else
+
+#include<dlfcn.h>
+#include<assert.h>
+
+int main(int argc, char **argv) {
+ void *dl;
+ fptr importedfunc;
+ int expected, actual;
+ char *error;
+ int ret = 1;
+
+ dlerror();
+ dl = dlopen(argv[1], RTLD_LAZY);
+ error = dlerror();
+ if(error) {
+ printf("Could not open %s: %s\n", argv[1], error);
+ goto nodl;
+ }
+
+ importedfunc = (fptr) dlsym(dl, "func");
+ if (importedfunc == NULL) {
+ printf ("Could not find 'func'\n");
+ goto out;
+ }
+
+ assert(importedfunc != func_from_language_runtime);
+
+ actual = (*importedfunc)();
+ expected = func_from_language_runtime ();
+ if (actual != expected) {
+ printf ("Got %i instead of %i\n", actual, expected);
+ goto out;
+ }
+
+ ret = 0;
+out:
+ dlclose(dl);
+nodl:
+ return ret;
+}
+
+#endif
diff --git a/test cases/common/125 shared module/runtime.c b/test cases/common/125 shared module/runtime.c
new file mode 100644
index 0000000..03bde86
--- /dev/null
+++ b/test cases/common/125 shared module/runtime.c
@@ -0,0 +1,19 @@
+#if defined _WIN32 || defined __CYGWIN__
+ #define DLL_PUBLIC __declspec(dllexport)
+#else
+ #if defined __GNUC__
+ #define DLL_PUBLIC __attribute__ ((visibility("default")))
+ #else
+ #pragma message ("Compiler does not support symbol visibility.")
+ #define DLL_PUBLIC
+ #endif
+#endif
+
+/*
+ * This file pretends to be a language runtime that supports extension
+ * modules.
+ */
+
+int DLL_PUBLIC func_from_language_runtime(void) {
+ return 86;
+}