摘要:本文以 xdg-desktop-portal 桌面门户守护进程为例,深入讲解如何在 OpenHarmony 平台上适配具有复杂多层依赖树的 C/C++ 项目。文章将按照依赖层次,从底层基础库到顶层应用,逐一解析每个依赖库的 HPKBUILD 配置、pkg-config 路径管理、rpath 配置等关键技术点。

本文是软件鸿蒙化迁移实践系列文章之一,专注于多层依赖树的系统化适配方法。

欢迎加入开源鸿蒙PC社区:https://harmonypc.csdn.net/

欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper

AtomGit 仓库地址:https://atomgit.com/OpenHarmonyPCDeveloper/ohos_xdg_desktop_portal_deps

项目信息说明

项目 说明
目标库 xdg-desktop-portal (桌面门户守护进程)
开源协议 LGPL-2.0-or-later
源码版本 1.21.2
目标平台 鸿蒙 PC
构建系统 Meson + Ninja
操作系统平台 WSL Ubuntu 24.04

一、多层依赖树结构解析

1.0 为什么需要理解依赖树

在 C/C++ 项目中,多层依赖树是最常见的架构模式。理解依赖树的重要性在于:

  • 构建顺序决定成败:必须自底向上构建,否则依赖库找不到
  • pkg-config 路径传递:每个库的 pkgconfig 路径必须正确传递给上层
  • 动态库加载问题:rpath 配置不当会导致运行时 symbol not found
  • 权限问题规避:鸿蒙系统文件权限限制需要特殊目录结构

1.1 xdg-desktop-portal 的完整依赖树

xdg-desktop-portal 的依赖展现了典型的多层依赖结构,共分为 2 层:

目标库:xdg-desktop-portal (桌面门户守护进程)
├── 直接依赖: glib, fuse3, json-glib, gdk-pixbuf, gstreamer, pipewire (6)
│
直接依赖的子依赖:
├── glib (基础工具库)
│   └── 依赖: libffi, pcre2
├── fuse3 (FUSE 文件系统)
│   └── 无额外依赖
├── json-glib (JSON 处理)
│   └── 依赖: glib
├── gdk-pixbuf (图像加载)
│   └── 依赖: glib, libpng
├── gstreamer (多媒体框架)
│   └── 依赖: glib
└── pipewire (音视频服务)
    └── 依赖: glib
│
基础依赖库:
├── libffi (外部函数接口)
├── pcre2 (正则表达式)
└── libpng (PNG 图像)

1.2 构建顺序原则

核心原则:自底向上,逐层构建

1 步:基础依赖库(libffi, pcre2, libpng)
              ↓
第 2 步:直接依赖库(glib, fuse3, json-glib, gdk-pixbuf, gstreamer, pipewire)
              ↓
第 3 步:xdg-desktop-portal (目标库)

1.3 依赖关系说明

依赖库 作用 依赖类型
glib 基础工具库(事件循环、数据结构) 直接依赖
fuse3 FUSE 文件系统(xdg-document-portal 必需) 直接依赖
json-glib JSON 解析(配置文件处理) 直接依赖
gdk-pixbuf 图像加载(截屏功能) 直接依赖
gstreamer 多媒体框架(屏幕录制) 直接依赖
pipewire 音视频服务(流媒体传输) 直接依赖

二、基础依赖库

第 1 层基础依赖库是整个依赖树的最底层,它们不依赖其他库(或仅依赖系统库),但被上层库广泛使用。在 xdg-desktop-portal 的适配过程中,主要涉及以下 3 个基础依赖库:

2.1 libffi - 外部函数接口库

作用说明:libffi(Foreign Function Interface)提供了一个便携式接口,允许 C/C++ 代码在运行时动态调用其他编程语言编写的函数。

依赖关系:无依赖(最底层)

被依赖情况:glib 依赖 libffi,用于实现动态类型系统(GType)和信号机制(GSignal)。

关键技术点

  • 使用 configure + make 构建系统(非 Meson)
  • 通过 --disable-multi-os-directory 避免创建多余的 OS 目录
  • 在鸿蒙 arm64-v8a 平台上使用 aarch64-linux-ohos 作为 host
  • 标准 configure 流程,无需特殊配置

2.2 pcre2 - 正则表达式库

作用说明:PCRE2(Perl Compatible Regular Expressions 2)是一个高性能的正则表达式库,提供与 Perl 5 兼容的正则表达式功能。

依赖关系:无依赖(最底层)

被依赖情况:glib 依赖 pcre2,用于 GRegex 模块提供正则表达式支持。

