本文面向将 external-hnp 目录下的已构建 HNP 包,按照项目现有打包规则,自动合入到最终产物 base.hnp 中。教程覆盖设计目标、格式差异、Makefile 改造、合入策略、验证方法与常见问题排查,确保在同一套 HNP 流水线下实现稳定、可维护的外部包整合。

🎯 目标与范围

  • 🎯 目标:在完成 build-hnp 内所有包的构建后,自动将 external-hnp/*.hnp 的内容合并进 build-hnp/sysroot,并随 base.hnp 一并打包分发。
  • 📋 范围:兼容三类外部 HNP 格式:
    1. 📁 标准 sysroot/ 结构:归档内顶层为 sysroot/(与本工程一致)
    2. 📂 usr/ 目录结构:归档内顶层为供应商自定义路径,实际内容位于其中的 usr/(例如:dog_1.0.0/usr/bin/dogjq_1.7/usr/bin/jqwhois_5.5.10/usr/bin/whois
    3. 🗂️ 顶层标准目录结构:归档内顶层为供应商自定义路径,但直接在顶层包含标准目录(例如:autoconf_1.0.0/bin/autoconf_1.0.0/share/icu_1.0.0/lib/

📚 现有打包规则回顾

🔧 Makefile 结构

# build-hnp/Makefile

STAMP=$(patsubst %,%/.stamp,$(PKGS))
EXTERNAL_HNP=$(wildcard ../external-hnp/*.hnp)
PKGS_MARKER=.pkgs-$(OHOS_ARCH)

all: copy

copy: base.hnp
	rm -f ../entry/hnp/$(OHOS_ABI)/*.hnp
	cp $^ ../entry/hnp/$(OHOS_ABI)
	cp $^ ../entry/hnp/$(OHOS_ABI)/base-public.hnp

.PHONY: check-pkgs
check-pkgs:
	@echo "$(PKGS)" > $(PKGS_MARKER).tmp
	@if [ -f $(PKGS_MARKER) ]; then \
		if ! cmp -s $(PKGS_MARKER) $(PKGS_MARKER).tmp; then \
			echo "PKGS changed from '$$(cat $(PKGS_MARKER))' to '$(PKGS)', removing base.hnp to force rebuild..."; \
			rm -f base.hnp; \
		fi; \
	else \
		echo "Creating PKGS marker file with: $(PKGS)"; \
	fi
	@mv $(PKGS_MARKER).tmp $(PKGS_MARKER)

base.hnp: check-pkgs $(STAMP) utils/pbcopy utils/pbpaste utils/empty.a Makefile $(EXTERNAL_HNP)
	# ... 构建步骤 ...

🔗 依赖关系说明

  • 📌 $(STAMP):所有内部包的构建标记,例如 gettext/.stampbash/.stamp,由 PKGS 变量生成
  • 📦 $(EXTERNAL_HNP):使用 wildcard 函数自动发现 external-hnp 目录下的所有 .hnp 文件
  • check-pkgs.PHONY 目标,每次构建时都会检查 PKGS 变量是否改变,如果改变则删除 base.hnp 强制重新构建
  • 🔄 自动依赖检测
    • external-hnp 目录下新增、删除或修改 .hnp 文件时,base.hnp 会自动重新构建
    • PKGS 变量改变时(例如添加或删除包),check-pkgs 会检测到变化并删除 base.hnp,强制重新构建
  • ⚡ 增量构建:无需手动触发,新增外部 HNP 包或修改 PKGS 后,直接执行 create-hnp.sh 即可自动重新构建

📦 打包流程

  1. 🧹 清理阶段:删除 sysroot/share/{man,doc,info} 减小体积
  2. 🔧 工具注入:复制 pbcopy/pbpaste 与工具链运行时对象
  3. 📥 外部包合入:合并 external-hnp 目录下的所有 HNP 包到 sysroot
  4. 🔐 权限设置:确保二进制文件具有执行权限
  5. 🧹 再次清理:删除外部包可能引入的文档文件
  6. 📦 打包:使用 zip -r base.hnp sysroot 创建最终包
  7. 📋 拷贝:将 base.hnp 复制到 entry/hnp/$(OHOS_ABI),同时生成 base-public.hnp

🎨 合入策略设计

⚡ 处理优先级

base.hnp 规则中,完成工具注入后、打包前执行外部 HNP 合入,按以下优先级处理:

  1. 🥇 优先处理 sysroot/ 目录:若归档内存在 sysroot/,则直接合并其内容到当前 sysroot/ 根目录(避免出现 sysroot/sysroot 嵌套)
  2. 🥈 处理 usr/ 目录结构:若归档内不存在 sysroot/,但存在一个或多个 usr/ 目录,则将其中的 usr/binusr/libusr/includeusr/shareusr/lib/pkgconfig 映射合入到当前 sysroot/binsysroot/libsysroot/includesysroot/sharesysroot/lib/pkgconfig
  3. 🥉 处理顶层标准目录结构:若两者均不存在,检查顶层目录(如 包名/bin/包名/lib/ 等)中是否存在标准目录(binlibincludeshare),如果存在,将这些目录的内容合并到 sysroot 对应目录
  4. 🔄 兜底策略:若以上情况均不匹配,则将归档内所有内容合并到当前 sysroot/

🗺️ 目录映射规则

  • usr/binsysroot/bin
  • usr/libsysroot/lib
  • usr/includesysroot/include
  • usr/sharesysroot/share
  • usr/lib/pkgconfigsysroot/lib/pkgconfig
  • 顶层 bin/lib/include/share/ 等目录同样映射到 sysroot 对应目录

🧹 清理策略

  • 🧹 合入前清理:删除 sysroot/share/{man,doc,info} 减小体积
  • 🧹 合入后再次清理:外部包可能包含大量手册与文档,合并后再次删除 sysroot/share/{man,doc,info} 缩小体积

💻 具体实现

🔧 Makefile 修改

📝 修改文件build-hnp/Makefile

✨ 关键修改点

  1. 📦 添加外部包依赖检测
EXTERNAL_HNP=$(wildcard ../external-hnp/*.hnp)
  1. ✅ 添加 PKGS 变化检测
PKGS_MARKER=.pkgs-$(OHOS_ARCH)

.PHONY: check-pkgs
check-pkgs:
	@echo "$(PKGS)" > $(PKGS_MARKER).tmp
	@if [ -f $(PKGS_MARKER) ]; then \
		if ! cmp -s $(PKGS_MARKER) $(PKGS_MARKER).tmp; then \
			echo "PKGS changed from '$$(cat $(PKGS_MARKER))' to '$(PKGS)', cleaning build artifacts to force rebuild..."; \
			rm -f base.hnp; \
			old_pkgs="$$(cat $(PKGS_MARKER))"; \
			for pkg in $$old_pkgs; do \
				if ! echo "$(PKGS)" | grep -qw "$$pkg"; then \
					echo "  Removing stale stamp file: $$pkg/.stamp"; \
					rm -f "$$pkg/.stamp"; \
				fi; \
			done; \
			for pkg in $(PKGS); do \
				if ! echo "$$old_pkgs" | grep -qw "$$pkg"; then \
					echo "  Removing stamp file for new package: $$pkg/.stamp"; \
					rm -f "$$pkg/.stamp"; \
				fi; \
			done; \
		fi; \
	else \
		echo "Creating PKGS marker file with: $(PKGS)"; \
	fi
	@mv $(PKGS_MARKER).tmp $(PKGS_MARKER)
  1. 🔗 更新 base.hnp 依赖
base.hnp: check-pkgs $(STAMP) utils/pbcopy utils/pbpaste utils/empty.a Makefile $(EXTERNAL_HNP)
  1. 📥 外部包合入逻辑
# merge external hnp packages
@if ls ../external-hnp/*.hnp >/dev/null 2>&1; then \
	echo "Merging external HNP packages into sysroot..."; \
	tmpdir="sysroot/.external_merge"; rm -rf "$$tmpdir"; mkdir -p "$$tmpdir"; \
	for pkg in ../external-hnp/*.hnp; do \
		echo "  -> $$pkg"; \
		if command -v ditto >/dev/null 2>&1; then \
			ditto -x -k "$$pkg" "$$tmpdir" 2>/dev/null || unzip -q -o "$$pkg" -d "$$tmpdir" 2>/dev/null || true; \
		else \
			unzip -q -o "$$pkg" -d "$$tmpdir" 2>/dev/null || true; \
		fi; \
		if [ -d "$$tmpdir/sysroot" ]; then \
			echo "    Found sysroot/, merging directly..."; \
			cp -a "$$tmpdir/sysroot/." sysroot/; \
		else \
			usr_dirs="$$(find "$$tmpdir" -type d -name usr 2>/dev/null)"; \
			if [ -n "$$usr_dirs" ]; then \
				for ud in $$usr_dirs; do \
					if [ -n "$$ud" ] && [ -d "$$ud" ]; then \
						echo "    Found usr directory: $$ud"; \
						for comp in bin lib include share; do \
							if [ -d "$$ud/$$comp" ] && [ -n "$$(ls -A "$$ud/$$comp" 2>/dev/null)" ]; then \
								mkdir -p "sysroot/$$comp"; \
								cp -a "$$ud/$$comp/." "sysroot/$$comp/" 2>/dev/null || true; \
							fi; \
						done; \
						if [ -d "$$ud/lib/pkgconfig" ] && [ -n "$$(ls -A "$$ud/lib/pkgconfig" 2>/dev/null)" ]; then \
							mkdir -p "sysroot/lib/pkgconfig"; \
							cp -a "$$ud/lib/pkgconfig/." "sysroot/lib/pkgconfig/" 2>/dev/null || true; \
						fi; \
					fi; \
				done; \
			else \
				top_dirs="$$(find "$$tmpdir" -maxdepth 1 -type d ! -path "$$tmpdir" 2>/dev/null)"; \
				merged=0; \
				for top_dir in $$top_dirs; do \
					if [ -n "$$top_dir" ] && [ -d "$$top_dir" ]; then \
						for comp in bin lib include share; do \
							if [ -d "$$top_dir/$$comp" ] && [ -n "$$(ls -A "$$top_dir/$$comp" 2>/dev/null)" ]; then \
								echo "    Found $$comp/ in $$(basename "$$top_dir"), merging to sysroot/$$comp/..."; \
								mkdir -p "sysroot/$$comp"; \
								cp -a "$$top_dir/$$comp/." "sysroot/$$comp/" 2>/dev/null || true; \
								merged=1; \
							fi; \
						done; \
						if [ -d "$$top_dir/lib/pkgconfig" ] && [ -n "$$(ls -A "$$top_dir/lib/pkgconfig" 2>/dev/null)" ]; then \
							mkdir -p "sysroot/lib/pkgconfig"; \
							cp -a "$$top_dir/lib/pkgconfig/." "sysroot/lib/pkgconfig/" 2>/dev/null || true; \
							merged=1; \
						fi; \
					fi; \
				done; \
				if [ "$$merged" = "0" ]; then \
					echo "    No standard directories found, merging all content..."; \
					cp -a "$$tmpdir/." sysroot/ 2>/dev/null || true; \
				fi; \
			fi; \
		fi; \
		rm -rf "$$tmpdir"; mkdir -p "$$tmpdir"; \
	done; \
	rm -rf "$$tmpdir"; \
	echo "  Ensuring executable permissions for binaries..."; \
	find sysroot/bin -type f -exec chmod +x {} \; 2>/dev/null || true; \
else \
	echo "No external HNP packages found, skipping merge"; \
fi

✨ 关键改进点

  1. 🔄 自动依赖检测

    • 使用 EXTERNAL_HNP=$(wildcard ../external-hnp/*.hnp) 自动发现所有外部 HNP 包
    • $(EXTERNAL_HNP) 添加到 base.hnp 的依赖中,实现增量构建
    • 使用 check-pkgs .PHONY 目标检测 PKGS 变量的变化,如果改变则删除 base.hnp 强制重新构建
  2. 🍎 优先使用 ditto 解压

    • 在 macOS 上优先使用 ditto 命令解压,能够正确处理所有 ZIP 格式,包括有 “volume label” 问题的包
    • 如果系统不支持 ditto,则回退到 unzip
  3. 📁 支持三种包结构

    • 标准 sysroot/ 结构:直接合并
    • usr/ 目录结构:将 usr/binusr/lib 等映射到 sysroot/binsysroot/lib
    • 顶层标准目录结构:检查顶层目录中的 bin/lib/include/share/ 等,合并到 sysroot 对应目录
  4. 🧠 智能目录映射

    • 使用 merged 标志跟踪是否成功合并了标准目录
    • 只有在完全没有标准目录时才使用兜底策略,避免将整个包目录复制到 sysroot/ 根目录
  5. 🛡️ 增强错误处理

    • 为所有关键操作添加 2>/dev/null || true,确保单个包失败不影响整体流程
  6. 🔐 确保文件权限

    • 合入后统一为 sysroot/bin 下的所有文件添加执行权限,避免运行时出现权限问题
  7. 📝 详细日志输出

    • 添加更详细的处理日志,包括目录发现和合并过程,便于问题排查

✅ 验证方法

1. 🚀 执行全量构建

OHOS_ARCH=aarch64 OHOS_ABI=arm64-v8a sh ./create-hnp.sh

2. 🔍 检查合入是否生效

📋 典型外部包结构示例

  • external-hnp/dog.hnpdog_1.0.0/usr/bin/dog
  • external-hnp/autoconf.hnpautoconf_1.0.0/bin/autoconf
  • external-hnp/icu.hnpicu_1.0.0/lib/libicu*.so

✅ 验证映射到 sysroot

# 检查二进制文件是否在正确位置(应该在 sysroot/bin/ 下,而不是 sysroot/包名/usr/bin/)
ls -la build-hnp/sysroot/bin | grep -E "(dog|jq|whois|exa|hashdeep|autoconf|icu)"

# 检查文件权限(应该具有执行权限)
ls -l build-hnp/sysroot/bin/dog
ls -l build-hnp/sysroot/bin/jq

# 检查库文件
ls -la build-hnp/sysroot/lib | grep -E "(libicu|libjq)"

# 检查库文件和头文件
ls -la build-hnp/sysroot/lib/pkgconfig | grep -E "(icu|jq)"
ls -la build-hnp/sysroot/include | head -20

3. 📦 验证打包结果

# 检查打包产物
ls -la entry/hnp/arm64-v8a
# 预期:base.hnp 与 base-public.hnp

# 验证打包后的文件结构(确保文件在 sysroot/bin/ 下,而不是嵌套在其他目录中)
unzip -l entry/hnp/arm64-v8a/base.hnp | grep "sysroot/bin/dog"
unzip -l entry/hnp/arm64-v8a/base.hnp | grep "sysroot/bin/jq"
unzip -l entry/hnp/arm64-v8a/base.hnp | grep "sysroot/lib/libicu"
# 应该看到类似:sysroot/bin/dog 而不是 sysroot/dog_1.0.0/usr/bin/dog

4. 🔗 验证依赖关系

# 检查 base.hnp 的依赖关系
OHOS_ARCH=aarch64 make -C build-hnp -p | grep "^base.hnp:"
# 应该看到 base.hnp 依赖了 external-hnp 目录下的所有 .hnp 文件

# 测试新增文件后的自动重建
touch external-hnp/test.hnp
OHOS_ARCH=aarch64 make -C build-hnp -n base.hnp
# 应该会触发重新构建(显示构建命令)

5. 🎮 运行时验证(在应用启动后)

# 在终端中执行外部包命令
dog --version
jq --version
whois --version
icu-config --version  # 如果 icu.hnp 包含此工具

# 如果仍然提示 command not found,检查:
# - PATH 环境变量是否包含 /data/app/bin
# - 文件是否确实在 base.hnp 的 sysroot/bin/ 目录下
# - 文件权限是否正确(应该具有执行权限)

💡 说明:部分外部包可能仅提供库与头文件等,而非二进制;请根据各自 hnp.json 与包目录结构确认合入内容。

🐛 常见问题与排查

1. ❌ command not found 问题

🔍 症状:应用启动后,执行外部 HNP 包命令(如 dogjqwhois 等)提示 command not found

🔎 根本原因

  1. 📦 解压工具兼容性问题:在 macOS 上,unzip 命令遇到某些 HNP 包的 “volume label” 问题时,会显示 “skipping” 但实际上跳过了文件解压,导致临时目录为空,后续复制操作失败
  2. 📂 路径处理问题:早期实现中使用 while read 在子shell中执行,可能导致复制操作失败
  3. 🔐 文件权限问题:二进制文件可能缺少执行权限

✅ 解决方案

  1. 🍎 优先使用 ditto 解压:在 macOS 上,ditto 命令能够正确处理所有 ZIP 格式的 HNP 包,包括有 “volume label” 问题的包。实现中优先检测并使用 ditto,如果不可用则回退到 unzip
  2. 📋 简化复制逻辑:直接使用 cp -a "$$ud/$$comp/." "sysroot/$$comp/" 复制整个目录内容,避免通配符匹配失败的问题
  3. 🔐 确保文件权限:在合入后统一执行 find sysroot/bin -type f -exec chmod +x {} \; 确保所有二进制文件具有执行权限

✅ 验证方法

# 检查文件是否在正确位置
ls -la build-hnp/sysroot/bin | grep -E "(dog|jq|whois|exa|hashdeep)"

# 检查文件权限
ls -l build-hnp/sysroot/bin/dog  # 应该显示 -rwxr-xr-x 或类似的可执行权限

# 检查打包后的内容
unzip -l entry/hnp/arm64-v8a/base.hnp | grep "sysroot/bin/dog"
# 应该看到:sysroot/bin/dog 而不是 sysroot/dog_1.0.0/usr/bin/dog

# 验证解压是否成功(在构建过程中检查临时目录)
# 如果看到 "skipping: ... volume label" 但文件仍然存在,说明解压成功
# 如果目录为空,说明解压失败,需要使用 ditto

image-20251120190051932

2. 📁 sysroot/sysroot 重复目录

归档内包含顶层 sysroot/,需走"直接合并其内容到当前 sysroot 根"的分支;本实现已覆盖该情况。

3. ⚠️ 解包兼容性问题(关键问题)

🔍 "volume label"问题:在 macOS 上,某些 HNP 包使用非标准的 ZIP 格式(包含 volume label),unzip 命令会显示 “skipping: … volume label” 警告,并且实际上跳过了文件解压,导致临时目录为空。

✅ 解决方案

  • 🍎 优先使用 dittoditto 是 macOS 系统自带的解压工具,能够正确处理所有 ZIP 格式,包括有 volume label 问题的包
  • 实现中检测系统是否支持 ditto,如果支持则优先使用,否则回退到 unzip

🔍 排查方法

# 方法1:使用 ditto(推荐)
mkdir -p /tmp/test_extract
ditto -x -k external-hnp/dog.hnp /tmp/test_extract
find /tmp/test_extract -type f -name "dog"
# 应该能找到文件

# 方法2:使用 unzip(可能失败)
mkdir -p /tmp/test_extract
unzip -q external-hnp/dog.hnp -d /tmp/test_extract
ls -la /tmp/test_extract
# 如果目录为空或只有目录结构没有文件,说明解压失败
# 如果看到 "skipping: ... volume label" 但文件存在,说明解压成功(虽然显示警告)

4. 📦 文档体积膨胀

外部包合入后再次清理 man/doc/info,并在必要时对 share/ 下的样例或多余资源做裁剪。

5. ⚔️ 覆盖冲突

若外部包与本地构建产物存在同名文件,合并以外部包为后写入;如需优先保留本地产物,可在 cp -a 前增加存在性判断或采用版本号目录避免覆盖。

6. 🗺️ 文件路径映射错误

🔍 症状:打包后的 base.hnp 中包含 sysroot/包名/usr/bin/sysroot/包名/bin/ 这样的路径,而不是 sysroot/bin/

🔎 原因

  • 合入逻辑未正确识别和处理 usr 目录结构
  • 对于顶层直接包含标准目录的包(如 autoconf_1.0.0/bin/),未正确映射到 sysroot/bin/

✅ 解决方案

  • 确保正确处理 usr/ 目录结构:将 usr/binusr/lib 等映射到 sysroot/binsysroot/lib
  • 对于顶层标准目录结构:检查顶层目录中是否存在 bin/lib/include/share/ 等标准目录,如果存在,将这些目录的内容合并到 sysroot 对应目录
  • 验证复制操作确实将文件复制到了目标位置:ls -la build-hnp/sysroot/bin | grep -E "(dog|autoconf)"

7. 📂 顶层目录结构未正确映射

🔍 症状:某些外部包(如 autoconf.hnpautomake.hnpicu.hnp)的内容没有被正确合并到 sysroot 对应目录,而是整个包目录被复制到了 sysroot/ 根目录下。

🔎 原因:这些包的目录结构是 包名/bin/包名/lib/ 等,而不是 包名/usr/bin/,之前的实现只处理了 usr/ 目录结构的情况。

✅ 解决方案

  • 在找不到 usr/ 目录时,检查顶层目录中是否存在标准目录(binlibincludeshare
  • 如果存在,将这些目录的内容合并到 sysroot 对应目录,而不是复制整个包目录
  • 使用 merged 标志跟踪是否成功合并了标准目录,只有在完全没有标准目录时才使用兜底策略

8. 🔄 新增外部包后未自动重新构建

🔍 症状:在 external-hnp 目录下新增了 .hnp 文件(如 icu.hnpless.hnp),执行 create-hnp.sh 后,base.hnp 没有重新构建,新增的包没有被包含进去。

🔎 原因base.hnp 的依赖中没有包含 external-hnp 目录下的文件,make 无法检测到新增文件,认为 base.hnp 已经是最新的。

✅ 解决方案

  • 在 Makefile 中添加 EXTERNAL_HNP=$(wildcard ../external-hnp/*.hnp) 自动发现所有外部 HNP 包
  • $(EXTERNAL_HNP) 添加到 base.hnp 的依赖中:base.hnp: check-pkgs $(STAMP) ... $(EXTERNAL_HNP)
  • 这样当 external-hnp 目录下新增、删除或修改 .hnp 文件时,base.hnp 会自动重新构建

✅ 验证方法

# 检查依赖关系
OHOS_ARCH=aarch64 make -C build-hnp -p | grep "^base.hnp:"
# 应该看到 base.hnp 依赖了 external-hnp 目录下的所有 .hnp 文件

# 新增文件后测试
touch external-hnp/test.hnp
OHOS_ARCH=aarch64 make -C build-hnp -n base.hnp
# 应该会触发重新构建(显示构建命令)

# 删除文件后测试
rm external-hnp/test.hnp
OHOS_ARCH=aarch64 make -C build-hnp -n base.hnp
# 应该会触发重新构建(因为依赖文件被删除)

🔄 增量重建与维护

📦 新增外部 HNP 包后的自动重建

  • 🔄 自动检测base.hnp 的依赖中包含 $(EXTERNAL_HNP),会自动检测 external-hnp 目录下的所有 .hnp 文件
  • ⚡ 自动重建:当 external-hnp 目录下新增、删除或修改 .hnp 文件时,执行 create-hnp.sh 会自动重新构建 base.hnp
  • ✨ 无需手动操作:新增外部包后,直接执行:
OHOS_ARCH=aarch64 OHOS_ABI=arm64-v8a sh ./create-hnp.sh

✅ PKGS 改变后的自动重建

  • 🔄 自动检测base.hnp 的依赖中包含 check-pkgs,每次构建时都会检查 PKGS 变量是否改变
  • ⚡ 自动重建:当 PKGS 变量改变时(例如添加或删除包),check-pkgs 会检测到变化并删除 base.hnp,强制重新构建
  • ✨ 无需手动操作:修改 PKGS 后,直接执行:
OHOS_ARCH=aarch64 OHOS_ABI=arm64-v8a sh ./create-hnp.sh

⚙️ 工作原理

  1. check-pkgs 是一个 .PHONY 目标,每次构建时都会执行
  2. 将当前的 PKGS 值写入 .pkgs-$(OHOS_ARCH).tmp 临时文件
  3. 如果 .pkgs-$(OHOS_ARCH) 文件存在,比较新旧值是否相同
  4. 如果不同,说明 PKGS 改变了,执行清理操作:
    • 删除 base.hnp 强制重新构建
    • 遍历旧的 PKGS 列表,删除不再需要的 .stamp 文件(从 PKGS 中移除的包)
    • 遍历新的 PKGS 列表,删除新添加的包的 .stamp 文件(强制重新构建)
  5. 将临时文件重命名为正式文件,记录当前的 PKGS

✨ 关键改进

  • 🧹 清理旧的 .stamp 文件:当包从 PKGS 中移除时,删除对应的 .stamp 文件,避免 make 误判
  • 🔄 清理新包的 .stamp 文件:当包添加到 PKGS 时,删除对应的 .stamp 文件,强制重新构建并复制到 sysroot

📋 示例场景

  • ➕ 添加包PKGSbash 改为 gettext bash
    • check-pkgs 检测到变化
    • 删除 base.hnpgettext/.stamp(新包)
    • 重新构建时会构建 gettextbash 两个包,并复制到 sysroot
  • ➖ 删除包PKGSgettext bash 改为 bash
    • check-pkgs 检测到变化
    • 删除 base.hnpgettext/.stamp(旧包)
    • 重新构建时只构建 bash 包,sysroot 中不再包含 gettext 的内容
  • 🔄 重新排序PKGSbash gettext 改为 gettext bash
    • 虽然包相同但顺序不同,check-pkgs 也会检测到变化
    • 删除 base.hnp 和所有相关 .stamp 文件
    • 重新构建所有包,确保 sysroot 内容与 PKGS 一致

📦 仅重新执行打包与合入

在所有包已构建的前提下,直接调用:

OHOS_ARCH=aarch64 OHOS_ABI=arm64-v8a sh ./create-hnp.sh

🔧 重建单个内部包

若需要只重建某一包,再执行对应 rebuild-<pkg> 后再次打包;例如:

make -C build-hnp rebuild-yyjson
OHOS_ARCH=aarch64 OHOS_ABI=arm64-v8a sh ./create-hnp.sh

🔄 强制重新构建所有外部包

如果需要强制重新合入所有外部包(例如修改了合入逻辑),可以删除 base.hnp 后重新构建:

rm -f build-hnp/base.hnp
OHOS_ARCH=aarch64 OHOS_ABI=arm64-v8a sh ./create-hnp.sh

🧹 清理构建产物

如果需要完全重新构建,可以清理所有构建产物:

make -C build-hnp clean
OHOS_ARCH=aarch64 OHOS_ABI=arm64-v8a sh ./create-hnp.sh

💡 实现细节与最佳实践

🛠️ 解压工具选择

  • 🍎 macOS 平台:优先使用 ditto,能够正确处理所有 ZIP 格式,包括有 volume label 问题的包
  • 🐧 其他平台:使用 unzip 作为主要解压工具
  • 🛡️ 错误处理:如果主要解压工具失败,自动尝试备用工具

📁 目录结构识别

  1. 🥇 优先检查 sysroot/:如果存在,直接合并,避免嵌套
  2. 🥈 其次检查 usr/:如果存在,映射标准目录到 sysroot
  3. 🥉 最后检查顶层标准目录:如果存在 bin/lib/ 等,合并到对应位置
  4. 🔄 兜底策略:只有在完全没有标准目录时才使用

🔐 文件权限管理

  • ✅ 合入后统一设置:使用 find sysroot/bin -type f -exec chmod +x {} \; 确保所有二进制文件具有执行权限
  • 🛡️ 避免权限问题:确保运行时能够正常执行外部包的命令

🛡️ 错误处理策略

  • 🔄 单个包失败不影响整体:使用 2>/dev/null || true 确保单个包解压或复制失败时,其他包仍能正常处理
  • 📝 详细日志输出:记录每个包的处理过程,便于问题排查

📝 总结

  • 🤖 自动化整合:通过在 base.hnp 规则中引入"外部 HNP 合入"阶段,实现对不同归档结构的自动识别与映射,最终统一收敛到项目内的 sysroot 目录布局
  • ⚡ 增量构建机制
    • 通过 $(EXTERNAL_HNP) 依赖,实现新增外部包时的自动重新构建
    • 通过 check-pkgs 机制,实现 PKGS 变量改变时的自动重新构建
    • 无需手动操作,修改外部包或内部包列表后,直接执行 create-hnp.sh 即可自动重新构建
  • 🍎 兼容性保障:优先使用 ditto 解压,确保在 macOS 平台上能够正确处理所有 ZIP 格式的 HNP 包,包括有 volume label 问题的包
  • 📦 体积控制:合入后再次清理冗余文档,有效控制包体积
  • 🔗 无缝衔接:与现有流水线无缝衔接,便于后续维护与扩展
  • 🛡️ 健壮性:增强的错误处理和权限管理,确保构建过程的稳定性和运行时的一致性
  • 🧠 智能检测:自动检测外部包和内部包列表的变化,确保构建产物始终与配置保持一致

📚 相关资源

  • 📝 Makefile 实现:build-hnp/Makefile
  • 📦 外部 HNP 包目录:external-hnp/
  • 🔧 构建脚本:create-hnp.sh
  • 📋 产物目录:entry/hnp/$(OHOS_ABI)/
Logo

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

更多推荐