3、CMake入门:CMakeLists.txt基本语法、project()与add_executable()、cmake_minimum_required()版本控制
说实话,很多刚接触跨平台编译的朋友,第一反应就是去折腾Makefile。但我个人的经验是——别跟自己过不去。CMake才是现代C++项目的标配,尤其是你要在Windows、Linux、macOS之间来回切换的时候。
这一章,咱们就把CMake最核心的三个东西讲透:CMakeLists.txt怎么写、project()和add_executable()怎么用、版本控制怎么搞。嗯,都是基本功,但坑也不少。
3.1 CMakeLists.txt:项目的“身份证”
每个用CMake管理的项目,根目录下必须有一个CMakeLists.txt文件。它就像项目的身份证,告诉CMake:你是谁、用啥编译器、要编译哪些源文件。
我见过有人把CMakeLists.txt写成几百行的“天书”,其实完全没必要。一个最小可用的CMakeLists.txt,可能就三五行:
cmake_minimum_required(VERSION 3.10)
project(HelloWorld)
add_executable(hello main.cpp)
你看,就这么简单。但简单归简单,每一行都有讲究。
3.2 cmake_minimum_required():版本控制不能马虎
这一行写在最前面,指定CMake的最低版本。为什么要写?因为不同版本的CMake,语法和功能有差异。你写了个新特性,结果同事的CMake版本太老,编译直接崩——这种事我遇到过不止一次。
语法很简单:
cmake_minimum_required(VERSION 3.10)
这里的3.10就是最低版本号。如果你用了3.12才有的功能,那这里就得写3.12。
target_precompile_headers(),这个功能是3.16才引入的。结果客户那边的CI环境装的是3.14,编译直接报错。后来我学乖了——要么升级CI环境,要么在CMakeLists.txt里加版本判断。
版本判断怎么写?其实也很简单:
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.16)
target_precompile_headers(my_target PRIVATE <vector>)
endif()
这样,低版本CMake会跳过这个功能,至少不会报错。
3.3 project():给项目起个名,顺便设语言
project()是CMake项目的“户口本”。它定义项目名称,还能指定编程语言。最常见的写法:
project(MyApp LANGUAGES CXX)
这里MyApp是项目名,CXX表示用C++。如果你只写project(MyApp),CMake默认会检测C和C++。我个人习惯显式指定语言,避免不必要的检测。
另外,project()还会自动定义几个变量:
PROJECT_NAME:项目名,比如MyAppPROJECT_SOURCE_DIR:源码根目录PROJECT_BINARY_DIR:构建目录
这些变量后面会经常用到,比如指定头文件路径:
include_directories(${PROJECT_SOURCE_DIR}/include)
project(),那PROJECT_SOURCE_DIR会指向最近的那个。想全局统一?用CMAKE_SOURCE_DIR。
3.4 add_executable():把源文件变成可执行文件
这一行告诉CMake:我要生成一个可执行文件,源文件是哪些。语法:
add_executable(hello main.cpp utils.cpp)
这里hello是目标名,后面跟源文件列表。目标名可以随便起,但最好和项目名或功能相关。
源文件多了怎么办?一行写不下?可以用变量:
set(SOURCES
main.cpp
utils.cpp
network.cpp
database.cpp
)
add_executable(my_server ${SOURCES})
这样清晰多了。我一般会把源文件按模块分组,比如SRC_CORE、SRC_NET、SRC_UI,然后统一加到add_executable()里。
file(GLOB)可以自动收集源文件,比如file(GLOB SRC "src/*.cpp")。但我不推荐——因为CMake不会自动检测新增文件,你得重新运行cmake。老老实实手动列出来,最靠谱。
3.5 知识体系:一张图看懂CMake入门
下面这张SVG图,把本章的核心逻辑串起来了。你一看就明白:
3.6 实战:一个完整的CMakeLists.txt
说了这么多,咱们来写一个完整的例子。假设项目结构如下:
my_project/
├── CMakeLists.txt
├── src/
│ ├── main.cpp
│ └── utils.cpp
└── include/
└── utils.h
CMakeLists.txt可以这样写:
cmake_minimum_required(VERSION 3.10)
project(MyTool LANGUAGES CXX)
# 指定头文件路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 收集源文件
set(SOURCES
src/main.cpp
src/utils.cpp
)
# 生成可执行文件
add_executable(mytool ${SOURCES})
然后编译:
mkdir build && cd build
cmake ..
make
搞定。你想想看,是不是比手写Makefile省心多了?
cmake_minimum_required()写在最前面,版本号别乱写project()定义项目名和语言,自动生成变量add_executable()指定目标名和源文件,用变量管理源文件更清晰
嗯,这一章就到这里。CMake入门其实不难,关键是养成好习惯。下一章咱们聊聊更高级的——库的编译和链接。