如何在 cmake 中使用 COMPONENTS 配置项目

2022-01-21 00:00:00 cmake components c++

我想使用 cmake 创建一个项目项目,该项目的访问方式与 Poco 使用的方式类似.我发现以 Poco 为例非常拥挤且难以理解,因此我正在尝试创建一个没有宏的最小版本,以便我可以看到发生了什么.我在这里为这个示例构建了一个存储库.

I want to create a project of projects using cmake which is accessible in a similar manner to that used by Poco. I find that using Poco as an example is crowded and difficult to follow so I am trying to create a minimal version, without macros so that I can see what is going on. I have constructed a repository for this example here.

https://github.com/markeastwood82/nomnoms

这和下面写的内容是我在阅读/研究现代 CMake"几天后如何解决这个问题的最佳猜测,除了它不太管用.本质上,我有一个库 noms,其中包含组件 fruitveg,我希望从应用程序 munch 动态链接它们.我可以安装 noms 库,但无法使用 munch 找到它.有人可以帮我把这个东西放在一起吗?

This, and what is written below is currently my best guess at how to solve this problem after some days of reading up / grappling with "modern CMake", except that it doesn't quite work. Essentially I have a library noms with components fruit and veg which I want link dynamically from an application munch. I can install the noms library, but cannot find it with munch. Can someone please help me to put this thing together?

两个项目的结构如下:

noms
|---- CMakeLists.txt
+---- fruit
|     |---- CMakeLists.txt
|     |---- fruit-config.cmake.in
|     +---- src
|     |     |----apple.cpp
|     |
|     +---- include/noms/fruit
|           |----apple.h
|
+---- veg
      |---- CMakeLists.txt
      |---- veg-config.cmake.in
      +---- src
      |     |---- asparagus.cpp
      |
      +---- include/noms/veg
            |---- asparagus.h

munch
|---- CmakeLists.txt
+---- src
      |---- main.cpp

文件 noms/CMakeLists.txt 包含以下内容.

The file noms/CMakeLists.txt contains the following.

cmake_minimum_required(VERSION 3.0)
set(project noms)
set(version 1.0.0)

project(${project})

add_subdirectory(fruit)
add_subdirectory(veg)

# UPDATE: implement advice from Tsyvarev

configure_file(${project}-config.cmake
  "${CMAKE_BINARY_DIR}/${project}-config.cmake"
  @ONLY
)

include(CMakePackageConfigHelpers)
write_basic_package_version_file(
  "${CMAKE_BINARY_DIR}/${project}-config-version.cmake"
  VERSION ${version}
  COMPATIBILITY AnyNewerVersion
)

install(
  FILES
    "${CMAKE_BINARY_DIR}/${project}-config.cmake"
  DESTINATION lib/cmake/${project}
)

文件 noms/fruit/CMakeLists.txt(和 noms/veg/CMakeLists.txt 几乎相同)是

The file noms/fruit/CMakeLists.txt (and almost identical for noms/veg/CMakeLists.txt) is

set(component fruit)

add_library(${component} SHARED src/apple.cpp)

# namespaced alias
add_library(${project}::${component} ALIAS ${component})

target_include_directories(${component}
  PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>
)

install(TARGETS ${component} EXPORT ${component}-targets
  COMPONENT ${component}
  LIBRARY DESTINATION lib
  ARCHIVE DESTINATION lib
  RUNTIME DESTINATION bin
  INCLUDES DESTINATION include
)

install(EXPORT ${component}-targets
  FILE "${project}-${component}-targets.cmake"
  NAMESPACE ${project}::
  DESTINATION lib/cmake/${project}
  COMPONENT ${component}
)

# This seems like a kludge, but it does place the file in the correct location
# on my machine (Ubuntu 18.04). Idea taken from Poco
configure_file("${component}-config.cmake.in"
  "${CMAKE_BINARY_DIR}/${project}-${component}-config.cmake"
  @ONLY
)

include(CMakePackageConfigHelpers)
write_basic_package_version_file(
  "${CMAKE_BINARY_DIR}/${project}-${component}-config-version.cmake"
  VERSION ${version}
  COMPATIBILITY AnyNewerVersion
)

install(
  FILES
    "${CMAKE_BINARY_DIR}/${project}-${component}-config.cmake"
    "${CMAKE_BINARY_DIR}/${project}-${component}-config-version.cmake"
  DESTINATION lib/cmake/${project}
  COMPONENT ${component}
)

