【CMake】(12)项目嵌套

项目嵌套

在一个大型项目中,通常会涉及到多个组件,比如库(静态库或动态库)、应用程序、测试单元等。为了更好地管理和维护这些组件,我们可以将它们组织成不同的子目录,并为每个子目录创建一个CMakeLists.txt文件。

add_subdirectory允许你在一个大型项目中包含多个子项目或子目录,从而实现项目的模块化管理。

基本用法

cmake 复制代码
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
  • source_dir 指定了包含CMakeLists.txt文件的子目录的路径。CMake将会处理这个目录下的CMakeLists.txt,并包含其定义的目标(targets)到主项目中。
  • binary_dir 是可选参数,用来指定生成的中间文件和目标文件的存放路径。如果不指定,CMake默认在当前正在执行的构建目录中创建与source_dir同名的目录作为binary_dir
  • EXCLUDE_FROM_ALL 也是可选的。添加这个选项会使得这个子目录中的目标不会被顶级目标ALL包含,即使用makemake all命令时,这个子目录中的目标不会被构建。这对于可选组件或示例代码非常有用。

示例一

项目目录结构

假设我们有以下项目结构:

bash 复制代码
MyProject/
├── CMakeLists.txt          # 根CMake配置文件
├── lib/                    # 存放库的目录
│   ├── utility/            # 实用工具库
│   └── core/               # 核心功能库
├── app/                    # 主应用程序
└── tests/                  # 测试代码

在这个项目中,我们希望构建两个库(utilitycore),一个应用程序(app),以及一个测试单元(tests)。

根目录的CMake配置

根目录的CMakeLists.txt文件是整个项目构建过程的起点。在这个文件中,我们首先定义了项目的基本信息,然后使用add_subdirectory命令添加每个子目录。

cmake 复制代码
cmake_minimum_required(VERSION 3.10)
project(MyProject VERSION 1.0)

# 设置C++标准
set(CMAKE_CXX_STANDARD 17)

# 包含各个子目录
add_subdirectory(lib/utility)
add_subdirectory(lib/core)
add_subdirectory(app)
add_subdirectory(tests)

这个文件指示CMake处理根目录下的每个子目录,每个子目录中都应该有自己的CMakeLists.txt文件来定义如何构建那个目录中的代码。

子目录的CMake配置

每个子目录中的CMakeLists.txt文件定义了该目录中组件的构建规则。

实用工具库(lib/utility/CMakeLists.txt

这个文件定义了一个静态库,并指定了库的源文件。通过target_include_directories命令,我们还指定了其他目标在编译时应该包含的头文件目录。

cmake 复制代码
add_library(utility STATIC utility.cpp)
target_include_directories(utility PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

核心功能库(lib/core/CMakeLists.txt

类似地,这个文件定义了一个动态库,并指定了库的源文件和头文件目录。

cmake 复制代码
add_library(core SHARED core.cpp)
target_include_directories(core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

主应用程序(app/CMakeLists.txt

这里,我们定义了一个可执行文件myapp,并指定了它的源文件。使用target_link_libraries命令,我们还告诉了CMake这个可执行文件依赖于哪些库。

cmake 复制代码
add_executable(myapp main.cpp)
target_link_libraries(myapp utility core)

测试单元(tests/CMakeLists.txt

在这个目录中,我们同样定义了一个可执行文件来运行测试,并链接了必要的库。

cmake 复制代码
enable_testing()
add_executable(mytest test.cpp)
target_link_libraries(mytest utility core)
add_test(NAME mytest COMMAND mytest)

示例二:静态库链接静态库

由于静态库是在编译时被链接到最终的可执行文件或动态库中的,因此你不能直接在一个静态库中"链接"另一个静态库,就像你在可执行文件或动态库中做的那样。但是,你可以组织你的CMake项目和代码,使得最终的可执行文件能够正确地链接到所有必要的静态库。

假设我们有两个静态库libAlibB,其中libB依赖于libA,并且有一个最终的可执行文件app需要链接这两个库。

CMakeLists.txt for libA

cmake 复制代码
add_library(libA STATIC libA.cpp libA.h)

CMakeLists.txt for libB

libB的CMakeLists.txt中,我们不直接链接libA,而是使用target_link_librarieslibA的链接需求传递给使用libB的目标:

cmake 复制代码
add_library(libB STATIC libB.cpp libB.h)

# 使用PUBLIC关键字传递链接依赖
target_link_libraries(libB PUBLIC libA)

CMakeLists.txt for app

在最终的应用程序中,当你链接libB时,libA的链接需求也会自动被链接到最终的可执行文件中:

cmake 复制代码
add_executable(app main.cpp)

# 只需要链接libB,libA的链接需求会自动被处理
target_link_libraries(app PRIVATE libB)

注意事项

  • 使用PUBLICINTERFACE关键字在目标之间传递链接依赖。PUBLIC意味着链接依赖对库自己和使用它的目标都是可见的,而INTERFACE意味着链接依赖只对使用它的目标可见。
  • 确保在最顶层的CMakeLists.txt文件中正确地包括了所有子项目的add_subdirectory命令,以确保CMake能够发现并配置所有相关的库。
相关推荐
凌云行者18 小时前
OpenGL入门005——使用Shader类管理着色器
c++·cmake·opengl
凌云行者18 小时前
OpenGL入门006——着色器在纹理混合中的应用
c++·cmake·opengl
赵民勇3 天前
cmake中execute_process详解
cmake
凌云行者3 天前
OpenGL入门004——使用EBO绘制矩形
c++·cmake·opengl
长弓聊编程6 天前
应该怎么理解CMakeLists.txt中一些指令的INTERFACE、PUBLIC和PRIVATE参数
cmake
凌云行者6 天前
OpenGL入门003——使用Factory设计模式简化渲染流程
c++·cmake·opengl
凌云行者7 天前
OpenGL入门002——顶点着色器和片段着色器
c++·cmake·opengl
梦起丶8 天前
CMake 生成器表达式介绍
c++·cmake
梦起丶10 天前
CMake 生成器表达式---条件表达式和逻辑运算符
c++·cmake
咩咩大主教12 天前
QtCreator通过CMake多文件编译.cpp、.qss、.h、.ui文件,达到MVC三层架构的效果
c++·qt·cmake·qtcreator·qss·多文件编译·qmake