【鸿蒙PC三方库移植适配框架解读系列】第八篇:扩展lycium框架使其满足rust三方库适配
本文介绍了如何在Lycium框架中扩展对Rust三方库的适配支持。通过修改envset.sh、build_hpk.sh、HPKBUILD模板和build.sh四个核心文件,实现了Rust与C/C++库的统一构建流程。关键改进包括:新增Rust交叉编译环境变量设置,建立cargo构建分支,提供Rust适配模板,并完善构建脚本。这些改动使得开发者能够沿用Lycium现有机制完成Rust库的鸿蒙化适配,
系列导读:本文是 Lycium 适配系列的番外篇,在 lycium 增强仓(
lycium_plusplus)语境下,不改变原有 C/C++ 三方库 的标准适配与构建链路,把 Rust 三方库 接到同一套HPKBUILD+build_hpk.sh机制上,并列出 建议修改的文件、修改要点与原因,最终实现开发者也可以通过lycium完成Rust三方库适配工作。
欢迎加入【开源鸿蒙PC社区】,一起共建鸿蒙化C/C++三方库生态。
前言
| 项目 | 说明 |
|---|---|
| macOS环境配置 | https://bxming.blog.csdn.net/article/details/159284830 |
| Ubuntu环境配置(或者windows + wsl) | https://bxming.blog.csdn.net/article/details/159284760 |
| lycium框架 | https://atomgit.com/OpenHarmonyPCDeveloper/lycium_plusplus.git |
| 扩展lycium框架 | https://gitcode.com/CodexBai/lycium_plusplus.git |
| 应用平台 | HarmonyOS PC |
1. lycium 在做什么
lycium 是围绕 HPKBUILD 描述文件组织的一套构建外壳:在宿主机上配置 OHOS SDK 与 LLVM 交叉工具链,按 archs 多架构循环,完成下载、解压、配置、编译与安装,并把产物归集到 lycium/usr/<库名>/<ARCH>/ 这类统一前缀下,便于北向应用或二次打包消费。
原 lycium 的 buildtools 路径主要围绕 CMake、autotools/configure 等典型 C/C++ 工程习惯设计:build_hpk.sh 会在调用各包的 build() 之前,自动拼接 CMAKE_INSTALL_PREFIX、CMAKE_FIND_ROOT_PATH、PKG_CONFIG_PATH 等与 C/C++ 交叉编译强相关的参数。了解lycium框架,可以阅读以下系列解读。
| 篇章 | 标题 | 内容 |
|---|---|---|
| 第一篇 | 概述与环境配置 | Lycium 概念、构建机要求、OHOS SDK 配置 |
| 第二篇 | 项目结构与适配目录创建 | 目录结构、community vs thirdparty、创建适配目录 |
| 第三篇 | HPKBUILD 编写详解 | 元数据字段、过程函数、三种构建系统写法 |
| 第四篇 | 构建执行与产物获取 | 构建流程、日志分析、多库递归、HAP 集成 |
| 第五篇 | 流程图与角色职责 | 完整流程图、各角色职责、协作时序 |
| 第六篇 | 关键注意事项与最佳实践 | 依赖管理、架构超集、日志调试、外部适配仓 |
| 第七篇 | 快速参考与模板 | 入门步骤、模板、完整案例、检查清单 |
**随着鸿蒙生态的发展,越来越多的 Rust 库需要在鸿蒙设备上运行,这就需要开发者自行适配Rust库。**这就意味着仓库中开始出现 Rust crate(例如命令行工具、带 -sys 的原生绑定库),这时就需要和C/C++三方库适配同样走 OHOS 交叉编译、并纳入同一套依赖调度与产物布局时,仅靠手写 HPKBUILD 而不扩展脚本,会出现重复配置 pkg-config、OpenSSL、链接器、CC/CXX 等问题。因此不创造新框架,而是在现有 HPKBUILD + 调度引擎架构上增加 buildtools=cargo 分支,复用已有的依赖解析、源码管理、产物归档等基础设施。
Rust 的构建体系(Cargo)与 C/C++(cmake/make/configure)有本质差异:
| 维度 | C/C++ | Rust |
|---|---|---|
| 构建系统 | cmake / make / configure | cargo(内置包管理与构建) |
| 编译器 | clang(来自 OHOS SDK) | rustc(来自 rustup)+ OHOS clang(作为 linker) |
| 目标标识 | arm64-v8a, armeabi-v7a |
aarch64-unknown-linux-ohos, armv7-unknown-linux-ohos |
| 依赖管理 | depends[] 手动声明 + pkg-config 搜索 |
Cargo.toml 自动解析 crate 依赖 |
| 产物类型 | .so / .a / .h |
.so (cdylib) / .a (staticlib) / .rlib (lib) |
| 工具链来源 | 100% OHOS SDK | Rust 编译器 (rustup) + OHOS SDK linker |
2. 框架能力扩展
要让 Lycium 支持 Rust,需要修改 3 个核心文件,改动量很小但影响深远。
| # | 文件路径 | 作用 |
|---|---|---|
| 1 | lycium/script/envset.sh |
新增 Rust 交叉编译环境变量设置 |
| 2 | lycium/script/build_hpk.sh |
新增 buildtools=cargo 构建分支 |
| 3 | lycium/template/HPKBUILD.cargo |
新增 Rust 适配模板 |
| 4 | lycium/build.sh |
新增存放Rust库 hpk 项目存放的目录 |
核心思路:四个文件各司其职——envset.sh 准备环境、build_hpk.sh 负责调度、HPKBUILD 模板供适配者参考、build.sh主构建脚本。
2.1 lycium/script/envset.sh
新增函数:lycium_rust_triple,核心作用为把鸿蒙的架构名称,转换成 Rust 语言能识别的目标三元组(target triple)。
# 与 build_hpk.sh cargodependpath 中 OHOS_RUST_TARGET 约定一致;供 HPKBUILD 中 $(lycium_rust_triple "$ARCH") 使用
lycium_rust_triple() {
case "${1:-}" in
arm64-v8a) echo aarch64-unknown-linux-ohos ;;
armeabi-v7a) echo armv7-unknown-linux-ohos ;;
x86_64) echo x86_64-unknown-linux-ohos ;;
*) echo "" ;;
esac
}
入参为 ARCH(或 $1),输出与 cargodependpath 中 OHOS_RUST_TARGET 相同的 triple 字符串;便于在 HPKBUILD 的 prepare() 里 source 同目录软链的 envset.sh 后写 $(lycium_rust_triple "$ARCH"),与 .cargo/config.toml 的 [target.…] 节保持一致。

