创建 Qt 工程时,我们通常使用 Qt 提供的 Online installer 安装 Qt Creator 和 Qt 库来创建、编译、发布 Qt 项目,这对开发环境和 CI Agent 环境有较强的要求,一旦环境安装不对或者安装时缺少了一些组件,可能导致无法编译出产物。最近一段时间,Qt 也拥抱 Conan,使我们可以通过 Conan 管理 Qt 库,这样我们就可以真正实现一套 CMake 脚本来管理和发布 Qt 的应用了。以下我们将演示如何通过 CMake + Conan 来组织 Qt 工程和实现程序的发布流程。
创建 CMakeLists.txt 和 conanfile.py
我们创建一个简单的工程来显示一个 Qt Widget UI 界面: main_form.h
1 |
|
main_form.cc
1 |
|
main.cc
1 |
|
创建 conanfile.py 来引入 Qt
1 | from conans import ConanFile, tools |
在 default_options
中我们设置 Qt 编译为动态库,并且启用 qttools,因为其中包括 macdeploy 和 windeploy 等工具。其中要注意的是 generators
配置:
- 添加
qt
支持主要是为了生成 qt.conf 配置文件,其内容决定了 Qt 依赖库的位置(如 plugins 目录),这在我们调试时非常有用,不需要拷贝这些依赖库到执行程序目录 - 添加
cmake_find_package_multi
和cmake_paths
主要是为了我们 CMakeLists.txt 中使用 find_package 能查找到指定 Qt 库文件以链接它们 - 添加
cmake
是为了能通过 CMakeLists.txt 来组织工程,引入头文件路径、库文件路径等信息
另外,由于 Qt 6.2.4 在 macOS 下编译时因为 CMake 和 Qt 依赖的 openssl 版本不一致,我们需要再主工程中覆盖 openssl 1.1.1n 版本。harfbuzz 因为旧版本编译不通过问题,我们覆盖使用 4.2.0 版本。在 Windows 下,我们还可以使用不同版本的 Qt。 接下来添加 CMakeLists.txt
1 | cmake_minimum_required(VERSION 3.18) |
执行 CMake 初始化:
1 | cmake -Bbuild -GXcode -DCMAKE_BUILD_TYPE=Debug |
第一次执行如果依赖库没有下载到预编译版本,则会执行本地编译,我这里因为以前执行过,所以直接使用本地缓存的预编译包,执行结果如下:
1 | -- The CXX compiler identification is AppleClang 13.1.6.13160021 |
可以看到,我们的 find_package 等命令成功命中了所有 Qt 依赖库,接下来执行编译:
1 | cmake --build build --config Debug |
如无故障,可执行程序就生成到 build/bin 目录下了。直接执行即可运行程序:
1 | open ./build/bin/QtWithConan.app |
一个空白的 Qt 应用就成功运行起来了: 我们查看一下 QtWithConan.app 的目录结构:
1 | ➜ bin git:(master) ✗ tree |
可以看到,这里面并没有 Qt 的依赖库文件,而是只有我们的 app 可执行文件,为什么他可以运行呢?其实执行 conan 包初始化时这些依赖库的路径信息会当做 @rpath 信息自动添加到我们的可执行程序中。使用 otool -l 命令可以查看详情:
1 | ➜ bin git:(master) ✗ otool -l QtWithConan.app/Contents/MacOS/QtWithConan |
可以看到,所有依赖库的路径信息都添加到 LC_RPATH 中了,这就使我们在本地调试应用的时候不需要将 Qt 的依赖库部署到可执行文件目录下了。但 Windows 有点不一样,Windows 不像 macOS 一样可以给执行程序添加 @rpath 信息,这就要求我们再调试的时候也需要部署 Qt 依赖库到可执行程序目录下。并且 macOS 的应用我们如果需要发布给其他人使用,也一样需要将依赖库拷贝到 app bundle 中。接下来我们将介绍如何通过 CMake 执行部署流程。
Deploy Qt 应用
Qt 在不同平台下提供了部署工具,如 macOS 下使用 macdeployqt,Windows 下使用 windeployqt 工具。在我们依赖 Qt 的时候,指定了 qttools 的工具包,这个工具包就包含了这些部署工具。所以我们在 CMake 中直接 find_package 即可找到他们。
1 | if (APPLE) |
将以上 CMake 脚本添加到 CMakeLists.txt 最后。然后重新执行 Release 的编译:
1 | cmake -Bbuild -GXcode -DCMAKE_BUILD_TYPE=Release |
执行后再次查看 QtWithConan.app 目录结构:
1 | ➜ bin git:(master) tree |
在 deploy 后,该应用就已经可以拷贝给其他人使用了。对于 macOS 下最低支持版本、签名等问题 CMake 均有处理方案,这里不过多做赘述,如果有需要的同学可以参考项目:https://github.com/nmgwddj/qt6-conan-cmake-sample
总结
Qt 的应用部署以前我们都是高度依赖 qmake 或其他第三方脚本,并且不同平台下处理会有各种各样的问题,通过 Conan + CMake 的方案,我们完全可以实现在不同平台下两条命令就可以编译出产物的需求。如:
1 | cmake -Bbuild -DCMAKE_BUILD_TYPE=Release |
你可以通过 CI 的打包工具将 .app 文件和 .exe 文件夹打包成压缩包或者制作成 dmg 进行分发。如果有更换 Qt 版本需求,我们仅需要再 conanfile.py 中修改一下 Qt 版本就可以自动执行所有编译流程了,永远告别 qmake 和繁琐的编译脚本。