`FetchContent` and `<PROJECT-NAME>_VERSION`

Let’s assume that the a project uses:

FetchContent_Declare(SomeProject
		GIT_REPOSITORY https://github.com/Someone/SomeProject
		GIT_TAG main)
FetchContent_MakeAvailable(SomeProject)

Then the user does not have access to SomeProject_VERSION because that value is not propagated. Should <PROJECT-NAME>_VERSION type of variables be converted to cache values when project() is called?

One possible clash I see is if someone implements something like:

FetchContent_Declare(SomeProject
		GIT_REPOSITORY https://github.com/Someone/SomeProject
		GIT_TAG v${SomeProject_VERSION})
FetchContent_MakeAvailable(SomeProject)

For example -DSomeProject_VERSION=v1.0.1-rc2 but after project() it is converted to SomeProject_VERSION=1.0.1. I think that that should be an intended behaviour, as long as it is possible to override the cli/cached passed argument with the cached version from project()

Do not try to set project versions as cache arguments on the command line. They should be set by the project, and the project should be able to assume nothing will interfere with those variables.

I believe the various <projectName>_VERSION* variables are stored in the cache, so after FetchContent_MakeAvailable() returns, those cache variables should be readable from any variable scope.

Sorry, that bit about the cache was wrong. Only CMAKE_PROJECT_VERSION* variables are stored in the cache, and those are for the top level project.

But still we have an inconsistency between find_package and FetchContent because you can retrieve <PackageName>_VERSION in one, but not the other.

Currently the workaround that I’ve found is to wrap it around an if at the end:


if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
	# Commands for find_package()
	write_basic_package_version_file(...)
else ()
	# Propagate variables for FetchContent
	return(PROPAGATE
			MyProj_VERSION
			MyProj_VERSION_MAJOR
			MyProj_VERSION_MINOR
			MyProj_VERSION_PATCH
			MyProj_VERSION_TWEAK
			)
endif ()

EDIT: updated from the original suggestion after giving this more thought

If a project calls FetchContent_Declare(SomeProject ...) without the FIND_PACKAGE_ARGS keyword, then it should not expect SomeProject_VERSION variables to be populated. If the SomeProject dependency wants to support being used with FIND_PACKAGE_ARGS and to provide version variables, it can do so by writing its own ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/someproject-config-version.cmake file which sets that version. For example, it can put code like the following somewhere after the project() call in its top level CMakeLists.txt file:

file(WRITE ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/someproject-config-version.cmake
    "set(PACKAGE_VERSION ${PROJECT_VERSION})\n"  # Addresses the need of this discussion
    "set(PACKAGE_VERSION_COMPATIBLE TRUE)\n"     # Required for find_package() redirection to work
    "set(PACKAGE_VERSION_EXACT TRUE)\n"          # Might be able to do better than this
)
endif()

I can even see how this could be generalised to automatically be applied to every project or a specific selection of projects by making use of CMAKE_PROJECT_INCLUDE, but you’d be making some assumptions about the name given to project() commands and how that maps to package names (or you’d have to implement such a mapping in the file you include using CMAKE_PROJECT_INCLUDE).

After giving this more thought, I’ve updated the previous comment with a more appropriate way of providing version details. It also may be possible to implement that approach in FetchContent to make it easier for projects to opt-in to that. I created issue 24636 to track that proposal.

Thanks for prompting this discussion, I didn’t see that solution when I originally implemented the find_package() integration back in CMake 3.24, but it looks viable to me now.