meson
meson copied to clipboard
What is the correct behavior when multiple `denpendency` are called with the same name and different configurations
Describe the bug
When multiple denpendency are called with the same name and different configurations, the system only selects the first one captured by meson.override_dependency
To Reproduce The example originated from https://github.com/mesonbuild/wrapdb/issues/881
Two files:
- main.cpp
int main()
{
return 0;
}
- meson.build
project('example', 'cpp', version: '0.0.0')
fmt1 = dependency('fmt', static: false)
fmt2 = dependency('fmt', static: true)
executable('example', 'main.cpp', dependencies: [fmt2])
run
mkdir subprojects
meson wrap install fmt
meson setup --wrap-mode=forcefallback builddir
meson compile -Cbuilddir
Expected behavior
We expect to build fmt as static library, but it is built as dynamic library.
The example seems artificial because normally we will not call denpendency multiple times with the same name and different configurations.
However, if a dependency in WrapDB calls another dependency and collides with the dependency called by user. The latter one (depending on the order they appear in meson.build) is ignored and causes confusion.
What is the correct approach to deal with this? I cannot come up with a general solution, but I think the ideal behavior should satisfy the following requirements:
- The user called
dependency('foo', static: true)should overridedependency('foo')called by other subprojects. - If the user called
dependency('foo', static: true)anddependency('foo', static: false)twice as two deps, we should (may be warn them and) build two versions without issues.
system parameters
- It is just a plain native build (for the same computer)
- operating system is Windows 10
- Python version is 3.10.8
meson --versionis 1.0.0ninja --versionis 1.11.0
Using a later copy is not an option.
Using two versions is not a general option either, I think -- this is an ODR violation and Meson enforces a single version of any subproject.
This can maybe get handled with default_library=both and having a cached dependency know how to handle both static and shared -- much like how you can use get_static_lib() on a both_libraries() object.
Otherwise I think we should just warn and continue on as we do today (or else error).
I think dependency('fmt', static: true) should be an error if the fmt subproject has already been configured with default_library=shared. We probably could do some clever stuff if fmt subproject has been configured with default_library=both.
I see 2 potential fixes for your case:
- The main project can have
default_options: 'fmt:default_library=static'. - If you want all subprojects to be static, @jpakkane had the idea a while back to have the new syntax
subprojects:default_library=static. Thesubprojects:prefix for an option would apply to all subprojects.
Another potential fix, but not easy, is to allow configuring a subproject multiple times. This has been requested many times to have both native and cross build of a subproject.
I could see configuring it twice, since native targets in a cross build don't get installed.
Building multiple native copies in a native build seems wrong, you would end up building tons of stuff you don't use, then installing the same things to the same location twice.
Thanks for quick replies!
I think the correct behavior should be:
For the first case (user requires static but subproject leaves static unspecified), default_options: 'fmt:default_library=static' is an acceptable workaround because it dominates all fmt afterward, but specifying static: true in dependency('fmt') should behave the same according to the documentation.
staticTells the dependency provider to try to get static libraries instead of dynamic ones (note that this is not supported by all dependency backends) Since 0.60.0 it also sets default_library option accordingly on the fallback subproject if it was not set explicitly in default_options keyword argument.
Either we stop declaring that static set default_library for fallback subproject and tell users that we do not ensure to build a static version of fallback subproject, the deterministic approach is to use default_options: 'foo:default_library=static' in main project or dependency('foo', default_options: 'default_library=static') or specify meson configure -Dfoo:default_library=static manually.
In this way, static is only a hint to find static dependency. The problem is that introducing new dependencies from WrapDB may influence the result of use-supplied dependency('foo', static: true) and confuses users because users are unaware of other calls to dependency('foo') that uses default value of foo:default_library = shared. We should definitely warn users about this (in documentation or in meson code).
Or we traverse all calls to dependency('fmt') to see whether there is a call where static: true is specified.
For the second case (user requires both versions of a dependency), we must ensure fmt:default_library=both or it is an error.
Any suggestions?
A side note:
In fact, default_library=both is not correctly handled by fmt (or other wraps) in WrapDB now, we should provide both versions of dep and call meson.override_dependency separately if default_library=both.
IMHO, deciding if a dependency should be static or shared is fundamentally a case-by-case user choice, using for example -Dprefer_static=true. Projects should rarely use static: true at all.
I think that static:true is actually a dependency-wise prefer_static.
IMHO, deciding if a dependency should be static or shared is fundamentally a case-by-case user choice, using for example
-Dprefer_static=true. Projects should rarely usestatic: trueat all.
A different perspective: not everything is a library or some other shared bit of code though. Some projects are the "end-user" with specific set of requirements targeting a very finite set of platforms and it's nice to have as much in the meson.build as possible (with as minimal effort...).
Anyway, all good and achievable. I just found the behaviour a bit unexpected!
Thanks
Aside from the static lib part, could anything be done in order to handle cases where you need the same subproject/dependency rebuilt with different options?