FetchContent, find_package, and missing package files

I am attempting to use coin3d as a vendored project through the FetchContent() and find_package() mechanism. Unfortunately, the package files are not generated by configure_package_config_file() and write_basic_package_version_file() called here and as a result the parent project does not have access to important variables such as COIN_VERSION.

My example CMakeLists.txt is as follows:

cmake_minimum_required(VERSION 3.31.0 FATAL_ERROR)

project(CoinTest)

include(FetchContent)
FetchContent_Declare(
    Coin
    SOURCE_DIR ${CMAKE_SOURCE_DIR}/../src/3rdParty/coin3d
    OVERRIDE_FIND_PACKAGE
    CMAKE_ARGS
        -DCMAKE_INSTALL_LIBDIR:FILEPATH=${CMAKE_PREFIX_PATH}/lib
        -DCMAKE_INSTALL_PREFIX:FILEPATH=${CMAKE_PREFIX_PATH}
        -DCMAKE_PREFIX_PATH:FILEPATH=${CMAKE_PREFIX_PATH}
        -DCOIN_BUILD_TESTS:BOOL=OFF
        -DSIMAGE_RUNTIME_LINKING:BOOL=ON
        -DUSE_EXTERNAL_EXPAT:BOOL=ON
    )
find_package(Coin CONFIG)

message("COIN_VERSION = " ${COIN_VERSION})

It is expected that COIN_VERSION = 4.0.5 would be printed, but COIN_VERSION = is what is shown.

How may I modify coin3d to be compatible with FetchContent?

If you are the maintainer of coin3d, consider using a target property to store the version. That will always be available to the consumer regardless of whether they obtain coin3d via find_package() or FetchContent. Another alternative would be to define a function that provides the coin3d version in a variable it returns. The coin3d package would need to ensure it includes whatever file defines that function as part of its coin3d-config.cmake file (or whatever you’re providing as part of the coin3d package). For FetchContent consumers, you would just need to include that same file as part of coin3d’s regular CMakeLists.txt files, since functions have global visibility in CMake.

The version is one of many variables that are needed by consumers. It was my understanding that packages such as googletest could be integrated via FetchContent with the OVERRIDE_FIND_PACKAGE option and it would “just work”. The relevant documentation: https://cmake.org/cmake/help/latest/module/FetchContent.html#integrating-with-find-package

I’m curious how to make this work, where a find_package() configuration can be built and made available via FetchContent.

The FetchContent / find_package() integration is not intended to fully replicate everything find_package() can do. The FetchContent integration works well for dependencies that define only global things and use project-specific names for them. This mostly means targets, functions, macros, and install components. I discourage projects from setting variables in their packages for this reason. Variables don’t play well with some aspects of dependency handling (thinking more broadly than just FetchContent here, you’re going to have some problems with some mainstream package managers as well). For projects that don’t try to define variables, the FetchContent / find_package() integration can, and often does, Just Work. But it relies on the project being added as a dependency following the rules for being able to be used in that way. Some projects do that, others don’t. Some are close enough that you don’t need the few bits and pieces that don’t come along for the ride with FetchContent (e.g. variables that you don’t need in the consuming project because you’re only using the dependency project’s targets).

The version details of a package are one area that FetchContent doesn’t currently have a mechanism for conveying back to the caller’s scope. Those details normally come from the <packageName>-config-version.cmake file, and that file doesn’t exist when providing the dependency via FetchContent. There is a proposal discussed somewhere in the CMake issue tracker for a way to potentially extract some sort of version information based on the dependency’s top level project version, but that’s at best a partial heuristic, not a robust equivalent. I never pursued that idea, and don’t have any plans to do so for the foreseeable future (but am more than happy for someone else to explore the idea).

1 Like

Wouldn’t the <project>_VERSION variable be defined once the project is included?

If the dependency defines a <project>_VERSION variable at all, it would be in the project’s scope and would not be visible in the scope where FetchContent_MakeAvailable() was called. I alluded to that in my earlier comment:

1 Like