Reputation: 2983
I use static libraries on linux. I have two static libriaries ("implementers") that share interface which is packed into third library ("common"). Exactly one of them (implementers) is needed to build binary. I need to build both versions of exec at the same time (separate targets). However I can create static libraries that use only common part. There is so many of them that
I want CMake to protect me from creating executables that are missing an implementer. This is needed because finding out about this takes long time (long compilation, even longer linking).
Pseudo-c++ and cmake look like following (I simplified by ignoring includes, which are actually important part of "common" lib).
// libFooBar - "common"
void foo(); // only declaration for includers. Is implemented by implementers
void bar() {
foo();
}
// libFooBarPrintf - "implementer1"
void foo() {
printf("Foo");
}
// libFooBarCout - "implementer2"
void foo() {
std::cout<<"Foo";
}
// userlib - uses only common interface
void useBar() {
bar();
}
// foobar_user.exe
// needs "common" for compilation and one of "implementers" for linking
int main () {
useBar();
return 0;
}
add_library(foobar STATIC foobar.cpp)
add_library(foobar_printf STATIC foobar_printf.cpp)
add_library(foobar_cout STATIC foobar_cout.cpp)
add_library( userlib STATIC userlib.cpp)
target_link_libraries(userlib PUBLIC foobar)
# this passes cmake, but fails to link with undefined ref to foo()
add_executable( user_bad main.cpp)
target_link_libraries(user_bad userlib)
#those two are working as intended
add_executable( user_printf main.cpp)
target_link_libraries(user_printf userlib foobar_printf)
add_executable( user_cout main.cpp)
target_link_libraries(user_cout userlib foobar_cout)
I want to achieve cmake error on user_bad
executable but not on userlib
library. I imagine it to look like (very-pseudo cmake):
set_target_properties(foobar_printf foobar_cout
PROPERTIES
provides_implementation_for_foobar 1)
set_target_properties(foobar
PROPERTIES
INTERFACE_FOR_EXECUTABLES_needs_property provides_implementation_for_foobar)
Is this possible?
Duplicating userlib to two versions (1 per implementer) is not scalable for my project.
Upvotes: 0
Views: 68
Reputation: 54589
This is problematic. You create a target with incomplete dependencies (foobar
), which CMake very much does not expect you to do, and is in no way obliged to handle correctly.
One way to get out of this without having to duplicate the libraries everywhere is through the use of object libraries:
add_library(foobar OBJECT ...)
add_library(foobar_printf STATIC foobar_printf.cpp $<TARGET_OBJECTS:foobar>)
add_executable(user_printf main.cpp)
target_link_libraries(user_printf userlib foobar_printf)
Take care though to at no point in the build tree end up with an incomplete target.
For instance, taking into account your example where foobar
is being consumed by an intermediate userlib
: At the point of declaration of userlib
you will need to decide which implementation to use. Otherwise you would end up with the same problematic situation as before, only now at the level of userlib
.
If you absolutely 100% need to defer this decision to the point of declaration of the executable, you should push it all the way into runtime and use dynamic libraries instead.
Note that while technically you may be able to solve this differently for certain build generators, if you're looking for a clean and robust solution, those are the only viable options I see.
Upvotes: 3