摘要:本文以 mpv 媒体播放器为例,深入讲解如何在 鸿蒙PC 平台上适配具有复杂多层依赖树的 C/C++ 项目。文章将按照依赖层次,从底层基础库到顶层应用,逐一解析每个依赖库的 HPKBUILD 配置、pkg-config 路径管理、头文件路径处理等关键技术点,所有代码均取自 thirdparty 目录下的真实项目。

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

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

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

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

项目信息说明

项目 说明
目标库 mpv (libmpv)
开源协议 LGPL-2.1-or-later
源码版本 0.41.0
目标平台 鸿蒙 PC
构建系统 Meson + Ninja
操作系统平台 WSL Ubuntu 24.04

一、多层依赖树结构解析

1.0 为什么需要理解依赖树

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

  • 构建顺序决定成败:必须自底向上构建,否则依赖库找不到
  • pkg-config 路径传递:每个库的 pkgconfig 路径必须正确传递给上层
  • 头文件路径差异:不同库的安装路径规范不一致,需要特殊处理
  • 链接路径配置:库文件位置不同,需要显式指定链接路径

1.1 mpv 的完整依赖树

mpv 的依赖展现了典型的金字塔形依赖结构,共分为 4 层:

4 层:mpv (目标库 - 媒体播放器)
├── 依赖: FFmpeg, libplacebo, libass
│
第 3 层:核心依赖库
├── FFmpeg (音视频编解码)
├── libplacebo (GPU 渲染)
│   └── 依赖: vulkan-headers, spirv-headers, fast_float
└── libass (字幕渲染)
    └── 依赖: fontconfig, harfbuzz, fribidi, freetype2
│
第 2 层:中间依赖库
├── fontconfig (字体配置)
│   └── 依赖: freetype2, libexpat
├── harfbuzz (文本整形)
│   └── 依赖: freetype2, glib
└── freetype2 (字体渲染)
    └── 依赖: zlib, libpng, brotli
│
第 1 层:基础依赖库
├── zlib (压缩库)
├── libpng (PNG 图像)
├── brotli (压缩算法)
├── libexpat (XML 解析)
├── fribidi (双向文本)
└── glib (基础工具库)
    └── 依赖: libffi, pcre2

1.2 构建顺序原则

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

1 步:zlib, libpng, brotli, libexpat, fribidi, libffi, pcre2
              ↓
第 2 步:glib (依赖 libffi, pcre2)
              ↓
第 3 步:freetype2 (依赖 zlib, libpng, brotli)
              ↓
第 4 步:fontconfig (依赖 freetype2, libexpat)
        harfbuzz (依赖 freetype2, glib)
              ↓
第 5 步:libass (依赖 fontconfig, harfbuzz, fribidi, freetype2)
              ↓
第 6 步:FFmpeg, libplacebo (并行构建)
              ↓
第 7 步:mpv (依赖 FFmpeg, libplacebo, libass)

二、第 1 层:基础依赖库适配

2.1 zlib - 压缩库

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

HPKBUILD 完整配置

# Contributor: Jeff Han <hanjinfei@foxmail.com>
# Maintainer: Jeff Han <hanjinfei@foxmail.com>
pkgname=zlib
pkgver=v1.2.13
pkgrel=0
pkgdesc=""
url=""
archs=("armeabi-v7a" "arm64-v8a" "x86_64")
license=("zlib License")
depends=()
makedepends=()

# 官方下载地址source="https://github.com/madler/$pkgname/archive/refs/tags/$pkgver.tar.gz"受网络影响可能存在下载失败的情况,现使用gitee镜像可以与官方仓库保持同步
source="https://gitee.com/mirrors/$pkgname/repository/archive/$pkgver.zip"

autounpack=true
downloadpackage=true
buildtools="configure"

builddir=$pkgname-${pkgver}
packagename=$builddir.zip

patch_flag=true

source envset.sh

prepare() {
    mkdir -p $builddir/$ARCH-build
    if [ "$patch_flag" == true ]
    then
        cd $builddir
        patch -p1 < ../zlib_ohos_pkg.patch
        cd $OLDPWD
        patch_flag=false
    fi
    if [ $ARCH == "armeabi-v7a" ]
    then
        setarm32ENV
    fi
    if [ $ARCH == "arm64-v8a" ]
    then
        setarm64ENV
    fi
}

build() {
    cd $builddir/$ARCH-build
    PKG_CONFIG_LIBDIR="${pkgconfigpath}" \
    ../configure "$@" > $buildlog 2>&1
    $MAKE VERBOSE=1 >> $buildlog 2>&1
    ret=$?
    cd $OLDPWD
    return $ret
}

package() {
    cd $builddir/$ARCH-build
    $MAKE install >> $buildlog 2>&1
    cd $OLDPWD
    if [ $ARCH == "armeabi-v7a" ]
    then
        unsetarm32ENV
    fi
    if [ $ARCH == "arm64-v8a" ]
    then
        unsetarm64ENV
    fi
}

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

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

关键技术点

  • depends=() 空数组表示无依赖
  • source envset.sh 导入 lycium 框架的环境变量设置函数
  • setarm64ENV 设置 arm64-v8a 交叉编译环境变量
  • patch -p1 < …/zlib_ohos_pkg.patch 应用鸿蒙适配补丁
  • 标准 configure + make 构建流程

构建命令

cd lycium_plusplus/lycium
./build.sh zlib

2.2 libpng - PNG 图像处理库

依赖关系:依赖 zlib

HPKBUILD 完整配置

# Contributor: Jeff Han <hanjinfei@foxmail.com>
# Maintainer: Jeff Han <hanjinfei@foxmail.com>
pkgname=libpng
pkgver=v1.6.39
pkgrel=0
pkgdesc=""
url=""
archs=("arm64-v8a")
license=("PNG Reference Library License version 2")
depends=()
makedepends=()
install=

# 官方下载地址source="https://sourceforge.net/projects/$pkgname/files/libpng16/$pkgver/$pkgname-$pkgver.tar.xz"受网络影响可能存在下载失败的情况,现使用gitee镜像可以与官方仓库保持同步
source="https://gitee.com/mirrors/$pkgname/repository/archive/$pkgver.zip"

autounpack=true
downloadpackage=true
buildtools="configure"

builddir=$pkgname-${pkgver}
packagename=$builddir.zip

source envset.sh
host=

prepare() {
    mkdir -p $builddir/$ARCH-build
    if [ $ARCH == "armeabi-v7a" ]
    then
        setarm32ENV
        host=arm-linux
    fi
    if [ $ARCH == "arm64-v8a" ]
    then
        setarm64ENV
        host=aarch64-linux
    fi
}

build() {
    cd $builddir/$ARCH-build
    # 显式设置 C 预处理器,避免 configure 自动检测使用 C++ 编译器
    export CPP="${CC:-${LYCIUM_ROOT}/../toolchain/${host}-ohos-clang} -E"
    PKG_CONFIG_LIBDIR="${pkgconfigpath}" ../configure "$@" \
    --host=$host --enable-shared --enable-static > $buildlog 2>&1
    # 修改导致测试失败的字符串
    find . -name Makefile -exec sed -i 's#mawk#awk#' {} +
    find . -name Makefile -exec sed -i 's#/bin/bash#bash#' {} +
    find . -name Makefile -exec sed -i 's#/usr/bin/sed#/bin/sed#' {} +
    find . -name Makefile -exec sed -i 's#/usr/bin/mkdir#mkdir#' {} +
    ret=-1
    if [ $LYCIUM_BUILD_OS == "CYGWI" ]
    then
        $MAKE libpng16.la >> $buildlog 2>&1
        ret=$?
    else
        $MAKE >> $buildlog 2>&1
        ret=$?
    fi
    cd $OLDPWD
    return $ret
}

package() {
    cd $builddir/$ARCH-build
    ret=-1
    if [ $LYCIUM_BUILD_OS == "CYGWI" ]
    then   
        $MAKE install-libLTLIBRARIES >> $buildlog 2>&1
        $MAKE install-library-links >> $buildlog 2>&1
        $MAKE install-pkgincludeHEADERS >> $buildlog 2>&1
        $MAKE install-header-links >> $buildlog 2>&1
        $MAKE install-pkgconfigDATA >> $buildlog 2>&1
        ret=$?
    else
        $MAKE install >> $buildlog 2>&1
        ret=$?
    fi
    cd $OLDPWD
    return $ret
}

check() {
    cd $builddir/$ARCH-build
    $MAKE pngtest pngunknown pngstest pngvalid pngimage pngcp timepng >> $buildlog 2>&1
    sed -i 's#/bin/bash#/bin/env bash#' pngtest pngunknown pngstest pngvalid pngimage pngcp timepng
    sed -i '/.*check-TESTS: $(check_PROGRAMS)/c\check-TESTS: #$(check_PROGRAMS)' Makefile
    cd $OLDPWD
    if [ $ARCH == "armeabi-v7a" ]
    then
        unsetarm32ENV
    fi
    if [ $ARCH == "arm64-v8a" ]
    then
        unsetarm64ENV
    fi
    unset host
    echo "The test must be on an OpenHarmony device!"
    # test CMD
    # make check-TESTS
}

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

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
}

关键技术点

  • source envset.sh 导入 lycium 环境变量
  • setarm64ENV 设置交叉编译环境,host=aarch64-linux
  • export CPP=“… -E” 显式设置 C 预处理器,避免使用 C++ 编译器
  • find … sed 替换 Makefile 中的路径,适配鸿蒙环境
  • Cygwin 特殊处理(LYCIUM_BUILD_OS == “CYGWI”)

构建命令

