14

我有一个简单的项目,它需要三个仅标头库才能编译:websocketppspdlognlohmann/json

项目结构如下所示:

└── src
    ├── app
    │   ├── CMakeLists.txt
    │   ├── src
    │   └── test
    ├── CMakeLists.txt
    ├── core
    │   ├── CMakeLists.txt
    │   ├── include
    │   ├── src
    │   └── test
    └── vendor
        ├── install.cmake
        ├── nlohmann_json
        ├── spdlog
        └── websocketpp

根 CMakeLists.txt 如下:

cmake_minimum_required(VERSION 3.6.1 FATAL_ERROR)

..

# External 3rd party libs that we include

include(vendor/install.cmake)

add_subdirectory(core)
add_subdirectory(app)

这个想法基本上是每个子目录都是一个库(例如core),并且app“聚合”了所有子目录。每个库(例如core)都是这样构建的(core/CMakeLists.txt):

project(foo-core VERSION 0.1 LANGUAGES CXX)
add_library(foo-core
  src/foobar/foobar.cc
  src/foobaz/baz.cc)

target_include_directories(foo-core PUBLIC
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
  $<INSTALL_INTERFACE:include>
  PRIVATE src)

target_link_libraries(foo-core websocketpp spdlog) # <- see here, using spdlog & websocketpp

# 'make install' to the correct location

install(TARGETS foo-core EXPORT FooCoreConfig
  ARCHIVE  DESTINATION lib
  LIBRARY  DESTINATION lib
  RUNTIME  DESTINATION bin)
install(DIRECTORY include/ DESTINATION include)

install(EXPORT FooCoreConfig DESTINATION share/FooCore/cmake)

export(TARGETS foo-core FILE FooCoreConfig.cmake)

请注意我如何链接依赖项(它们是仅标头库!)。这就是我获取它们的方式(vendor/install.cmake):

# spdlog

if((NOT SPDLOG_INCLUDE_DIR) OR (NOT EXISTS ${SPDLOG_INCLUDE_DIR}))
  message("Unable to find spdlog, cloning...")

  execute_process(COMMAND git submodule update --init -- vendor/spdlog
    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})

  set(SPDLOG_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/vendor/spdlog/include/
    CACHE PATH "spdlog include directory")

  install(DIRECTORY ${SPDLOG_INCLUDE_DIR}/spdlog DESTINATION include)

  # Setup a target

  add_library(spdlog INTERFACE)
  target_include_directories(spdlog INTERFACE
    $<BUILD_INTERFACE:${SPDLOG_INCLUDE_DIR}>
    $<INSTALL_INTERFACE:include>)

  install(TARGETS spdlog EXPORT spdlog DESTINATION include)
endif()

# websocketpp

if((NOT WEBSOCKETPP_INCLUDE_DIR) OR (NOT EXISTS ${WEBSOCKETPP_INCLUDE_DIR}))
  message("Unable to find websocketpp, cloning...")

  execute_process(COMMAND git submodule update --init -- vendor/websocketpp
    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})

  set(WEBSOCKETPP_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/vendor/websocketpp/
    CACHE PATH "websocketpp include directory")

  install(DIRECTORY ${WEBSOCKETPP_INCLUDE_DIR}/websocketpp DESTINATION include)

  # Setup a target

  add_library(websocketpp INTERFACE)
  target_include_directories(websocketpp INTERFACE
    $<BUILD_INTERFACE:${WEBSOCKETPP_INCLUDE_DIR}>
    $<INSTALL_INTERFACE:include>)

  install(TARGETS websocketpp EXPORT websocketpp DESTINATION include)
endif()

# nlohmann/json

if((NOT NLOHMANN_JSON_INCLUDE_DIR) OR (NOT EXISTS ${NLOHMANN_JSON_INCLUDE_DIR}))    
  message("Unable to find nlohmann/json, cloning...")

  execute_process(COMMAND git submodule update --init -- vendor/nlohmann_json
    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})

  set(NLOHMANN_JSON_INCLUDE_DIR
    ${CMAKE_CURRENT_SOURCE_DIR}/vendor/nlohmann_json/src/
    CACHE PATH "nlohmann/json include directory")

  install(FILES ${NLOHMANN_JSON_INCLUDE_DIR}/json.hpp DESTINATION include)

  # Setup a target

  add_library(nlohmann_json INTERFACE )
  target_include_directories(nlohmann_json INTERFACE
    $<BUILD_INTERFACE:${NLOHMANN_JSON_INCLUDE_DIR}>
    $<INSTALL_INTERFACE:include>)

  install(TARGETS nlohmann_json EXPORT nlohmann_json DESTINATION include)
endif()

到目前为止一切顺利:您可以看到依赖项是作为 git 子模块获取的,幸运的是,这使得管理它们变得更容易。但是,当我使用 编译我的项目时mkdir build && cd build && cmake ../src,出现以下错误:

CMake 错误:安装(EXPORT FooCoreConfig ...)包括目标 foo-core,它需要不在导出集中的目标 websocketpp。

CMake 错误:安装(EXPORT FooCoreConfig ...)包括目标 foo-core,它需要不在导出集中的目标 spdlog。

包括标头,例如#include <spdlog/spdlog.h>#include <nlohmann/json.hpp>产生错误,指出标头未找到。

说实话,我对 CMake 不太满意,过去两天我一直在调试它。这可能是非常简单的事情,但我不知道如何实现它。实际上,只需将 -I 作为编译器标志传递即可使用我想要的库,但 CMake 抽象似乎让我感到困惑。如果有人能解释为什么这不起作用,我会很高兴,并且希望将这些库包含到我的项目中的正确方法是什么。提前致谢!

4

1 回答 1

2

正如您所说:您没有在导出集中安装目标。换句话说,您install(EXPORT ...的标头目标缺少一行。例如,考虑到您的仅标头库websocketpp,您应该具有:

add_library(websocketpp INTERFACE)
target_include_directories(websocketpp INTERFACE
  $<BUILD_INTERFACE:${WEBSOCKETPP_INCLUDE_DIR}>
  $<INSTALL_INTERFACE:include>)

install(TARGETS websocketpp EXPORT websocketpp-config DESTINATION include)

# here is the missing line:

install(EXPORT websocketpp-config DESTINATION share/websocketpp/cmake)

其他图书馆也是如此。

于 2016-12-27T13:37:00.127 回答