diff --git a/docs/markdown/snippets/shared_static_only_args.md b/docs/markdown/snippets/shared_static_only_args.md new file mode 100644 index 000000000000..0963c714bdf3 --- /dev/null +++ b/docs/markdown/snippets/shared_static_only_args.md @@ -0,0 +1,9 @@ +## `_(shared|static)_args` for both_library, library, and build_target + +We now allow passing arguments like `c_static_args` and `c_shared_args`. This +allows a [[both_libraries]] to have arguments specific to either the shared or +static library, as well as common arguments to both. + +There is a drawback to this, since Meson now cannot re-use object files between +the static and shared targets. This could lead to much higher compilation time +when using a [[both_libraries]] if there are many sources. diff --git a/docs/yaml/functions/library.yaml b/docs/yaml/functions/library.yaml index f9e336b9b808..1d406f13c039 100644 --- a/docs/yaml/functions/library.yaml +++ b/docs/yaml/functions/library.yaml @@ -16,6 +16,12 @@ description: | The keyword arguments for this are the same as for [[build_target]] +warnings: + - using _shared_args and/or _static_args may lead to much higher + compilation times with both_library, as object files cannot be shared between + the static and shared targets. It is guaranteed to not duplicate the build if + these arguments are empty arrays + posargs_inherit: _build_target_base varargs_inherit: _build_target_base kwargs_inherit: @@ -32,3 +38,31 @@ kwargs: type being build. - 'c': Create a "cdylib" or "staticlib" crate depending on the library type being build. + + _static_args: + type: list[str] + since: 1.3.0 + description: + Arguments that are only passed to a static library + + vala_static_args: + type: list[str | file] + since: 1.3.0 + description: + Arguments that are only passed to a static library + + Like `vala_args`, [[files]] is allowed in addition to string + + _shared_args: + type: list[str] + since: 1.3.0 + description: + Arguments that are only passed to a shared library + + vala_shared_args: + type: list[str | file] + since: 1.3.0 + description: + Arguments that are only passed to a shared library + + Like `vala_args`, [[files]] is allowed in addition to string diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 5ff5c0b5001d..23ce14620a64 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -216,7 +216,9 @@ def dump_value(self, arr, list_sep, indent): known_library_kwargs = ( build.known_shlib_kwargs | - build.known_stlib_kwargs + build.known_stlib_kwargs | + {f'{l}_shared_args' for l in compilers.all_languages - {'java'}} | + {f'{l}_static_args' for l in compilers.all_languages - {'java'}} ) known_build_target_kwargs = ( @@ -3202,6 +3204,9 @@ def build_both_libraries(self, node: mparser.BaseNode, args: T.Tuple[str, Source # FIXME: rustc supports generating both libraries in a single invocation, # but for now compile twice. reuse_object_files = False + elif any(k.endswith(('static_args', 'shared_args')) and v for k, v in kwargs.items()): + # Ensure not just the keyword arguments exist, but that they are non-empty. + reuse_object_files = False else: reuse_object_files = static_lib.pic @@ -3336,6 +3341,16 @@ def build_target_decorator_caller(self, node, args, kwargs): raise RuntimeError('Unreachable code') self.kwarg_strings_to_includedirs(kwargs) self.__process_language_args(kwargs) + if targetclass is build.StaticLibrary: + for lang in compilers.all_languages - {'java'}: + deps, args = self.__convert_file_args(kwargs.get(f'{lang}_static_args', [])) + kwargs['language_args'][lang].extend(args) + kwargs['depend_files'].extend(deps) + elif targetclass is build.SharedLibrary: + for lang in compilers.all_languages - {'java'}: + deps, args = self.__convert_file_args(kwargs.get(f'{lang}_shared_args', [])) + kwargs['language_args'][lang].extend(args) + kwargs['depend_files'].extend(deps) # Filter out kwargs from other target types. For example 'soversion' # passed to library() when default_library == 'static'. diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 45cb07b3d495..2f3f37f67908 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -397,6 +397,35 @@ class Library(_BuildTarget, _SharedLibMixin, _StaticLibMixin, _LibraryMixin): """For library, both_library, and as a base for build_target""" + c_static_args: NotRequired[T.List[str]] + c_shared_args: NotRequired[T.List[str]] + cpp_static_args: NotRequired[T.List[str]] + cpp_shared_args: NotRequired[T.List[str]] + cuda_static_args: NotRequired[T.List[str]] + cuda_shared_args: NotRequired[T.List[str]] + fortran_static_args: NotRequired[T.List[str]] + fortran_shared_args: NotRequired[T.List[str]] + d_static_args: NotRequired[T.List[str]] + d_shared_args: NotRequired[T.List[str]] + objc_static_args: NotRequired[T.List[str]] + objc_shared_args: NotRequired[T.List[str]] + objcpp_static_args: NotRequired[T.List[str]] + objcpp_shared_args: NotRequired[T.List[str]] + rust_static_args: NotRequired[T.List[str]] + rust_shared_args: NotRequired[T.List[str]] + vala_static_args: NotRequired[T.List[T.Union[str, File]]] # Yes, Vala is really special + vala_shared_args: NotRequired[T.List[T.Union[str, File]]] # Yes, Vala is really special + cs_static_args: NotRequired[T.List[str]] + cs_shared_args: NotRequired[T.List[str]] + swift_static_args: NotRequired[T.List[str]] + swift_shared_args: NotRequired[T.List[str]] + cython_static_args: NotRequired[T.List[str]] + cython_shared_args: NotRequired[T.List[str]] + nasm_static_args: NotRequired[T.List[str]] + nasm_shared_args: NotRequired[T.List[str]] + masm_static_args: NotRequired[T.List[str]] + masm_shared_args: NotRequired[T.List[str]] + class BuildTarget(Library): diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index 4b3a8a422a34..153a0d9e92bc 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -665,7 +665,7 @@ def _convert_darwin_versions(val: T.List[T.Union[str, int]]) -> T.Optional[T.Tup _EXCLUSIVE_SHARED_LIB_KWS: T.List[KwargInfo] = [ _DARWIN_VERSIONS_KW, KwargInfo('soversion', (str, int, NoneType), convertor=lambda x: str(x) if x is not None else None), - KwargInfo('version', (str, NoneType), validator=_validate_shlib_version) + KwargInfo('version', (str, NoneType), validator=_validate_shlib_version), ] # The total list of arguments used by SharedLibrary @@ -712,6 +712,13 @@ def _convert_darwin_versions(val: T.List[T.Union[str, int]]) -> T.Optional[T.Tup for a in _LANGUAGE_KWS], ] +_SHARED_STATIC_ARGS: T.List[KwargInfo[T.List[str]]] = [ + *[l.evolve(name=l.name.replace('_', '_static_'), since='1.3.0') + for l in _LANGUAGE_KWS], + *[l.evolve(name=l.name.replace('_', '_shared_'), since='1.3.0') + for l in _LANGUAGE_KWS], +] + # Arguments used by both_library and library LIBRARY_KWS = [ *_BUILD_TARGET_KWS, @@ -719,6 +726,7 @@ def _convert_darwin_versions(val: T.List[T.Union[str, int]]) -> T.Optional[T.Tup *_EXCLUSIVE_SHARED_LIB_KWS, *_EXCLUSIVE_SHARED_MOD_KWS, *_EXCLUSIVE_STATIC_LIB_KWS, + *_SHARED_STATIC_ARGS, _VS_MODULE_DEFS_KW, _JAVA_LANG_KW, ] @@ -730,6 +738,7 @@ def _convert_darwin_versions(val: T.List[T.Union[str, int]]) -> T.Optional[T.Tup *_EXCLUSIVE_SHARED_MOD_KWS, *_EXCLUSIVE_STATIC_LIB_KWS, *_EXCLUSIVE_EXECUTABLE_KWS, + *_SHARED_STATIC_ARGS, *[a.evolve(deprecated='1.3.0', deprecated_message='The use of "jar" in "build_target()" is deprecated, and this argument is only used by jar()') for a in _EXCLUSIVE_JAR_KWS], KwargInfo( diff --git a/test cases/common/178 bothlibraries/meson.build b/test cases/common/178 bothlibraries/meson.build index 843a607a1e89..62f2061f8d67 100644 --- a/test cases/common/178 bothlibraries/meson.build +++ b/test cases/common/178 bothlibraries/meson.build @@ -55,9 +55,17 @@ test('runtest-both-2', exe_both2) # the executable linking using the C compiler. # https://github.com/Netflix/vmaf/issues/1107 libccpp = both_libraries('ccpp', 'foo.cpp', 'libfile.c', - cpp_args : ['-std=c++11']) + cpp_args : ['-std=c++11'], + c_static_args : ['-DSTATIC_COMPILATION'], + cpp_static_args : ['-DSTATIC_COMPILATION'], +) exe = executable('prog-ccpp', 'main2.c', link_with: libccpp.get_static_lib(), c_args : ['-DSTATIC_COMPILATION'], ) test('runtest-ccpp', exe) + +exe = executable('prog-ccpp-shared', 'main2.c', + link_with: libccpp.get_shared_lib(), +) +test('runtest-ccpp-shared', exe) diff --git a/test cases/common/3 static/lib3.c b/test cases/common/3 static/lib3.c new file mode 100644 index 000000000000..f834cf8b8422 --- /dev/null +++ b/test cases/common/3 static/lib3.c @@ -0,0 +1,11 @@ +int func3(const int x) { + return x + 1; +} + +#ifndef WORK +# error "did not get static only C args" +#endif + +#ifdef BREAK +# error "got shared only C args, but shouldn't have" +#endif diff --git a/test cases/common/3 static/meson.build b/test cases/common/3 static/meson.build index 04ff2f6f301e..1127ecb447f7 100644 --- a/test cases/common/3 static/meson.build +++ b/test cases/common/3 static/meson.build @@ -1,4 +1,4 @@ -project('static library test', 'c') +project('static library test', 'c', default_options : ['default_library=static']) lib = static_library('mylib', get_option('source'), link_args : '-THISMUSTNOBEUSED') # Static linker needs to ignore all link args. @@ -12,3 +12,8 @@ endif assert(has_not_changed, 'Static library has changed.') assert(not is_disabler(lib), 'Static library is a disabler.') + +if get_option('default_library') == 'static' + library('lib2', 'lib3.c', c_static_args : ['-DWORK'], c_shared_args : ['-DBREAK']) +endif +build_target('lib4', 'lib3.c', c_static_args : ['-DWORK'], target_type : 'static_library') diff --git a/test cases/common/4 shared/libfile2.c b/test cases/common/4 shared/libfile2.c new file mode 100644 index 000000000000..fee1d1efd872 --- /dev/null +++ b/test cases/common/4 shared/libfile2.c @@ -0,0 +1,22 @@ +#if defined _WIN32 || defined __CYGWIN__ + #define DLL_PUBLIC __declspec(dllexport) +#else + #if defined __GNUC__ + #define DLL_PUBLIC __attribute__ ((visibility("default"))) + #else + #pragma message ("Compiler does not support symbol visibility.") + #define DLL_PUBLIC + #endif +#endif + +#ifndef WORK +# error "Did not get shared only arguments" +#endif + +#ifdef BREAK +# error "got static only C args, but shouldn't have" +#endif + +int DLL_PUBLIC libfunc(void) { + return 3; +} diff --git a/test cases/common/4 shared/meson.build b/test cases/common/4 shared/meson.build index 1c88bc5877d6..7f79ad6302f4 100644 --- a/test cases/common/4 shared/meson.build +++ b/test cases/common/4 shared/meson.build @@ -1,4 +1,4 @@ -project('shared library test', 'c') +project('shared library test', 'c', default_options : ['default_library=shared']) lib = shared_library('mylib', 'libfile.c') build_target('mylib2', 'libfile.c', target_type: 'shared_library') @@ -11,3 +11,8 @@ endif assert(has_not_changed, 'Shared library has changed.') assert(not is_disabler(lib), 'Shared library is a disabler.') + +if get_option('default_library') == 'shared' + library('mylib5', 'libfile2.c', c_shared_args : ['-DWORK']) +endif +build_target('mylib4', 'libfile2.c', target_type: 'shared_library', c_shared_args : ['-DWORK'], c_static_args : ['-DBREAK']) diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index f5048837f77a..18ed3bf5c193 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -394,7 +394,7 @@ def test_static_library_overwrite(self): self.init(testdir) # Get name of static library targets = self.introspect('--targets') - self.assertEqual(len(targets), 1) + self.assertGreaterEqual(len(targets), 1) libname = targets[0]['filename'][0] # Build and get contents of static library self.build()