# 必须先构建 zlib
./build.sh zlib
./build.sh libpng

2.3 fribidi - 双向文本处理

依赖关系:无依赖

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: 小肉头君 <chenbaodi@huawei.com>
# Maintainer: 小肉头君 <chenbaodi@huawei.com>

pkgname=fribidi
pkgver=v1.0.12
pkgrel=0
pkgdesc="The Free Implementation of the Unicode Bidirectional Algorithm."
url="https://github.com/fribidi/fribidi"
archs=("arm64-v8a")
license=("LGPL-2.1-only")
depends=()
makedepends=("unzip" "yacc" "flex")
# 原仓地址: https://github.com/$pkgname/$pkgname/archive/refs/tags/$pkgver.tar.gz, 因网络原因使用镜像
source="https://gitee.com/mirrors/$pkgname/repository/archive/$pkgver.zip"

autounpack=true
downloadpackage=true
buildtools="meson"

builddir=$pkgname-$pkgver
packagename=$builddir.zip

source envset.sh
autogenflag=true
buildhostc2man=true
host=
originalPath=
prepare() {
    mkdir -p $builddir/$ARCH-build
    # 编译c2man
    if $buildhostc2man
    then
        # 原仓地址: https://github.com/fribidi/c2man/archive/refs/heads/master.zip,因网络原因使用镜像
        curl -f -L https://gitee.com/lycium_pkg_mirror/c2man/repository/archive/master.zip -o c2man.zip
        if [ $? != 0 ]
        then
            return -1
        fi
        unzip c2man.zip > $buildlog 2>&1
        cd c2man-master
        PKG_CONFIG_LIBDIR="${pkgconfigpath}" ./Configure -d -e > $buildlog 2>&1
        $MAKE VERBOSE=1 >> $buildlog 2>&1
        cd $OLDPWD
        buildhostc2man=false
    fi
    # 把c2man的路径配置到PATH中
    originalPath=$PATH
    export PATH=$PATH:$LYCIUM_ROOT/../thirdparty/$pkgname/c2man-master
    if [ $ARCH == "armeabi-v7a" ]
    then
        setarm32ENV
        host=arm-linux
    fi
    if [ $ARCH == "arm64-v8a" ]
    then
        setarm64ENV
        host=aarch64-linux
    fi

    if $autogenflag
    then
        cd $builddir
        ./autogen.sh > $buildlog 2>&1
        autogenflag=false
        cd $OLDPWD
    fi

}

build() {
    # Meson 交叉编译配置
    local abs_builddir="${LYCIUM_ROOT}/../thirdparty/${pkgname}/${builddir}"
    local cross_file="${abs_builddir}/ohos-cross.ini"
    local build_path="${abs_builddir}/ohos-build"
    
    cat > "$cross_file" << EOF
[binaries]
c = '${CC}'
cpp = '${CXX}'
ar = '${AR}'
strip = '${STRIP}'
pkgconfig = 'pkg-config'

[built-in options]
prefix = '/usr'

[host_machine]
system = 'linux'
cpu_family = '${ARCH/arm64-v8a/aarch64}'
cpu = '${ARCH/arm64-v8a/aarch64}'
endian = 'little'
EOF

    meson setup "$build_path" "$abs_builddir" \
        --cross-file "$cross_file" \
        --prefix="/usr" \
        -Ddefault_library=both \
        -Dc_args=$(echo $CFLAGS | sed 's/ /","/g; s/^/"/; s/$/"/') \
        -Dcpp_args=$(echo $CXXFLAGS | sed 's/ /","/g; s/^/"/; s/$/"/') > "$buildlog" 2>&1
    
    ninja -C "$build_path" >> "$buildlog" 2>&1
    ret=$?
    return $ret
}

package() {
    local abs_builddir="${LYCIUM_ROOT}/../thirdparty/${pkgname}/${builddir}"
    cd "$abs_builddir"
    # 使用 lycium 标准的 destdir 变量,确保使用当前 ARCH
    local destdir="${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}"
    
    # meson install 使用 destdir
    DESTDIR="${destdir}" ninja -C "ohos-build" install >> "$buildlog" 2>&1
}

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

# 清理环境
cleanbuild() {
    rm -rf ${PWD}/$builddir ${PWD}/c2man.zip ${PWD}/c2man-master #${PWD}/$packagename   
}

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
}

关键注意

  1. c2man 工具编译:fribidi 需要先编译 c2man 文档生成工具
curl -f -L https://gitee.com/lycium_pkg_mirror/c2man/repository/archive/master.zip -o c2man.zip
unzip c2man.zip
cd c2man-master
./Configure -d -e
$MAKE
  1. **PATH **环境变量:将 c2man 加入 PATH
export PATH=$PATH:$LYCIUM_ROOT/../thirdparty/$pkgname/c2man-master
  1. autogen.sh:执行 autogen 生成 configure 脚本
  2. prefix=/usr:导致安装路径特殊
    1. 头文件安装在 usr/include 而非 include
    2. pkgconfig 文件在 usr/lib/pkgconfig 而非 lib/pkgconfig

构建命令

./build.sh fribidi

三、第 2 层:中间依赖库适配

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
}

关键技术点

  1. 子模块下载:glib 需要 gvdb 和 proxy-libintl 两个子模块
git clone https://gitee.com/lycium_pkg_mirror/gvdb.git $builddir/subprojects/gvdb
git clone --depth 1 --branch 0.4 https://gitee.com/lycium_pkg_mirror/proxy-libintl.git $builddir/subprojects/proxy-libintl
  1. pkgconfigpath 循环收集:遍历 depends 数组,拼接所有依赖的 pkgconfig 路径
  2. 交叉编译文件替换:使用 sed 替换 ohos_sdk 占位符为实际路径
