[鸿蒙PC三方库适配实战] xdg-desktop-portal 多层依赖树完整解析与鸿蒙PC适配实践
本文以 xdg-desktop-portal 桌面门户守护进程为例,系统讲解在 OpenHarmony 平台上适配复杂 C/C++ 项目的技术方案。文章重点剖析了多层依赖树的结构特点,详细展示了从底层基础库(libffi、pcre2、libpng)到中间层核心库(glib、fuse3等)再到目标应用的完整构建流程。通过实际案例,阐述了 HPKBUILD 配置、pkg-config 路径管理、rpa
摘要:本文以 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 |
适配难点总结:
- 多层依赖管理
- 6 个直接依赖库,2 层结构
- 构建顺序必须正确
- pkg-config 路径必须完整
- Meson 构建系统
- 交叉编译配置复杂
- 需要双配置文件(native + cross)
- glib 工具版本匹配问题
- 动态库加载
- rpath 配置容易遗漏
- 权限问题导致加载失败
- LD_LIBRARY_PATH 无效
- 鸿蒙系统限制
- tar 解压权限被强制修改
- musl libc 不支持 pthread_cancel
- 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 类项目鸿蒙移植方案,后面再做类似复杂库移植效率会高很多。
更多推荐



所有评论(0)