1 前言

本文是《Ports软件组织模式与跨平台包管理神器pkgsrc》的续作。

在前文中,我们聊到了 pkgsrc 的高度可移植性。作为鸿蒙玩家,我很好奇它在鸿蒙系统上的表现究竟如何。经过一番折腾,我成功在鸿蒙开发板和鸿蒙容器中跑通了基础流程。

接下来的内容,将完整还原这一实验过程,希望能为对开源软件移植和包管理器移植感兴趣的朋友提供一些参考。

2 环境配置

2.1 准备鸿蒙环境

这套流程仅在鸿蒙开发板(dayu200)和鸿蒙容器(docker-mini-openharmony)上进行了验证,并未在鸿蒙 PC 上验证。如果你想要尽可能成功复现,建议使用跟我相同的环境,用鸿蒙开发板或鸿蒙容器。

如果你的环境是鸿蒙容器,无需特殊准备,可以直接往下看。

如果你的环境是鸿蒙开发版,需要先做以下准备工作:

  1. 把根目录挂载为读写,挂载命令是 mount -o remount,rw /
  2. 鸿蒙版 curl 放到你的开发板 /bin 目录中,确保能正常敲出 curl 命令。
  3. 给开发板联网,因为编译软件包的过程中需要联网下载源码。
  4. 创建 /tmp 目录,因为 pkgsrc 工作过程中会使用到 /tmp 目录。

2.2 准备基础命令行工具

创建一个目录用来装命令行工具

mkdir -p /data/my-tools
cd /data/my-tools

准备一些基础的命令行工具,以满足前置依赖