ohos_sdk_path=${OHOS_SDK//\/\\\/}
sed -i 's/ohos_sdk/'"$ohos_sdk_path"'/g' $ARCH-cross-file.txt
  1. –pkg-config-path 参数:Meson 特有的依赖查找参数

构建命令

# 必须先构建依赖
./build.sh libffi
./build.sh pcre2
./build.sh glib

3.2 freetype2 - 字体渲染引擎

依赖关系:依赖 zlib、libpng、bzip2、brotli

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: tyBrave <tianyong13@huawei.com>
# Maintainer: tyBrave <tianyong13@huawei.com>

pkgname=freetype2
pkgver=VER-2-13-0
pkgrel=0
pkgdesc="FreeType is a freely available software library to render fonts."
url="https://freetype.org"
archs=("arm64-v8a")
license=("GPL FTL")
depends=("zlib" "bzip2" "brotli" "libpng")
makedepends=()
# 官方下载地址https://sourceforge.net/projects/freetype/files/$pkgname/$pkgver/freetype-$pkgver.tar.xz受网络影响可能存在下载失败的情况,现使用gitee镜像可以与官方仓库保持同步
source="https://gitee.com/lycium_pkg_mirror/${pkgname:0:8}/repository/archive/$pkgver.zip"

autounpack=true
downloadpackage=true

builddir=${pkgname:0:8}-${pkgver}
packagename=$builddir.zip

testdir=

prepare() {
    testdir=`pwd`/$builddir/$ARCH-build/freetype-cmake-testbuild
    rm -rf $testdir
    mkdir -p $testdir
}

build() {
    cd $builddir
    ${OHOS_SDK}/native/build-tools/cmake/bin/cmake "$@" -DOHOS_ARCH=$ARCH -DFT_DISABLE_BROTLI=OFF -DFT_DISABLE_BZIP2=OFF -DFT_DISABLE_HARFBUZZ=ON -DFT_DISABLE_PNG=OFF -DFT_DISABLE_ZLIB=OFF -DFT_ENABLE_ERROR_STRINGS=ON -B$testdir/ftb -S./ -L > `pwd`/$ARCH-build/build.log 2>&1
    make -j4 -C $testdir/ftb >> `pwd`/$ARCH-build/build.log 2>&1
    ret=$?
    cd $OLDPWD
    return $ret
}

package() {
    cd "$builddir"
    make -C $testdir/ftb install >> `pwd`/$ARCH-build/build.log 2>&1
    cd $OLDPWD
}

check() {
    echo -e " \n
    cmake_minimum_required(VERSION 3.16.5) \n
    project(freetype-cmake-testbuild)\n

    set(FREETYPE_LIBRARY $LYCIUM_ROOT/usr/$pkgname/$ARCH/lib) \n
    set(FREETYPE_INCLUDE_DIRS $LYCIUM_ROOT/usr/$pkgname/$ARCH/include/freetype2) \n
        
    include_directories($LYCIUM_ROOT/usr/$pkgname/$ARCH/include/freetype2) \n

    add_executable(freetype-cmake-test main.c)\n
        
    target_link_libraries(freetype-cmake-test PUBLIC $LYCIUM_ROOT/usr/$pkgname/$ARCH/lib/libfreetype.a)\n
    target_link_libraries(freetype-cmake-test PUBLIC $LYCIUM_ROOT/usr/brotli/$ARCH/lib/libbrotlidec.so)\n
    target_link_libraries(freetype-cmake-test PUBLIC $LYCIUM_ROOT/usr/bzip2/$ARCH/lib/libbz2.a)\n
    target_link_libraries(freetype-cmake-test PUBLIC $LYCIUM_ROOT/usr/libpng/$ARCH/lib/libpng.a)\n
    target_link_libraries(freetype-cmake-test PUBLIC $LYCIUM_ROOT/usr/zlib/$ARCH/lib/libz.a)\n

    enable_testing()\n
    add_test(freetype-cmake-test freetype-cmake-test)" > $testdir/CMakeLists.txt



    echo -e " \n
    #include <stdio.h> \n
    #include <stdlib.h> \n

    #include <ft2build.h> \n
    #include <freetype/freetype.h> \n

    FT_Library library; \n

    int main(int argc,char*argv[]) \n
    { \n
       FT_Error error; \n
       FT_Int major = 0; \n
       FT_Int minor = 0; \n
       FT_Int patch = 0; \n

       error = FT_Init_FreeType(&library); \n
       if (error) return EXIT_FAILURE; \n

       FT_Library_Version(library, &major, &minor, &patch); \n
       if (major != FREETYPE_MAJOR|| minor != FREETYPE_MINOR|| patch != FREETYPE_PATCH) return EXIT_FAILURE; \n
       printf(\"FT_Library_Version:%d.%d.%d\", major, minor, patch);\n
       error = FT_Done_FreeType(library); \n
       if (error) return EXIT_FAILURE; \n
     
       return EXIT_SUCCESS;
    }" > $testdir/main.c

    mkdir -p $testdir/tb
    cd $testdir/tb
    
    ${OHOS_SDK}/native/build-tools/cmake/bin/cmake $testdir -DCMAKE_TOOLCHAIN_FILE=$OHOS_SDK/native/build/cmake/ohos.toolchain.cmake -DCMAKE_BUILD_TYPE=Release -DOHOS_ARCH=$ARCH >> $testdir/../build.log 2>&1
    ${OHOS_SDK}/native/build-tools/cmake/bin/cmake --build . --config Release >> $testdir/../build.log 2>&1
    ret=$?
    cd $OLDPWD
    echo "The test must be on an OpenHarmony device!"
    return $ret
    # cd freetype-cmake-testbuild/tb
    # ctest -V -C Release
}

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

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
}

关键技术点

  1. CMake 构建:使用 CMake 而非 Meson
  2. 特性开关:通过 -DFT_DISABLE_* 控制启用/禁用哪些依赖
-DFT_DISABLE_BROTLI=OFF    # 启用 brotli
-DFT_DISABLE_BZIP2=OFF     # 启用 bzip2
-DFT_DISABLE_PNG=OFF       # 启用 libpng
-DFT_DISABLE_ZLIB=OFF      # 启用 zlib
-DFT_DISABLE_HARFBUZZ=ON   # 禁用 harfbuzz
  1. ${pkgname:0:8} 字符串截取:freetype2 源码目录名是 freetype 而非 freetype2
  2. CMake 自动查找依赖:CMake 工具链会自动处理依赖库路径

构建命令

# 按顺序构建所有依赖
./build.sh zlib
./build.sh libpng
./build.sh brotli
./build.sh bzip2
./build.sh freetype2

3.3 fontconfig - 字体配置库

依赖关系:依赖 freetype2、libpng、zlib、bzip2、brotli、libexpat

HPKBUILD 完整配置

# Contributor: 小肉头君 <chenbaodi@huawei.com>
# Maintainer: 小肉头君 <chenbaodi@huawei.com>

pkgname=fontconfig
pkgver=2.14.2
pkgrel=0
pkgdesc="Font configuration and customization library."
url="https://gitlab.freedesktop.org/fontconfig/fontconfig"
archs=("arm64-v8a")
license=("HPND" "Public Domain" "MIT-Modern-Variant" "MIT")
depends=("freetype2" "libpng" "zlib" "bzip2" "brotli" "libexpat")
makedepends=()
source="https://www.freedesktop.org/software/${pkgname}/release/${pkgname}-${pkgver}.tar.gz"

autounpack=true
downloadpackage=true
buildtools="configure"

builddir=$pkgname-$pkgver 
packagename=$builddir.tar.gz 
autogenflag=true
testpatch=true

source envset.sh
host=
prepare() {
    mkdir -p $builddir/$ARCH-build
    if [ $ARCH == "armeabi-v7a" ]
    then
        setarm32ENV
        host=arm-linux
    fi
    if [ $ARCH == "arm64-v8a" ]
    then
        setarm64ENV
        host=aarch64-linux
    fi
    if $autogenflag
    then
        cd $builddir
        ./autogen.sh > `pwd`/build.log 2>&1
        cd $OLDPWD
        autogenflag=false
    fi
}

build() {
    cd $builddir/$ARCH-build
    # 显式设置 C 预处理器,避免 configure 自动检测使用 C++ 编译器
    export CPP="${CC:-${LYCIUM_ROOT}/../toolchain/${host}-ohos-clang} -E"
    FREETYPE_CFLAGS="-I${LYCIUM_ROOT}/usr/freetype2/$ARCH/include/freetype2 ${LYCIUM_ROOT}/usr/bzip2/$ARCH/lib/libbz2.a \
    ${LYCIUM_ROOT}/usr/libpng/$ARCH/lib/libpng.a ${LYCIUM_ROOT}/usr/brotli/$ARCH/lib/libbrotlicommon.so \
    ${LYCIUM_ROOT}/usr/brotli/$ARCH/lib/libbrotlienc.so ${LYCIUM_ROOT}/usr/brotli/$ARCH/lib/libbrotlidec.so -lz" \
    FREETYPE_LIBS="${LYCIUM_ROOT}/usr/freetype2/$ARCH/lib/libfreetype.a" CFLAGS="$FREETYPE_CFLAGS $FREETYPE_LIBS" \
    PKG_CONFIG_PATH="${pkgconfigpath}" ../configure "$@" --host=$host --disable-libxml2 --enable-static \
        --enable-shared --disable-silent-rules > `pwd`/build.log 2>&1 # --sysconfdir="/etc" --localstatedir="/var" --datarootdir="/share" 
    make -j4 >> `pwd`/build.log 2>&1
    # 对最关键一步的退出码进行判断
    ret=$?
    cd $OLDPWD
    return $ret
}

package() {
    cd $builddir/$ARCH-build
    make install >> `pwd`/build.log 2>&1
    cd $OLDPWD
}

check() {
    # 下载字库
    # 测试过程需要curl下载字体,为避免测试机下载失败,采用提前下载好
    cd $builddir/$ARCH-build/test
    mkdir noto
    curl -s -o noto/noto.zip https://noto-website-2.storage.googleapis.com/pkgs/NotoSans-hinted.zip
    cd $OLDPWD
    cd $builddir/$ARCH-build/test/noto
    unzip noto.zip  > `pwd`/build.log 2>&1
    tar -zcf noto.tar.gz *.ttf README LICENSE_OFL.txt
    cp noto.tar.gz ../
    cd $OLDPWD
    # 编译测试用例
    cd $builddir/$ARCH-build/test
    make test-pthread test-crbug1004254 test-bz89617 test-bz131804 test-migration \
    test-bz96676 test-name-parse test-bz106618 test-bz106632 test-issue107 test-bz1744377 \
    test-issue180 test-family-matching >> `pwd`/build.log 2>&1
    if $testpatch
    then
        sed -i '435d' ../../test/run-test.sh
        sed -i '434a cp $MyPWD/noto.tar.gz "$FONTDIR"/noto.tar.gz' ../../test/run-test.sh
        sed -i '/.*(cd "$FONTDIR"; unzip noto.zip)/c\    (cd "$FONTDIR"; tar -zxf noto.tar.gz)' ../../test/run-test.sh
        testpatch=false
    fi
    sed -i '/.*check-TESTS: $(check_PROGRAMS) $(check_SCRIPTS)/c\check-TESTS: $(check_SCRIPTS) #$(check_PROGRAMS)' Makefile
    sed -i '/.*test-bz89617.log: test-bz89617$(EXEEXT)/c\test-bz89617.log: #test-bz89617$(EXEEXT)' Makefile
    sed -i '/.*test-bz131804.log: test-bz131804$(EXEEXT)/c\test-bz131804.log: #test-bz131804$(EXEEXT)' Makefile
    sed -i '/.*test-bz96676.log: test-bz96676$(EXEEXT)/c\test-bz96676.log: #test-bz96676$(EXEEXT)' Makefile
    sed -i '/.*test-name-parse.log: test-name-parse$(EXEEXT)/c\test-name-parse.log: #test-name-parse$(EXEEXT)' Makefile
    sed -i '/.*test-bz106632.log: test-bz106632$(EXEEXT)/c\test-bz106632.log: #test-bz106632$(EXEEXT)' Makefile
    sed -i '/.*test-issue107.log: test-issue107$(EXEEXT)/c\test-issue107.log: #test-issue107$(EXEEXT)' Makefile
    sed -i '/.*test-issue110.log: test-issue110$(EXEEXT)/c\test-issue110.log: #test-issue110$(EXEEXT)' Makefile
    sed -i '/.*test-d1f48f11.log: test-d1f48f11$(EXEEXT)/c\test-d1f48f11.log: #test-d1f48f11$(EXEEXT)' Makefile
    sed -i '/.*test-bz1744377.log: test-bz1744377$(EXEEXT)/c\test-bz1744377.log: #test-bz1744377$(EXEEXT)' Makefile
    sed -i '/.*test-issue180.log: test-issue180$(EXEEXT)/c\test-issue180.log: #test-issue180$(EXEEXT)' Makefile
    sed -i '/.*test-family-matching.log: test-family-matching$(EXEEXT)/c\test-family-matching.log: #test-family-matching$(EXEEXT)' Makefile

    cd $OLDPWD
    if [ $ARCH == "armeabi-v7a" ]
    then
        unsetarm32ENV
    fi
    if [ $ARCH == "arm64-v8a" ]
    then
        unsetarm64ENV
    fi
    unset host
    echo "The test must be on an OpenHarmony device!"
    # real test
    # copy 依赖库
    # make -C test check-TESTS
    
}

cleanbuild(){
    rm -rf ${PWD}/$builddir #${PWD}/$packagename
}

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
}

