Automatic fallback to pkgconfig if `find_package` fails?

Wherever dependencies are installed, CMake should be able to find them. OS package-manager, vcpkg | conan (and friends), explicit submodule, explicit FetchContent | file(DOWNLOAD.

Use-cases: much faster build times in CI, Docker, &etc. Switch between development mode (say, vcpkg) and production (say, OS deps). Use vcpkg on Windows and OS deps everywhere else.

Currently I create a pkg-config aware solution that works pretty well:

find_package(LibArchive QUIET)
if (NOT LibArchive_FOUND)
    find_package(PkgConfig)
    if (PkgConfig_FOUND)
        set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH ON)
        pkg_search_module(LibArchive LibArchive)
    endif (PkgConfig_FOUND)
endif (NOT LibArchive_FOUND)

if (NOT LibArchive_FOUND AND NOT DEFINED LibArchive_QUIET)
    message(FATAL_ERROR "Could NOT find LibArchive")
endif (NOT LibArchive_FOUND AND NOT DEFINED LibArchive_QUIET)

…and sometimes I go pretty hardcore like with: FindCurlCustom.cmake. But it would be preferable to have a simple find_package for all scenarios, maybe with a PREFER keyword to choose which location to prefer it from (maybe a priority list).

What do you think?

Part of that picture has been discussed at some length in issue 21687. It covers integrating find_package() and FetchContent. I had to put that work on hold but am planning on returning to it once CMake 3.23 is released.

In big projects, I find that the time taken for dependency/package-related things often dominates the overall configure time. One of the limitations of find_package() is that it is inherently serial. FetchContent_MakeAvailable() or something like it, on the other hand, has the potential to allow fetching dependencies in parallel in the future. This matters when you start having sources downloaded or where a package manager hooks into the process (you may want to see the very long discussion in issue 22619 for more on that side topic).

One idea I had concerning this topic is, CMake could offer a feature that allows the user/project to specify a script to be run if find_package fails. This could be a way for package managers to hook into the process, and could also offer projects a way to fall back to FetchContent without manually writing find modules for every possible case where this fallback may be necessary.

There’s no good way of doing this. Questions to consider:

  • Given find_package(Foo), what is the .pc name associated with it? (Tricky examples include GTK mapping to gtk+-3.0.pc.)
  • If a component is requested, how does that map to a .pc file?
  • The proposed PREFER is just going to always be a variable that is forwarded from the cache (and possibly supplemented in code), but a static list isn’t something I see being useful to projects that can’t already just say “look here” in some other way. Basically, I don’t see this being any better than _ROOT or CMAKE_PREFIX_PATH which are already available.
  • As for user/project hooks, I think this is solely something for a package manager to do. I would rather just set paths or prefixes up rather than code up some imperative callback to do the same thing.
2 Likes