curl -fSLO https://github.com/Harmonybrew/ohos-busybox/releases/download/1.37.0/busybox-1.37.0-ohos-arm64.tar.gz
curl -fSLO https://github.com/Harmonybrew/ohos-git/releases/download/2.45.2/git-2.45.2-ohos-arm64.tar.gz
curl -fSLO https://github.com/Harmonybrew/ohos-gawk/releases/download/5.3.2/gawk-5.3.2-ohos-arm64.tar.gz
curl -fSLO https://github.com/Harmonybrew/ohos-grep/releases/download/3.12/grep-3.12-ohos-arm64.tar.gz
curl -fSLO https://github.com/Harmonybrew/ohos-diffutils/releases/download/3.12/diffutils-3.12-ohos-arm64.tar.gz
curl -fSLO https://github.com/Harmonybrew/ohos-coreutils/releases/download/9.10/coreutils-9.10-ohos-arm64.tar.gz
ls | grep tar.gz$ | xargs -n 1 tar -zxf
ln -sf $(pwd)/*-ohos-arm64/bin/* /bin/

这里要注意一下,我把所有的命令全部软链接到 /bin 目录下了,这样做可以省略多处地方的环境变量配置。

解释一下这些工具的用途:

  • busybox:用里面的 unzip 命令来解压 ohos-sdk
  • git:用来下载 pkgsrc 源码树
  • gawk:pkgsrc 在 bootstrap 阶段需要用到 awk 命令
  • grep:pkgsrc 在 bootstrap 阶段需要用到标准的 GNU grep,toybox 里面的 grep 无法满足使用需求
  • diffutils:pkgsrc 在 bootstrap 阶段需要用到 diff 命令
  • coreutils:pkgsrc 在 bootstrap 阶段需要用到 tr 等命令

2.3 准备 ohos-sdk

准备鸿蒙版 ohos-sdk

sdk_download_url="https://cidownload.openharmony.cn/version/Master_Version/ohos-sdk-public_ohos/20260108_020526/version-Master_Version-ohos-sdk-public_ohos-20260108_020526-ohos-sdk-public_ohos.tar.gz"
curl -fSL -o ohos-sdk.tar.gz $sdk_download_url
mkdir /data/my-tools/ohos-sdk
tar -zxf ohos-sdk.tar.gz -C /data/my-tools/ohos-sdk
cd /data/my-tools/ohos-sdk/ohos
busybox unzip -q native-*.zip
chmod 0755 /data/my-tools/ohos-sdk/ohos/native/llvm/bin/*

把 llvm 里面的命令封装一份放到 /bin 目录下

essential_tools="clang clang++ clang-cpp ld.lld lldb llvm-addr2line llvm-ar llvm-cxxfilt llvm-nm llvm-objcopy llvm-objdump llvm-ranlib llvm-readelf llvm-size llvm-strings llvm-strip"

for executable in $essential_tools; do
    cat <<EOF > /bin/$executable
#!/bin/sh
exec /data/my-tools/ohos-sdk/ohos/native/llvm/bin/$executable "\$@"
EOF
    chmod 0755 /bin/$executable
done

做这一步同样是为了省事:如果这些命令不在 /bin 目录下,折腾 pkgsrc 的时候需要多做一些配置。

为什么要做成封装脚本而不是软链接呢?主要是因为 clang 驱动器经过软链接之后不能正常找到 sysroot。为了照顾 clang,这里干脆把所有命令都封装了,看起来也整齐一些。

最后把 llvm 软链接成 cc、gcc 等命令

cd /bin
ln -s clang cc
ln -s clang gcc
ln -s clang++ c++
ln -s clang++ g++
ln -s clang-cpp cpp
ln -s ld.lld ld
ln -s llvm-addr2line addr2line
ln -s llvm-ar ar
ln -s llvm-cxxfilt c++filt
ln -s llvm-nm nm
ln -s llvm-objcopy objcopy
ln -s llvm-objdump objdump
ln -s llvm-ranlib ranlib
ln -s llvm-readelf readelf
ln -s llvm-size size
ln -s llvm-strip strip

这样做是为了让各种开源软件能够直接在鸿蒙环境上进行编译,而无需配置 CC、CXX、LD 等变量。

3 安装 pkgsrc

下载 pkgsrc 源码树,我使用的是 2025Q4 版本的源码树

cd /data
git clone --depth 1 -b pkgsrc-2025Q4 https://github.com/NetBSD/pkgsrc.git

源码下载好之后,首先要把 bootstrap 脚本改一下

sed -i '/^BOOTSTRAP_MKCONF=/i\
\
if [ -f "/lib/ld-musl-aarch64.so.1" ]\
then\
    echo "DL_TYPE= native" >> ${TARGET_MKCONF}\
    echo "DL_LIBS=" >> ${TARGET_MKCONF}\
    echo "BUILDLINK_TRANSFORM+= rm:-ldl" >> ${TARGET_MKCONF}\
    echo "USE_BUILTIN.dl= yes" >> ${TARGET_MKCONF}\
    echo "BUILTIN_LIB_FOUND.dl= yes" >> ${TARGET_MKCONF}\
    echo "CHECK_BUILTIN.dl= no" >> ${TARGET_MKCONF}\
    echo "" >> ${TARGET_MKCONF}\
fi' /data/pkgsrc/bootstrap/bootstrap

这个修改的作用是让 pkgsrc 在编译开源软件的时候不要链接 dl 这个库,因为鸿蒙的 musl libc 不会单独提供 libdl.so,dl 相关的接口是直接集成在 libc.so 里面的。

然后是修改 Linux.bsd.lib.mk

sed -i 's/${RANLIB} -t/${RANLIB}/g' /data/pkgsrc/pkgtools/bootstrap-mk-files/files/mods/Linux.bsd.lib.mk

做这一步处理是因为 ohos-sdk 里面的 llvm-ranlib 不支持 -t 参数,所以要把这个参数去掉,以免编译开源软件时出现报错。

再把 m4 这个包也处理一下,对它打个补丁做鸿蒙适配

cat <<'EOF' > /data/pkgsrc/devel/m4/patches/patch-lib_freadahead.c
--- lib/freadahead.c
+++ lib/freadahead.c
@@ -94,6 +94,12 @@
   if (fp->state == 4 /* WR */ || fp->rp >= fp->wp)
     return 0;
   return fp->wp - fp->rp;
+#elif defined __OHOS__
+  struct {
+      unsigned flags;
+      unsigned char *rpos, *rend;
+  } *f = (void *)fp;
+  return f->rend ? f->rend - f->rpos : 0;
 #elif defined SLOW_BUT_NO_HACKS     /* users can define this */
   abort ();
   return 0;
EOF
echo "SHA1 (patch-lib_freadahead.c) = $(sha1sum /data/pkgsrc/devel/m4/patches/patch-lib_freadahead.c | awk '{print $1}')" >> /data/pkgsrc/devel/m4/distinfo

