如何在 add_subdirectory 之后使用 find_package

How to use find_package after add_subdirectory

我正在尝试将第三方包导入到我的项目中。所以我一直在关注:

但这失败了:

/tmp/top-level/bin/extern/MyLib
CMake Error at bin/extern/MyLib/MyLibConfig.cmake:12 (include):
  include could not find load file:

    /tmp/top-level/bin/extern/MyLib/MyLibTargets.cmake
Call Stack (most recent call first):
  CMakeLists.txt:6 (find_package)

文档中缺少什么?作为参考,我的最高水平 cmakelists.txt 是:

cmake_minimum_required(VERSION 3.18)
project(top-level)

add_subdirectory(extern)
find_package(MyLib CONFIG REQUIRED HINTS
             ${CMAKE_CURRENT_BINARY_DIR}/extern/MyLib)

'MyLib' 的 cmakelists.txt 文件是:

cmake_minimum_required(VERSION 3.18)
project(MyLib VERSION 1.0 LANGUAGES C)

add_library(MyLib mylib.c)
add_library(MyLib::MyLib ALIAS MyLib)

install(
  TARGETS MyLib
  EXPORT MyLibTargets
  LIBRARY DESTINATION lib
  ARCHIVE DESTINATION lib
  RUNTIME DESTINATION bin
  INCLUDES
  DESTINATION include)

include(CMakePackageConfigHelpers)
write_basic_package_version_file(
  MyLibConfigVersion.cmake
  VERSION ${PACKAGE_VERSION}
  COMPATIBILITY AnyNewerVersion)

install(
  EXPORT MyLibTargets
  FILE MyLibTargets.cmake
  NAMESPACE MyLib::
  DESTINATION lib/cmake/MyLib)

configure_file(MyLibConfig.cmake.in MyLibConfig.cmake @ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/MyLibConfig.cmake"
              "${CMAKE_CURRENT_BINARY_DIR}/MyLibConfigVersion.cmake"
        DESTINATION lib/cmake/MyLib)

错误消息不言自明:

您使用 build 目录中的脚本 MyLibConfig.cmake,此脚本尝试加载由 install(EXPORT MyLibTargets) 创建的脚本 MyLibTargets.cmake

但后一个脚本仅在项目 安装 后才能工作,在项目 构建 时无法工作.

实际上,在那种情况下不需要整个调用 find_package(MyLib): 由于当前项目构建 MyLib,您已经可以访问目标 MyLib::MyLib


如果你想让你的顶级项目变得灵活,这样它就可以在 MyLib 已经安装或正在构建的情况下工作,那么你可以使用 find_package 有条件地:

cmake_minimum_required(VERSION 3.18)
project(top-level)

# This project could be built as standalone.
# In that case 'MyLib' is assumed to be already installed.
#
# Also, this project could work as a subproject of some other project,
# which also builds `MyLib` via 'add_subdirectory(MyLib)'.

if(NOT TARGET MyLib::MyLib)
    find_package(MyLib CONFIG REQUIRED)
endif()

# ... use MyLib via 'MyLib::MyLib' target.

或者,您可以编写 MyLibConfig.cmake 脚本,即使当前正在构建 MyLib 也可以使用它。

if(TARGET MyLib::MyLib)
  return()
endif()

# ... usual content of the config file.

在这种情况下,可以简化根项目的 CMakeLists.txt

cmake_minimum_required(VERSION 3.18)
project(top-level)

# Normal use case is that 'MyLib' is already installed.
# But the project could work as a subproject in other scenarios.
#
# In those scenarios, a parent project should care about
# 'find_package' to work.
find_package(MyLib CONFIG REQUIRED)

# ... use MyLib via 'MyLib::MyLib' target.

在构建'MyLib'的情况下项目的用法如下:

- CMakeLists.txt (outer)
- MyLib
  - CMakeLists.txt (MyLib)
- top_level
  - CMakeLists.txt ("top-level")

CMakeLists.txt:

cmake_minimum_required(VERSION 3.18)
project(outer)

add_subdirectory(MyLib)

# Help inner project to find config file for MyLib.
#
# Here we use *internal* knowledge of MyLib project,
# that it generates 'MyLibConfig.cmake' directly in its build directory.
#
# Note: find_package expects 'XXX_DIR' variable to be CACHE one.
set(MyLib_DIR "${CMAKE_CURRENT_BINARY_DIR}/MyLib"
  CACHE INTERNAL "Directory with MyLibConfig.cmake"
)

add_subdirectory(top_level)