From 58881ce1b8d400ec93482eb62fd7b568454a8296 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 | 4 ++-- mesonbuild/interpreter/interpreter.py | 15 ++++++++++++- mesonbuild/interpreter/type_checking.py | 9 ++++++-- 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, 108 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 f9e336b9b808..28b23d51eb74 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 5076b9341b2a..b78371a25cfa 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 @@ -52,3 +57,9 @@ kwargs: Set the specific ABI to compile (when compiling rust). - 'rust' (default): Create a "dylib" crate. - 'c': Create a "cdylib" crate. + + _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 615baa229976..262efa36738b 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 @@ -31,3 +36,9 @@ kwargs: Set the specific ABI to compile (when compiling rust). - 'rust' (default): Create a "rlib" crate. - 'c': Create a "staticlib" crate. + + _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 63f4c1bee544..302b35ff17a1 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', 'rust_abi'} +known_shlib_kwargs = known_build_target_kwargs | {f'{l}_shared_args' for l in all_languages} | {'version', 'soversion', 'vs_module_defs', 'darwin_versions', 'rust_abi'} known_shmod_kwargs = known_build_target_kwargs | {'vs_module_defs', 'rust_abi'} -known_stlib_kwargs = known_build_target_kwargs | {'pic', 'prelink', 'rust_abi'} +known_stlib_kwargs = known_build_target_kwargs | {f'{l}_static_args' for l in all_languages} | {'pic', 'prelink', 'rust_abi'} known_jar_kwargs = known_exe_kwargs | {'main_class', 'java_resources'} def _process_install_tag(install_tag: T.Optional[T.List[T.Optional[str]]], diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 5216b98c3d40..f201a4b94b9b 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3194,7 +3194,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) @@ -3210,6 +3210,9 @@ def build_both_libraries(self, node, args, kwargs): # 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 @@ -3320,6 +3323,16 @@ 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'}: + args, deps = self.__convert_file_args(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'}: + args, deps = self.__convert_file_args(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/type_checking.py b/mesonbuild/interpreter/type_checking.py index 54fc23d769d1..e88b7e7f2003 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -612,7 +612,10 @@ def _convert_darwin_versions(val: T.List[T.Union[str, int]]) -> T.Optional[T.Tup # 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 = [ @@ -627,7 +630,9 @@ 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), + *[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 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 acab026e954c..93666e143111 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()