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
pull/2711/head
Xavier Claessens 7 years ago
parent 809f018333
commit 68f9846b7c
  1. 33
      docs/markdown/Reference-manual.md
  2. 9
      docs/markdown/snippets/both-libraries.md
  3. 2
      mesonbuild/coredata.py
  4. 63
      mesonbuild/interpreter.py
  5. 7
      test cases/common/189 bothlibraries/libfile.c
  6. 8
      test cases/common/189 bothlibraries/main.c
  7. 12
      test cases/common/189 bothlibraries/meson.build
  8. 13
      test cases/common/189 bothlibraries/mylib.h

@ -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 exception that there is no `is_parallel` keyword, because benchmarks
are never run in parallel. 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() ### build_target()
Creates a build target whose type can be set dynamically with the 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, ...) buildtarget library(library_name, list_of_sources, ...)
``` ```
Builds a library that is either static or shared depending on the Builds a library that is either static, shared or both depending on the value of
value of `default_library` user option. You should use this instead of `default_library` user option. You should use this instead of
[`shared_library`](#shared_library) or [`shared_library`](#shared_library),
[`static_library`](#static_library) most of the time. This allows you [`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 to toggle your entire project (including subprojects) from shared to
static with only one option. 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 libraries. Defaults to `dylib` for shared libraries and `rlib` for
static libraries. 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() ### message()
@ -1670,7 +1690,8 @@ These are objects returned by the [functions listed above](#functions).
### `build target` object ### `build target` object
A build target is either an [executable](#executable), 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). [shared module](#shared_module).
- `extract_all_objects()` is same as `extract_objects` but returns all - `extract_all_objects()` is same as `extract_objects` but returns all

@ -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.

@ -422,7 +422,7 @@ builtin_options = {
'werror': [UserBooleanOption, 'Treat warnings as errors.', False], 'werror': [UserBooleanOption, 'Treat warnings as errors.', False],
'warning_level': [UserComboOption, 'Compiler warning level to use.', ['1', '2', '3'], '1'], 'warning_level': [UserComboOption, 'Compiler warning level to use.', ['1', '2', '3'], '1'],
'layout': [UserComboOption, 'Build directory layout.', ['mirror', 'flat'], 'mirror'], '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'], 'backend': [UserComboOption, 'Backend to use.', backendlist, 'ninja'],
'stdsplit': [UserBooleanOption, 'Split stdout and stderr in test logs.', True], 'stdsplit': [UserBooleanOption, 'Split stdout and stderr in test logs.', True],
'errorlogs': [UserBooleanOption, "Whether to print the logs from failing tests.", True], 'errorlogs': [UserBooleanOption, "Whether to print the logs from failing tests.", True],

@ -608,6 +608,29 @@ class SharedLibraryHolder(BuildTargetHolder):
# Set to True only when called from self.func_shared_lib(). # Set to True only when called from self.func_shared_lib().
target.shared_library_only = False 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): class SharedModuleHolder(BuildTargetHolder):
def __init__(self, target, interp): def __init__(self, target, interp):
super().__init__(target, interp) super().__init__(target, interp)
@ -1458,6 +1481,7 @@ permitted_kwargs = {'add_global_arguments': {'language'},
'shared_library': build.known_shlib_kwargs, 'shared_library': build.known_shlib_kwargs,
'shared_module': build.known_shmod_kwargs, 'shared_module': build.known_shmod_kwargs,
'static_library': build.known_stlib_kwargs, 'static_library': build.known_stlib_kwargs,
'both_libraries': known_library_kwargs,
'library': known_library_kwargs, 'library': known_library_kwargs,
'subdir': {'if_found'}, 'subdir': {'if_found'},
'subproject': {'version', 'default_options'}, 'subproject': {'version', 'default_options'},
@ -1559,6 +1583,7 @@ class Interpreter(InterpreterBase):
'shared_library': self.func_shared_lib, 'shared_library': self.func_shared_lib,
'shared_module': self.func_shared_module, 'shared_module': self.func_shared_module,
'static_library': self.func_static_lib, 'static_library': self.func_static_lib,
'both_libraries': self.func_both_lib,
'test': self.func_test, 'test': self.func_test,
'vcs_tag': self.func_vcs_tag, 'vcs_tag': self.func_vcs_tag,
'subdir_done': self.func_subdir_done, 'subdir_done': self.func_subdir_done,
@ -2492,6 +2517,10 @@ root and issuing %s.
holder.held_object.shared_library_only = True holder.held_object.shared_library_only = True
return holder 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']) @permittedKwargs(permitted_kwargs['shared_module'])
def func_shared_module(self, node, args, kwargs): def func_shared_module(self, node, args, kwargs):
return self.build_target(node, args, kwargs, SharedModuleHolder) return self.build_target(node, args, kwargs, SharedModuleHolder)
@ -2515,6 +2544,8 @@ root and issuing %s.
return self.build_target(node, args, kwargs, SharedLibraryHolder) return self.build_target(node, args, kwargs, SharedLibraryHolder)
elif target_type == 'static_library': elif target_type == 'static_library':
return self.build_target(node, args, kwargs, StaticLibraryHolder) 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': elif target_type == 'library':
return self.build_library(node, args, kwargs) return self.build_library(node, args, kwargs)
elif target_type == 'jar': elif target_type == 'jar':
@ -3171,10 +3202,40 @@ different subdirectory.
if idname not in self.coredata.target_guids: if idname not in self.coredata.target_guids:
self.coredata.target_guids[idname] = str(uuid.uuid4()).upper() 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): 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, SharedLibraryHolder)
elif default_library == 'static':
return self.build_target(node, args, kwargs, StaticLibraryHolder) 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): def build_target(self, node, args, kwargs, targetholder):
if not args: if not args:

@ -0,0 +1,7 @@
#include "mylib.h"
DO_EXPORT int retval = 42;
DO_EXPORT int func() {
return retval;
}

@ -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;
}

@ -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)

@ -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
Loading…
Cancel
Save