关键技术点

  • 启用 UTF-8/UTF-16/UTF-32 支持(--enable-utf
  • 启用 Unicode 属性支持(--enable-unicode-properties
  • glib 的字符串处理(GString)和模式匹配功能依赖 pcre2
  • 标准 configure 流程,无需特殊配置

2.3 libpng - PNG 图像处理库

作用说明:libpng 是官方 PNG(Portable Network Graphics)图像格式的参考库,提供 PNG 图像的读写功能。

依赖关系:依赖 zlib(压缩库)

被依赖情况:gdk-pixbuf 依赖 libpng,用于加载 PNG 格式的图像文件。

关键技术点

  • 依赖 zlib 提供压缩/解压缩功能
  • 在 configure 阶段需要显式设置 C 预处理器(export CPP),避免 configure 自动检测使用 C++ 编译器
  • xdg-desktop-portal 的截屏功能通过 gdk-pixbuf 间接依赖 libpng
  • 标准 configure 流程,需正确处理 CPP 环境变量

2.4 基础依赖库构建顺序

由于这 3 个库都位于依赖树的最底层,它们之间没有依赖关系,可以并行构建:

# 第 1 步:构建所有基础依赖库(无顺序要求)
cd lycium_plusplus/lycium
./build.sh libffi
./build.sh pcre2
./build.sh libpng  # 会自动依赖 zlib

注意事项

  • libffi 和 pcre2 是完全独立的库,没有任何依赖
  • libpng 依赖 zlib,但 zlib 通常已经预装在系统中或由 lycium 框架提供
  • 这 3 个库必须在 glib 之前构建完成
  • 构建产物安装在 ${LYCIUM_ROOT}/usr/<库名>/${ARCH}/ 目录下

三、直接依赖库适配

3.1 glib - 基础工具库(核心依赖)

依赖关系:依赖 libffi, pcre2

HPKBUILD 完整配置

# Copyright (c) 2023 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.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Contributor: 城meto <myxuan475@126.com>
# Maintainer: 城meto <myxuan475@126.com>

pkgname=glib
pkgver=2.77.1
pkgrel=0
pkgdesc="GLib is a general-purpose, portable utility library, which provides many useful data types, macros, type conversions, string utilities, file utilities, a mainloop abstraction, and so on."
url="https://docs.gtk.org/glib/"
archs=("armeabi-v7a" "arm64-v8a")
license=("LGPL-2.1-or-later")
depends=("libffi" "pcre2")
makedepends=("meson" "ninja") 

# 原仓位置: https://github.com/GNOME/${pkgname}/archive/refs/tags/$pkgver.tar.gz, 因网络原因使用镜像
source="https://gitee.com/mirrors/$pkgname/repository/archive/$pkgver.zip"

downloadpackage=true
autounpack=true
buildtools="meson"

builddir=$pkgname-$pkgver
packagename=$pkgname-$pkgver.zip
pkgconfigpath=
clonesubmodule=true

prepare() {
    # 下载子模块
    if $clonesubmodule
    then
        # 下载gvdb,因网络原因使用镜像, 原仓地址: https://github.com/GNOME/gvdb.git
        git clone https://gitee.com/lycium_pkg_mirror/gvdb.git $builddir/subprojects/gvdb
        if [ $? -ne 0 ];then
            return -1
        fi
        cd $builddir/subprojects/gvdb
        git checkout --detach 0854af0fdb6d527a8d1999835ac2c5059976c210
        cd $OLDPWD
        # 下载 libintl, meson 下载会失败, 原仓地址: https://github.com/frida/proxy-libintl.git 
        git  clone --depth 1 --branch 0.4 https://gitee.com/lycium_pkg_mirror/proxy-libintl.git $builddir/subprojects/proxy-libintl
        if [ $? -ne 0 ];then
            return -2
        fi
        clonesubmodule=false
    fi

    # 依赖库加入 pkg_config_path 路径
    for depend in ${depends[@]}
    do
        dependpath=$LYCIUM_ROOT/usr/$depend/$ARCH/lib/pkgconfig
        if [ ! -d ${dependpath} ]
        then
            continue
        fi
        pkgconfigpath=$pkgconfigpath"${dependpath}:"
    done
    pkgconfigpath=${pkgconfigpath%:*}

    cp $ARCH-cross-file.txt $builddir
    mkdir -p $builddir/$ARCH-build
}

build() {
    cd $builddir
    ohos_sdk_path=${OHOS_SDK//\//\\\/}
    sed -i 's/ohos_sdk/'"$ohos_sdk_path"'/g' $ARCH-cross-file.txt

    # 需要设置pkg路径
    meson setup $ARCH-build --cross-file $ARCH-cross-file.txt \
        --pkg-config-path=$pkgconfigpath \
        --prefix=$LYCIUM_ROOT/usr/$pkgname/$ARCH > $ARCH-build/build.log 2>&1
    ninja -C $ARCH-build -v >> $ARCH-build/build.log 2>&1
    ret=$?
    cd $OLDPWD
    return $ret
}

package() {
    cd $builddir
    ninja -v -C $ARCH-build install >> build.log 2>&1
    # 还原
    unset pkgconfigpath
    cd $OLDPWD
}

check() {
    echo "The test must be on an OpenHarmony device!"
    # TODO
}

# 清理环境
cleanbuild() {
    rm -rf ${PWD}/$builddir
}

# 打包产物
archive() {
    export HNP_TOOL="${HNP_TOOL:-${OHOS_SDK}/toolchains/hnpcli}"

    mkdir -p ${LYCIUM_ROOT}/output/$ARCH

    # 打包 tar.gz
    pushd ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH} > /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 ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}/
        ${HNP_TOOL} pack \
            -i ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH} \
            -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
}

关键技术点

  • depends=(“libffi” “pcre2”) 声明依赖关系
  • 创建 ohos-cross.ini 交叉编译配置文件
  • pkg_config_path 指定 libffi 和 pcre2 的 pkgconfig 路径
  • -Dlibmount=disabled 禁用 libmount(鸿蒙不需要)
  • -Dtests=false 禁用测试加速构建
  • glib 是 xdg-desktop-portal 的核心依赖,提供事件循环、数据结构等基础功能

构建命令

# 必须先构建依赖
cd lycium_plusplus/lycium
./build.sh libffi
./build.sh pcre2

# 然后构建 glib
./build.sh glib

3.2 fuse3 - FUSE 文件系统库

依赖关系:无依赖(独立构建)

HPKBUILD 完整配置

#!/bin/bash

# -----------------------------------------------------------------------------
# fuse3 HPKBUILD - OpenHarmony 鸿蒙适配
# -----------------------------------------------------------------------------

pkgname=fuse3
pkgver=3.16.2
pkgrel=0
pkgdesc="Filesystem in Userspace (FUSE) library"
url="https://github.com/libfuse/libfuse"
archs=("arm64-v8a")
license=("LGPL-2.1-or-later")
depends=()
makedepends=("meson" "ninja")
autounpack=false
downloadpackage=false
buildtools="meson"
srcpath="${LYCIUM_ROOT}/../Projects/libfuse"
builddir="libfuse-${pkgver}"

