移植 OpenHarmony 的 Qt 源码文件对比合并工具。
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 16 天前 | ||
| 16 天前 | ||
| 16 天前 | ||
| 16 天前 | ||
| 16 天前 | ||
| 16 天前 | ||
| 16 天前 | ||
| 16 天前 | ||
| 12 天前 | ||
| 12 天前 | ||
| 16 天前 | ||
| 16 天前 | ||
| 16 天前 | ||
| 16 天前 |
KDiff3 的 OpenHarmony 适配记录
这份文档记录我们把 KDiff3(一个文本/源码差异比较与合并工具,能做两文件/三文件逐行对比、三路合并、目录递归比较)搬到 OpenHarmony / HarmonyOS PC 上的过程:鸿蒙工程怎么组织、环境怎么搭、HAP 怎么编出来、业务库怎么交叉编译,以及最后哪些功能能用、哪些受平台限制或 KDE 依赖剥离影响没做。原项目是一个标准的 KDE 桌面 Qt Widgets 应用,上游主页:https://invent.kde.org/sdk/kdiff3,版本 0.9.98。
这次走的是 Qt-for-OpenHarmony 这条路:用一个 ArkTS 壳工程起 UIAbility,铺 XComponent 承载 Qt 窗口,通过 libqohos.so(Qt-OHOS QPA 平台插件)加载外部交叉编译好的 libkdiff3.so。KDiff3 的 C++/Qt 源码编译成独立共享库,由 QPA 插件 dlopen 后调用其 main 入口。
需要说明一点:KDiff3 上游是个 KDE 应用,强依赖 KF5 全家桶(KIO / KCrash / KXmlGui / KConfig / KI18n 等)。鸿蒙第一版的策略是「先把 KDE 桌面集成全剥掉,保核心 diff/merge」——所以远程文件、崩溃上报、KDE 菜单/工具栏体系、完整国际化等全被主动砍掉了。这不是 bug,是第一版取舍。下面会逐项讲清楚。
一、工程结构 / 鸿蒙工程在哪
鸿蒙工程就是仓库根目录,主模块为 entry/,另带一个 qEmbeddedUiExtensionHost/ 特性模块。用 DevEco Studio 打开仓库根目录即可。
ohos_KDiff3-main/
├── AppScope/ # 应用级配置(包名、图标、版本)
│ ├── app.json5 # bundleName = com.example.kdiff3ohos
│ └── resources/base/
│ ├── element/string.json # 应用名 "KDiff3"
│ └── media/ # app_icon / startIcon
│
├── entry/ # 主模块(HAP 业务壳)
│ ├── libs/arm64-v8a/ # 预编译 native 库 ⭐
│ │ ├── libkdiff3.so # KDiff3 业务库(交叉编译产物)
│ │ ├── libqohos.so # Qt-OHOS QPA 桥接库
│ │ ├── libQt5*.so # Qt 5.12.12 运行时
│ │ ├── libQt5PrintSupport.so # Qt 打印支持(运行时缺这个会闪退,见下文)
│ │ ├── platforms/libqohos.so # QPA 平台插件
│ │ ├── styles/libqohosstyle.so # QStyle 插件
│ │ ├── imageformats/*.so # 图片格式插件
│ │ └── libc++_shared.so # OHOS NDK C++ 运行时
│ ├── src/main/
│ │ ├── module.json5 # Ability / 权限 / 设备类型 / 文件关联
│ │ ├── cpp/ # Native 构建入口(示例 libentry.so)
│ │ │ ├── CMakeLists.txt
│ │ │ └── hello.cpp
│ │ ├── ets/
│ │ │ ├── qabilitystage/
│ │ │ │ └── QAbilityStage.ets # 应用入口(初始化 Qt、加载 libkdiff3.so)
│ │ │ ├── qability/
│ │ │ │ ├── QAbility.ets # 主 UIAbility(生命周期委托 libqohos)
│ │ │ │ ├── OhosExportModules.ts # 导出 43 个鸿蒙系统 API 给 Qt
│ │ │ │ └── QEmbeddedUiExtensionAbility.ets
│ │ │ ├── pages/
│ │ │ │ ├── MainWindowNativeNode.ets # 主窗口 XComponent
│ │ │ │ ├── SubWindowNativeNode.ets # 子窗口
│ │ │ │ ├── FloatWindowNativeNode.ets # 浮动窗口
│ │ │ │ └── UiExtensionNativeNode.ets # UI 扩展窗口
│ │ │ └── common/
│ │ │ └── QtAppConstants.ets # APP_LIBRARY_NAME = libkdiff3.so
│ │ └── resources/
│ │ └── rawfile/kdiff3/ # 自检样例文本
│ │ ├── left.txt # 左侧对比文件
│ │ └── right.txt # 右侧对比文件
│ ├── build-profile.json5 # CMake + arm64-v8a ABI
│ └── oh-package.json5
│
├── qEmbeddedUiExtensionHost/ # EmbeddedUI 扩展宿主模块
├── build-profile.json5 # 顶层构建配置
├── oh-package.json5 # 工程级 ohpm 依赖
└── README.md
关键一点:鸿蒙工程不包含 KDiff3 业务源码。业务逻辑全部在外部 libkdiff3.so 中,由 libqohos.so 通过 dlopen / dlsym("main") 加载。entry/src/main/cpp/CMakeLists.txt 只编译一个示例性质的 libentry.so,不涉及 KDiff3 本体。
二、环境要求
| 项目 | 要求 | 备注 |
|---|---|---|
| DevEco Studio | 支持 HarmonyOS PC(Stage 模型)的版本 | 命令行构建用 hvigorw / ohpm |
| HarmonyOS SDK | compatibleSdkVersion / targetSdkVersion = 6.1.0(23) |
见 build-profile.json5 |
| Qt for OpenHarmony | 5.12.12(arm64-v8a) |
ArkTS 侧通过 libqohos.so 接入 |
| 目标设备 | 2in1 / tablet(arm64-v8a) |
见 module.json5 的 deviceTypes |
| 启动业务库 | libkdiff3.so |
见 QtAppConstants.ets |
| 签名 | 用自己的调试 / 发布证书 | 仓库里 signingConfigs 是本机配置,换机器要重配 |
应用信息(取自 AppScope/app.json5):
bundleName:com.example.kdiff3ohos- 显示名:KDiff3
- 版本:
versionName 1.0.0/versionCode 1000000
三、编译与运行
前置准备:放入 Qt 依赖库
entry/libs/arm64-v8a/ 目录需要放入以下交叉编译产物:
entry/libs/arm64-v8a/
├── libkdiff3.so # KDiff3 业务库
├── libqohos.so # Qt-OHOS QPA 桥接库
├── libQt5Core.so / libQt5Gui.so / libQt5Widgets.so / ...
├── libQt5PrintSupport.so # Qt 打印支持(缺这个会闪退,见下文)
├── libc++_shared.so
├── platforms/libqohos.so
├── imageformats/
└── styles/
构建 HAP
# 1. 安装 ohpm 依赖
ohpm install
# 2. 用 DevEco Studio 打开仓库根目录
# File → Project Structure → Signing Configs 配好签名
# 3. 构建
# Build → Build Hap(s)/APP(s) → Build Hap(s)
# 产物路径
# entry/build/default/outputs/default/entry-default-signed.hap
命令行构建:
hvigorw --mode module -p module=entry@default -p product=default assembleHap
推送到设备
hdc install entry/build/default/outputs/default/entry-default-signed.hap
hdc shell aa start -a QAbility -b com.example.kdiff3ohos
业务库交叉编译(参考)
cd ~/ohos-qt-port/kdiff3/kdiff3-0.9.98/src-QT4
mkdir -p build-ohos && cd build-ohos
cmake .. \
-DCMAKE_TOOLCHAIN_FILE=$OHOS_SDK_ROOT/native/build/cmake/ohos.toolchain.cmake \
-DOHOS_ARCH=arm64-v8a \
-DOHOS_PLATFORM=OHOS \
-DCMAKE_PREFIX_PATH=/opt/qt-ohos/qt-5.12.12-ohos/qt-5.12.12-ohos
ninja
# 裁剪 + 4KB 对齐修复
$OHOS_SDK_ROOT/native/llvm/bin/llvm-strip --strip-unneeded libkdiff3.so
python3 fix_elf_align_v2.py libkdiff3.so
# 部署到工程
scp libkdiff3.so mac:~/Downloads/Kdiff3Ohos/entry/libs/arm64-v8a/
四、这个项目是怎么适配的
适配思路一句话:ArkTS 壳层负责生命周期和窗口承载,KDiff3 业务逻辑不动,由 Qt-OHOS 桥接层把两者连起来;KDE 桌面集成全部剥掉,保核心 diff/merge。
1. 启动链路
HarmonyOS 拉起 com.example.kdiff3ohos(QAbility)
→ QAbilityStage.onCreate() 初始化 Qt-OHOS QPA
→ qpa.setupQtApplication({ appName: 'libkdiff3.so' })
→ QAbility.onWindowStageCreate() 加载 MainWindowNativeNode
→ MainWindowNativeNode 起 XComponent(libraryname: 'qohos')
→ QPA 插件在 XComponent 上建 surface,dlopen("libkdiff3.so")
→ 调用 main(argc, argv)
→ KDiff3 Qt 主窗口渲染到 XComponent
2. ArkTS 桥接层(entry/src/main/ets/)
QAbilityStage.ets:onCreate里调qpa.handleAbilityStageOnCreate(this),初始化 Qt 应用上下文。onAcceptWant/onNewProcessRequest负责多实例 / 多进程的 key 分配。QAbility.ets:launchType设成specified,isolationProcess: true(独立进程)。生命周期回调全部委托给libqohos.so:onCreate/onDestroy/onWindowStageCreate/onNewWant/onForeground/onBackground/onContinue。MainWindowNativeNode.ets:一块XComponent,type: XComponentType.NODE,libraryname: 'qohos'——KDiff3 的 Qt 界面最终渲染到这块 surface 上。OhosExportModules.ts:把 43 个鸿蒙系统 API(文件访问、剪贴板、i18n、window、display 等)打包导出给 Qt 侧使用。
3. KDE 依赖剥离
KDiff3 上游是 KDE 应用,强依赖 KF5 全家桶。这次交叉编译时做了以下剥离:
| 上游 KDE 组件 | 功能 | 剥离方式 | 替代 |
|---|---|---|---|
| KIO | 远程文件比较(FTP/SFTP/SMB) | 宏屏蔽 / 编译排除 | 砍掉,只支持本地文件 |
| KCrash | 崩溃收集上报 | 宏屏蔽 | 不接 |
| KXmlGui | KDE 风格菜单 / 工具栏 / 快捷键体系 | 编译排除 | 简化为 Qt 原生菜单 |
| KConfig | KDE 配置系统 | 编译排除 | 替换为 QSettings |
| KI18n | 完整多语言支持 | 编译排除 | 降为 QObject::tr 或硬编码英文 |
| KDE Widgets | KDE 风格对话框 / 向导 | 编译排除 | 全换成标准 Qt Widgets |
| KRecentFilesAction | 打开最近项目 | 没迁移 | 无 |
| KStatusNotifier | 系统托盘 / 通知 | 砍掉 | 无 |
4. 文件关联(module.json5)
QAbility 注册了 file:// + general.text 的技能声明,支持 ohos.want.action.sendData,maxFileSupported: 10。从鸿蒙系统的文件管理器里用「打开方式」选 KDiff3 打开文本文件是有接入路径的。rawfile/kdiff3/ 里还带了两个自检样例文件(left.txt / right.txt),方便快速验证对比功能。
5. 权限声明(module.json5)
| 权限 | 用途 |
|---|---|
ohos.permission.SYSTEM_FLOAT_WINDOW |
浮动窗口显示 |
ohos.permission.FILE_ACCESS_PERSIST |
持久化文件访问 |
ohos.permission.PRINT |
打印功能 |
ohos.permission.PREPARE_APP_TERMINATE |
应用终止前保存状态 |
ohos.permission.STORE_PERSISTENT_DATA |
持久化存储 |
ohos.permission.READ_PASTEBOARD |
读取剪贴板 |
ohos.permission.GET_FILE_ICON |
获取文件图标 |
6. 已知运行时 bug:缺 libQt5PrintSupport.so 闪退
装到真机上第一次启动闪退,hilog 抓到的是 dlopen: library "libQt5PrintSupport.so" not found。原因是 libkdiff3.so 的 NEEDED 依赖闭包里包含了 libQt5PrintSupport.so,但 entry/libs/arm64-v8a/ 下没放这个 .so。把缺的 .so 从 Qt-OHOS 编译产物拷过来就解决了。编译和运行时都是能跑通的。
五、功能支持情况(T0 / T1 / T2)
先把口径说清楚:
- 分档:T0 是「装、起、最低限度能用」,T1 是「KDiff3 的日常核心」,T2 是「远程文件、打印、KDE 桌面集成这些增强项,多依赖 KDE 组件或桌面平台能力」。
- 状态只有两个:能用 指功能正常;未适配 指桌面端特有或平台没有对应概念的能力,按平台特点或第一版策略主动砍掉,是取舍不是没做完。
T0 — 基础能力
| 能力 | 状态 | 说明 |
|---|---|---|
| 编译打包成 HAP | 能用 | hvigorw assembleHap / DevEco 都能构建;需先放入 native 库 |
| 安装 / 启动 / 进主界面 | 能用 | ArkTS 起 UIAbility + XComponent,QPA 插件加载 libkdiff3.so,拉起 KDiff3 主窗口 |
| 主窗口渲染 | 能用 | Qt Widgets 经 XComponent 宿主渲染,主窗口界面正常显示 |
| 从系统「打开方式」接收文本文件 | 能用 | module.json5 注册了 file:// + general.text 文件关联 |
| 自检样例文件 | 能用 | rawfile/kdiff3/ 内置 left.txt / right.txt,方便快速验证对比 |
| 进程隔离 | 能用 | isolationProcess: true,业务崩溃不影响壳 |
小结:装、起、进界面、从文件管理器接收文件这条主链路是通的。运行时缺 libQt5PrintSupport.so 闪退的问题已定位并解决。
T1 — 核心比较能力
| 能力 | 状态 | 说明 |
|---|---|---|
| 本地两个文件比较 | 能用 | KDiff3 核心功能,逐行对比、高亮差异 |
| 三方合并 | 能用 | 共同祖先 + 两个派生版本的智能合并 |
| 冲突解决 | 能用 | 手动编辑冲突区域,逐块选择保留版本 |
| 本地目录比较 | 能用 | 递归扫描目录,列出差异文件 |
| 语法高亮 | 能用 | C/C++/Python/JavaScript 等多语言 |
| Qt 原生菜单 / 工具栏 | 能用 | 从 KXmlGui 简化为 Qt 原生菜单,功能等价但不是 KDE 风格 |
小结:文件差异对比、三方合并、目录比较这些 KDiff3 的日常核心功能,代码路径都齐了,libkdiff3.so 交叉编译已通过。菜单/工具栏从 KDE 体系换成 Qt 原生,功能等价。
T2 — 增强能力(KDE 桌面集成 / 平台限制)
| 能力 | 状态 | 说明 |
|---|---|---|
| 远程文件比较(FTP/SFTP/SMB) | 未适配 | 上游用 KIO 实现,鸿蒙版砍掉 KIO 依赖,只支持本地文件 |
| 崩溃收集上报 | 未适配 | 上游用 KCrash,鸿蒙版宏屏蔽,不接 |
| KDE 风格菜单 / 工具栏 / 快捷键 | 未适配 | 上游用 KXmlGui,鸿蒙版简化为 Qt 原生菜单 |
| KDE 配置系统 | 未适配 | 上游用 KConfig,鸿蒙版替换为 QSettings |
| 完整多语言支持 | 未适配 | 上游用 KI18n,鸿蒙版降为 QObject::tr 或硬编码英文 |
| KDE 风格对话框 / 向导 | 未适配 | 上游用 KDE Widgets,鸿蒙版全换成标准 Qt Widgets |
| 打开最近项目 | 未适配 | 上游用 KRecentFilesAction,鸿蒙版没迁移 |
| 系统托盘 / 通知 | 未适配 | 上游用 KStatusNotifier,鸿蒙版砍掉 |
| 打印 | 未适配 | module.json5 声明了 PRINT 权限,也打包了 libQt5PrintSupport.so;但鸿蒙 PC 没有 CUPS 打印子系统,Qt-OHOS 的 PrintSupport 大概率是空壳或未启用,点打印大概率没反应或失败 |
| 复杂子窗口 / 模态对话框 | 未适配 | Qt-OHOS 平台插件的子窗口 / 模态对话框实现不完整,涉及弹窗的路径有崩溃风险 |
| Embedded UI | 未适配 | 桥接代码已预留,但宿主页仍是模板内容(Hello from EmbeddedComponentImpl),不是成熟功能 |
x86_64 模拟器 |
未适配 | 构建配置只开了 arm64-v8a |
小结:T2 这批里,最值得说的是两点。第一,KDE 全家桶全部被主动砍掉了。 KDiff3 上游强依赖 KF5——远程文件靠 KIO、崩溃上报靠 KCrash、菜单/工具栏靠 KXmlGui、配置靠 KConfig、国际化靠 KI18n。鸿蒙第一版的策略是「先把 KDE 桌面集成全剥掉,保核心 diff/merge」,所以远程文件、崩溃上报、KDE 菜单体系、完整国际化全没了。这不是 bug,是第一版取舍——先把核心功能掏出来,KDE 桌面集成以后再补。第二,打印大概率不可用。 虽然声明了 PRINT 权限、也打包了 libQt5PrintSupport.so(甚至缺了它还会闪退),但鸿蒙 PC 没有 CUPS 打印子系统,Qt-OHOS 的 PrintSupport 大概率是空壳。这两个都卡在平台能力边界或 KDE 依赖剥离上,不是代码没写。
六、整体到什么程度
把三档合起来看:
- T0 基础能力:宿主工程结构、启动链路、主窗口承载、文件关联、自检样例都到位,能编能装能起。运行时缺
libQt5PrintSupport.so闪退的问题已定位并解决。 - T1 核心比较能力:文件差异对比、三方合并、冲突解决、目录比较、语法高亮——这些是 KDiff3 的日常核心,代码路径都齐了,
libkdiff3.so交叉编译已通过。菜单/工具栏从 KDE 体系换成 Qt 原生,功能等价。 - T2 增强能力:远程文件、崩溃上报、KDE 菜单/配置/国际化、打开最近项目、系统托盘、打印——这些大多因为 KDE 依赖剥离或平台能力边界而未适配。远程文件是因为砍了 KIO、只支持本地文件;打印是因为鸿蒙没有 CUPS 打印栈;KDE 菜单/配置/国际化是第一版主动剥掉的,以后可以补。
一句话总结:这次把「ArkTS → XComponent → Qt QPA → libkdiff3.so」这条管线在鸿蒙 PC 上接通了,KDiff3 的主窗口能起来,本地文件差异对比、三方合并、目录比较这些核心功能有完整的代码路径。KDE 全家桶(远程文件、崩溃上报、KDE 菜单、完整国际化、系统托盘)全部被主动砍掉——这是第一版策略,先把核心功能掏出来,KDE 桌面集成以后再补。打印虽然声明了权限也打包了库,但鸿蒙没有 CUPS 打印栈,大概率不可用。