FetchContent: Reduce amount of boilerplate required for population
TL;DR:
- FetchContent boilerplate is unnecessarily large for bootstrapping common libraries amongst dependencies when a superbuild is not desired nor feasible
- We should permit some kind of a
FetchContentOnce
call to be made before the first call toproject()
. If it has been made already, then this function becomes a no-op - Possibly store the FetchContentOnce call into a special directory
- This function should error when in
cmake -P
script mode. The full FetchContent boilerplate is necessary there.
Hello!
This issue is based off of a twitter thread between myself and @craig.scott. Feel free to change the issue title, it might not be accurate enough. Effectively, I'm writing a CMake library that handles "bootstrapping" projects. If your project is root, you need to download the bootstrapping library before your initial call to project()
(but after cmake_minimum_required
). Currently, as shown in the twitter thread, each user that might want to use this library must currently copy and paste
cmake_minimum_required(VERSION <NN>.<NN>)
include(FetchContent)
FetchContent_Declare(ixm URL https://hub.aliasa.io/slurps-mad-rips/ixm)
FetchContent_GetProperties(ixm)
if(NOT ixm_POPULATED)
FetchContent_Populate(ixm)
add_subdirectory(${ixm_SOURCE_DIR} ${ixm_BINARY_DIR})
endif()
project(<project-name>)
Into each and every single project. This becomes tedious and unwieldly.
My request here is that an additional function, such as FetchContentOnce
or some other bikesheddable name, be added to the FetchContent module that is only ever invoked once regardless of name or resource. This would then reduce the above boilerplate to
cmake_minimum_required(VERSION <NN>.<NN>)
include(FetchContent)
FetchContentOnce(ixm URL https://hub.aliasa.io/slurps-mad-rips/ixm)
project(<project-name>)
And then if any child projects would make this call it wouldn't do anything, as the resource was already populated via the call to FetchContentOnce
Additionally, we cannot always know the name of a project, so asking users to use the CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE
is also unfeasible and just as error prone.
Lastly, this function would ONLY be available when not run in cmake -P
script mode. If it is, an error should occur. FetchContentOnce can be implemented with the following.
function (FetchContentOnce)
if (DEFINED CMAKE_SCRIPT_MODULE_FILE)
message(FATAL_ERROR "FetchContentOnce is disabled under cmake(1) -P script mode")
endif()
if (DEFINED PROJECT_NAME)
return()
endif()
set(once cmake-fetch-content-once) # place whatever name we decide upon here
FetchContent_Declare(${once} ${ARGN})
FetchContent_GetProperties(${once})
if (NOT ${once}_POPULATED)
FetchContent_Populate(${once})
# Can't add a subdirectory if it's not technically a subdirectory
if (EXISTS "${${once}_SOURCE_DIR}/CMakeLists.txt")
add_subdirectory(${${once}_SOURCE_DIR} ${${once}_BINARY_DIR})
endif()
endif()
endfunction()
This approach:
- Is not usable in
cmake -P
script mode - Is only ever called once (assuming users don't undefine PROJECT_NAME, but I'm sure there's SOME way to ensure this is ever actually only called once)
- Doesn't
add_subdirectory
unless we know for sure it's safe to do so. - Doesn't take a name, meaning that while a collision of content might occur, the call will only ever execute safely once
Whether all of the features for this implementation are fair or not, I figure starting a discussion for this is necessary (this also helps keep it on @craig.scott's radar if it is deemed a good enough idea :)