pkgsrc 仓库里面大部分软件包的构建阶段都会依赖到 m4 这个包,如果不把它修好,很多软件都会编不出来。

最后就可以开始真正的安装了,执行以下命令进行 bootstrap(引导安装)

cd /data/pkgsrc/bootstrap
./bootstrap \
  --prefix /data/pkg \
  --varbase /data/pkg/var \
  --pkgdbdir /data/pkg/pkgdb \
  --prefer-pkgsrc yes \
  --compiler clang

这个过程中它会自动编译 pkgsrc 核心工具,并安装到 /data/pkg 目录下。

到这一步,pkgsrc 就算安装完毕了。从配置工具到安装 pkgsrc,所有动作全部都是脚本化的。如果你需要反复在不同设备安装 pkgsrc,你可以把前面的所有命令拼接起来,拼接之后你就得到了一个 pkgsrc 的安装脚本。

4 从源码树安装软件包

首先,需要把 pkgsrc 核心工具添加到 PATH 中

export PATH=/data/pkg/bin:/data/pkg/sbin:$PATH

这里介绍一下 pkgsrc 的核心工具:

  • bmake 构建工具。这个是 BSD make,跟我们平时在 Linux 上用的 GNU make 有一些差异。bmake 命令在 /data/pkg/bin 目录下。
  • pkg_install 包管理套件。包含了 pkg_addpkg_adminpkg_createpkg_deletepkg_info 等命令。这些命令在 /data/pkg/sbin 目录下。

配完 PATH 后就可以安装软件包了,以 unzip 这个软件包为例,进入源码树去安装它

cd /data/pkgsrc/archivers/unzip
bmake install

安装完成的效果是这样的

在这里插入图片描述

unzip 命令可以打出帮助文档

在这里插入图片描述

pkg_info 命令查看软件包列表,能看到 unzip 在列表中

在这里插入图片描述

5 从软件源安装软件包

5.1 整体流程

pkg_install 包管理套件里面有个命令叫 pkg_add,它提供了安装二进制包的功能,支持离线和在线安装二进制包。在这个章节中,我会走一遍在线安装二进制包的流程。

需要注意的是,要体验在线安装的功能,我们还需要自己把软件源(软件仓库)搭建起来。因为目前为止还没有人为鸿蒙搭建过 pkgsrc 软件源,没有现成的源给我们用。

因此,我们整个实验流程涉及这些事情:

  • 准备制品包和包索引
  • 准备 bootstrap kit
  • 准备一个软件源
  • 上传相关文件
  • 准备一个新环境
  • 在线安装软件包

接下来我们就一步一步地完成这些操作。

5.2 准备制品包和包索引

首先准备制品包。制品包不需要用专门的命令去打,只要你 bmake install 安装过一个软件,它自己以及它的级联依赖都会被自动打包成制品包,放置在制品包目录下。在我的这个环境上,它们位于 /data/pkgsrc/packages/All 目录。

在这里插入图片描述

我们可以看到 unzip 以及它的运行期依赖 zlib 都在这个目录里面了,此外里面还有一些构建期的依赖。这里我就不去区分哪个包是干什么用的了,等下统一全部上传到软件仓库里面。

接下来生成包索引

cd /data/pkgsrc/packages/All
pkg_info -X *.tgz | gzip > pkg_summary.gz

现在 /data/pkgsrc/packages/All 目录下能看到有一个 pkg_summary.gz 文件了。

5.3 准备 bootstrap kit

我们需要将机器上的 /data/pkg 目录打成压缩包。压缩包中的内容被称为 bootstrap kit(引导套件),因为这是经过 bootstrap 生成的。我们后续需要将它分发给用户使用,对用户而言这就是一个“开箱即用”的包管理器。

打包操作是这样的

pkg_delete unzip zlib
cd /data
tar -zcf bootstrap.tar.gz /data/pkg
# 会有告警提示 “removing leading '/' from member names”,可以忽略

注意看,我打包前执行了一次 pkg_delete 操作,把刚刚装好的 unzip 和它的级联依赖 zlib 卸载掉了。如果不这么做,它们会被带到用户环境上,我们的 bootstrap kit 就不纯净了。

