项目嵌套
在一个大型项目中,通常会涉及到多个组件,比如库(静态库或动态库)、应用程序、测试单元等。为了更好地管理和维护这些组件,我们可以将它们组织成不同的子目录,并为每个子目录创建一个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
包含,即使用make
或make all
命令时,这个子目录中的目标不会被构建。这对于可选组件或示例代码非常有用。
示例一
项目目录结构
假设我们有以下项目结构:
bash
MyProject/
├── CMakeLists.txt # 根CMake配置文件
├── lib/ # 存放库的目录
│ ├── utility/ # 实用工具库
│ └── core/ # 核心功能库
├── app/ # 主应用程序
└── tests/ # 测试代码
在这个项目中,我们希望构建两个库(utility
和core
),一个应用程序(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项目和代码,使得最终的可执行文件能够正确地链接到所有必要的静态库。
假设我们有两个静态库libA
和libB
,其中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_libraries
将libA
的链接需求传递给使用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)
注意事项
- 使用
PUBLIC
或INTERFACE
关键字在目标之间传递链接依赖。PUBLIC
意味着链接依赖对库自己和使用它的目标都是可见的,而INTERFACE
意味着链接依赖只对使用它的目标可见。 - 确保在最顶层的
CMakeLists.txt
文件中正确地包括了所有子项目的add_subdirectory
命令,以确保CMake能够发现并配置所有相关的库。