关键技术点

  1. autogen.sh:需要先生成 configure 脚本
  2. **显式****设置 FREETYPE_CFLAGS/**LIBS:fontconfig 需要直接指定 freetype2 及其所有依赖的库路径
FREETYPE_CFLAGS="-I.../include/freetype2 .../libbz2.a .../libpng.a .../libbrotlicommon.so -lz"
FREETYPE_LIBS=".../libfreetype.a"
CFLAGS="$FREETYPE_CFLAGS $FREETYPE_LIBS"
  1. configure 选项
    1. –disable-libxml2:禁用 libxml2 依赖
    2. –enable-static --enable-shared:同时构建静态库和动态库

构建命令

# 按顺序构建所有依赖
./build.sh freetype2
./build.sh libexpat
./build.sh fontconfig

3.4 harfbuzz - 文本整形引擎

依赖关系:依赖 freetype2、zlib、libpng、brotli、bzip2

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: 小肉头君 <chenbaodi@huawei.com>, 城meto <myxuan475@126.com>
# Maintainer: 小肉头君 <chenbaodi@huawei.com>

pkgname=harfbuzz 
pkgver=7.1.0 
pkgrel=0 
pkgdesc="HarfBuzz text shaping engine."
url="https://github.com/harfbuzz/harfbuzz"
archs=("arm64-v8a")
license=("Old MIT" "OFL-1.1" "MIT" "Apache-2.0")
depends=("freetype2" "zlib" "libpng" "brotli" "bzip2")
makedepends=()

# 原仓地址: https://github.com/$pkgname/$pkgname/archive/refs/tags/$pkgver.tar.gz, 因网络原因使用镜像
source="https://gitee.com/lycium_pkg_mirror/$pkgname/repository/archive/$pkgver.zip"

autounpack=true
downloadpackage=true
buildtools="configure"

builddir=$pkgname-$pkgver 
packagename=$builddir.zip

source envset.sh
autogenflag=true
host=

firstflag=true

prepare() {

    if [ $firstflag == true ]
    then
        firstflag=false
        #这里屏蔽使用python脚本来检测参数,oh上面不支持python
        sed -i 's/^TESTS += \$(dist_check_SCRIPTS/#TESTS += \$(dist_check_SCRIPTS/g' $builddir/src/Makefile.am
    fi   

    mkdir -p $builddir/$ARCH-build
    if [ $ARCH == "armeabi-v7a" ]
    then
        setarm32ENV
        host=arm-linux
    fi
    if [ $ARCH == "arm64-v8a" ]
    then
        setarm64ENV
        host=aarch64-linux
    fi
    if $autogenflag
    then
        cd $builddir
        ./autogen.sh > `pwd`/build.log 2>&1
        cd $OLDPWD
        autogenflag=false
    fi
}

build() {
    cd $builddir/$ARCH-build
    # 显式设置 C 预处理器,避免 configure 自动检测使用 C++ 编译器
    export CPP="${CC:-${LYCIUM_ROOT}/../toolchain/${host}-ohos-clang} -E"
    # 添加 bzip2 库路径到 LDFLAGS
    export LDFLAGS="-L${LYCIUM_ROOT}/usr/bzip2/$ARCH/lib"
    PKG_CONFIG_LIBDIR=${pkgconfigpath} ../configure "$@" --host=$host --with-freetype=yes --with-icu=no --with-glib=no -with-cairo=no --enable-static > `pwd`/build.log 2>&1
    make -j4 VERBOSE=1 >> `pwd`/build.log 2>&1
    ret=$?
    cd $OLDPWD
    return $ret
}

# 安装打包
package() {
    cd $builddir
    make -C $ARCH-build install >> `pwd`/$ARCH-build/build.log 2>&1
    cd $OLDPWD
}

# 测试,需要在 ohos 设备上进行
check() {
    cd $builddir/$ARCH-build/src
    make test-algs test-array test-bimap test-iter test-machinery test-map test-multimap test-number test-ot-tag test-priority-queue test-set test-serialize test-unicode-ranges test-vector test-repacker test-classdef-graph >> `pwd`/../build.log 2>&1
    if [ $ARCH == "armeabi-v7a" ]
    then
        unsetarm32ENV
    fi
    if [ $ARCH == "arm64-v8a" ]
    then
        unsetarm64ENV
    fi

    sed -i 's/^check-TESTS: \$(check_PROGRAMS/check-TESTS: #\$(check_PROGRAMS/g' Makefile
    sed -i 's/^test-algs.log: test-algs/test-algs.log:# test-algs/g' Makefile
    sed -i 's/^test-array.log: test-array/test-array.log:# test-array/g' Makefile
    sed -i 's/^test-bimap.log: test-bimap/test-bimap.log:# test-bimap/g' Makefile
    sed -i 's/^test-iter.log: test-iter/test-iter.log:# test-iter/g' Makefile
    sed -i 's/^test-machinery.log: test-machinery/test-machinery.log:# test-machinery/g' Makefile
    sed -i 's/^test-map.log: test-map/test-map.log:# test-map/g' Makefile
    sed -i 's/^test-multimap.log: test-multimap/test-multimap.log:# test-multimap/g' Makefile
    sed -i 's/^test-number.log: test-number/test-number.log:# test-number/g' Makefile
    sed -i 's/^test-ot-tag.log: test-ot-tag/test-ot-tag.log:# test-ot-tag/g' Makefile
    sed -i 's/^test-priority-queue.log: test-priority-queue/test-priority-queue.log:# test-priority-queue/g' Makefile
    sed -i 's/^test-set.log: test-set/test-set.log:# test-set/g' Makefile
    sed -i 's/^test-serialize.log: test-serialize/test-serialize.log:# test-serialize/g' Makefile
    sed -i 's/^test-unicode-ranges.log: test-unicode-ranges/test-unicode-ranges.log:# test-unicode-ranges/g' Makefile
    sed -i 's/^test-vector.log: test-vector/test-vector.log:# test-vector/g' Makefile
    sed -i 's/^test-repacker.log: test-repacker/test-repacker.log:# test-repacker/g' Makefile
    sed -i 's/^test-classdef-graph.log: test-classdef-graph/test-classdef-graph.log:# test-classdef-graph/g' Makefile

    cd $OLDPWD
    unset host
    echo "The test must be on an OpenHarmony device!"
    # real test CMD
    # make -C src check-TESTS
}

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

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
}

关键技术点

  1. Makefile.am 修改:屏蔽 Python 脚本检测(鸿蒙不支持 Python)
sed -i 's/^TESTS += \$(dist_check_SCRIPTS/#TESTS += \$(dist_check_SCRIPTS/g' $builddir/src/Makefile.am
  1. LDFLAGS 设置:显式添加 bzip2 库路径
export LDFLAGS="-L${LYCIUM_ROOT}/usr/bzip2/$ARCH/lib"
  1. configure 选项
    1. –with-freetype=yes:启用 freetype2 支持
    2. –with-icu=no:禁用 ICU(国际化组件)
    3. –with-glib=no:禁用 glib 依赖
    4. –with-cairo=no:禁用 Cairo 图形库

构建命令

# 按顺序构建所有依赖
./build.sh freetype2
./build.sh zlib
./build.sh libpng
./build.sh brotli
./build.sh bzip2
./build.sh harfbuzz

四、第 3 层:核心依赖库适配

4.1 libass - 字幕渲染库

依赖关系:依赖 freetype2、fribidi、harfbuzz、libpng、zlib、fontconfig

HPKBUILD 完整配置

# Contributor: 小肉头君 <chenbaodi@huawei.com>
# Maintainer: 小肉头君 <chenbaodi@huawei.com>

pkgname=libass
pkgver=0.17.1
pkgrel=0
pkgdesc="libass is a portable subtitle renderer for the ASS/SSA (Advanced Substation Alpha/Substation Alpha) subtitle format."
url="https://github.com/libass/libass"
archs=("arm64-v8a")
license=("ISC License")
depends=("freetype2" "fribidi" "harfbuzz" "libpng" "zlib" "fontconfig") # fontconfig是测试需要的三方库
makedepends=()
source="https://github.com/$pkgname/$pkgname/archive/refs/tags/$pkgver.tar.gz"

autounpack=true
downloadpackage=true
buildtools="configure"

builddir=$pkgname-$pkgver 
packagename=$builddir.tar.gz

source envset.sh
autogenflag=true
host=
prepare() {
    mkdir -p $builddir/$ARCH-build
    if [ $ARCH == "armeabi-v7a" ]
    then
        setarm32ENV
        host=arm-linux
    fi
    if [ $ARCH == "arm64-v8a" ]
    then
        setarm64ENV
        host=aarch64-linux
    fi

    if $autogenflag
    then
        cd $builddir
        ./autogen.sh > $buildlog 2>&1
        cd $OLDPWD
        autogenflag=false
    fi
}

build() {
    cd $builddir/$ARCH-build
    # 显式设置 C 预处理器,避免 configure 自动检测使用 C++ 编译器
    export CPP="${CC:-${LYCIUM_ROOT}/../toolchain/${host}-ohos-clang} -E"
    # 设置 PKG_CONFIG_PATH 包含所有依赖库的 pkgconfig
    export PKG_CONFIG_PATH="${LYCIUM_ROOT}/usr/harfbuzz/$ARCH/lib/pkgconfig:${LYCIUM_ROOT}/usr/freetype2/$ARCH/lib/pkgconfig:${LYCIUM_ROOT}/usr/fribidi/$ARCH/lib/pkgconfig:${LYCIUM_ROOT}/usr/fontconfig/$ARCH/lib/pkgconfig:${LYCIUM_ROOT}/usr/libexpat/$ARCH/lib/pkgconfig:${LYCIUM_ROOT}/usr/libpng/$ARCH/lib/pkgconfig:${LYCIUM_ROOT}/usr/zlib/$ARCH/lib/pkgconfig:${LYCIUM_ROOT}/usr/bzip2/$ARCH/lib/pkgconfig:${LYCIUM_ROOT}/usr/brotli/$ARCH/lib/pkgconfig"
    FRIBIDI_CFLAGS="-I${LYCIUM_ROOT}/usr/fribidi/$ARCH/usr/include/fribidi -I${LYCIUM_ROOT}/usr/fribidi/$ARCH/usr/include" FRIBIDI_LIBS="-L${LYCIUM_ROOT}/usr/fribidi/$ARCH/usr/lib -lfribidi" FREETYPE_CFLAGS="-I${LYCIUM_ROOT}/usr/freetype2/$ARCH/include/freetype2" FREETYPE_LIBS="-L${LYCIUM_ROOT}/usr/freetype2/$ARCH/lib -L${LYCIUM_ROOT}/usr/brotli/$ARCH/lib -L${LYCIUM_ROOT}/usr/bzip2/$ARCH/lib -lfreetype -lbz2 -lbrotlicommon -lbrotlienc -lbrotlidec -lz" FONTCONFIG_CFLAGS="-I${LYCIUM_ROOT}/usr/fontconfig/$ARCH/include" FONTCONFIG_LIBS="-L${LYCIUM_ROOT}/usr/fontconfig/$ARCH/lib -L${LYCIUM_ROOT}/usr/libexpat/$ARCH/lib -lfontconfig -lexpat" HARFBUZZ_CFLAGS="-I${LYCIUM_ROOT}/usr/harfbuzz/$ARCH/include/harfbuzz" HARFBUZZ_LIBS="-L${LYCIUM_ROOT}/usr/harfbuzz/$ARCH/lib -lharfbuzz" ../configure "$@" --enable-test --enable-static --host=$host >> $buildlog 2>&1
    make -j4 >> `pwd`/build.log 2>&1
    # 对最关键一步的退出码进行判断
    ret=$?
    cd $OLDPWD
    return $ret
}

package() {
    cd $builddir/$ARCH-build
    make install >> $buildlog 2>&1
    cd $OLDPWD
    if [ $ARCH == "armeabi-v7a" ]
    then
        unsetarm32ENV
    fi
    if [ $ARCH == "arm64-v8a" ]
    then
        unsetarm64ENV
    fi
    unset host
}

check() {
    echo "The test must be on an OpenHarmony device!"
    # real test
    # 测试时需要把Unbutu中的usr/share/fonts打包推送到ohos设备中,放在ohos设备同一路径中
    # ./test/test testass.png  ../compare/test/sub1.ass 0.03
}

cleanbuild(){
    rm -rf ${PWD}/$builddir #${PWD}/$packagename
}

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
}

关键技术点

  1. 硬编码 PKG_CONFIG_PATH:libass 不使用循环收集,而是直接硬编码所有依赖路径
export PKG_CONFIG_PATH="${LYCIUM_ROOT}/usr/harfbuzz/$ARCH/lib/pkgconfig:${LYCIUM_ROOT}/usr/freetype2/$ARCH/lib/pkgconfig:..."
  1. **显式****设置 CFLAGS/**LIBS:每个依赖库都需要单独指定头文件和库路径
FRIBIDI_CFLAGS="-I.../usr/include/fribidi -I.../usr/include"
FRIBIDI_LIBS="-L.../usr/lib -lfribidi"
FREETYPE_CFLAGS="-I.../include/freetype2"
FREETYPE_LIBS="-L.../lib -L.../brotli/lib -L.../bzip2/lib -lfreetype -lbz2 -lbrotlicommon ..."
FONTCONFIG_CFLAGS="-I.../include"
FONTCONFIG_LIBS="-L.../lib -L.../libexpat/lib -lfontconfig -lexpat"
HARFBUZZ_CFLAGS="-I.../include/harfbuzz"
HARFBUZZ_LIBS="-L.../lib -lharfbuzz"
  1. fribidi 路径特殊:头文件在 usr/include/fribidi 而非 include
  2. configure 选项
    1. –enable-test:启用测试(需要字体文件)
    2. –enable-static:构建静态库

构建命令

# 必须先构建所有依赖
./build.sh freetype2
./build.sh fontconfig
./build.sh harfbuzz
./build.sh fribidi
./build.sh libass

4.2 libplacebo - GPU 渲染库

依赖关系:无运行时依赖(构建时依赖 meson、ninja)

HPKBUILD 完整配置

#!/bin/bash
# HPKBUILD for libplacebo
# =============================================================================
# libplacebo OpenHarmony 适配脚本
# -----------------------------------------------------------------------------
# - 使用 Meson 构建系统
# - 高性能视频渲染库,支持多种 GPU 后端
# - 依赖:Vulkan-Headers(已包含在 3rdparty)
# - 注意:鸿蒙可能无完整 Vulkan 支持,尝试编译核心库
# =============================================================================

# Contributor: OpenHarmony adaptation
# Maintainer: OpenHarmony adaptation

# --- 包元数据(lycium 索引与下载用)---
pkgname=libplacebo
pkgver=7.362
pkgrel=0
pkgdesc="Reusable library for GPU-accelerated video/image rendering primitives"
url="https://code.videolan.org/videolan/libplacebo"
# 交叉目标 ABI;鸿蒙 arm64-v8a
archs=("arm64-v8a")
license=("LGPL-2.1-or-later")
# 运行时依赖:无
depends=()
# 构建依赖:meson, ninja
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"

        # 复制整个 libplacebo 源码
        cp -rf "$srcpath"/* "$builddir/"
        cp -rf "$srcpath"/.git* "$builddir/" 2>/dev/null || true

        # 初始化子模块(3rdparty 依赖)
        if [ -f "$builddir/.gitmodules" ]; then
            cd "$builddir"
            git submodule update --init --recursive 2>/dev/null || true
            cd ..
        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():使用 Meson 构建 libplacebo
# -----------------------------------------------------------------------------
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 交叉编译配置文件
    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'

[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 配置:禁用 Vulkan 等可能不支持的特性
    meson setup ohos-build \
        --cross-file ohos-cross.ini \
        --prefix=/usr \
        --buildtype=release \
        -D vulkan=disabled \
        -D opengl=disabled \
        -D d3d11=disabled \
        -D glslang=disabled \
        -D shaderc=disabled \
        -D lcms=disabled \
        -D dovi=disabled \
        -D libdovi=disabled \
        -D demos=false \
        -D tests=false \
        -D bench=false \
        > "$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=$?

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

    echo "Build completed successfully!"
    cd "$OLDPWD"
}

# -----------------------------------------------------------------------------
# package():打包产物
# -----------------------------------------------------------------------------
package() {
    cd "$builddir"

    # 使用 lycium 标准的 destdir 变量
    : ${destdir:=${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}}

    # 手动复制编译产物
    mkdir -p "${destdir}/usr/lib"
    mkdir -p "${destdir}/usr/include"
    mkdir -p "${destdir}/usr/lib/pkgconfig"

    # 复制库文件
    cp -f ohos-build/src/libplacebo.so* "${destdir}/usr/lib/" 2>/dev/null || true

    # 复制头文件
    cp -rf src/include/libplacebo "${destdir}/usr/include/" 2>/dev/null || true

    # 复制构建生成的 config.h
    if [ -f ohos-build/src/include/libplacebo/config.h ]; then
        cp -f ohos-build/src/include/libplacebo/config.h "${destdir}/usr/include/libplacebo/"
        echo "  ✅ Installed config.h"
    fi

    # 复制 pkg-config 文件
    if [ -f ohos-build/meson-private/libplacebo.pc ]; then
        cp -f ohos-build/meson-private/libplacebo.pc "${destdir}/usr/lib/pkgconfig/"
    fi

    echo "Package completed!"
    cd "$OLDPWD"
}

# -----------------------------------------------------------------------------
# check():验证构建产物
# -----------------------------------------------------------------------------
check() {
    : ${destdir:=${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}}

    # 检查关键文件是否存在
    if [ ! -f "${destdir}/usr/lib/libplacebo.so" ] && [ ! -f "${destdir}/usr/lib/libplacebo.a" ]; then
        echo "ERROR: libplacebo library not found!"
        exit 1
    fi

    if [ ! -d "${destdir}/usr/include/libplacebo" ]; then
        echo "ERROR: libplacebo header not found!"
        exit 1
    fi

    echo "Check passed!"
}

# -----------------------------------------------------------------------------
# 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}/${pkgname}-${pkgver}"

    # 清理 output
    rm -rf "${LYCIUM_ROOT}/output/${pkgname}"*

    # 清理 usr 产物
    rm -rf "${LYCIUM_ROOT}/usr/${pkgname}"

    echo "Clean completed"
}

关键技术点

  1. 本地****源码:使用 autounpack=false 和 srcpath 从 Projects 目录复制源码
  2. 子模块初始化:git submodule update --init --recursive 初始化 3rdparty 依赖
  3. 禁用 GPU 特性:鸿蒙 PC 可能无完整 Vulkan 支持,需要禁用
-D vulkan=disabled
-D opengl=disabled
-D d3d11=disabled
-D glslang=disabled
-D shaderc=disabled
  1. config.h 必须复制:这是 Meson 构建时生成的配置文件,未包含在源码中
  2. pkg-config 文件:需要手动复制 libplacebo.pc 到 pkgconfig 目录

构建命令

# libplacebo 无运行时依赖,直接构建
./build.sh libplacebo

4.3 FFmpeg - 音视频编解码库

依赖关系:依赖 zlib、libiconv 等

HPKBUILD 关键配置

# Contributor: Jeff Han <hanjinfei@foxmail.com>
# Maintainer: Jeff Han <hanjinfei@foxmail.com>
pkgname=FFmpeg
pkgver=8.0
pkgrel=0
pkgdesc="FFmpeg is a collection of libraries and tools to process multimedia content such as audio, video, subtitles and related metadata."
url="https://github.com/FFmpeg/FFmpeg/"
archs=("armeabi-v7a" "arm64-v8a")
license=("GPL2" "GPL3" "LGPL3" "MIT" "X11" "BSD-styl")
depends=("openssl_1_1_1w")
makedepends=()
source="https://github.com/FFmpeg/$pkgname/archive/refs/tags/$pkgver.tar.gz"

autounpack=false
downloadpackage=false
buildtools="configure"

builddir=$pkgname-${pkgver}
packagename=$builddir.tar.gz
srcpath="${LYCIUM_ROOT}/../Projects/FFmpeg"
source envset.sh
buildhost=true
arch=
ldflags=

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
        
        # 添加执行权限
        chmod +x "$builddir/configure" 2>/dev/null || true
        find "$builddir" -name "*.sh" -type f -exec chmod +x {} \; 2>/dev/null || true
        
        echo "Prepare completed in: $builddir"
    else
        echo "ERROR: Source not found at $srcpath"
        exit 1
    fi

    mkdir -p $pkgname-$ARCH-build
    cp -rf $builddir $pkgname-$ARCH-build/
    
    # 构建主机工具(bin2c 等)
    if [ $buildhost == true ]; then
        echo "Building host tools..."
        cd $pkgname-$ARCH-build/$builddir
        ./configure --enable-static --disable-shared --disable-doc \
            --target-os=linux --disable-optimizations > $publicbuildlog 2>&1
        $MAKE tools/bin2c >> $publicbuildlog 2>&1 || $MAKE ffbuild/bin2c >> $publicbuildlog 2>&1
        buildhost=false
        cd $OLDPWD
    fi
    
    cd  $pkgname-$ARCH-build/$builddir
    if [ -f ../../FFmpeg_oh_test.patch ]; then
        patch -p1 < ../../FFmpeg_oh_test.patch
    fi
    cd $OLDPWD

    if [ $ARCH == "armeabi-v7a" ]
    then
        setarm32ENV
        arch=arm
        ldflags="-L${OHOS_SDK}/native/sysroot/usr/lib/arm-linux-ohos"
    elif [ $ARCH == "arm64-v8a" ]
    then
        setarm64ENV
        arch=aarch64
        ldflags="-L${OHOS_SDK}/native/sysroot/usr/lib/aarch64-linux-ohos"
    else
        echo "${ARCH} not support"
        return -1
    fi

    return $ret
}

build() {
    cd $pkgname-$ARCH-build/$builddir
    PKG_CONFIG_LIBDIR="${pkgconfigpath}" ./configure "$@" --enable-neon --enable-asm --enable-network \
    --disable-vulkan --enable-cross-compile --disable-librtmp --disable-x86asm --enable-openssl --enable-protocols \
    --enable-static --enable-shared --disable-doc --disable-htmlpages --target-os=linux --arch=$arch \
    --cc=${CC} --ld=${CC} --strip=${STRIP} --host-cc="gcc" --host-ld="gcc" --host-os=linux \
    --host-ldflags=${ldflags} --sysroot=${OHOS_SDK}/native/sysroot > $buildlog 2>&1
    $MAKE >> $buildlog 2>&1
    ret=$?
    cd $OLDPWD
    return $ret
}

package() {
    cd $pkgname-$ARCH-build/$builddir
    $MAKE install >> $buildlog 2>&1
    cd $OLDPWD
}

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/ffmpeg/hnp.json ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}/
        ${HNP_TOOL} pack \
            -i ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH} \
            -o ${LYCIUM_ROOT}/output/$ARCH/
        echo "HNP pack completed: ${LYCIUM_ROOT}/output/$ARCH/${pkgname}.hnp"
    else
        echo "WARNING: hnpcli not found, skip HNP packaging"
    fi
}

checktestfiles() {
    cd $pkgname-$ARCH-build/$builddir/tests/ref

    tmpdir=("fate" "acodec" "lavf" "lavf-fate" "pixfmt" "seek" "vsynth")
    for dir in ${tmpdir[*]}
    do
        for file in `ls $dir`
        do
            if [ ! -f $dir/$file ]; then
                continue
            fi
            str=`cat $dir/$file | grep "\*tests"`
            if [ ! -z "$str" ]
            then
                sed -i.bak 's/\*tests/tests/g' $dir/$file
            fi
        done
    done

    cd $OLDPWD
}

copyhostbin() {
    file=$1
    if [[ -f tests/$file ]] && [[ ! -f tests/$file.${ARCH} ]]
    then
        mv tests/$file tests/$file.${ARCH}
        cp ../../$builddir/tests/$file tests/$file
    fi
}

check() {
    # 跳过测试(需要 x86_64 测试工具,交叉编译环境不支持)
    return 0
    cd $pkgname-$ARCH-build/$builddir
    # disable running cmd
    sed -i.bak 's/        $(Q)$(SRC_PATH)\/tests\/fate-run.sh/#        $(Q)$(SRC_PATH)\/tests\/fate-run.sh/g' tests/Makefile
    # disable check git sources
    sed -i.bak 's/include $(SRC_PATH)\/tests\/fate\/source.mak/#include $(SRC_PATH)\/tests\/fate\/source.mak/g' tests/Makefile
    # disable check ffprobe,this use xmllint command, which ohos is not support
    sed -i.bak 's/include $(SRC_PATH)\/tests\/fate\/ffprobe.mak/#include $(SRC_PATH)\/tests\/fate\/ffprobe.mak/g' tests/Makefile

    # change x86 cmd for generate test target
    mv ffmpeg ffmpeg.${ARCH} 2>/dev/null || true
    # 如果主机工具已构建,直接使用当前目录的 ffmpeg
    if [ ! -f ffmpeg ]; then
        cp ../../$builddir/ffmpeg ./ 2>/dev/null || true
    fi
    retrytimes=0
    ret=0
    while true
    do
        $MAKE check >> $buildlog 2>&1
        if [ $? -eq 0 ]
        then
            break;
        fi

        copyhostbin base64
        copyhostbin audiomatch
        copyhostbin audiogen
        copyhostbin videogen
        copyhostbin tiny_psnr
        copyhostbin tiny_ssim
        copyhostbin rotozoom

        let retrytimes=$retrytimes+1
        if [ $retrytimes -gt 4 ]
        then
            ret=1
            break
        fi
    done

    mv ffmpeg.${ARCH} ffmpeg
    for file in `ls tests/*.${ARCH}`
    do
        tmpfile=${file%.*}
        mv $file $tmpfile
    done

    # reduction running cmd for real test
    sed -i.bak 's/#        $(Q)$(SRC_PATH)\/tests\/fate-run.sh/        $(Q)$(SRC_PATH)\/tests\/fate-run.sh/g' tests/Makefile
    cd $OLDPWD
    checktestfiles

    echo "The test must be on an OpenHarmony device!"
    # skip running test on host
    # real test CMD
    # make check

    return $ret
}

recoverpkgbuildenv() {
    unset arch
    unset ldflags
    if [ $ARCH == "armeabi-v7a" ]
    then
        unsetarm32ENV
    elif [ $ARCH == "arm64-v8a" ]
    then
        unsetarm64ENV
    else
        echo "${ARCH} not support"
        return -1
    fi
}

# 清理环境
cleanbuild() {
    rm -rf ${PWD}/${builddir} ${PWD}/$pkgname-arm64-v8a-build ${PWD}/$pkgname-armeabi-v7a-build #${PWD}/$packagename
}

关键技术点

  • FFmpeg 使用自有的 configure 系统
  • –enable-cross-compile 启用交叉编译
  • –enable-shared 构建动态库
  • –extra-cflags 和 --extra-ldflags 传递交叉编译参数

构建命令

./build.sh zlib
./build.sh FFmpeg

五、第 4 层:目标库 mpv 适配

5.1 mpv - 媒体播放器

依赖关系:依赖 FFmpeg、libplacebo、libass(传递依赖包含 11 个库)

完整的依赖链

mpv 直接依赖:
├── FFmpeg
├── libplacebo
└── libass

mpv 传递依赖(通过 libass):
├── fontconfig
│   ├── freetype2
│   │   ├── zlib
│   │   ├── libpng
│   │   └── brotli
│   └── libexpat
├── harfbuzz
│   ├── freetype2 (已包含)
│   └── glib
│       ├── libffi
│       └── pcre2
└── fribidi

mpv 传递依赖(通过 libplacebo):
├── vulkan-headers
└── spirv-headers

总计:14 个依赖库

HPKBUILD 完整配置

#!/bin/bash

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

pkgname=mpv
pkgver=0.41.0
pkgrel=0
pkgdesc="A free, open source, and cross-platform media player"
url="https://mpv.io"
archs=("arm64-v8a")
license=("LGPL-2.1-or-later")
depends=("FFmpeg" "libplacebo")
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/"
        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 -I${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/include"
    export CXXFLAGS="--target=aarch64-linux-ohos --sysroot=${OHOS_SDK}/native/sysroot -O2 -fPIC -I${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/include"
    export LDFLAGS="--target=aarch64-linux-ohos --sysroot=${OHOS_SDK}/native/sysroot"

    # 创建 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/FFmpeg/${ARCH}/lib/pkgconfig', '${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/lib/pkgconfig', '${LYCIUM_ROOT}/usr/libass/${ARCH}/lib/pkgconfig', '${LYCIUM_ROOT}/usr/fontconfig/${ARCH}/lib/pkgconfig', '${LYCIUM_ROOT}/usr/libexpat/${ARCH}/lib/pkgconfig', '${LYCIUM_ROOT}/usr/harfbuzz/${ARCH}/lib/pkgconfig', '${LYCIUM_ROOT}/usr/fribidi/${ARCH}/usr/lib/pkgconfig', '${LYCIUM_ROOT}/usr/freetype2/${ARCH}/lib/pkgconfig', '${LYCIUM_ROOT}/usr/zlib/${ARCH}/lib/pkgconfig', '${LYCIUM_ROOT}/usr/libpng/${ARCH}/lib/pkgconfig', '${LYCIUM_ROOT}/usr/brotli/${ARCH}/lib/pkgconfig']

[built-in options]
c_args = ['--target=aarch64-linux-ohos', '--sysroot=${OHOS_SDK}/native/sysroot', '-O2', '-fPIC', '-I${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/include']
cpp_args = ['--target=aarch64-linux-ohos', '--sysroot=${OHOS_SDK}/native/sysroot', '-O2', '-fPIC', '-I${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/include']
c_link_args = ['--target=aarch64-linux-ohos', '--sysroot=${OHOS_SDK}/native/sysroot', '-L${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/lib']
cpp_link_args = ['--target=aarch64-linux-ohos', '--sysroot=${OHOS_SDK}/native/sysroot', '-L${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/lib']

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

    # 设置 FFmpeg、libplacebo 和 libass 及其所有依赖的 pkg-config 路径
    export PKG_CONFIG_PATH="${LYCIUM_ROOT}/usr/FFmpeg/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/lib/pkgconfig:${LYCIUM_ROOT}/usr/libass/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/fontconfig/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/libexpat/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/harfbuzz/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/fribidi/${ARCH}/usr/lib/pkgconfig:${LYCIUM_ROOT}/usr/freetype2/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/zlib/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/libpng/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/brotli/${ARCH}/lib/pkgconfig:${PKG_CONFIG_PATH}"
    export PKG_CONFIG_LIBDIR="${LYCIUM_ROOT}/usr/FFmpeg/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/lib/pkgconfig:${LYCIUM_ROOT}/usr/libass/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/fontconfig/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/libexpat/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/harfbuzz/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/fribidi/${ARCH}/usr/lib/pkgconfig:${LYCIUM_ROOT}/usr/freetype2/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/zlib/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/libpng/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/brotli/${ARCH}/lib/pkgconfig"
    
    echo "PKG_CONFIG_PATH: $PKG_CONFIG_PATH"
    pkg-config --list-all | grep -E 'libav|libplacebo|libass' || true

    # 设置 LDFLAGS 包含 libplacebo 的库路径
    export LDFLAGS="-L${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/lib ${LDFLAGS}"

    # Meson 配置:禁用图形界面和不支持的特性
    meson setup ohos-build \
        --cross-file ohos-cross.ini \
        --prefix=/usr \
        --buildtype=release \
        -Dgpl=false \
        -Dcplayer=false \
        -Dlibmpv=true \
        -Dbuild-date=false \
        -Dtests=false \
        -Dfuzzers=false \
        -Dmanpage-build=disabled \
        -Dcdda=disabled \
        -Ddvbin=disabled \
        -Ddvdnav=disabled \
        -Djavascript=disabled \
        -Djpeg=disabled \
        -Dlcms2=disabled \
        -Dlibarchive=disabled \
        -Dlibavdevice=disabled \
        -Dlibbluray=disabled \
        -Dlua=disabled \
        -Drubberband=disabled \
        -Dsdl2-gamepad=disabled \
        -Dsdl2-audio=disabled \
        -Dsdl2-video=disabled \
        -Dsubrandr=disabled \
        -Duchardet=disabled \
        -Dvapoursynth=disabled \
        -Dzimg=disabled \
        -Dalsa=disabled \
        -Daudiounit=disabled \
        -Dcoreaudio=disabled \
        -Djack=disabled \
        -Dopenal=disabled \
        -Dopensles=disabled \
        -Doss-audio=disabled \
        -Dpipewire=disabled \
        -Dpulse=disabled \
        -Dsndio=disabled \
        -Dgl=disabled \
        -Degl-android=disabled \
        -Degl-x11=disabled \
        -Degl-wayland=disabled \
        -Ddrm=disabled \
        -Dgbm=disabled \
        -Dx11=disabled \
        -Dwayland=disabled \
        -Dcocoa=disabled \
        -Dmacos-media-player=disabled \
        -Dios-gl=disabled \
        -Dandroid-media-ndk=disabled \
        -Dcaca=disabled \
        -Dplain-gl=disabled \
        -Dzlib=enabled \
        > "$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}

    # 手动复制编译产物
    mkdir -p "${destdir}/usr/bin"
    mkdir -p "${destdir}/usr/lib"
    mkdir -p "${destdir}/usr/include/mpv"

    # 复制 mpv 可执行文件
    if [ -f ohos-build/mpv ]; then
        cp -f ohos-build/mpv "${destdir}/usr/bin/mpv"
        chmod +x "${destdir}/usr/bin/mpv"
        echo "  ✅ Installed mpv binary"
    fi

    # 复制 libmpv 库文件
    if ls ohos-build/libmpv.so* 1> /dev/null 2>&1; then
        find ohos-build -maxdepth 1 -name 'libmpv.so*' -type f -exec cp -f {} "${destdir}/usr/lib/" \;
        echo "  ✅ Installed libmpv library"
    fi

    # 复制头文件
    if [ -d include/mpv ]; then
        cp -rf include/mpv/* "${destdir}/usr/include/mpv/"
        echo "  ✅ Installed mpv headers"
    fi

    find "${destdir}" -name "*.bak" -type f -delete 2>/dev/null || true
    find "${destdir}" -name "*:Zone.Identifier" -type f -delete 2>/dev/null || true

    echo "Package completed!"
    return 0
}

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

关键技术点

  1. pkg_config_path 包含 11 个路径
    1. 直接依赖:FFmpeg、libplacebo、libass
    2. 传递依赖:fontconfig、harfbuzz、fribidi、freetype2、zlib、libpng、brotli、libexpat
  2. fribidi 路径特殊
'${LYCIUM_ROOT}/usr/fribidi/${ARCH}/usr/lib/pkgconfig'
# 注意是 usr/lib/pkgconfig 而非 lib/pkgconfig
  1. libplacebo 路径特殊
'${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/lib/pkgconfig'
# 注意是 usr/lib/pkgconfig 而非 lib/pkgconfig
  1. 头文件路径显式添加
export CFLAGS="... -I${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/include"
  1. 链接路径显式添加
c_link_args = ['...','-L${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/lib']

构建命令

# 完整构建流程(按依赖顺序)
cd lycium_plusplus/lycium

# 第 1 层:基础依赖
./build.sh zlib
./build.sh libpng
./build.sh brotli
./build.sh libexpat
./build.sh fribidi
./build.sh libffi
./build.sh pcre2

# 第 2 层:中间依赖
./build.sh glib
./build.sh freetype2
./build.sh harfbuzz
./build.sh fontconfig

# 第 3 层:核心依赖
./build.sh libass
./build.sh vulkan-headers
./build.sh spirv-headers
./build.sh libplacebo
./build.sh FFmpeg

# 第 4 层:目标库
./build.sh mpv

5.2 鸿蒙PC 上验证结果

解压 mpv 编译产物,进入库目录后查看文件,验证该 arm64 架构 ELF 共享库的有效性,再为其添加执行权限,最后执行鸿蒙系统签名操作

# 解压mpv编译产物
tar -zxf mpv_0.41.0.tar.gz

# 进入库目录
cd usr/lib

# 查看文件是否存在
ls

# 验证是否为鸿蒙 arm64 有效库(核心验证)
file libmpv.so.2.5.0

# 查看ELF魔数,确认架构合法
od -N 4 libmpv.so.2.5.0

通过 strings 命令提取 libmpv.so.2.5.0 共享库中的字符串信息,并结合 grep 过滤出版本号相关内容,成功匹配到 mpv v0.41.0-UNKNOWN 标识,验证了该动态库的版本正确性与编译完整性

strings libmpv.so.2.5.0 | grep "0.41.0"

六、多层依赖管理核心技巧

6.1 依赖状态检查

lycium 使用 usr/hpk_build.csv 跟踪构建状态:

# 查看某个库的构建状态
cat usr/hpk_build.csv | grep freetype2

# 查看所有已构建的库
cat usr/hpk_build.csv | awk -F',' '{print $1}' | sort -u

6.2 依赖构建失败处理

# 清理特定库的构建状态
cat usr/hpk_build.csv | grep -v freetype2 > /tmp/hpk_temp.csv
mv /tmp/hpk_temp.csv usr/hpk_build.csv

# 清理产物
rm -rf usr/freetype2
rm -rf ../thirdparty/freetype2/freetype2-*/

