FetchContent_MakeHermetic¶
Set the configuration variables of Hermetic FetchContent for the content
Overview¶
This module allows to setup an Hermetic FetchContent configuration for a given content, augmenting what FetchContent can do by enabling consuming dependencies that are configured in a separate cmake execution.
The following shows a typical example of declaring content detail for an examplary dependency:
include(HermeticFetchContent)
# Hermetic FetchContent consumes information of FetchContent contents declared using the same name
FetchContent_Declare(
zstd
GIT_REPOSITORY https://github.com/facebook/zstd.git
GIT_TAG 63779c798237346c2b245c546c40b72a5a5913fe
SOURCE_SUBDIR build/cmake
)
# additional Hermetic FetchContent configuration
FetchContent_MakeHermetic(
zstd
HERMETIC_BUILD_SYSTEM cmake
)
# choose when this content gets build
HermeticFetchContent_MakeAvailableAtConfigureTime(zstd)
# OR
HermeticFetchContent_MakeAvailableAtBuildTime(zstd)
Hermetic FetchContent does consume information defined in FetchContent_Declare calls whenever possible
but enables additional configuration through the FetchContent_MakeHermetic()
command.
Commands¶
- FetchContent_MakeHermetic¶
FetchContent_MakeHermetic( <name> [HERMETIC_BUILD_SYSTEM cmake | autotools | openssl] [HERMETIC_TOOLCHAIN_EXTENSION <cmake code>] [HERMETIC_FIND_PACKAGES <list of hermetic content names>] [HERMETIC_CREATE_TARGET_ALIASES <cmake code>] [HERMETIC_PREPATCHED_RESOLVER <cmake code>] [HERMETIC_CMAKE_EXPORT_LIBRARY_DECLARATION <cmake code>] [HERMETIC_DISCOVER_TARGETS_FILE_PATTERN <regex pattern>] )
The
FetchContent_MakeHermetic()
function records options that describe the additional parameters required to populate and consume the specified content in a hermetic way.The content
<name>
can be any string without spaces, but good practice would be to use only letters, numbers and underscores.The
HERMETIC_BUILD_SYSTEM
allows selecting which build system scheme will be used to configure and build the specified content. Currently the available choices are:cmake
For contents that come with a working CMake buildsystem
autotools
For contents using the
GNU Autotools
as build systemopenssl
To consume the native openssl buildsystem
The
HERMETIC_TOOLCHAIN_EXTENSION
option enables injecting code into the toolchain used in the isolated build of the specified content, enabling to set the required build configuration without interfering with the build configuration of other parts of your project.FetchContent_MakeHermetic( rapidjson HERMETIC_BUILD_SYSTEM cmake HERMETIC_TOOLCHAIN_EXTENSION [=[ # don't build doc, examples & tests set(RAPIDJSON_BUILD_DOC OFF CACHE BOOL "" FORCE) set(RAPIDJSON_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) set(RAPIDJSON_BUILD_TESTS OFF CACHE BOOL "" FORCE) # use std::string set(RAPIDJSON_HAS_STDSTRING ON CACHE BOOL "" FORCE) ]=] )
Additionally to these dependency specific toolchain fragments Hermetic FetchContent is able to forward a defined set of CMake variables to the sub-builds by injecting values of the variables specified in the global
HERMETIC_FETCHCONTENT_FORWARDED_CMAKE_VARIABLES
into the generated proxy toolchain files:set(HERMETIC_FETCHCONTENT_FORWARDED_CMAKE_VARIABLES "CMAKE_BUILD_TYPE" "CMAKE_EXE_LINKER_FLAGS" "CMAKE_CXX_FLAGS" "CMAKE_C_FLAGS" )
The
HERMETIC_FIND_PACKAGES
option enables specifying which packages that are provided by other Hermetic FetchContent declarations will be able to befind_package
-ed by the content being specified. This enables controlling the packages available in the individual builds by other means than by just order of declaration.In this example, the library
libxml2
gets tofind_package()
Ìconv
andZLIB
. These libraries have to be made available using Hermetic FetchContent in the same build. Such a call tofind_package()
will fail for other packages that are not explicitely whitelisted, either using theHERMETIC_FIND_PACKAGES
option OR by adding said library to the globalHERMETIC_FETCHCONTENT_BYPASS_PROVIDER_FOR_PACKAGES
setting which allowsfind_package()
to fall-back to CMake's native implementation if it cannot resolve the package among the whitelisted Hermetic FetchContent builds.# omitting the FetchContent_Declare() call FetchContent_MakeHermetic( LibXml2 HERMETIC_BUILD_SYSTEM cmake HERMETIC_FIND_PACKAGES "Iconv;ZLIB" # <- this HERMETIC_TOOLCHAIN_EXTENSION [=[ set(LIBXML2_WITH_LZMA OFF) set(LIBXML2_WITH_PYTHON OFF) ]=] )
The
HERMETIC_CREATE_TARGET_ALIASES
options allows defining aliases for target during the configure phase. The CMake code provided will be invoked for every CMake target library that is exported by the content. The scope in which this code will be run contains the variableTARGET_NAME
which will be set to the exported target name. The provided code fragment must set thelist
TARGET_ALIASES
to contain all the names under which the exported target will be imported when it is consumed.It is recommended to test for specific values of
TARGET_NAME
instead of doing blanket declarations of aliases "no matter the input" to avoid issues in cases where a library might start to provide multiple exported libraries depending on the build configuration or just over time.In the example below, we will be renaming the target
ZLIB::zlib
that is exported by the project toZLIB::ZLIB
(notice the difference in casing). This can be required if for example another project is unable to find the library in it's original casing.FetchContent_Declare( ZLIB GIT_REPOSITORY https://github.com/cpp-pm/zlib.git GIT_TAG 57af136e436c5596e4f1c63fd5bdd2ce988777d1 ) FetchContent_MakeHermetic( ZLIB HERMETIC_BUILD_SYSTEM cmake HERMETIC_CREATE_TARGET_ALIASES [=[ if("${TARGET_NAME}" STREQUAL "ZLIB::zlib") set(TARGET_ALIASES "ZLIB::ZLIB") endif() ]=] ) HermeticFetchContent_MakeAvailableAtConfigureTime(ZLIB)
The option
HERMETIC_PREPATCHED_RESOLVER
enables defining a lookup method for source population. This is particularly useful when used in combination with thePATCH_COMMAND
setting inFetchContent_Declare()
because it allows the developper to store a pre-patched version of the source code of a library in a central location to avoid the burden of running potentially lengthy patch operations on every build while still retaining the capability of quickly trying out other versions of a dependency during upgrade trials.As for the previous option Hermetic FetchContent expects
HERMETIC_PREPATCHED_RESOLVER
to contain an executable fragment of CMake code (as a string) that is called in a isolated scope that contains the following variables with the values defined inFetchContent_Declare
:GIT_REPOSITORY
URL of the git repository. Any URL understood by the git command may be used.
GIT_TAG
Git branch name, tag or commit hash to be checked out
URL
List of paths and/or URL(s) of the external project's source.
URL_HASH
Hash of the archive file to be downloaded. Should be of the form <algo>=<hashValue> where algo can be any of the hashing algorithms supported by the file() command.
The CMake code can redefine any of these variables to change the source of the content. The CMake code must set
RESOLVED_PATCH
to a truthy value if it is able to set an alternate source for the content.If
RESOLVED_PATCH
is set to a truthy value the source populate operation will rely on the new information provided an skip the execution of thePATCH_COMMAND
. Otherwise the normal expected behavior is executed (e.g. downloading from the original source and running any specifiedPATCH_COMMAND
)In the following example we will assume that the project is storing a patched copy of the specified repository and revision of the original sources in it own github organization under a given commit ID:
FetchContent_Declare( boost GIT_REPOSITORY https://github.com/boostorg/boost.git # that's v1.84 GIT_TAG ad09f667e61e18f5c31590941e748ac38e5a81bf # apply the patch "some.patch" PATCH_COMMAND cmake -E patch < ${CMAKE_CURRENT_LIST_DIR}/patchtest/some.patch ) FetchContent_MakeHermetic( boost HERMETIC_BUILD_SYSTEM cmake HERMETIC_PREPATCHED_RESOLVER [=[ if(${GIT_TAG} STREQUAL "ad09f667e61e18f5c31590941e748ac38e5a81bf") # hypothetical <my-org>/boost-prepatched.git repo set(GIT_REPOSITORY "https://github.com/<my-org>/boost-prepatched.git") set(GIT_TAG "9c83f4a27e4227dbe02e4a47ede372ac2a4a043e") set(RESOLVED_PATCH TRUE) endif() ]=] ) HermeticFetchContent_MakeAvailableAtBuildTime(boost)
The developer can use the options
HERMETIC_CMAKE_EXPORT_LIBRARY_DECLARATION
to provide a a library export declaration that will enable Hermetic FetchContent to consume the targets as-if the project had provided a proper package configuration. This is useful in cases in which no such information is available (for example in the case of HERMETIC_BUILD_SYSTEM == autotools or HERMETIC_BUILD_SYSTEM == openssl or if the project's CMake buildsystem does not install and export targets)The following example shows how to define the
Pcap::Pcap
target properties required for Hermetic FetchContent to be able to construct a so-called "targets cache":FetchContent_MakeHermetic( Pcap HERMETIC_BUILD_SYSTEM autotools HERMETIC_CMAKE_EXPORT_LIBRARY_DECLARATION [=[ add_library(Pcap::Pcap STATIC IMPORTED) set_property( TARGET Pcap::Pcap PROPERTY IMPORTED_LOCATION "@HFC_PREFIX_PLACEHOLDER@/lib/libpcap.a" ) set_property( TARGET Pcap::Pcap PROPERTY INTERFACE_INCLUDE_DIRECTORIES "@HFC_PREFIX_PLACEHOLDER@/include" ) ]=] )
The following template variables are available to define the targets:
@HFC_SOURCE_DIR_PLACEHOLDER@
which will be replaced with the source directory location when the library is consumed later on.@HFC_BINARY_DIR_PLACEHOLDER@
which will be replaced with the binaries directory location when the library is consumed later on.@HFC_PREFIX_PLACEHOLDER@
which will be replaced with the final install prefix location when the library is consumed later on.
In cases in which the dependencie's CMake build system does provide target exports files but is not complying to the common file nameming scheme for those exports (hermetic fetchContent uses the following by default
([Tt]argets|[Ee]xport(s?))\.cmake
), another pattern can be supplied using theHERMETIC_DISCOVER_TARGETS_FILE_PATTERN
option.
- HermeticFetchContent_MakeAvailableAtBuildTime¶
HermeticFetchContent_MakeAvailableAtBuildTime( <name> )
The
HermeticFetchContent_MakeAvailableAtBuildTime()
function makes the content<name>
available at build time (basically only running the content's configure step) an exporting the required targets.After this call the content's targets are available for target linking and dependency management. Build graph matters (e.g. when and what to build) are left to the project's build system.
- HermeticFetchContent_MakeAvailableAtConfigureTime¶
HermeticFetchContent_MakeAvailableAtConfigureTime( <name> )
The
HermeticFetchContent_MakeAvailableAtConfigureTime()
function makes the content<name>
available to the project an other Hermetic FetchContent contents by running the complete build when this statement gets executed.This is particularly useful when integrating with other build systems that cannot be integrated in the project's build graph seamlessly and that - thus - require headers and libraries to be available in an installed location during their configure step.
- HermeticFetchContent_SetBaseDir¶
HermeticFetchContent_SetBaseDir( <directory> )
Set the base directory for all the hermetic dependency build directory and related folders
Build introspection¶
HermeticFetchContent adds a number of cmake build targets to the build system that can be executed after the project configuration to enable some build introspection:
- build target "hfc_list_dependencies_build_dirs"¶
Prints all project's dependencies build directories to the console. Warning! This list might be incomplete if the project build at hand did not actually build the whole list of dependencies
- build target "hfc_list_dependencies_install_dirs"¶
Prints all project's dependencies install directories to the console. Warning! Depending on the build state, the listed install trees might not be populated (yet).
- build target "hfc_echo_${content_name}_install_dir"¶
Prints the install directory for ${content_name}
on the console. Warning! Depending on the build state, the install tree might not be populated (yet).
- build target "hfc_list_${content_name}_STATIC_LIBRARIES_locations"¶
Prints the list of static libraries of ${content_name}
on the console.
Prints the list of shared libraries of ${content_name}
on the console.
- build target "hfc_list_${content_name}_MODULE_LIBRARIES_locations"¶
Prints the list of module libraries of ${content_name}
on the console.
- build target "hfc_list_${content_name}_UNKNOWN_LIBRARIES_locations"¶
Prints the list of 'unknown type' libraries of ${content_name}
on the console.
- build target "hfc_list_${content_name}_EXECUTABLES_locations"¶
Prints the list of executables of ${content_name}
on the console.
Rationale¶
- Ressembling add_subdirectory through external cmake execution is key to providing reusable install trees :
One project depending on a library can reuse the library built by another project
The only common element has to be the common CMAKE_TOOLCHAIN_FILE settings
Finally it allows a build graph optimization to be at maximum parallelity. Indeed usual CMake practices for externally built libraries found via find_package allows reusing the same libraries across project, but requires building them in advance before the configure of the dependent project might even be started. However not all target depend directly of the said dependencies.
With this techniques it can integrate the clean build of the dependencies in the graph, effectively building at the finest granularity of dependencies everything that isn't dependent of the dependencies. Possibly allowing building earlier parts of the project that do not depend on the dependency itself.
Plain add_subdirectory allows this but lacks the isolation from the parent CMake project, hindering the reuse of the dependent library install tree without the presence of the sources, which is a big speedup in CI workflows, and new developer computer setup, optimizing avoid the need of cloning the actual dependencies source files with all the git history and every private implementation files.