From f6b4ed028b9336b23d81526a24bebf077d2df13f Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Tue, 12 Dec 2023 15:09:05 -0500 Subject: [PATCH 1/4] allow internal_dependency object to carry both_lib Otherwise, the both_libs object is resolved by the interpreter and we no longer have access to it from the dependency object. --- mesonbuild/build.py | 16 ++++++++++++++-- mesonbuild/interpreter/interpreter.py | 1 + 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index a7a43b40cffe..45ff4cc2c5bc 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -137,6 +137,15 @@ def get_target_macos_dylib_install_name(ld) -> str: name.append('.dylib') 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 @@ -782,8 +791,8 @@ def __init__( # 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_as_list(kwargs, 'link_with') + self.link_targets - link_whole_targets = extract_as_list(kwargs, 'link_whole') + self.link_whole_targets + 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) self.link_targets.clear() self.link_whole_targets.clear() self.link(link_targets) @@ -2493,6 +2502,9 @@ def get_default_object(self) -> BuildTarget: return self.static raise MesonBugException(f'self._preferred_library == "{self._preferred_library}" is neither "shared" nor "static".') + def get_id(self) -> str: + return self.get_default_object().get_id() + class CommandBase: depend_files: T.List[File] diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 9de0f8762e17..d69f569c214f 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -690,6 +690,7 @@ def func_files(self, node: mparser.FunctionNode, args: T.Tuple[T.List[str]], kwa KwargInfo('version', (str, NoneType)), KwargInfo('objects', ContainerTypeInfo(list, build.ExtractedObjects), listify=True, default=[], since='1.1.0'), ) + @noSecondLevelHolderResolving def func_declare_dependency(self, node: mparser.BaseNode, args: T.List[TYPE_var], kwargs: kwtypes.FuncDeclareDependency) -> dependencies.Dependency: deps = kwargs['dependencies'] From 13e4a6b652ed15c540eea880dee88c8b3340689a Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Tue, 12 Dec 2023 15:49:59 -0500 Subject: [PATCH 2/4] add default_both_libraries core option --- docs/markdown/Builtin-options.md | 11 +++++ .../snippets/default_both_libraries.md | 4 ++ mesonbuild/build.py | 4 +- mesonbuild/interpreter/interpreter.py | 7 ++- mesonbuild/options.py | 2 + .../common/273 both libraries/meson.build | 44 +++++++++++++++++++ .../common/273 both libraries/src/api.h | 15 +++++++ .../common/273 both libraries/src/library.c | 10 +++++ .../common/273 both libraries/src/library.h | 5 +++ .../common/273 both libraries/src/main.c | 8 ++++ .../common/273 both libraries/test.json | 16 +++++++ 11 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 docs/markdown/snippets/default_both_libraries.md create mode 100644 test cases/common/273 both libraries/meson.build create mode 100644 test cases/common/273 both libraries/src/api.h create mode 100644 test cases/common/273 both libraries/src/library.c create mode 100644 test cases/common/273 both libraries/src/library.h create mode 100644 test cases/common/273 both libraries/src/main.c create mode 100644 test cases/common/273 both libraries/test.json diff --git a/docs/markdown/Builtin-options.md b/docs/markdown/Builtin-options.md index 6adc4218bda1..178f793e483a 100644 --- a/docs/markdown/Builtin-options.md +++ b/docs/markdown/Builtin-options.md @@ -79,6 +79,7 @@ machine](#specifying-options-per-machine) section for details. | genvslite {vs2022} | vs2022 | Setup multi-builtype ninja build directories and Visual Studio solution | no | no | | buildtype {plain, debug,
debugoptimized, release, minsize, custom} | debug | Build type to use | no | no | | debug | true | Enable debug symbols and other information | no | no | +| default_both_libraries {shared, static, auto} | shared | Default library type for both_libraries | no | no | | default_library {shared, static, both} | shared | Default library type | no | yes | | errorlogs | true | Whether to print the logs from failing tests. | no | no | | install_umask {preserve, 0000-0777} | 022 | Default umask to apply on permissions of installed files | no | no | @@ -177,6 +178,16 @@ fails. `vsenv` is `true` by default when using the `vs` backend. + +#### Details for `default_both_libraries` + +Since `1.6.0`, you can select the default type of library selected when using +a `both_libraries` object. This can be either 'shared' (default value, compatible +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. + + ## Base options These are set in the same way as universal options, either by diff --git a/docs/markdown/snippets/default_both_libraries.md b/docs/markdown/snippets/default_both_libraries.md new file mode 100644 index 000000000000..279c43d22b57 --- /dev/null +++ b/docs/markdown/snippets/default_both_libraries.md @@ -0,0 +1,4 @@ +## New built-in option for default both_libraries + +`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. diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 45ff4cc2c5bc..122d94774992 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2486,8 +2486,8 @@ def get_default_install_dir(self) -> T.Union[T.Tuple[str, str], T.Tuple[None, No return self.environment.get_shared_module_dir(), '{moduledir_shared}' class BothLibraries(SecondLevelHolder): - def __init__(self, shared: SharedLibrary, static: StaticLibrary) -> None: - self._preferred_library = 'shared' + def __init__(self, shared: SharedLibrary, static: StaticLibrary, preferred_library: Literal['shared', 'static', 'auto']) -> None: + self._preferred_library = preferred_library self.shared = shared self.static = static self.subproject = self.shared.subproject diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index d69f569c214f..e3e1e5b6730b 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3230,6 +3230,11 @@ def add_target(self, name: str, tobj: build.Target) -> None: def build_both_libraries(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargsType], kwargs: kwtypes.Library) -> build.BothLibraries: shared_lib = self.build_target(node, args, kwargs, build.SharedLibrary) static_lib = self.build_target(node, args, kwargs, build.StaticLibrary) + preferred_library = self.coredata.get_option(OptionKey('default_both_libraries')) + if preferred_library == 'auto': + preferred_library = self.coredata.get_option(OptionKey('default_library')) + if preferred_library == 'both': + preferred_library = 'shared' if self.backend.name == 'xcode': # Xcode is a bit special in that you can't (at least for the moment) @@ -3261,7 +3266,7 @@ def build_both_libraries(self, node: mparser.BaseNode, args: T.Tuple[str, Source # Keep only compilers used for linking static_lib.compilers = {k: v for k, v in static_lib.compilers.items() if k in compilers.clink_langs} - return build.BothLibraries(shared_lib, static_lib) + return build.BothLibraries(shared_lib, static_lib, preferred_library) def build_library(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargsType], kwargs: kwtypes.Library): default_library = self.coredata.get_option(OptionKey('default_library', subproject=self.subproject)) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 020c2e623cb4..f1f3554c27ea 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -63,6 +63,7 @@ 'buildtype', 'debug', 'default_library', + 'default_both_libraries', 'errorlogs', 'genvslite', 'install_umask', @@ -624,6 +625,7 @@ def add_to_argparse(self, name: str, parser: argparse.ArgumentParser, help_suffi (OptionKey('debug'), BuiltinOption(UserBooleanOption, 'Enable debug symbols and other information', True)), (OptionKey('default_library'), BuiltinOption(UserComboOption, 'Default library type', 'shared', choices=['shared', 'static', 'both'], yielding=False)), + (OptionKey('default_both_libraries'), BuiltinOption(UserComboOption, 'Default library type for both_libraries', 'shared', choices=['shared', 'static', 'auto'])), (OptionKey('errorlogs'), BuiltinOption(UserBooleanOption, "Whether to print the logs from failing tests", True)), (OptionKey('install_umask'), BuiltinOption(UserUmaskOption, 'Default umask to apply on permissions of installed files', '022')), (OptionKey('layout'), BuiltinOption(UserComboOption, 'Build directory layout', 'mirror', choices=['mirror', 'flat'])), diff --git a/test cases/common/273 both libraries/meson.build b/test cases/common/273 both libraries/meson.build new file mode 100644 index 000000000000..b80a9ce7d01f --- /dev/null +++ b/test cases/common/273 both libraries/meson.build @@ -0,0 +1,44 @@ +project( + 'test both libraries', + 'c', + meson_version: '>= 1.6.0', +) + +expected = 0 + + +with_library = library( + 'with_library', + files('src/library.c'), + c_shared_args: ['-DEXPORT'], +) + +with_library_dep = declare_dependency( + link_with: with_library, +) + +if get_option('default_library') == 'shared' + expected += 1 +elif get_option('default_library') == 'both' + if get_option('default_both_libraries') in ['shared', 'auto'] + expected += 1 + endif +endif + + + +mainlink = executable( + 'mainlink', + files('src/main.c'), + c_args: [f'-DEXPECTED=@expected@'], + link_with: [with_library], +) +test('link with', mainlink) + +maindep = executable( + 'maindep', + files('src/main.c'), + c_args: [f'-DEXPECTED=@expected@'], + dependencies: [with_library_dep], +) +test('use dep', maindep) diff --git a/test cases/common/273 both libraries/src/api.h b/test cases/common/273 both libraries/src/api.h new file mode 100644 index 000000000000..a20ded3642d4 --- /dev/null +++ b/test cases/common/273 both libraries/src/api.h @@ -0,0 +1,15 @@ +#pragma once + +#if defined EXPORT + #if defined _WIN32 || defined __CYGWIN__ + #define API __declspec(dllexport) + #else + #if defined __GNUC__ + #define API __attribute__((visibility("default"))) + #else + #define API + #endif + #endif +#else + #define API +#endif diff --git a/test cases/common/273 both libraries/src/library.c b/test cases/common/273 both libraries/src/library.c new file mode 100644 index 000000000000..decdb6ce9d38 --- /dev/null +++ b/test cases/common/273 both libraries/src/library.c @@ -0,0 +1,10 @@ +#include "library.h" + +int library_function(void) +{ +#if defined EXPORT + return 1; +#else + return 0; +#endif +} diff --git a/test cases/common/273 both libraries/src/library.h b/test cases/common/273 both libraries/src/library.h new file mode 100644 index 000000000000..7f57af4f136d --- /dev/null +++ b/test cases/common/273 both libraries/src/library.h @@ -0,0 +1,5 @@ +#pragma once + +#include "api.h" + +int API library_function(void); diff --git a/test cases/common/273 both libraries/src/main.c b/test cases/common/273 both libraries/src/main.c new file mode 100644 index 000000000000..340322e455ed --- /dev/null +++ b/test cases/common/273 both libraries/src/main.c @@ -0,0 +1,8 @@ +#include "library.h" + + +int main(void) +{ + int sum = library_function(); + return sum == EXPECTED ? 0 : sum; +} diff --git a/test cases/common/273 both libraries/test.json b/test cases/common/273 both libraries/test.json new file mode 100644 index 000000000000..08aa54790e46 --- /dev/null +++ b/test cases/common/273 both libraries/test.json @@ -0,0 +1,16 @@ +{ + "matrix": { + "options": { + "default_library": [ + { "val": "shared" }, + { "val": "static" }, + { "val": "both" } + ], + "default_both_libraries": [ + { "val": "shared" }, + { "val": "static" }, + { "val": "auto" } + ] + } + } +} From 14a99e615b6346909bb9b8517c9657296791c844 Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Wed, 13 Dec 2023 11:12:00 -0500 Subject: [PATCH 3/4] auto select static or shared when linking both_libraries together --- docs/markdown/Builtin-options.md | 3 + .../snippets/default_both_libraries.md | 7 ++ mesonbuild/build.py | 35 ++++++---- mesonbuild/interpreter/interpreter.py | 10 ++- .../common/273 both libraries/meson.build | 65 +++++++++++++++---- .../common/273 both libraries/meson.options | 1 + .../273 both libraries/src/both_libraries.c | 10 +++ .../273 both libraries/src/both_libraries.h | 5 ++ .../common/273 both libraries/src/library.c | 6 +- .../common/273 both libraries/src/main.c | 2 +- .../common/273 both libraries/test.json | 4 ++ 11 files changed, 122 insertions(+), 26 deletions(-) create mode 100644 test cases/common/273 both libraries/meson.options create mode 100644 test cases/common/273 both libraries/src/both_libraries.c create mode 100644 test cases/common/273 both libraries/src/both_libraries.h diff --git a/docs/markdown/Builtin-options.md b/docs/markdown/Builtin-options.md index 178f793e483a..2018b9575aed 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 279c43d22b57..dc9cb6bca3a8 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 122d94774992..b86a6a2a6541 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 @@ def __init__( # 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 @@ def process_vs_module_defs_kw(self, kwargs: T.Dict[str, T.Any]) -> None: '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 @@ def __init__(self, shared: SharedLibrary, static: StaticLibrary, preferred_libra 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 e3e1e5b6730b..7eac40bb85d4 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -31,7 +31,7 @@ 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 @@ def func_shared_lib(self, node: mparser.BaseNode, @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 @@ def func_shared_module(self, node: mparser.BaseNode, @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 @@ def func_jar(self, node: mparser.BaseNode, @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 @@ def build_library(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVararg 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 b80a9ce7d01f..de7668ce4e9a 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 000000000000..2e3c357ae0b2 --- /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 000000000000..ab1bd1fd9965 --- /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 000000000000..39c4c8430154 --- /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 decdb6ce9d38..bdd965f7fb96 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 340322e455ed..1b367896d189 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 08aa54790e46..2aba26e48a3a 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 } ] } } From 37de678ba35f29b3bc68e434942a2a94f550baf5 Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Wed, 13 Dec 2023 16:02:11 -0500 Subject: [PATCH 4/4] alias_target with both_libs builds both --- .../alias_target_of_both_libraries.md | 5 ++++ .../snippets/dep_as_shared_as_static.md | 8 ++++++ docs/yaml/functions/alias_target.yaml | 3 ++ docs/yaml/objects/dep.yaml | 25 +++++++++++++++++ mesonbuild/build.py | 23 +++++++++++++-- mesonbuild/dependencies/base.py | 22 +++++++++++++++ mesonbuild/interpreter/interpreter.py | 7 +++-- mesonbuild/interpreter/interpreterobjects.py | 28 +++++++++++++++++++ .../common/178 bothlibraries/meson.build | 1 + .../common/273 both libraries/meson.build | 28 ++++++++++++++++++- 10 files changed, 143 insertions(+), 7 deletions(-) create mode 100644 docs/markdown/snippets/alias_target_of_both_libraries.md create mode 100644 docs/markdown/snippets/dep_as_shared_as_static.md diff --git a/docs/markdown/snippets/alias_target_of_both_libraries.md b/docs/markdown/snippets/alias_target_of_both_libraries.md new file mode 100644 index 000000000000..ab77a6556576 --- /dev/null +++ b/docs/markdown/snippets/alias_target_of_both_libraries.md @@ -0,0 +1,5 @@ +## `alias_target` of `both_libraries` + +Previously, when passing a [[@both_libs]] object to [[alias_target]], the alias +would only point to the shared library. It now points to both the static and the +shared library. diff --git a/docs/markdown/snippets/dep_as_shared_as_static.md b/docs/markdown/snippets/dep_as_shared_as_static.md new file mode 100644 index 000000000000..a84e9eeaa182 --- /dev/null +++ b/docs/markdown/snippets/dep_as_shared_as_static.md @@ -0,0 +1,8 @@ +## New `as_static` and `as_shared` methods on internal dependencies + +[[@dep]] object returned by [[declare_dependency]] now has `.as_static()` and +`.as_shared()` methods, to convert to a dependency that prefers the `static` +or the `shared` version of the linked [[@both_libs]] target. + +When the same dependency is used without those methods, the +`default_both_libraries` option determines which version is used. diff --git a/docs/yaml/functions/alias_target.yaml b/docs/yaml/functions/alias_target.yaml index bc14f84da2fe..9833569018f7 100644 --- a/docs/yaml/functions/alias_target.yaml +++ b/docs/yaml/functions/alias_target.yaml @@ -9,6 +9,9 @@ description: | are built. Dependencies can be any build target. Since 0.60.0, this includes [[@run_tgt]]. + *Since 1.6.0* passing a [[@both_libs]] object builds both shared and + static libraries. + posargs: target_name: type: str diff --git a/docs/yaml/objects/dep.yaml b/docs/yaml/objects/dep.yaml index 76543d2c1ab9..28d93d79f7f6 100644 --- a/docs/yaml/objects/dep.yaml +++ b/docs/yaml/objects/dep.yaml @@ -221,3 +221,28 @@ methods: pkgconfig_define: type: list[str] description: See [[dep.get_pkgconfig_variable]] + + - name: as_static + returns: dep + since: 1.6.0 + description: | + Only for dependencies created with [[declare_dependency]], + returns a copy of the dependency object that prefer the `static` version + of [[both_libraries]]. + kwargs: + recursive: + type: bool + description: If true, this is recursively applied to dependencies + + - name: as_shared + returns: dep + since: 1.6.0 + description: | + Only for dependencies created with [[declare_dependency]], + returns a copy of the dependency object that prefer the `shared` version + of [[both_libraries]]. + kwargs: + recursive: + type: bool + description: If true, this is recursively applied to dependencies + \ No newline at end of file diff --git a/mesonbuild/build.py b/mesonbuild/build.py index b86a6a2a6541..5eff0ed805c5 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1746,6 +1746,10 @@ def _resolve_both_libs(lib: LibTypes) -> LibTypes: lib_list = listify(kwargs.get(key, [])) + self_libs return [_resolve_both_libs(t) for t in lib_list] + def get(self, lib_type: T.Literal['static', 'shared', 'auto']) -> LibTypes: + """Base case used by BothLibraries""" + return self + 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'. @@ -2501,7 +2505,7 @@ def __init__(self, shared: SharedLibrary, static: StaticLibrary, preferred_libra def __repr__(self) -> str: return f'' - def get(self, lib_type: T.Literal['static', 'shared', 'auto']) -> T.Union[StaticLibrary, SharedLibrary]: + def get(self, lib_type: T.Literal['static', 'shared', 'auto']) -> LibTypes: if lib_type == 'static': return self.static if lib_type == 'shared': @@ -2575,6 +2579,10 @@ def get_internal_static_libraries(self) -> OrderedSet[BuildTargetTypes]: def get_internal_static_libraries_recurse(self, result: OrderedSet[BuildTargetTypes]) -> None: pass + def get(self, lib_type: T.Literal['static', 'shared', 'auto']) -> LibTypes: + """Base case used by BothLibraries""" + return self + class CustomTarget(Target, CustomTargetBase, CommandBase): typename = 'custom' @@ -2896,14 +2904,23 @@ class AliasTarget(RunTarget): typename = 'alias' - def __init__(self, name: str, dependencies: T.Sequence['Target'], + def __init__(self, name: str, dependencies: T.Sequence[T.Union[Target, BothLibraries]], subdir: str, subproject: str, environment: environment.Environment): - super().__init__(name, [], dependencies, subdir, subproject, environment) + super().__init__(name, [], list(self._deps_generator(dependencies)), subdir, subproject, environment) def __repr__(self): repr_str = "<{0} {1}>" return repr_str.format(self.__class__.__name__, self.get_id()) + @staticmethod + def _deps_generator(dependencies: T.Sequence[T.Union[Target, BothLibraries]]) -> T.Iterator[Target]: + for dep in dependencies: + if isinstance(dep, BothLibraries): + yield dep.shared + yield dep.static + else: + yield dep + class Jar(BuildTarget): known_kwargs = known_jar_kwargs diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 08e81f0d79d5..fa278d97c07f 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -248,6 +248,14 @@ def generate_system_dependency(self, include_type: str) -> 'Dependency': new_dep.include_type = self._process_include_type_kw({'include_type': include_type}) return new_dep + def get_as_static(self, recursive: bool) -> Dependency: + """Used as base case for internal_dependency""" + return self + + def get_as_shared(self, recursive: bool) -> Dependency: + """Used as base case for internal_dependency""" + return self + class InternalDependency(Dependency): def __init__(self, version: str, incdirs: T.List['IncludeDirs'], compile_args: T.List[str], link_args: T.List[str], @@ -345,6 +353,20 @@ def generate_link_whole_dependency(self) -> Dependency: new_dep.libraries = [] return new_dep + def get_as_static(self, recursive: bool) -> Dependency: + new_dep = copy.copy(self) + new_dep.libraries = [lib.get('static') for lib in self.libraries] + if recursive: + new_dep.ext_deps = [dep.get_as_static(True) for dep in self.ext_deps] + return new_dep + + def get_as_shared(self, recursive: bool) -> Dependency: + new_dep = copy.copy(self) + new_dep.libraries = [lib.get('shared') for lib in self.libraries] + if recursive: + new_dep.ext_deps = [dep.get_as_shared(True) for dep in self.ext_deps] + return new_dep + class HasNativeKwarg: def __init__(self, kwargs: T.Dict[str, T.Any]): self.for_machine = self.get_for_machine_from_kwargs(kwargs) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 7eac40bb85d4..bd4d1596231a 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2165,10 +2165,11 @@ def func_run_target(self, node: mparser.FunctionNode, args: T.Tuple[str], return tg @FeatureNew('alias_target', '0.52.0') - @typed_pos_args('alias_target', str, varargs=build.Target, min_varargs=1) + @typed_pos_args('alias_target', str, varargs=(build.Target, build.BothLibraries), min_varargs=1) @noKwargs - def func_alias_target(self, node: mparser.BaseNode, args: T.Tuple[str, T.List[build.Target]], - kwargs: 'TYPE_kwargs') -> build.AliasTarget: + @noSecondLevelHolderResolving + def func_alias_target(self, node: mparser.BaseNode, args: T.Tuple[str, T.List[T.Union[build.Target, build.BothLibraries]]], + kwargs: TYPE_kwargs) -> build.AliasTarget: name, deps = args if any(isinstance(d, build.RunTarget) for d in deps): FeatureNew.single_use('alias_target that depends on run_targets', '0.60.0', self.subproject) diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index 155cfd23e56e..2cd55321bfcb 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -41,6 +41,10 @@ class EnvironmentSeparatorKW(TypedDict): separator: str + class InternalDependencyAsKW(TypedDict): + + recursive: bool + _ERROR_MSG_KW: KwargInfo[T.Optional[str]] = KwargInfo('error_message', (str, NoneType)) @@ -462,6 +466,8 @@ def __init__(self, dep: Dependency, interpreter: 'Interpreter'): 'include_type': self.include_type_method, 'as_system': self.as_system_method, 'as_link_whole': self.as_link_whole_method, + 'as_static': self.as_static_method, + 'as_shared': self.as_shared_method, }) def found(self) -> bool: @@ -580,6 +586,28 @@ def as_link_whole_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> D new_dep = self.held_object.generate_link_whole_dependency() return new_dep + @FeatureNew('dependency.as_static', '1.6.0') + @noPosargs + @typed_kwargs( + 'dependency.as_static', + KwargInfo('recursive', bool, default=False), + ) + def as_static_method(self, args: T.List[TYPE_var], kwargs: InternalDependencyAsKW) -> Dependency: + if not isinstance(self.held_object, InternalDependency): + raise InterpreterException('as_static method is only supported on declare_dependency() objects') + return self.held_object.get_as_static(kwargs['recursive']) + + @FeatureNew('dependency.as_shared', '1.6.0') + @noPosargs + @typed_kwargs( + 'dependency.as_shared', + KwargInfo('recursive', bool, default=False), + ) + def as_shared_method(self, args: T.List[TYPE_var], kwargs: InternalDependencyAsKW) -> Dependency: + if not isinstance(self.held_object, InternalDependency): + raise InterpreterException('as_shared method is only supported on declare_dependency() objects') + return self.held_object.get_as_shared(kwargs['recursive']) + _EXTPROG = T.TypeVar('_EXTPROG', bound=ExternalProgram) class _ExternalProgramHolder(ObjectHolder[_EXTPROG]): diff --git a/test cases/common/178 bothlibraries/meson.build b/test cases/common/178 bothlibraries/meson.build index 62f2061f8d67..654d94e66f80 100644 --- a/test cases/common/178 bothlibraries/meson.build +++ b/test cases/common/178 bothlibraries/meson.build @@ -2,6 +2,7 @@ project('both libraries linking test', 'c', 'cpp') both_libs = both_libraries('mylib', 'libfile.c') dep = declare_dependency(link_with: both_libs) +alias_target('alias', both_libs) 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'], diff --git a/test cases/common/273 both libraries/meson.build b/test cases/common/273 both libraries/meson.build index de7668ce4e9a..00da1c8e6cf5 100644 --- a/test cases/common/273 both libraries/meson.build +++ b/test cases/common/273 both libraries/meson.build @@ -77,11 +77,37 @@ test('test both libs', main) 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. + + if get_option('use_dep') + main_static_deps = [with_library_dep.as_static(recursive: true)] + main_static_links = [] + else + main_static_deps = [] + main_static_links = [with_library.get_static_lib()] + endif main_static = executable( 'main_static', files('src/main.c'), c_args: [f'-DEXPECTED=0'], - link_with: with_library.get_static_lib(), + link_with: main_static_links, + dependencies: main_static_deps, ) test('test static', main_static) + + + if get_option('use_dep') + main_shared_deps = [with_library_dep.as_shared(recursive: true)] + main_shared_links = [] + else + main_shared_deps = [] + main_shared_links = [with_library.get_shared_lib()] + endif + main_shared = executable( + 'main_shared', + files('src/main.c'), + c_args: [f'-DEXPECTED=2'], + link_with: main_shared_links, + dependencies: main_shared_deps, + ) + test('test shared', main_shared) endif