# 重新构建
./build.sh freetype2

6.3 pkg-config 路径调试

# 在 build() 函数中添加调试输出
echo "PKG_CONFIG_PATH: $PKG_CONFIG_PATH"
pkg-config --list-all | grep -E 'libav|libplacebo|libass|fontconfig|harfbuzz|freetype2' || true

6.4 头文件路径验证

# 验证各库头文件是否存在
ls /home/weishuo/lycium_plusplus/lycium/usr/libplacebo/arm64-v8a/usr/include/libplacebo/
ls /home/weishuo/lycium_plusplus/lycium/usr/fribidi/arm64-v8a/usr/include/
ls /home/weishuo/lycium_plusplus/lycium/usr/fontconfig/arm64-v8a/include/

七、常见问题与解决方案(FAQ)

Q1:构建 mpv 时报错 “dependency libass not found”?

A:libass 未构建或 pkg-config 路径不完整。

# 检查 libass 状态
cat usr/hpk_build.csv | grep libass

# 构建 libass 及其依赖
./build.sh fontconfig
./build.sh harfbuzz
./build.sh fribidi
./build.sh libass

# 验证 pkg-config
export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:${LYCIUM_ROOT}/usr/libass/${ARCH}/lib/pkgconfig"
pkg-config --list-all | grep libass

