[鸿蒙PC三方库适配实战] Linux 桌面门户服务 xdg-desktop-portal 的鸿蒙PC平台迁移实践
本文详细介绍了如何将Linux桌面门户服务xdg-desktop-portal适配到OpenHarmony平台,重点解决基于Meson构建系统的C语言项目在鸿蒙环境下的交叉编译难题。通过lycium_plusplus构建框架,系统性地处理了glib、fuse3等复杂依赖链管理,配置pkg-config路径和rpath相对路径,修复文件权限问题,最终生成HNP包。文章提供了完整的迁移指南,包括环境准
摘要:本文详细介绍了如何将 Linux 桌面门户服务 xdg-desktop-portal 适配到 OpenHarmony 平台。文章将系统性地讲解如何利用 lycium_plusplus 构建框架,处理基于 Meson 构建系统的 C 语言项目在鸿蒙环境下的交叉编译流程,展示如何处理复杂的依赖链管理(glib、fuse3、json-glib、pipewire 等)、pkg-config 路径配置、rpath 相对路径注入、文件权限修复以及 HNP 包生成的完整实践。
本文是软件鸿蒙化迁移实践系列文章之一,专注于 C/C++ 原生库的鸿蒙适配,为开发者提供完整的迁移指南。
欢迎加入开源鸿蒙PC社区:https://harmonypc.csdn.net/
欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper
AtomGit 仓库地址:https://atomgit.com/OpenHarmonyPCDeveloper/ohos_xdg_desktop_portal_porting
项目信息说明
| 项目 | 说明 |
|---|---|
| 名称 | xdg-desktop-portal |
| 开源协议 | LGPL-2.0-or-later |
| 源码版本 | 1.21.2 |
| 目标平台 | 鸿蒙 PC |
| 依赖项 | Meson, Ninja, glib, fuse3, json-glib, pipewire |
| 操作系统平台 | WSL Ubuntu 24.04 |
一、背景介绍
1.0 功能与效果
xdg-desktop-portal 在本实践中的预期能力如下:
功能:提供 Linux 桌面门户服务,为沙箱化应用(Flatpak、Snap 等)提供访问系统资源的标准化 D-Bus 接口,包括文件选择器、屏幕录制、通知系统、打印服务、位置服务、摄像头/麦克风访问控制等核心功能。
效果:在鸿蒙平台上成功编译出 ARM64 架构的可执行文件和依赖库,验证复杂 Linux 桌面组件的交叉编译能力。由于鸿蒙系统架构差异,运行时需鸿蒙原生开发实现类似门户服务功能。
1.1 什么是 OpenHarmony HNP 生态
HNP(Harmony Native Package)是 OpenHarmony 的原生包格式,lycium 是增强型构建框架,支持自动下载源码、交叉编译(arm64-v8a、armeabi-v7a)、一键生成 HNP 包以及开源声明聚合(README.OpenSource)。C/C++ 原生库的适配是鸿蒙系统生态建设中的重要一环。
1.2 为什么适配 C/C++ 原生库会有难度
- 构建系统差异:xdg-desktop-portal 使用 Meson + Ninja 构建系统,需要配置交叉编译工具链(aarch64-linux-ohos-clang)和交叉编译配置文件。
- 复杂依赖链:xdg-desktop-portal 依赖 glib(核心库)、fuse3(文件系统)、json-glib(JSON 解析)、pipewire(多媒体),而 glib 又依赖 pcre2、libffi 等,形成多层依赖树。
- pkg-config 路径管理:Meson 依赖 pkg-config 查找依赖库,需要正确配置所有依赖库的 pkgconfig 路径,包括传递依赖。
- rpath 相对路径配置:鸿蒙系统文件权限限制导致 LD_LIBRARY_PATH 不生效,需要配置 $ORIGIN 相对路径使二进制文件自动查找同级目录的库。
- 文件权限问题:鸿蒙系统解压 tar 包后会强制修改 .so 文件权限为 770,导致动态链接器无法加载,需要在打包时设置正确权限。
- 鸿蒙平台特性限制:xdg-desktop-portal 依赖 D-Bus 系统服务和 systemd 用户会话,鸿蒙使用不同的系统架构,导致编译成功但无法独立运行。
1.3 xdg-desktop-portal 简介
xdg-desktop-portal 是由 Flatpak 社区开发的桌面门户守护进程,为沙箱化应用提供标准化系统资源访问接口。其主要特点包括:
- 标准化接口:通过 D-Bus 提供统一的 Portal API,沙箱应用无需直接访问系统资源。
- 多后端支持:支持 GTK、KDE、GNOME 等多种桌面环境后端。
- 安全隔离:为 Flatpak、Snap 等沙箱应用提供安全的系统资源访问机制。
- 核心功能:文件选择器、屏幕录制、通知、打印、位置、媒体设备访问等。
- 跨平台特性:原生支持 Linux 桌面环境,本次适配扩展到 OpenHarmony 平台验证交叉编译能力。
- 开源地址:
- GitHub:https://github.com/flatpak/xdg-desktop-portal
- AtomGit 仓库地址:https://atomgit.com/weixin_62765017/ohos_xdg-desktop-portal
二、环境准备
2.0 系统要求
- 开发环境:Ubuntu 24.04(推荐 WSL 2)
- 核心工具:Meson(v0.60+)、Ninja、GCC/G++、Git
- 构建框架:lycium_plusplus
- 鸿蒙 SDK:OpenHarmony SDK(提供交叉编译工具链 aarch64-linux-ohos-clang)
- 目标架构:arm64-v8a(AArch64)
2.0.1 扩展阅读与参考教程
下方汇总展示了多位老师在鸿蒙 OpenHarmony 适配方面的高质量教程。若在前提准备(环境、工具链、框架)部分还有不清楚的地方,可参考这些文章进一步学习。 以下资源不分先后顺序,均具有参考价值。
| 资源类型 | 描述 | 链接 |
|---|---|---|
| 三方库交叉编译环境(Ubuntu) | 在 Ubuntu 中搭建鸿蒙PC 三方库交叉编译构建开发环境 | 👉 点击查看 |
| 三方库交叉编译环境(macOS) | 在 macOS 中搭建鸿蒙PC 三方库交叉编译开发环境 | 👉 点击查看 |
| 基础环境搭建 | Windows 10 上安装和使用 WSL 2、安装 Ubuntu 24 详细指南 | 👉 点击查看 |
| Mac 移植指南 | 鸿蒙PC命令行适配指南(Mac 版) | 👉 点击查看 |
| Win 移植指南 | 鸿蒙PC 生态三方软件移植:开发环境搭建及三方库移植指南 | 👉 点击查看 |
| 全流程适配指南 | OpenHarmony Linux 命令行工具适配实战:基于 Cursor × WSL 的 tree 2.2.1 交叉编译与 HNP 打包全流程指南 | 👉 点击查看 |
| 官方构建文档 | 新脚手架:社区维护的鸿蒙PC 生态命令行工具构建框架 lycium_plusplus(原 build 仓库为旧方式,请以本仓库为准) | 👉 点击查看 |
2.1 lycium_plusplus 框架
lycium_plusplus 是本次适配工作的核心工具,主要用于统一管理各类第三方库的构建流程,通过规范编译、依赖与打包逻辑,实现三方库在目标平台上高效、稳定地编译与集成,是整个适配环节中保障构建一致性与可维护性的关键支撑。
# 克隆 lycium_plusplus 项目
git clone https://gitcode.com/OpenHarmonyPCDeveloper/lycium_plusplus.git
cd lycium_plusplus
2.2 Meson 与交叉编译环境
由于 xdg-desktop-portal 是 C 语言项目,使用 Meson + Ninja 构建系统,需要 OpenHarmony SDK 提供的交叉编译工具链。
# 安装 Meson 和 Ninja
sudo apt-get install -y meson ninja-build
# 验证安装
meson --version
ninja --version
# 配置 OpenHarmony SDK 环境变量
export OHOS_SDK=/home/weishuo/ohos-sdk/linux
三、实战:以 xdg-desktop-portal 为例的适配步骤
3.1 创建项目目录结构
在 lycium_plusplus/thirdparty/ 目录下为 xdg-desktop-portal 创建专属目录。
cd lycium_plusplus/thirdparty
mkdir -p xdg-desktop-portal
cd xdg-desktop-portal
3.2 创建 HPKBUILD 文件
基于 lycium 构建系统,自动完成 xdg-desktop-portal 在 arm64-v8a 架构下的源码准备、交叉编译、依赖修复、产物打包,并生成可直接在鸿蒙设备运行的二进制程序、依赖库与 .hnp 安装包,实现沙箱应用桌面集成能力的移植。
- 准备本地源码、修复 fuse3 等依赖兼容问题
- 调用鸿蒙 Clang 工具链 + Meson 交叉编译
- 自动打包可执行文件 + 所有依赖库,生成运行包
- 提供清理、构建、打包全流程一键执行能力
#!/bin/bash
# -----------------------------------------------------------------------------
# xdg-desktop-portal HPKBUILD - OpenHarmony 鸿蒙适配
# -----------------------------------------------------------------------------
pkgname=xdg-desktop-portal
pkgver=1.21.2
pkgrel=0
pkgdesc="Desktop integration portal for sandboxed applications"
url="https://github.com/flatpak/xdg-desktop-portal"
archs=("arm64-v8a")
license=("LGPL-2.0-or-later")
depends=("glib" "fuse3" "json-glib" "gdk-pixbuf" "gstreamer" "pipewire")
makedepends=("meson" "ninja")
autounpack=false
downloadpackage=false
buildtools="meson"
srcpath="${LYCIUM_ROOT}/../Projects/${pkgname}"
builddir="${pkgname}-${pkgver}"
# -----------------------------------------------------------------------------
# prepare():准备源码
# -----------------------------------------------------------------------------
prepare() {
if [ -d "$srcpath" ]; then
echo "Using local source from: $srcpath"
mkdir -p "$builddir"
cp -rf "$srcpath"/* "$builddir/"
# 复制 libglnx wrap 文件
if [ -d "${PWD}/subprojects" ]; then
cp -r ${PWD}/subprojects "$builddir/"
echo "复制 subprojects 目录"
fi
# 应用 fuse3 pthread_cancel 兼容 patch
PATCH_FILE="${PWD}/0001-fix-fuse3-pthread-cancel-musl-compat.patch"
if [ -f "$PATCH_FILE" ]; then
echo "应用 fuse3 pthread_cancel 兼容 patch..."
FUSE3_SRC="/home/weishuo/lycium_plusplus/thirdparty/fuse3/libfuse-3.16.2"
if [ -d "$FUSE3_SRC" ]; then
cd "$FUSE3_SRC"
patch -p1 < "$PATCH_FILE"
echo "✅ fuse3 patch 应用成功"
cd - > /dev/null
else
echo "⚠️ 警告:fuse3 源码目录不存在: $FUSE3_SRC"
fi
fi
# 修复 fuse3 pkg-config 文件路径
FUSE3_PC="/home/weishuo/lycium_plusplus/lycium/usr/fuse3/${ARCH}/usr/lib/pkgconfig/fuse3.pc"
if [ -f "$FUSE3_PC" ]; then
echo "修复 fuse3.pc 路径..."
sed -i "s|^prefix=/usr$|prefix=/home/weishuo/lycium_plusplus/lycium/usr/fuse3/${ARCH}/usr|" "$FUSE3_PC"
echo "✅ fuse3.pc 修复完成"
fi
find "$builddir" -name "*.bak" -type f -delete 2>/dev/null || true
find "$builddir" -name "*:Zone.Identifier" -type f -delete 2>/dev/null || true
echo "Prepare completed in: $builddir"
else
echo "ERROR: Source not found at $srcpath"
exit 1
fi
}
# -----------------------------------------------------------------------------
# build():编译构建
# -----------------------------------------------------------------------------
build() {
cd "$builddir"
# 设置交叉编译环境变量
export CC="${OHOS_SDK}/native/llvm/bin/aarch64-linux-ohos-clang"
export CXX="${OHOS_SDK}/native/llvm/bin/aarch64-linux-ohos-clang++"
export AR="${OHOS_SDK}/native/llvm/bin/llvm-ar"
export RANLIB="${OHOS_SDK}/native/llvm/bin/llvm-ranlib"
export STRIP="${OHOS_SDK}/native/llvm/bin/llvm-strip"
export CFLAGS="--target=aarch64-linux-ohos --sysroot=${OHOS_SDK}/native/sysroot -O2 -fPIC"
export CXXFLAGS="--target=aarch64-linux-ohos --sysroot=${OHOS_SDK}/native/sysroot -O2 -fPIC"
export LDFLAGS="--target=aarch64-linux-ohos --sysroot=${OHOS_SDK}/native/sysroot"
# 创建 Meson native 配置文件(build machine 工具)
cat > ohos-native.ini << EOF
[binaries]
glib-compile-resources = '/usr/bin/glib-compile-resources'
glib-mkenums = '/usr/bin/glib-mkenums'
gdbus-codegen = '/usr/bin/gdbus-codegen'
EOF
# 创建 Meson 交叉编译配置文件
cat > ohos-cross.ini << EOF
[binaries]
c = '${OHOS_SDK}/native/llvm/bin/aarch64-linux-ohos-clang'
cpp = '${OHOS_SDK}/native/llvm/bin/aarch64-linux-ohos-clang++'
ar = '${OHOS_SDK}/native/llvm/bin/llvm-ar'
strip = '${OHOS_SDK}/native/llvm/bin/llvm-strip'
pkgconfig = 'pkg-config'
[properties]
pkg_config_path = ['${LYCIUM_ROOT}/usr/glib/${ARCH}/lib/pkgconfig', '${LYCIUM_ROOT}/usr/fuse3/${ARCH}/usr/lib/pkgconfig', '${LYCIUM_ROOT}/usr/json-glib/${ARCH}/usr/lib/pkgconfig', '${LYCIUM_ROOT}/usr/gdk-pixbuf/${ARCH}/usr/lib/pkgconfig', '${LYCIUM_ROOT}/usr/gstreamer/${ARCH}/usr/lib/pkgconfig', '${LYCIUM_ROOT}/usr/pipewire/${ARCH}/usr/lib/pkgconfig']
[built-in options]
c_args = ['--target=aarch64-linux-ohos', '--sysroot=${OHOS_SDK}/native/sysroot', '-O2', '-fPIC']
cpp_args = ['--target=aarch64-linux-ohos', '--sysroot=${OHOS_SDK}/native/sysroot', '-O2', '-fPIC']
c_link_args = ['--target=aarch64-linux-ohos', '--sysroot=${OHOS_SDK}/native/sysroot']
cpp_link_args = ['--target=aarch64-linux-ohos', '--sysroot=${OHOS_SDK}/native/sysroot']
[host_machine]
system = 'linux'
cpu_family = 'aarch64'
cpu = 'aarch64'
endian = 'little'
EOF
# 设置依赖库的 pkg-config 路径(注意:fuse3、json-glib、gdk-pixbuf、gstreamer、pipewire 安装在 /usr/lib/pkgconfig)
export PKG_CONFIG_PATH="${LYCIUM_ROOT}/usr/glib/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/pcre2/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/libffi/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/fuse3/${ARCH}/usr/lib/pkgconfig:${LYCIUM_ROOT}/usr/json-glib/${ARCH}/usr/lib/pkgconfig:${LYCIUM_ROOT}/usr/gdk-pixbuf/${ARCH}/usr/lib/pkgconfig:${LYCIUM_ROOT}/usr/gstreamer/${ARCH}/usr/lib/pkgconfig:${LYCIUM_ROOT}/usr/pipewire/${ARCH}/usr/lib/pkgconfig:${PKG_CONFIG_PATH}"
export PKG_CONFIG_LIBDIR="${LYCIUM_ROOT}/usr/glib/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/pcre2/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/libffi/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/fuse3/${ARCH}/usr/lib/pkgconfig:${LYCIUM_ROOT}/usr/json-glib/${ARCH}/usr/lib/pkgconfig:${LYCIUM_ROOT}/usr/gdk-pixbuf/${ARCH}/usr/lib/pkgconfig:${LYCIUM_ROOT}/usr/gstreamer/${ARCH}/usr/lib/pkgconfig:${LYCIUM_ROOT}/usr/pipewire/${ARCH}/usr/lib/pkgconfig"
# 使用系统的 glib 工具(build machine)而不是交叉编译的版本
# 创建临时符号链接,让 meson 使用 x86_64 的工具
if [ ! -f "${LYCIUM_ROOT}/usr/glib/${ARCH}/bin/glib-compile-resources.orig" ]; then
mv "${LYCIUM_ROOT}/usr/glib/${ARCH}/bin/glib-compile-resources" "${LYCIUM_ROOT}/usr/glib/${ARCH}/bin/glib-compile-resources.orig" 2>/dev/null || true
ln -sf /usr/bin/glib-compile-resources "${LYCIUM_ROOT}/usr/glib/${ARCH}/bin/glib-compile-resources"
fi
if [ ! -f "${LYCIUM_ROOT}/usr/glib/${ARCH}/bin/glib-mkenums.orig" ]; then
mv "${LYCIUM_ROOT}/usr/glib/${ARCH}/bin/glib-mkenums" "${LYCIUM_ROOT}/usr/glib/${ARCH}/bin/glib-mkenums.orig" 2>/dev/null || true
ln -sf /usr/bin/glib-mkenums "${LYCIUM_ROOT}/usr/glib/${ARCH}/bin/glib-mkenums"
fi
if [ ! -f "${LYCIUM_ROOT}/usr/glib/${ARCH}/bin/gdbus-codegen.orig" ]; then
mv "${LYCIUM_ROOT}/usr/glib/${ARCH}/bin/gdbus-codegen" "${LYCIUM_ROOT}/usr/glib/${ARCH}/bin/gdbus-codegen.orig" 2>/dev/null || true
ln -sf /usr/bin/gdbus-codegen "${LYCIUM_ROOT}/usr/glib/${ARCH}/bin/gdbus-codegen"
fi
echo "PKG_CONFIG_PATH: $PKG_CONFIG_PATH"
pkg-config --list-all | grep -E 'glib|fuse3|json-glib|gdk-pixbuf|gstreamer|pipewire' || true
# Meson 配置:禁用不需要的功能 - 直接输出到终端
meson setup ohos-build \
--cross-file ohos-cross.ini \
--native-file ohos-native.ini \
--prefix=/usr \
--buildtype=release \
-Ddocumentation=disabled \
-Dman-pages=disabled \
-Dtests=disabled \
-Dinstalled-tests=false \
-Dsystemd=disabled \
-Dgeoclue=disabled \
-Dgudev=disabled \
-Dflatpak-interfaces=disabled \
-Dsandboxed-image-validation=disabled \
-Dsandboxed-sound-validation=disabled \
-Db_lundef=false \
-Db_lto=false \
--cmake-prefix-path=/usr \
-Dc_link_args="-Wl,-rpath,\$ORIGIN"
ret=$?
if [ $ret -ne 0 ]; then
echo "Meson configuration failed!" >&2
cd "$OLDPWD"
return $ret
fi
# 编译 - 直接输出到终端
ninja -C ohos-build
ret=$?
cd "$OLDPWD"
return $ret
}
# -----------------------------------------------------------------------------
# check():验证构建产物
# -----------------------------------------------------------------------------
check() {
echo "The test must be on an OpenHarmony device!"
}
# -----------------------------------------------------------------------------
# package():打包产物
# -----------------------------------------------------------------------------
package() {
: ${destdir:=${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}}
echo "Compileing OpenHarmony ${ARCH} ${pkgname} v${pkgver} libs...."
# 进入源码目录
cd ${builddir}
# 使用 ninja install 安装到 destdir
DESTDIR="${destdir}" ninja -C ohos-build install
ret=$?
cd "$OLDPWD"
return $ret
}
# -----------------------------------------------------------------------------
# archive():打包成 tar.gz 和 HNP(包含依赖库)
# -----------------------------------------------------------------------------
archive() {
export HNP_TOOL="${HNP_TOOL:-${OHOS_SDK}/toolchains/hnpcli}"
mkdir -p ${LYCIUM_ROOT}/output/$ARCH
# 创建临时目录,复制 xdg-desktop-portal 和依赖库
local tmpdir=$(mktemp -d)
local xdg_dir="${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}"
# 复制 xdg-desktop-portal 文件
cp -r "${xdg_dir}"/* "${tmpdir}/"
# 复制所有依赖库到 usr/libexec/(与二进制同级)
# glib 系列
local glib_dir="${LYCIUM_ROOT}/usr/glib/${ARCH}"
if [ -d "${glib_dir}/lib" ]; then
cp -L "${glib_dir}/lib/libglib-2.0.so"* "${tmpdir}/usr/libexec/" 2>/dev/null || true
cp -L "${glib_dir}/lib/libgobject-2.0.so"* "${tmpdir}/usr/libexec/" 2>/dev/null || true
cp -L "${glib_dir}/lib/libgio-2.0.so"* "${tmpdir}/usr/libexec/" 2>/dev/null || true
cp -L "${glib_dir}/lib/libgmodule-2.0.so"* "${tmpdir}/usr/libexec/" 2>/dev/null || true
cp -L "${glib_dir}/lib/libgthread-2.0.so"* "${tmpdir}/usr/libexec/" 2>/dev/null || true
cp -L "${glib_dir}/lib/libintl.so"* "${tmpdir}/usr/libexec/" 2>/dev/null || true
cp -L "${glib_dir}/lib/libpcre2-8.so"* "${tmpdir}/usr/libexec/" 2>/dev/null || true
fi
# json-glib
local json_dir="${LYCIUM_ROOT}/usr/json-glib/${ARCH}"
if [ -d "${json_dir}/usr/lib" ]; then
cp -L "${json_dir}/usr/lib/libjson-glib-1.0.so"* "${tmpdir}/usr/libexec/" 2>/dev/null || true
fi
# pipewire
local pw_dir="${LYCIUM_ROOT}/usr/pipewire/${ARCH}"
if [ -d "${pw_dir}/usr/lib" ]; then
cp -L "${pw_dir}/usr/lib/libpipewire-0.3.so"* "${tmpdir}/usr/libexec/" 2>/dev/null || true
cp -L "${pw_dir}/usr/lib/libspa-0.2.so"* "${tmpdir}/usr/libexec/" 2>/dev/null || true
cp -L "${pw_dir}/usr/lib/libdbus-1.so"* "${tmpdir}/usr/libexec/" 2>/dev/null || true
fi
# fuse3
local fuse_dir="${LYCIUM_ROOT}/usr/fuse3/${ARCH}"
if [ -d "${fuse_dir}/usr/lib" ]; then
cp -L "${fuse_dir}/usr/lib/libfuse3.so"* "${tmpdir}/usr/libexec/" 2>/dev/null || true
fi
# 修复权限
chmod 755 "${tmpdir}/usr/libexec/"*.so* 2>/dev/null || true
chmod 755 "${tmpdir}/usr/libexec/"xdg-* 2>/dev/null || true
# 打包 tar.gz(包含所有依赖)
pushd "${tmpdir}" > /dev/null 2>&1
tar -zcf ${LYCIUM_ROOT}/output/$ARCH/${pkgname}_${pkgver}-with-deps.tar.gz .
echo "Archive completed: ${LYCIUM_ROOT}/output/$ARCH/${pkgname}_${pkgver}-with-deps.tar.gz"
popd > /dev/null 2>&1
# 清理临时目录
rm -rf "${tmpdir}"
# 也保留原始不带依赖的包
pushd "${xdg_dir}" > /dev/null 2>&1
tar -zcf ${LYCIUM_ROOT}/output/$ARCH/${pkgname}_${pkgver}.tar.gz .
echo "Archive completed: ${LYCIUM_ROOT}/output/$ARCH/${pkgname}_${pkgver}.tar.gz"
popd > /dev/null 2>&1
# 打包 HNP
if [ -f "${HNP_TOOL}" ]; then
cp ${LYCIUM_ROOT}/../thirdparty/${pkgname}/hnp.json "${xdg_dir}/"
${HNP_TOOL} pack \
-i "${xdg_dir}" \
-o ${LYCIUM_ROOT}/output/$ARCH/
echo "Archive completed: ${LYCIUM_ROOT}/output/$ARCH/${pkgname}.hnp"
else
echo "Warning: hnpcli not found at ${HNP_TOOL}, skipping HNP generation"
fi
echo ""
echo "Build ${pkgname} v${pkgver} end!"
echo "ALL JOBS DONE!!!"
echo ""
echo "Generated packages:"
echo " - ${LYCIUM_ROOT}/output/$ARCH/${pkgname}_${pkgver}.tar.gz (only xdg-desktop-portal)"
echo " - ${LYCIUM_ROOT}/output/$ARCH/${pkgname}_${pkgver}-with-deps.tar.gz (with all dependencies)"
echo " - ${LYCIUM_ROOT}/output/$ARCH/${pkgname}.hnp"
}
# -----------------------------------------------------------------------------
# cleanbuild():清理构建产物
# -----------------------------------------------------------------------------
cleanbuild() {
echo "Cleaning build artifacts for ${pkgname}..."
# 清理构建目录
rm -rf "${LYCIUM_ROOT}/../thirdparty/${pkgname}/${builddir}"
# 清理 output
rm -rf "${LYCIUM_ROOT}/output/${pkgname}"*
# 清理 usr 产物
rm -rf "${LYCIUM_ROOT}/usr/${pkgname}"
echo "Clean completed"
}

3.3 创建 hnp.json(包元数据)
鸿蒙 OpenHarmony 专用的 hnp 安装包配置文件(hnp.json),用于告诉系统安装器:把编译好的 xdg-desktop-portal 可执行文件、动态库、配置文件,安装到鸿蒙设备的对应目录,让程序能正常运行
{
"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"]
}
}

3.4 创建 README.OpenSource(开源声明)
xdg-desktop-portal 及其所有依赖库的开源软件清单文件,完整记录了每个组件的名称、开源协议、版本、源码下载地址和描述,用于合规声明、源码溯源和构建依赖管理
- HPKBUILD → 负责编译构建
- hnp.json → 负责安装规则
- 这个 **JSON → 负责开源合规 + 依赖清单**
[
{
"Name": "xdg-desktop-portal",
"License": "LGPL-2.0-or-later",
"License File": "https://github.com/flatpak/xdg-desktop-portal/blob/master/COPYING",
"Version Number": "1.21.2",
"Owner": "your-email@example.com",
"Upstream URL": "https://github.com/flatpak/xdg-desktop-portal/releases/download/1.21.2/xdg-desktop-portal-1.21.2.tar.xz",
"Description": "Desktop integration portal for sandboxed applications, providing a D-Bus interface for file access, printing, screenshots, and other desktop services."
},
{
"Name": "glib",
"License": "LGPL-2.1-or-later",
"License File": "https://gitlab.gnome.org/GNOME/glib/-/blob/main/COPYING",
"Version Number": "2.76.0",
"Owner": "your-email@example.com",
"Upstream URL": "https://download.gnome.org/sources/glib/2.76/glib-2.76.0.tar.xz",
"Description": "GLib is a general-purpose utility library."
},
{
"Name": "fuse3",
"License": "LGPL-2.1-or-later",
"License File": "https://github.com/libfuse/libfuse/blob/master/LGPL2.txt",
"Version Number": "3.16.0",
"Owner": "your-email@example.com",
"Upstream URL": "https://github.com/libfuse/libfuse/releases/download/fuse-3.16.0/fuse-3.16.0.tar.xz",
"Description": "FUSE (Filesystem in Userspace) is an interface for userspace programs to export a filesystem to the Linux kernel."
},
{
"Name": "json-glib",
"License": "LGPL-2.1-or-later",
"License File": "https://gitlab.gnome.org/GNOME/json-glib/-/blob/main/COPYING",
"Version Number": "1.8.0",
"Owner": "your-email@example.com",
"Upstream URL": "https://download.gnome.org/sources/json-glib/1.8/json-glib-1.8.0.tar.xz",
"Description": "JSON-GLib is a library for reading and parsing JSON using GLib and GObject data types."
},
{
"Name": "gdk-pixbuf",
"License": "LGPL-2.1-or-later",
"License File": "https://gitlab.gnome.org/GNOME/gdk-pixbuf/-/blob/master/COPYING",
"Version Number": "2.42.10",
"Owner": "your-email@example.com",
"Upstream URL": "https://download.gnome.org/sources/gdk-pixbuf/2.42/gdk-pixbuf-2.42.10.tar.xz",
"Description": "GDK Pixbuf is a library for image loading and manipulation."
},
{
"Name": "gstreamer",
"License": "LGPL-2.1-or-later",
"License File": "https://gstreamer.freedesktop.org/src/gstreamer/gstreamer-1.22.0.tar.xz",
"Version Number": "1.22.0",
"Owner": "your-email@example.com",
"Upstream URL": "https://gstreamer.freedesktop.org/src/gstreamer/gstreamer-1.22.0.tar.xz",
"Description": "GStreamer is a library for constructing graphs of media-handling components."
},
{
"Name": "pipewire",
"License": "MIT",
"License File": "https://gitlab.freedesktop.org/pipewire/pipewire/-/blob/master/COPYING",
"Version Number": "0.3.79",
"Owner": "your-email@example.com",
"Upstream URL": "https://gitlab.freedesktop.org/pipewire/pipewire/-/archive/0.3.79/pipewire-0.3.79.tar.gz",
"Description": "PipeWire is a server for handling audio and video streams and hardware on Linux."
}
]

3.5 创建 HPKCHECK 检查脚本
自动化验证脚本(HPKCHECK),用于在编译完成后自动检查二进制文件、依赖库、权限、架构、配置文件是否完整且符合鸿蒙 OpenHarmony 运行要求,并输出详细检查报告,确保编译产物能正常部署
- 二进制文件是否存在、是否为正确的 ARM64 架构
- 依赖库是否齐全、权限是否正常
- D-Bus 服务、Portal 接口文件是否完整
- RPATH 运行时路径是否配置正确
- 最终输出通过 / 失败结果,保障部署安全
# Copyright (c) 2025 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
HPK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "${HPK_DIR}" || exit 1
# 支持两种模式:
# 1. lycium 构建系统调用:source ./HPKBUILD
# 2. 独立验证:传入产物目录作为参数
if [ -n "$1" ]; then
# 独立验证模式
TARGET_DIR="$1"
pkgname="xdg-desktop-portal"
ARCH="aarch64"
else
# lycium 构建系统模式
source ./HPKBUILD > /dev/null 2>&1
TARGET_DIR="${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}"
fi
logfile="${HPK_DIR}/${pkgname}_${ARCH}_test.log"
checkprepare() {
return 0
}
openharmonycheck() {
res=0
inst_bin="${TARGET_DIR}/libexec"
inst_share="${TARGET_DIR}/share"
# 检查构建产物是否存在
if [ ! -d "${inst_bin}" ]; then
echo "no xdg-desktop-portal artifacts in ${inst_bin}" >&2
return 1
fi
[ -d /usr/CIusr/bin ] && export PATH="/usr/CIusr/bin:${PATH}"
{
echo "start test times: $(date)"
echo "=== xdg-desktop-portal 构建产物检查 ==="
echo ""
# 1. 检查二进制文件
echo "--- 二进制文件检查 ---"
if [ -f "${inst_bin}/xdg-desktop-portal" ]; then
echo "✓ xdg-desktop-portal binary found"
file "${inst_bin}/xdg-desktop-portal"
# 检查架构
arch_info=$(file "${inst_bin}/xdg-desktop-portal" | grep -o "ARM aarch64")
if [ -n "$arch_info" ]; then
echo "✓ Architecture: ARM aarch64 (correct)"
else
echo "✗ ERROR: Wrong architecture!"
res=1
fi
# 检查 rpath
rpath_info=$(readelf -d "${inst_bin}/xdg-desktop-portal" 2>/dev/null | grep RUNPATH)
if [ -n "$rpath_info" ]; then
echo "✓ RUNPATH: $(echo $rpath_info | grep -o '\$ORIGIN')"
else
echo "✗ WARNING: No RUNPATH found"
fi
else
echo "✗ ERROR: xdg-desktop-portal binary not found"
res=1
fi
echo ""
# 2. 检查 xdg-document-portal
echo "--- 辅助二进制检查 ---"
if [ -f "${inst_bin}/xdg-document-portal" ]; then
echo "✓ xdg-document-portal binary found"
file "${inst_bin}/xdg-document-portal"
else
echo "⚠ Warning: xdg-document-portal not found"
fi
echo ""
# 3. 检查依赖库
echo "--- 依赖库检查 ---"
lib_count=$(find "${inst_bin}" -name "*.so*" -type f 2>/dev/null | wc -l)
if [ "$lib_count" -gt 0 ]; then
echo "✓ Found $lib_count shared libraries in libexec/"
find "${inst_bin}" -name "*.so*" -type f 2>/dev/null | head -10 | while read lib; do
basename "$lib"
done
else
echo "✗ ERROR: No shared libraries found!"
res=1
fi
echo ""
# 4. 检查 D-Bus 服务文件
echo "--- D-Bus 服务文件检查 ---"
if [ -d "${inst_share}/dbus-1" ]; then
echo "✓ D-Bus service files found"
find "${inst_share}/dbus-1" -name "*.service" 2>/dev/null | while read svc; do
echo " - $(basename $svc)"
done
else
echo "⚠ Warning: D-Bus service files not found"
fi
echo ""
# 5. 检查 Portal 接口定义
echo "--- Portal 接口检查 ---"
if [ -d "${inst_share}/xdg-desktop-portal" ]; then
echo "✓ Portal interfaces found"
find "${inst_share}/xdg-desktop-portal" -name "*.portal" 2>/dev/null | while read portal; do
echo " - $(basename $portal)"
done
else
echo "⚠ Warning: Portal interfaces not found"
fi
echo ""
# 6. 检查权限
echo "--- 文件权限检查 ---"
perm_issues=0
find "${inst_bin}" -type f \( -name "*.so*" -o -name "xdg-*" \) 2>/dev/null | while read f; do
perm=$(stat -c "%a" "$f" 2>/dev/null || stat -f "%Lp" "$f" 2>/dev/null)
if [ "$perm" != "755" ]; then
echo "✗ WARNING: $(basename $f) has permission $perm (expected 755)"
perm_issues=$((perm_issues + 1))
fi
done
if [ $perm_issues -eq 0 ]; then
echo "✓ All files have correct permissions (755)"
fi
echo ""
# 7. 验证依赖库链接
echo "--- 动态库依赖检查 ---"
if [ -f "${inst_bin}/xdg-desktop-portal" ]; then
needed_count=$(readelf -d "${inst_bin}/xdg-desktop-portal" 2>/dev/null | grep NEEDED | wc -l)
echo "✓ NEEDED libraries: $needed_count"
readelf -d "${inst_bin}/xdg-desktop-portal" 2>/dev/null | grep NEEDED | while read line; do
echo " - $(echo $line | grep -o '\[.*\]')"
done
fi
echo ""
# 8. 总结
echo "=== 构建产物总结 ==="
echo "二进制文件: $(find "${inst_bin}" -maxdepth 1 -type f -executable 2>/dev/null | wc -l) 个"
echo "依赖库: $lib_count 个"
echo "D-Bus 服务: $(find "${inst_share}/dbus-1" -name "*.service" 2>/dev/null | wc -l) 个"
echo "Portal 接口: $(find "${inst_share}/xdg-desktop-portal" -name "*.portal" 2>/dev/null | wc -l) 个"
echo ""
if [ $res -ne 0 ]; then
echo "✗ Some checks failed!"
else
echo "✓ All checks passed!"
fi
echo "end test times: $(date)"
} >> "${logfile}" 2>&1
# 输出日志到终端
cat "${logfile}"
if [ $res -ne 0 ] && [ -n "${LYCIUM_FAULT_PATH}" ]; then
mkdir -p "${LYCIUM_FAULT_PATH}/${pkgname}"
fi
cd "${OLDPWD}"
return $res
}
# 执行验证
if [ -n "$1" ]; then
echo "=== 独立验证模式 ==="
echo "产物目录: $1"
echo ""
openharmonycheck
exit $?
else
openharmonycheck
exit $?
fi

四、编译流程与完整示例
4.1 环境准备
# 设置 lycium_plusplus 路径
export LYCIUM_ROOT=/home/weishuo/lycium_plusplus
export ARCH=aarch64
# 进入项目目录
cd /home/weishuo/lycium_plusplus/thirdparty/xdg-desktop-portal
4.2 创建项目结构并执行编译
# 进入第三方库目录,创建 xdg-desktop-portal 文件夹
cd lycium_plusplus/thirdparty
mkdir -p xdg-desktop-portal
cd xdg-desktop-portal
# 编辑构建配置文件(写入你的编译脚本)
vim HPKBUILD
# 返回构建工具主目录
cd ../../lycium
# 关键:清理历史构建记录
# 从构建日志中删除旧的 xdg-desktop-portal 记录
grep -v 'xdg-desktop-portal' usr/hpk_build.csv > usr/hpk_build.csv.tmp
mv usr/hpk_build.csv.tmp usr/hpk_build.csv
# 删除旧的源码目录和编译产物
rm -rf ../thirdparty/xdg-desktop-portal/xdg-desktop-portal-*
rm -rf output/*/xdg-desktop-portal*
# 核心:开始编译构建
./build.sh xdg-desktop-portal
4.3 构建成功输出示例
成功地把 xdg-desktop-portal v1.21.2 适配并交叉编译到鸿蒙 OpenHarmony 平台,生成了可直接安装运行的 .hnp 安装包,整个移植流程全部完成

4.4 验证产物
xdg-desktop-portal_1.21.2-with-deps.tar.gz
带所有依赖库的完整压缩包,包含程序 + 所有.so 依赖,可直接部署到鸿蒙设备
xdg-desktop-portal_1.21.2.tar.gz
仅主程序的基础压缩包,不含依赖,适合后续单独管理依赖
xdg-desktop-portal.hnp
鸿蒙 OpenHarmony 标准安装包格式,可直接通过鸿蒙包管理器安装

五、鸿蒙 PC 上验证结果
解压鸿蒙平台的 xdg-desktop-portal 完整部署包,进入程序目录查看文件,再用鸿蒙签名工具给二进制文件添加自签名,最后赋予程序可执行权限,完成鸿蒙设备上运行前的准备工作
# 解压包含所有依赖的安装包
tar -zxf xdg-desktop-portal_1.21.2-with-deps.tar.gz
# 进入程序可执行文件目录
cd usr/libexec/
# 查看目录下的程序和依赖库
ls
# 鸿蒙设备签名工具:给二进制文件签名(必须签名才能运行)
binary-sign-tool sign -inFile xdg-desktop-portal -outFile xdg-desktop-portal -selfSign "1"
# 给程序添加可执行权限
chmod +x xdg-desktop-portal

鸿蒙设备上对 xdg-desktop-portal 二进制文件的验证结果
- file:命令用来快速识别文件格式和架构,是交叉编译场景下的常用检查
- readelf -d :专门看 ELF 文件的动态链接信息,用来排查找不到共享库的问题
# 1. 查看文件类型,确认它是ARM64架构的ELF可执行文件
file xdg-desktop-portal
# 2. 读取动态链接段,查看它依赖的库和运行路径
readelf -d xdg-desktop-portal
xdg-desktop-portal 程序的格式与依赖验证:file 命令确认它是适配鸿蒙设备的 arm64 架构、基于 musl libc 的动态链接 ELF 文件;readelf -d 进一步显示其 RUNPATH 设为当前目录($ORIGIN),并列出了 libglib、libgio 等所有依赖库,证明程序的架构、依赖路径均配置正确,运行时可自动加载依赖,为后续执行扫清了障碍

六、常见问题与解决方案(FAQ)
Q1: 运行时提示 “symbol not found” 或 “cannot open shared object file”?
A: 这是 rpath 配置错误导致的经典问题。
错误现象:
$ ./usr/libexec/xdg-desktop-portal
CANNOT LINK EXECUTABLE: could not load needed library 'libglib-2.0.so.0'
(symbol not found: g_log)
根本原因:
- rpath 使用了绝对路径(如 -rpath,/usr/lib)
- 解压后路径失效,动态链接器找不到依赖库
- LD_LIBRARY_PATH 在鸿蒙系统无效
解决方案: 使用 $ORIGIN 相对路径
# HPKBUILD 中的正确配置
meson setup ohos-build \
-Dc_link_args="-Wl,-rpath,\$ORIGIN" \
...
# 验证 rpath
$ readelf -d usr/libexec/xdg-desktop-portal | grep RUNPATH
0x000000000000001d (RUNPATH) Library runpath: [$ORIGIN]
注意:
- Meson 不识别环境变量 LDFLAGS
- 必须使用 -Dc_link_args 参数
Q2: 解压后动态库权限变成 770,导致 “Permission denied”?
**A:**鸿蒙系统 tar 的已知 bug!
错误现象:
$ tar xf xdg-desktop-portal-*.tar.gz
$ ls -l usr/libexec/*.so*
-rwxr-x--- 1 user group 770 # ❌ 权限被强制修改
$ ./usr/libexec/xdg-desktop-portal
bash: ./usr/libexec/xdg-desktop-portal: Permission denied
根本原因:
- 鸿蒙系统强制重置 tar 解压文件的权限
- 标准 chmod 命令无效或权限被再次重置
- 影响所有 .so 文件和二进制文件
解决方案: 使用 Python tarfile 模块
# 错误做法(会导致权限问题)
tar xf xdg-desktop-portal-*.tar.gz
# 正确做法(使用 Python)
python3 -c "import tarfile; tarfile.open('xdg-desktop-portal-*.tar.gz').extractall()"
# 修复权限
chmod 755 usr/libexec/*.so*
chmod 755 usr/libexec/xdg-*
# 验证权限
$ ls -l usr/libexec/xdg-desktop-portal
-rwxr-xr-x 1 user group 755 # ✅ 正确
自动化脚本(extract.sh):
#!/bin/bash
# 在鸿蒙设备上使用
python3 -c "import tarfile; tarfile.open('$1').extractall()"
find usr/libexec/ -type f -exec chmod 755 {} \;
Q3: 为什么依赖库要放在 libexec/ 而不是 lib/ 目录?
**A:**这是规避鸿蒙权限问题的关键设计!
原因分析:
| 目录 | 解压后权限 | 是否可用 | 说明 |
|---|---|---|---|
/lib |
770 | 否 | 鸿蒙强制修改 |
/usr/lib |
770 | 否 | 鸿蒙强制修改 |
/usr/libexec |
755 | 是 | 保持正确权限 |
HPKBUILD 中的配置:
archive() {
# 将所有依赖库复制到 libexec/
cp -L "${glib_dir}/lib/libglib-2.0.so"* "${tmpdir}/usr/libexec/"
cp -L "${json_dir}/usr/lib/libjson-glib-1.0.so"* "${tmpdir}/usr/libexec/"
cp -L "${fuse_dir}/lib/libfuse3.so"* "${tmpdir}/usr/libexec/"
# 修复权限
chmod 755 "${tmpdir}/usr/libexec/"*.so*
chmod 755 "${tmpdir}/usr/libexec/"xdg-*
}
配合 rpath $ORIGIN:
- 二进制文件在 usr/libexec/xdg-desktop-portal
- 依赖库在 usr/libexec/libglib-2.0.so.0
- $ORIGIN 指向二进制所在目录,可以找到所有依赖
Q4: Meson 编译时提示 “dependency not found”?
**A:**pkg-config 路径配置不完整。
错误现象:
Run-time dependency glib-2.0 found: NO (tried pkgconfig and cmake)
meson.build:50:0: ERROR: Dependency "glib-2.0" not found
根本原因:
- 交叉编译环境下,Meson 无法自动找到依赖库
- pkg-config 默认路径不包含 lycium 构建的库
解决方案: 完整配置 pkg-config 路径
# HPKBUILD 中的配置
-Dpkg_config_path='\
${LYCIUM_ROOT}/usr/glib/${ARCH}/lib/pkgconfig:\
${LYCIUM_ROOT}/usr/fuse3/${ARCH}/lib/pkgconfig:\
${LYCIUM_ROOT}/usr/json-glib/${ARCH}/lib/pkgconfig:\
${LYCIUM_ROOT}/usr/gdk-pixbuf/${ARCH}/lib/pkgconfig:\
${LYCIUM_ROOT}/usr/gstreamer/${ARCH}/lib/pkgconfig:\
${LYCIUM_ROOT}/usr/pipewire/${ARCH}/lib/pkgconfig'
验证 pkg-config:
# 检查 pkg-config 是否找到依赖
pkg-config --modversion glib-2.0
# 2.84.3
pkg-config --cflags glib-2.0
# -I/home/weishuo/lycium_plusplus/usr/glib/aarch64/include/glib-2.0
Q5: HPKBUILD 中复制依赖库时提示 “No such file or directory”?
**A:**依赖库路径错误,需要确认实际产物位置。
错误现象:
cp: cannot stat '/home/weishuo/lycium_plusplus/usr/json-glib/aarch64/lib/libjson-glib-1.0.so':
No such file or directory
根本原因:
- 不同依赖库的安装路径不一致
- 有些库在 lib/,有些在 usr/lib/
正确路径对照表:
# HPKBUILD 中的正确配置
depends_paths=(
"${LYCIUM_ROOT}/usr/glib/${ARCH}/lib" # glib → lib/
"${LYCIUM_ROOT}/usr/fuse3/${ARCH}/lib" # fuse3 → lib/
"${LYCIUM_ROOT}/usr/json-glib/${ARCH}/usr/lib" # json-glib → usr/lib/ ✅
"${LYCIUM_ROOT}/usr/gdk-pixbuf/${ARCH}/usr/lib" # gdk-pixbuf → usr/lib/ ✅
"${LYCIUM_ROOT}/usr/gstreamer/${ARCH}/usr/lib" # gstreamer → usr/lib/ ✅
"${LYCIUM_ROOT}/usr/pipewire/${ARCH}/usr/lib" # pipewire → usr/lib/ ✅
)
查找实际路径:
# 查找 .so 文件位置
find ${LYCIUM_ROOT}/usr/json-glib -name "*.so"
# /home/weishuo/lycium_plusplus/usr/json-glib/aarch64/usr/lib/libjson-glib-1.0.so
Q6: lycium 构建系统提示 “dependency not completed”?
**A:**hpk_build.csv 中缺少依赖库的完成标记。
错误现象:
[ERROR] xdg-desktop-portal depends on json-glib (arm64-v8a) not completed
[ERROR] xdg-desktop-portal depends on pipewire (arm64-v8a) not completed
根本原因:
- lycium 构建系统通过 hpk_build.csv 跟踪依赖状态
- 新添加的依赖库未标记为已完成
解决方案: 手动添加完成标记
# 编辑 hpk_build.csv
vim ${LYCIUM_ROOT}/lycium/script/hpk_build.csv
# 添加以下行(tab 分隔)
json-glib arm64-v8a ✓ gdk-pixbuf arm64-v8a ✓
gstreamer arm64-v8a ✓ pipewire arm64-v8a ✓
CSV** 格式说明:**
包名<TAB>架构<TAB>状态
json-glib<TAB>arm64-v8a<TAB>✓
Q7: Meson 提示 “wrap-file download failed”?
**A:**网络问题或 wrap-file 配置错误。
错误现象:
Downloading libglnx source...
ERROR: Wrap file not found or download failed
解决方案 1: 使用 --wrap-mode=nodownload
meson setup ohos-build \
--wrap-mode=nodownload \
...
解决方案 2: 手动下载并配置 wrap-file
# 在 subprojects/ 目录创建 wrap 文件
cat > subprojects/libglnx.wrap << EOF
[wrap-file]
directory = libglnx-1.0
source_url = https://github.com/lfkdsk/libglnx/archive/refs/tags/1.0.tar.gz
source_filename = libglnx-1.0.tar.gz
source_hash = xxxxxx
EOF
# 手动下载源码
cd subprojects/
wget https://github.com/lfkdsk/libglnx/archive/refs/tags/1.0.tar.gz
tar xf 1.0.tar.gz
Q8: 编译时找不到头文件(如 glib.h: No such file)?
**A:**依赖库的头文件未正确安装或 pkg-config 路径错误。
错误现象:
../src/portal-impl.c:24:10: fatal error: glib.h: No such file or directory
24 | #include <glib.h>
| ^~~~~~~~
排查步骤:
# 1. 查找头文件位置
find ${LYCIUM_ROOT}/usr -name "glib.h"
# /home/weishuo/lycium_plusplus/usr/glib/aarch64/include/glib-2.0/glib.h
# 2. 检查 pkg-config 输出
pkg-config --cflags glib-2.0
# -I/home/weishuo/lycium_plusplus/usr/glib/aarch64/include/glib-2.0
# 3. 如果头文件缺失,重新编译依赖库
# 确保 HPKBUILD 的 package() 中包含:
install -d ${PREFIX}/include/glib-2.0
cp -r glib/*.h ${PREFIX}/include/glib-2.0/
常见头文件路径:
glib: ${LYCIUM_ROOT}/usr/glib/${ARCH}/include/glib-2.0/
json-glib: ${LYCIUM_ROOT}/usr/json-glib/${ARCH}/usr/include/json-glib-1.0/
fuse3: ${LYCIUM_ROOT}/usr/fuse3/${ARCH}/include/
七、技术总结
xdg-desktop-portal 适配至 OpenHarmony 平台,形成了一套可复用的 Meson 类项目鸿蒙移植范式
- 标准化适配模板:建立 Meson 项目的 HPKBUILD 构建规范,统一交叉编译、依赖管理与打包流程
- 复杂依赖链治理:通过精准配置 pkg-config 路径,解决 glib、fuse3、pipewire、json-glib 等多层依赖查找与链接问题
- 关键平台问题解决:注入 $ORIGIN 相对运行路径、规避权限限制、修复动态库加载与解压权限问题
- 产物规范化:配套 hnp.json 安装配置、README.OpenSource 开源声明、HPKCHECK 自动化校验,实现合规与可验证交付
- 通用迁移能力:方案可直接复用于 Linux 桌面服务、系统工具、中间件等同类 Meson 项目的鸿蒙化移植
八、结语
本次实践基于 lycium_plusplus 框架,完成 Linux 桌面门户服务 xdg-desktop-portal 1.21.2 到 OpenHarmony(arm64)的完整迁移。通过配置 Meson 交叉编译环境、统一管理 glib、fuse3、pipewire 等多层依赖、注入 $ORIGIN 运行时路径、规避鸿蒙权限与解压问题,成功编译出可部署的二进制程序、依赖库及 HNP 安装包。整套方案形成了 Meson 类 C 项目鸿蒙化迁移的标准化流程,验证了复杂 Linux 桌面组件在鸿蒙平台的交叉编译可行性,同时解决了依赖查找、权限限制、动态库加载等核心适配难题。
提示:本文基于 xdg-desktop-portal 1.21.2 版本进行适配。不同版本的依赖项、构建配置及接口实现可能存在差异,建议在适配前先熟悉项目的 meson.build 构建文件与依赖声明规则。
更多推荐




所有评论(0)