aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml2
-rw-r--r--mesonbuild/backend/backends.py9
-rw-r--r--mesonbuild/backend/ninjabackend.py11
-rw-r--r--mesonbuild/backend/vs2010backend.py6
-rw-r--r--mesonbuild/build.py32
-rw-r--r--mesonbuild/compilers.py26
-rw-r--r--mesonbuild/coredata.py2
-rw-r--r--mesonbuild/interpreter.py12
-rw-r--r--mesonbuild/mesonmain.py2
-rwxr-xr-xrun_unittests.py11
-rw-r--r--test cases/common/139 override options/meson.build6
-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
-rw-r--r--test cases/frameworks/5 protocol buffers/meson.build9
17 files changed, 173 insertions, 17 deletions
diff --git a/.travis.yml b/.travis.yml
index b3346a1..fbb11ac 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,7 +14,7 @@ compiler:
env:
- MESON_ARGS=""
- - MESON_ARGS="--unity"
+ - MESON_ARGS="--unity=on"
language:
- cpp
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index bcf06d5..389e759 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -306,7 +306,7 @@ class Backend:
# With unity builds, there's just one object that contains all the
# sources, and we only support extracting all the objects in this mode,
# so just return that.
- if self.get_option_for_target('unity', target):
+ if self.is_unity(target):
comp = get_compiler_for_source(extobj.target.compilers.values(),
extobj.srclist[0])
# There is a potential conflict here, but it is unlikely that
@@ -607,6 +607,13 @@ class Backend:
libs.append(os.path.join(self.get_target_dir(t), f))
return libs
+ def is_unity(self, target):
+ optval = self.get_option_for_target('unity', target)
+ if optval == 'on' or (optval == 'subprojects' and target.subproject != ''):
+ return True
+ return False
+
+
def get_custom_target_sources(self, target):
'''
Custom target sources can be of various object types; strings, File,
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index 98a2110..3bf840d 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -343,7 +343,7 @@ int dummy;
outname = self.get_target_filename(target)
obj_list = []
use_pch = self.environment.coredata.base_options.get('b_pch', False)
- is_unity = self.get_option_for_target('unity', target)
+ is_unity = self.is_unity(target)
if use_pch and target.has_pch():
pch_objects = self.generate_pch(target, outfile)
else:
@@ -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) if len(target_args) else []
+
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..432bdd0 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():
@@ -612,7 +614,7 @@ class Vs2010Backend(backends.Backend):
# Prefix to use to access the source tree's subdir from the vcxproj dir
proj_to_src_dir = os.path.join(proj_to_src_root, target.subdir)
(sources, headers, objects, languages) = self.split_sources(target.sources)
- if self.get_option_for_target('unity', target):
+ if self.is_unity(target):
sources = self.generate_unity_files(target, sources)
compiler = self._get_cl_compiler(target)
buildtype_args = compiler.get_buildtype_args(self.buildtype)
@@ -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..bcbe318 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,
@@ -306,7 +307,8 @@ class BuildTarget(Target):
super().__init__(name, subdir, True)
self.subproject = subproject # Can not be calculated from subdir as subproject dirname can be changed per project.
self.is_cross = is_cross
- self.is_unity = environment.coredata.get_builtin_option('unity')
+ unity_opt = environment.coredata.get_builtin_option('unity')
+ self.is_unity = unity_opt == 'on' or (unity_opt == 'subprojects' and subproject != '')
self.environment = environment
self.sources = []
self.compilers = OrderedDict()
@@ -314,6 +316,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 +569,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 +716,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 +808,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/mesonbuild/coredata.py b/mesonbuild/coredata.py
index 27f1dd7..b9b889d 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -295,7 +295,7 @@ def get_builtin_option_default(optname):
builtin_options = {
'buildtype': [UserComboOption, 'Build type to use.', ['plain', 'debug', 'debugoptimized', 'release', 'minsize'], 'debug'],
'strip': [UserBooleanOption, 'Strip targets on install.', False],
- 'unity': [UserBooleanOption, 'Unity build.', False],
+ 'unity': [UserComboOption, 'Unity build.', ['on', 'off', 'subprojects'], 'off'],
'prefix': [UserStringOption, 'Installation prefix.', default_prefix()],
'libdir': [UserStringOption, 'Library directory.', default_libdir()],
'libexecdir': [UserStringOption, 'Library executable directory.', default_libexecdir()],
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index 18f91ea..649b842 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -1188,7 +1188,10 @@ class MesonMain(InterpreterObject):
raise InterpreterException('Tried to access compiler for unspecified language "%s".' % cname)
def is_unity_method(self, args, kwargs):
- return self.build.environment.coredata.get_builtin_option('unity')
+ optval = self.interpreter.environment.coredata.get_builtin_option('unity')
+ if optval == 'on' or (optval == 'subprojects' and self.interpreter.subproject != ''):
+ return True
+ return False
def is_subproject_method(self, args, kwargs):
return self.interpreter.is_subproject()
@@ -1400,8 +1403,11 @@ class Interpreter(InterpreterBase):
raise InvalidCode('Import takes one argument.')
modname = args[0]
if modname not in self.environment.coredata.modules:
- module = importlib.import_module('mesonbuild.modules.' + modname).initialize()
- self.environment.coredata.modules[modname] = module
+ try:
+ module = importlib.import_module('mesonbuild.modules.' + modname)
+ except ImportError:
+ raise InvalidArguments('Module "%s" does not exist' % (modname, ))
+ self.environment.coredata.modules[modname] = module.initialize()
return ModuleHolder(modname, self.environment.coredata.modules[modname], self)
@stringArgs
diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py
index 51041cc..7e4be7f 100644
--- a/mesonbuild/mesonmain.py
+++ b/mesonbuild/mesonmain.py
@@ -54,7 +54,7 @@ add_builtin_argument('sharedstatedir')
add_builtin_argument('backend')
add_builtin_argument('buildtype')
add_builtin_argument('strip', action='store_true')
-add_builtin_argument('unity', action='store_true')
+add_builtin_argument('unity')
add_builtin_argument('werror', action='store_true')
add_builtin_argument('layout')
add_builtin_argument('default-library')
diff --git a/run_unittests.py b/run_unittests.py
index 7bdd57b..e8ecbb2 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -390,8 +390,11 @@ class BasePlatformTests(unittest.TestCase):
return output
def init(self, srcdir, extra_args=None, default_args=True):
+ self.assertTrue(os.path.exists(srcdir))
if extra_args is None:
extra_args = []
+ if not isinstance(extra_args, list):
+ extra_args = [extra_args]
args = [srcdir, self.builddir]
if default_args:
args += ['--prefix', self.prefix,
@@ -1374,6 +1377,14 @@ class LinuxlikeTests(BasePlatformTests):
cpp = env.detect_cpp_compiler(False)
self._test_stds_impl(testdir, cpp, 'cpp')
+ def test_unity_subproj(self):
+ testdir = os.path.join(self.common_test_dir, '49 subproject')
+ self.init(testdir, extra_args='--unity=subprojects')
+ self.assertTrue(os.path.exists(os.path.join(self.builddir, 'subprojects/sublib/simpletest@exe/simpletest-unity.c')))
+ self.assertTrue(os.path.exists(os.path.join(self.builddir, 'subprojects/sublib/sublib@sha/sublib-unity.c')))
+ self.assertFalse(os.path.exists(os.path.join(self.builddir, 'user@exe/user-unity.c')))
+ self.build()
+
def test_installed_modes(self):
'''
Test that files installed by these tests have the correct permissions.
diff --git a/test cases/common/139 override options/meson.build b/test cases/common/139 override options/meson.build
index 0db0513..4dd8d79 100644
--- a/test cases/common/139 override options/meson.build
+++ b/test cases/common/139 override options/meson.build
@@ -1,8 +1,6 @@
project('option override', 'c',
- default_options : 'unity=true')
+ default_options : 'unity=on')
executable('mustunity', 'one.c', 'two.c')
executable('notunity', 'three.c', 'four.c',
- override_options : ['unity=false'])
-
-
+ override_options : ['unity=off'])
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();
+}
diff --git a/test cases/frameworks/5 protocol buffers/meson.build b/test cases/frameworks/5 protocol buffers/meson.build
index 5c28344..c99e0a8 100644
--- a/test cases/frameworks/5 protocol buffers/meson.build
+++ b/test cases/frameworks/5 protocol buffers/meson.build
@@ -1,7 +1,12 @@
project('protocol buffer test', 'cpp')
-protoc = find_program('protoc')
-dep = dependency('protobuf')
+protoc = find_program('protoc', required : false)
+dep = dependency('protobuf', required : false)
+
+if not protoc.found() or not dep.found()
+ error('MESON_SKIP_TEST: protoc tool and/or protobuf pkg-config dependency not found')
+endif
+
gen = generator(protoc, \
output : ['@BASENAME@.pb.cc', '@BASENAME@.pb.h'],