开源鸿蒙PC三方库实战与复现:迁移 Linux 桌面门户服务 xdg-desktop-portal
开源鸿蒙PC三方库实战与复现:迁移 Linux 桌面门户服务 xdg-desktop-portal
欢迎加入开源鸿蒙 PC 社区:https://harmonypc.csdn.net/
开源仓库地址:https://atomgit.com/OpenHarmonyPCDeveloper/ohos_xdg_desktop_portal_porting
xdg-desktop-portal。它依赖链长、还涉及鸿蒙特有的文件权限和动态库加载问题,是检验适配功力的好题目。
本篇基于社区作者 程序山海 的《https://bigstar.blog.csdn.net/article/details/161798885》整理,结合我自己的复现重新讲述。验证截图复用自原作者公开素材,致谢。
它是干什么的,为什么难
xdg-desktop-portal 是 Flatpak 社区开发的桌面门户守护进程。它的作用是:给沙箱化应用(Flatpak、Snap 这类被隔离起来、不能直接乱碰系统的应用)提供一套标准化的 D-Bus 接口,让它们能安全可控地访问系统资源——文件选择器、屏幕录制、通知、打印、位置、摄像头/麦克风访问等等。简单说,它是沙箱应用和真实系统之间的「受控中介」。
它的难度有两层,而且这两层都很硬:
第一层:依赖极其复杂。 它直接依赖 glib、fuse3、json-glib、gdk-pixbuf、gstreamer、pipewire 六个库,而 glib 自己又依赖 pcre2、libffi……这又是一棵比 mpv 还规整的多层依赖树(下一篇会完整拆解)。
第二层:平台差异巨大。 它深度绑定 D-Bus 系统服务和 systemd 用户会话,而鸿蒙的系统架构跟标准 Linux 桌面完全不同。这意味着——即便编译通过,它也未必能在鸿蒙上原样运行起来提供完整门户功能。
所以这次我把适配目标定得很务实:先验证能在鸿蒙上完整交叉编译出 arm64 产物,跑通整条复杂依赖链和打包流程。至于运行时的完整门户能力,那需要鸿蒙原生层另做对接实现,不在本次范围。适配工作要实事求是地分层看待目标,这点很重要。
| 项目 | 信息 |
|---|---|
| 库 | xdg-desktop-portal |
| 版本 | 1.21.2 |
| 协议 | LGPL-2.0-or-later |
| 构建系统 | Meson + Ninja |
| 直接依赖 | glib, fuse3, json-glib, gdk-pixbuf, gstreamer, pipewire |
| 架构 | arm64-v8a |

这一篇最有价值的两个新知识点
依赖树的处理上一篇 mpv 已经讲透了(下一篇还会就这棵树做完整拆解),这里我重点说两个 mpv 没碰到的、极具鸿蒙运行时特色的问题。这两点属于「能编出来」之后、「能加载起来」要补的课。
知识点一:rpath 用 $ORIGIN 解决「找不到库」
问题背景
在标准 Linux 上,程序运行时要找它依赖的 .so,常见做法是设 LD_LIBRARY_PATH 环境变量指向库目录。但在鸿蒙 PC 上,由于文件系统权限的限制,这个环境变量经常不生效——你设了也白设,程序还是报找不到库。
解法:把库查找路径「焊」进二进制
我的解法是给二进制注入相对 rpath,用 $ORIGIN 这个特殊变量:
# Meson 链接参数
-Dc_link_args="-Wl,-rpath,\$ORIGIN"
$ORIGIN 是 ELF 动态链接器认识的特殊占位符,代表「可执行文件自己所在的那个目录」。注入之后,程序运行时会自动去自己的同级目录找依赖库,完全不依赖任何环境变量。
这么做的好处是可移植:部署时只要把可执行文件和它的依赖 .so 放在同一个目录里,整包搬到哪都能跑,不会断链。
这其实和第三篇 CodeArts 里用的 BUILD_RPATH "$ORIGIN" 是同一个思路——可见 $ORIGIN 是鸿蒙这边解决运行时库查找问题的「标准答案」,几乎每个有依赖库的产物都会用到它。

