文章目录
- 前言
- 痛点
- 转机
- 雏形
- 实践
- 后语
前言
曾听闻:C++/Qt 没有包管理器,开发起来太不方便。这是一个有过 node.js 开发经验的人对 Qt 的吐槽。
确实,像 python、golang、node.js 这些编程语言都有包管理器,给用户带来了极佳的开发体验。第三方组件触手可得,妈妈再也不用担心我该怎么编译了。
我曾经思考过这个问题,觉得给 Qt 增加包管理器不难,难的是谁来牵头做。比较适合做这个事的应该是 Qt 官方,官方制定标准,Qt 开发者的软件兼容这个标准即可。
PS:我没有思考该怎么做,不过,读者看完本文可能会有点启发。
痛点
作者在维护 qtcanpool 的时候,遇到了库的管理问题,还没有上升到包管理层面(背后的逻辑是相似的)。
举个例子:qtcanpool 的库管理是在 src/libs 目录下添加库目录,然后库目录中直接存放库管理文件和源码文件(源码文件不能进一步放在子 src 目录,不然会影响头文件包含),比如下图的 qcanpool。应用程序在使用库的头文件的时候,头文件需要包含库目录名作为前缀,比如:
#include "qcanpool/extensionbutton.h"
问题描述:如果作者想引入第三方库,该如何做呢?
问题分析:通常第三方库有自己的目录结构,比如 src、example、tests、doc 等子目录,显然这不符合 qtcanpool 当前的库管理方式,需要做额外的操作。
解决方案一:在 qtcanpool/src/libs 目录下创建第三方库的名称目录,然后将第三方库 src 目录下的内容拷贝过来,再增加相关的库管理文件(pro、pri 文件)。
思考一下:这个方案有哪些问题?
- 拷贝?
- 太麻烦了
- 后续怎么升级呢?
- 拷贝新的文件覆盖老的文件,修改库管理文件。——还是麻烦
解决方案二:利用 git 的 submodule 功能,在 qtcanpool/src/libs 下创建第三方库的名称目录(如:qads),然后将第三方库以 git 子模块的形式添加到该目录下(如:Qt-Advanced-Docking-System),最后添加相关库管理文件。参考连接:https://gitee.com/icanpool/qtcanpool/tree/2.2.1/src/libs/qads
思考一下:
- 拷贝?
- 拷贝是不可能拷贝的。相比方案一,库文件不需要再拷贝了(开心)
- 后续怎么升级呢?
- 更新 git 子模块即可,本质上只需要更新到第三方库的某个提交(开心)
- 那么问题来了,第三方库的目录结构不符合 qtcanpool 的管理方式,该怎么使用呢?
- 这就是个问题了,为此需要单独增加一个 qads-inc.pri 管理文件,配置第三方库的头文件路径,接着应用程序的工程中需要 include 这个 pri 文件,然后应用程序中直接包含第三方库的相关头文件(不需要添加库前缀 qads)。—— 这产生了头文件包含风格不统一问题
痛苦是一种常态,作者接受了方案二。
转机
作者先表态:qmake 用习惯了提好用的,cmake 一言难尽。
Qt6 弃用 qmake,全面转向 cmake,并且在 Qt7 中大概率不再支持 qmake。
网友关于 cmake 的感悟:cmake 基本用过的人都不会觉得它有多好,其它 make 系统,比 cmake 使用体验好的没有 cmake 功能多,功能多的没有 cmake 生态完善,总之全面超越 cmake 的还没有出现。
大势所趋,趋之若鹜,毋庸置疑,qtcanpool 也需要支持 cmake。
qtcanpool 的工程管理的思想是来自 qtcreator,那么 cmake 自然也要保持延续。
qtcanpool 的 cmake 框架在 qtcanpool/cmake 目录:
- 定义一个库,使用方法:add_qtc_library(包含该方法调用的 CMakeLists.txt 文件的上级目录会自动作为头文件搜索路径)
- 定义一个应用,使用方法:add_qtc_executable
用户可以参考 qcanpool 库和 fancydemo 应用,此处不再赘述。
再赘述一下:qtc 是 qtcreator 的缩写,也是 qtcanpool 的缩写,挺完美的!
雏形
啊,cmake 真香!
cmake 管理确实很方便,就是学习曲线有点陡峭!
qtcanpool 集成了 cmake 之后,定义一个库非常方便(通过 add_qtc_library)。而且 cmake 通过 add_subdirectory 也非常方便引入库。
qtcanpool 中应用程序使用库头文件是遵循包含库目录名称的,如:#include "qcanpool/fancywindow.h“
思考:这个目录名称有点像模块名,可类比于 python 的包名(比如:import qcanpool,from qcanpool import FancyWindow
)。python 开发的第三方库通常源码会放在模块名目录下,如:src/qcanpool/*.py,qcanpool/*.py
。
基于上述思考,如果 Qt 开发的第三方库源码不放在 src 目录下,而是放在库名称的目录下,是不是就可以保证包含头文件的时候带有库名前缀呢?
实践
样例库:qxmaterial
仓库地址:https://gitee.com/icanpool/qtcanpool-qxmaterial
目录结构:
- examples:存放使用库的示例应用
- qxmaterial: 是库名称目录,存放源码文件
- CMakeLists.txt:库的 cmake 配置文件
cmake_minimum_required(VERSION 3.10)if (NOT QTCANPOOL_DIR) include(config.cmake) endif()add_subdirectory(qxmaterial)
- confg.cmake:该仓库作为独立仓库(非 qtcanpool 子仓库)时的工程配置文件,该文件需要依赖 qtcanpool/cmake 框架。
如何使用呢?
- qtcanpool-qxmaterial 作为一个独立仓库时。可以独立将库向前演进,通过 examples 中的应用示例可以独立验证该库。有点小瑕疵,就是该仓库需要依赖 qtcanpool 中的 cmake 框架(依赖的目的是为了保证框架的唯一性),默认二者需要保证目录层级是同级关系(可以根据实际情况修改 config.cmake 中的 QTCANPOOL_DIR 路径),如:
qtcanpool qtcanpool-qxmaterial
- qtcanpool-qxmaterial 作为 qtcanpool 的子库时。利用 git 的 submodule 功能,在 qtcanpool/src/libs 下创建第三方库的名称目录(如:qxmaterial),然后将第三方库以 git 子模块的形式添加到该目录下(如:qtcanpool-qxmaterial,子模块路径可以更改,不一定要保持和仓库名称一致),并添加一个库管理文件 CMakeLists.txt。
qtcanpoolsrclibsqxmaterialqtcanpool-qxmaterialCMakeListst.txt
CMakeLists.txt 内容如下:
add_subdirectory(qtcanpool-qxmaterial)
背后的逻辑是:
-
add_subdirectory(qtcanpool-qxmaterial) 会包含 qtcanpool-qxmaterial 目录内的 CMakeLists.txt 文件,内容如下:
-
如果作为 qtcanpool 的子库(即子目录),qtcanpool 会在顶层 CMakeLists.txt 中定义 QTCANPOOL_DIR,那么就不会包含 config.cmake,add_subdirectory(qtcanpool-qxmaterial) 相当于进一步调用 add_subdirectory(qxmaterial)
-
add_subdirectory(qxmaterial) 会包含 qtcanpool-qxmaterial/qxmaterial 目录内的 CMakeListst.txt 文件,内容如下:
-
qxmaterial 目录内的 CMakeLists.txt 文件中调用 add_qtc_library 添加库,根据规则,那么该 CMakeLists.txt 文件的上一级目录会作为头文件的搜索目录,即 qtcanpool/src/libs/qxmaterial/qtcanpool-qxmaterial 会作为头文件搜索路径,如下所示:
qtcanpoolsrclibsqxmaterialqtcanpool-qxmaterial -----> 作为头文件搜索路径qxmaterial -----> 应用程序如果包含 "qxmaterial/xxx.h" 头文件,将自动会匹配该目录内的对应头文件CMakeLists.txtCMakeLists.txtCMakeListst.txt
后语
本文所述的包管理只适合 qtcanpool,读者们若有启发欢迎交流!