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 31a1ccfee63c..6e03e47c1a39 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]]], diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 03a3dec42dcd..fb5886533e7d 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3169,7 +3169,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) @@ -3181,6 +3181,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 @@ -3265,6 +3268,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 346baf9f7841..f1af8efb1272 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -531,7 +531,10 @@ def _validate_win_subsystem(value: T.Optional[str]) -> T.Optional[str]: # 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 = [ @@ -542,7 +545,10 @@ def _validate_win_subsystem(value: T.Optional[str]) -> T.Optional[str]: # 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 f7fb1b0c5f00..7d9ad34d28d8 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()