知识点二:鸿蒙解压 tar 会改坏权限
问题现象(极其隐蔽)
这个坑非常坑。我在构建机上打好的 tar 包,库文件权限都正常。但拷到鸿蒙系统、用系统自带的 tar 命令解压后,.so 文件的权限被强制改成了 770。结果动态链接器去加载这些库时,因为权限不对而失败——而你完全想不到是「解压」这一步搞的鬼。
绕过办法
两招配合:
- 别用鸿蒙系统的 tar 解压,改用 Python 的
tarfile模块来解,解完再手动把权限修正回正常值(如 755):
import tarfile, os
with tarfile.open("xdg-desktop-portal_1.21.2-with-deps.tar.gz") as t:
t.extractall(".")
# 解压后修复 .so 权限
for root, dirs, files in os.walk("usr"):
for f in files:
if f.endswith(".so") or ".so." in f:
os.chmod(os.path.join(root, f), 0o755)
- 把依赖库放进
libexec/目录。这是原作者的一个巧思——libexec/这个目录的权限在鸿蒙解压后能保持 755,天然规避了那个 770 的 bug。所以 archive 阶段我会把所有依赖.so收拢到libexec/下。
产物的组织:带不带依赖的取舍
这次打了三个产物,体现了「自包含 vs 精简」的不同取向:
| 产物 | 内容 | 适用场景 |
|---|---|---|
xdg-desktop-portal_1.21.2-with-deps.tar.gz |
主程序 + 全部依赖 .so | 开箱即用,部署省心 |
xdg-desktop-portal_1.21.2.tar.gz |
仅主程序 | 系统已有依赖时用,省空间 |
xdg-desktop-portal.hnp |
鸿蒙标准安装包 | 走 HNP 生态分发 |
archive() 阶段会把所有依赖 .so 收拢到 libexec/ 一起打进 with-deps 包,配合前面的 $ORIGIN rpath,部署时就不会缺库——这两个机制是配套使用的。
hnp.json
{
"type": "hnp-config",
"name": "xdg-desktop-portal",
"version": "1.21.2",
"description": "Desktop integration portal for sandboxed applications",
"license": "LGPL-2.0-or-later",
"arch": "arm64-v8a",
"install": {
"bin": ["usr/libexec/xdg-desktop-portal", "usr/libexec/xdg-document-portal"],
"lib": ["usr/lib/*.so*"],
"share": ["usr/share/dbus-1/services", "usr/share/xdg-desktop-portal"]
}
}
注意 bin 入口指向的是 usr/libexec/ 下的可执行文件——正是为了配合上面说的权限规避策略。
验证
# 用上面的 Python 方式解压(避免权限 bug)
# 进入 libexec
cd usr/libexec/
# 鸿蒙强制签名
binary-sign-tool sign -inFile xdg-desktop-portal -outFile xdg-desktop-portal -selfSign "1"
chmod +x xdg-desktop-portal
# 验证架构
file xdg-desktop-portal # 期望 ARM aarch64
# 验证 rpath 和依赖(关键)
readelf -d xdg-desktop-portal
# 重点看两行:
# RUNPATH: $ORIGIN ← 库查找路径配对了
# NEEDED: 各依赖 .so ← 依赖列表是否完整
readelf -d 里能看到 RUNPATH: $ORIGIN 和完整的依赖列表,就说明库查找路径和依赖打包都对了。


常见问题速查(FAQ)
| 现象 | 根因 | 解决 |
|---|---|---|
运行时 symbol not found / cannot open shared object |
rpath 没配对 | 用 $ORIGIN 注入 rpath |
| 解压后 .so 权限变 770 | 鸿蒙系统 tar 的 bug | Python tarfile 解压 + 修权限 |
| 依赖库为什么要放 libexec | 规避 770 权限问题 | 该目录解压后保持 755 |
Meson dependency not found |
pkg-config 路径不全 | 串全所有依赖的 pkgconfig 路径 |
复制依赖库 No such file or directory |
依赖实际安装路径不符 | 确认真实安装路径再复制 |
lycium 提示 dependency not completed |
依赖没标记完成 | 在 hpk_build.csv 标记 |
wrap-file download failed |
Meson 想联网下子项目 | --wrap-mode=nodownload 或手动下载 |
| 编译找不到头文件 | 头文件路径/pkgconfig 没配 | 检查并补 -I |
小结
xdg-desktop-portal 这一篇,把两个鸿蒙运行时特有的硬骨头讲明白了:
$ORIGINrpath —— 解决运行时库查找,绕开失效的 LD_LIBRARY_PATH;- Python tarfile 解压 + libexec 目录 —— 绕过鸿蒙解压改坏权限的 bug。
这两点是前面纯交叉编译篇章里没有的,属于「编出来之后还要让它能加载、能跑」要补的课。
它也再次提醒我一个朴素的道理:有些库(尤其深度绑定 systemd/D-Bus 的系统服务),即便编译通过,也未必能在鸿蒙上原样运行。适配的目标要实事求是地分层看待——能完整编译、能打包、能加载,本身就是有价值的阶段性成果,不必强求一步到位。
我会把 xdg-desktop-portal 的整棵依赖树从最底层逐个拆开,做一次完整的「多层依赖适配方法论」总结,把全系列的经验拧成一份行动清单。感谢原创适配作者 程序山海 的开源分享。
更多推荐




所有评论(0)