qmake / jom / 影子构建:把编译管起来

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/ 干干净净。好处三条:

  1. 源码目录不污染,git 不用天天 ignore 一堆东西;
  2. 可以同时存在 build-debug 和 build-release,互不干扰;
  3. 想重来?直接 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。

相关推荐
郝学胜-神的一滴1 小时前
Numpy数据处理详解 01:NumPy 从环境搭建到入门上手
开发语言·人工智能·python·程序人生·数据分析·numpy
Ai-_Man1 小时前
从聊天框到正式文档:AI导出鸭瞄准AI大模型应用的“最后一公里”
开发语言·前端·javascript·人工智能·小程序
EatFan1 小时前
2026 Java 后端面试风向变了:八股文只是门槛,场景化追问才是淘汰线
java·开发语言·jvm·面试·后端面试·八股文·full gc
江屿风1 小时前
【Linux系统】【从菜鸟驿站到操作系统:一节课打通Linux重定向与缓冲区真相】流食般投喂
linux·运维·服务器·开发语言·笔记
Zootopia6261 小时前
论文复现|五机编队任务链
c语言·c++·考研·无人机·创业创新·学习方法·高考
外收内放1 小时前
Python基础语法练习题(49-50)
开发语言·python
翼辉cto1 小时前
Kotlin Multiplatform 三方库 SQLDelight 的 OpenHarmony 鸿蒙化适配实战
开发语言·kotlin·harmonyos
Cc.Y2 小时前
Java零基础入门:集合框架:ArrayList 与 LinkedList —— 告别数组的“死板“,拥抱动态容器的“灵活
java·开发语言·python
qeen872 小时前
【QT】 信号与槽初始
开发语言·笔记·qt·学习