# DESTINATION will be automatically prefixed by ${CMAKE_INSTALL_PREFIX}
install(
  DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include
  COMPONENT ${component}
  DESTINATION ${CMAKE_INSTALL_PREFIX}
)

最后,noms/fruit/fruit-config.cmake.in 的文件(和 noms/veg/veg-config.cmake 几乎相同)是

Finally, the file for noms/fruit/fruit-config.cmake.in (and almost identical for noms/veg/veg-config.cmake) is

include(${CMAKE_CURRENT_LIST_DIR}/noms-fruit-config-version.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/noms-fruit-config-targets.cmake)

对于项目munch,文件munch/CMakeLists.txt很简单

cmake_minimum_required(VERSION 3.0)
set(project munch)

find_package(noms REQUIRED COMPONENTS fruit)

add_executable(${project} src/main.cpp)

target_include_directories(${project}
  PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:$include>
)

target_link_libraries(${project}
  PUBLIC
    noms::fruit
)

安装 noms 后,我尝试构建 munch,但是 cmake 找不到 noms-config.cmake 文件.我不知道它应该包含什么,也不知道我应该把它放在哪里.我也不确定在 noms/<food-type>/CMakeLists.txt 中使用 configure_file 的方法是否合适,因为这似乎不需要这里的例子

After installing noms I try to build munch, however cmake can't find the noms-config.cmake file. I don't know what it should contain, nor where I should really put it. I am also not sure it the method of using configure_file in noms/<food-type>/CMakeLists.txt is appropriate, given this does not seem to be required in the example here

https://github.com/boostcon/cppnow_presentations_2017/blob/master/05-19-2017_friday/effective_cmake__daniel_pfeifer__cppnow_05-19-2017.pdf

我的问题与此处提出的问题基本相同,目前没有答案(接受的答案不正确,正如我在该线程中评论的那样).实际上公平地说,由于该线程已有 6 年历史,在撰写本文时它可能是正确的,但似乎没有使用现在推荐的 xxx-config.cmake 方法.

My question is essentially the same as was asked here, which is currently unanswered (the accepted answer is incorrect, as I have commented in that thread). Actually to be fair, since that thread is 6 years old, it may have been correct at the time of writing, but does not appear to utilize the xxx-config.cmake method that is now recommended.

如何在 CMAKE 中导出带有组件的库?

请随时提出我所犯的任何错误,或者任何可以做得更好的事情.谢谢

Please feel free to pull me up on any mistakes I have made, or anything that could be done better. Thanks

* 更新 *

除了上面更新的 noms/CMakeLists.txt 文件之外,我还根据下面 @Tsyvarev 的建议实现了以下 noms/noms-config.cmake 文件...

In addition to the updated noms/CMakeLists.txt file above, I have implemented the following noms/noms-config.cmake file as per advice from @Tsyvarev below ...

foreach(component ${noms_FIND_COMPONENTS})
    include(${CMAKE_CURRENT_LIST_DIR}/noms-${component}-config.cmake)
endforeach()

生成的代码现在可以工作了,谢谢!

The resulting code now works, thankyou!


推荐答案

CMake 不会自动处理 COMPONENTS 列表.它将任务留给 noms-config.cmake 脚本,在发出命令时搜索并执行该脚本

CMake doesn't process COMPONENTS list automatically. It leaves that mission to noms-config.cmake script, which is searched and executed when one issues the command

find_package(noms COMPONENTS fruit veg)

来自 find_package 文档:

在配置模式下,find_package 会自动处理 REQUIREDQUIET[version] 选项,但将其留给包配置文件以对包有意义的方式处理组件.

In Config mode find_package handles REQUIRED, QUIET, and [version] options automatically but leaves it to the package configuration file to handle components in a way that makes sense for the package.

在此配置文件中,您可以从 noms_FIND_COMPONENTS 变量中提取请求的组件列表(通过 find_package()COMPONENTS 选项给出),并执行适当的操作.例如:

Inside this config file, you may extract list of requested components (given via COMPONENTS option to find_package()) from the noms_FIND_COMPONENTS variable, and perform appropriate actions. For example:

noms-config.cmake:

foreach(component ${noms_FIND_COMPONENTS})
  # For requested component, execute its "config" script
  include(${CMAKE_CURRENT_LIST_DIR}/${component}-config.cmake)
endforeach()

这意味着,您将把脚本 noms-config.cmake 安装到 lib/cmake/noms 子目录,靠近另一个 .cmake 您已经在项目中安装的脚本.

This implies, that you will install the script noms-config.cmake to lib/cmake/noms subdirectory, near the other .cmake scripts you have already installed in your project.

相关文章