diff options
-rw-r--r-- | docs/markdown/Reference-manual.md | 33 | ||||
-rw-r--r-- | docs/markdown/snippets/both-libraries.md | 9 | ||||
-rw-r--r-- | mesonbuild/backend/backends.py | 12 | ||||
-rw-r--r-- | mesonbuild/build.py | 183 | ||||
-rw-r--r-- | mesonbuild/coredata.py | 2 | ||||
-rw-r--r-- | mesonbuild/interpreter.py | 183 | ||||
-rw-r--r-- | mesonbuild/modules/pkgconfig.py | 22 | ||||
-rw-r--r-- | mesonbuild/modules/python3.py | 7 | ||||
-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 |
12 files changed, 295 insertions, 196 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/backend/backends.py b/mesonbuild/backend/backends.py index ad45204..916f680 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -22,6 +22,7 @@ import json import subprocess from ..mesonlib import MesonException from ..mesonlib import get_compiler_for_source, classify_unity_sources +from ..mesonlib import File from ..compilers import CompilerArgs from collections import OrderedDict import shlex @@ -414,11 +415,20 @@ class Backend: objname = objname.replace('/', '_').replace('\\', '_') objpath = os.path.join(proj_dir_to_build_root, targetdir, objname) 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) if objname: objpath = os.path.join(proj_dir_to_build_root, targetdir, objname) result.append(objpath) + return result def get_pch_include_args(self, compiler, target): diff --git a/mesonbuild/build.py b/mesonbuild/build.py index aca592e..08e0c9d 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -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 .compilers import is_object, clike_langs, sort_clike, lang_suffixes -known_basic_kwargs = {'install': True, - 'c_pch': True, - 'cpp_pch': True, - 'c_args': True, - 'objc_args': True, - 'objcpp_args': True, - 'cpp_args': True, - 'cs_args': True, - 'vala_args': True, - 'fortran_args': True, - 'd_args': True, - 'd_import_dirs': True, - 'd_unittest': True, - 'd_module_versions': True, - 'java_args': True, - 'rust_args': True, - 'link_args': True, - 'link_depends': True, - 'link_with': True, - 'link_whole': True, - 'implicit_include_directories': True, - 'include_directories': True, - 'dependencies': True, - 'install_dir': True, - 'main_class': True, - 'name_suffix': True, - 'gui_app': True, - 'extra_files': True, - 'install_rpath': True, - 'build_rpath': True, - 'resources': True, - 'sources': True, - 'objects': True, - 'native': True, - 'build_by_default': True, - 'override_options': True, - } - -# These contain kwargs supported by both static and shared libraries. These are -# combined here because a library() call might be shared_library() or -# static_library() at runtime based on the configuration. -# FIXME: Find a way to pass that info down here so we can have proper target -# kwargs checking when specifically using shared_library() or static_library(). -known_lib_kwargs = known_basic_kwargs.copy() -known_lib_kwargs.update({'version': True, # Only for shared libs - 'soversion': True, # Only for shared libs - 'name_prefix': True, - 'vs_module_defs': True, # Only for shared libs - 'vala_header': True, - 'vala_vapi': True, - 'vala_gir': True, - 'pic': True, # Only for static libs - 'rust_crate_type': True, # Only for Rust libs - }) - -known_exe_kwargs = known_basic_kwargs.copy() -known_exe_kwargs.update({'implib': True, - 'export_dynamic': True - }) -known_jar_kwargs = known_basic_kwargs.copy() -known_jar_kwargs.update({'target_type': 'jar'}) +pch_kwargs = set(['c_pch', 'cpp_pch']) + +lang_arg_kwargs = set([ + 'c_args', + 'cpp_args', + 'd_args', + 'd_import_dirs', + 'd_unittest', + 'd_module_versions', + 'fortran_args', + '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', + 'sources', +]) + +known_build_target_kwargs = ( + buildtarget_kwargs | + lang_arg_kwargs | + pch_kwargs | + vala_kwargs | + rust_kwargs | + cs_kwargs) + +known_exe_kwargs = known_build_target_kwargs | {'implib', 'export_dynamic'} +known_shlib_kwargs = known_build_target_kwargs | {'version', 'soversion', 'vs_module_defs'} +known_shmod_kwargs = known_build_target_kwargs +known_stlib_kwargs = known_build_target_kwargs | {'pic'} +known_jar_kwargs = known_exe_kwargs | {'main_class'} class InvalidArguments(MesonException): pass @@ -214,9 +211,10 @@ class ExtractedObjects: ''' 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.srclist = srclist + self.genlist = genlist if is_unity: self.check_unity_compatible() @@ -337,6 +335,8 @@ a hard error in the future.''' % name) class BuildTarget(Target): + known_kwargs = known_build_target_kwargs + def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs): super().__init__(name, subdir, subproject, True) self.is_cross = is_cross @@ -396,7 +396,7 @@ class BuildTarget(Target): def check_unknown_kwargs(self, kwargs): # Override this method in derived classes that have more # 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): unknowns = [] @@ -626,13 +626,17 @@ class BuildTarget(Target): if not isinstance(src, str): raise MesonException('Object extraction arguments must be strings.') src = File(False, self.subdir, src) + # FIXME: It could be a generated source if src not in self.sources: raise MesonException('Tried to extract unknown source %s.' % 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): - 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): return self.get_transitive_link_deps() @@ -1184,6 +1188,8 @@ class GeneratedList: return self.extra_args class Executable(BuildTarget): + known_kwargs = known_exe_kwargs + def __init__(self, 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 @@ -1239,9 +1245,6 @@ class Executable(BuildTarget): def type_suffix(self): return "@exe" - def check_unknown_kwargs(self, kwargs): - self.check_unknown_kwargs_int(kwargs, known_exe_kwargs) - def get_import_filename(self): """ The name of the import library that will be outputted by the compiler @@ -1259,6 +1262,8 @@ class Executable(BuildTarget): return self.is_linkwithable class StaticLibrary(BuildTarget): + known_kwargs = known_stlib_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: kwargs['pic'] = environment.coredata.base_options['b_staticpic'].value @@ -1297,9 +1302,6 @@ class StaticLibrary(BuildTarget): def type_suffix(self): return "@sta" - def check_unknown_kwargs(self, kwargs): - self.check_unknown_kwargs_int(kwargs, known_lib_kwargs) - def process_kwargs(self, kwargs, environment): super().process_kwargs(kwargs, environment) if 'rust_crate_type' in kwargs: @@ -1313,6 +1315,8 @@ class StaticLibrary(BuildTarget): return True class SharedLibrary(BuildTarget): + known_kwargs = known_shlib_kwargs + def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs): self.soversion = None self.ltversion = None @@ -1501,9 +1505,6 @@ class SharedLibrary(BuildTarget): else: 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): """ 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 # into something else. class SharedModule(SharedLibrary): + known_kwargs = known_shmod_kwargs + def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs): if 'version' in kwargs: raise MesonException('Shared modules must not specify the version kwarg.') @@ -1568,19 +1571,20 @@ class SharedModule(SharedLibrary): self.import_filename = None class CustomTarget(Target): - known_kwargs = {'input': True, - 'output': True, - 'command': True, - 'capture': False, - 'install': True, - 'install_dir': True, - 'build_always': True, - 'depends': True, - 'depend_files': True, - 'depfile': True, - 'build_by_default': True, - 'override_options': True, - } + known_kwargs = set([ + 'input', + 'output', + 'command', + 'capture', + 'install', + 'install_dir', + 'build_always', + 'depends', + 'depend_files', + 'depfile', + 'build_by_default', + 'override_options', + ]) def __init__(self, name, subdir, subproject, kwargs, absolute_paths=False): super().__init__(name, subdir, subproject, False) @@ -1814,6 +1818,8 @@ class RunTarget(Target): return "@run" class Jar(BuildTarget): + known_kwargs = known_jar_kwargs + def __init__(self, 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: @@ -1836,9 +1842,6 @@ class Jar(BuildTarget): # All jar targets are installable. pass - def check_unknown_kwargs(self, kwargs): - self.check_unknown_kwargs_int(kwargs, known_jar_kwargs) - class CustomTargetIndex: """A special opaque object returned by indexing a CustomTarget. This object 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 55320d0..88565af 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -605,6 +605,31 @@ class StaticLibraryHolder(BuildTargetHolder): class SharedLibraryHolder(BuildTargetHolder): def __init__(self, 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): def __init__(self, target, interp): @@ -1418,71 +1443,17 @@ class MesonMain(InterpreterObject): raise InterpreterException('Unknown cross property: %s.' % propname) -pch_kwargs = set(['c_pch', 'cpp_pch']) - -lang_arg_kwargs = set([ - 'c_args', - 'cpp_args', - 'd_args', - 'd_import_dirs', - 'd_unittest', - 'd_module_versions', - 'fortran_args', - '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']) +known_library_kwargs = ( + build.known_shlib_kwargs | + build.known_stlib_kwargs +) + +known_build_target_kwargs = ( + known_library_kwargs | + build.known_exe_kwargs | + build.known_jar_kwargs | + {'target_type'} +) permitted_kwargs = {'add_global_arguments': {'language'}, 'add_global_link_arguments': {'language'}, @@ -1491,12 +1462,12 @@ permitted_kwargs = {'add_global_arguments': {'language'}, 'add_project_arguments': {'language'}, 'add_test_setup': {'exe_wrapper', 'gdb', 'timeout_multiplier', 'env'}, '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'}, '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'}, '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'}, 'generator': {'arguments', 'output', 'depfile', 'capture', 'preserve_path_from'}, 'include_directories': {'is_system'}, @@ -1504,12 +1475,14 @@ permitted_kwargs = {'add_global_arguments': {'language'}, 'install_headers': {'install_dir', 'subdir'}, 'install_man': {'install_dir'}, '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'}, 'run_target': {'command', 'depends'}, - 'shared_library': shlib_kwargs, - 'shared_module': shmod_kwargs, - 'static_library': stlib_kwargs, + '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'}, 'test': {'args', 'env', 'is_parallel', 'should_fail', 'timeout', 'workdir', 'suite'}, @@ -1610,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, @@ -2539,20 +2513,24 @@ root and issuing %s. @permittedKwargs(permitted_kwargs['shared_library']) 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']) def func_shared_module(self, node, args, kwargs): return self.build_target(node, args, kwargs, SharedModuleHolder) + @permittedKwargs(permitted_kwargs['library']) def func_library(self, node, args, kwargs): - if self.coredata.get_builtin_option('default_library') == 'shared': - return self.func_shared_lib(node, args, kwargs) - return self.func_static_lib(node, args, kwargs) + return self.build_library(node, args, kwargs) @permittedKwargs(permitted_kwargs['jar']) def func_jar(self, node, args, kwargs): - kwargs['target_type'] = 'jar' return self.build_target(node, args, kwargs, JarHolder) @permittedKwargs(permitted_kwargs['build_target']) @@ -2561,15 +2539,17 @@ root and issuing %s. raise InterpreterException('Missing target_type keyword argument') target_type = kwargs.pop('target_type') if target_type == 'executable': - return self.func_executable(node, args, kwargs) + return self.build_target(node, args, kwargs, ExecutableHolder) 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': - 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': - return self.func_library(node, args, kwargs) + return self.build_library(node, args, kwargs) elif target_type == 'jar': - return self.func_jar(node, args, kwargs) + return self.build_target(node, args, kwargs, JarHolder) else: raise InterpreterException('Unknown target_type.') @@ -3222,6 +3202,41 @@ 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): + 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): if not args: raise InterpreterException('Target does not have a name.') @@ -3257,7 +3272,13 @@ different subdirectory. mlog.debug('Unknown target type:', str(targetholder)) raise RuntimeError('Unreachable code') 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) + if is_cross: self.add_cross_stdlib_info(target) l = targetholder(target, self) diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py index ef74d63..c587f84 100644 --- a/mesonbuild/modules/pkgconfig.py +++ b/mesonbuild/modules/pkgconfig.py @@ -105,26 +105,24 @@ class DependenciesHelper: if obj.found(): processed_libs += obj.get_link_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) if public: if not hasattr(obj, 'generated_pc'): obj.generated_pc = self.name - elif isinstance(obj, 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. + elif isinstance(obj, (build.SharedLibrary, build.StaticLibrary)): processed_libs.append(obj) + self.add_priv_libs(obj.get_dependencies()) + self.add_priv_libs(obj.get_external_deps()) if public: if not hasattr(obj, 'generated_pc'): obj.generated_pc = self.name - self.add_priv_libs(obj.get_dependencies()) - self.add_priv_libs(obj.get_external_deps()) elif isinstance(obj, str): processed_libs.append(obj) else: diff --git a/mesonbuild/modules/python3.py b/mesonbuild/modules/python3.py index 9fd9f80..d2bf1dc 100644 --- a/mesonbuild/modules/python3.py +++ b/mesonbuild/modules/python3.py @@ -19,10 +19,7 @@ from . import ExtensionModule from mesonbuild.modules import ModuleReturnValue from . import permittedSnippetKwargs from ..interpreterbase import noKwargs -from ..interpreter import shlib_kwargs - -mod_kwargs = set() -mod_kwargs.update(shlib_kwargs) +from ..build import known_shmod_kwargs class Python3Module(ExtensionModule): @@ -30,7 +27,7 @@ class Python3Module(ExtensionModule): super().__init__() self.snippets.add('extension_module') - @permittedSnippetKwargs(mod_kwargs) + @permittedSnippetKwargs(known_shmod_kwargs) def extension_module(self, interpreter, state, args, kwargs): if 'name_prefix' in kwargs: raise mesonlib.MesonException('Name_prefix is set automatically, specifying it is forbidden.') 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 |