How to properly connect <Package>Config.cmake files to avoid getting wrong dependencies?
@brad.king, @kyle.edwards, @ben.boeckel, @craig.scott
While completing the final functionality for various use cases for package config file linking in:
it occurred occurred to me that the suggested way to pull in upstream dependencies in a generated <Package>Config.cmake
file as described in:
(which is also echoed on the book "Professional CMake") may not be ideal.
Why is it that after the parent CMake project <Package>
calls find_package(<UpstreamPackage>)
to find some external package <UpstreamPackage>
when the project <Package>
is being configured (which sets the vars <UpstreamPackage>_DIR
and <UpstreamPackage>_CONFIG
when it finds a package-config file for <UpstreamPackage>
), that it would then throw that information away just do a naked call to find_depenency(<UpstreamPackage>)
in the generated <Package>Config.cmake
file like shown in:
showing:
include(CMakeFindDependencyMacro)
find_dependency(<UpstreamPackage>)
?
By doing a naked call to find_depenency(<UpstreamPackage>)
in <Package>Config.camke
, would that not open up downstream CMake projects to pull in a different installation for the package <UpstreamPackage>
when calling find_package(<Package>)
? (This could happen if the search path for external packages was different in the downstream CMake project configuration and there were multiple installs of <UpstreamPackage>
on the system.) Would that that would be a disaster, in general, if a different/incompatible installation of <UpstreamPackage>
was found by a downstream customer of <Package>
?
Would it not make more sense to instead have the generated <Package>Config.cmake
file call:
include("${<UpstreamPackage>_CONFIG}")
? That would eliminate any possibility of of having the downstream customer of <Package>
getting a different/incompatible install of <UpstreamPackage>
.
Now, I understand that it may not be desirable to put in an absolute path to ${<UpstreamPackage>_CONFIG}
(because we want packages to be relocatable) but why not at least write a relative path to ${<UpstreamPackage>_CONFIG}
from the path of the generated <Package>Config.cmake
file? So you would compute that relative path in the the outer <Package>
CMake configure (e.g. as the variable <UpstreamPackage>_RELATIVE_PATH_TO_CONFIG_FILE
) and write that into the generated <Package>Config.cmake
file as:
include("${<UpstreamPackage>_RELATIVE_PATH_TO_CONFIG_FILE}")
?
However, I guess that would not work if you wanted to support the binary installation of packages where upstream packages may be moved from the relative location on the machines were when the downstream packages were configured and built. In that case, you would want to instead set <UpstreamPackage>_DIR
then call find_dependency(<UpstreamPackage>)
in the <Package>Config.cmake
file as:
set(<UpstreamPackage>_DIR "@<UpstreamPackage>_RELATIVE_PATH_TO_CONFIG_FILE_DIR@")
find_dependency(<UpstreamPackage>)
(where <UpstreamPackage>_RELATIVE_PATH_TO_CONFIG_FILE_DIR
is computed in the outer <Package>
CMake configure as the relative path from the generated <Package>Config.cmake
file to the <UpstreamPackage>_DIR
directory returned from find_package(<UpstreamPackage>)
).
In this way, if the package config file for <UpstreamPackage>
was not located at the relative directory location ${<UpstreamPackage>_RELATIVE_PATH_TO_CONFIG_FILE_DIR}
, then the standard find_package()
search paths would be search in the stated order.
But the problem with the above approach is that using a relative path to an upstream package does not work if you are pulling in upstream packages that have standard install locations on a system but the install location of the downstream package changes. So in that case, you would want to use an absolute path to the <UpstreamPackage>Config.cmake
file as shown at the very top.
NOTE: This discussion of the locations of <Package>Config.cmake
files sounds almost identical to the discussion of how to set RPATH
for shared libraries given in the section "RPATH" in the book "Professional CMake" and:
- https://cmake.org/cmake/help/v3.25/prop_tgt/BUILD_RPATH_USE_ORIGIN.html
- https://cmake.org/cmake/help/v3.25/prop_tgt/INSTALL_RPATH_USE_LINK_PATH.html
So, should the CMake project for <Package>
key off of BUILD_RPATH_USE_ORIGIN
as to whether to put in absolute to relative paths to pull in upstream packages from downstream package config files? That would seem to be consistent with with the behavior for RPATH.
Are these reasonable concerns?