HarmonyOS鸿蒙PC开源MeldNext软件移植:开源工具Meld到鸿蒙PC实践总结
本文结合本仓库MeldNext的实际源码、工程结构系统总结开源差异比较工具 Meld 从 Linux 桌面到鸿蒙 PC 的完整移植过程。移植成功后的效果一、开源软件 Meld 与 MeldNext 在鸿蒙上的差异Meld 是 GNOME 桌面环境下的可视化差异比较与合并工具原始仓库https://gitlab.gnome.org/GNOME/meld而 MeldNext 是其在鸿蒙 PC 上的移植版本。两者在技术架构上存在以下核心差异维度原生 MeldLinux 桌面MeldNext鸿蒙 PCUI 框架GTK 3Python PyGObjectArkUIArkTS 声明式 UIXComponent内嵌原生渲染编程语言Python 为主C 为辅TypeScriptArkTSC17运行形态独立桌面应用进程直接启动HAP 应用包通过 Ability 生命周期管理UI 渲染GTK 窗口系统 Cairo 绘制ArkUI 框架OpenGL ES 3.x硬件加速差异比较引擎内置 Python diff 库 GNU diffutilsGNU diffutils 3.10/3.12交叉编译为 HNP 二进制文本编辑器GtkSourceViewScintilla Lexilla通过 OpenGL 渲染到 XComponent版本控制通过 subprocess 调用 git/svnGit SVN通过 Lycium 交叉编译为 HNP 运行字符编码Python 内置编码检测uchardetMozilla 编码检测库通过 N-API 桥接构建系统Meson Python setuptoolsHvigorCMakeLycium部署方式apt/pip 安装DevEco Studio 构建签名 →hdc install HAP包名org.gnome.meldcom.develop.opensource.meldnext设备支持Linux 桌面phone2in1鸿蒙 PC/平板商用分发GPL-3.0 开源社区GPL-3.0上架鸿蒙应用市场核心差异在于MeldNext 没有将 GTK 的 Python 代码移植到鸿蒙而是完全使用 ArkTS 重写了 UI 层同时将核心 C/C 底层库通过交叉编译的方式嵌入鸿蒙运行时通过 N-API 和 HNP 机制与 ArkTS 交互。二、技术架构与技术原理2.1 总体架构┌─────────────────────────────────────────────────────────────────────┐ │ ArkTS UI 层 (ArkUI) │ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────────────┐ │ │ │ 文件比较 │ │ 文件夹比较 │ │ 版本控制 │ │ 设置/快捷键/历史 │ │ │ │ 页面 │ │ 页面 │ │ 页面 │ │ 页面 │ │ │ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ └─────────┬─────────┘ │ │ │ │ │ │ │ │ └─────────────┴──────┬──────┴──────────────────┘ │ │ │ │ │ ┌────────┴────────┐ │ │ │ N-API 桥接层 │ │ │ │ (napi_init.cpp) │ │ │ └────────┬────────┘ │ ├─────────────────────────────┼─────────────────────────────────────┤ │ ┌────────────┴────────────┐ │ │ │ 原生 C 模块 │ │ │ ┌─────────────┼──────────────────────┼──────────────┐ │ │ │ entry 模块 │ seditor 模块 │ shell 模块 │ │ │ │ (libentry) │ (libxrender) │ (libshell) │ │ │ │ │ │ │ │ │ │ · diffutils │ · Scintilla 编辑器 │ · VT100 终端 │ │ │ │ · uchardet │ · Lexilla 词法分析 │ · FreeType │ │ │ │ · SHA256 │ · RTTR 反射 │ · utf8proc │ │ │ │ · 命令执行 │ · OpenGL ES 渲染 │ · EGL/GLES │ │ │ └─────────────┴──────────────────────┴──────────────┘ │ ├─────────────────────────────┼─────────────────────────────────────┤ │ ┌────────┴────────┐ │ │ │ HNP 运行时 │ │ │ │ (/data/service/ │ │ │ │ hnp/) │ │ │ │ · diff / cmp │ │ │ │ · git / svn │ │ │ └─────────────────┘ │ ├─────────────────────────────────────────────────────────────────────┤ │ HarmonyOS 系统层 │ │ (AbilityKit, ArkUI, HiLog, EGL/GLES, native_window, uv, ace_napi) │ └─────────────────────────────────────────────────────────────────────┘2.2 核心技术原理2.2.1 N-API 桥接机制N-APINative API是鸿蒙系统中 ArkTS 与 C 原生代码交互的标准通道。MeldNext 注册了三个 N-API 模块模块名动态库注册函数暴露能力entrylibentry.soRegisterEntryModulediffFile, detectCharset, syscall_sync/async/buf/by_line, calcHash, setEnvseditorlibxrender.soRegisterSeditorModuleScintillaWidget 创建/释放, lexerList, createNativeNodeshelllibshell.so通过 napi_initrun, send, createSurface, destroySurface, resizeSurface, scroll, checkCopy/PasteN-API 桥接的关键模式包括同步调用napi_call_function直接调用 JS 回调适用于短耗时操作如setEnv、detectCharset异步工作napi_create_async_worknapi_queue_async_work适用于耗时操作如diffFile、syscall_async逐行流式回调napi_create_threadsafe_function 跨线程回调适用于命令执行的逐行输出如syscall_by_line// 异步工作模式示例napi_create_async_work(env,nullptr,resourceName,ExecuteDiffFileCB,// 后台线程执行CompleteDiffFileCB,// 主线程完成回调data,asyncWork);napi_queue_async_work(env,asyncWork);// 逐行流式回调模式napi_create_threadsafe_function(env,jsCallback,...,CallJs,safeFunc);// 后台线程逐行调用napi_call_threadsafe_function(safeFunc,lineData,napi_tsfn_blocking);2.2.2 XComponent OpenGL ES 原生渲染对于 Scintilla 编辑器seditor模块和终端模拟器shell模块MeldNext 使用XComponent组件在 ArkUI 中嵌入原生渲染表面ArkTS XComponent 控件 │ idxcomponent, typesurface │ ▼ C Native 层 │ OH_NativeWindow_CreateNativeWindowFromSurfaceId(surfaceId) │ eglGetDisplay() → eglInitialize() → eglCreateWindowSurface() │ eglMakeCurrent() → OpenGL ES 渲染循环 │ ▼ 渲染到屏幕 │ eglSwapBuffers(egl_display, egl_surface)编辑器seditor/xrenderScintilla 引擎在OhWidget的鸿蒙平台适配层中将文本行转换为 OpenGL ES 顶点/纹理数据通过 EGL 渲染到 XComponent 表面终端shellVT100/xterm 终端模拟器在收到数据后解析 ANSI 转义序列通过 FreeType 栅格化字体字形使用 OpenGL ES 绘制到屏幕2.2.3 HNPHarmonyOS Native Plugin机制对于 GNU diffutils、Git、SVN 等命令行工具MeldNext 采用 HNP 机制部署Lycium 交叉编译在 Ubuntu 22.04 上使用 OHOS Native SDK 交叉编译为 arm64-v8a/armeabi-v7a/x86_64 二进制HNP 打包使用hnpcli pack将二进制 清单文件打包为.hnp文件HAP 集成在module.json5的hnpPackages字段声明 HNP 包运行时调用通过 N-API 的syscall_by_line()调用/data/service/hnp/下的二进制ArkTS 接收逐行回调2.2.4 编码检测流程ArkTS 打开文件 │ fs.openSync(uri) → fd │ napi.detectCharset(fd) ▼ C (uchardet) │ uchardet_new() → uchardet_handle_data() → uchardet_data_end() │ uchardet_get_charset() → 返回编码名称 ▼ ArkTS 收到编码 │ 如 UTF-8, GB2312, Shift_JIS 等 │ 用于 Scintilla 编辑器正确显示文本三、底层依赖开源库介绍及移植要求3.1 移植库一览序号库名称版本原始用途在 MeldNext 中的角色移植方式代码复用率1GNU diffutils3.10/3.12文件差异比较核心 diff 引擎源码嵌入 entry Lycium 交叉编译 HNP100%2uchardetlatestMozilla 字符编码检测文件编码自动识别源码嵌入 entryadd_subdirectory~98%3Scintillalatest源码编辑器组件文本编辑 语法高亮源码嵌入 seditor需编写 OHOS 平台适配~90%4Lexillalatest词法分析器集合编程语言语法高亮Scintilla 子项目同步移植~90%5FreeTypelatest字体栅格化引擎终端字体渲染源码嵌入 shell100%6utf8proclatestUTF-8 字符串处理终端 Unicode 支持源码嵌入 shell100%7RTTR0.9.6C 运行时类型反射xrender 属性绑定源码嵌入 seditor100%8Gitv2.52.0分布式版本控制版本控制集成Lycium 交叉编译为 HNP100%9Subversion1.14.5集中式版本控制版本控制集成Lycium 交叉编译为 HNP100%10TinyXMLlatestXML 解析Scintilla 配置解析源码嵌入 xrender100%3.2 移植到 HarmonyOS 需要满足的要求3.2.1 编译工具链要求项说明编译器必须使用BiSheng毕昇编译器HarmonyOS native SDK 自带不可使用 GCCC 标准建议使用 C17CMAKE_CXX_STANDARD 17CMake 版本3.5.0OHOS SDK 自带工具链文件必须指定ohos.toolchain.cmake路径架构参数通过-DOHOS_ARCH指定目标架构3.2.2 代码层面的适配要求要求项说明平台宏OHOS 编译环境下默认定义OHOS宏可用于条件编译POSIX APIOHOS 支持大部分 POSIX APIpopen、fork、execvp、pipe、chdir、setenv等可直接使用文件系统使用 OHOS 路径规则/data/storage/el2/base/haps/等日志输出使用HiLoghilog/log.h而非 printf/cout线程模型使用 N-API 的线程安全函数不要直接操作 JS 线程OpenGL ES使用 EGL GLESv3鸿蒙支持 OpenGL ES 3.2窗口系统使用OH_NativeWindow而非 X11/Wayland3.2.3 平台适配层Scintilla 示例Scintilla 的移植需要实现以下平台接口位于seditor/src/main/cpp/scintilla/oh/scintilla/oh/ ├── OhWidget.cpp # 鸿蒙 XComponent Widget 封装 ├── OhWidget.h ├── PlatOH.cpp # 鸿蒙平台接口Window 创建、菜单、定时器等 ├── ScintillaOh.cpp # Scintilla 鸿蒙适配主入口 ├── ScintillaOh.h └── SurfaceOhImpl.cpp # 基于 EGL/GLES 的表面绘制实现 └── SurfaceOhImpl.h这些文件实现了 Scintilla 的Platform.h中所声明的全部纯虚接口将 Scintilla 的绘制请求转换为 OpenGL ES 渲染命令。3.2.4 HNP 打包要求要求项说明清单文件hnp.json描述二进制安装路径和符号链接架构匹配HNP 包架构必须与设备架构一致安装路径安装到/data/service/hnp/目录DevEco 集成需要修改packing-tool-options.js支持--hnp-path参数四、移植流程4.1 总体移植流程┌─────────────────────────────────────────────────────────────┐ │ 第1阶段环境准备 │ │ DevEco Studio 5.0.5 HarmonyOS SDK BiSheng 编译器 │ │ OHOS Native SDK Lycium 框架Ubuntu 22.04 │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 第2阶段底层库编译 │ │ │ │ ┌─────────────────────┐ ┌─────────────────────────────┐ │ │ │ 方式ACMake 嵌入 │ │ 方式BLycium 交叉编译 │ │ │ │ │ │ │ │ │ │ · uchardet │ │ · diffutils → HNP │ │ │ │ · diffutils部分 │ │ · git → HNP │ │ │ │ · FreeType │ │ · svn → HNP │ │ │ │ · utf8proc │ │ · svn-apr / svn-apr-util │ │ │ │ · RTTR │ │ │ │ │ │ · TinyXML │ └─────────────────────────────┘ │ │ │ · Scintilla(需适配) │ │ │ └─────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 第3阶段N-API 桥接开发 │ │ │ │ 编写 napi_init.cpp 注册模块和导出函数 │ │ 编写 types/libentry/Index.d.ts 类型声明 │ │ 配置 oh-package.json5 声明原生库依赖 │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 第4阶段集成到 DevEco │ │ │ │ 1. 配置 build-profile.json5签名、产品、模块 │ │ 2. 配置 module.json5hnpPackages、权限、Ability │ │ 3. 放置 HNP 包到 entry/hnp/abi/ │ │ 4. 修改 packing-tool-options.js支持 HNP 路径 │ │ 5. 编写 ArkTS UI 页面 │ │ 6. 编写 XComponent 控件 │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 第5阶段签名 安装 │ │ │ │ DevEco Studio Build Run → 自动签名 → 安装到真机 │ │ 或 hdc install entry-default-unsigned.hap │ └─────────────────────────────────────────────────────────────┘4.2 编译指令4.2.1 CMake 编译自动由 Hvigor 调用每个模块的build-profile.json5中配置externalNativeOptionsHvigor 构建时会自动调用 CMake{externalNativeOptions:{path:./src/main/cpp/CMakeLists.txt,arguments:,cppFlags:}}如需手动调试可使用以下命令# entry 模块cmake-Bbuild-GNinja\-DCMAKE_TOOLCHAIN_FILE$SDK_PATH/build/cmake/ohos.toolchain.cmake\-DOHOS_ARCHarm64-v8a\-DCMAKE_BUILD_TYPEDebug cmake--buildbuild关键 CMakeLists.txt# entry/src/main/cpp/CMakeLists.txt cmake_minimum_required(VERSION 3.5.0) project(ohpc-app-diffui2) set(CMAKE_PLATFORM_NO_VERSIONED_SONAME TRUE) add_subdirectory(uchartdet) add_library(entry SHARED napi_init.cpp napi_cmd.cpp napi_sha256.cpp) target_link_libraries(entry PUBLIC libace_napi.z.so hilog_ndk.z libuchardet libohcrypto.so)# seditor/src/main/cpp/CMakeLists.txt set(CMAKE_CXX_STANDARD 17) add_subdirectory(scintilla) # 含 oh/ 平台适配 add_subdirectory(rttr-0.9.6) add_subdirectory(xrender) # OpenGL 渲染层 add_library(seditor SHARED napi_init.cpp) target_link_libraries(seditor PUBLIC libace_napi.z.so hilog_ndk.z)# shell/src/main/cpp/CMakeLists.txt set(CMAKE_CXX_STANDARD 17) find_library(EGL-lib EGL) find_library(GLES-lib GLESv3) add_subdirectory(freetype) add_subdirectory(utf8proc) add_library(shell SHARED napi_init.cpp terminal.cpp) target_link_libraries(shell PUBLIC libace_napi.z.so ${EGL-lib} ${GLES-lib} libnative_window.so libhilog_ndk.z.so freetype utf8proc)4.2.2 Lycium 交叉编译Ubuntu 22.04环境准备# 1. 安装 Ubuntu 22.04# 2. 下载 OHOS Native SDK如 native-linux-x64-5.0.1.111-Release.zip# 3. 解压到 ~/sdk/# 4. 克隆 tpc_c_cplusplus 仓库gitclone https://gitcode.com/openharmony-sig/tpc_c_cplusplus ~/tpc_c_cplusplus# 5. 设置环境变量exportOHOS_SDK~/sdk编译 diffutilscd~/tpc_c_cplusplus# 新建 ~/tpc_c_cplusplus/thirdparty/diffutils/HPKBUILD# 编写 HPKBUILD 脚本见 lycium/diff.HPKBUILD.sh./lycium/build.sh diffutils# 输出~/tpc_c_cplusplus/lycium/usr/diffutils/arch/编译 Git# 新建 ~/tpc_c_cplusplus/thirdparty/git/HPKBUILD# 依赖 zlib./lycium/build.shgit编译 SVN按依赖顺序# 1. 先编译依赖./lycium/build.sh svn-apr ./lycium/build.sh svn-apr-util# 2. 再编译 SVN./lycium/build.sh svn打包为 HNPhnpcli pack-i./arm64-v8a-o./HNP 清单hnp.json{type:hnp-config,name:superred.diffui,version:1.0,install:{links:[{source:/bin/diff,target:diff},{source:/bin/cmp,target:cmp},{source:/bin/diff3,target:diff3},{source:/bin/sdiff,target:sdiff},{source:/bin/mdiff,target:mdiff}]}}4.3 上下层交互方式4.3.1 N-API 直接调用同步/异步// ArkTS 侧importnapifromlibentry.so;// 同步调用编码检测constencoding:stringnapi.detectCharset(fd);// 异步带回调差异比较napi.diffFile(file1Path,file2Path,(result:string){// 处理 diff 结果});// 异步带逐行回调命令执行napi.syscall_by_line(diff -u a.txt b.txt,,(line:string){// 逐行处理输出if(line[EOF]){/* 完成 */}});// C 侧导出函数napi_init.cppstaticnapi_valueDetectCharset(napi_env env,napi_callback_info info){// 1. 解析参数napi_get_cb_info(env,info,argc,args,nullptr,nullptr);napi_get_value_int32(env,args[0],fd);// 2. 调用 uchardetautohandleuchardet_new();// ... 读取文件数据 ...uchardet_handle_data(handle,buffer,len);uchardet_data_end(handle);constchar*charsetuchardet_get_charset(handle);// 3. 返回结果napi_create_string_utf8(env,charset,strlen(charset),result);returnresult;}4.3.2 XComponent OpenGL ES 原生渲染// ArkTS 侧 - 编辑器Builderbuild(){XComponent({id:editor_xcomponent,type:surface,controller:this.xcController}).onLoad((){letsurfaceIdthis.xcController.getXComponentSurfaceId();napi.createNativeNode(surfaceId);// 创建 Scintilla 编辑器})}// ArkTS 侧 - 终端Builderbuild(){XComponent({id:terminal_xcomponent,type:surface,controller:this.xcController}).onLoad((){letsurfaceIdthis.xcController.getXComponentSurfaceId();napi.createSurface(surfaceId);// 创建 EGL 表面napi.run();// 启动终端进程})}// C 侧 - 终端表面创建staticnapi_valueCreateSurface(napi_env env,napi_callback_info info){// 1. 获取 surfaceIdnapi_get_value_bigint_int64(env,args[0],surface_id,lossless);// 2. 创建 NativeWindowOHNativeWindow*native_window;OH_NativeWindow_CreateNativeWindowFromSurfaceId(surface_id,native_window);// 3. 初始化 EGLegl_displayeglGetDisplay(EGL_DEFAULT_DISPLAY);eglInitialize(egl_display,major_version,minor_version);// ... 创建 EGL 上下文和表面 ...// 4. 进入 EGL 渲染循环eglMakeCurrent(egl_display,egl_surface,egl_surface,egl_context);}4.3.3 HNP 命令执行// ArkTS 调用 HNP 二进制napi.syscall_by_line(diff --text --colornever --normal file1.txt file2.txt,/data/service/hnp,// HNP 安装路径(line:string){// 接收 diff 输出行process.stdoutline;});4.4 关键配置文件文件关键作用build-profile.json5根签名配置debug/release/release_test、产品定义targetSdk 5.1.1、编译器BiSheng、模块注册entry/build-profile.json5externalNativeOptions指向 CMakeLists.txtentry/src/main/module.json5声明hnpPackagesdiffui.hnp, base.hnp, git.hnp, superred_svn.hnp、权限、Abilityentry/oh-package.json5声明对libentry.so和app/seditor的依赖entry/src/main/cpp/types/libentry/Index.d.tsN-API 类型声明AppScope/app.json5应用元信息bundleName, versionCode, versionNametools/packing-tool-options.js修改后的 DevEco 打包工具支持 HNP 路径注入keys/*签名证书debug.p12, release.p12, *.p7b profile4.5 注意事项ABI 一致性CMake 编译时的OHOS_ARCH必须与设备架构一致。真机一般为arm64-v8a模拟器为x86_64HNP 依赖顺序SVN 的编译必须先编译svn-apr和svn-apr-util再编译svnScintilla 平台适配Scintilla 的oh/目录需要实现SurfaceImpl、WindowImpl等平台抽象接口工作量约占 Scintilla 移植的 90%OpenGL 版本使用GLESv3OpenGL ES 3.2而非桌面 OpenGL线程安全syscall_by_line的后台线程输出必须通过napi_create_threadsafe_function转发到 JS 线程不可直接调用 JS 回调权限声明文件比较功能需要声明READ_WRITE_DOCUMENTS_DIRECTORY、READ_WRITE_DOWNLOAD_DIRECTORY、READ_WRITE_DESKTOP_DIRECTORY、FILE_ACCESS_PERSIST权限打包工具修改标准的 DevEco 打包工具不支持 HNP需按tools/packing-tool-options.js修改后替换原始文件字体资源终端模块的 TTF 字体需放入resources/rawfile/目录C 侧通过 OHOS 资源 API 读取签名算法必须使用SHA256withECDSA不兼容 SHA1开发环境限制DevEco Studio 需安装在 D 盘且路径不含空格五、移植总结5.1 移植模式MeldNext 项目展示了三种底层库移植到鸿蒙 PC 的模式模式适用场景代表库复杂度源码内嵌 N-API库源码较小需频繁调用diffutils, uchardet★★☆源码适配 XComponent需要原生渲染/复杂交互Scintilla, 终端★★★Lycium/HNP 独立二进制独立命令行工具不常交互Git, SVN, diff★☆☆5.2 关键技术决策UI 层全部使用 ArkTS不保留 Qt/GTK 等传统 UI 框架确保最佳鸿蒙原生体验与性能C 引擎层完全复用只需编写 HarmonyOS 平台适配层约 5-10% 的代码量OpenGL ES XComponent作为高性能原生渲染通道解决编辑器/终端的复杂绘制需求N-API 异步模型使用napi_create_async_worknapi_create_threadsafe_function确保不阻塞 UI 线程HNP 机制让传统命令行工具无需改造即可在鸿蒙上运行大幅降低移植成本5.3 效果指标说明代码复用率C 核心引擎~95%未修改仅需编写 ~5% 的平台适配层功能完整性双文件/三文件比较、文件夹比较、版本控制集成、内置编辑器、终端模拟器均正常运行架构支持arm64-v8a真机、x86_64模拟器、armeabi-v7a目标 SDKHarmonyOS 5.1.1兼容 5.0.3支持 phone 和 2in1PC/平板5.4 关键技术决策UI 层 100% ArkTS 重写不保留 GTK 代码充分利用鸿蒙 ArkUI 声明式框架和布局能力C 引擎层跨平台复用GNU diffutils、Scintilla、FreeType、uchardet 等库的算法核心完全不变仅在平台接口层做鸿蒙适配OpenGL ES XComponent 承载原生渲染解决编辑器/终端的高性能绘制需求60fps 滚动、即时渲染HNP 承载命令行工具无需改写 Git/SVN/diffutils 的源码通过 Lycium 一键交叉编译即完成移植N-API 异步 线程安全回调保证 UI 线程不阻塞命令输出可以逐行流式渲染到界面5.6 参考资源原始 Meld 仓库https://gitlab.gnome.org/GNOME/meldMeldNext 鸿蒙开源地址https://gitcode.com/OpenHarmonyPCDeveloper/MeldNextOpenHarmony 三方库移植指导https://gitcode.com/openharmony-sig/tpc_c_cplusplusDevEco Studiohttps://developer.harmonyos.com/cn/develop/deveco-studio/