CMake 多目录项目构建完整笔记
一、项目目录结构
cc/
├── CMakeLists.txt # 顶层构建文件
├── include/ # 公共头文件目录
│ ├── calc.h # 四则运算头文件
│ └── sort.h # 排序算法头文件
├── calc/ # 四则运算静态库目录
│ ├── add.cpp
│ ├── sub.cpp
│ ├── mul.cpp
│ ├── div.cpp
│ └── CMakeLists.txt
├── lib/ # 静态库输出目录(自动生成)
│ └── libcalc.a
│ └── libsort.a
├── sort/ # 排序算法静态库目录
│ ├── insert.cpp # 插入排序
│ ├── select.cpp # 选择排序
│ └── CMakeLists.txt
├── test1/ # 四则运算测试程序
│ ├── calc.cpp # 测试主文件
│ └── CMakeLists.txt
├── test2/ # 排序算法测试程序
│ ├── sort.cpp # 测试主文件
│ └── CMakeLists.txt
├── bin/ # 可执行文件输出目录(自动生成)
│ ├── app1 # 四则运算测试程序
│ └── app2 # 排序算法测试程序
└── build/ # 外部构建目录(推荐方式)
二、顶层 CMakeLists.txt(项目入口)
cmake_minimum_required(VERSION 3.15)
project(test)# 定义项目根目录路径
set(PROJECT_ROOT ${CMAKE_CURRENT_SOURCE_DIR})# 定义静态库生成路径
set(LIBPATH ${PROJECT_ROOT}/lib)# 定义可执行文件的存储目录
set(EXECPATH ${PROJECT_ROOT}/bin)# 定义头文件路径
set(HEADPATH ${PROJECT_ROOT}/include)# 定义库文件的名字
set(CALCLIB calc)
set(SORTLIB sort)# 定义可执行文件的名字
set(APPNAME1 app1)
set(APPNAME2 app2)# 给当前节点添加子目录
add_subdirectory(calc)
add_subdirectory(sort)
add_subdirectory(test1)
add_subdirectory(test2)
三、子目录 CMakeLists.txt 详解
1. calc/CMakeLists.txt(四则运算静态库)
cmake_minimum_required(VERSION 3.15)
project(calc)# 搜索当前目录下所有源文件
aux_source_directory(./ SRC)# 加载公共头文件路径
include_directories(${HEADPATH})# 设置静态库生成路径
set(LIBRARY_OUTPUT_PATH ${LIBPATH})# 生成静态库 calc
add_library(${CALCLIB} STATIC ${SRC})
2. sort/CMakeLists.txt(排序算法静态库)
cmake_minimum_required(VERSION 3.15)
project(sort)# 搜索当前目录下所有源文件
aux_source_directory(./ SRC)# 加载公共头文件路径
include_directories(${HEADPATH})# 设置静态库生成路径
set(LIBRARY_OUTPUT_PATH ${LIBPATH})# 生成静态库 sort
add_library(${SORTLIB} STATIC ${SRC})
3. test1/CMakeLists.txt(四则运算测试程序)
cmake_minimum_required(VERSION 3.15)
project(test1)# 搜索当前目录下所有源文件
aux_source_directory(./ SRC)# 加载公共头文件路径
include_directories(${HEADPATH})# 链接的库路径(告诉编译器去哪找静态库)
link_directories(${LIBPATH})# 设置可执行文件输出到 bin 文件夹
set(EXECUTABLE_OUTPUT_PATH ${EXECPATH})# 生成可执行文件 app1
add_executable(${APPNAME1} ${SRC})# 现代 CMake 链接方式(推荐)
target_link_libraries(${APPNAME1} PRIVATE ${CALCLIB})
4. test2/CMakeLists.txt(排序算法测试程序)
cmake_minimum_required(VERSION 3.15)
project(test2)# 搜索当前目录下所有源文件
aux_source_directory(./ SRC)# 加载公共头文件路径
include_directories(${HEADPATH})# 链接的库路径(告诉编译器去哪找静态库)
link_directories(${LIBPATH})# 设置可执行文件输出到 bin 文件夹
set(EXECUTABLE_OUTPUT_PATH ${EXECPATH})# 生成可执行文件 app2
add_executable(${APPNAME2} ${SRC})# 现代 CMake 链接方式(推荐)
target_link_libraries(${APPNAME2} PRIVATE ${SORTLIB})
四、关键配置解析
1. 变量定义(顶层文件)
| 变量 |
作用 |
示例值 |
PROJECT_ROOT |
项目根目录路径 |
${CMAKE_CURRENT_SOURCE_DIR} |
LIBPATH |
静态库输出路径 |
${PROJECT_ROOT}/lib |
EXECPATH |
可执行文件输出路径 |
${PROJECT_ROOT}/bin |
HEADPATH |
公共头文件路径 |
${PROJECT_ROOT}/include |
CALCLIB/SORTLIB |
静态库目标名 |
calc / sort |
APPNAME1/APPNAME2 |
可执行文件目标名 |
app1 / app2 |
2. add_subdirectory 命令
- 作用:添加子目录,构建子项目
- 示例:
add_subdirectory(calc) → 进入 calc/ 目录执行其 CMakeLists.txt
- 执行顺序:按添加顺序构建,先构建库,再构建可执行文件
3. aux_source_directory 命令
- 作用:自动扫描指定目录下的所有
.cpp 文件,存入变量
- 示例:
aux_source_directory(./ SRC) → 当前目录下所有源文件存入 SRC
- 注意:仅扫描当前目录,不递归子目录
4. target_link_libraries 命令
- 作用:将可执行文件与静态库链接
- 语法:
target_link_libraries(目标名 PRIVATE 库名)
- 特点:现代 CMake 推荐方式,自动处理依赖关系
五、编译与运行步骤
1. 清空并进入构建目录
cd build
rm -rf *
2. 生成构建文件
cmake ..
3. 编译项目
make
4. 运行测试程序
# 四则运算测试
./bin/app1# 排序算法测试
./bin/app2
六、常见问题与解决
1. 头文件找不到
- 原因:未设置
include_directories(${HEADPATH})
- 解决:确保子目录
CMakeLists.txt 中包含该命令
2. 静态库找不到
- 原因:未设置
link_directories(${LIBPATH}) 或静态库未生成
- 解决:
- 检查
lib/ 目录下是否生成 libcalc.a 和 libsort.a
- 确保
test1/ 和 test2/ 中包含 link_directories(${LIBPATH})
3. 链接错误 undefined reference
- 原因:未使用
target_link_libraries 链接静态库
- 解决:确保可执行文件的
CMakeLists.txt 中包含链接命令
七、总结
- 项目结构规范:公共头文件统一放在
include/,库文件放在 lib/,可执行文件放在 bin/
- 模块化构建:使用
add_subdirectory 管理子项目,实现库与可执行文件分离
- 现代 CMake 实践:优先使用
target_link_libraries 链接库,避免旧版 link_libraries
- 路径管理:通过顶层变量统一管理路径,避免硬编码