From 933f665fe4ccee58fd5c062324b1124751fd4bf5 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 | 6 +++++ docs/yaml/functions/shared_library.yaml | 11 ++++++++++ docs/yaml/functions/static_library.yaml | 11 ++++++++++ mesonbuild/build.py | 5 +++-- mesonbuild/interpreter/interpreter.py | 11 +++++++++- mesonbuild/interpreter/type_checking.py | 10 +++++++-- test cases/common/3 static/lib3.c | 11 ++++++++++ test cases/common/3 static/meson.build | 8 ++++++- test cases/common/4 shared/libfile2.c | 22 +++++++++++++++++++ test cases/common/4 shared/meson.build | 8 ++++++- unittests/allplatformstests.py | 2 +- 12 files changed, 106 insertions(+), 8 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..4af4ea5042c6 --- /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 f10ef8e88c84..803e0da4b447 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: diff --git a/docs/yaml/functions/shared_library.yaml b/docs/yaml/functions/shared_library.yaml index 956fb2cb2ce1..919ad55e9d94 100644 --- a/docs/yaml/functions/shared_library.yaml +++ b/docs/yaml/functions/shared_library.yaml @@ -6,6 +6,11 @@ posargs_inherit: _build_target_base varargs_inherit: _build_target_base kwargs_inherit: _build_target_base +warnings: + - using _shared_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 this is an empty array + kwargs: version: type: str @@ -44,3 +49,9 @@ kwargs: description: | Specify a Microsoft module definition file for controlling symbol exports, etc., on platforms where that is possible (e.g. Windows). + + _shared_args: + type: list[str | file] + since: 1.3.0 + description: + Arguments that are only passed to a shared library diff --git a/docs/yaml/functions/static_library.yaml b/docs/yaml/functions/static_library.yaml index 1d42d600a4b6..de254c01afb2 100644 --- a/docs/yaml/functions/static_library.yaml +++ b/docs/yaml/functions/static_library.yaml @@ -6,6 +6,11 @@ posargs_inherit: _build_target_base varargs_inherit: _build_target_base kwargs_inherit: _build_target_base +warnings: + - using _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 this is an empty array + kwargs: pic: type: bool @@ -23,3 +28,9 @@ kwargs: If `true` the object files in the target will be prelinked, meaning that it will contain only one prelinked object file rather than the individual object files. + + _static_args: + type: list[str | file] + since: 1.3.0 + description: + Arguments that are only passed to a static library diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 32e57c3ff16c..a595016e1224 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -115,9 +115,9 @@ cs_kwargs) known_exe_kwargs = known_build_target_kwargs | {'implib', 'export_dynamic', 'pie'} -known_shlib_kwargs = known_build_target_kwargs | {'version', 'soversion', 'vs_module_defs', 'darwin_versions'} +known_shlib_kwargs = known_build_target_kwargs | {f'{l}_shared_args' for l in all_languages} | {'version', 'soversion', 'vs_module_defs', 'darwin_versions'} known_shmod_kwargs = known_build_target_kwargs | {'vs_module_defs'} -known_stlib_kwargs = known_build_target_kwargs | {'pic', 'prelink'} +known_stlib_kwargs = known_build_target_kwargs | {f'{l}_static_args' for l in all_languages} | {'pic', 'prelink'} known_jar_kwargs = known_exe_kwargs | {'main_class', 'java_resources'} def _process_install_tag(install_tag: T.Optional[T.List[T.Optional[str]]], @@ -143,6 +143,7 @@ def get_target_macos_dylib_install_name(ld) -> str: class InvalidArguments(MesonException): pass + @dataclass(eq=False) class DependencyOverride(HoldableObject): dep: dependencies.Dependency diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index c9b895e4ce88..7a769b2a298e 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3164,7 +3164,7 @@ def add_target(self, name: str, tobj: build.Target) -> None: self.coredata.target_guids[idname] = str(uuid.uuid4()).upper() @FeatureNew('both_libraries', '0.46.0') - def build_both_libraries(self, node, args, kwargs): + def build_both_libraries(self, node, args, kwargs: T.Dict[str, T.Any]): shared_lib = self.build_target(node, args, kwargs, build.SharedLibrary) static_lib = self.build_target(node, args, kwargs, build.StaticLibrary) @@ -3176,6 +3176,9 @@ def build_both_libraries(self, node, args, kwargs): # Feel free to submit patches to get this fixed if it is an # issue for you. 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 @@ -3260,6 +3263,12 @@ def build_target_decorator_caller(self, node, args, kwargs): raise RuntimeError('Unreachable code') self.kwarg_strings_to_includedirs(kwargs) self.__extract_language_args(kwargs) + if targetclass is build.StaticLibrary: + for lang in compilers.all_languages - {'java'}: + kwargs['language_args'][lang].extend(kwargs[f'{lang}_static_args']) + elif targetclass is build.SharedLibrary: + for lang in compilers.all_languages - {'java'}: + kwargs['language_args'][lang].extend(kwargs[f'{lang}_shared_args']) # Filter out kwargs from other target types. For example 'soversion' # passed to library() when default_library == 'static'. diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index b188eb990efa..b5573fd83e10 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -516,7 +516,10 @@ def link_whole_validator(values: T.List[T.Union[StaticLibrary, CustomTarget, Cus # Arguments exclusive to StaticLibrary. These are separated to make integrating # them into build_target easier -_EXCLUSIVE_STATIC_LIB_KWS: T.List[KwargInfo] = [] +_EXCLUSIVE_STATIC_LIB_KWS: T.List[KwargInfo] = [ + *[l.evolve(name=f'{l.name.split("_", 1)[0]}_static_args', since='1.3.0') + for l in _LANGUAGE_KWS], +] # The total list of arguments used by StaticLibrary STATIC_LIB_KWS = [ @@ -527,7 +530,10 @@ def link_whole_validator(values: T.List[T.Union[StaticLibrary, CustomTarget, Cus # Arguments exclusive to SharedLibrary. These are separated to make integrating # them into build_target easier -_EXCLUSIVE_SHARED_LIB_KWS: T.List[KwargInfo] = [] +_EXCLUSIVE_SHARED_LIB_KWS: T.List[KwargInfo] = [ + *[l.evolve(name=f'{l.name.split("_", 1)[0]}_shared_args', since='1.3.0') + for l in _LANGUAGE_KWS], +] # The total list of arguments used by SharedLibrary SHARED_LIB_KWS = [ 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..c31843089cb9 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,9 @@ 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 +static_library('lib3', 'lib3.c', c_static_args : ['-DWORK']) +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..47e6b5a2d3aa 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,9 @@ 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 +shared_library('mylib3', 'libfile2.c', c_shared_args : ['-DWORK']) +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 b4bf3714a4e8..802129d52534 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -362,7 +362,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()