diff --git a/docs/markdown/Builtin-options.md b/docs/markdown/Builtin-options.md index 178f793e4..2018b9575 100644 --- a/docs/markdown/Builtin-options.md +++ b/docs/markdown/Builtin-options.md @@ -187,6 +187,9 @@ with previous meson versions), 'static', or 'auto'. With auto, the value from `default_library` option is used, unless it is 'both', in which case 'shared' is used instead. +When `default_both_libraries` is 'auto', passing a [[@both_libs]] dependecy +in [[both_libraries]] will link the static dependency with the static lib, +and the shared dependency with the shared lib. ## Base options diff --git a/docs/markdown/snippets/default_both_libraries.md b/docs/markdown/snippets/default_both_libraries.md index 279c43d22..dc9cb6bca 100644 --- a/docs/markdown/snippets/default_both_libraries.md +++ b/docs/markdown/snippets/default_both_libraries.md @@ -2,3 +2,10 @@ `both_libraries` targets used to be considered as a shared library by default. There is now the `default_both_libraries` option to change this default. + +When `default_both_libraries` is 'auto', [[both_libraries]] with dependencies +that are [[@both_libs]] themselves will link with the same kind of library. +For example, if `libA` is a [[@both_libs]] and `libB` is a [[@both_libs]] +linked with `libA` (or with an internal dependency on `libA`), +the static lib of `libB` will link with the static lib of `libA`, and the +shared lib of `libA` will link with the shared lib of `libB`. diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 122d94774..b86a6a2a6 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -138,14 +138,6 @@ def get_target_macos_dylib_install_name(ld) -> str: return ''.join(name) -def extract_targets_as_list( - kwargs: T.Dict[str, T.Sequence[LibTypes]], - key: T.Literal['link_with', 'link_whole'], - self_libs: T.List[LibTypes]) -> T.List[LibTypes]: - lib_list = listify(kwargs.get(key, [])) + self_libs - return [t.get_default_object() if isinstance(t, BothLibraries) else t for t in lib_list] - - class InvalidArguments(MesonException): pass @@ -791,8 +783,8 @@ class BuildTarget(Target): # we have to call process_compilers() first and we need to process libraries # from link_with and link_whole first. # See https://github.com/mesonbuild/meson/pull/11957#issuecomment-1629243208. - link_targets = extract_targets_as_list(kwargs, 'link_with', self.link_targets) - link_whole_targets = extract_targets_as_list(kwargs, 'link_whole', self.link_whole_targets) + link_targets = self.extract_targets_as_list(kwargs, 'link_with') + link_whole_targets = self.extract_targets_as_list(kwargs, 'link_whole') self.link_targets.clear() self.link_whole_targets.clear() self.link(link_targets) @@ -1740,6 +1732,20 @@ class BuildTarget(Target): 'a file object, a Custom Target, or a Custom Target Index') self.process_link_depends(path) + def extract_targets_as_list(self, kwargs: T.Dict[str, T.Union[LibTypes, T.Sequence[LibTypes]]], key: T.Literal['link_with', 'link_whole']) -> T.List[LibTypes]: + bl_type = self.environment.coredata.get_option(OptionKey('default_both_libraries')) + if bl_type == 'auto': + bl_type = 'static' if isinstance(self, StaticLibrary) else 'shared' + + def _resolve_both_libs(lib: LibTypes) -> LibTypes: + if isinstance(lib, BothLibraries): + return lib.get(bl_type) + return lib + + self_libs: T.List[LibTypes] = self.link_targets if key == 'link_with' else self.link_whole_targets + lib_list = listify(kwargs.get(key, [])) + self_libs + return [_resolve_both_libs(t) for t in lib_list] + class FileInTargetPrivateDir: """Represents a file with the path '/path/to/build/target_private_dir/fname'. target_private_dir is the return value of get_target_private_dir which is e.g. 'subdir/target.p'. @@ -2495,7 +2501,14 @@ class BothLibraries(SecondLevelHolder): def __repr__(self) -> str: return f'' - def get_default_object(self) -> BuildTarget: + def get(self, lib_type: T.Literal['static', 'shared', 'auto']) -> T.Union[StaticLibrary, SharedLibrary]: + if lib_type == 'static': + return self.static + if lib_type == 'shared': + return self.shared + return self.get_default_object() + + def get_default_object(self) -> T.Union[StaticLibrary, SharedLibrary]: if self._preferred_library == 'shared': return self.shared elif self._preferred_library == 'static': diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index e3e1e5b67..7eac40bb8 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -31,7 +31,7 @@ from ..interpreterbase import InterpreterException, InvalidArguments, InvalidCod from ..interpreterbase import Disabler, disablerIfNotFound from ..interpreterbase import FeatureNew, FeatureDeprecated, FeatureBroken, FeatureNewKwargs from ..interpreterbase import ObjectHolder, ContextManagerObject -from ..interpreterbase import stringifyUserArguments +from ..interpreterbase import stringifyUserArguments, resolve_second_level_holders from ..modules import ExtensionModule, ModuleObject, MutableModuleObject, NewExtensionModule, NotFoundExtensionModule from ..optinterpreter import optname_regex @@ -1876,6 +1876,7 @@ class Interpreter(InterpreterBase, HoldableObject): @permittedKwargs(known_library_kwargs) @typed_pos_args('both_libraries', str, varargs=SOURCES_VARARGS) @typed_kwargs('both_libraries', *LIBRARY_KWS, allow_unknown=True) + @noSecondLevelHolderResolving def func_both_lib(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargsType], kwargs: kwtypes.Library) -> build.BothLibraries: @@ -1893,6 +1894,7 @@ class Interpreter(InterpreterBase, HoldableObject): @permittedKwargs(known_library_kwargs) @typed_pos_args('library', str, varargs=SOURCES_VARARGS) @typed_kwargs('library', *LIBRARY_KWS, allow_unknown=True) + @noSecondLevelHolderResolving def func_library(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargsType], kwargs: kwtypes.Library) -> build.Executable: @@ -1910,12 +1912,16 @@ class Interpreter(InterpreterBase, HoldableObject): @permittedKwargs(known_build_target_kwargs) @typed_pos_args('build_target', str, varargs=SOURCES_VARARGS) @typed_kwargs('build_target', *BUILD_TARGET_KWS, allow_unknown=True) + @noSecondLevelHolderResolving def func_build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargsType], kwargs: kwtypes.BuildTarget ) -> T.Union[build.Executable, build.StaticLibrary, build.SharedLibrary, build.SharedModule, build.BothLibraries, build.Jar]: target_type = kwargs['target_type'] + if target_type not in {'both_libraries', 'library'}: + args, kwargs = resolve_second_level_holders(args, kwargs) + if target_type == 'executable': return self.build_target(node, args, kwargs, build.Executable) elif target_type == 'shared_library': @@ -3272,8 +3278,10 @@ class Interpreter(InterpreterBase, HoldableObject): default_library = self.coredata.get_option(OptionKey('default_library', subproject=self.subproject)) assert isinstance(default_library, str), 'for mypy' if default_library == 'shared': + args, kwargs = resolve_second_level_holders(args, kwargs) return self.build_target(node, args, T.cast('kwtypes.StaticLibrary', kwargs), build.SharedLibrary) elif default_library == 'static': + args, kwargs = resolve_second_level_holders(args, kwargs) return self.build_target(node, args, T.cast('kwtypes.SharedLibrary', kwargs), build.StaticLibrary) elif default_library == 'both': return self.build_both_libraries(node, args, kwargs) diff --git a/test cases/common/273 both libraries/meson.build b/test cases/common/273 both libraries/meson.build index b80a9ce7d..de7668ce4 100644 --- a/test cases/common/273 both libraries/meson.build +++ b/test cases/common/273 both libraries/meson.build @@ -7,38 +7,81 @@ project( expected = 0 +with_bl = both_libraries( + 'with_bl', + files('src/both_libraries.c'), + c_shared_args: ['-DEXPORT'], +) + +with_bl_dep = declare_dependency( + link_with: with_bl, +) + + +if get_option('use_dep') + lib_deps = [with_bl_dep] + lib_links = [] +else + lib_deps = [] + lib_links = [with_bl] +endif + + with_library = library( 'with_library', files('src/library.c'), c_shared_args: ['-DEXPORT'], + link_with: lib_links, + dependencies: lib_deps, ) with_library_dep = declare_dependency( link_with: with_library, ) + if get_option('default_library') == 'shared' expected += 1 + if get_option('default_both_libraries') in ['shared', 'auto'] + expected += 1 + endif elif get_option('default_library') == 'both' if get_option('default_both_libraries') in ['shared', 'auto'] + expected += 2 + endif +else + if get_option('default_both_libraries') == 'shared' expected += 1 endif endif +if get_option('use_dep') + main_deps = [with_library_dep] + main_links = [] +else + main_deps = [] + main_links = [with_library] +endif -mainlink = executable( - 'mainlink', +main = executable( + 'main', files('src/main.c'), c_args: [f'-DEXPECTED=@expected@'], - link_with: [with_library], + link_with: main_links, + dependencies: main_deps, ) -test('link with', mainlink) +test('test both libs', main) -maindep = executable( - 'maindep', - files('src/main.c'), - c_args: [f'-DEXPECTED=@expected@'], - dependencies: [with_library_dep], -) -test('use dep', maindep) + +if get_option('default_library') == 'both' and get_option('default_both_libraries') == 'auto' + # With those options, even if the both_libraries defaults to 'shared', + # 'static' version is used when linking to the static part of another both_libraries. + main_static = executable( + 'main_static', + files('src/main.c'), + c_args: [f'-DEXPECTED=0'], + link_with: with_library.get_static_lib(), + ) + test('test static', main_static) +endif diff --git a/test cases/common/273 both libraries/meson.options b/test cases/common/273 both libraries/meson.options new file mode 100644 index 000000000..2e3c357ae --- /dev/null +++ b/test cases/common/273 both libraries/meson.options @@ -0,0 +1 @@ +option('use_dep', type: 'boolean', value: false) diff --git a/test cases/common/273 both libraries/src/both_libraries.c b/test cases/common/273 both libraries/src/both_libraries.c new file mode 100644 index 000000000..ab1bd1fd9 --- /dev/null +++ b/test cases/common/273 both libraries/src/both_libraries.c @@ -0,0 +1,10 @@ +#include "both_libraries.h" + +int both_libraries_function(void) +{ +#if defined EXPORT + return 1; +#else + return 0; +#endif +} diff --git a/test cases/common/273 both libraries/src/both_libraries.h b/test cases/common/273 both libraries/src/both_libraries.h new file mode 100644 index 000000000..39c4c8430 --- /dev/null +++ b/test cases/common/273 both libraries/src/both_libraries.h @@ -0,0 +1,5 @@ +#pragma once + +#include "api.h" + +int API both_libraries_function(void); diff --git a/test cases/common/273 both libraries/src/library.c b/test cases/common/273 both libraries/src/library.c index decdb6ce9..bdd965f7f 100644 --- a/test cases/common/273 both libraries/src/library.c +++ b/test cases/common/273 both libraries/src/library.c @@ -1,10 +1,12 @@ #include "library.h" +#include "both_libraries.h" int library_function(void) { + int sum = both_libraries_function(); #if defined EXPORT - return 1; + return sum + 1; #else - return 0; + return sum; #endif } diff --git a/test cases/common/273 both libraries/src/main.c b/test cases/common/273 both libraries/src/main.c index 340322e45..1b367896d 100644 --- a/test cases/common/273 both libraries/src/main.c +++ b/test cases/common/273 both libraries/src/main.c @@ -4,5 +4,5 @@ int main(void) { int sum = library_function(); - return sum == EXPECTED ? 0 : sum; + return sum == EXPECTED ? 0 : 1; } diff --git a/test cases/common/273 both libraries/test.json b/test cases/common/273 both libraries/test.json index 08aa54790..2aba26e48 100644 --- a/test cases/common/273 both libraries/test.json +++ b/test cases/common/273 both libraries/test.json @@ -10,6 +10,10 @@ { "val": "shared" }, { "val": "static" }, { "val": "auto" } + ], + "use_dep": [ + { "val": false }, + { "val": true } ] } }