3、第一个CMake项目:CMakeLists.txt基础、project()与cmake_minimum_required()、构建与运行

好,咱们现在正式开始动手写第一个CMake项目。

说实话,很多初学者一上来就去看复杂的CMake脚本,结果被各种变量和函数绕晕了。我个人习惯是,先搞懂最核心的三个东西:CMakeLists.txt怎么写、project()和cmake_minimum_required()是干什么的、以及怎么把代码跑起来。这三样搞明白了,后面再学别的就顺了。

3.1 CMakeLists.txt:CMake的“剧本”

每个CMake项目,根目录下都必须有一个CMakeLists.txt文件。你可以把它理解成一份“剧本”,告诉CMake你的项目叫什么、用什么语言、有哪些源文件、要生成什么目标(可执行文件还是库)。

我在项目中遇到过有人把CMakeLists.txt写成了CMakeList.txt(少了个s),结果CMake死活不认。嗯,文件名必须一字不差,大小写也敏感。

一个最简单的CMakeLists.txt长这样:

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

就三行。咱们一行一行拆开看。

3.2 cmake_minimum_required():定个“最低版本”

这行代码的意思是:告诉CMake,我这个项目至少需要3.10版本才能构建。

为什么要写这个?因为CMake不同版本之间,有些函数的行为会变。比如早期版本里某些变量是自动设置的,后来版本改了规则。如果你不指定最低版本,别人用旧版CMake跑你的项目,可能就编译不过。

⚠️ 注意: 我曾经见过一个项目,cmake_minimum_required()写的是2.8,结果在新版CMake上各种警告。后来发现是有些旧语法被废弃了。所以我的建议是:尽量写一个你测试过的、比较新的版本。比如现在普遍用3.10以上,我就写3.10。

语法很简单:

cmake_minimum_required(VERSION <版本号>)

版本号格式是主版本.次版本,也可以带上补丁号,比如3.10.2。但通常只写到次版本就够了。

3.3 project():给你的项目起个名

project()是每个CMake项目必须调用的第二个函数。它主要干三件事:

  • 设置项目名称:比如project(HelloWorld),项目名就是HelloWorld。
  • 设置语言:默认支持C和C++。你也可以指定只支持C,比如project(HelloWorld LANGUAGES C)。
  • 自动定义一些变量:比如PROJECT_NAME、PROJECT_SOURCE_DIR、PROJECT_BINARY_DIR。这些变量后面会经常用到。

举个例子:

project(MyApp VERSION 1.0.0 DESCRIPTION "我的第一个CMake应用" LANGUAGES CXX)

这里我指定了版本号、描述,还明确只支持C++(CXX)。

💡 小技巧: 我个人习惯在project()里就把版本号写上,这样后面生成安装包或者打印版本信息时,直接引用PROJECT_VERSION变量就行,不用再手动定义。

3.4 add_executable():生成可执行文件

这行告诉CMake:我要生成一个可执行文件,名字叫hello,源文件是main.cpp。

add_executable(hello main.cpp)

如果有多个源文件,可以这样写:

add_executable(hello main.cpp utils.cpp helper.cpp)

或者用变量收集:

set(SOURCES main.cpp utils.cpp helper.cpp)
add_executable(hello ${SOURCES})

我个人更推荐用变量方式,尤其是源文件多的时候,看起来清爽,也方便后面加条件编译。

3.5 构建与运行:从CMakeLists.txt到可执行文件

好,现在CMakeLists.txt写好了,怎么把它变成真正的程序?分两步走:

第一步:配置(Configure)

在项目根目录下,执行:

mkdir build
cd build
cmake ..

这里我习惯创建一个build目录,在里边构建。这叫“外部构建”,好处是构建产生的临时文件不会污染源码目录。你想想看,如果所有.o文件和可执行文件都堆在源码目录里,那得多乱。

cmake ..会读取上一级目录的CMakeLists.txt,然后生成构建系统文件(比如Makefile)。

第二步:编译(Build)

配置成功后,执行:

cmake --build .

或者直接用make(如果你生成的是Makefile):

make

我个人更推荐cmake --build .,因为它不依赖具体的构建工具。不管底层是Make、Ninja还是别的,都能统一调用。

第三步:运行

编译成功后,在build目录下会生成hello(Linux/macOS)或hello.exe(Windows)。直接运行:

./hello

如果一切正常,你会看到程序输出。

🔑 核心流程总结:
  1. 写CMakeLists.txt(项目剧本)
  2. mkdir build && cd build(创建构建目录)
  3. cmake ..(配置,生成构建系统)
  4. cmake --build .(编译,生成可执行文件)
  5. ./hello(运行)

3.6 一个完整的例子

咱们把上面讲的串起来,写一个完整的项目。

目录结构:

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

CMakeLists.txt:

cmake_minimum_required(VERSION 3.10)
project(HelloWorld VERSION 1.0.0 LANGUAGES CXX)
add_executable(hello main.cpp)

main.cpp:

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

注意这里我用了HelloWorld_VERSION,这是CMake自动生成的变量,对应project()里指定的版本号。变量名规则是项目名_VERSION。

构建与运行:

cd my_project
mkdir build
cd build
cmake ..
cmake --build .
./hello

输出应该是:

Hello, CMake!
项目版本: 1.0.0

3.7 避坑指南

最后,分享几个我踩过的坑:

  • 文件名写错:CMakeLists.txt少个s、大小写不对,CMake直接报错。
  • 忘记创建build目录:直接在源码目录里cmake .也行,但会生成一堆临时文件。我建议养成外部构建的习惯。
  • cmake_minimum_required()写在project()后面:有些旧版本CMake允许,但新版本会警告。标准写法是cmake_minimum_required()在第一行,project()在第二行。
  • 源文件路径不对:如果源文件在子目录,记得写相对路径,比如add_executable(hello src/main.cpp)。

好了,第一个CMake项目就讲到这里。你现在可以自己动手试试,写一个最简单的CMakeLists.txt,编译运行一下。下一章咱们会深入聊聊变量和条件控制,到时候就能写出更灵活的构建脚本了。