包含子库的现代CMake库

时间:2019-05-09 11:53:34

标签: cmake

我有几个库(子项目)作为一个主库的一部分。如何将子库(b)汇总到“主库”(a)中?

目标是仅拥有一个图书馆a并按如下方式使用子图书馆#include <b/b.h>

(有一种如下包含路径的方法:#include <a/b/b.h>吗?)

假设以下文件夹结构:

a
|-- CMakeLists.txt
|-- aConfig.cmake
`-- b
    |-- CMakeLists.txt
    |-- include
    |   `-- b
    |       `-- b.h
    `-- src
        `-- b.cpp


我有以下库a,具体取决于库b。必须安装库a,并以现代CMake方式使用find_package(a CONFIG REQUIRED)命令找到该库。该库不提供任何实现,但应汇总这些子库。我以为制作target_include_directories Public会将INTERFACE_INCLUDE_DIRECTORIES属性传播到库a,因为它链接到b,但我无法设法将其传播到工作:

cmake_minimum_required(VERSION 3.0.2)

project(a)

find_package(Eigen3 3.2.2 REQUIRED)

add_subdirectory(b)

## Build
add_library(a)
add_library(a::a ALIAS a)

target_link_library(a
PUBLIC
  b::b
)

## Install

include(GNUInstallDirs)

# create export set
install(TARGETS a
  EXPORT "aTargets"
  ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
  LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
)

install(
  DIRECTORY
    b/include/
  DESTINATION
    "${CMAKE_INSTALL_INCLUDEDIR}"
)

install(EXPORT "${PROJECT_NAME}Targets"
  FILE
    "aTargets.cmake"
  NAMESPACE
    "a::"
  DESTINATION
    lib/cmake/a
)

install(
  FILES
    "aConfig.cmake"
  DESTINATION
    "lib/cmake/a"
)

2 个答案:

答案 0 :(得分:1)

这将要求您更改目录结构。标头包含与物理路径相关,并且没有类似于a/b/b.h的物理路径。解决方案是使用以下方式更改目录结构:

a
|-- CMakeLists.txt
|-- aConfig.cmake
`-- b
    |-- CMakeLists.txt
    |-- include
    |   `-- a
    |       `-- b
    |           `-- b.h
    `-- src
        `-- b.cpp

并将目标包含目录设置为a/b/include

答案 1 :(得分:1)

同时使用interface librariesrelocatable packages可以解决您的问题。下面是一个最小的工作示例。

假设我们具有以下目录结构

.
├── a
│   ├── b
│   │   ├── CMakeLists.txt
│   │   ├── include
│   │   │   └── b
│   │   │       └── b.h
│   │   └── src
│   │       └── b.cpp
│   ├── c
│   │   ├── CMakeLists.txt
│   │   ├── include
│   │   │   └── c
│   │   │       └── c.h
│   │   └── src
│   │       └── c.cpp
│   └── CMakeLists.txt
├── CMakeLists.txt
└── main.cpp

9 directories, 9 files

其中a是您的超级项目,其中包含其他子项目。 bc库的内容如下:

# a/b/CMakeLists.txt
cmake_minimum_required(VERSION 3.9)

project(b CXX)

add_library(b
  include/b/b.h
  src/b.cpp
)
add_library(b::b ALIAS b)

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

install(TARGETS
  b
    ARCHIVE DESTINATION lib
    LIBRARY DESTINATION lib
)
install(DIRECTORY include/b DESTINATION include)

/* a/b/include/b/b.h */
#ifdef __cplusplus
extern "C" {
#endif

double funcb(const double);

#ifdef __cplusplus
}
#endif

/* a/b/src/b.cpp */
#include "b/b.h"

extern "C" {
double funcb(const double x) { return x + x; }
}

# a/c/CMakeLists.txt
cmake_minimum_required(VERSION 3.9)

project(c CXX)

add_library(c
  include/c/c.h
  src/c.cpp
)
add_library(c::c ALIAS c)

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

install(TARGETS
  c
    ARCHIVE DESTINATION lib
    LIBRARY DESTINATION lib
)
install(DIRECTORY include/c DESTINATION include)

/* a/c/include/c/c.h */
#ifdef __cplusplus
extern "C" {
#endif

double funcc(const double);

#ifdef __cplusplus
}
#endif

/* a/c/src/c.cpp */
#include "c/c.h"

extern "C" {
double funcc(const double x) { return x * x; }
}

然后,您只需将a变成INTERFACE库,如下所示:

# a/CMakeLists.txt
cmake_minimum_required(VERSION 3.9)

project(a)

add_subdirectory(b)
add_subdirectory(c)

add_library(a INTERFACE)
add_library(a::a ALIAS a)
target_link_libraries(a
  INTERFACE
    b::b
    c::c
)

最后,您的应用程序将照常使用bc库:

#include <iostream>
using namespace std;

#include "b/b.h"
#include "c/c.h"

int main(int argc, char* argv[]) {
  cout << funcb(5) << '\n';
  cout << funcc(5) << '\n';
  return 0;
}

,相应的CMakeLists.txt将显示

cmake_minimum_required(VERSION 3.9)

project(app)

add_subdirectory(a)

add_executable(app main.cpp)
target_link_libraries(app PRIVATE a::a)

我希望这可以解决您的问题。