2、第一个CMake项目:构建一个最简单的Hello World项目、理解CMakeLists.txt的基本结构、配置与构建分离(out-of-source build)
说实话,很多初学者学CMake,上来就被各种变量、函数、模块吓住了。其实没必要。CMake最核心的思想,说白了就两件事:描述项目和生成构建文件。今天我们就从零开始,亲手搭一个Hello World项目。你跟着我走一遍,就会发现这东西其实挺朴素的。
2.1 项目结构:先搭个架子
我个人习惯,每个CMake项目都单独建一个目录。这样干净,不会跟其他项目混在一起。咱们先创建一个文件夹,就叫 hello_cmake。
hello_cmake/
├── CMakeLists.txt
└── main.cpp
对,就两个文件。一个C++源文件,一个CMake的配置文件。别小看这个结构,它已经是一个完整的CMake项目了。
main.cpp 的内容很简单:
#include <iostream>
int main() {
std::cout << "Hello, CMake World!" << std::endl;
return 0;
}
嗯,这里要注意:main.cpp 里不要写任何CMake相关的东西。它就是纯C++代码。CMake只管怎么编译它,不管它里面写了什么。
2.2 CMakeLists.txt:项目的“说明书”
接下来是重头戏——CMakeLists.txt。这是CMake项目的核心配置文件。CMake读的就是它,然后根据它生成Makefile或者Visual Studio工程文件。
咱们的 CMakeLists.txt 长这样:
cmake_minimum_required(VERSION 3.16)
project(HelloCMake)
add_executable(hello main.cpp)
三行代码,搞定。我来逐行解释一下。
| 指令 | 作用 | 我的理解 |
|---|---|---|
cmake_minimum_required(VERSION 3.16) |
指定CMake最低版本 | 告诉使用者:“你的CMake版本不能低于3.16,否则别想编译我的项目。” 我在项目中遇到过因为版本太低导致某些特性用不了的情况,所以这个版本号别乱写,要跟你实际用的CMake版本匹配。 |
project(HelloCMake) |
定义项目名称 | 给项目起个名字。这个名字会影响到后面生成的一些变量,比如 PROJECT_NAME、PROJECT_SOURCE_DIR 等。 |
add_executable(hello main.cpp) |
声明一个可执行文件 | 告诉CMake:“我要编译一个叫 hello 的可执行文件,它的源代码是 main.cpp。” 如果有多个源文件,可以继续往后加,用空格隔开。 |
cmake_minimum_required 最好放在第一行。CMake在处理文件时,会先检查版本号,如果版本不满足,直接报错退出,不会继续往下执行。这样可以避免后面出现莫名其妙的错误。
2.3 配置与构建分离:为什么我强烈推荐out-of-source build
好,现在项目文件准备好了。接下来要编译它。但这里有个关键问题:在哪里执行CMake命令?
有两种做法:
- In-source build(源码内构建):直接在项目根目录下执行
cmake .,生成的中间文件(Makefile、CMakeCache.txt等)跟源代码混在一起。 - Out-of-source build(源码外构建):在项目根目录外单独建一个
build目录,在build目录里执行cmake ..,生成的中间文件全部放在build目录里。
我建议你从一开始就养成out-of-source build的习惯。为什么?
- 干净:源代码目录里只有
.cpp和.h文件,没有乱七八糟的CMakeFiles、CMakeCache.txt、Makefile等。想清理构建产物,直接rm -rf build就行,不会误删源代码。 - 灵活:可以同时维护多个构建目录,比如
build-debug、build-release、build-windows,每个目录对应不同的配置。切换构建时,只需要换个目录,不需要重新配置整个项目。 - 安全:不会因为误操作把构建文件提交到版本控制里。你想想看,如果
CMakeFiles目录被提交到Git,那每次构建都会产生大量变更,烦不烦?
cmake .,然后 make,结果编译出来的可执行文件跟源代码混在一起。后来他想清理,不小心把 main.cpp 也删了……嗯,从那以后我再也不敢用in-source build了。
2.4 动手实践:配置与构建
好,理论说完了,咱们来实际操作一遍。
- 在
hello_cmake目录下创建build目录:mkdir build - 进入
build目录:cd build - 执行CMake配置:
cmake .. - 执行构建:
cmake --build .
执行完第三步,你会看到类似这样的输出:
-- The C compiler identification is GNU 11.4.0
-- The CXX compiler identification is GNU 11.4.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc - works
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ - works
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/user/hello_cmake/build
看到 Configuring done 和 Generating done,说明配置成功了。CMake在 build 目录里生成了Makefile(或者其他构建系统的文件)。
第四步 cmake --build . 等价于在 build 目录里执行 make。它会调用编译器,把 main.cpp 编译成可执行文件 hello。
构建完成后,在 build 目录下会多出一个 hello 文件(Windows下是 hello.exe)。运行它:
./hello
Hello, CMake World!
搞定。第一个CMake项目,跑起来了。
2.5 知识体系:一张图看懂CMake构建流程
为了让你更直观地理解整个流程,我画了一张图。它展示了从源代码到可执行文件的完整路径,以及配置与构建分离的核心思想。
2.6 常见问题与避坑
第一次跑CMake,可能会遇到几个小坑。我帮你提前踩一遍。
如果你在
cmake_minimum_required 里写了 3.16,但系统装的CMake是 3.10,CMake会直接报错退出。解决办法:要么升级CMake,要么把版本号改低一点。但我建议升级,因为新版本有很多好用的特性。
如果你直接在项目根目录下执行
cmake .,那就是in-source build。虽然也能编译成功,但后面清理起来很麻烦。我建议你养成习惯:先 mkdir build && cd build,再 cmake ..。
在
add_executable 里,源文件路径是相对于 CMakeLists.txt 的。如果你把 main.cpp 放在 src 子目录里,就要写成 add_executable(hello src/main.cpp)。路径写错了,CMake会提示找不到文件。
2.7 小结
这一章我们做了三件事:
- 写了一个最简单的
CMakeLists.txt,理解了它的三行核心指令。 - 实践了out-of-source build,把配置和构建分离开。
- 成功编译并运行了第一个CMake项目。
别看这个项目小,它背后的思想——配置与构建分离——是CMake所有高级功能的基础。后面的章节里,我们会在这个基础上不断加东西:添加库、管理依赖、跨平台编译……但核心流程不会变。
好,这一章就到这儿。你可以在自己的机器上动手试试,把每一步都跑通。遇到问题别慌,回头看看上面的避坑指南,多半能找到答案。