diff options
-rw-r--r-- | .travis.yml | 2 | ||||
-rw-r--r-- | mesonbuild/backend/backends.py | 9 | ||||
-rw-r--r-- | mesonbuild/backend/ninjabackend.py | 11 | ||||
-rw-r--r-- | mesonbuild/backend/vs2010backend.py | 6 | ||||
-rw-r--r-- | mesonbuild/build.py | 32 | ||||
-rw-r--r-- | mesonbuild/compilers.py | 26 | ||||
-rw-r--r-- | mesonbuild/coredata.py | 2 | ||||
-rw-r--r-- | mesonbuild/interpreter.py | 12 | ||||
-rw-r--r-- | mesonbuild/mesonmain.py | 2 | ||||
-rwxr-xr-x | run_unittests.py | 11 | ||||
-rw-r--r-- | test cases/common/139 override options/meson.build | 6 | ||||
-rw-r--r-- | test cases/common/145 whole archive/dylib.c | 7 | ||||
-rw-r--r-- | test cases/common/145 whole archive/libfile.c | 7 | ||||
-rw-r--r-- | test cases/common/145 whole archive/meson.build | 22 | ||||
-rw-r--r-- | test cases/common/145 whole archive/mylib.h | 21 | ||||
-rw-r--r-- | test cases/common/145 whole archive/prog.c | 5 | ||||
-rw-r--r-- | test cases/frameworks/5 protocol buffers/meson.build | 9 |
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'], |