From 3374a8586df83f499da1718cb2db6418c5b961cf Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 13 Jul 2023 10:26:14 -0700 Subject: [PATCH] interpreter: add _(static|shared)_args Which allow passing arguments specifically to the static or shared libraries. For design, this is all handled in the interpreter, by the build layer the arguments are combined into the existing fields. This limits changes required in the mid and backend layers --- .../snippets/shared_static_only_args.md | 9 +++++ docs/yaml/functions/library.yaml | 34 +++++++++++++++++++ mesonbuild/interpreter/interpreter.py | 17 +++++++++- mesonbuild/interpreter/kwargs.py | 29 ++++++++++++++++ mesonbuild/interpreter/type_checking.py | 11 +++++- .../common/178 bothlibraries/meson.build | 10 +++++- test cases/common/3 static/lib3.c | 11 ++++++ test cases/common/3 static/meson.build | 7 +++- test cases/common/4 shared/libfile2.c | 22 ++++++++++++ test cases/common/4 shared/meson.build | 7 +++- unittests/allplatformstests.py | 2 +- 11 files changed, 153 insertions(+), 6 deletions(-) create mode 100644 docs/markdown/snippets/shared_static_only_args.md create mode 100644 test cases/common/3 static/lib3.c create mode 100644 test cases/common/4 shared/libfile2.c 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..09b325ba12cc --- /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_library]] 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_library]] 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()