5.4 准备一个软件源

软件源的形态可以是 ftp 服务器或 http 服务器,我这里用 nginx 搭了个 http 服务器。

注意:nginx 配置要加上 autoindex on;

location / {
    autoindex on;
    try_files $uri $uri/ =404;
}

5.5 上传相关文件

接下来我们将制品包、包索引、bootstrap kit 上传到软件源。

由于我的软件源是 nginx 服务器,我将它们放置到默认的 /var/www/html 目录即可

在这里插入图片描述

5.6 准备一个新环境

为了体现出这个在线安装的流程是几乎“零依赖”的,我这里没有选择继续用前面配好的鸿蒙环境来做在线安装的实验,而是选择用一个新环境来作为用户环境,进行实验。

如果你用的鸿蒙环境是鸿蒙容器,那就很好处理,启动一个新容器即可。

如果你用的鸿蒙环境是鸿蒙开发板,且手上没有第二块开发板,那我建议你给开发板重刷一次镜像。因为前面的实验已经对系统目录做了不可逆的改造,只能通过重刷镜像来还原。重刷完镜像之后,需要按照 2.1 章节做一下配置,把 curl 和 /tmp 目录准备好。至于其他工具(git、diffutils、ohos-sdk 等)就不用再准备了。

5.7 执行在线安装

现在我们可以在新环境上面尝试在线安装软件包了。

首先下载 bootstrap kit,也就是“开箱即用”的包管理器

mkdir -p /data
cd /data
curl -fSLO http://192.168.0.119/bootstrap.tar.gz
tar -zxf bootstrap.tar.gz -C /

把 pkgsrc 核心工具添加到 PATH 中

export PATH=/data/pkg/bin:/data/pkg/sbin:$PATH

执行一次在线安装

export PKG_PATH="http://192.168.0.119/All"
pkg_add unzip

这是安装完成的效果,能看到 unzip 和它的级联依赖 zlib 都被安装进来了

在这里插入图片描述

6 小结

6.1 关于兼容性

我在 OpenHarmony(开源鸿蒙)上并没有处理什么棘手问题,很轻易就能将 pkgsrc 跑起来了。除了得益于 pkgsrc 本身的兼容性设计以外,还得益于 OpenHarmony 对 Linux 的兼容性,以及鸿蒙开发板、鸿蒙容器提供的 root 权限。

OpenHarmony 用的就是 Linux 内核,uname 返回值是 Linux,所以在 bootstrap 和构建软件包的过程中它能够自然而然地走到 Linux 的构建逻辑中,使用 Linux 的构建配置。

这套东西如果要在 HarmonyOS(闭源鸿蒙)中跑起来,需要面对的挑战会远大于 OpenHarmony。

首先,它的 uname 返回值不是 Linux,这可能会导致 pkgsrc 或其他软件包将其视为陌生平台,需要进行额外的适配工作。针对这个问题,有一个规避的思路:我们可以在 OpenHarmony 上把制品构建好,在HarmonyOS 上直接用在线安装的方式下载制品,避免在 HarmonyOS 上进行实时构建,这样可以减少遇到此类问题的概率。

其次,HarmonyOS 的设备并不向用户开放 root 权限,我们只能在应用沙箱环境中执行命令。而应用沙箱中的安全限制是非常多的,很容易遇到权限相关的问题,导致装进来的软件包无法正常使用。这类问题除了等系统自己解决以外没别的办法。

出于上述原因,我这次没有选择在鸿蒙 PC 上进行实验,而是选择在鸿蒙开发板、鸿蒙容器上进行实验。

6.2 进一步研究方向

我这里只是验证了基本功能,关于 pkgsrc 还有不少东西可以去实验和调试,比如可以去尝试调通 pkgin。

pkgin 是 pkgsrc 生态中另一款很常用的包管理工具,它是 pkg_install 的封装,在命令设计和使用体验上与 Debian 的 apt 类似,提供了更丰富的包管理功能。若想要得到最佳的包管理体验,pkgin 的正常工作是很重要的。

我这次实验并没有去将它构建出来,如果你感兴趣,可以自行探索。

Logo

赋能鸿蒙PC开发者,共建全场景原生生态,共享一次开发多端部署创新价值。

更多推荐