# -----------------------------------------------------------------------------
# prepare():准备源码
# -----------------------------------------------------------------------------
prepare() {
    if [ -d "$srcpath" ]; then
        echo "Using local source from: $srcpath"
        mkdir -p "$builddir"
        cp -rf "$srcpath"/* "$builddir/"
        
        # 应用 musl libc 兼容性修复:注释掉 pthread_cancel 和 pthread_setcancelstate 调用
        echo "应用 musl libc pthread_cancel 兼容性修复..."
        cd "$builddir"
        # fuse_loop_mt.c: 注释掉 pthread_setcancelstate 和 pthread_cancel
        sed -i '141s/^\(\s*\)pthread_setcancelstate(PTHREAD_CANCEL_ENABLE/\1\/\/ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE/' lib/fuse_loop_mt.c
        sed -i '143s/^\(\s*\)pthread_setcancelstate(PTHREAD_CANCEL_DISABLE/\1\/\/ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE/' lib/fuse_loop_mt.c
        sed -i '140s/^$/\/\/ OpenHarmony musl libc does not support pthread cancellation/' lib/fuse_loop_mt.c
        sed -i 's/^\(\s*\)pthread_cancel(w->thread_id);/\1\/\/ pthread_cancel(w->thread_id); \/\/ Disabled for musl compat/' lib/fuse_loop_mt.c
        # fuse.c: 注释掉 pthread_cancel
        sed -i 's/^\(\s*\)pthread_cancel(f->prune_thread);/\1\/\/ pthread_cancel(f->prune_thread); \/\/ Disabled for musl compat/' lib/fuse.c
        # fuse_uring.c: 注释掉 pthread_cancel
        sed -i 's/^\(\s*\)pthread_cancel(queue->tid);/\1\/\/ pthread_cancel(queue->tid); \/\/ Disabled for musl compat/' lib/fuse_uring.c
        cd - > /dev/null
        echo "[OK] musl 兼容性修复完成"
        
        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"

    # 设置 buildlog
    buildlog="${LYCIUM_ROOT}/log/${pkgname}-build.log"
    mkdir -p "${LYCIUM_ROOT}/log"

    # 设置交叉编译环境变量
    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 NM="${OHOS_SDK}/native/llvm/bin/llvm-nm"

    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 -std=c++17"
    export LDFLAGS="--target=aarch64-linux-ohos --sysroot=${OHOS_SDK}/native/sysroot -Wl,--unresolved-symbols=ignore-in-shared-libs"

    # 创建 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]

[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', '-std=c++17']
c_link_args = ['--target=aarch64-linux-ohos', '--sysroot=${OHOS_SDK}/native/sysroot', '-lpthread', '-Wl,--allow-shlib-undefined']
cpp_link_args = ['--target=aarch64-linux-ohos', '--sysroot=${OHOS_SDK}/native/sysroot', '-lpthread', '-Wl,--allow-shlib-undefined']
b_lundef = false

[host_machine]
system = 'linux'
cpu_family = 'aarch64'
cpu = 'aarch64'
endian = 'little'
EOF

    # Meson 配置
    meson setup ohos-build \
        --cross-file ohos-cross.ini \
        --prefix=/usr \
        --buildtype=release \
        -Duseroot=false \
        -Ddisable-mtab=true \
        -Dtests=false \
        -Dutils=false \
        -Dexamples=false \
        -Ddisable-libc-symbol-version=true \
        -Dudevrulesdir=/lib/udev/rules.d \
        > "$buildlog" 2>&1

    ret=$?
    if [ $ret -ne 0 ]; then
        echo "Meson configuration failed!"
        cat "$buildlog" >&2
        cd "$OLDPWD"
        return $ret
    fi

    # 编译
    ninja -C ohos-build >> "$buildlog" 2>&1
    ret=$?

    cd "$OLDPWD"
    return $ret
}

# -----------------------------------------------------------------------------
# check():验证构建产物
# -----------------------------------------------------------------------------
check() {
    echo "The test must be on an OpenHarmony device!"
}

# -----------------------------------------------------------------------------
# package():打包产物
# -----------------------------------------------------------------------------
package() {
    : ${destdir:=${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}}

    # 进入源码目录
    cd ${builddir}

    # 使用 ninja install 安装到 destdir
    DESTDIR="${destdir}" ninja -C ohos-build install >> "$buildlog" 2>&1
    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

    # 打包 tar.gz
    pushd ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH} > /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 ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}/
        ${HNP_TOOL} pack \
            -i ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH} \
            -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
}

# -----------------------------------------------------------------------------
# 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"
}

关键技术点

  • patch -p1 < …/0001-fix-fuse3-pthread-cancel-musl-compat.patch 应用 musl 兼容补丁
  • -Duseroot=false 禁用 root 权限(鸿蒙不需要)
  • -Dutils=false 禁用工具程序(只需要库)
  • fuse3 是 xdg-document-portal 的核心依赖,提供文件访问代理功能
  • musl libc 兼容补丁修复 pthread_cancel 问题

构建命令

cd lycium_plusplus/lycium
./build.sh fuse3

musl 兼容补丁内容

--- a/include/fuse_common.h
+++ b/include/fuse_common.h
@@ -1,6 +1,8 @@
 #ifndef FUSE_COMMON_H_
 #define FUSE_COMMON_H_
 
+#include <pthread.h>
+
 // musl libc 不实现 pthread_cancel
 // 需要提供兼容实现
 #ifdef __OHOS__
 #define pthread_cancel(tid) ENOSYS
 #endif

4.1 json-glib - JSON 处理库

依赖关系:依赖 glib

HPKBUILD 完整配置

#!/bin/bash

# -----------------------------------------------------------------------------
# json-glib HPKBUILD - OpenHarmony 鸿蒙适配
# -----------------------------------------------------------------------------

pkgname=json-glib
pkgver=1.8.0
pkgrel=0
pkgdesc="GLib JSON manipulation library"
url="https://gitlab.gnome.org/GNOME/json-glib"
archs=("arm64-v8a")
license=("LGPL-2.1-or-later")
depends=("glib")
makedepends=("meson" "ninja")
autounpack=false
downloadpackage=false
buildtools="meson"
srcpath="${LYCIUM_ROOT}/../Projects/json-glib"
builddir="json-glib-${pkgver}"

# -----------------------------------------------------------------------------
# prepare():准备源码
# -----------------------------------------------------------------------------
prepare() {
    if [ -d "$srcpath" ]; then
        echo "Using local source from: $srcpath"
        mkdir -p "$builddir"
        cp -rf "$srcpath"/* "$builddir/"
        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"

    # 设置 buildlog
    buildlog="${LYCIUM_ROOT}/log/${pkgname}-build.log"
    mkdir -p "${LYCIUM_ROOT}/log"

    # 设置交叉编译环境变量
    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 NM="${OHOS_SDK}/native/llvm/bin/llvm-nm"

    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"

    # 设置 glib 及其依赖的 pkg-config 路径
    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"
    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"

    # 创建 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/pcre2/${ARCH}/lib/pkgconfig', '${LYCIUM_ROOT}/usr/libffi/${ARCH}/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

    # Meson 配置
    echo "=== Debug: PKG_CONFIG_PATH ==="
    echo "PKG_CONFIG_PATH: $PKG_CONFIG_PATH"
    echo "PKG_CONFIG_LIBDIR: $PKG_CONFIG_LIBDIR"
    echo "Testing pkg-config:"
    pkg-config --modversion gio-2.0 || echo "FAILED"
    echo "================================"
    
    meson setup ohos-build \
        --cross-file ohos-cross.ini \
        --prefix=/usr \
        --buildtype=release \
        -Ddocumentation=disabled \
        -Dman=false \
        -Dtests=false \
        -Dintrospection=disabled \
        -Dwrap_mode=nofallback \
        > "$buildlog" 2>&1

    ret=$?
    if [ $ret -ne 0 ]; then
        echo "Meson configuration failed!"
        cat "$buildlog" >&2
        cd "$OLDPWD"
        return $ret
    fi

    # 编译
    ninja -C ohos-build >> "$buildlog" 2>&1
    ret=$?

    cd "$OLDPWD"
    return $ret
}

# -----------------------------------------------------------------------------
# check():验证构建产物
# -----------------------------------------------------------------------------
check() {
    echo "The test must be on an OpenHarmony device!"
}

# -----------------------------------------------------------------------------
# package():打包产物
# -----------------------------------------------------------------------------
package() {
    : ${destdir:=${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}}

    # 进入源码目录
    cd ${builddir}

    # 使用 ninja install 安装到 destdir
    DESTDIR="${destdir}" ninja -C ohos-build install >> "$buildlog" 2>&1
    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

    # 打包 tar.gz
    pushd ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH} > /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 ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}/
        ${HNP_TOOL} pack \
            -i ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH} \
            -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
}

# -----------------------------------------------------------------------------
# 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"
}

关键技术点

  • depends=(“glib”) 声明依赖 glib
  • pkg_config_path 指定 glib 的 pkgconfig 路径
  • -Dintrospection=disabled 禁用 GObject Introspection(鸿蒙不需要)
  • -Dgtk_doc=false 禁用 GTK 文档生成
  • json-glib 为 xdg-desktop-portal 提供 JSON 配置文件解析功能

构建命令

# 必须先构建依赖
cd lycium_plusplus/lycium
./build.sh glib

# 然后构建 json-glib
./build.sh json-glib

4.2 gdk-pixbuf - 图像加载库

依赖关系:依赖 glib, libpng

HPKBUILD 完整配置

#!/bin/bash

# -----------------------------------------------------------------------------
# gdk-pixbuf HPKBUILD - OpenHarmony 鸿蒙适配
# -----------------------------------------------------------------------------

pkgname=gdk-pixbuf
pkgver=2.44.6
pkgrel=0
pkgdesc="Image loading library for GTK+"
url="https://gitlab.gnome.org/GNOME/gdk-pixbuf"
archs=("arm64-v8a")
license=("LGPL-2.1-or-later")
depends=("glib")
makedepends=("meson" "ninja")
autounpack=false
downloadpackage=false
buildtools="meson"
srcpath="${LYCIUM_ROOT}/../Projects/gdk-pixbuf"
builddir="gdk-pixbuf-${pkgver}"

# -----------------------------------------------------------------------------
# prepare():准备源码
# -----------------------------------------------------------------------------
prepare() {
    if [ -d "$srcpath" ]; then
        echo "Using local source from: $srcpath"
        mkdir -p "$builddir"
        cp -rf "$srcpath"/* "$builddir/"
        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"

    # 设置 buildlog
    buildlog="${LYCIUM_ROOT}/log/${pkgname}-build.log"
    mkdir -p "${LYCIUM_ROOT}/log"

    # 设置交叉编译环境变量
    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 NM="${OHOS_SDK}/native/llvm/bin/llvm-nm"

    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"

    # 设置 glib 及其依赖的 pkg-config 路径
    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"
    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"

    # 创建 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/pcre2/${ARCH}/lib/pkgconfig', '${LYCIUM_ROOT}/usr/libffi/${ARCH}/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

    # Meson 配置
    echo "=== Debug: PKG_CONFIG_PATH ==="
    echo "PKG_CONFIG_PATH: $PKG_CONFIG_PATH"
    echo "PKG_CONFIG_LIBDIR: $PKG_CONFIG_LIBDIR"
    echo "Testing pkg-config:"
    pkg-config --modversion gio-2.0 || echo "FAILED"
    echo "================================"
    
    meson setup ohos-build \
        --cross-file ohos-cross.ini \
        --prefix=/usr \
        --buildtype=release \
        -Ddocumentation=false \
        -Dman=false \
        -Dtests=false \
        -Dintrospection=disabled \
        -Drelocatable=false \
        -Dinstalled_tests=false \
        -Dpng=disabled \
        -Djpeg=disabled \
        -Dtiff=disabled \
        -Dgif=disabled \
        -Dglycin=disabled \
        -Dothers=disabled \
        -Dthumbnailer=disabled \
        -Dgio_sniffing=false \
        -Dbuiltin_loaders=all \
        -Dwrap_mode=nofallback \
        > "$buildlog" 2>&1

    ret=$?
    if [ $ret -ne 0 ]; then
        echo "Meson configuration failed!"
        cat "$buildlog" >&2
        cd "$OLDPWD"
        return $ret
    fi

    # 编译
    ninja -C ohos-build >> "$buildlog" 2>&1
    ret=$?

    cd "$OLDPWD"
    return $ret
}

# -----------------------------------------------------------------------------
# check():验证构建产物
# -----------------------------------------------------------------------------
check() {
    echo "The test must be on an OpenHarmony device!"
}

# -----------------------------------------------------------------------------
# package():打包产物
# -----------------------------------------------------------------------------
package() {
    : ${destdir:=${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}}

    # 进入源码目录
    cd ${builddir}

    # 使用 ninja install 安装到 destdir
    DESTDIR="${destdir}" ninja -C ohos-build install >> "$buildlog" 2>&1
    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

    # 打包 tar.gz
    pushd ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH} > /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 ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}/
        ${HNP_TOOL} pack \
            -i ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH} \
            -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
}

# -----------------------------------------------------------------------------
# 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"
}

关键技术点

  • depends=(“glib” “libpng”) 声明依赖
  • pkg_config_path 包含依赖库的路径
  • -Dman=false 禁用 man 手册生成
  • -Dinstalled_tests=false 禁用安装测试
  • gdk-pixbuf 为 xdg-desktop-portal 提供图像加载功能(截屏、缩略图)

构建命令

# 必须先构建所有依赖
cd lycium_plusplus/lycium
./build.sh glib
./build.sh libpng

# 然后构建 gdk-pixbuf
./build.sh gdk-pixbuf

4.3 gstreamer - 多媒体框架

依赖关系:依赖 glib

HPKBUILD 完整配置

#!/bin/bash

# -----------------------------------------------------------------------------
# gstreamer HPKBUILD - OpenHarmony 鸿蒙适配
# -----------------------------------------------------------------------------

pkgname=gstreamer
pkgver=1.29.1.1
pkgrel=0
pkgdesc="GStreamer open-source multimedia framework core library"
url="https://gstreamer.freedesktop.org"
archs=("arm64-v8a")
license=("LGPL-2.1-or-later")
depends=("glib")
makedepends=("meson" "ninja" "python3")
autounpack=false
downloadpackage=false
buildtools="meson"
srcpath="${LYCIUM_ROOT}/../Projects/gstreamer"
builddir="gstreamer-${pkgver}"

# -----------------------------------------------------------------------------
# prepare():准备源码
# -----------------------------------------------------------------------------
prepare() {
    if [ -d "$srcpath" ]; then
        echo "Using local source from: $srcpath"
        mkdir -p "$builddir"
        cp -rf "$srcpath"/* "$builddir/"
        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"

    # 设置 buildlog
    buildlog="${LYCIUM_ROOT}/log/${pkgname}-build.log"
    mkdir -p "${LYCIUM_ROOT}/log"

    # 设置交叉编译环境变量
    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 NM="${OHOS_SDK}/native/llvm/bin/llvm-nm"

    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"

    # 设置 glib 及其依赖的 pkg-config 路径
    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"
    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"

    # 创建 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/pcre2/${ARCH}/lib/pkgconfig', '${LYCIUM_ROOT}/usr/libffi/${ARCH}/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

    # Meson 配置 - 禁用所有插件和额外依赖,只构建核心库
    echo "=== Debug: PKG_CONFIG_PATH ==="
    echo "PKG_CONFIG_PATH: $PKG_CONFIG_PATH"
    echo "PKG_CONFIG_LIBDIR: $PKG_CONFIG_LIBDIR"
    echo "Testing pkg-config:"
    pkg-config --modversion gio-2.0 || echo "FAILED"
    echo "================================"
    
    meson setup ohos-build \
        --cross-file ohos-cross.ini \
        --prefix=/usr \
        --buildtype=release \
        -Ddoc=disabled \
        -Dgtk_doc=disabled \
        -Dtests=disabled \
        -Dexamples=disabled \
        -Dbenchmarks=disabled \
        -Dintrospection=disabled \
        -Dbase=disabled \
        -Dgood=disabled \
        -Dugly=disabled \
        -Dbad=disabled \
        -Dlibav=disabled \
        -Ddevtools=disabled \
        -Dges=disabled \
        -Drtsp_server=disabled \
        -Dgst-examples=disabled \
        -Dpython=disabled \
        -Dsharp=disabled \
        -Dtls=disabled \
        -Dlibnice=disabled \
        -Dgtk=disabled \
        -Dgpl=disabled \
        -Dwrap_mode=nofallback \
        > "$buildlog" 2>&1

    ret=$?
    if [ $ret -ne 0 ]; then
        echo "Meson configuration failed!"
        cat "$buildlog" >&2
        cd "$OLDPWD"
        return $ret
    fi

    # 编译
    ninja -C ohos-build >> "$buildlog" 2>&1
    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

    # 打包 tar.gz
    pushd ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH} > /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 ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}/
        ${HNP_TOOL} pack \
            -i ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH} \
            -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!!!"
}

# -----------------------------------------------------------------------------
# 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"
}

关键技术点

  • depends=(“glib”) 声明依赖关系
  • makedepends=(“meson” “ninja”) 构建时依赖
  • -Dtools=disabled 禁用工具程序(只需要库)
  • -Dexamples=disabled 禁用示例代码
  • -Dbenchmarks=disabled 禁用性能测试
  • gstreamer** 为 xdg-desktop-portal 提供屏幕录制功能的多媒体处理**

构建命令

# 必须先构建依赖
cd lycium_plusplus/lycium
./build.sh glib

# 然后构建 gstreamer
./build.sh gstreamer

4.4 pipewire - 音视频服务

依赖关系:依赖 glib

HPKBUILD 完整配置

#!/bin/bash

# -----------------------------------------------------------------------------
# pipewire HPKBUILD - OpenHarmony 鸿蒙适配
# -----------------------------------------------------------------------------

pkgname=pipewire
pkgver=1.7.0
pkgrel=0
pkgdesc="PipeWire multimedia server and audio/video routing framework"
url="https://pipewire.org"
archs=("arm64-v8a")
license=("MIT" "LGPL-2.1-or-later" "GPL-2.0-only")
depends=("glib")
makedepends=("meson" "ninja" "python3")
autounpack=false
downloadpackage=false
buildtools="meson"
srcpath="${LYCIUM_ROOT}/../Projects/pipewire"
builddir="pipewire-${pkgver}"

# -----------------------------------------------------------------------------
# prepare():准备源码
# -----------------------------------------------------------------------------
prepare() {
    if [ -d "$srcpath" ]; then
        echo "Using local source from: $srcpath"
        mkdir -p "$builddir"
        cp -rf "$srcpath"/* "$builddir/"
        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"

    # 设置 buildlog
    buildlog="${LYCIUM_ROOT}/log/${pkgname}-build.log"
    mkdir -p "${LYCIUM_ROOT}/log"

    # 设置交叉编译环境变量
    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 NM="${OHOS_SDK}/native/llvm/bin/llvm-nm"

    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"

    # 设置 glib 的 pkg-config 路径
    export PKG_CONFIG_PATH="${LYCIUM_ROOT}/usr/glib/${ARCH}/lib/pkgconfig"
    export PKG_CONFIG_LIBDIR="${LYCIUM_ROOT}/usr/glib/${ARCH}/lib/pkgconfig"

    # 创建 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']

[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

    # Meson 配置 - 禁用不必要的功能,只构建核心库
    echo "=== Debug: PKG_CONFIG_PATH ==="
    echo "PKG_CONFIG_PATH: $PKG_CONFIG_PATH"
    echo "PKG_CONFIG_LIBDIR: $PKG_CONFIG_LIBDIR"
    echo "Testing pkg-config:"
    pkg-config --modversion glib-2.0 || echo "FAILED"
    echo "================================"
    
    meson setup ohos-build \
        --cross-file ohos-cross.ini \
        --prefix=/usr \
        --buildtype=release \
        -Ddocs=disabled \
        -Dtests=disabled \
        -Dexamples=disabled \
        -Dman=disabled \
        -Dudev=disabled \
        -Dlibsystemd=disabled \
        -Dlogind=disabled \
        -Dsystemd-system-service=disabled \
        -Dsystemd-user-service=disabled \
        -Dselinux=disabled \
        -Dpipewire-alsa=disabled \
        -Dpipewire-jack=disabled \
        -Dpipewire-v4l2=disabled \
        -Dspa-plugins=disabled \
        -Dalsa=disabled \
        -Dbluez5=disabled \
        -Dcontrol=disabled \
        -Daudiotestsrc=disabled \
        -Dffmpeg=disabled \
        -Djack=disabled \
        -Dsupport=disabled \
        -Dtest=disabled \
        -Dv4l2=disabled \
        -Ddbus=disabled \
        -Dlibcamera=disabled \
        -Dvideoconvert=disabled \
        -Dvideotestsrc=disabled \
        -Dvulkan=disabled \
        -Dpw-cat=disabled \
        -Dsdl2=disabled \
        -Dsndfile=disabled \
        -Dlibpulse=disabled \
        -Droc=disabled \
        -Davahi=disabled \
        -Dlibusb=disabled \
        -Dsession-managers=[] \
        -Draop=disabled \
        -Dlv2=disabled \
        -Dx11=disabled \
        -Dx11-xfixes=disabled \
        -Dlibcanberra=disabled \
        -Dflatpak=disabled \
        -Dreadline=disabled \
        -Dgsettings=disabled \
        -Dcompress-offload=disabled \
        -Dpam-defaults-install=false \
        -Drlimits-install=false \
        -Dopus=disabled \
        -Dgstreamer=disabled \
        -Dgstreamer-device-provider=disabled \
        -Davb=disabled

    ret=$?
    if [ $ret -ne 0 ]; then
        echo "Meson configuration failed!" >&2
        cd "$OLDPWD"
        exit 1
    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

    # 打包 tar.gz
    pushd ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH} > /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 ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}/
        ${HNP_TOOL} pack \
            -i ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH} \
            -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!!!"
}

# -----------------------------------------------------------------------------
# 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"
}

关键技术点

  • depends=(“glib”) 声明依赖
  • pkg_config_path 包含依赖库的路径
  • -Dsession_manager=disabled 禁用会话管理器(鸿蒙不需要)
  • -Dsystemd=disabled 禁用 systemd 集成
  • -Dbluez5=disabled 禁用蓝牙支持
  • -Dv4l2=disabled 禁用视频设备支持
  • pipewire 为 xdg-desktop-portal 提供屏幕录制的流媒体传输功能

构建命令

# 必须先构建所有依赖
cd lycium_plusplus/lycium
./build.sh glib

# 然后构建 pipewire
./build.sh pipewire

依赖关系总结

库名 依赖 被依赖
glib libffi, pcre2 json-glib, gdk-pixbuf, gstreamer, pipewire, xdg-desktop-portal
fuse3 xdg-desktop-portal
json-glib glib xdg-desktop-portal
gdk-pixbuf glib, libpng xdg-desktop-portal
gstreamer glib xdg-desktop-portal
pipewire glib xdg-desktop-portal

四、目标库适配:xdg-desktop-portal

4.1 xdg-desktop-portal - 桌面门户守护进程(目标库)

依赖关系:依赖 glib, fuse3, json-glib, gdk-pixbuf, gstreamer, pipewire

HPKBUILD 完整配置

#!/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 "[OK] fuse3 patch 应用成功"
                cd - > /dev/null
            else
                echo "[WARN]  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 "[OK] 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"
}

关键技术点

  • depends=(“glib” “fuse3” “json-glib” “gdk-pixbuf” “gstreamer” “pipewire”) 声明 6 个依赖
  • pkg_config_path 包含 6 个依赖库的路径
  • ohos-native.ini 配置 build machine 工具(glib-compile-resources 等)
  • ohos-cross.ini 配置 host machine 交叉编译工具链
  • -Dc_link_args=“-Wl,-rpath,$ORIGIN” 设置 rpath 为相对路径
  • archive() 函数将所有依赖库复制到 libexec/ 目录
  • libexec/ 目录规避鸿蒙文件权限问题
  • rpath $ORIGIN 确保动态库在任意路径下都能加载

构建命令

# 必须先构建所有依赖
cd lycium_plusplus/lycium
./build.sh glib
./build.sh fuse3
./build.sh json-glib
./build.sh gdk-pixbuf
./build.sh gstreamer
./build.sh pipewire

# 然后构建 xdg-desktop-portal
./build.sh xdg-desktop-portal

4.2 鸿蒙 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

五、交叉编译环境下 pkg-config 路径治理与依赖查找机制

5.1 pkg-config 路径传递与依赖查找链

pkg-config 在多层依赖中的作用

pkg-config 是 C/C++ 项目中管理编译和链接flags的标准工具。

每个库安装时会生成 .pc 文件,包含:
- 头文件路径(Cflags)
- 库文件路径(Libs)
- 依赖关系(Requires)

在交叉编译环境中,必须显式指定 pkg-config 路径。

路径配置示例

# xdg-desktop-portal 的 pkg_config_path 配置
pkg_config_path = [
    '${LYCIUM_ROOT}/usr/glib/${ARCH}/lib/pkgconfig',          # glib
    '${LYCIUM_ROOT}/usr/fuse3/${ARCH}/usr/lib/pkgconfig',     # fuse3
    '${LYCIUM_ROOT}/usr/json-glib/${ARCH}/usr/lib/pkgconfig', # json-glib
    '${LYCIUM_ROOT}/usr/gdk-pixbuf/${ARCH}/usr/lib/pkgconfig',# gdk-pixbuf
    '${LYCIUM_ROOT}/usr/gstreamer/${ARCH}/usr/lib/pkgconfig', # gstreamer
    '${LYCIUM_ROOT}/usr/pipewire/${ARCH}/usr/lib/pkgconfig'   # pipewire
]

路径差异说明

库名 pkgconfig 路径 原因
glib ${ARCH}/lib/pkgconfig 标准安装路径
fuse3 ${ARCH}/usr/lib/pkgconfig 安装在 /usr 前缀
json-glib ${ARCH}/usr/lib/pkgconfig 安装在 /usr 前缀
gdk-pixbuf ${ARCH}/usr/lib/pkgconfig 安装在 /usr 前缀
gstreamer ${ARCH}/usr/lib/pkgconfig 安装在 /usr 前缀
pipewire ${ARCH}/usr/lib/pkgconfig 安装在 /usr 前缀

5.2 路径配置陷阱与典型排障案例

问题 1:Meson 找不到依赖库

错误现象

Run-time dependency glib-2.0 found: NO (tried pkgconfig and cmake)

meson.build:50:0: ERROR: Dependency "glib-2.0" not found

解决方案

# 检查 pkg-config 路径是否正确
pkg-config --modversion glib-2.0

# 如果找不到,检查 .pc 文件是否存在
find ${LYCIUM_ROOT}/usr/glib -name "glib-2.0.pc"

# 确认 pkg_config_path 配置
# 在 ohos-cross.ini 中检查路径是否正确
问题 2:依赖库路径不一致

错误现象

cp: cannot stat '/home/weishuo/lycium_plusplus/usr/json-glib/aarch64/lib/libjson-glib-1.0.so': 
No such file or directory

解决方案

# 查找实际的 .so 文件位置
find ${LYCIUM_ROOT}/usr/json-glib -name "*.so"
# 输出:/home/weishuo/lycium_plusplus/usr/json-glib/aarch64/usr/lib/libjson-glib-1.0.so

# 注意:有些库安装在 lib/,有些安装在 usr/lib/
# 需要在 HPKBUILD 中使用正确的路径
问题 3:glib 工具版本不匹配

错误现象

meson setup: error: Unable to detect the version of glib-compile-resources

解决方案

# 使用 build machine 的 glib 工具(x86_64 版本)
# 在 ohos-native.ini 中配置:
[binaries]
glib-compile-resources = '/usr/bin/glib-compile-resources'
glib-mkenums = '/usr/bin/glib-mkenums'
gdbus-codegen = '/usr/bin/gdbus-codegen'

# 或者创建符号链接
ln -sf /usr/bin/glib-compile-resources ${LYCIUM_ROOT}/usr/glib/${ARCH}/bin/glib-compile-resources
问题 4:pkg-config 路径顺序错误

错误现象

# 找到了错误版本的库
pkg-config --modversion glib-2.0
# 输出:2.70.0 (应该是 2.78.0)

解决方案

# pkg-config 按照路径顺序查找,先找到的优先
# 确保自定义路径在系统路径之前

export PKG_CONFIG_PATH="${LYCIUM_ROOT}/usr/glib/${ARCH}/lib/pkgconfig:${PKG_CONFIG_PATH}"

# 验证
pkg-config --modversion glib-2.0
# 应该输出:2.78.0

pkg-config 路径管理总结

配置项 作用 示例
pkg_config_path Meson 交叉编译配置中的路径 ['${LYCIUM_ROOT}/usr/glib/${ARCH}/lib/pkgconfig']
PKG_CONFIG_PATH 环境变量,追加到默认路径 ${LYCIUM_ROOT}/usr/glib/${ARCH}/lib/pkgconfig:${PKG_CONFIG_PATH}
PKG_CONFIG_LIBDIR 环境变量,替换默认路径 ${LYCIUM_ROOT}/usr/glib/${ARCH}/lib/pkgconfig
pkg-config --list-all 列出所有可用的包 验证配置是否正确

六、ELF 运行时路径(rpath)配置与 musl 动态链接器行为分析

6.1 $ORIGIN 相对路径机制与动态库加载原理

什么是 rpath?

rpath (Run-time Path) 是嵌入在 ELF 二进制文件中的运行时库搜索路径。
当程序启动时,动态链接器会按照以下顺序查找依赖库:

1. DT_RPATH/DT_RUNPATH(rpath)
2. LD_LIBRARY_PATH 环境变量
3. /etc/ld.so.cache 缓存
4. 默认系统路径(/lib, /usr/lib)

rpath 的两种类型

类型 特点 优先级
DT_RPATH 旧标准,会被 LD_LIBRARY_PATH 覆盖
DT_RUNPATH 新标准,优先于 LD_LIBRARY_PATH

xdg-desktop-portal 中的 rpath 配置

# HPKBUILD 中的配置
meson setup ohos-build \
    -Dc_link_args="-Wl,-rpath,\$ORIGIN"

# 说明:
# -Wl 表示传递参数给链接器
# -rpath 设置运行时库路径
# \$ORIGIN 表示二进制文件所在目录
# 需要转义 $ 符号,避免 shell 提前展开

验证 rpath 是否生效

# 使用 readelf 查看
$ readelf -d usr/libexec/xdg-desktop-portal | grep RUNPATH
 0x000000000000001d (RUNPATH) Library runpath: [$ORIGIN]

# 使用 objdump 查看
$ objdump -x usr/libexec/xdg-desktop-portal | grep RUNPATH
RUNPATH              $ORIGIN

$ORIGIN 的优势

$ORIGIN 是相对路径,指向二进制文件所在目录。

优势:
1. 可移植性:无论解压到哪个目录,都能找到依赖库
2. 独立性:不依赖系统路径,避免库冲突
3. 沙箱友好:适合容器化部署

示例:
二进制文件:/opt/app/usr/libexec/xdg-desktop-portal
依赖库:    /opt/app/usr/libexec/libglib-2.0.so.0

$ORIGIN 解析为:/opt/app/usr/libexec/
动态链接器会在该目录下查找 libglib-2.0.so.0

6.2 鸿蒙文件系统权限约束与 libexec/ 目录规避方案

鸿蒙文件系统权限限制

鸿蒙系统在解压 tar.gz 包时,会强制修改文件权限:

标准 Linux:
-rwxr-xr-x (755) 正常

鸿蒙系统:
-rwxr-x--- (770) 权限被强制修改

这导致:
1. 其他用户无法执行二进制文件
2. 动态链接器无法加载 .so 文件
3. 运行时出现 "Permission denied" 错误

问题复现

# 在鸿蒙设备上解压
$ tar xf xdg-desktop-portal_1.21.2-with-deps.tar.gz

# 检查权限
$ ls -l usr/libexec/*.so*
-rwxr-x--- 1 user group 770 libglib-2.0.so.0  # 错误

# 尝试运行
$ ./usr/libexec/xdg-desktop-portal
bash: ./usr/libexec/xdg-desktop-portal: Permission denied

解决方案 1:使用 Python tarfile 模块

# Python tarfile 不会修改权限
python3 -c "import tarfile; tarfile.open('xdg-desktop-portal_1.21.2-with-deps.tar.gz').extractall()"

# 检查权限
$ ls -l usr/libexec/*.so*
-rwxr-xr-x 1 user group 755 libglib-2.0.so.0  # 正确

解决方案 2:自动化修复脚本

#!/bin/bash
# extract.sh - 鸿蒙设备上的解压脚本

if [ -z "$1" ]; then
    echo "Usage: $0 <tar.gz file>"
    exit 1
fi

echo "Extracting $1..."
python3 -c "import tarfile; tarfile.open('$1').extractall()"

echo "Fixing permissions..."
find usr/libexec/ -type f -exec chmod 755 {} \;

echo "Verifying permissions..."
fail=0
for f in usr/libexec/*.so* usr/libexec/xdg-*; do
    perm=$(stat -c "%a" "$f" 2>/dev/null || stat -f "%Lp" "$f" 2>/dev/null)
    if [ "$perm" != "755" ]; then
        echo "[FAIL] $f has permission $perm"
        fail=1
    fi
done

if [ $fail -eq 0 ]; then
    echo "[OK] All files have correct permissions (755)"
fi

解决方案 3:使用 libexec/ 目录

# HPKBUILD archive() 函数中的策略
archive() {
    # 将依赖库复制到 libexec/ 目录
    cp -L "${glib_dir}/lib/libglib-2.0.so"* "${tmpdir}/usr/libexec/"
    
    # libexec/ 目录在 HPK 包中保持 755 权限
    # 而 /lib 和 /usr/lib 会被修改为 770
    
    # 修复权限
    chmod 755 "${tmpdir}/usr/libexec/"*.so*
}

为什么 libexec/ 目录有效?

HPK 包的目录结构权限规则:

/usr/lib/       → 解压后 770(错误)
/usr/libexec/   → 解压后 755(正确)
/usr/bin/       → 解压后 755(正确)

原因:
鸿蒙系统对某些目录有特殊权限管控,
libexec/ 目录不在限制范围内,
因此可以保持正确的 755 权限。

完整的权限修复流程

# 1. 解压(使用 Python)
python3 -c "import tarfile; tarfile.open('xdg-desktop-portal_1.21.2-with-deps.tar.gz').extractall()"

# 2. 修复权限
chmod 755 usr/libexec/*.so*
chmod 755 usr/libexec/xdg-*

# 3. 验证
$ ls -l usr/libexec/xdg-desktop-portal
-rwxr-xr-x 1 user group 755 xdg-desktop-portal

$ ls -l usr/libexec/libglib-2.0.so.0
-rwxr-xr-x 1 user group 755 libglib-2.0.so.0

# 4. 测试运行(如果系统支持)
$ ./usr/libexec/xdg-desktop-portal --version

七、多层依赖构建自动化与产物验证体系

7.1 自底向上构建流程编排与依赖编排脚本

多层依赖树适配的关键技术

技术点 解决方案 关键配置
构建顺序 自底向上,逐层构建 2 层 9 个库
pkg-config 路径 显式配置每个依赖的路径 pkg_config_path 数组
交叉编译 Meson cross file ohos-cross.ini
rpath 配置 $ORIGIN 相对路径 -Dc_link_args=“-Wl,-rpath,$ORIGIN”
权限问题 libexec/ 目录 + Python 解压 chmod 755
依赖打包 archive() 函数 复制所有 .so 到 libexec/
glib 工具 build machine 工具 ohos-native.ini
musl 兼容 pthread_cancel 补丁 patch -p1

适配难点总结

  1. 多层依赖管理
    1. 6 个直接依赖库,2 层结构
    2. 构建顺序必须正确
    3. pkg-config 路径必须完整
  2. Meson 构建系统
    1. 交叉编译配置复杂
    2. 需要双配置文件(native + cross)
    3. glib 工具版本匹配问题
  3. 动态库加载
    1. rpath 配置容易遗漏
    2. 权限问题导致加载失败
    3. LD_LIBRARY_PATH 无效
  4. 鸿蒙系统限制
    1. tar 解压权限被强制修改
    2. musl libc 不支持 pthread_cancel
    3. D-Bus/FUSE 支持有限

7.2 适配踩坑记录与典型问题汇总

Q1: Meson 找不到依赖库?

A: 检查 pkg-config 路径配置:

pkg-config --modversion glib-2.0
find ${LYCIUM_ROOT}/usr/glib -name "glib-2.0.pc

Q2: 编译时找不到头文件?

A: 检查依赖库的头文件安装路径:

find ${LYCIUM_ROOT}/usr -name "glib.h"
pkg-config --cflags glib-2.0

Q3: 运行时 symbol not found?

A: rpath 配置错误,使用 $ORIGIN 相对路径:

-Dc_link_args="-Wl,-rpath,\$ORIGIN"

Q4: 解压后权限变成 770?

A: 使用 Python tarfile 模块:

python3 -c "import tarfile; tarfile.open('file.tar.gz').extractall()"
chmod 755 usr/libexec/*.so*

Q5: 依赖库路径不一致?

A: 有些在 lib/,有些在 usr/lib/:

find ${LYCIUM_ROOT}/usr/json-glib -name "*.so"
# 确认实际路径后再配置

Q6: glib 工具版本不匹配?

A: 使用 build machine 工具:

[binaries]
glib-compile-resources = '/usr/bin/glib-compile-resources'

Q7: wrap-file 下载失败?

A: 使用 --wrap-mode=nodownload:

meson setup --wrap-mode=nodownload ...

Q8: hpk_build.csv 缺少依赖标记?

A: 手动添加完成标记:

echo -e "json-glib\tarm64-v8a\t[OK]" >> hpk_build.csv

八、总结

这次以 xdg-desktop-portal 做鸿蒙移植,让我完整跑通了一套多层依赖库的适配流程。从梳理两层依赖关系(6 个直接依赖:glib、fuse3、json-glib、gdk-pixbuf、gstreamer、pipewire,以及它们的基础依赖 libffi、pcre2、libpng)、按自底向上顺序逐个编译,到统一写 HPKBUILD、配 Meson 交叉编译、管 pkg-config 路径,再到解决 rpath 加载、musl 兼容(fuse3 pthread_cancel 补丁)、鸿蒙解压权限异常这些实际坑,最终沉淀出一套能直接复用的 Meson 类项目鸿蒙移植方案,后面再做类似复杂库移植效率会高很多。

Logo

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

更多推荐