2. CMake基础入门:CMakeLists.txt语法、项目配置、目标定义、库链接
各位同学,咱们今天聊聊CMake。说实话,我刚入行那会儿,对CMake也是又爱又恨。爱的是它跨平台,恨的是它那套语法——嗯,确实有点反直觉。但用久了你会发现,它其实挺有逻辑的。
CMake是什么?说白了,它就是个「项目生成器」。你写一份CMakeLists.txt,它就能帮你生成Visual Studio的.sln、Xcode的.xcodeproj,或者Linux下的Makefile。我当年在Windows上写Qt程序,拿到Linux上一编译就报错,后来用了CMake,世界清净了。
核心思想:CMake不直接编译代码,它负责「描述项目结构」,然后交给本地构建工具去干活。
2.1 CMakeLists.txt 基本语法
每个CMake项目,根目录下必须有一个CMakeLists.txt。这是CMake的「剧本」。我习惯把第一行写成这样:
cmake_minimum_required(VERSION 3.16)
为什么要指定版本?因为不同版本的CMake,有些函数行为不一样。我曾经在3.10上写了个target_sources,结果同事用3.5编译直接报错——嗯,从那以后我每次都写版本号。
接下来是项目名:
project(MyQtApp VERSION 1.0.0 LANGUAGES CXX)
这里LANGUAGES CXX告诉CMake:我们只用C++。如果你混用C和C++,可以写成C CXX。我个人习惯明确指定,避免CMake去猜。
小技巧:项目名不要用中文,也不要用空格。我见过有人写project(我的应用),结果路径解析直接崩了。
2.2 项目配置:变量与缓存
CMake里变量用set()来定义。比如:
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON) # Qt的MOC自动处理
这里CMAKE_AUTOMOC是Qt项目的标配。你想想看,如果不开启这个,每个带Q_OBJECT的头文件都得手动跑moc,那得多痛苦。
还有一种变量叫「缓存变量」,用set(... CACHE ...)定义。比如:
set(BUILD_SHARED_LIBS ON CACHE BOOL "Build shared libraries")
缓存变量会出现在cmake-gui里,用户可以在配置时修改。我记得有一次,客户非要静态链接,我就在CMakeLists.txt里加了个缓存选项,让他们自己勾选——省得我改代码。
2.3 目标定义:add_executable 与 add_library
CMake里最核心的概念是「目标」(target)。目标分两种:可执行文件和库。
可执行文件:
add_executable(MyApp main.cpp mainwindow.cpp)
静态库:
add_library(MyLib STATIC lib.cpp lib.h)
动态库:
add_library(MyLib SHARED lib.cpp lib.h)
这里有个坑:如果你不写STATIC或SHARED,CMake会根据BUILD_SHARED_LIBS变量的值来决定。我建议你显式指定,否则别人看你的代码会一头雾水。
注意:Qt的插件库必须用MODULE类型,不能用SHARED。我当年在这个问题上卡了两天,最后发现是库类型写错了。
2.4 库链接:target_link_libraries
链接库是CMake里最常用的操作。语法很简单:
target_link_libraries(MyApp PRIVATE Qt5::Widgets Qt5::Core)
这里的关键字PRIVATE、PUBLIC、INTERFACE,很多人搞不清楚。我打个比方:
- PRIVATE:自己用,不传给下游。比如你内部用了log4cpp,但不想让调用你的人知道。
- PUBLIC:自己用,也传给下游。比如Qt的Widgets,你的用户也要用。
- INTERFACE:自己不用,只传给下游。比如纯头文件库。
我曾经接手过一个项目,所有链接都写PUBLIC,结果依赖传递得乱七八糟。改了一周才理清楚。所以我的建议是:能用PRIVATE就用PRIVATE,别偷懒。
2.5 查找包:find_package
Qt项目里,find_package是标配。比如:
find_package(Qt5 REQUIRED COMPONENTS Widgets Core Gui)
REQUIRED表示找不到就报错。如果你不想让编译中断,可以去掉REQUIRED,然后自己判断Qt5_FOUND变量。
这里有个细节:CMake找Qt的方式是去系统路径下找Qt5Config.cmake或qt5-config.cmake。如果你装了多个版本的Qt,可以用CMAKE_PREFIX_PATH指定路径:
set(CMAKE_PREFIX_PATH "C:/Qt/5.15.2/msvc2019_64")
嗯,Windows上路径里的反斜杠要转义,或者直接用正斜杠。我习惯用正斜杠,省心。
2.6 完整示例:一个简单的Qt项目
咱们把上面的知识点串起来,写一个完整的CMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
project(SimpleQtApp VERSION 1.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
find_package(Qt5 REQUIRED COMPONENTS Widgets Core)
add_executable(SimpleQtApp
main.cpp
mainwindow.cpp
mainwindow.h
resources.qrc
)
target_link_libraries(SimpleQtApp
PRIVATE
Qt5::Widgets
Qt5::Core
)
这个文件虽然短,但涵盖了CMake的80%常用操作。你想想看,从版本声明到项目名,从C++标准到Qt的自动处理,再到查找包和链接库——一条龙全有了。
2.7 知识体系结构图
下面这张图,是我自己总结的CMake核心知识脉络。你可以把它当成一个「思维导图」来用:
2.8 避坑指南
最后,分享几个我踩过的坑:
- 路径中的空格:我曾经把项目放在
C:\My Projects\下,CMake解析路径时直接炸了。解决方案:要么路径别带空格,要么用file(TO_CMAKE_PATH)转换。 - Qt版本冲突:系统里装了Qt5和Qt6,
find_package找到了错误的版本。我后来用set(CMAKE_PREFIX_PATH)强制指定路径,才搞定。 - MOC文件未生成:忘了写
set(CMAKE_AUTOMOC ON),结果编译报错说找不到moc_mainwindow.cpp。嗯,这个坑我至少帮三个同事填过。
好了,CMake的基础就讲到这里。你把这些练熟了,后面讲Qt界面参数调优的时候,咱们就能直接上手改代码,不用再被构建系统绊住脚。
可可精品资料库