aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2017-04-15 18:16:35 +0300
committerGitHub <noreply@github.com>2017-04-15 18:16:35 +0300
commit4e1249c920a6f64e2ca953334e9ec700f30693da (patch)
tree4ff1e03ac95c0827c6519b0fe684f41602bb47c3
parentdcc95d7f705c5fbc036d7d6511f6df50beaac44a (diff)
parent62b86824f03d6b401f7bc8da6ce68b334726df44 (diff)
downloadmeson-4e1249c920a6f64e2ca953334e9ec700f30693da.zip
meson-4e1249c920a6f64e2ca953334e9ec700f30693da.tar.gz
meson-4e1249c920a6f64e2ca953334e9ec700f30693da.tar.bz2
Merge pull request #1549 from mesonbuild/linkwhole
Add option to link the entire contents of a static library to a target.
-rw-r--r--mesonbuild/backend/ninjabackend.py9
-rw-r--r--mesonbuild/backend/vs2010backend.py4
-rw-r--r--mesonbuild/build.py29
-rw-r--r--mesonbuild/compilers.py26
-rw-r--r--test cases/common/145 whole archive/dylib.c7
-rw-r--r--test cases/common/145 whole archive/libfile.c7
-rw-r--r--test cases/common/145 whole archive/meson.build22
-rw-r--r--test cases/common/145 whole archive/mylib.h21
-rw-r--r--test cases/common/145 whole archive/prog.c5
9 files changed, 129 insertions, 1 deletions
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index 98a2110..dc05d34 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -2187,6 +2187,10 @@ rule FORTRAN_DEP_HACK
raise RuntimeError('Unknown build target type.')
return commands
+ def get_link_whole_args(self, linker, target):
+ target_args = self.build_target_link_arguments(linker, target.link_whole_targets)
+ return linker.get_link_whole_for(target_args)
+
def generate_link(self, target, outfile, outname, obj_list, linker, extra_args=[]):
if isinstance(target, build.StaticLibrary):
linker_base = 'STATIC'
@@ -2226,6 +2230,11 @@ rule FORTRAN_DEP_HACK
# Add link args specific to this BuildTarget type, such as soname args,
# PIC, import library generation, etc.
commands += self.get_target_type_link_args(target, linker)
+ # Archives that are copied wholesale in the result. Must be before any
+ # other link targets so missing symbols from whole archives are found in those.
+ if not isinstance(target, build.StaticLibrary):
+ commands += self.get_link_whole_args(linker, target)
+
if not isinstance(target, build.StaticLibrary):
# Add link args added using add_project_link_arguments()
commands += self.build.get_project_link_args(linker, target.subproject)
diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py
index 46eab11..9937521 100644
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -225,6 +225,8 @@ class Vs2010Backend(backends.Backend):
elif isinstance(target, build.BuildTarget):
for ldep in target.link_targets:
all_deps[ldep.get_id()] = ldep
+ for ldep in target.link_whole_targets:
+ all_deps[ldep.get_id()] = ldep
for obj_id, objdep in self.get_obj_target_deps(target.objects):
all_deps[obj_id] = objdep
for gendep in target.get_generated_sources():
@@ -927,6 +929,8 @@ class Vs2010Backend(backends.Backend):
for t in target.get_dependencies():
lobj = self.build.targets[t.get_id()]
linkname = os.path.join(down, self.get_target_filename_for_linking(lobj))
+ if t in target.link_whole_targets:
+ linkname = compiler.get_link_whole_for(linkname)[0]
additional_links.append(linkname)
for lib in self.get_custom_target_provided_libraries(target):
additional_links.append(self.relpath(lib, self.get_target_dir(target)))
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 6c16cf9..6815b0c 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -37,6 +37,7 @@ known_basic_kwargs = {'install': True,
'link_args': True,
'link_depends': True,
'link_with': True,
+ 'link_whole': True,
'include_directories': True,
'dependencies': True,
'install_dir': True,
@@ -314,6 +315,7 @@ class BuildTarget(Target):
self.external_deps = []
self.include_dirs = []
self.link_targets = []
+ self.link_whole_targets = []
self.link_depends = []
self.name_prefix_set = False
self.name_suffix_set = False
@@ -566,6 +568,15 @@ class BuildTarget(Target):
if hasattr(linktarget, "held_object"):
linktarget = linktarget.held_object
self.link(linktarget)
+ lwhole = kwargs.get('link_whole', [])
+ if not isinstance(lwhole, list):
+ lwhole = [lwhole]
+ for linktarget in lwhole:
+ # Sorry for this hack. Keyword targets are kept in holders
+ # in kwargs. Unpack here without looking at the exact type.
+ if hasattr(linktarget, "held_object"):
+ linktarget = linktarget.held_object
+ self.link_whole(linktarget)
c_pchlist = kwargs.get('c_pch', [])
if not isinstance(c_pchlist, list):
c_pchlist = [c_pchlist]
@@ -704,7 +715,7 @@ class BuildTarget(Target):
def get_dependencies(self):
transitive_deps = []
- for t in self.link_targets:
+ for t in self.link_targets + self.link_whole_targets:
transitive_deps.append(t)
if isinstance(t, StaticLibrary):
transitive_deps += t.get_dependencies()
@@ -796,6 +807,22 @@ You probably should put it in link_with instead.''')
raise InvalidArguments('Tried to mix cross built and native libraries in target {!r}'.format(self.name))
self.link_targets.append(t)
+ def link_whole(self, target):
+ if not isinstance(target, list):
+ target = [target]
+ for t in target:
+ if hasattr(t, 'held_object'):
+ t = t.held_object
+ if not isinstance(t, StaticLibrary):
+ raise InvalidArguments('{!r} is not a static library.'.format(t))
+ if isinstance(self, SharedLibrary) and not t.pic:
+ msg = "Can't link non-PIC static library {!r} into shared library {!r}. ".format(t.name, self.name)
+ msg += "Use the 'pic' option to static_library to build with PIC."
+ raise InvalidArguments(msg)
+ if self.is_cross != t.is_cross:
+ raise InvalidArguments('Tried to mix cross built and native libraries in target {!r}'.format(self.name))
+ self.link_whole_targets.append(t)
+
def add_pch(self, language, pchlist):
if len(pchlist) == 0:
return
diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py
index e6be8b1..c8ce487 100644
--- a/mesonbuild/compilers.py
+++ b/mesonbuild/compilers.py
@@ -707,6 +707,11 @@ class Compiler:
def get_std_shared_module_link_args(self):
return self.get_std_shared_lib_link_args()
+ def get_link_whole_for(self, args):
+ if isinstance(args, list) and len(args) == 0:
+ return []
+ raise EnvironmentException('Language %s does not support linking whole archives.' % self.language)
+
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
@@ -2274,6 +2279,13 @@ class VisualStudioCCompiler(CCompiler):
pdbarr += ['pdb']
return ['/DEBUG', '/PDB:' + '.'.join(pdbarr)]
+ def get_link_whole_for(self, args):
+ # Only since VS2015
+ if not isinstance(args, list):
+ args = [args]
+ return ['/WHOLEARCHIVE:' + x for x in args]
+
+
class VisualStudioCPPCompiler(VisualStudioCCompiler, CPPCompiler):
def __init__(self, exelist, version, is_cross, exe_wrap):
self.language = 'cpp'
@@ -2425,6 +2437,10 @@ class GnuCompiler:
return ['-bundle']
return ['-shared']
+ def get_link_whole_for(self, args):
+ return ['-Wl,--whole-archive'] + args + ['-Wl,--no-whole-archive']
+
+
class GnuCCompiler(GnuCompiler, CCompiler):
def __init__(self, exelist, version, gcc_type, is_cross, exe_wrapper=None, defines=None):
CCompiler.__init__(self, exelist, version, is_cross, exe_wrapper)
@@ -2460,6 +2476,7 @@ class GnuCCompiler(GnuCompiler, CCompiler):
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):
@@ -2589,6 +2606,15 @@ class ClangCompiler:
return ['-bundle', '-Wl,-undefined,dynamic_lookup']
return ['-shared']
+ def get_link_whole_for(self, args):
+ if self.clang_type == CLANG_OSX:
+ result = []
+ for a in args:
+ result += ['-Wl,-force_load', a]
+ return result
+ return ['-Wl,--whole-archive'] + args + ['-Wl,--no-whole-archive']
+
+
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)
diff --git a/test cases/common/145 whole archive/dylib.c b/test cases/common/145 whole archive/dylib.c
new file mode 100644
index 0000000..9e287a4
--- /dev/null
+++ b/test cases/common/145 whole archive/dylib.c
@@ -0,0 +1,7 @@
+#define BUILDING_DLL
+
+#include<mylib.h>
+
+int func2() {
+ return 42;
+}
diff --git a/test cases/common/145 whole archive/libfile.c b/test cases/common/145 whole archive/libfile.c
new file mode 100644
index 0000000..b2690a0
--- /dev/null
+++ b/test cases/common/145 whole archive/libfile.c
@@ -0,0 +1,7 @@
+#define BUILDING_DLL
+
+#include<mylib.h>
+
+int func1() {
+ return 42;
+}
diff --git a/test cases/common/145 whole archive/meson.build b/test cases/common/145 whole archive/meson.build
new file mode 100644
index 0000000..eadebf8
--- /dev/null
+++ b/test cases/common/145 whole archive/meson.build
@@ -0,0 +1,22 @@
+project('whole archive', 'c')
+
+cc = meson.get_compiler('c')
+
+if cc.get_id() == 'msvc'
+ if cc.version().version_compare('<19')
+ error('MESON_SKIP_TEST link_whole only works on VS2015 or newer.')
+ endif
+endif
+
+stlib = static_library('allofme', 'libfile.c')
+
+# Nothing in dylib.c uses func1, so the linker would throw it
+# away and thus linking the exe would fail.
+dylib = shared_library('shlib', 'dylib.c',
+ link_whole : stlib)
+
+exe = executable('prog', 'prog.c',
+ link_with : dylib)
+
+test('prog', exe)
+
diff --git a/test cases/common/145 whole archive/mylib.h b/test cases/common/145 whole archive/mylib.h
new file mode 100644
index 0000000..813cc67
--- /dev/null
+++ b/test cases/common/145 whole archive/mylib.h
@@ -0,0 +1,21 @@
+#pragma once
+
+/* Both funcs here for simplicity. */
+
+#if defined _WIN32 || defined __CYGWIN__
+#if defined BUILDING_DLL
+ #define DLL_PUBLIC __declspec(dllexport)
+#else
+ #define DLL_PUBLIC __declspec(dllimport)
+#endif
+#else
+ #if defined __GNUC__
+ #define DLL_PUBLIC __attribute__ ((visibility("default")))
+ #else
+ #pragma message ("Compiler does not support symbol visibility.")
+ #define DLL_PUBLIC
+ #endif
+#endif
+
+int DLL_PUBLIC func1();
+int DLL_PUBLIC func2();
diff --git a/test cases/common/145 whole archive/prog.c b/test cases/common/145 whole archive/prog.c
new file mode 100644
index 0000000..9111ad4
--- /dev/null
+++ b/test cases/common/145 whole archive/prog.c
@@ -0,0 +1,5 @@
+#include<mylib.h>
+
+int main(int argc, char **argv) {
+ return func1() - func2();
+}