auto select static or shared when linking both_libraries together

pull/13609/head
Charles Brunet 12 months ago committed by Dylan Baker
parent 2d6915a598
commit 0fc363021e
  1. 3
      docs/markdown/Builtin-options.md
  2. 7
      docs/markdown/snippets/default_both_libraries.md
  3. 35
      mesonbuild/build.py
  4. 10
      mesonbuild/interpreter/interpreter.py
  5. 63
      test cases/common/273 both libraries/meson.build
  6. 1
      test cases/common/273 both libraries/meson.options
  7. 10
      test cases/common/273 both libraries/src/both_libraries.c
  8. 5
      test cases/common/273 both libraries/src/both_libraries.h
  9. 6
      test cases/common/273 both libraries/src/library.c
  10. 2
      test cases/common/273 both libraries/src/main.c
  11. 4
      test cases/common/273 both libraries/test.json

@ -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' `default_library` option is used, unless it is 'both', in which case 'shared'
is used instead. 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 ## Base options

@ -2,3 +2,10 @@
`both_libraries` targets used to be considered as a shared library by default. `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. 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`.

@ -138,14 +138,6 @@ def get_target_macos_dylib_install_name(ld) -> str:
return ''.join(name) 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): class InvalidArguments(MesonException):
pass pass
@ -791,8 +783,8 @@ class BuildTarget(Target):
# we have to call process_compilers() first and we need to process libraries # we have to call process_compilers() first and we need to process libraries
# from link_with and link_whole first. # from link_with and link_whole first.
# See https://github.com/mesonbuild/meson/pull/11957#issuecomment-1629243208. # See https://github.com/mesonbuild/meson/pull/11957#issuecomment-1629243208.
link_targets = extract_targets_as_list(kwargs, 'link_with', self.link_targets) link_targets = self.extract_targets_as_list(kwargs, 'link_with')
link_whole_targets = extract_targets_as_list(kwargs, 'link_whole', self.link_whole_targets) link_whole_targets = self.extract_targets_as_list(kwargs, 'link_whole')
self.link_targets.clear() self.link_targets.clear()
self.link_whole_targets.clear() self.link_whole_targets.clear()
self.link(link_targets) self.link(link_targets)
@ -1740,6 +1732,20 @@ class BuildTarget(Target):
'a file object, a Custom Target, or a Custom Target Index') 'a file object, a Custom Target, or a Custom Target Index')
self.process_link_depends(path) 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: class FileInTargetPrivateDir:
"""Represents a file with the path '/path/to/build/target_private_dir/fname'. """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'. 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: def __repr__(self) -> str:
return f'<BothLibraries: static={repr(self.static)}; shared={repr(self.shared)}>' return f'<BothLibraries: static={repr(self.static)}; shared={repr(self.shared)}>'
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': if self._preferred_library == 'shared':
return self.shared return self.shared
elif self._preferred_library == 'static': elif self._preferred_library == 'static':

