diff options
author | Xavier Claessens <xavier.claessens@collabora.com> | 2018-01-03 11:46:13 -0500 |
---|---|---|
committer | Xavier Claessens <xavier.claessens@collabora.com> | 2018-04-03 15:38:01 -0400 |
commit | 68f9846b7c584815d821c60fcdff866fe629955a (patch) | |
tree | d90a14a8d2e790bea3084e3226d0f5f326e5f482 | |
parent | 809f01833336be63eb07441e120de2e4f8c4b3c4 (diff) | |
download | meson-68f9846b7c584815d821c60fcdff866fe629955a.zip meson-68f9846b7c584815d821c60fcdff866fe629955a.tar.gz meson-68f9846b7c584815d821c60fcdff866fe629955a.tar.bz2 |
Add both_libraries() to build both shared and static libraries
Also support default_library='both' to make library() build both shared
and static libraries.
Closes #484
-rw-r--r-- | docs/markdown/Reference-manual.md | 33 | ||||
-rw-r--r-- | docs/markdown/snippets/both-libraries.md | 9 | ||||
-rw-r--r-- | mesonbuild/coredata.py | 2 | ||||
-rw-r--r-- | mesonbuild/interpreter.py | 65 | ||||
-rw-r--r-- | test cases/common/189 bothlibraries/libfile.c | 7 | ||||
-rw-r--r-- | test cases/common/189 bothlibraries/main.c | 8 | ||||
-rw-r--r-- | test cases/common/189 bothlibraries/meson.build | 12 | ||||
-rw-r--r-- | test cases/common/189 bothlibraries/mylib.h | 13 |
8 files changed, 140 insertions, 9 deletions
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index 5109b25..d98fc19 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -112,6 +112,24 @@ run. The behavior of this function is identical to `test` with the exception that there is no `is_parallel` keyword, because benchmarks are never run in parallel. +### both_libraries() + +``` meson + buildtarget both_libraries(library_name, list_of_sources, ...) +``` + +Builds both a static and shared library with the given sources. Positional and +keyword arguments are otherwise the same as for [`library`](#library). Source +files will be compiled only once and object files will be reused to build both +shared and static libraries, unless `b_staticpic` user option or `pic` argument +are set to false in which case sources will be compiled twice. + +The returned [buildtarget](#build-target-object) always represents the shared +library. In addition it supports the following extra methods: + +- `get_shared_lib()` returns the shared library build target +- `get_static_lib()` returns the static library build target + ### build_target() Creates a build target whose type can be set dynamically with the @@ -885,10 +903,11 @@ dropped. That means that `join_paths('foo', '/bar')` returns `/bar`. buildtarget library(library_name, list_of_sources, ...) ``` -Builds a library that is either static or shared depending on the -value of `default_library` user option. You should use this instead of -[`shared_library`](#shared_library) or -[`static_library`](#static_library) most of the time. This allows you +Builds a library that is either static, shared or both depending on the value of +`default_library` user option. You should use this instead of +[`shared_library`](#shared_library), +[`static_library`](#static_library) or +[`both_libraries`](#both_libraries) most of the time. This allows you to toggle your entire project (including subprojects) from shared to static with only one option. @@ -911,7 +930,8 @@ The keyword arguments for this are the same as for [`executable`](#executable) w libraries. Defaults to `dylib` for shared libraries and `rlib` for static libraries. -`static_library` and `shared_library` also accept these keyword arguments. +`static_library`, `shared_library` and `both_libraries` also accept these keyword +arguments. ### message() @@ -1670,7 +1690,8 @@ These are objects returned by the [functions listed above](#functions). ### `build target` object A build target is either an [executable](#executable), -[shared](#shared_library), [static library](#static_library) or +[shared library](#shared_library), [static library](#static_library), +[both shared and static library](#both_libraries) or [shared module](#shared_module). - `extract_all_objects()` is same as `extract_objects` but returns all diff --git a/docs/markdown/snippets/both-libraries.md b/docs/markdown/snippets/both-libraries.md new file mode 100644 index 0000000..1632f63 --- /dev/null +++ b/docs/markdown/snippets/both-libraries.md @@ -0,0 +1,9 @@ +## Building both shared and static libraries + +A new function `both_libraries()` has been added to build both shared and static +libraries at the same time. Source files will be compiled only once and object +files will be reused to build both shared and static libraries, unless +`b_staticpic` user option or `pic` argument are set to false in which case +sources will be compiled twice. + +The returned `buildtarget` object always represents the shared library. diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 993effc..88d007a 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -422,7 +422,7 @@ builtin_options = { 'werror': [UserBooleanOption, 'Treat warnings as errors.', False], 'warning_level': [UserComboOption, 'Compiler warning level to use.', ['1', '2', '3'], '1'], 'layout': [UserComboOption, 'Build directory layout.', ['mirror', 'flat'], 'mirror'], - 'default_library': [UserComboOption, 'Default library type.', ['shared', 'static'], 'shared'], + 'default_library': [UserComboOption, 'Default library type.', ['shared', 'static', 'both'], 'shared'], 'backend': [UserComboOption, 'Backend to use.', backendlist, 'ninja'], 'stdsplit': [UserBooleanOption, 'Split stdout and stderr in test logs.', True], 'errorlogs': [UserBooleanOption, "Whether to print the logs from failing tests.", True], diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 6bd0b97..88565af 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -608,6 +608,29 @@ class SharedLibraryHolder(BuildTargetHolder): # Set to True only when called from self.func_shared_lib(). target.shared_library_only = False +class BothLibrariesHolder(BuildTargetHolder): + def __init__(self, shared_holder, static_holder, interp): + # FIXME: This build target always represents the shared library, but + # that should be configurable. + super().__init__(shared_holder.held_object, interp) + self.shared_holder = shared_holder + self.static_holder = static_holder + self.methods.update({'get_shared_lib': self.get_shared_lib_method, + 'get_static_lib': self.get_static_lib_method, + }) + + def __repr__(self): + r = '<{} {}: {}, {}: {}>' + h1 = self.shared_holder.held_object + h2 = self.static_holder.held_object + return r.format(self.__class__.__name__, h1.get_id(), h1.filename, h2.get_id(), h2.filename) + + def get_shared_lib_method(self, args, kwargs): + return self.shared_holder + + def get_static_lib_method(self, args, kwargs): + return self.static_holder + class SharedModuleHolder(BuildTargetHolder): def __init__(self, target, interp): super().__init__(target, interp) @@ -1458,6 +1481,7 @@ permitted_kwargs = {'add_global_arguments': {'language'}, 'shared_library': build.known_shlib_kwargs, 'shared_module': build.known_shmod_kwargs, 'static_library': build.known_stlib_kwargs, + 'both_libraries': known_library_kwargs, 'library': known_library_kwargs, 'subdir': {'if_found'}, 'subproject': {'version', 'default_options'}, @@ -1559,6 +1583,7 @@ class Interpreter(InterpreterBase): 'shared_library': self.func_shared_lib, 'shared_module': self.func_shared_module, 'static_library': self.func_static_lib, + 'both_libraries': self.func_both_lib, 'test': self.func_test, 'vcs_tag': self.func_vcs_tag, 'subdir_done': self.func_subdir_done, @@ -2492,6 +2517,10 @@ root and issuing %s. holder.held_object.shared_library_only = True return holder + @permittedKwargs(permitted_kwargs['both_libraries']) + def func_both_lib(self, node, args, kwargs): + return self.build_both_libraries(node, args, kwargs) + @permittedKwargs(permitted_kwargs['shared_module']) def func_shared_module(self, node, args, kwargs): return self.build_target(node, args, kwargs, SharedModuleHolder) @@ -2515,6 +2544,8 @@ root and issuing %s. return self.build_target(node, args, kwargs, SharedLibraryHolder) elif target_type == 'static_library': return self.build_target(node, args, kwargs, StaticLibraryHolder) + elif target_type == 'both_libraries': + return self.build_both_libraries(node, args, kwargs) elif target_type == 'library': return self.build_library(node, args, kwargs) elif target_type == 'jar': @@ -3171,10 +3202,40 @@ different subdirectory. if idname not in self.coredata.target_guids: self.coredata.target_guids[idname] = str(uuid.uuid4()).upper() + def build_both_libraries(self, node, args, kwargs): + shared_holder = self.build_target(node, args, kwargs, SharedLibraryHolder) + + # Check if user forces non-PIC static library. + pic = True + if 'pic' in kwargs: + pic = kwargs['pic'] + elif 'b_staticpic' in self.environment.coredata.base_options: + pic = self.environment.coredata.base_options['b_staticpic'].value + + if pic: + # Exclude sources from args and kwargs to avoid building them twice + static_args = [args[0]] + static_kwargs = kwargs.copy() + static_kwargs['sources'] = [] + static_kwargs['objects'] = shared_holder.held_object.extract_all_objects() + else: + static_args = args + static_kwargs = kwargs + + static_holder = self.build_target(node, static_args, static_kwargs, StaticLibraryHolder) + + return BothLibrariesHolder(shared_holder, static_holder, self) + def build_library(self, node, args, kwargs): - if self.coredata.get_builtin_option('default_library') == 'shared': + default_library = self.coredata.get_builtin_option('default_library') + if default_library == 'shared': return self.build_target(node, args, kwargs, SharedLibraryHolder) - return self.build_target(node, args, kwargs, StaticLibraryHolder) + elif default_library == 'static': + return self.build_target(node, args, kwargs, StaticLibraryHolder) + elif default_library == 'both': + return self.build_both_libraries(node, args, kwargs) + else: + raise InterpreterException('Unknown default_library value: %s.', default_library) def build_target(self, node, args, kwargs, targetholder): if not args: diff --git a/test cases/common/189 bothlibraries/libfile.c b/test cases/common/189 bothlibraries/libfile.c new file mode 100644 index 0000000..085ef3b --- /dev/null +++ b/test cases/common/189 bothlibraries/libfile.c @@ -0,0 +1,7 @@ +#include "mylib.h" + +DO_EXPORT int retval = 42; + +DO_EXPORT int func() { + return retval; +} diff --git a/test cases/common/189 bothlibraries/main.c b/test cases/common/189 bothlibraries/main.c new file mode 100644 index 0000000..03a8e02 --- /dev/null +++ b/test cases/common/189 bothlibraries/main.c @@ -0,0 +1,8 @@ +#include "mylib.h" + +DO_IMPORT int func(); +DO_IMPORT int retval; + +int main(int argc, char **arg) { + return func() == retval ? 0 : 1; +} diff --git a/test cases/common/189 bothlibraries/meson.build b/test cases/common/189 bothlibraries/meson.build new file mode 100644 index 0000000..3a13d62 --- /dev/null +++ b/test cases/common/189 bothlibraries/meson.build @@ -0,0 +1,12 @@ +project('both libraries linking test', 'c') + +both_libs = both_libraries('mylib', 'libfile.c') +exe_shared = executable('prog-shared', 'main.c', link_with : both_libs.get_shared_lib()) +exe_static = executable('prog-static', 'main.c', + c_args : ['-DSTATIC_COMPILATION'], + link_with : both_libs.get_static_lib()) +exe_both = executable('prog-both', 'main.c', link_with : both_libs) + +test('runtest-shared', exe_shared) +test('runtest-static', exe_static) +test('runtest-both', exe_both) diff --git a/test cases/common/189 bothlibraries/mylib.h b/test cases/common/189 bothlibraries/mylib.h new file mode 100644 index 0000000..1038a01 --- /dev/null +++ b/test cases/common/189 bothlibraries/mylib.h @@ -0,0 +1,13 @@ +#pragma once + +#ifdef _WIN32 + #ifdef STATIC_COMPILATION + #define DO_IMPORT extern + #else + #define DO_IMPORT __declspec(dllimport) + #endif + #define DO_EXPORT __declspec(dllexport) +#else + #define DO_IMPORT extern + #define DO_EXPORT +#endif |