开源鸿蒙PC三方库实战与复现:xdg-desktop-portal 依赖树全解与方法论总结
开源鸿蒙PC三方库实战与复现:xdg-desktop-portal 依赖树全解与方法论总结
欢迎加入开源鸿蒙 PC 社区:https://harmonypc.csdn.net/
开源仓库地址:https://atomgit.com/OpenHarmonyPCDeveloper/ohos_xdg_desktop_portal_deps
本篇基于社区作者 程序山海 的《https://bigstar.blog.csdn.net/article/details/161798745》整理,结合我自己的理解重新组织。依赖关系图复用自原作者公开素材,致谢。
为什么用它
走到这里,系列里已经讲过 CMake、Meson、Makefile 三套构建系统,讲过 musl 兼容、强制签名、rpath、解压权限等一系列鸿蒙特性。xdg-desktop-portal 的依赖树恰好规整而完整,能把「多层依赖」这件事讲到极致。用它收尾、做一次系统化复盘,再合适不过——既是对上一篇的延续,也是对全系列的收束。
理解依赖树为什么这么重要
在动手拆树之前,先说清楚「为什么非得先理解依赖树」。在 C/C++ 项目里,多层依赖是最常见的架构,而理解它的重要性体现在四个直接后果上:
- 构建顺序决定成败:必须自底向上,否则上层编译时依赖库根本不存在;
- pkg-config 路径要逐层传递:每个库的 pkgconfig 路径必须正确传给依赖它的上层;
- 动态库加载问题:rpath 配置不当,运行时就
symbol not found; - 权限问题规避:鸿蒙的文件权限限制,需要靠特殊目录结构(libexec)来绕。
这四点,恰好串起了前面几篇的所有经验。
完整依赖树拆解
xdg-desktop-portal 的依赖分两层(不算目标本身),外加最底下的基础库:
目标库:xdg-desktop-portal(桌面门户守护进程)
├── 直接依赖(6 个):glib, fuse3, json-glib, gdk-pixbuf, gstreamer, pipewire
│
├── glib → 依赖 libffi, pcre2
├── fuse3 → 无额外依赖(但需 musl 补丁)
├── json-glib → 依赖 glib
├── gdk-pixbuf → 依赖 glib, libpng
├── gstreamer → 依赖 glib
└── pipewire → 依赖 glib
│
基础依赖:libffi, pcre2, libpng
构建顺序还是那条铁律——自底向上,逐层构建:
第 1 步:libffi, pcre2, libpng(基础,互相独立,可并行)
↓
第 2 步:glib, fuse3, json-glib, gdk-pixbuf, gstreamer, pipewire(直接依赖)
↓
第 3 步:xdg-desktop-portal(目标)
第 1 层:基础依赖库
这三个库处在依赖树的最底层,自己几乎不依赖别人(或只依赖系统库),却被上层广泛使用。
libffi —— 外部函数接口库
提供便携接口,让 C/C++ 在运行时动态调用其他语言的函数(glib 的某些机制要用它)。
- 依赖:无
- 关键点:用 configure + make;加
--disable-multi-os-directory;host 设为aarch64-linux-ohos。
眼熟吗?第九篇 JNA 也用到了 libffi。只不过那次因为老版本 configure 不认识 ohos,借用了 android 目标。可见同一个基础库在不同上层项目里反复出现,啃过一次后面就省力。
pcre2 —— 正则表达式库
高性能正则库,提供与 Perl 5 兼容的功能。glib 的 GRegex 就依赖它。
- 依赖:无
- 关键点:启用 UTF-8/16/32 和 Unicode 属性支持,否则上层用到相关功能会缺。
libpng —— PNG 图像处理库
官方 PNG 参考库,gdk-pixbuf 加载 PNG 要用。
- 依赖:zlib
- 关键点:需要显式
export CPP指定 C 预处理器,避免构建系统误用 C++ 编译器导致诡异错误。
基础层构建
cd lycium_plusplus/lycium
./build.sh libffi
./build.sh pcre2
./build.sh libpng # 会自动带上 zlib
libffi 和 pcre2 完全独立,libpng 依赖 zlib,三者可以并行编。它们必须都在 glib 之前完成。

