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
如果一切正常,你会看到程序输出。
- 写
CMakeLists.txt(项目剧本) mkdir build && cd build(创建构建目录)cmake ..(配置,生成构建系统)cmake --build .(编译,生成可执行文件)./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,编译运行一下。下一章咱们会深入聊聊变量和条件控制,到时候就能写出更灵活的构建脚本了。