目标:用仓库现有脚本把 OpenSSL 3.6.0 + FFmpeg(n6.1.4)交叉编译出 OHOS 可用产物(headers + libs),并通过 hnpcli 产出 HNP 包。

涉及的关键文件(均在本仓):

  • lycium 顶层编译入口:lycium/build.sh
  • HPK 打包与 hnpcli 调用:lycium/script/build_hpk.sh
  • 工具链/hnpcli 环境设置:lycium/script/envset.sh
  • OpenSSL 配置:thirdparty/openssl/HPKBUILD
  • FFmpeg 配置:thirdparty/FFmpeg/HPKBUILD
  • HNP 元信息:thirdparty/FFmpeg/hnp.json

1. 一键构建

在 macOS/Linux 上交叉编译 OHOS 目标时,lycium/build.sh 强依赖 OHOS_SDK 环境变量(脚本内部会自动设置 SYSROOT=${OHOS_SDK}/native/sysroot)。

export OHOS_SDK="/ABS/PATH/TO/ohos-sdk"

cd /Volumes/coder/开源/lycium_plusplus/lycium
./build.sh openssl FFmpeg

期望输出(关键行示例,具体版本号以你本机 SDK 为准):

Build OS Darwin
OHOS_SDK=/ABS/PATH/TO/ohos-sdk
CLANG_VERSION=...
Start building openssl 3.6.0!
Compileing OpenHarmony arm64-v8a openssl 3.6.0 libs...
Start building FFmpeg n6.1.4!
Compileing OpenHarmony arm64-v8a FFmpeg n6.1.4 libs...
ALL JOBS DONE!!!

产物位置:

lycium/usr/openssl/<ARCH>/{include,lib,...}
lycium/usr/FFmpeg/arm64-v8a/{include,lib,bin?}
lycium/output/<ARCH>/openssl_3.6.0.tar.gz
lycium/output/arm64-v8a/FFmpeg_n6.1.4.tar.gz
lycium/output/arm64-v8a/*.hnp            (若 hnpcli 可用且存在 hnp.json)

2. 构建前检查(把环境信息记录下来)

2.1 检查 OHOS SDK/NDK 是否可用

test -n "${OHOS_SDK}" && echo "OHOS_SDK=${OHOS_SDK}"
test -d "${OHOS_SDK}/native/sysroot" && echo "SYSROOT=${OHOS_SDK}/native/sysroot"

"${OHOS_SDK}/native/llvm/bin/clang" --version | head -n 1
"${OHOS_SDK}/native/llvm/bin/aarch64-linux-ohos-clang" --version | head -n 1
"${OHOS_SDK}/native/build-tools/cmake/bin/cmake" --version | head -n 1

if [ -x "${OHOS_SDK}/toolchains/hnpcli" ]; then
  "${OHOS_SDK}/toolchains/hnpcli" --version || true
else
  echo "hnpcli not found: ${OHOS_SDK}/toolchains/hnpcli"
fi

期望输出(示例):

OHOS_SDK=/ABS/PATH/TO/ohos-sdk
SYSROOT=/ABS/PATH/TO/ohos-sdk/native/sysroot
clang version ...
aarch64-linux-ohos-clang version ...
cmake version ...
hnpcli version ...

2.2 检查构建依赖命令是否齐全

lycium/build.sh 会检查并要求以下命令存在(非 HarmonyOS 主机):gcc g++ cmake make pkg-config autoconf autoreconf automake patch unzip tar git ninja curl sha512sum wget

for c in gcc g++ cmake make pkg-config autoconf autoreconf automake patch unzip tar git ninja curl sha512sum wget; do
  command -v "$c" >/dev/null 2>&1 || echo "missing: $c"
done

3. OpenSSL 3.6.0:确保源码离线可复现

本仓的 OpenSSL 构建被配置为“使用本地 tarball”,不会在构建时下载:

  • thirdparty/openssl/HPKBUILDsource="openssl-3.6.0.tar.gz"downloadpackage=false

因此你需要确保文件存在:

ls -lh /Volumes/coder/开源/lycium_plusplus/thirdparty/openssl/openssl-3.6.0.tar.gz

期望输出:

-rw-r--r--  ...  openssl-3.6.0.tar.gz

4. FFmpeg(n6.1.4):对齐 OpenSSL 3.6.0 并规避 librtmp 坑

4.1 关键事实(以本仓为准)

  • thirdparty/FFmpeg/HPKBUILDpkgver=n6.1.4depends=("openssl"),且目标架构当前只启用 archs=("arm64-v8a")
  • 交叉编译时,FFmpeg 的 configure 会显式注入 OpenSSL include/lib 路径:
    • --extra-cflags="-I$LYCIUM_ROOT/usr/openssl/$ARCH/include"
    • --extra-ldflags="... -L$LYCIUM_ROOT/usr/openssl/$ARCH/lib"

4.2 复制粘贴执行:只编译指定库(推荐)

export OHOS_SDK="/ABS/PATH/TO/ohos-sdk"

cd /Volumes/coder/开源/lycium_plusplus/lycium
./build.sh openssl FFmpeg

如果你倾向用外层编排脚本(不改 build.sh):
跳过即可,本仓核心入口就是 lycium/build.sh

4.3 产物自检:确认是 AArch64 + sysroot 正确

SO="/Volumes/coder/开源/lycium_plusplus/lycium/usr/FFmpeg/arm64-v8a/lib/libavcodec.so"
"${OHOS_SDK}/native/llvm/bin/llvm-readelf" -h "${SO}" | egrep "Class:|Machine:" || true

期望输出(示例):

Class:                             ELF64
Machine:                           AArch64

5. HNP 打包(hnpcli):自动触发 + 手动触发

5.1 自动触发(默认)

当你执行 ./build.sh ... 时,库的 archive() 阶段会被调用:

  • lycium/script/build_hpk.shdefault_archive() 会在 ${LYCIUM_ROOT}/output/$ARCH/ 下生成 tar.gz
  • 如果 HNP_TOOL 非空且安装目录存在 hnp.json,会执行:
${HNP_TOOL} pack -i "$packdir" -o "${LYCIUM_ROOT}/output/$ARCH/"

其中 HNP_TOOLlycium/script/envset.shsetarm64ENV() 中设置为:

${OHOS_SDK}/toolchains/hnpcli

thirdparty/FFmpeg/hnp.json 已在仓库内提供(包含 nameversion),用于避免 name or version argv is miss

5.2 手动触发(排障用)

export OHOS_SDK="/ABS/PATH/TO/ohos-sdk"
HNP_TOOL="${OHOS_SDK}/toolchains/hnpcli"

PKG_DIR="/Volumes/coder/开源/lycium_plusplus/lycium/usr/FFmpeg/arm64-v8a"
OUT_DIR="/Volumes/coder/开源/lycium_plusplus/lycium/output/arm64-v8a"

test -f "${PKG_DIR}/hnp.json" && cat "${PKG_DIR}/hnp.json"
mkdir -p "${OUT_DIR}"
"${HNP_TOOL}" pack -i "${PKG_DIR}" -o "${OUT_DIR}"
ls -lh "${OUT_DIR}"

期望输出(示例):

.../lycium/output/arm64-v8a/FFmpeg_n6.1.4.tar.gz
.../lycium/output/arm64-v8a/ffmpeg-... .hnp

5.3 编译完成但没有输出产物:排查与解决方案

这里的“没有输出产物”通常分两类:

  • A 类:lycium/usr/<pkg>/<ARCH>/ 已有产物,但 lycium/output/<ARCH>/ 没有对应的 *.tar.gz
  • B 类:*.tar.gz 有,但 *.hnp 没有生成(或 hnpcli 打包失败)
5.3.1 快速定位:先看安装目录,再看输出目录
export ARCH="arm64-v8a"
export PKG="FFmpeg"

ls -la "/Volumes/coder/开源/lycium_plusplus/lycium/usr/${PKG}/${ARCH}" || true
ls -la "/Volumes/coder/开源/lycium_plusplus/lycium/output/${ARCH}" || true

判断逻辑:

  • 如果 lycium/usr/${PKG}/${ARCH} 不存在或空:问题通常在 package()(install)阶段没有把文件装进去
  • 如果安装目录有 include/lib,但 lycium/output/${ARCH} 没有 tar:问题通常在 archive() 阶段没跑到或被覆盖/失败了
5.3.2 检查构建日志与失败原因

lycium 会把各库的构建日志落在库目录下,例如:

thirdparty/FFmpeg/FFmpeg-n6.1.4-arm64-v8a-lycium_build.log
thirdparty/openssl/openssl-3.6.0-arm64-v8a-lycium_build.log

另外,如果 build_hpk.sh 内部某条命令失败,会在当前库目录生成 last_error

cd /Volumes/coder/开源/lycium_plusplus/thirdparty/FFmpeg
ls -la last_error || true
tail -n 80 "FFmpeg-n6.1.4-arm64-v8a-lycium_build.log" || true
5.3.3 A 类问题:没有 *.tar.gz(output 为空)

lycium/script/build_hpk.sh 的默认归档逻辑(default_archive())只有在安装目录存在时才会打包:

packdir=$LYCIUM_ROOT/usr/$pkgname/$ARCH

所以 A 类问题最常见的两个原因:

  1. 安装目录不完整或没生成:先回看该库的 package() 是否执行成功(日志里通常能看到 make install
  2. 该库的 HPKBUILD 自己实现了 archive(),但实现不正确导致没有写入 lycium/output/$ARCH/

兜底手段(不改脚本,直接从安装目录手动打一个 tar.gz):

export ARCH="arm64-v8a"
export PKG="FFmpeg"
export VER="n6.1.4"

OUT="/Volumes/coder/开源/lycium_plusplus/lycium/output/${ARCH}"
DIR="/Volumes/coder/开源/lycium_plusplus/lycium/usr/${PKG}/${ARCH}"

mkdir -p "${OUT}"
cd "${DIR}"
tar -zvcf "${OUT}/${PKG}_${VER}.tar.gz" *
ls -lh "${OUT}/${PKG}_${VER}.tar.gz"
5.3.4 B 类问题:没有 *.hnp(hnpcli 没跑起来)

hnp 打包触发条件(以 build_hpk.sh 默认逻辑为例):

  • HNP_TOOL 非空(通常来自 lycium/script/envset.sh,arm64 下默认 ${OHOS_SDK}/toolchains/hnpcli
  • 安装目录里存在 hnp.json

先确认 hnpcli 是否存在且可执行:

test -x "${OHOS_SDK}/toolchains/hnpcli" && "${OHOS_SDK}/toolchains/hnpcli" --version || true

再确认安装目录里是否有 hnp.json

export ARCH="arm64-v8a"
export PKG="FFmpeg"
ls -la "/Volumes/coder/开源/lycium_plusplus/lycium/usr/${PKG}/${ARCH}/hnp.json" || true

如果 hnp.json 不在安装目录但仓库里有(例如 thirdparty/FFmpeg/hnp.json),可以先拷贝再手动 pack:

export ARCH="arm64-v8a"

PKG_DIR="/Volumes/coder/开源/lycium_plusplus/lycium/usr/FFmpeg/${ARCH}"
OUT_DIR="/Volumes/coder/开源/lycium_plusplus/lycium/output/${ARCH}"

cp /Volumes/coder/开源/lycium_plusplus/thirdparty/FFmpeg/hnp.json "${PKG_DIR}/hnp.json"
mkdir -p "${OUT_DIR}"
"${OHOS_SDK}/toolchains/hnpcli" pack -i "${PKG_DIR}" -o "${OUT_DIR}"
ls -lh "${OUT_DIR}"

常见现象与对策:

  • name or version argv is miss:安装目录缺少 hnp.json 或其 name/version 为空
  • hnpcli not found${OHOS_SDK}/toolchains/hnpcli 不存在,检查 SDK 安装与路径
  • permission denied:hnpcli 无执行权限,或输出目录不可写

6. 交叉编译环境详解(以本仓为准)

6.1 OHOS_SDK 与 SYSROOT 的关系

在 macOS/Linux 主机上交叉编译时:

  • 你需要设置 OHOS_SDK
  • lycium/build.sh 会自动设置 SYSROOT=${OHOS_SDK}/native/sysroot

工具链环境变量主要在 lycium/script/envset.sh 中按架构设置(例如 setarm64ENV()):

  • 编译器:CC=${OHOS_SDK}/native/llvm/bin/aarch64-linux-ohos-clang
  • 归档工具:AR=${OHOS_SDK}/native/llvm/bin/llvm-ar
  • HNP 工具:HNP_TOOL=${OHOS_SDK}/toolchains/hnpcli
  • sysroot:CFLAGS/LDFLAGS 里包含 --sysroot=${SYSROOT}

如果遇到 sysroot/头文件找不到,优先确认这两点:

echo "OHOS_SDK=${OHOS_SDK}"
echo "SYSROOT=${SYSROOT}"
test -d "${SYSROOT}/usr/include" && echo "sysroot include ok"

6.2 ARCH 如何决定(为什么默认只编 arm64-v8a)

每个库的 HPKBUILD 里通过 archs=(...) 控制目标架构集合:

  • thirdparty/FFmpeg/HPKBUILD 当前只启用 archs=("arm64-v8a")
  • thirdparty/openssl/HPKBUILD 当前启用 archs=("armeabi-v7a" "arm64-v8a" "x86_64")

lycium 会对 archs 逐一循环编译,并把产物分别安装到:

lycium/usr/<pkgname>/<ARCH>/
lycium/output/<ARCH>/

如果你希望 FFmpeg 也支持 armeabi-v7ax86_64,第一步通常是扩展 thirdparty/FFmpeg/HPKBUILDarchs,并按 ARCH 分支补齐 setarm32ENV/setx86_64ENV 的配置、以及 --arch=/--extra-ldflags= 等参数。

6.3 pkg-config 路径与依赖发现机制

lycium/script/build_hpk.sh 会在配置阶段按依赖自动拼出 pkgconfigpath,并以 PKG_CONFIG_LIBDIR="${pkgconfigpath}" 的方式注入到构建流程。

这意味着:

  • 依赖库必须先安装到 lycium/usr/<dep>/<ARCH>/,并且其 lib/pkgconfig/*.pc 需要存在(如果该库依赖 pkg-config 发现)
  • 依赖链必须写在 HPKBUILDdepends=(...) 里,并且执行 ./build.sh 时参数要包含依赖项(例如 ./build.sh openssl FFmpeg

快速自检(以 OpenSSL 为例):

export ARCH="arm64-v8a"
ls -la "/Volumes/coder/开源/lycium_plusplus/lycium/usr/openssl/${ARCH}/lib/pkgconfig" || true

6.4 FFmpeg 的“host build”行为(为什么会先构建一次宿主)

thirdparty/FFmpeg/HPKBUILD 里存在一个 buildhost=true 的流程:先在宿主(macOS/Linux)上构建一套 host 工具与库,用于后续部分测试/生成步骤,再进入 OHOS 交叉编译阶段。

当你看到日志里先出现一次 host configure/make,再出现 Compileing OpenHarmony arm64-v8a FFmpeg ...,这是预期行为。

6.5 增量构建与强制重编(hpk_build.csv)

lycium 会在 lycium/usr/hpk_build.csv 记录“已完成构建”的库,后续再次执行 ./build.sh 会跳过这些库(除非你改了版本或清理了记录)。

定位是否跳过某库:

cat /Volumes/coder/开源/lycium_plusplus/lycium/usr/hpk_build.csv | tail -n 50
grep -n ",FFmpeg," /Volumes/coder/开源/lycium_plusplus/lycium/usr/hpk_build.csv || true
grep -n ",openssl," /Volumes/coder/开源/lycium_plusplus/lycium/usr/hpk_build.csv || true

强制重编的做法(原则):删除对应库在 hpk_build.csv 里的记录,再重新执行 ./build.sh ...

6.6 常用交叉编译命令清单

只编一个库(例如只编 OpenSSL):

export OHOS_SDK="/ABS/PATH/TO/ohos-sdk"
cd /Volumes/coder/开源/lycium_plusplus/lycium
./build.sh openssl

编译多个库并显式包含依赖(推荐):

export OHOS_SDK="/ABS/PATH/TO/ohos-sdk"
cd /Volumes/coder/开源/lycium_plusplus/lycium
./build.sh openssl FFmpeg

用外层编排脚本跑 OHOS 目标(不改 build.sh):
跳过即可,本仓核心入口就是 lycium/build.sh

7. HNP 打包进阶(打包规则与可复用模板)

7.1 HNP 自动打包触发条件(再强调一次)

lycium/script/build_hpk.sh 的默认逻辑为准,HNP 打包需要同时满足:

  • HNP_TOOL 非空(通常为 ${OHOS_SDK}/toolchains/hnpcli
  • 安装目录存在 hnp.json(位于 lycium/usr/<pkgname>/<ARCH>/hnp.json

如果 hnp.json 不在安装目录,默认归档逻辑会尝试从库源码目录拷贝过去:

if [ ! -f "$packdir/hnp.json" ] && [ -f "${PWD}/hnp.json" ]
then
    cp "${PWD}/hnp.json" "$packdir"
fi

所以更稳妥的放置策略是:把 hnp.json 放在库目录(与 HPKBUILD 同级),例如:

  • thirdparty/FFmpeg/hnp.json

7.2 hnp.json 的最小字段与错误对照

本仓的 FFmpeg 示例:

{
    "type":"hnp-config",
    "name":"ffmpeg",
    "version":"n6.1.4",
    "install":{}
}

常见错误与字段关系:

  • name or version argv is misshnp.json 缺失或 name/version 为空
  • “能打 tar 但不出 hnp”:通常是 HNP_TOOL 未设置或 hnpcli 不可执行

7.3 为某个库自定义 archive() 的推荐模板

如果某个库需要自定义 archive()(例如想改包名、改输出路径、或额外拷贝文件),建议遵循本仓约定:

  • tar.gz 输出到:${LYCIUM_ROOT}/output/$ARCH/
  • HNP 输出到:${LYCIUM_ROOT}/output/$ARCH/
  • 打包输入目录为安装目录:${LYCIUM_ROOT}/usr/$pkgname/$ARCH

示例模板:

archive() {
    mkdir -p "${LYCIUM_ROOT}/output/${ARCH}"
    packdir="${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}"
    if [ -d "${packdir}" ]
    then
        if [ ! -f "${packdir}/hnp.json" ] && [ -f "${PWD}/hnp.json" ]
        then
            cp "${PWD}/hnp.json" "${packdir}"
        fi
        pushd "${packdir}" > /dev/null
            tar -zvcf "${LYCIUM_ROOT}/output/${ARCH}/${pkgname}_${pkgver}.tar.gz" *
        popd > /dev/null
        if [ -n "${HNP_TOOL}" ] && [ -f "${packdir}/hnp.json" ]
        then
            "${HNP_TOOL}" pack -i "${packdir}" -o "${LYCIUM_ROOT}/output/${ARCH}/"
        fi
    fi
}

7.4 多架构 HNP 与产物命名建议

如果同一库启用了多个 archs,建议在产物命名上体现 ARCH,避免覆盖:

${pkgname}_${pkgver}_${ARCH}.tar.gz

对应的 HNP 输出建议也按 ARCH 分目录组织(本仓默认就是按 output/$ARCH/ 输出)。

7.5 hnpcli 参数与打包结果校验

本仓默认调用参数为:

hnpcli pack -i <install_dir> -o <output_dir>

你可以先查看本机 hnpcli 支持的参数:

export OHOS_SDK="/ABS/PATH/TO/ohos-sdk"
"${OHOS_SDK}/toolchains/hnpcli" --help || true
"${OHOS_SDK}/toolchains/hnpcli" pack --help || true

打包完成后,至少确认两件事:

export ARCH="arm64-v8a"
ls -lh "/Volumes/coder/开源/lycium_plusplus/lycium/output/${ARCH}" || true
test -f "/Volumes/coder/开源/lycium_plusplus/lycium/output/${ARCH}/FFmpeg_n6.1.4.tar.gz" && echo "tar.gz ok"
ls "/Volumes/coder/开源/lycium_plusplus/lycium/output/${ARCH}"/*.hnp 2>/dev/null || true

8. 常见报错速查(按症状直接定位)

8.1 hnpcli 报错:name or version argv is miss

  • 确保安装目录存在 hnp.json(本仓已提供 thirdparty/FFmpeg/hnp.json
  • 手动 pack 一次验证参数:hnpcli pack -i <pkgdir> -o <outdir>

8.2 OpenSSL 下载失败/不可复现

  • 本仓 OpenSSL 默认不下载,必须提供 thirdparty/openssl/openssl-3.6.0.tar.gz

8.3 librtmp + OpenSSL 3.x 链接失败

  • 典型根因:librtmp 依赖旧 OpenSSL API
  • 优先策略:保证主链路稳定,禁用 librtmp(在 FFmpeg 的配置选项中不要启用它)

8.4 FFmpeg version 显示为 git hash(例如 82e1f01)

当 FFmpeg 在 git 源码树里构建时,版本信息可能携带 commit hash(更像“构建标识”而不是语义版本)。如果你需要稳定展示版本,优先用包元信息:

  • thirdparty/FFmpeg/HPKBUILDpkgver=n6.1.4
  • thirdparty/FFmpeg/hnp.jsonversion=n6.1.4

8.5 同时出现 host 与 OHOS 两段构建,且日志很长

这是 thirdparty/FFmpeg/HPKBUILD 的预期行为:会先构建一套宿主产物(hostbuild),再进入 OHOS 交叉编译阶段。排查交叉编译问题时,优先关注包含 Compileing OpenHarmony <ARCH> FFmpeg ... 的那段日志。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