Q2:编译时报错 “fatal error: fribidi.h: No such file or directory”?

A:fribidi 头文件在 usr/include 而非 include

# 在 mpv 的 build() 中添加
export CFLAGS="${CFLAGS} -I${LYCIUM_ROOT}/usr/fribidi/${ARCH}/usr/include"

# 或者在 Meson 交叉编译配置中
c_args = ['...', '-I${LYCIUM_ROOT}/usr/fribidi/${ARCH}/usr/include']

Q3:链接时报错 “unable to find library -lplacebo”?

A:libplacebo 库文件在 usr/lib 而非 lib

# 在 Meson 交叉编译配置中
c_link_args = ['...','-L${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/lib']

Q4:lycium 直接跳过 mpv 构建?

A:构建缓存导致,需要清理。

# 清理 hpk_build.csv 中的 mpv 记录
cat usr/hpk_build.csv | grep -v mpv > /tmp/hpk_temp.csv
mv /tmp/hpk_temp.csv usr/hpk_build.csv

# 清理产物
rm -rf usr/mpv
rm -rf ../thirdparty/mpv/mpv-*/

# 重新构建
./build.sh mpv

Q5:glib 构建时报错 “subproject gvdb not found”?

A:glib 需要下载子模块 gvdb 和 proxy-libintl。

