diff --git a/docs/markdown/snippets/dep_as_static.md b/docs/markdown/snippets/dep_as_static.md new file mode 100644 index 000000000000..d084bcd869a3 --- /dev/null +++ b/docs/markdown/snippets/dep_as_static.md @@ -0,0 +1,10 @@ +## Allow the use of static version of `both_libraries` in internal dependencies + +Internal dependencies can now choose the static version +of a `both_libraries` linked library. This allows to use the same +dependency objects for building either a static or a shared library from +the same hierarchy of dependencies. + +`dep` object returned by [[declare_dependency]] now has `.as_static()` method, +to convert it to a dependency that prefer the `static` version of the linked + [[both_libraries]] targets. diff --git a/docs/yaml/objects/dep.yaml b/docs/yaml/objects/dep.yaml index d847690f0dd5..dc925c1a485f 100644 --- a/docs/yaml/objects/dep.yaml +++ b/docs/yaml/objects/dep.yaml @@ -210,3 +210,16 @@ methods: pkgconfig_define: type: list[str] description: See [[dep.get_pkgconfig_variable]] + + - name: as_static + returns: dep + since: 1.3.0 + description: | + Only 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 diff --git a/mesonbuild/build.py b/mesonbuild/build.py index ac2993fec487..45e967650f62 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -140,6 +140,12 @@ 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.Any], key: str, 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 @@ -769,8 +775,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) @@ -2483,6 +2489,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/dependencies/base.py b/mesonbuild/dependencies/base.py index c4861ff12116..581b2da87726 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -337,6 +337,15 @@ def generate_link_whole_dependency(self) -> Dependency: new_dep.libraries = [] return new_dep + def get_as_static(self, recursive: bool) -> Dependency: + from ..build import BothLibraries + + new_dep = copy.copy(self) + new_dep.libraries = [lib.static if isinstance(lib, BothLibraries) else lib for lib in self.libraries] + if recursive: + new_dep.ext_deps = [dep.get_as_static(True) if isinstance(dep, InternalDependency) else dep 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 cc83cbd47bb3..072e1610f674 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -695,6 +695,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'] diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index effeebb2019e..4ccaeb58fde8 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -41,6 +41,10 @@ class EnvironmentSeparatorKW(TypedDict): separator: str + class InternalDependencyAsStaticKW(TypedDict): + + recursive: bool + _ERROR_MSG_KW: KwargInfo[T.Optional[str]] = KwargInfo('error_message', (str, NoneType)) @@ -455,6 +459,7 @@ 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, }) def found(self) -> bool: @@ -566,6 +571,17 @@ 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.3.0') + @noPosargs + @typed_kwargs( + 'dependency.as_static', + KwargInfo('recursive', bool, default=False), + ) + def as_static_method(self, args: T.List[TYPE_var], kwargs: InternalDependencyAsStaticKW) -> 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']) + _EXTPROG = T.TypeVar('_EXTPROG', bound=ExternalProgram) class _ExternalProgramHolder(ObjectHolder[_EXTPROG]): diff --git a/test cases/common/267 dep as_static/lib1.c b/test cases/common/267 dep as_static/lib1.c new file mode 100644 index 000000000000..a479de250224 --- /dev/null +++ b/test cases/common/267 dep as_static/lib1.c @@ -0,0 +1,4 @@ +int libfunc1(void) +{ + return 1 + NUMBER; +} diff --git a/test cases/common/267 dep as_static/lib1.def b/test cases/common/267 dep as_static/lib1.def new file mode 100644 index 000000000000..3f09bb2fb4c7 --- /dev/null +++ b/test cases/common/267 dep as_static/lib1.def @@ -0,0 +1,3 @@ +EXPORTS + + libfunc1 @1 diff --git a/test cases/common/267 dep as_static/lib2.c b/test cases/common/267 dep as_static/lib2.c new file mode 100644 index 000000000000..9fb488c24ed7 --- /dev/null +++ b/test cases/common/267 dep as_static/lib2.c @@ -0,0 +1,4 @@ +int libfunc2(void) +{ + return 2 + NUMBER; +} diff --git a/test cases/common/267 dep as_static/lib2.def b/test cases/common/267 dep as_static/lib2.def new file mode 100644 index 000000000000..a5e26d995a9e --- /dev/null +++ b/test cases/common/267 dep as_static/lib2.def @@ -0,0 +1,3 @@ +EXPORTS + + libfunc2 @2 diff --git a/test cases/common/267 dep as_static/lib3.c b/test cases/common/267 dep as_static/lib3.c new file mode 100644 index 000000000000..6ab1beda2d37 --- /dev/null +++ b/test cases/common/267 dep as_static/lib3.c @@ -0,0 +1,4 @@ +int libfunc3(void) +{ + return 3; +} diff --git a/test cases/common/267 dep as_static/lib3.def b/test cases/common/267 dep as_static/lib3.def new file mode 100644 index 000000000000..2a8ab16b5cf9 --- /dev/null +++ b/test cases/common/267 dep as_static/lib3.def @@ -0,0 +1,3 @@ +EXPORTS + + libfunc3 @3 diff --git a/test cases/common/267 dep as_static/lib4.c b/test cases/common/267 dep as_static/lib4.c new file mode 100644 index 000000000000..f841c220891e --- /dev/null +++ b/test cases/common/267 dep as_static/lib4.c @@ -0,0 +1,4 @@ +int libfunc4(void) +{ + return 4; +} diff --git a/test cases/common/267 dep as_static/main.c b/test cases/common/267 dep as_static/main.c new file mode 100644 index 000000000000..1ec50d3b57b4 --- /dev/null +++ b/test cases/common/267 dep as_static/main.c @@ -0,0 +1,10 @@ +int libfunc1(void); +int libfunc2(void); +int libfunc3(void); +int libfunc4(void); + +int main(void) +{ + int sum = libfunc1() + libfunc2() + libfunc3() + libfunc4(); + return sum == EXPECTED ? 0 : sum; +} diff --git a/test cases/common/267 dep as_static/meson.build b/test cases/common/267 dep as_static/meson.build new file mode 100644 index 000000000000..da52390c5e6d --- /dev/null +++ b/test cases/common/267 dep as_static/meson.build @@ -0,0 +1,47 @@ +project( + 'as_static', + ['c'], + meson_version: '>= 1.3.0', +) + +lib1 = library('lib1', 'lib1.c', vs_module_defs: 'lib1.def', c_static_args: ['-DNUMBER=100'], c_shared_args: ['-DNUMBER=1000']) +dep1 = declare_dependency(link_with: lib1) + +lib2 = both_libraries('lib2', 'lib2.c', vs_module_defs: 'lib2.def', c_static_args: ['-DNUMBER=100'], c_shared_args: ['-DNUMBER=1000']) +dep2 = declare_dependency(link_with: lib2) + +lib3 = shared_library('lib3', 'lib3.c', vs_module_defs: 'lib3.def') +dep3 = declare_dependency(link_with: lib3) + +lib4 = static_library('lib4', 'lib4.c') +dep4 = declare_dependency(link_with: lib4) + + +if get_option('default_library') == 'static' + lib1_n = 101 +else + lib1_n = 1001 # shared or both will use shared +endif +lib2_n = 1002 # shared +lib3_n = 3 +lib4_n = 4 + +expected = lib1_n + lib2_n + lib3_n + lib4_n +dep_default = declare_dependency(dependencies: [dep1, dep2, dep3, dep4]) +main_default = executable('main_default', 'main.c', dependencies: [dep_default], c_args: ['-DEXPECTED=@0@'.format(expected)]) +test('default', main_default) + + +if get_option('default_library') == 'shared' + lib1_n = 1001 +else + lib1_n = 101 # static or both will use static +endif +lib2_n = 102 # static +lib3_n = 3 +lib4_n = 4 + +expected = lib1_n + lib2_n + lib3_n + lib4_n +dep_static = declare_dependency(dependencies: [dep1, dep2, dep3, dep4]).as_static(recursive: true) +main_static = executable('main_static', 'main.c', dependencies: [dep_static], c_args: ['-DEXPECTED=@0@'.format(expected)]) +test('static', main_static) diff --git a/test cases/common/267 dep as_static/test.json b/test cases/common/267 dep as_static/test.json new file mode 100644 index 000000000000..4e49a9ef2923 --- /dev/null +++ b/test cases/common/267 dep as_static/test.json @@ -0,0 +1,11 @@ +{ + "matrix": { + "options": { + "default_library": [ + { "val": "shared" }, + { "val": "static" }, + { "val": "both" } + ] + } + } +}