This in-part has been discussed before, though in a very scattered manner, so I wanted to seek clarification of what I’ve come to find here, all in one place.
In this situation there is a lib, and several applications that depend on this lib. Each project is handled separately and are not inherently part of the same source tree. I control the lib and applications.
My investigation started because in my applications I always want the lib to be built from source, while also being able to use its targets, hence I’m using FetchContent; however, I was surprised to find that when running cmake --install
the targets of my lib were also installed along with the app’s targets. After looking into this, I now understand why this happens (tl;dr because FetchContent uses add_subdirectory), so then I started looking into how to best circumvent this behavior.
To be frank, ideally there would be something that was a mix of FetchContent and ExternalProject, where I can still ultimately still include the lib at configure time using find_package()
, but instruct CMake how to download, build, and install that package in an isolated context before hand transparently (so that the targets are imported in the exact same way as if I had a local copy of the lib and used find_package). I know something like this is possible by using ExternalProject and a super build, but this requires restructuring and is obviously not at all transparent.
Overall, this led me down the path of how to use my lib with FetchContent, such that I can get as close to the above ideal as possible.
Relevant resources I perused:
-
FetchContent and find_package() integration (can’t link because of forum link limit
)
- Namespace support for target names in nested projects (nice idea but not implemented yet)
- FetchContent_Declare populate fails with option EXCLUDE_FROM_ALL
I already use the practice of “namespaced” targets to avoid name collisions and ensure a consistent
target interface between consuming the library via find_package() and add_subdirectory():
add_library(project_friendlylibname)
add_library(Project::FriendlyLibName ALIAS project_friendlylibname)
set_target_properties(project_friendlylibname PRROPERTIES
EXPORT_NAME FriendlyLibName
...
)
install(TARGETS project_friendlylibname
EXPORT FriendlyLibNameTargets
COMPONENT FriendlyLibName
...
)
install(EXPORT FriendlyLibNameTargets
NAMESPACE Project:::
...
)
export(EXPORT FriendlyLibNameTargets
NAMESPACE Project:::
...
)
# Other stripped out stuff...
As for the install
issue, without significant restructuring (i.e. super build), it seems like I have two reasonable options:
- When consuming, instead of FetchContent_MakeAvailable, do:
if(NOT lib_POPULATED)
FetchContent_Populate(lib)
add_subdirectory(${lib_SOURCE_DIR} ${lib_BINARY_DIR} EXCLUDE_FROM_ALL)
endif()
Annoying, since it requires now less clean use of FetchContent in all dependent projects, but if this does the trick then its not really that bad.
P.S. It seems there was a similar solution of using something like set_directory_properties(PROPERTIES EXCLUDE_FROM_ALL YES)
before fetching, but this relied on what was ultimately decided as unintended behavior of that directory property, which was removed with !3863.
- Within the library I could check for
PROJECT_IS_TOP_LEVEL
, and if false, addEXCLUDE_FROM_ALL
to all relevant invocations of theinstall
command, so that when theall
target is installed in the consuming application, none of the library’s components will be installed unless they were explicitly specified (i.e. generally never). These seems reasonable to me, though it may have side effects I’m not thinking of.
So basically, are there any better/other approaches one could recommend for resolving this install issue, and are there any other best practices I should know of for making my library as consumer friendly with FetchContent as possible?
Hoping in the long run we keep getting closer to really straight forward inclusion of dependencies via CMake that other languages have intrinsically.
Can link to lib in question if desired (again, new user link limit).