系列导读:本文是 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 路径主要围绕 CMakeautotools/configure 等典型 C/C++ 工程习惯设计:build_hpk.sh 会在调用各包的 build() 之前,自动拼接 CMAKE_INSTALL_PREFIXCMAKE_FIND_ROOT_PATHPKG_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),输出与 cargodependpathOHOS_RUST_TARGET 相同的 triple 字符串;便于在 HPKBUILDprepare()source 同目录软链的 envset.sh 后写 $(lycium_rust_triple "$ARCH"),与 .cargo/config.toml[target.…] 节保持一致。

image-20260513111903400

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_*_LINKEROPENSSL_*,避免串架构。

  • 遍历 depends

    • 拼接各依赖的 lib/pkgconfigPKG_CONFIG_PATH,并设置允许交叉编译 PKG_CONFIG_ALLOW_CROSS=1,供 pkg-configbuild.rs 使用。
    • 若依赖名为 opensslopenssl111,且安装前缀存在,则设置 OPENSSL_DIROPENSSL_LIB_DIROPENSSL_INCLUDE_DIR
  • ARCH 设置 OHOS_RUST_TARGET,与 官方 rustc 内置 OHOS target 一致,可通过rustup target list查看当前支持的 target:

    • arm64-v8aaarch64-unknown-linux-ohos
    • armeabi-v7aarmv7-unknown-linux-ohos
    • x86_64x86_64-unknown-linux-ohos
  • source lycium/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-clangarm-linux-ohos-clangx86_64-linux-ohos-clang;若无 SDK 路径则退化为宿主 CC

image-20260513133837513

在**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_TARGETHPKBUILDcargo build --target "${OHOS_RUST_TARGET}" 直接使用,减少手写 case "$ARCH"

2.3 lycium/template/HPKBUILD.cargo

新增

  • 在原有的HPKBUILD模板的**buildtools** 说明中显式列出 cargo
  • 按照Rust编译方式调整buildpackage方法。
  • 开发者仅需要在适配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_depsthirdparty 都未找到同名包时,再尝试 $LYCIUM_ROOT/$rustadapt_dir/$lib
  • main:无参「全量构建」时,在 findhpkdir $hpksdir 之后,若 RustAdapt 目录存在,再执行 findhpkdir $rustadapt_dir,把其中含 HPKBUILD 的子目录并入 hpkPaths
  • buildhpk 中依赖包路径解析:在 external_deps 拉取逻辑之后,按顺序查找 thirdpartyRustAdaptcommunity;并将「依赖是否已在 donelist」的判断改为按路径 目录名${deppath##*/})与已完成库名比较,使无论从哪个根目录编出的依赖都能被正确识别。

为何这样改

  • rustadapt_dir:把 Rust 适配仓从 thirdparty 中物理分离时,仍能被 ./build.sh 某库名 与全量扫描覆盖。
  • 依赖解析:Rust 包 depends=(openssl …) 时,未编依赖会触发 build_hpk.sh 退出码 101 并写入临时依赖列表;build.sh 必须把 openssl 等解析到真实目录并加入下一轮 nextroundlist,否则多轮调度无法收敛。
  • 目录名匹配 donelist:避免仅把 thirdparty/库名 当作「已完成」的误判,与 RustAdaptcommunity 路径一致。

image-20260513151141129

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 封装的场景,如 ripgrepsqlite3-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 的 特性开关、补丁、测试二进制是否安装 仍由各自 HPKBUILDbuild() / package() 中完成。

Logo

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

更多推荐