Best practices for handling multiple calls to package-config.cmake across large projects

Hello,

I have some questions about what is considered best practice for writing a basic package config file when searching for dependencies, and I haven’t had much luck deducing it from the internet. To explain my question, assume the following simple installed configuration file for a package foo that has a dependency on MPI of any version. This package foo is a low-level dependency that is consumed in several places throughout a much larger project consisting of several subprojects.

include(CMakeFindDependencyMacro)

find_dependency(MPI REQUIRED)

include("${CMAKE_CURRENT_LIST_DIR}/foo-targets.cmake")

My understanding, based on looking at a trace-expand, is that if a consuming project calls find_package(foo REQUIRED), the code in the installed config file will be executed every time, even if somewhere else in the project (maybe in a subproject) there was already a successful call to find_package(foo REQUIRED). Is that true? This behavior surprised me greatly, but I can see arguments for why it is necessary if a different version is found later.

I have seen some config files which guard against the additional calls to FindMPI, for instance, as follows:

if( NOT TARGET MPI::MPI_C)
   find_dependency(MPI REQUIRED)
endif()

So my questions are:

  • In the first case, will the MPI_C found by the last call to find_package(MPI REQUIRED) be used as the target for all packages linking to MPI::MPI_C? In the second case, is that saying “use whatever MPI has already been found. I will let whoever called it first set the version”?
  • Is one or the other considered “best practice”? Or is it just a preference of “last call wins” versus “first call wins”? What is the justification?
  • Should my config file also be checking to see if foo target is defined upon entry and just return or something similar to guard against multiple calls to find_package(foo)? What is the justification if not?

Hopefully my questions are clear; let me know if I can clarify!

Simon

The package can’t really change the target within the scope, so it’s not like there’s anything to be done if the target would be different. So yes, it will use whatever was last found in the scope.

FindMPI is tricky, so skipping it if someone already called it seems sensible.

No. You might not define the targets, but any variables might be important. For example, if you supported components, variables could be different based on the component list.

@ben.boeckel , thanks for the responses!