diff --git a/docs/markdown/Builtin-options.md b/docs/markdown/Builtin-options.md
index 6adc4218bda1..2018b9575aed 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,19 @@ 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.
+
+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
These are set in the same way as universal options, either by
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/default_both_libraries.md b/docs/markdown/snippets/default_both_libraries.md
new file mode 100644
index 000000000000..dc9cb6bca3a8
--- /dev/null
+++ b/docs/markdown/snippets/default_both_libraries.md
@@ -0,0 +1,11 @@
+## 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.
+
+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/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 a7a43b40cffe..5eff0ed805c5 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -137,6 +137,7 @@ def get_target_macos_dylib_install_name(ld) -> str:
name.append('.dylib')
return ''.join(name)
+
class InvalidArguments(MesonException):
pass
@@ -782,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_as_list(kwargs, 'link_with') + self.link_targets
- link_whole_targets = extract_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)
@@ -1731,6 +1732,24 @@ 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]
+
+ 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'.
@@ -2477,8 +2496,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
@@ -2486,13 +2505,23 @@ def __init__(self, shared: SharedLibrary, static: StaticLibrary) -> None:
def __repr__(self) -> str:
return f''
- def get_default_object(self) -> BuildTarget:
+ def get(self, lib_type: T.Literal['static', 'shared', 'auto']) -> LibTypes:
+ 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':
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]
@@ -2550,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'
@@ -2871,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 9de0f8762e17..bd4d1596231a 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
@@ -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']
@@ -1875,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:
@@ -1892,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:
@@ -1909,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':
@@ -2158,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)
@@ -3229,6 +3237,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)
@@ -3260,14 +3273,16 @@ 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))
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/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/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/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
new file mode 100644
index 000000000000..00da1c8e6cf5
--- /dev/null
+++ b/test cases/common/273 both libraries/meson.build
@@ -0,0 +1,113 @@
+project(
+ 'test both libraries',
+ 'c',
+ meson_version: '>= 1.6.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',
+ 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
+
+main = executable(
+ 'main',
+ files('src/main.c'),
+ c_args: [f'-DEXPECTED=@expected@'],
+ link_with: main_links,
+ dependencies: main_deps,
+)
+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: 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
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/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/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
new file mode 100644
index 000000000000..bdd965f7fb96
--- /dev/null
+++ b/test cases/common/273 both libraries/src/library.c
@@ -0,0 +1,12 @@
+#include "library.h"
+#include "both_libraries.h"
+
+int library_function(void)
+{
+ int sum = both_libraries_function();
+#if defined EXPORT
+ return sum + 1;
+#else
+ return sum;
+#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..1b367896d189
--- /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 : 1;
+}
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..2aba26e48a3a
--- /dev/null
+++ b/test cases/common/273 both libraries/test.json
@@ -0,0 +1,20 @@
+{
+ "matrix": {
+ "options": {
+ "default_library": [
+ { "val": "shared" },
+ { "val": "static" },
+ { "val": "both" }
+ ],
+ "default_both_libraries": [
+ { "val": "shared" },
+ { "val": "static" },
+ { "val": "auto" }
+ ],
+ "use_dep": [
+ { "val": false },
+ { "val": true }
+ ]
+ }
+ }
+}