CMake does not warn when a PUBLIC link dependency will not be installed

We have a CMake module to fetch configured build dependencies as a dependency-provider:

                message(DEBUG "${BemanExemplar_debug}")
                FetchContent_Declare(
                    "${BemanExemplar_name}"
                    GIT_REPOSITORY "${BemanExemplar_repo}"
                    GIT_TAG "${BemanExemplar_tag}"
                    # NO! EXCLUDE_FROM_ALL
                )
                FetchContent_MakeAvailable("${BemanExemplar_name}")

The problem starts with EXCLUDE_FROM_ALL

  • If set, it works fine to fetch i.e.GTest without installation, which is acceptable for unit test frameworks.
  • if NOT set, it works for external libs: fetch, build, install, what we need!

To work without EXCLUDE_FROM_ALL we have to use:

set(BUILD_GMOCK OFF)
set(INSTALL_GTEST OFF) # Disable GoogleTest installation
find_package(GTest CONFIG REQUIRED)
  1. Is this an acceptable or recommended pattern for handling dependencies that should not be installed, or is there a better approach when using FetchContent?

  2. If a target links to a dependency using a PUBLIC link interface, but that dependency will not be installed, CMake does not emit any configuration warning.
    In contrast, CMake does warn when a PUBLIC FILE_SET is not installed.

  3. Would it make sense for CMake to emit a warning in this case as well (PUBLIC link dependency that will not be installed)?
    If so, is there an existing mechanism to enable such a warning, or would this require a new feature

see too The FindGTest.cmake module does not respect the version parameter?

FetchContent is primarily intended for building your project locally and running things from your build directory. It is not a good fit for installing your main project unless you are building against static libraries and are only installing executables. Once you stray from that, you’re taking on a lot of responsibilities for understanding what all your dependencies do for their install steps, most of which were probably defined expecting that dependency to be the top level project. You might be lucky and have only well-behaved dependencies that still work when installed as part of a parent project, but it is not a strategy I recommend.

If you add the EXCLUDE_FROM_ALL keyword, you need to understand its effects. You can see precisely what those effects are in the directory property documentation, which the FetchContent docs for EXCLUDE_FROM_ALL direct you to for full details. Here are the critical parts:

  • Targets defined in the subdirectory or below will not be included in the ALL target of the parent directory. Those targets must be built explicitly by the user, or be a dependency of another target that will be built.
  • Targets defined in the subdirectory or below will be excluded from IDE project files.
  • Any install rules defined in the subdirectory or below will be ignored when installing the parent directory.

Note that these effects are not the same as those for the EXCLUDE_FROM_ALL target property.

Those docs also say this:

EXCLUDE_FROM_ALL is meant for when the subdirectory contains a separate part of the project that is useful, but not necessary, such as a set of examples, or e.g. an integrated 3rd party library.

That last point makes clear that using EXCLUDE_FROM_ALL wouldn’t be appropriate if your project needs GTest to build successfully.

Your proposed method of setting variables like INSTALL_GTEST to false before calling find_package(GTest) looks like you’re expecting the find_package() call to be intercepted by a dependency provider that will use FetchContent to fulfil the request. You’re free to do that, if that accomplishes what you need. Just understand that your project is starting to assume a particular dependency provider behavior or implementation. That may be fine inside an organisation with controlled build environments. It wouldn’t be so appropriate for an open source project that has no control whatsoever over how consuming projects will use it.

1 Like