@ -31,7 +31,7 @@ from ..interpreterbase import InterpreterException, InvalidArguments, InvalidCod
from ..interpreterbase import Disabler, disablerIfNotFound from ..interpreterbase import Disabler, disablerIfNotFound
from ..interpreterbase import FeatureNew, FeatureDeprecated, FeatureBroken, FeatureNewKwargs from ..interpreterbase import FeatureNew, FeatureDeprecated, FeatureBroken, FeatureNewKwargs
from ..interpreterbase import ObjectHolder, ContextManagerObject 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 ..modules import ExtensionModule, ModuleObject, MutableModuleObject, NewExtensionModule, NotFoundExtensionModule
from ..optinterpreter import optname_regex from ..optinterpreter import optname_regex
@ -1876,6 +1876,7 @@ class Interpreter(InterpreterBase, HoldableObject):
@permittedKwargs(known_library_kwargs) @permittedKwargs(known_library_kwargs)
@typed_pos_args('both_libraries', str, varargs=SOURCES_VARARGS) @typed_pos_args('both_libraries', str, varargs=SOURCES_VARARGS)
@typed_kwargs('both_libraries', *LIBRARY_KWS, allow_unknown=True) @typed_kwargs('both_libraries', *LIBRARY_KWS, allow_unknown=True)
@noSecondLevelHolderResolving
def func_both_lib(self, node: mparser.BaseNode, def func_both_lib(self, node: mparser.BaseNode,
args: T.Tuple[str, SourcesVarargsType], args: T.Tuple[str, SourcesVarargsType],
kwargs: kwtypes.Library) -> build.BothLibraries: kwargs: kwtypes.Library) -> build.BothLibraries:
@ -1893,6 +1894,7 @@ class Interpreter(InterpreterBase, HoldableObject):
@permittedKwargs(known_library_kwargs) @permittedKwargs(known_library_kwargs)
@typed_pos_args('library', str, varargs=SOURCES_VARARGS) @typed_pos_args('library', str, varargs=SOURCES_VARARGS)
@typed_kwargs('library', *LIBRARY_KWS, allow_unknown=True) @typed_kwargs('library', *LIBRARY_KWS, allow_unknown=True)
@noSecondLevelHolderResolving
def func_library(self, node: mparser.BaseNode, def func_library(self, node: mparser.BaseNode,
args: T.Tuple[str, SourcesVarargsType], args: T.Tuple[str, SourcesVarargsType],
kwargs: kwtypes.Library) -> build.Executable: kwargs: kwtypes.Library) -> build.Executable:
@ -1910,12 +1912,16 @@ class Interpreter(InterpreterBase, HoldableObject):
@permittedKwargs(known_build_target_kwargs) @permittedKwargs(known_build_target_kwargs)
@typed_pos_args('build_target', str, varargs=SOURCES_VARARGS) @typed_pos_args('build_target', str, varargs=SOURCES_VARARGS)
@typed_kwargs('build_target', *BUILD_TARGET_KWS, allow_unknown=True) @typed_kwargs('build_target', *BUILD_TARGET_KWS, allow_unknown=True)
@noSecondLevelHolderResolving
def func_build_target(self, node: mparser.BaseNode, def func_build_target(self, node: mparser.BaseNode,
args: T.Tuple[str, SourcesVarargsType], args: T.Tuple[str, SourcesVarargsType],
kwargs: kwtypes.BuildTarget kwargs: kwtypes.BuildTarget
) -> T.Union[build.Executable, build.StaticLibrary, build.SharedLibrary, ) -> T.Union[build.Executable, build.StaticLibrary, build.SharedLibrary,
build.SharedModule, build.BothLibraries, build.Jar]: build.SharedModule, build.BothLibraries, build.Jar]:
target_type = kwargs['target_type'] 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': if target_type == 'executable':
return self.build_target(node, args, kwargs, build.Executable) return self.build_target(node, args, kwargs, build.Executable)
elif target_type == 'shared_library': 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)) default_library = self.coredata.get_option(OptionKey('default_library', subproject=self.subproject))
assert isinstance(default_library, str), 'for mypy' assert isinstance(default_library, str), 'for mypy'
if default_library == 'shared': 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) return self.build_target(node, args, T.cast('kwtypes.StaticLibrary', kwargs), build.SharedLibrary)
elif default_library == 'static': 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) return self.build_target(node, args, T.cast('kwtypes.SharedLibrary', kwargs), build.StaticLibrary)
elif default_library == 'both': elif default_library == 'both':
return self.build_both_libraries(node, args, kwargs) return self.build_both_libraries(node, args, kwargs)

@ -7,38 +7,81 @@ project(
expected = 0 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 = library(
'with_library', 'with_library',
files('src/library.c'), files('src/library.c'),
c_shared_args: ['-DEXPORT'], c_shared_args: ['-DEXPORT'],
link_with: lib_links,
dependencies: lib_deps,
) )
with_library_dep = declare_dependency( with_library_dep = declare_dependency(
link_with: with_library, link_with: with_library,
) )
if get_option('default_library') == 'shared' if get_option('default_library') == 'shared'
expected += 1 expected += 1
if get_option('default_both_libraries') in ['shared', 'auto']
expected += 1
endif
elif get_option('default_library') == 'both' elif get_option('default_library') == 'both'
if get_option('default_both_libraries') in ['shared', 'auto'] if get_option('default_both_libraries') in ['shared', 'auto']
expected += 2
endif
else
if get_option('default_both_libraries') == 'shared'
expected += 1 expected += 1
endif endif
endif endif
if get_option('use_dep')
main_deps = [with_library_dep]
main_links = []
else
main_deps = []
main_links = [with_library]
endif
mainlink = executable( main = executable(
'mainlink', 'main',
files('src/main.c'), files('src/main.c'),
c_args: [f'-DEXPECTED=@expected@'], 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', 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'), files('src/main.c'),
c_args: [f'-DEXPECTED=@expected@'], c_args: [f'-DEXPECTED=0'],
dependencies: [with_library_dep], link_with: with_library.get_static_lib(),
) )
test('use dep', maindep) test('test static', main_static)
endif

@ -0,0 +1 @@
option('use_dep', type: 'boolean', value: false)

@ -0,0 +1,10 @@
#include "both_libraries.h"
int both_libraries_function(void)
{
#if defined EXPORT
return 1;
#else
return 0;
#endif
}

@ -0,0 +1,5 @@
#pragma once
#include "api.h"
int API both_libraries_function(void);

@ -1,10 +1,12 @@
#include "library.h" #include "library.h"
#include "both_libraries.h"
int library_function(void) int library_function(void)
{ {
int sum = both_libraries_function();
#if defined EXPORT #if defined EXPORT
return 1; return sum + 1;
#else #else
return 0; return sum;
#endif #endif
} }

@ -4,5 +4,5 @@
int main(void) int main(void)
{ {
int sum = library_function(); int sum = library_function();
return sum == EXPECTED ? 0 : sum; return sum == EXPECTED ? 0 : 1;
} }

@ -10,6 +10,10 @@
{ "val": "shared" }, { "val": "shared" },
{ "val": "static" }, { "val": "static" },
{ "val": "auto" } { "val": "auto" }
],
"use_dep": [
{ "val": false },
{ "val": true }
] ]
} }
} }

Loading…
Cancel
Save