Use find_package() on external project

Viewed 2470

I have an External project called messages. I am using ExternalProject_Add in order to fetch and build the project.

If i use find_package(messages REQUIRED) in top level CMakeLists.txt the cmake .. fails because it couldn't find the package installation files, which is logical as they are only build during make command invocation.

I am not sure, if there is way use find_package() on ExternalProjects. If so, please point me to an example.

Thanks BhanuKiran

2 Answers

You have misunderstood how ExternalProject is supposed to work. You cannot find_package(messages REQUIRED) because it hasn't been built yet. ExternalProject merely creates the build steps necessary to build the subproject.

You have two options:

  1. Use add_subdirectory or FetchContent in place of ExternalProject. In this case, you don't need a find_package call. This effectively adds the sub-project to the main build and imports the subproject's targets.
  2. Use two ExternalProject calls: one for messages and another for main_project, which depends on messages. If messages uses the export(EXPORT) function, you can point CMAKE_PREFIX_PATH or messages_ROOT to the build directory. Otherwise you'll need to run the install step for messages and set up an install prefix inside your build directory. Then the find_project(messages REQUIRED) call inside main_project will succeed. This will likely require re-structuring your build.

Generally speaking, ExternalProject is only useful for defining super-builds, which are chains of CMake builds that depend on one another. And super builds are only useful when you need completely different configure-time options, like different toolchains (eg. you're cross compiling, but need a code generator to run on the build machine). If that's not the case, prefer FetchContent or add_subdirectory with a git submodule.

It is best to use FetchContent with CMake 3.14+ since it adds the FetchContent_MakeAvailable macro that cuts down on boilerplate.

Docs:

https://cmake.org/cmake/help/latest/module/ExternalProject.html https://cmake.org/cmake/help/latest/module/FetchContent.html

Since I like keeping my CMake file agnostic on how I get my packages.

I was using FetchContent and added this file (Findalib.cmake):

if(NOT alib_POPULATED)
    set(alib_BUILD_TESTS OFF CACHE BOOL INTERNAL)
    set(alib_BUILD_EXAMPLES OFF CACHE BOOL INTERNAL)
    set(alib_BUILD_DOCS OFF CACHE BOOL INTERNAL)
    FetchContent_MakeAvailable(alib)
endif()
set(alib_FOUND TRUE)

Then, in my CMake files:

find_package(alib REQUIRED)
target_link_libraries(my-executable PUBLIC alib::alib)

That way, packages are only declared in my file in which I declare dependencies, and I fetch them only if I try to find them.

Related