用 CMake 构建 C++ 程序
上一章每次修改后都要手动执行编译命令。本章把编译参数写进 CMakeLists.txt,再用 CMake 和 Ninja 重复构建。
查看工程文件
示例工程包含构建规则、辅助脚本和程序源码:
工程目录
cmake-project/
├── CMakeLists.txt # 构建规则
├── build-and-run.sh # 一键配置、构建并运行
└── main.cpp # 程序源码
程序入口仍然只负责输出问候句,避免把语言细节和构建工具混在一起。
main.cpp
// 程序入口:向标准输出打印问候。
#include <iostream>
int main() {
std::cout << "Hello, World!\n";
return 0;
}构建规则如下:
CMakeLists.txt
# 构建 CMakeProject 可执行目标。
cmake_minimum_required(VERSION 3.31)
project(CMakeProject LANGUAGES CXX)
# 使用 C++20 作为语言标准。
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 可执行文件统一输出到 build/bin。
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
# 编译数据库,记录每个源文件的实际编译参数。
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# 创建 app 可执行目标。
add_executable(app main.cpp)
# 编译时启用 libc++ 与常用警告。
target_compile_options(app PRIVATE -stdlib=libc++ -Wall -Wextra -Werror)
# 链接时也使用 libc++。
target_link_options(app PRIVATE -stdlib=libc++)project() 设置工程名,set() 选择 C++20、输出目录和编译数据库。CMAKE_EXPORT_COMPILE_COMMANDS 生成的 compile_commands.json 供 clangd 读取,编辑器的代码提示才能拿到真实编译参数。
配置工程
在工程根目录执行下面的命令。CMake 会读取构建规则,并生成 Ninja 使用的文件。
Ubuntu Bash(工程根目录)
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=clang++-S . 指定源码目录,-B build 指定构建目录,-G Ninja 选择 Ninja。配置命令成功后,构建目录中会保存后续构建所需的规则。
构建并运行
Ubuntu Bash(工程根目录)
# 编译并链接 app
cmake --build build
# 运行程序
./build/bin/app输出如下:
输出
Hello, World!
CMake 生成 build/bin/app,Ninja 负责实际编译和链接。修改 main.cpp 后再次执行 cmake --build build 即可。
也可以直接运行 bash build-and-run.sh,依次完成配置、构建和运行。
常见错误
配置时找不到 Ninja
先检查命令是否安装:
Ubuntu Bash
command -v cmake ninja clang++缺少工具时,回到准备 WSL2 C++ 开发环境重新安装。
构建后找不到 app
执行 ./build/bin/app 时提示文件不存在,或构建结束后 build/bin/app 仍未生成,按下面的顺序定位后重新构建并运行。
确认执行位置
当前目录不是工程根目录时,
build/会指向错误位置。用根目录特征文件确认位置:Ubuntu Bash
ls CMakeLists.txt输出包含
CMakeLists.txt,说明当前位于工程根目录。否则先用cd进入示例工程目录。重新构建并定位失败来源
重新构建并显示每条实际执行的编译命令:
Ubuntu Bash(工程根目录)
cmake --build build --verbose错误通常来自第一条编译信息,按文件名和行号修正源码后重新构建。
确认产物已生成
Ubuntu Bash(工程根目录)
ls -l build/bin/app命令列出
app文件后,运行./build/bin/app,确认输出仍为Hello, World!。
自测问题
先不要展开答案,试着自己把回答完整说出来,再展开对照。说不出来的部分,回到对应小节对照正文。
修改
main.cpp后,为什么通常只需要再次执行cmake --build build,而不必重新执行配置命令?查看答案
配置阶段已经把
CMakeLists.txt转换成构建规则。修改源文件只影响构建阶段,cmake --build build会根据依赖关系重新编译并链接。只有构建规则、生成器或编译器配置改变时,才需要重新配置。cmake -S . -B build和cmake --build build分别负责什么?查看答案
前者读取项目配置并生成构建规则,后者执行这些规则,实际调用编译器和链接器生成程序。
本章回顾
你已经能用 CMake 保存构建规则,并通过 cmake --build 构建 app。下一章会把代码拆成多个文件。