摘要:本文详细介绍了如何将 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 构建文件与依赖声明规则。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