FetchContent Does Not Isolate Host from Target

Hey,

I’m not sure how to word the title correctly.
I’m working on integrating libarchive (GitHub - libarchive/libarchive: Multi-format archive and compression library) for a project. Because of the nature of the project, I’m working offline, so I’m using

FetchContent_Declare(libarchive_external
   SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libarchive-3.8.0"
   EXCLUDE_FROM_ALL
   SYSTEM
)
FetchContent_MakeAvailable(libarchive_external)

You can look at libarchive’s CMakeLists.txt, but it defines its project and sets a variable option(BUILD_SHARED_LIBS "Build shared libraries" ON). I can set this variable to OFF before the FetchContent_Declare, but if I don’t then my entire project is forced to use shared libraries.

This is affecting my host project. It’s forcing everything in the host project to be built as a shared library. Here’s the kicker: I’m not even linking against libarchive yet. Just the call with FetchContent is causing this issue. Shouldn’t EXCLUDE_FROM_ALL prevent this dependency from being built if nothing is using it?

Also, dependencies being built are not QUIET. I get every status message possible. From reading the documentation, I see that this is not the intended behavior.

Also, we have CMake warnings turned on for uninitialized variables and whatnot. Still, when using FetchContent, I see dev warnings for the dependency I’m building, which completely ruins using CMake warnings.

We’re using CMake 3.31. We’ll likely upgrade when we build a new dev docker image in the next month or so.

If this is intended behavior, then it shouldn’t be. It’s a poor intentional design that using FetchContent, which is supposed to be fairly analogous to ExternalProject_Add, can affect the build configuration of the host project, especially as there are CMake libraries on GitHub that allow people to get quickly started. I could almost foresee an xz style attack if you could identify a large library that uses FetchContent for some other dependency. I’m unsure how prevalent this is in the wild for large projects, but it’s a valid attack vector.

You are probably looking for ExternalProject instead of FetchContent.

FetchContent is designed to bring whatever you fetch directly into your build. It’s equivalent of getting the dependency extracted somewhere, and using add_subdirectory(). This is very much intentional design choice, but, as you have noticed, it leaks things into the project using it.

If you want the dependency build to be isolated from your build, I’d recommend going with the superbuild pattern and using ExternalProject_Add.

FetchContent has been designed in response to a demand for downloading parts of the project from external sources and using them as if they were your own sources. It’s your responsibility as an author to choose between FetchContent with all its cheviats, ExternalProject with all its cheviats, or vendoring / git submodules.

1 Like

Another option is to use a package manager for dependencies.

I gave a presentation on vcpkg a few years ago and I’ve been using it happily with CMake ever since.