Day 01 解决了"编译器能不能被找到"。这一篇讲"找到之后,怎么把编译这件事管起来",而不是每次都在工程目录里糊一团中间文件。
我刚接手那个老项目时,所有人都是直接在 Qt Creator 里点"构建"。问题来了:源码目录里塞满了 *.obj、moc_*.cpp、ui_*.h,git 一拉全红;想同时留一份 Debug 和一份 Release 根本不可能,因为都往同一个目录写;换台机器或者 CI 上一跑,构建脚本没有,全靠人手点。这就是没把编译"管起来"的代价。
先搞清楚 qmake 在干嘛
很多人以为 qmake 是编译器,不是。它干的事是:把你的 .pro 翻译成 Makefile(或者 .vcxproj),并且自动把 moc、uic、rcc 这几个 Qt 特有的代码生成步骤串进去。
你写的 Q_OBJECT 类,编译器不认识。得先靠 moc 生成 moc_xxx.cpp,再一起编。手写这个流程能累死人,qmake(以及后来的 CMake)就是干这个的。所以别纠结"能不能不用 qmake",在 Qt 5.12 这套里,它就是你和构建系统之间的翻译层。
最小 .pro 长这样:
pro
QT += core gui widgets
TARGET = myapp
SOURCES += main.cpp mainwindow.cpp
HEADERS += mainwindow.h
FORMS += mainwindow.ui
qmake 读完它会生成 Makefile,jom 拿着 Makefile 去编译。
为什么是 jom 不是 nmake
Day 01 说过,Qt 官方推荐 jom。它语法兼容 nmake,但支持 -j 多核并行。nmake 没有并行能力,大项目编起来慢得让人想睡一觉。
命令行构建的标准两步:
bat
qmake -r
jom -j 8
-r 是递归处理子目录(你的工程如果用了 SUBDIRS 模板,不加 -r 子项目不会被处理)。jom -j 8 开 8 个并行任务。
还有个事:Qt Creator 那个"构建"按钮背后其实就是 qmake && jom(或 nmake)。点按钮很方便,但脚本化才是工程化的开始------你总不希望 CI 也靠人手动点。
影子构建(shadow build)
这是我最想安利的一点。默认情况下 qmake 在 .pro 所在目录生成 Makefile 和所有中间文件,源码和垃圾混在一起。影子构建的意思是:在一个单独的目录里生成构建产物,源码目录保持干净。
命令行这么做:
bat
mkdir build && cd build
qmake ../src/myapp.pro
jom -j 8
生成的全在 build/ 里,src/ 干干净净。好处三条:
- 源码目录不污染,git 不用天天 ignore 一堆东西;
- 可以同时存在
build-debug和build-release,互不干扰; - 想重来?直接
rm -rf build完事,不影响源码。
Qt Creator 里那个 "Shadow build" 勾选框就是干这个的,默认其实是开着的。但自己写命令行脚本时,得手动建目录,不然又回到原地构建了。
一个容易踩的坑:改了 .pro 不重新 qmake
命令行构建有个和 Creator 不同的地方:Creator 在构建前会自动检测 .pro 变化并重跑 qmake;你自己敲命令不会。如果你加了源文件、改了 LIBS,只敲 jom 是没用的,Makefile 还是旧的。
所以脚本里永远把 qmake -r 放在 jom 前面,或者至少改了 .pro 之后手动重跑一次 qmake。我见过有人困惑"我明明加了文件怎么链接报 unresolved",查了半小时,最后发现是没重跑 qmake。
想要 Visual Studio 工程?
有些同事就是习惯在 VS 里干活。qmake 能直接生成 .vcxproj:
bat
qmake -tp vc ../src/myapp.pro
生成的 .vcxproj 双击就能在 VS2017 里打开,moc/uic 步骤也都接好了。注意 -tp vc 生成的是"用 qmake 管理"的项目,改了 .pro 还是得重跑这条命令来更新。
小结
把编译管起来的最小闭环就是:
bat
mkdir build && cd build
qmake -r ../src/myapp.pro
jom -j 8
源码干净、可并行、可脚本化、可重复。下一篇讲 .pro / .pri 本身------这套语法坑比你想的多,尤其是 = 和 +=、还有条件作用域。
本篇属于「Qt 工程化实战」专栏 DAY 02。