Merge pull request #2711 from xclaesse/both-library

Add both_library() to build both shared and static library
pull/3361/head
Jussi Pakkanen 7 years ago committed by GitHub
commit aef1a81b35
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 33
      docs/markdown/Reference-manual.md
  2. 9
      docs/markdown/snippets/both-libraries.md
  3. 12
      mesonbuild/backend/backends.py
  4. 183
      mesonbuild/build.py
  5. 2
      mesonbuild/coredata.py
  6. 183
      mesonbuild/interpreter.py
  7. 22
      mesonbuild/modules/pkgconfig.py
  8. 7
      mesonbuild/modules/python3.py
  9. 7
      test cases/common/189 bothlibraries/libfile.c
  10. 8
      test cases/common/189 bothlibraries/main.c
  11. 12
      test cases/common/189 bothlibraries/meson.build
  12. 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.

@ -22,6 +22,7 @@ import json
import subprocess import subprocess
from ..mesonlib import MesonException from ..mesonlib import MesonException
from ..mesonlib import get_compiler_for_source, classify_unity_sources from ..mesonlib import get_compiler_for_source, classify_unity_sources
from ..mesonlib import File
from ..compilers import CompilerArgs from ..compilers import CompilerArgs
from collections import OrderedDict from collections import OrderedDict
import shlex import shlex
@ -414,11 +415,20 @@ class Backend:
objname = objname.replace('/', '_').replace('\\', '_') objname = objname.replace('/', '_').replace('\\', '_')
objpath = os.path.join(proj_dir_to_build_root, targetdir, objname) objpath = os.path.join(proj_dir_to_build_root, targetdir, objname)
return [objpath] return [objpath]
for osrc in extobj.srclist:
sources = list(extobj.srclist)
for gensrc in extobj.genlist:
for s in gensrc.get_outputs():
path = self.get_target_generated_dir(extobj.target, gensrc, s)
dirpart, fnamepart = os.path.split(path)
sources.append(File(True, dirpart, fnamepart))
for osrc in sources:
objname = self.object_filename_from_source(extobj.target, osrc, False) objname = self.object_filename_from_source(extobj.target, osrc, False)
if objname: if objname:
objpath = os.path.join(proj_dir_to_build_root, targetdir, objname) objpath = os.path.join(proj_dir_to_build_root, targetdir, objname)
result.append(objpath) result.append(objpath)
return result return result
def get_pch_include_args(self, compiler, target): def get_pch_include_args(self, compiler, target):

