2、第一个CMake项目:从零开始编写CMakeLists.txt、cmake_minimum_required、project()命令详解

2.1 从零开始:一个最简单的CMake项目长什么样?

说实话,我见过不少新手一上来就复制粘贴别人的CMakeLists.txt,结果编译报错一脸懵。其实CMake项目没那么玄乎,说白了就是一个文本文件,名字叫CMakeLists.txt,放在你的源码目录里。

咱们先看一个最简版本。假设你有一个main.cpp:

#include <iostream>
int main() {
    std::cout << "Hello, CMake!" << std::endl;
    return 0;
}

那它的CMakeLists.txt长这样:

cmake_minimum_required(VERSION 3.10)
project(HelloCMake)
add_executable(hello main.cpp)

就三行。嗯,你没看错,三行就够了。我刚开始学CMake时也觉得不可思议——这么简单?后来才发现,简单是因为项目小,但核心骨架确实就这些。

2.2 cmake_minimum_required:版本声明,别不当回事

这条命令的作用,就是告诉CMake:我的项目至少需要哪个版本的CMake才能构建。

语法很简单:

cmake_minimum_required(VERSION <版本号> [FATAL_ERROR])

我个人习惯在项目里写VERSION 3.10或3.16。为什么?因为3.10之后支持了很多现代特性,比如target_compile_features、INTERFACE库这些。如果你写2.8,那很多新语法就用不了。

⚠️ 避坑指南
我曾经在一个团队项目里看到有人写了cmake_minimum_required(VERSION 2.8),结果新同事的机器上装了CMake 3.20,但项目里用了target_sources——这个命令在3.0之后才稳定。编译报错查了半天,最后发现是版本声明太低,CMake没有启用新特性。记住:版本声明不是摆设,它决定了CMake的行为模式。

另外,FATAL_ERROR参数是可选的。加上它,如果版本不满足,CMake会直接报错退出,而不是给个警告继续跑。我建议加上,避免后续莫名其妙的错误。

2.3 project():给项目一个身份

project()是CMake项目的核心命令。它不只是起个名字那么简单,它还会干几件重要的事:

  • 设置项目名称:比如project(MyApp),后面可以用${PROJECT_NAME}引用
  • 设置语言支持:默认支持C和C++,你也可以指定LANGUAGES CXX只支持C++
  • 定义版本变量:如果你写了project(MyApp VERSION 1.0.0),CMake会自动生成PROJECT_VERSION、PROJECT_VERSION_MAJOR等变量
  • 设置一些路径变量:比如PROJECT_SOURCE_DIR、PROJECT_BINARY_DIR

完整语法长这样:

project(<项目名>
        [VERSION <主版本>.<次版本>.<补丁>]
        [DESCRIPTION <描述文本>]
        [HOMEPAGE_URL <网址>]
        [LANGUAGES <语言列表>])

举个例子:

project(MyAwesomeApp
        VERSION 2.1.0
        DESCRIPTION "一个很酷的应用"
        HOMEPAGE_URL "https://example.com"
        LANGUAGES CXX)

你想想看,如果你要做一个库,别人用的时候想知道版本号,直接用${PROJECT_VERSION}就能拿到,多方便。

2.4 第一个项目的完整流程

咱们来走一遍实际操作的流程。假设你的项目结构是:

my_project/
├── CMakeLists.txt
└── src/
    └── main.cpp

CMakeLists.txt内容:

cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
project(FirstDemo VERSION 0.1.0 LANGUAGES CXX)

add_executable(first_demo src/main.cpp)

然后在终端里:

mkdir build
cd build
cmake ..
cmake --build .

就这么简单。我个人习惯在build目录里构建,这样源码目录干干净净,不会有一堆编译中间文件。

💡 小技巧
如果你用的是CMake 3.13以上版本,可以直接用cmake -B build代替mkdir build && cd build && cmake ..。一步到位,省事。

2.5 常见错误与避坑

我整理了几个新手最容易踩的坑,你看看自己中过几个:

错误写法 问题说明 正确写法
cmake_minimum_required(3.10) 漏了VERSION关键字 cmake_minimum_required(VERSION 3.10)
project()写在cmake_minimum_required前面 CMake会警告,某些变量可能未定义 先写cmake_minimum_required,再写project()
add_executable(hello hello.cpp)拼错文件名 编译时报找不到文件,但错误信息不明显 仔细检查文件名大小写和路径
在源码目录直接运行cmake . 生成的文件混在源码里,清理麻烦 使用mkdir build && cd build && cmake ..
⚠️ 避坑指南
我曾经犯过一个低级错误:在project()里写了VERSION 1.0,但后面代码里用了${PROJECT_VERSION_MAJOR}和${PROJECT_VERSION_MINOR}。结果MINOR变量是空的——因为CMake要求版本号必须是主.次.补丁三段式,只写两段的话,MINOR和PATCH都不会生成。从那以后,我写版本号都老老实实写三段。

2.6 小结

这一章的内容其实就两个核心命令:cmake_minimum_required和project()。它们就像项目的身份证和出生证明,缺一不可。记住:

  • cmake_minimum_required要写在最前面,版本号别写太低
  • project()不只是起名字,它定义了很多有用的变量
  • 构建时用build目录,保持源码干净

下一章我们会深入add_executable和add_library,看看怎么把多个源文件组织起来。到时候你会发现,CMake的威力才刚刚开始展现。