2

I am trying to understand the documentation from:

So I tried a naive cmakelists.txt file such as:

% cat CMakeLists.txt
cmake_minimum_required(VERSION 3.24)
project(p)

include(FetchContent)
FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG        703bd9caab50b139428cea1aaff9974ebee5742e # release-1.10.0
  FIND_PACKAGE_ARGS NAMES gtest
)

# This will try calling find_package()
FetchContent_MakeAvailable(googletest)
find_package(GTest)
message(${GTest_DIR})

If I run it, here is what I get:

$ cmake ..
[...]
/usr/lib/x86_64-linux-gnu/cmake/GTest

For some reason find_package(GTest) still tries to inspect the system for GTest instead of the one declared in the FetchContent code block.

What did I misunderstood from the cmake documentation ? I assumed the function FetchContent was precisely meant for the case when user cannot use ExternalProject since it does not integrate with find_package.

For reference:

% cmake --version
cmake version 3.24.1
malat
  • 12,152
  • 13
  • 89
  • 158

1 Answers1

3

In very simple form: The option FIND_PACKAGE_ARGS makes FetchContent to use results of find_package. But for make find_package to use results of FetchContent, you need to pass another option: OVERRIDE_FIND_PACKAGE.


Both FetchContent and find_package are intended to introduce a 3d-party project for use it during the build of main project. However, these two approaches uses different mechanisms:

  1. FetchContent builds the 3d-party project from sources, alongside with the main one, but
  2. find_package works with already installed 3d-party project.

CMake provides two "directions" of integration between FetchContent and find_package; each "direction" has its own, independent purposes.

1. Write a new project equally consuming a 3d-party project both in sources and installed.

Assume you write a project, which uses some 3d-party project. Assume you want to support two variants:

  1. Use installed variant of the 3d-party project, if it is already available, or
  2. Build the 3d-party project from sources alongside with your main project.

Parameter FIND_PACKAGE_ARGS for FetchContent_Declare tells CMake to use find_package for locate installed 3d-party project, and, if it is available, do not build it from sources.

Such integration will work only if the main project uses the 3d-party project via interface, provided both by 3d-party project's sources and by find_package.

E.g. GTest's sources create ALIAS targets in namespace GTest (like GTest::gtest_main) and find_package(GTest) creates IMPORTED targets with the same namespace. So, the outer project can safely link with GTest::gtest_main and this will work whenever GTest is already installed or is being built from sources.

2. Make an existing project to work with a 3d-party project built from sources

Assume you are creating a superproject, which combines several already existed subprojects. One of this subprojects executes find_package for the 3d-party project and uses its results. Assume you want that 3d-party project to be built from the sources.

Most of project-specific scripts, consumed by find_package, work only with the installed packages and doesn't work with the one which is currently being built. Some special power is needed..

Ignore 'find_package'

Parameter OVERRIDE_FIND_PACKAGE for FetchContent_Declare means to ignore further find_package calls because the 3d-party project is being built from sources.

This approach works if the subproject uses only those results from find_package, which are available from the 3d-party project's sources too. E.g. it will work if the subproject uses target GTest::gtest_main for link with. But it won't work if the subproject uses GTEST_MAIN_LIBRARIES variable, which is not set in the GTest sources.

Change a script executed by 'find_package'

But what if the subproject uses a part of interface, which is available only from find_package (and its project-specific script), but is not available from the sources of the 3d-party project? In that case it is possible to create a fake script, which will be executed by find_package instead of the normal one.

file(WRITE ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/gtest-extra.cmake
[=[
  set(GTEST_MAIN_LIBRARIES GTest::gtest_main)
]=])

Lines between [=[ and ]=] becomes part of the created script, which defines GTEST_MAIN_LIBRARIES variable suitable for link in a subproject.

(Note, that the file gtest-extra.cmake has a sence only when OVERRIDE_FIND_PACKAGE parameter has been specified for FetchContent_Declare.)

Tsyvarev
  • 60,011
  • 17
  • 110
  • 153