@ -26,67 +26,64 @@ from .mesonlib import get_filenames_templates_dict, substitute_values
from .mesonlib import for_windows, for_darwin, for_cygwin, for_android, has_path_sep from .mesonlib import for_windows, for_darwin, for_cygwin, for_android, has_path_sep
from .compilers import is_object, clike_langs, sort_clike, lang_suffixes from .compilers import is_object, clike_langs, sort_clike, lang_suffixes
known_basic_kwargs = {'install': True, pch_kwargs = set(['c_pch', 'cpp_pch'])
'c_pch': True,
'cpp_pch': True, lang_arg_kwargs = set([
'c_args': True, 'c_args',
'objc_args': True, 'cpp_args',
'objcpp_args': True, 'd_args',
'cpp_args': True, 'd_import_dirs',
'cs_args': True, 'd_unittest',
'vala_args': True, 'd_module_versions',
'fortran_args': True, 'fortran_args',
'd_args': True, 'java_args',
'd_import_dirs': True, 'objc_args',
'd_unittest': True, 'objcpp_args',
'd_module_versions': True, 'rust_args',
'java_args': True, 'vala_args',
'rust_args': True, 'cs_args',
'link_args': True, ])
'link_depends': True,
'link_with': True, vala_kwargs = set(['vala_header', 'vala_gir', 'vala_vapi'])
'link_whole': True, rust_kwargs = set(['rust_crate_type'])
'implicit_include_directories': True, cs_kwargs = set(['resources', 'cs_args'])
'include_directories': True,
'dependencies': True, buildtarget_kwargs = set([
'install_dir': True, 'build_by_default',
'main_class': True, 'build_rpath',
'name_suffix': True, 'dependencies',
'gui_app': True, 'extra_files',
'extra_files': True, 'gui_app',
'install_rpath': True, 'link_with',
'build_rpath': True, 'link_whole',
'resources': True, 'link_args',
'sources': True, 'link_depends',
'objects': True, 'implicit_include_directories',
'native': True, 'include_directories',
'build_by_default': True, 'install',
'override_options': True, 'install_rpath',
} 'install_dir',
'name_prefix',
# These contain kwargs supported by both static and shared libraries. These are 'name_suffix',
# combined here because a library() call might be shared_library() or 'native',
# static_library() at runtime based on the configuration. 'objects',
# FIXME: Find a way to pass that info down here so we can have proper target 'override_options',
# kwargs checking when specifically using shared_library() or static_library(). 'sources',
known_lib_kwargs = known_basic_kwargs.copy() ])
known_lib_kwargs.update({'version': True, # Only for shared libs
'soversion': True, # Only for shared libs known_build_target_kwargs = (
'name_prefix': True, buildtarget_kwargs |
'vs_module_defs': True, # Only for shared libs lang_arg_kwargs |
'vala_header': True, pch_kwargs |
'vala_vapi': True, vala_kwargs |
'vala_gir': True, rust_kwargs |
'pic': True, # Only for static libs cs_kwargs)
'rust_crate_type': True, # Only for Rust libs
}) known_exe_kwargs = known_build_target_kwargs | {'implib', 'export_dynamic'}
known_shlib_kwargs = known_build_target_kwargs | {'version', 'soversion', 'vs_module_defs'}
known_exe_kwargs = known_basic_kwargs.copy() known_shmod_kwargs = known_build_target_kwargs
known_exe_kwargs.update({'implib': True, known_stlib_kwargs = known_build_target_kwargs | {'pic'}
'export_dynamic': True known_jar_kwargs = known_exe_kwargs | {'main_class'}
})
known_jar_kwargs = known_basic_kwargs.copy()
known_jar_kwargs.update({'target_type': 'jar'})
class InvalidArguments(MesonException): class InvalidArguments(MesonException):
pass pass
@ -214,9 +211,10 @@ class ExtractedObjects:
''' '''
Holds a list of sources for which the objects must be extracted Holds a list of sources for which the objects must be extracted
''' '''
def __init__(self, target, srclist, is_unity): def __init__(self, target, srclist, genlist, is_unity):
self.target = target self.target = target
self.srclist = srclist self.srclist = srclist
self.genlist = genlist
if is_unity: if is_unity:
self.check_unity_compatible() self.check_unity_compatible()
@ -337,6 +335,8 @@ a hard error in the future.''' % name)
class BuildTarget(Target): class BuildTarget(Target):
known_kwargs = known_build_target_kwargs
def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs): def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs):
super().__init__(name, subdir, subproject, True) super().__init__(name, subdir, subproject, True)
self.is_cross = is_cross self.is_cross = is_cross
@ -396,7 +396,7 @@ class BuildTarget(Target):
def check_unknown_kwargs(self, kwargs): def check_unknown_kwargs(self, kwargs):
# Override this method in derived classes that have more # Override this method in derived classes that have more
# keywords. # keywords.
self.check_unknown_kwargs_int(kwargs, known_basic_kwargs) self.check_unknown_kwargs_int(kwargs, self.known_kwargs)
def check_unknown_kwargs_int(self, kwargs, known_kwargs): def check_unknown_kwargs_int(self, kwargs, known_kwargs):
unknowns = [] unknowns = []
@ -626,13 +626,17 @@ class BuildTarget(Target):
if not isinstance(src, str): if not isinstance(src, str):
raise MesonException('Object extraction arguments must be strings.') raise MesonException('Object extraction arguments must be strings.')
src = File(False, self.subdir, src) src = File(False, self.subdir, src)
# FIXME: It could be a generated source
if src not in self.sources: if src not in self.sources:
raise MesonException('Tried to extract unknown source %s.' % src) raise MesonException('Tried to extract unknown source %s.' % src)
obj_src.append(src) obj_src.append(src)
return ExtractedObjects(self, obj_src, self.is_unity) return ExtractedObjects(self, obj_src, [], self.is_unity)
def extract_all_objects(self): def extract_all_objects(self):
return ExtractedObjects(self, self.sources, self.is_unity) # FIXME: We should add support for transitive extract_objects()
if self.objects:
raise MesonException('Cannot extract objects from a target that itself has extracted objects')
return ExtractedObjects(self, self.sources, self.generated, self.is_unity)
def get_all_link_deps(self): def get_all_link_deps(self):
return self.get_transitive_link_deps() return self.get_transitive_link_deps()
@ -1184,6 +1188,8 @@ class GeneratedList:
return self.extra_args return self.extra_args
class Executable(BuildTarget): class Executable(BuildTarget):
known_kwargs = known_exe_kwargs
def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs): def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs):
super().__init__(name, subdir, subproject, is_cross, sources, objects, environment, kwargs) super().__init__(name, subdir, subproject, is_cross, sources, objects, environment, kwargs)
# Unless overridden, executables have no suffix or prefix. Except on # Unless overridden, executables have no suffix or prefix. Except on
@ -1239,9 +1245,6 @@ class Executable(BuildTarget):
def type_suffix(self): def type_suffix(self):
return "@exe" return "@exe"
def check_unknown_kwargs(self, kwargs):
self.check_unknown_kwargs_int(kwargs, known_exe_kwargs)
def get_import_filename(self): def get_import_filename(self):
""" """
The name of the import library that will be outputted by the compiler The name of the import library that will be outputted by the compiler
@ -1259,6 +1262,8 @@ class Executable(BuildTarget):
return self.is_linkwithable return self.is_linkwithable
class StaticLibrary(BuildTarget): class StaticLibrary(BuildTarget):
known_kwargs = known_stlib_kwargs
def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs): def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs):
if 'pic' not in kwargs and 'b_staticpic' in environment.coredata.base_options: if 'pic' not in kwargs and 'b_staticpic' in environment.coredata.base_options:
kwargs['pic'] = environment.coredata.base_options['b_staticpic'].value kwargs['pic'] = environment.coredata.base_options['b_staticpic'].value
@ -1297,9 +1302,6 @@ class StaticLibrary(BuildTarget):
def type_suffix(self): def type_suffix(self):
return "@sta" return "@sta"
def check_unknown_kwargs(self, kwargs):
self.check_unknown_kwargs_int(kwargs, known_lib_kwargs)
def process_kwargs(self, kwargs, environment): def process_kwargs(self, kwargs, environment):
super().process_kwargs(kwargs, environment) super().process_kwargs(kwargs, environment)
if 'rust_crate_type' in kwargs: if 'rust_crate_type' in kwargs:
@ -1313,6 +1315,8 @@ class StaticLibrary(BuildTarget):
return True return True
class SharedLibrary(BuildTarget): class SharedLibrary(BuildTarget):
known_kwargs = known_shlib_kwargs
def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs): def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs):
self.soversion = None self.soversion = None
self.ltversion = None self.ltversion = None
@ -1501,9 +1505,6 @@ class SharedLibrary(BuildTarget):
else: else:
raise InvalidArguments('Invalid rust_crate_type "{0}": must be a string.'.format(rust_crate_type)) raise InvalidArguments('Invalid rust_crate_type "{0}": must be a string.'.format(rust_crate_type))
def check_unknown_kwargs(self, kwargs):
self.check_unknown_kwargs_int(kwargs, known_lib_kwargs)
def get_import_filename(self): def get_import_filename(self):
""" """
The name of the import library that will be outputted by the compiler The name of the import library that will be outputted by the compiler
@ -1559,6 +1560,8 @@ class SharedLibrary(BuildTarget):
# A shared library that is meant to be used with dlopen rather than linking # A shared library that is meant to be used with dlopen rather than linking
# into something else. # into something else.
class SharedModule(SharedLibrary): class SharedModule(SharedLibrary):
known_kwargs = known_shmod_kwargs
def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs): def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs):
if 'version' in kwargs: if 'version' in kwargs:
raise MesonException('Shared modules must not specify the version kwarg.') raise MesonException('Shared modules must not specify the version kwarg.')
@ -1568,19 +1571,20 @@ class SharedModule(SharedLibrary):
self.import_filename = None self.import_filename = None
class CustomTarget(Target): class CustomTarget(Target):
known_kwargs = {'input': True, known_kwargs = set([
'output': True, 'input',
'command': True, 'output',
'capture': False, 'command',
'install': True, 'capture',
'install_dir': True, 'install',
'build_always': True, 'install_dir',
'depends': True, 'build_always',
'depend_files': True, 'depends',
'depfile': True, 'depend_files',
'build_by_default': True, 'depfile',
'override_options': True, 'build_by_default',
} 'override_options',
])
def __init__(self, name, subdir, subproject, kwargs, absolute_paths=False): def __init__(self, name, subdir, subproject, kwargs, absolute_paths=False):
super().__init__(name, subdir, subproject, False) super().__init__(name, subdir, subproject, False)
@ -1814,6 +1818,8 @@ class RunTarget(Target):
return "@run" return "@run"
class Jar(BuildTarget): class Jar(BuildTarget):
known_kwargs = known_jar_kwargs
def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs): def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs):
super().__init__(name, subdir, subproject, is_cross, sources, objects, environment, kwargs) super().__init__(name, subdir, subproject, is_cross, sources, objects, environment, kwargs)
for s in self.sources: for s in self.sources:
@ -1836,9 +1842,6 @@ class Jar(BuildTarget):
# All jar targets are installable. # All jar targets are installable.
pass pass
def check_unknown_kwargs(self, kwargs):
self.check_unknown_kwargs_int(kwargs, known_jar_kwargs)
class CustomTargetIndex: class CustomTargetIndex:
"""A special opaque object returned by indexing a CustomTarget. This object """A special opaque object returned by indexing a CustomTarget. This object

@ -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],

@ -605,6 +605,31 @@ class StaticLibraryHolder(BuildTargetHolder):
class SharedLibraryHolder(BuildTargetHolder): class SharedLibraryHolder(BuildTargetHolder):
def __init__(self, target, interp): def __init__(self, target, interp):
super().__init__(target, interp) super().__init__(target, interp)
# 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): class SharedModuleHolder(BuildTargetHolder):
def __init__(self, target, interp): def __init__(self, target, interp):
@ -1418,71 +1443,17 @@ class MesonMain(InterpreterObject):
raise InterpreterException('Unknown cross property: %s.' % propname) raise InterpreterException('Unknown cross property: %s.' % propname)
pch_kwargs = set(['c_pch', 'cpp_pch']) known_library_kwargs = (
build.known_shlib_kwargs |
lang_arg_kwargs = set([ build.known_stlib_kwargs
'c_args', )
'cpp_args',
'd_args', known_build_target_kwargs = (
'd_import_dirs', known_library_kwargs |
'd_unittest', build.known_exe_kwargs |
'd_module_versions', build.known_jar_kwargs |
'fortran_args', {'target_type'}
'java_args', )
'objc_args',
'objcpp_args',
'rust_args',
'vala_args',
'cs_args',
])
vala_kwargs = set(['vala_header', 'vala_gir', 'vala_vapi'])
rust_kwargs = set(['rust_crate_type'])
cs_kwargs = set(['resources', 'cs_args'])
buildtarget_kwargs = set([
'build_by_default',
'build_rpath',
'dependencies',
'extra_files',
'gui_app',
'link_with',
'link_whole',
'link_args',
'link_depends',
'implicit_include_directories',
'include_directories',
'install',
'install_rpath',
'install_dir',
'name_prefix',
'name_suffix',
'native',
'objects',
'override_options',
'pic',
'sources',
'vs_module_defs',
])
build_target_common_kwargs = (
buildtarget_kwargs |
lang_arg_kwargs |
pch_kwargs |
vala_kwargs |
rust_kwargs |
cs_kwargs)
exe_kwargs = (build_target_common_kwargs) | {'implib', 'export_dynamic'}
shlib_kwargs = (build_target_common_kwargs) | {'version', 'soversion'}
shmod_kwargs = shlib_kwargs
stlib_kwargs = shlib_kwargs
jar_kwargs = exe_kwargs.copy()
jar_kwargs.update(['main_class'])
build_target_kwargs = exe_kwargs.copy()
build_target_kwargs.update(['target_type'])
permitted_kwargs = {'add_global_arguments': {'language'}, permitted_kwargs = {'add_global_arguments': {'language'},
'add_global_link_arguments': {'language'}, 'add_global_link_arguments': {'language'},
@ -1491,12 +1462,12 @@ permitted_kwargs = {'add_global_arguments': {'language'},
'add_project_arguments': {'language'}, 'add_project_arguments': {'language'},
'add_test_setup': {'exe_wrapper', 'gdb', 'timeout_multiplier', 'env'}, 'add_test_setup': {'exe_wrapper', 'gdb', 'timeout_multiplier', 'env'},
'benchmark': {'args', 'env', 'should_fail', 'timeout', 'workdir', 'suite'}, 'benchmark': {'args', 'env', 'should_fail', 'timeout', 'workdir', 'suite'},
'build_target': build_target_kwargs, 'build_target': known_build_target_kwargs,
'configure_file': {'input', 'output', 'configuration', 'command', 'install_dir', 'capture', 'install'}, 'configure_file': {'input', 'output', 'configuration', 'command', 'install_dir', 'capture', 'install'},
'custom_target': {'input', 'output', 'command', 'install', 'install_dir', 'build_always', 'capture', 'depends', 'depend_files', 'depfile', 'build_by_default'}, 'custom_target': {'input', 'output', 'command', 'install', 'install_dir', 'build_always', 'capture', 'depends', 'depend_files', 'depfile', 'build_by_default'},
'dependency': {'default_options', 'fallback', 'language', 'main', 'method', 'modules', 'optional_modules', 'native', 'required', 'static', 'version'}, 'dependency': {'default_options', 'fallback', 'language', 'main', 'method', 'modules', 'optional_modules', 'native', 'required', 'static', 'version'},
'declare_dependency': {'include_directories', 'link_with', 'sources', 'dependencies', 'compile_args', 'link_args', 'link_whole', 'version'}, 'declare_dependency': {'include_directories', 'link_with', 'sources', 'dependencies', 'compile_args', 'link_args', 'link_whole', 'version'},
'executable': exe_kwargs, 'executable': build.known_exe_kwargs,
'find_program': {'required', 'native'}, 'find_program': {'required', 'native'},
'generator': {'arguments', 'output', 'depfile', 'capture', 'preserve_path_from'}, 'generator': {'arguments', 'output', 'depfile', 'capture', 'preserve_path_from'},
'include_directories': {'is_system'}, 'include_directories': {'is_system'},
@ -1504,12 +1475,14 @@ permitted_kwargs = {'add_global_arguments': {'language'},
'install_headers': {'install_dir', 'subdir'}, 'install_headers': {'install_dir', 'subdir'},
'install_man': {'install_dir'}, 'install_man': {'install_dir'},
'install_subdir': {'exclude_files', 'exclude_directories', 'install_dir', 'install_mode', 'strip_directory'}, 'install_subdir': {'exclude_files', 'exclude_directories', 'install_dir', 'install_mode', 'strip_directory'},
'jar': jar_kwargs, 'jar': build.known_jar_kwargs,
'project': {'version', 'meson_version', 'default_options', 'license', 'subproject_dir'}, 'project': {'version', 'meson_version', 'default_options', 'license', 'subproject_dir'},
'run_target': {'command', 'depends'}, 'run_target': {'command', 'depends'},
'shared_library': shlib_kwargs, 'shared_library': build.known_shlib_kwargs,
'shared_module': shmod_kwargs, 'shared_module': build.known_shmod_kwargs,
'static_library': stlib_kwargs, 'static_library': build.known_stlib_kwargs,
'both_libraries': known_library_kwargs,
'library': known_library_kwargs,
'subdir': {'if_found'}, 'subdir': {'if_found'},
'subproject': {'version', 'default_options'}, 'subproject': {'version', 'default_options'},
'test': {'args', 'env', 'is_parallel', 'should_fail', 'timeout', 'workdir', 'suite'}, 'test': {'args', 'env', 'is_parallel', 'should_fail', 'timeout', 'workdir', 'suite'},
@ -1610,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,
@ -2539,20 +2513,24 @@ root and issuing %s.
@permittedKwargs(permitted_kwargs['shared_library']) @permittedKwargs(permitted_kwargs['shared_library'])
def func_shared_lib(self, node, args, kwargs): def func_shared_lib(self, node, args, kwargs):
return self.build_target(node, args, kwargs, SharedLibraryHolder) holder = self.build_target(node, args, kwargs, SharedLibraryHolder)
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']) @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)
@permittedKwargs(permitted_kwargs['library'])
def func_library(self, node, args, kwargs): def func_library(self, node, args, kwargs):
if self.coredata.get_builtin_option('default_library') == 'shared': return self.build_library(node, args, kwargs)
return self.func_shared_lib(node, args, kwargs)
return self.func_static_lib(node, args, kwargs)
@permittedKwargs(permitted_kwargs['jar']) @permittedKwargs(permitted_kwargs['jar'])
def func_jar(self, node, args, kwargs): def func_jar(self, node, args, kwargs):
kwargs['target_type'] = 'jar'
return self.build_target(node, args, kwargs, JarHolder) return self.build_target(node, args, kwargs, JarHolder)
@permittedKwargs(permitted_kwargs['build_target']) @permittedKwargs(permitted_kwargs['build_target'])
@ -2561,15 +2539,17 @@ root and issuing %s.
raise InterpreterException('Missing target_type keyword argument') raise InterpreterException('Missing target_type keyword argument')
target_type = kwargs.pop('target_type') target_type = kwargs.pop('target_type')
if target_type == 'executable': if target_type == 'executable':
return self.func_executable(node, args, kwargs) return self.build_target(node, args, kwargs, ExecutableHolder)
elif target_type == 'shared_library': elif target_type == 'shared_library':
return self.func_shared_lib(node, args, kwargs) return self.build_target(node, args, kwargs, SharedLibraryHolder)
elif target_type == 'static_library': elif target_type == 'static_library':
return self.func_static_lib(node, args, kwargs) 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.func_library(node, args, kwargs) return self.build_library(node, args, kwargs)
elif target_type == 'jar': elif target_type == 'jar':
return self.func_jar(node, args, kwargs) return self.build_target(node, args, kwargs, JarHolder)
else: else:
raise InterpreterException('Unknown target_type.') raise InterpreterException('Unknown target_type.')
@ -3222,6 +3202,41 @@ 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):
default_library = self.coredata.get_builtin_option('default_library')
if default_library == 'shared':
return self.build_target(node, args, kwargs, SharedLibraryHolder)
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): def build_target(self, node, args, kwargs, targetholder):
if not args: if not args:
raise InterpreterException('Target does not have a name.') raise InterpreterException('Target does not have a name.')
@ -3257,7 +3272,13 @@ different subdirectory.
mlog.debug('Unknown target type:', str(targetholder)) mlog.debug('Unknown target type:', str(targetholder))
raise RuntimeError('Unreachable code') raise RuntimeError('Unreachable code')
self.kwarg_strings_to_includedirs(kwargs) self.kwarg_strings_to_includedirs(kwargs)
# Filter out kwargs from other target types. For example 'soversion'
# passed to library() when default_library == 'static'.
kwargs = {k: v for k, v in kwargs.items() if k in targetclass.known_kwargs}
target = targetclass(name, self.subdir, self.subproject, is_cross, sources, objs, self.environment, kwargs) target = targetclass(name, self.subdir, self.subproject, is_cross, sources, objs, self.environment, kwargs)
if is_cross: if is_cross:
self.add_cross_stdlib_info(target) self.add_cross_stdlib_info(target)
l = targetholder(target, self) l = targetholder(target, self)

@ -105,26 +105,24 @@ class DependenciesHelper:
if obj.found(): if obj.found():
processed_libs += obj.get_link_args() processed_libs += obj.get_link_args()
processed_cflags += obj.get_compile_args() processed_cflags += obj.get_compile_args()
elif isinstance(obj, build.SharedLibrary): elif isinstance(obj, build.SharedLibrary) and obj.shared_library_only:
# Do not pull dependencies for shared libraries because they are
# only required for static linking. Adding private requires has
# the side effect of exposing their cflags, which is the
# intended behaviour of pkg-config but force Debian to add more
# than needed build deps.
# See https://bugs.freedesktop.org/show_bug.cgi?id=105572
processed_libs.append(obj) processed_libs.append(obj)
if public: if public:
if not hasattr(obj, 'generated_pc'): if not hasattr(obj, 'generated_pc'):
obj.generated_pc = self.name obj.generated_pc = self.name
elif isinstance(obj, build.StaticLibrary): elif isinstance(obj, (build.SharedLibrary, build.StaticLibrary)):
# Due to a "feature" in pkgconfig, it leaks out private dependencies.
# Thus we will not add them to the pc file unless the target
# we are processing is a static library.
#
# This way (hopefully) "pkgconfig --libs --static foobar" works
# and "pkgconfig --cflags/--libs foobar" does not have any trace
# of dependencies that the build file creator has not explicitly
# added to the dependency list.
processed_libs.append(obj) processed_libs.append(obj)
self.add_priv_libs(obj.get_dependencies())
self.add_priv_libs(obj.get_external_deps())
if public: if public:
if not hasattr(obj, 'generated_pc'): if not hasattr(obj, 'generated_pc'):
obj.generated_pc = self.name obj.generated_pc = self.name
self.add_priv_libs(obj.get_dependencies())
self.add_priv_libs(obj.get_external_deps())
elif isinstance(obj, str): elif isinstance(obj, str):
processed_libs.append(obj) processed_libs.append(obj)
else: else:

@ -19,10 +19,7 @@ from . import ExtensionModule
from mesonbuild.modules import ModuleReturnValue from mesonbuild.modules import ModuleReturnValue
from . import permittedSnippetKwargs from . import permittedSnippetKwargs
from ..interpreterbase import noKwargs from ..interpreterbase import noKwargs
from ..interpreter import shlib_kwargs from ..build import known_shmod_kwargs
mod_kwargs = set()
mod_kwargs.update(shlib_kwargs)
class Python3Module(ExtensionModule): class Python3Module(ExtensionModule):
@ -30,7 +27,7 @@ class Python3Module(ExtensionModule):
super().__init__() super().__init__()
self.snippets.add('extension_module') self.snippets.add('extension_module')
@permittedSnippetKwargs(mod_kwargs) @permittedSnippetKwargs(known_shmod_kwargs)
def extension_module(self, interpreter, state, args, kwargs): def extension_module(self, interpreter, state, args, kwargs):
if 'name_prefix' in kwargs: if 'name_prefix' in kwargs:
raise mesonlib.MesonException('Name_prefix is set automatically, specifying it is forbidden.') raise mesonlib.MesonException('Name_prefix is set automatically, specifying it is forbidden.')

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