HermeticFetchContent - an improved way to handle dependencies in CMake without reinventing the wheel
FetchContent is a really powerful tool, but it has some sharp edges. HermeticFetchContent improves on the existing FetchContent capabilities.
written by @TheGrizzyDev // February 25, 2025HermeticFetchContent - an improved way to handle dependencies in CMake without reinventing the wheel
Handling dependencies with CMake
Dependency management has always been one of the most difficult topics in C++ development. CMake's answer to this challenge is twofold:
- FetchContent, which pulls a depenency at configure-time
- ExternalProject, which does the same thing at build-time
Both are extremely powerful and useful primitives.
In particular, FetchContent allows to use targets of the projects like find_package
enables which is otherwise impossible with ExternalProject, as the dependencies will be avaiable only at build time.
But despite being so powerful, FetchContent still has a few rough edges that we thought we could improve on. That's why we have been working on HermeticFetchContent, with the goal of making dependency management more reproducible in cmake, whilst making builds faster and more cacheable.
Options and dependencies isolation
Arguably the biggest problem with FetchContent is that when setting options from a dependency, one ends up setting them in the project and viceversa. Let's say if one wants to pull raylib, they'd end up doing something like this:
set(RAYLIB_VERSION 4.5.0)
FetchContent_Declare(
raylib
DOWNLOAD_EXTRACT_TIMESTAMP OFF
URL https://github.com/raysan5/raylib/archive/refs/tags/${RAYLIB_VERSION}.tar.gz
FIND_PACKAGE_ARGS
)
FetchContent_MakeAvailable(raylib)
As a dependency of the main project, we are not interested in raylib's examples to be built, this requires overriding BUILD_EXAMPLES
. What if the project has the same property to control whether to build the examples or not? In that case we'd end up changing that option as well. One way in which libraries authors have avoided this issue has been to namespace each property ( e.g. fmt
namespaces all the options with FMT_
). This is fine until one only need one version/configuration of a given library within a build.
HermeticFetchContent solves this problem by allowing to scope options. Let's go back to the previous example and see how we can disable BUILD_EXAMPLES
without impacting the broader project scope of the options.
set(RAYLIB_VERSION 4.5.0)
FetchContent_Declare(
raylib
URL https://github.com/raysan5/raylib/archive/refs/tags/${RAYLIB_VERSION}.tar.gz
URL_HASH MD5=feb608a91e27f71f9b33f0073177b1ee
FIND_PACKAGE_ARGS ${RAYLIB_VERSION} EXACT
)
FetchContent_MakeHermetic(
raylib
HERMETIC_BUILD_SYSTEM cmake
HERMETIC_TOOLCHAIN_EXTENSION [=[
set(BUILD_EXAMPLES OFF CACHE INTERNAL "")
]=]
)
HermeticFetchContent_MakeAvailableAtBuildTime(raylib)
With this change, even though we set BUILD_EXAMPLES
, the option won't be visible within the broader scope and thus the same option can be reused without any collision.
Granular parallelism
CMake superbuild
A common pattern to handle dependencies in CMake is to use a so-called superbuild to build all dependencies and then pass them to the project. The problem with this approach is that now all targets depend on building successfully all the targets from the dependencies, creating a bottleneck in the build, which only really starts after all dependencies have been build.
Hermetic FetchContent
HermeticFetchContent instead allows to depend on more granular targets, making it possible to build dependencies in parallel to parts of the project rather than one after the other. Cutting the wait-time on clean build and developer onboarding.
This is a feature of FetchContent itself, but one key ability that HermeticFetchContent provides is that it can automatically reuse prebuilt dependencies install trees. Thanks to the added hermeticity of HermeticFetchContent, dependencies are only rebuilt when there are changes that impact the dependency's ABI. This is particularly useful as it makes it possible to share dependencies across projects and users safely.
FetchContent with a source cache
HermeticFetchContent caches the sources of dependencies, by default it stores all fetched dependencies sources under thirdparty/cache
within the project. This way pre-cached downloads are retained even after clearing the build. To guarantee supply chain security and allow proper caching and invalidation, packages fetched with HermeticFetchContent enforce the need for an URL_HASH
or GIT_TAG
, which are used to check fetched content integrity upon downloading the package.
Foreign Build system support
Furthermore, FetchCotent is limited to fetching and using modern CMake dependencies only, a limitation that HermeticFetchContent does not share, as it can handle foreing build systems out of the box, ranging from CMake using autotools to OpenSSL's build system. It can even help generating modern CMake Targets declarations for foreign dependencies thanks to HERMETIC_CMAKE_EXPORT_LIBRARY_DECLARATION
.
And much more!
If this isn't enough, HermeticFetchContent also help generate SBOMs (SPDX), check for NTIA compliance, taking into account all the dependencies and licenses the project makes use of.
There's no shortage of reasons to try HFC, but if you need even more: it's open source and we'd love to hear what you think! Head over to the GitHub repo and try it out. And if you're feeling particularly brave (or over-caffeinated), why not contribute? Help us make it even better.

Antonio Di Stefano
@TheGrizzyDev
Software Engineer @ EngFlow