项目嵌套
在一个大型项目中,通常会涉及到多个组件,比如库(静态库或动态库)、应用程序、测试单元等。为了更好地管理和维护这些组件,我们可以将它们组织成不同的子目录,并为每个子目录创建一个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能够发现并配置所有相关的库。