The issue I am facing with replacing FetchContent_Populate with FetchContent_MakeAvailable is that FetchContent_MakeAvailable seems to automatically “execute” CMakeLists.txt included in re-logging but I don’t want this to happen. FetchContent_Populate did not do that.
Use the SOURCE_SUBDIR option and set it to a path that doesn’t exist. Issue 26220 asks for a dedicated keyword to say “don’t add the subdirectory, only populate it”.
Many open source projects continued to use FetchContent_Populate() long after FetchContent_MakeAvailable() replaced it as the recommended way to populate content. That prevented those projects from being handled by dependency providers. The deprecation is a clear signal that projects relying on FetchContent_Populate() need to migrate. Everything you could do with FetchContent_Populate() before, you can do with FetchContent_MakeAvailable() now. My earlier comment explains how to use SOURCE_SUBDIR to prevent calling add_subdirectory() for those who were using FetchContent_Populate() to only download things but not add them to the project. That comment also linked to an issue that discusses that use case in more detail.
Projects don't always need to add the populated content to the build.
Sometimes the project just wants to make the downloaded content
available at a predictable location.
...
It’s not wrong, it just doesn’t cover the case specifically being discussed here. That example assumes there is no CMakeLists.txt in the top level of the toolchains project. If I find time, I’ll update that example to make things clearer.
I also just added a comment to the linked issue to give more clarity on my thinking around the requested feature.
and I am simply not calling FetchContent_MakeAvailable.
Unless I am wrong it is not FetchContent_Populate that is deprecated, it is FetchContent_Populate(<name>) with a single argument: per the documentation
FetchContent_Populate(<name>)
Changed in version 3.30: This form is deprecated. Policy CMP0169 provides backward compatibility for projects that still need to use this form, but projects should be updated to use FetchContent_MakeAvailable() instead.
Can you please confirm? I am not sure why the “workaround” that was suggested was to use a hack when it seems to me that the proper and not hacky way is simply to not use FetchContent_Declare and use the other form of FetchContent_Populate which is not deprecated.
Switching to the multi-argument form of FetchContent_Populate() is not the right choice. Using that form in project code is almost always wrong. That multi-argument form exists almost exclusively for use in CMake script mode. Using the multi-argument form in projects still prevents dependency providers from being used, and it bypasses all the logic that allows the developer to use their own local override with CMake variables of the form FETCHCONTENT_SOURCE_DIR_<uppercaseDepName>.
The right change is to keep the call to FetchContent_Declare(), and replace your call to FetchContent_Populate(jamba) with FetchContent_MakeAvailable(jamba). If you want to prevent FetchContent_MakeAvailable(jamba) from adding the jamba source directory to the build, add a SOURCE_SUBDIR argument to your FetchContent_Declare() call and set it to a path that does not exist in the jamba sources.
You can decide whether you want to still keep the message() output, but personally I’d remove it. A good rule of thumb is to only log output if something goes wrong. When you have a lot of output, it trains developers to ignore all output and then they frequently miss things that are actually important. If you follow this advice, your project could be just this:
All the CONFIGURE_COMMAND, BUILD_COMMAND, INSTALL_COMMAND and TEST_COMMAND keywords are useless and can be removed. They have no meaning for FetchContent and the values you were setting is what FetchContent enforces internally anyway.
There’s also not much point specifying BINARY_DIR if you’re going to prevent FetchContent_MakeAvailable() from adding the dependency to the project. The binary directory won’t be used in that case.
Craig, thank you for the detailed explanation. I am going to try your suggestion with the invalid SOURCE_SUBDIR. That being said, I have a few questions:
what do you mean by: “Using the multi-argument form in projects still prevents dependency providers from being used”?
when you say, “the logic that allows developer to … FETCHCONTENT_SOURCE_DIR_<uppercaseDepName>.” I believe this is what I am offering to the developer: if they specify JAMBA_ROOT_DIR, then it does not download it, and simply use the one that is local.
At the end of the day, the logic that I want, is to be able to fetch the content, period. Which is what the module FetchContent is supposed to do. I think the MakeAvailable part is not what the name FetchContent implies, but somehow you have to use FetchContent_MakeAvailable in order for the content fetching to happen.
As an analogy, when I “download” something with my browser, I am not expecting the browser to “execute” it…
I also wanted to add a little bit of background about this project (Jamba).
Jamba is a framework not a library. As such you “fetch” it and then you use it. And by using it, you include a file. Something like:
# Step 1: fetch Jamba (either via GIT TAG or DOWNLOAD URL or use local version)
# => ${JAMBA_ROOT_DIR} now contains the location of the framework
# Step 2: include the framework, which sets up a bunch of variables and cmake functions
include(${JAMBA_ROOT_DIR}/jamba.cmake)
# Step 3: use the framework
jamba_add_vst_plugin(....)
I feel like the module FetchContent should allow to do what its name suggest: fetching content and stop there. I understand that using SOURCE_SUBDIR with an invalid path will do just that when then invoking FetchContent_MakeAvailable but this is really twisting things into a pretzel to achieve what should be (and was without warning) possible.
FetchContent_Populate() has two forms, as you’ve already discovered. The form that takes a single argument (a dependency name) requires an earlier call to FetchContent_Declare() to define how to populate that dependency. This one-argument form is what FetchContent_MakeAvailable() replaces (and extends with additional capabilities). Dependency providers can intercept calls to FetchContent_MakeAvailable() and populate the dependency in whatever way they like, using the information given in FetchContent_Declare(), or ignoring that information and choosing to populate it some other way (e.g. the cmake-conan dependency provider takes the information from your conanfile.py or other equivalent Conan-specific files).
The other form of FetchContent_Populate() takes more than one argument. It does not expect or use any information from an earlier call to FetchContent_Declare(), it is its own standalone call. It was only ever intended for use by standalone CMake scripts, executed in CMake script mode (i.e. cmake -P scriptName). It should not be used in project code. Such calls cannot be intercepted by dependency providers, and this is a pain point for open source projects that ignore this advice and use it anyway. It forces consumers who want to get their dependencies a different way to have to modify that project. Dependency providers give that flexible capability without having to modify projects, but it requires projects to follow the recommended way of doing things.
Yes, but it is using a different variable to the official one FetchContent provides and that developers would already know how to use. And your implementation reimplements functionality you can already get for free just by using FetchContent_MakeAvailable() instead.
There’s a lot of history here. When FetchContent was first added to CMake, FetchContent_MakeAvailable() wasn’t part of the picture. It didn’t take long for folks to complain there were too many calls involved in populating content when following the canonical pattern. FetchContent_MakeAvailable() was added to simplify things down to the minimum two calls (FetchContent_Declare() being the other call). A few CMake releases later, FetchContent_MakeAvailable() was made more flexible by allowing it to download content that had no CMakeLists.txt file in their top level. That essentially enhanced the command’s behavior to become “add it to the project if you can, but it’s not an error if you can’t”. Later on, dependency providers were added, and those take advantage of this consolidation by intercepting FetchContent_MakeAvailable(). This still allows projects to specify how things should happen if there is no dependency provider (the FetchContent_Declare() call defines that).
The name FetchContent_MakeAvailable() was chosen carefully. It doesn’t say anything about how the dependency is “made available”. I was thinking forward to likely features that would be added later, and those did end up being added, as described above. The phrase “make available” can mean adding it to the project (the default behavior), or it could mean only downloading it (which is what happens if the content doesn’t have a CMakeLists.txt at the top level, or if a SOURCE_SUBDIR points it somewhere else that doesn’t exist).
Naming is hard. Sometimes names are constrained by what names are already in use. That has been the case for FetchContent. I can only encourage folks to consider the considerable gain in flexibility for their consumers by adopting FetchContent_MakeAvailable(), even if it isn’t the perfect name for your particular use case (there never will be a perfect name for something that covers the broad set of use cases that FetchContent_MakeAvailable() does).
Thank you for providing the very detailed explanation including history.
I have now changed my code to use FetchContent_Declare + SOURCE_SUBDIR / FetchContent_MakeAvailable.
Due to how my framework is being used, I would rather keep the simpler way to customize it from a user point of view even if it means I am re-implementing some of the features that FetchContent_MakeAvailable offers (note that my project was created prior to FetchContent_MakeAvailable existing so did not have a choice at the time).
As long as this is the official way to achieve what I need (and I do see that it is documented on the website) and it is not a hacky workaround that will be removed in an upcoming release, then I am fine with the changes.