2.2 lycium/script/build_hpk.sh
新增函数:cargodependpath,核心作用:配置依赖路径、编译工具链、环境变量,让 Rust 能正确交叉编译出鸿蒙可执行程序。这个函数接收一个架构参数(arm64-v8a/armeabi-v7a/x86_64),然后:
- 清空旧的编译环境变量
- 配置依赖库(pkg-config)路径
- 专门处理 OpenSSL 依赖
- 设置 Rust 目标架构
- 配置鸿蒙交叉编译链接器(clang)
- 加载对应架构的编译环境脚本
# Rust(cargo):为 depends 拼接 pkg-config / OpenSSL 路径,并设置 OHOS 交叉链接器与 C/C++ 编译环境(供 build.rs/cc-rs)
cargodependpath() {
# ===== 重置环境,避免之前的编译残留影响本次构建 =====
# 初始化空变量
buildargs=""
pkgconfigpath=""
# 清空 3 个架构的Rust链接器环境变量(错误信息丢到空设备)
unset CARGO_TARGET_AARCH64_UNKNOWN_LINUX_OHOS_LINKER \
CARGO_TARGET_ARMV7_UNKNOWN_LINUX_OHOS_LINKER \
CARGO_TARGET_X86_64_UNKNOWN_LINUX_OHOS_LINKER 2>/dev/null
# 清空OpenSSL 相关环境变量
unset OPENSSL_DIR OPENSSL_LIB_DIR OPENSSL_INCLUDE_DIR 2>/dev/null
# ===== 让系统编译时能找到鸿蒙平台的依赖库,而不是开发机本地库。 =====
# 如果depends数组不为空,存在依赖库
if [ ${#depends[@]} -ne 0 ]
then
# 遍历所有依赖
for depend in ${depends[@]}
do
# 拼接依赖的pkgconfig路径(lycium框架交叉编译库后安装的库路径)
dependpath=$LYCIUM_ROOT/usr/$depend/$1/lib/pkgconfig
# 如果路径存在,就加入PKG_CONFIG_PATH
if [ -d "${dependpath}" ]
then
pkgconfigpath=$pkgconfigpath"${dependpath}:"
fi
# 如果依赖是openssl / openssl111
case "$depend" in
openssl|openssl111)
# 如果目录存在
if [ -d "$LYCIUM_ROOT/usr/$depend/$1" ]
then
# 导出OpenSSL路径,让Rust的openssl crate能找到
export OPENSSL_DIR="$LYCIUM_ROOT/usr/$depend/$1"
export OPENSSL_LIB_DIR="$OPENSSL_DIR/lib"
export OPENSSL_INCLUDE_DIR="$OPENSSL_DIR/include"
fi
;;
esac
done
# 去掉路径末尾多余的冒号
pkgconfigpath=${pkgconfigpath%:*}
fi
# 如果收集到了依赖库
if [ -n "$pkgconfigpath" ]
then
# 导出给pkg-config
export PKG_CONFIG_PATH="$pkgconfigpath"
# 允许交叉编译
export PKG_CONFIG_ALLOW_CROSS=1
else
# 没有依赖就清空路径
unset PKG_CONFIG_PATH
fi
# ===== 把鸿蒙架构 → 转为 Rust 识别的目标架构。 =====
# 根据架构,设置Rust目标的三元组
case "$1" in
arm64-v8a) export OHOS_RUST_TARGET=aarch64-unknown-linux-ohos ;;
armeabi-v7a) export OHOS_RUST_TARGET=armv7-unknown-linux-ohos ;;
x86_64) export OHOS_RUST_TARGET=x86_64-unknown-linux-ohos ;;
*) echo "cargodependpath: unknown ARCH=$1"; return 1 ;;
esac
# ===== 给 Rust 指定交叉编译链接器,优先使用鸿蒙 SDK 自带的专用 clang,保证兼容鸿蒙系统。=====
# 如果环境脚本存在
if [ -f "${LYCIUM_ROOT}/script/envset.sh" ]
then
# 加载环境脚本
. "${LYCIUM_ROOT}/script/envset.sh"
# 如果是鸿蒙系统 + arm64位架构
if [ -n "${TARGET_HARMONYOS}" ] && [ "$1" = "arm64-v8a" ]
then
# 设置鸿蒙专属环境
setHarmonyOSENV
# 使用clang作为链接器
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_OHOS_LINKER="${CC:-clang}"
else
# 根据架构加载对应的编译环境
case "$1" in
armeabi-v7a) setarm32ENV ;;
arm64-v8a) setarm64ENV ;;
x86_64) setx86_64ENV ;;
esac
# 如果有 OHOS_SDK 且目录存在
if [ -n "$OHOS_SDK" ] && [ -d "$OHOS_SDK/native/llvm/bin" ]
then
# 使用 SDK 自带的 专属 clang 链接器
case "$1" in
arm64-v8a)
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_OHOS_LINKER="$OHOS_SDK/native/llvm/bin/aarch64-linux-ohos-clang"
;;
armeabi-v7a)
export CARGO_TARGET_ARMV7_UNKNOWN_LINUX_OHOS_LINKER="$OHOS_SDK/native/llvm/bin/arm-linux-ohos-clang"
;;
x86_64)
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_OHOS_LINKER="$OHOS_SDK/native/llvm/bin/x86_64-linux-ohos-clang"
;;
esac
else
# 没有 SDK 就用系统默认 clang
case "$1" in
arm64-v8a) export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_OHOS_LINKER="${CC:-clang}" ;;
armeabi-v7a) export CARGO_TARGET_ARMV7_UNKNOWN_LINUX_OHOS_LINKER="${CC:-clang}" ;;
x86_64) export CARGO_TARGET_X86_64_UNKNOWN_LINUX_OHOS_LINKER="${CC:-clang}" ;;
esac
fi
fi
fi
return 0
}
在 buildtools=cargo 时,于每个 ARCH 上在进入 prepare() 之前调用(保证 prepare 里写 .cargo/config.toml 时能读到已导出的 OHOS_RUST_TARGET 等变量)。主要行为:
-
将
buildargs置空;cargo分支不使用与 CMake 相同的buildargs拼接。 -
清空**
unset** 上一架构可能残留的CARGO_TARGET_*_LINKER、OPENSSL_*,避免串架构。 -
遍历
depends:- 拼接各依赖的
lib/pkgconfig到PKG_CONFIG_PATH,并设置允许交叉编译PKG_CONFIG_ALLOW_CROSS=1,供pkg-config类build.rs使用。 - 若依赖名为
openssl或openssl111,且安装前缀存在,则设置OPENSSL_DIR、OPENSSL_LIB_DIR、OPENSSL_INCLUDE_DIR。
- 拼接各依赖的
-
按
ARCH设置OHOS_RUST_TARGET,与 官方rustc内置 OHOS target 一致,可通过rustup target list查看当前支持的 target:arm64-v8a→aarch64-unknown-linux-ohosarmeabi-v7a→armv7-unknown-linux-ohosx86_64→x86_64-unknown-linux-ohos。
-
sourcelycium/script/envset.sh后:- 若
TARGET_HARMONYOS且当前为arm64-v8a,调用setHarmonyOSENV,并把CARGO_TARGET_AARCH64_UNKNOWN_LINUX_OHOS_LINKER设为CC(默认clang)。 - 否则按架构调用
setarm32ENV/setarm64ENV/setx86_64ENV,并在存在OHOS_SDK/native/llvm/bin时,把上述CARGO_TARGET_*_LINKER设为对应的aarch64-linux-ohos-clang、arm-linux-ohos-clang、x86_64-linux-ohos-clang;若无 SDK 路径则退化为宿主CC。
- 若