第 2 层:直接依赖库
glib —— 基础工具库(核心中的核心)
事件循环、数据结构、对象系统……glib 是整个 GNOME 系的地基,这棵树里好几个库都依赖它。
- 依赖:libffi, pcre2
- 关键点:要下子模块(源码不全编不动);Meson 选项里
-Dlibmount=disabled -Dtests=false关掉用不上的部分。
fuse3 —— FUSE 文件系统库
xdg-document-portal(文档门户)必需。
- 依赖:无
- 关键点:要打 musl 补丁处理 pthread_cancel;
-Duseroot=false -Dutils=false。
注意这个 pthread_cancel 的 musl 补丁——这正是第一篇 MediaInfo 里 ZenLib 的同款问题!musl 不支持
pthread_cancel,所以要么打补丁注释掉,要么定义个兼容宏(如#define pthread_cancel(tid) ENOSYS)。鸿蒙适配的坑高度可复用,这是又一个铁证——啃过一次,再遇到就是肌肉记忆。
json-glib —— JSON 处理库
配置文件解析用。
- 依赖:glib
- 关键点:
depends=("glib"),cross file 里设好 pkg_config_path 指向 glib;-Dintrospection=disabled -Dtests=false。
gdk-pixbuf —— 图像加载库
截屏功能要用。
- 依赖:glib, libpng
- 关键点:
-Dintrospection=disabled -Dbuiltin_loaders=all(把图像加载器都内建进去,避免运行时动态加载缺失)。
gstreamer —— 多媒体框架
屏幕录制要用。
- 依赖:glib
- 关键点:禁用所有插件和额外依赖,只编核心,否则又会拖出一大堆依赖。
pipewire —— 音视频服务
流媒体传输用。
- 依赖:glib
- 关键点:大量系统集成功能要 disable(
-Dsystemd=disabled -Dselinux=disabled等),因为鸿蒙没有这些。
第 3 层:目标库 xdg-desktop-portal
到这一步,要把前面所有成果整合:
depends=("glib" "fuse3" "json-glib" "gdk-pixbuf" "gstreamer" "pipewire")- 配两个 Meson 配置文件:
ohos-native.ini(给构建机上跑的 glib 工具用)和ohos-cross.ini(交叉编译用); pkg_config_path串进全部依赖库的路径;-Dc_link_args="-Wl,-rpath,\$ORIGIN"注入相对 rpath;archive()把所有依赖.so复制到libexec/目录(规避权限 bug)。
这里出现了一个上一篇没细说的细节:native file 和 cross file 的区分。glib 在构建过程中需要一些工具(如 glib-compile-resources)在**构建机(x86)上运行,而最终库要给目标机(arm64)**用。这两者的工具版本和库版本不能混,所以要分别用 native file 和 cross file 描述。这是复杂 glib 系项目特有的讲究。
两个支撑性的硬核机制
原文专门深挖了两个机制,我认为它们是多层依赖适配的「内功」。
内功一:pkg-config 路径治理
交叉编译里 pkg-config 是依赖查找的中枢。我总结的几条铁则:
- 每个库编完会在自己安装目录生成
.pc文件,里面写明「头文件在哪、库在哪、还连带依赖谁」,上层全靠读它; - 交叉环境别信自动探测,老老实实把所有相关
.pc目录串进PKG_CONFIG_PATH; - 注意不同库的 pkgconfig 目录位置可能不一致(有的
lib/pkgconfig,有的usr/lib/pkgconfig),逐个核对; - 注意路径顺序——同名
.pc出现在多处时,顺序会影响最终用哪个; - glib 这类要分清「构建机工具版本」和「目标机库版本」,别让 native 和 cross 串味。
内功二:rpath 与 musl 动态链接器行为
$ORIGIN相对路径让产物可移植、不依赖系统路径,是鸿蒙库查找的标准解;- 鸿蒙文件系统权限约束(解压后目录权限异常 770)要靠 Python tarfile 解压 + 修权限 + 放 libexec 来规避;
- musl 的动态链接器行为和 glibc 有细微差异,遇到加载问题时要意识到「这不是标准 glibc 环境」。
全系列方法论总结
写到收官,我把这十一篇沉淀的东西归成五条,作为以后适配任何鸿蒙三方库的「行动清单」。
一、构建系统三选一,套路各异
| 构建系统 | 核心套路 | 本系列案例 |
|---|---|---|
| CMake | cmake -S 子目录 -DOHOS_ARCH,开关控制依赖 |
MediaInfo、G’MIC |
| Meson | 核心是写对 cross file(工具链 + pkg_config_path) | libplacebo、mpv、xdg-desktop-portal |
| Makefile | 警惕环境变量(LDFLAGS)覆盖默认值 | JNA |
二、鸿蒙平台特性,必踩的几个
- musl 兼容:缺的函数(如 pthread_cancel),用不上就打补丁注释/定义兼容宏(MediaInfo、fuse3 都遇到);
- 强制签名:
binary-sign-tool sign,可塞进构建 POST_BUILD 自动化(CodeArts 篇); - rpath:统一用
$ORIGIN解决运行时库查找(CodeArts、xdg 篇); - 解压权限 bug:用 Python tarfile 绕过系统 tar,依赖放 libexec(xdg 篇)。
三、多层依赖,方法固定
- 先画依赖树 → 自底向上逐层编 → 每层验证 pkgconfig → 串好 PKG_CONFIG_PATH;
- 排查「找不到 X」永远是沿树往下定位「断在哪一层」,而不是在原地猜(mpv、xdg 篇)。
四、环境基本功,每篇都用
- CRLF → LF:Python 二进制转换,治
$'\r': command not found; - 清构建缓存:删
hpk_build.csv+ 旧产物,否则 lycium 跳过构建; - 清 Windows 元数据:删
:Zone.Identifier文件。
这三件事看着琐碎,却是几乎每个库都要做的「开机动作」。

五、心态与目标
- 做减法优先:用不上的特性、后端、依赖,先关掉编通再说,别一上来全开(G’MIC 的 OpenMP、mpv 的图形后端、JNA 的 JAWT 都是例子);
- 验证要分层:能编 ≠ 能加载 ≠ 能完整运行。复杂系统服务(如绑定 D-Bus/systemd 的 xdg-desktop-portal)要实事求是看适配目标,阶段性成果也有价值。
结语
这个系列从一个小巧的 MediaInfo 起步,一路走过 DevEco/CodeArts 的调用集成、HPKBUILD 配方编写、Meson 交叉编译,再到 mpv、xdg-desktop-portal 这样的复杂多层依赖项目,最后落到跨语言的 JNA——把鸿蒙 PC 三方库适配的主要场景和坑都趟了一遍。
回头看,这些库虽然领域各异、构建系统不同,但底层的方法论是高度一致的:理清结构、自底向上、做减法、串好路径、处理好鸿蒙的几个平台特性、分层验证。掌握了这套,再去碰新的库,就不再是「从零摸索」,而是「按图施工」。
希望这份梳理能帮到同样想给鸿蒙生态添砖加瓦的同学。最后,由衷感谢三位原创适配作者的开源分享——wei_shuo、Dream-Y.ocean、程序山海。正是他们把一手实践公开出来,后来者才能站在肩膀上少走弯路。也欢迎大家加入开源鸿蒙 PC 社区,一起把生态做厚、做实。
更多推荐




所有评论(0)