# 检查 prepare() 函数中的子模块下载逻辑
git clone https://gitee.com/lycium_pkg_mirror/gvdb.git $builddir/subprojects/gvdb
git clone --depth 1 --branch 0.4 https://gitee.com/lycium_pkg_mirror/proxy-libintl.git $builddir/subprojects/proxy-libintl

# 如果网络失败,可以手动下载后放到对应目录

Q6:fribidi 构建时报错 “c2man not found”?

A:fribidi 需要先编译 c2man 文档生成工具。

# 检查 prepare() 函数中的 c2man 编译逻辑
curl -f -L https://gitee.com/lycium_pkg_mirror/c2man/repository/archive/master.zip -o c2man.zip
unzip c2man.zip
cd c2man-master
./Configure -d -e
$MAKE

# 将 c2man 加入 PATH
export PATH=$PATH:$LYCIUM_ROOT/../thirdparty/fribidi/c2man-master

Q7:fontconfig 构建时报错 “freetype-config not found”?

A:fontconfig 需要显式设置 FREETYPE_CFLAGS 和 FREETYPE_LIBS。

# 在 fontconfig 的 build() 函数中
export FREETYPE_CFLAGS="-I${LYCIUM_ROOT}/usr/freetype2/$ARCH/include/freetype2"
export FREETYPE_LIBS="${LYCIUM_ROOT}/usr/freetype2/$ARCH/lib/libfreetype.a"