在**builpackage **函数中的新增:
- 当构建工具是 cargo(Rust 编译器)时,自动调用环境配置函数 cargodependpath 配置鸿蒙编译环境。
buildpackage {
# ...
# 如果当前使用的构建工具是 cargo(Rust 官方构建工具)
if [ "$buildtools" == "cargo" ]
then
# 就执行:确保 cargodependpath 函数运行,传入当前架构(arm64-v8a/armeabi-v7a/x86_64)
sure cargodependpath $ARCH
fi
# 再执行 prepare
sure prepare
# ...
}
- 在每个
arch循环里:**若buildtools==cargo,先cargodependpath $ARCH,再prepare,最后 **build $buildargs。
说明
- 链接器环境变量名 必须随 Rust triple 变化(例如
aarch64-unknown-linux-ohos对应CARGO_TARGET_AARCH64_UNKNOWN_LINUX_OHOS_LINKER),与 NDK 里 clang 可执行文件名(如aarch64-linux-ohos-clang)不是同一字符串。OHOS_RUST_TARGET供HPKBUILD里cargo build --target "${OHOS_RUST_TARGET}"直接使用,减少手写case "$ARCH"。
2.3 lycium/template/HPKBUILD.cargo
新增
- 在原有的
HPKBUILD模板的**buildtools** 说明中显式列出cargo。 - 按照Rust编译方式调整
build和package方法。 - 开发者仅需要在适配rust三方库时拷贝该文件到
RustAdapt/<库名称>/目录下,修改元数据即可。然后根据每次交叉编译构建完的结果进行修改,直到编译构建成功。
# 与原有的HPKBUILD模板对比,主要修改以下代码
# ...
buildtools= # 编译方法: cmake(默认) | configure | cargo | 其它则不在此注入 buildargs,由 build() 自行处理
# ...
# 执行编译构建的命令
build() {
# 进入编译工作目录(Rust 项目源码所在文件夹)
cd $builddir
# 【核心编译命令】正式编译 Rust 项目
# --release :编译正式发布版(优化、速度快)
# --locked :严格使用 Cargo.lock 锁定的依赖版本,保证编译一致
# --target :指定编译目标架构(鸿蒙:aarch64-unknown-linux-ohos)
# ${OHOS_RUST_TARGET:?} :必须有这个环境变量,否则直接报错退出
cargo build --release --locked --target "${OHOS_RUST_TARGET:?}"
# 对最关键一步的退出码进行判断
ret=$?
cd $OLDPWD
return $ret
}
# 打包安装
package() {
# 进入 Rust 项目目录(和编译时同一个目录)
cd $builddir
# 定义安装路径:鸿蒙库的安装目录
DEST="$LYCIUM_ROOT/usr/$pkgname/$ARCH"
# 【关键】把编译好的 Rust 程序 → 复制到目标目录的 bin 文件夹
cp target/${OHOS_RUST_TARGET}/release/xxx "$DEST/bin/"
cd $OLDPWD
}
# ...
2.4 lycium/build.sh
新增内容概览
- 增加变量
rustadapt_dir="../RustAdapt",表示相对lycium/目录的 Rust 适配根路径。 findarglibsdir:在external_deps、thirdparty都未找到同名包时,再尝试$LYCIUM_ROOT/$rustadapt_dir/$lib。main:无参「全量构建」时,在findhpkdir $hpksdir之后,若RustAdapt目录存在,再执行findhpkdir $rustadapt_dir,把其中含HPKBUILD的子目录并入hpkPaths。buildhpk中依赖包路径解析:在external_deps拉取逻辑之后,按顺序查找thirdparty、RustAdapt、community;并将「依赖是否已在donelist」的判断改为按路径 目录名(${deppath##*/})与已完成库名比较,使无论从哪个根目录编出的依赖都能被正确识别。
为何这样改
rustadapt_dir:把 Rust 适配仓从thirdparty中物理分离时,仍能被./build.sh 某库名与全量扫描覆盖。- 依赖解析:Rust 包
depends=(openssl …)时,未编依赖会触发build_hpk.sh退出码 101 并写入临时依赖列表;build.sh必须把openssl等解析到真实目录并加入下一轮nextroundlist,否则多轮调度无法收敛。 - 目录名匹配
donelist:避免仅把thirdparty/库名当作「已完成」的误判,与RustAdapt、community路径一致。

3. 适配全流程
完成框架改造后,开发者使用 Rust 库的流程与 C/C++ 基本一致:
R0 环境准备 → R1 创建目录 → R2 编写 HPKBUILD → R3 构建 → R4 产物集成
3.1 R0:环境准备
适配者需要安装 Rust 工具链并添加 OHOS 目标:
# 安装 rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env
# 添加 OHOS 目标
rustup target add aarch64-unknown-linux-ohos armv7-unknown-linux-ohos
# 验证
rustc --version
# 输出:rustc 1.75.0-nightly (...)
ls ~/.rustup/toolchains/nightly-*/lib/rustlib/aarch64-unknown-linux-ohos/lib/
⚠️ 注意:Rust 对 OHOS 的目标支持通常来自社区维护的 nightly 工具链(如
rust-lang/rust仓库中的aarch64-unknown-linux-ohos目标)。如果官方不支持,可能需要使用自定义 nightly 构建或使用rust-std组件。
3.2 R1:创建适配目录
cd lycium/RustAdapt
mkdir -p mylib
cd mylib
3.3 R2:编写 HPKBUILD
参考 2.3 节的模板,根据具体库的信息进行修改。
3.4 R3:构建执行
cd lycium
./build.sh mylib
框架进入 buildtools=cargo 分支后,执行顺序:
| 步骤 | 组件 | 说明 |
|---|---|---|
| 1 | cleanhpk() |
清理上次构建 |
| 2 | builddepends() |
解析 C 依赖库 |
| 3 | checkmakedepends() |
检查 cargo / rustc |
| 4 | download() |
下载 crate 源码 |
| 5 | checksum() |
SHA512 校验 |
| 6 | unpack() |
解压 |
| 7 | 架构循环 | for arch in ${archs[@]} |
| 7.1 | setenv() |
设置 Rust 交叉编译环境 |
| 7.2 | prepare() |
生成 .cargo/config.toml |
| 7.3 | build() |
执行 cargo build --target ... |
| 7.4 | package() |
安装产物到 usr/<pkgname>/<arch>/ |
| 7.5 | archive() |
归档 / HNP 打包(可选) |
3.5 R4:产物获取
| 产物类型 | 路径 | 说明 |
|---|---|---|
| 动态库 (.so) | lycium/usr/<pkgname>/<arch>/lib/ |
cdylib crate 类型输出 |
| 静态库 (.a) | lycium/usr/<pkgname>/<arch>/lib/ |
staticlib crate 类型输出 |
| Rust rlib | lycium/usr/<pkgname>/<arch>/lib/ |
供其他 Rust crate 链接 |
| 归档包 | lycium/output/<arch>/ |
全量产物归档 |
4. 三种典型场景的 HPKBUILD
场景 A:纯 Rust cdylib(输出 .so)
适用于需要被 C/C++ 或其他语言通过 FFI 调用的 Rust 库。
Cargo.toml(上游):
[lib]
crate-type = ["cdylib"]
name = "mylib"
HPKBUILD 关键配置:
pkgname=mylib
buildtools=cargo
archs=("arm64-v8a")
场景 B:Rust 封装 C 库(staticlib + C 头文件)
适用于对已有 C 库进行 Rust 封装的场景,如 ripgrep、sqlite3-sys 等。
HPKBUILD:
pkgname=ripgrep
archs=("arm64-v8a") # ripgrep 主要支持 arm64-v8a
depends=("pcre2") # 启用 PCRE2 功能需要 pcre2 库
makedepends=("cargo" "rustc") # 需要 Rust 工具链
source="https://github.com/BurntSushi/ripgrep/archive/refs/tags/${pkgver}.tar.gz"
# 为编译设置环境,如设置环境变量,创建编译目录等
prepare() {
cd $builddir
mkdir -p .cargo
if [ ! -f .cargo/config.toml ]; then
touch .cargo/config.toml
fi
if ! grep -q "^\[target\.${OHOS_RUST_TARGET:?}\]$" .cargo/config.toml; then
cat >> .cargo/config.toml <<EOF
[target.${OHOS_RUST_TARGET}]
linker = "${CC:?}"
EOF
fi
cd ${OLDPWD}
}
框架会在 depends 解析后自动将 C 库的 sysroot 和 pkg-config 路径传入 Rust 构建环境。
场景 C:纯 Rust 库作为 Rust crate 依赖(输出 .rlib)
适用于被其他 Rust crate 作为依赖使用的库。
Cargo.toml(上游):
[lib]
name = "my_util"
# 默认就是 lib crate 类型 → 输出 rlib
5. 小结
本次扩展的实质是:保留 lycium 的「包描述 + 多轮依赖 + 多架构 + 统一安装前缀」模型,在 build.sh 上增加 Rust 适配仓的发现与依赖解析,在 build_hpk.sh 上增加 **cargo 专用的依赖与环境注入函数 cargodependpath,在 **envset.sh 上提供 lycium_rust_triple 辅助。Rust 与 C/C++ 共用同一套入口脚本,降低双栈维护成本;具体 crate 的 特性开关、补丁、测试二进制是否安装 仍由各自 HPKBUILD 在 build() / package() 中完成。
更多推荐



所有评论(0)