# 同时需要设置所有 freetype2 依赖的库路径
export FREETYPE_CFLAGS="$FREETYPE_CFLAGS ${LYCIUM_ROOT}/usr/bzip2/$ARCH/lib/libbz2.a ..."
export FREETYPE_LIBS="$FREETYPE_LIBS ${LYCIUM_ROOT}/usr/brotli/$ARCH/lib/libbrotlicommon.so ..."

Q8:harfbuzz 构建时报错 “Python detection failed”?

A:鸿蒙不支持 Python,需要屏蔽 Makefile.am 中的 Python 检测。

# 在 harfbuzz 的 prepare() 函数中
sed -i 's/^TESTS += \$(dist_check_SCRIPTS/#TESTS += \$(dist_check_SCRIPTS/g' $builddir/src/Makefile.am

Q9:libass 构建时报错 “harfbuzz not found”?

A:libass 不使用循环收集 pkgconfig,而是硬编码所有路径。

# 在 libass 的 build() 函数中,确保 PKG_CONFIG_PATH 包含 harfbuzz
export PKG_CONFIG_PATH="${LYCIUM_ROOT}/usr/harfbuzz/$ARCH/lib/pkgconfig:${LYCIUM_ROOT}/usr/freetype2/$ARCH/lib/pkgconfig:..."

# 同时需要设置 HARFBUZZ_CFLAGS 和 HARFBUZZ_LIBS
export HARFBUZZ_CFLAGS="-I${LYCIUM_ROOT}/usr/harfbuzz/$ARCH/include/harfbuzz"
export HARFBUZZ_LIBS="-L${LYCIUM_ROOT}/usr/harfbuzz/$ARCH/lib -lharfbuzz"

Q10:libplacebo 构建时报错 “vulkan not found”?

A:鸿蒙 PC 可能无完整 Vulkan 支持,需要禁用相关特性。

# 在 libplacebo 的 meson setup 中
meson setup ohos-build \
    --cross-file ohos-cross.ini \
    -D vulkan=disabled \
    -D opengl=disabled \
    -D d3d11=disabled \
    -D glslang=disabled \
    -D shaderc=disabled

Q11:mpv构建时报错 “meson配置失败,找不到 FFmpeg”?

A:pkg_config_path 未包含 FFmpeg 或路径错误。

# 在 mpv 的 ohos-cross.ini 中,确保包含 FFmpeg 路径
pkg_config_path = [
    '${LYCIUM_ROOT}/usr/FFmpeg/${ARCH}/lib/pkgconfig',
    '${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/lib/pkgconfig',
    ...
]

# 验证 FFmpeg pkgconfig 文件是否存在
ls ${LYCIUM_ROOT}/usr/FFmpeg/${ARCH}/lib/pkgconfig/
# 应该看到 libavcodec.pc libavformat.pc libavutil.pc 等文件

Q12:构建产物中没有 .so 动态库,只有 .a 静态库

A:检查构建配置是否启用了动态库构建。

# CMake 项目(如 freetype2)
-DBUILD_SHARED_LIBS=ON

# configure 项目(如 libpng)
--enable-shared --enable-static

# Meson 项目(如 glib)
-Ddefault_library=both

Q13:多个库都依赖 zlib,是否需要多次构建?

A:不需要。lycium 会自动检查 hpk_build.csv,已构建的库会跳过。

# 查看 zlib 构建状态
cat usr/hpk_build.csv | grep zlib

# 如果显示已构建,后续库会自动使用
# 如果需要强制重新构建 zlib
cat usr/hpk_build.csv | grep -v zlib > /tmp/hpk_temp.csv
mv /tmp/hpk_temp.csv usr/hpk_build.csv
rm -rf usr/zlib
./build.sh zlib

Q14:如何调试 pkg-config 路径是否正确?

A:在 build() 函数中添加调试输出。

# 在 build() 函数开头添加
echo "=== PKG_CONFIG_PATH ==="
echo $PKG_CONFIG_PATH
echo "=== pkg-config list ==="
pkg-config --list-all | grep -E 'libav|libplacebo|libass|fontconfig|harfbuzz|freetype2|fribidi'
echo "=== check specific package ==="
pkg-config --cflags libass
pkg-config --libs libass

Q15:构建顺序错误导致依赖找不到怎么办?

A:严格按照自底向上的顺序构建。

# 错误示例:先构建 libass 再构建 freetype2
./build.sh libass  # ❌ 失败,freetype2 未构建
./build.sh freetype2

# 正确示例:先构建底层依赖
./build.sh zlib
./build.sh libpng
./build.sh freetype2
./build.sh fontconfig
./build.sh harfbuzz
./build.sh fribidi
./build.sh libass  # ✅ 成功

八、技术总结

8.1 多层依赖适配核心原则

  1. 自底向上构建:严格按依赖层次顺序,从第 1 层到第 4 层
  2. 依赖声明准确:depends 数组声明所有直接依赖
  3. pkg-config 完整:收集所有传递依赖的 pkgconfig 路径
  4. 路径差异处理:fribidi、libplacebo 等库路径特殊,需显式配置

8.2 关键技术点

技术点 解决方案 示例
依赖声明 depends=(“lib1” “lib2”) libass 依赖 6 个库
pkg-config 收集 遍历 depends 数组 prepare() 函数
特殊路径处理 判断路径是否存在 fribidi usr/lib/pkgconfig
头文件路径 CFLAGS 显式添加 libplacebo usr/include
链接路径 c_link_args 配置 libplacebo usr/lib
构建缓存 清理 hpk_build.csv grep -v 命令

8.3 通用适配能力

此套多层依赖树适配方案可直接迁移至:

  • VLC媒体播放器 - 类似的音视频依赖链
  • GStreamer多媒体框架 - 更复杂的多层依赖
  • 任何具有多层依赖的 C/C++ 项目
Logo

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

更多推荐