fix(devkit): M24 r1 评审 — P1 --with-sources 静默漏源码 + time64 归因更正

P1(阻断):stamp 命中时 main 提前 return,--with-sources 在 return 之后,
于是「先构建再 --with-sources」会打印成功、退出码 0、却不产出 src/。

- 把源码整理抽成 stage_sources(),stamp 命中路径先兑现 WITH_SOURCES 再返回;
  整理后按钉住的 sha256 复核(sha 漂移/缺文件都重铺),任何失败即非零退出
- 新增 test_build_devkit.sh(12 项,秒级):stamp 命中下的 --with-sources、
  src 缺文件/被改坏的自愈、--verify-only 的 MANIFEST 篡改检测
- skip 条件额外要求 MANIFEST 与 bin 逐条 sha 一致;--clean 同时清 out/src;
  全新构建未带 --with-sources 时删除上次残留 src/(避免源码与二进制错配)

P2-a:top 排除理由改写。根因是 zig 0.17 musl 的 time64 ABI 错配(mips o32 是
ILP32 + 内核 4.4 无 time64 syscall,16B timespec 直传 32 位 syscall),不是
procps 的 bug;top 的 Mem 一栏是「百分比/总容量」不是 used GiB。新增 DEVKIT.md
§9 记录证据(vmstat -t 空列、usleep(0.5s)×10 实测 0s、sleep(1) 正常、ps lstart
正常),并说明 M16 的 patch_v2/sdk/meow_ui_time.c 可复用(本轮实测该 .o 独立
编译、仅 5 个 libc 未定义符号)但本轮不接入的取舍——需要单独 mission 做链接期
接管 + 全量回归,不是 -D 能修的。

P2-b:--with-sources 现在同时放 build_devkit.sh + DEVKIT.md(GPLv2 §3 的
"scripts used to control compilation");补 LGPL-2.1 §6 静态链接 libproc2 的
义务说明;MANIFEST/README.txt 写明 src/ 与 licenses/。

P2-c:lsof 内嵌 Termux musl 的 resolv.conf 路径记为工具链副作用(实测
lsof -i 约 3s 超时、-nP 瞬时;端口名/本机名不受影响),不换工具链。

P3:软链路径写实(/bin/ps、/usr/bin/{free,lsof,top});MANIFEST 用 zig version
而非 clang 版本;chmod 措辞改成检查挂载参数;--verify-only 增加 MANIFEST 逐条
sha256/体积比对且不再依赖 date;stamp 纳入 zig 完整版本;许可证复制失败即报错。

验证:test_build_devkit.sh 12/12 通过;--verify-only 通过(含 MANIFEST 对账);
独立工作区离线重建 85s/0 下载,六个二进制 sha256 与上一版逐字节一致;设备侧
二进制未变(sha 与 r1 真机验证过的一致)。
This commit is contained in:
AskaEth committed 2026-11-06 19:29:27 +08:00
1 parent b52e6a08bc
commit 0531603779
4 files changed
+431 -143

No files matched your search

+188 -53
View File
@@ -12,18 +12,22 @@
# fw_apps/devkit/MANIFEST.txt 版本/许可证/体积/校验摘要
# fw_apps/devkit/licenses/ 各上游 COPYING(分发留痕)
# fw_apps/devkit/README.txt 上卡后怎么加 PATH、和 busybox 的同名关系
# (--with-sources 时另有 fw_apps/devkit/src/ 源码包,GPL 合规用)
# (--with-sources 时另有 fw_apps/devkit/src/:三份源码包 + build_devkit.sh +
# DEVKIT.md,GPLv2 §3 的“对应源码”含构建控制脚本)
#
# 源码固定为「版本 + sha256 + 固定 URL」,全部校验后才编译;缓存命中则零联网。
#
# 工具链:zig cc --target=mipsel-linux-musleabi(需 zig 0.17.x)
# 不依赖 ~/.tools 下的私有脚本,自己生成 wrapper($WORK/bin/zigcc)。
# 不需要内核头(musl 头足够;只有 lsof 缺 11 个 TCP 状态宏,本脚本 -D 补上)。
# ⚠ 该工具链在 4.4 内核上是 time64 ABI 错配:`vmstat -t` 时间戳列会是空,
# 原因/证据/后续修法见 DEVKIT.md「已知限制:工具链 time64」。不是 -D 能修的。
#
# 许可证(二进制将来随卡分发时请保留此节):
# procps-ng 4.0.4 GPL-2.0-or-later(COPYING=GPLv2;library/ 为 LGPL-2.1+)
# *** 分发二进制必须提供对应源码(GPLv2 §3)***
# → 用 --with-sources 把源码包一起放上卡,或随卡附书面要约。
# procps-ng 4.0.4 GPL-2.0-or-later(COPYING=GPLv2)
# *** 分发二进制必须提供对应源码(GPLv2 §3,含构建脚本)***
# procps library/ LGPL-2.1+:ps/vmstat/slabtop/free/pmap 静态链接 libproc2,
# LGPL-2.1 §6 要求提供可重链接 object code 或书面要约(见 DEVKIT.md §6)
# ncurses 6.5 MIT 风格(X11 许可),保留版权声明即可
# lsof 4.99.4 Purdue Research Foundation 许可(BSD 风格 4 条),
# 要求文档与源码中保留作者与 Purdue 的署名(COPYING 第 2 条)
@@ -32,10 +36,14 @@
# 用法:
# bash scripts/build/build_devkit.sh # 增量:缓存命中则不联网
# bash scripts/build/build_devkit.sh --force # 强制重编
# bash scripts/build/build_devkit.sh --clean # 清空工作区后重编
# bash scripts/build/build_devkit.sh --clean # 清空工作区与 out/src 后重编
# bash scripts/build/build_devkit.sh --offline # 只用已缓存的源码包(CI/离线)
# bash scripts/build/build_devkit.sh --with-sources
# bash scripts/build/build_devkit.sh --verify-only # 只对现有产物做静态/架构自检
# # 整理 src/(三份源码包 + 本脚本 + DEVKIT.md)。**产物已是最新时也会执行**,
# # 且整理后按 sha256 复核;任何一步失败即非零退出(P1 回归点)。
# bash scripts/build/build_devkit.sh --verify-only
# # 只对现有产物自检:静态/架构/NEEDED + MANIFEST 逐条 sha256/体积比对。
# # 只需 file / readelf / sha256sum / awk;不需要 zig、make、date、网络。
#
# 环境变量:
# MEOW_DEVKIT_WORK 工作区(源码包/编译树/prefix),默认 ~/.cache/meow-devkit
@@ -44,6 +52,7 @@
set -eu
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")"
OUT_DIR="$ROOT/fw_apps/devkit"
BIN_DIR="$OUT_DIR/bin"
WORK="${MEOW_DEVKIT_WORK:-$HOME/.cache/meow-devkit}"
@@ -68,6 +77,14 @@ SRC_NCURSES="ncurses-${NCURSES_VER}.tar.gz|https://mirrors.tuna.tsinghua.edu.cn/
SRC_PROCPS="procps-ng-${PROCPS_VER}.tar.xz|https://downloads.sourceforge.net/project/procps-ng/Production/procps-ng-${PROCPS_VER}.tar.xz|22870d6feb2478adb617ce4f09a787addaf2d260c5a8aa7b17d889a962c5e42e"
SRC_LSOF="lsof-${LSOF_VER}.tar.xz|https://deb.debian.org/debian/pool/main/l/lsof/lsof_${LSOF_VER}+dfsg.orig.tar.xz|dc3e7f51cf03bc7fbcdd9e62d4a52825682092031bd1ae08b15fe7f4f582aebf"
# 卡上 src/ 必须包含的文件:三份源码包 + 构建脚本 + 本文档。
# GPLv2 §3 的“对应源码”明确包含 "scripts used to control compilation and
# installation",缺了脚本,收卡人拿不到本轮用的 -D/configure 参数。
SRC_STAGE_LIST="ncurses-${NCURSES_VER}.tar.gz procps-ng-${PROCPS_VER}.tar.xz lsof-${LSOF_VER}.tar.xz build_devkit.sh DEVKIT.md"
# 交付的工具——验证、MANIFEST 比对、stamp 自检都从这里取,避免多处手写漂移
TOOL_NAMES="vmstat slabtop free ps pmap lsof"
FORCE=0
CLEAN=0
OFFLINE=0
@@ -80,7 +97,7 @@ info() { printf ' %s\n' "$*"; }
die() { printf '\n!!! %s\n' "$*" >&2; exit 1; }
usage() {
sed -n '3,40p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
sed -n '3,/^# ============/p' "$SCRIPT_PATH" | sed 's/^# \{0,1\}//'
}
while [ $# -gt 0 ]; do
@@ -100,13 +117,13 @@ done
# 0) 工具链与宿主依赖
# ---------------------------------------------------------------------------
check_toolchain() {
local zig_bin zig_ver major_minor
local zig_bin major_minor
zig_bin="$(command -v zig || true)"
[ -n "$zig_bin" ] || die "找不到 zig。装法:pkg install zig(需 0.17.x)"
zig_ver="$("$zig_bin" version)"
major_minor="${zig_ver%.*}"
ZIG_VERSION="$("$zig_bin" version)"
major_minor="${ZIG_VERSION%.*}"
[ "$major_minor" = "$ZIG_WANT" ] || \
die "zig 版本是 $zig_ver,本脚本只在 $ZIG_WANT.x 上验证过(target/静态链接行为可能变)"
die "zig 版本是 $ZIG_VERSION,本脚本只在 $ZIG_WANT.x 上验证过(target/静态链接行为可能变)"
mkdir -p "$WRAP_DIR"
cat > "$WRAP_DIR/zigcc" <<EOF
@@ -118,7 +135,7 @@ EOF
# (lsof 会把 ${CC} 写进 version.h),换机器/换工作区也能逐字节复现。
PATH="$WRAP_DIR:$PATH"; export PATH
ZIGCC="zigcc"
info "zig $zig_ver → $WRAP_DIR/zigcc"
info "zig $ZIG_VERSION → $WRAP_DIR/zigcc"
for t in make curl tar sha256sum file readelf; do
command -v "$t" >/dev/null 2>&1 || die "缺宿主工具: $t"
@@ -166,6 +183,54 @@ unpack() { # unpack <tarball> <destdir>
tar xf "$SRC_DIR/$1" -C "$2" --strip-components=1
}
# ---------------------------------------------------------------------------
# 1b) 交付源码包(--with-sources):卡上 src/ 的完整性与整理
# P1 回归点:stamp 命中时也必须走这里,绝不能因为“跳过编译”就把源码静默漏掉。
# ---------------------------------------------------------------------------
expected_sha_for() { # expected_sha_for <tarball名> → 钉住的 sha256
case "$1" in
"ncurses-${NCURSES_VER}.tar.gz") printf '%s' "$SRC_NCURSES" | awk -F'|' '{print $3}' ;;
"procps-ng-${PROCPS_VER}.tar.xz") printf '%s' "$SRC_PROCPS" | awk -F'|' '{print $3}' ;;
"lsof-${LSOF_VER}.tar.xz") printf '%s' "$SRC_LSOF" | awk -F'|' '{print $3}' ;;
*) return 1 ;;
esac
}
sources_complete() {
local f got
[ -d "$OUT_DIR/src" ] || return 1
for f in $SRC_STAGE_LIST; do
[ -s "$OUT_DIR/src/$f" ] || return 1
done
# 三份源码包按钉住的 sha 逐一核对(版本漂移比缺文件更危险:看起来有源码,其实对不上)
for f in "ncurses-${NCURSES_VER}.tar.gz" "procps-ng-${PROCPS_VER}.tar.xz" "lsof-${LSOF_VER}.tar.xz"; do
got="$(sha256sum "$OUT_DIR/src/$f" | cut -d' ' -f1)"
[ "$got" = "$(expected_sha_for "$f")" ] || return 1
done
cmp -s "$SCRIPT_PATH" "$OUT_DIR/src/build_devkit.sh" || return 1
cmp -s "$ROOT/scripts/build/DEVKIT.md" "$OUT_DIR/src/DEVKIT.md" || return 1
return 0
}
stage_sources() {
local f
log "整理 $OUT_DIR/src(三份源码包 + 构建脚本 + 文档)"
fetch_src "$SRC_NCURSES"
fetch_src "$SRC_PROCPS"
fetch_src "$SRC_LSOF"
rm -rf "$OUT_DIR/src"
mkdir -p "$OUT_DIR/src"
for f in "ncurses-${NCURSES_VER}.tar.gz" "procps-ng-${PROCPS_VER}.tar.xz" "lsof-${LSOF_VER}.tar.xz"; do
cp "$SRC_DIR/$f" "$OUT_DIR/src/" || die "复制源码包失败: $f"
done
cp "$SCRIPT_PATH" "$OUT_DIR/src/build_devkit.sh" \
|| die "复制构建脚本失败: $SCRIPT_PATH(GPLv2 §3 要求带构建脚本)"
cp "$ROOT/scripts/build/DEVKIT.md" "$OUT_DIR/src/DEVKIT.md" \
|| die "复制 DEVKIT.md 失败: $ROOT/scripts/build/DEVKIT.md"
sources_complete || die "src/ 整理后仍不完整(sha256 或文件缺失)"
info "共 $(du -sh "$OUT_DIR/src" | cut -f1):$(ls "$OUT_DIR/src" | tr '\n' ' ')"
}
# ---------------------------------------------------------------------------
# 2) ncurses:只为 slabtop 提供静态 ncursesw(不单独分发)
# procps 的 slabtop/top 硬依赖 ncurses(configure 里 --without-ncurses 会把
@@ -330,14 +395,20 @@ write_meta() {
log "写 MANIFEST / README / 许可证"
mkdir -p "$OUT_DIR/licenses"
cp "$BUILD_DIR/procps-src/COPYING" "$OUT_DIR/licenses/procps-ng-${PROCPS_VER}-COPYING" 2>/dev/null || true
cp "$BUILD_DIR/procps-src/COPYING.LIB" "$OUT_DIR/licenses/procps-ng-${PROCPS_VER}-COPYING.LIB" 2>/dev/null || true
cp "$BUILD_DIR/ncurses-src/COPYING" "$OUT_DIR/licenses/ncurses-${NCURSES_VER}-COPYING" 2>/dev/null || true
cp "$BUILD_DIR/lsof/COPYING" "$OUT_DIR/licenses/lsof-${LSOF_VER}-COPYING" 2>/dev/null || true
# 许可证文件是分发合规的一部分:复制失败必须报错,不能 `|| true` 静默吞掉
cp "$BUILD_DIR/procps-src/COPYING" "$OUT_DIR/licenses/procps-ng-${PROCPS_VER}-COPYING" \
|| die "复制许可证失败: procps COPYING(分发合规必需)"
cp "$BUILD_DIR/procps-src/COPYING.LIB" "$OUT_DIR/licenses/procps-ng-${PROCPS_VER}-COPYING.LIB" \
|| die "复制许可证失败: procps COPYING.LIB(LGPL-2.1 静态链接义务用)"
cp "$BUILD_DIR/ncurses-src/COPYING" "$OUT_DIR/licenses/ncurses-${NCURSES_VER}-COPYING" \
|| die "复制许可证失败: ncurses COPYING"
cp "$BUILD_DIR/lsof/COPYING" "$OUT_DIR/licenses/lsof-${LSOF_VER}-COPYING" \
|| die "复制许可证失败: lsof COPYING"
{
echo "MeowOS devkit — 静态 MIPS32r2 调试工具集"
echo "生成时间: $(date -u '+%Y-%m-%dT%H:%M:%SZ') zig: $("${ZIGCC}" --version 2>/dev/null | head -1) target: $ZIG_TARGET"
# zig 版本用 `zig version`(不是 zigcc --version 的 clang 版本号)
echo "生成时间: $(date -u '+%Y-%m-%dT%H:%M:%SZ') zig: $ZIG_VERSION target: $ZIG_TARGET"
echo
printf '%-8s %-18s %-20s %10s %s\n' "tool" "source" "license" "size(B)" "sha256"
while IFS='|' read -r src name origin lic; do
@@ -348,15 +419,18 @@ write_meta() {
$(printf '%s\n' "$TOOLS" | sed '/^$/d')
EOF
echo
echo "源码(可复现性 + GPL 合规):"
echo "源码(可复现性 + GPL/LGPL 合规):"
echo " ncurses $NCURSES_VER MIT-style sha256 $(printf '%s' "$SRC_NCURSES" | awk -F'|' '{print $3}')"
echo " procps-ng $PROCPS_VER GPL-2.0+ sha256 $(printf '%s' "$SRC_PROCPS" | awk -F'|' '{print $3}')"
echo " procps-ng $PROCPS_VER GPL-2.0+ / library=LGPL-2.1+ sha256 $(printf '%s' "$SRC_PROCPS" | awk -F'|' '{print $3}')"
echo " lsof $LSOF_VER Purdue/BSD sha256 $(printf '%s' "$SRC_LSOF" | awk -F'|' '{print $3}')"
echo " 下载地址与重编方法: scripts/build/build_devkit.sh(--with-sources 可把源码包放到 src/)"
echo " (--with-sources 时)src/ 内还有 build_devkit.sh + DEVKIT.md(构建控制脚本,GPLv2 §3 要求)"
echo " 下载地址与重编方法: scripts/build/build_devkit.sh(--with-sources 把上述文件放进 src/)"
echo
echo "注意: procps-ng 是 GPL-2.0-or-later。把本目录随卡交付给第三方时,"
echo " GPLv2 §3 要求提供对应源码 —— 请用 --with-sources 一起带上 src/,"
echo " GPLv2 §3 要求提供对应源码(含构建脚本)—— 请用 --with-sources 带上 src/,"
echo " 或随卡附书面要约(有效期 ≥3 年)。仅给出下载 URL 不构成合规。"
echo " 另外 ps/vmstat/slabtop/free/pmap 静态链接了 LGPL-2.1+ 的 libproc2:"
echo " LGPL-2.1 §6 要求提供可重链接的 object code 或书面要约,详见 DEVKIT.md §6。"
} > "$OUT_DIR/MANIFEST.txt"
cat > "$OUT_DIR/README.txt" <<'EOS'
@@ -372,17 +446,21 @@ MeowOS devkit — 设备端调试工具(静态 MIPS32r2)
export PATH="/usr/data/mnt/sd_0/MeowOS/opt/devkit/bin:$PATH"
fi
exFAT 没有 Unix 权限位(挂载 fmask=0022 → 文件一律 0755),也没有符号链接。
本目录里都是实体可执行文件,拷到卡上即可执行,不需要 chmod,也不能用软链。
若手工从其他文件系统拷入后发现不可执行,执行一次:
chmod 755 /usr/data/mnt/sd_0/MeowOS/opt/devkit/bin/*
源码与许可证:
若随卡分发了源码:源码(含构建脚本 build_devkit.sh、DEVKIT.md)在 ../src/;
各上游许可证原文在 ../licenses/;版本/体积/sha256 见 ../MANIFEST.txt。
exFAT 没有 Unix 权限位(挂载 fmask=0022 → 新文件一律 0755),也没有符号链接。
本目录里都是实体可执行文件,拷到卡上即可执行,无需 chmod,也不能用软链。
若拷入后无法执行,请检查挂载参数(不要有 noexec;`mount | grep sd_0`),
而不是反复 chmod —— fmask=0022 下 chmod 本身是 no-op。
与 busybox 同名命令的关系(devkit/bin 前插 PATH 时生效):
free ps lsof → 会被本目录版本覆盖(procps/lsof 字段更全)
vmstat slabtop pmap → busybox 没有,直接新增
要临时用回 busybox 版:/bin/busybox free / busybox ps / busybox lsof
注意:/usr/bin/{free,ps,lsof,top} 目前都是指向 /bin/busybox 的软链,
所以“前插 devkit/bin”是唯一让新版本生效的方式(不要覆盖 /usr/bin 里的软链:
要临时用回 busybox 版:busybox free / busybox ps / busybox lsof
注意:/bin/ps -> busybox,/usr/bin/{free,lsof,top} -> ../../bin/busybox,
所以“前插 devkit/bin”是唯一让新版本生效的方式(不要覆盖这些软链:
rootfs 是 ubifs,随固件回刷会被还原,而且 busybox 软链是基线的一部分)。
已知限制:
@@ -390,7 +468,13 @@ rootfs 是 ubifs,随固件回刷会被还原,而且 busybox 软链是基线
/proc/<pid>/smaps 不存在,所以 pmap -x 拿不到 PSS/私有脏页。
- slabtop -o(一次性输出)不需要 terminfo,也不要求 TTY;
交互模式走 /usr/share/terminfo(设备已有 xterm 等条目;已编进 ncurses)。
- 不提供 top:procps 4.0.4 的 top 在本机输出不可信(见仓库 scripts/build/DEVKIT.md)。
- 本工具链(zig 0.17 musl)在 4.4 内核上是 time64 ABI 错配:
`vmstat -t` 的时间戳列会是**空**(localtime 返回 NULL)。
vmstat 采样间隔、ps 的 lstart、slabtop/free/pmap/lsof 均正常。
原因与后续修法见 ../src/DEVKIT.md「已知限制:工具链 time64」。
- lsof 不带 -n 时会尝试反向解析(编译进 musl 的 resolv.conf 路径是构建机的,
设备上不存在)→ 实测 `lsof -i` 约 3s 超时;请用 `lsof -nP`。
- 不提供 top:见 ../src/DEVKIT.md(同样是 time64 问题,表头时钟溢出)。
EOS
}
@@ -399,23 +483,25 @@ EOS
# ---------------------------------------------------------------------------
stamp_value() {
{
sha256sum "${BASH_SOURCE[0]}"
echo "$NCURSES_VER $PROCPS_VER $LSOF_VER $ZIG_TARGET $TARGET_KERNEL_VSTR"
sha256sum "$SCRIPT_PATH"
# 带 zig 的完整版本(0.17.0 → 0.17.1 也要触发重建:工具链 patch 会改产物)
echo "zig=$ZIG_VERSION target=$ZIG_TARGET kernel=$TARGET_KERNEL_VSTR"
echo "ncurses=$NCURSES_VER procps=$PROCPS_VER lsof=$LSOF_VER"
} | sha256sum | cut -d' ' -f1
}
main() {
local t0 t1
t0=$(date +%s)
mkdir -p "$SRC_DIR" "$BUILD_DIR" "$PREFIX"
if [ "$VERIFY_ONLY" = 1 ]; then
# 只校验现有产物:不要求 zig/make,评审者用系统自带 file/readelf 即可
# 只校验现有产物:不要求 zig/make/date,评审者用 file/readelf/sha256sum 即可
collect_verify_existing
[ "$FAIL" = 0 ] || die "$FAIL 个产物未通过自检"
[ "$FAIL" = 0 ] || die "$FAIL 项未通过自检"
return 0
fi
t0=$(date +%s)
mkdir -p "$SRC_DIR" "$BUILD_DIR" "$PREFIX"
check_toolchain
local cur_stamp old_stamp=""
@@ -423,24 +509,41 @@ main() {
[ -f "$WORK/stamp" ] && old_stamp="$(cat "$WORK/stamp")"
if [ "$CLEAN" = 1 ]; then
log "清理工作区 $WORK"
log "清理工作区 $WORK 与输出目录的 src/"
rm -rf "$BUILD_DIR" "$PREFIX"
mkdir -p "$BUILD_DIR" "$PREFIX"
if [ -d "$OUT_DIR/src" ]; then
rm -rf "$OUT_DIR/src"
fi
old_stamp=""
fi
if [ "$FORCE" = 0 ] && [ "$cur_stamp" = "$old_stamp" ] && [ -d "$BIN_DIR" ]; then
local ok=1
for n in vmstat slabtop free ps pmap lsof; do
[ -f "$BIN_DIR/$n" ] && verify_binary "$BIN_DIR/$n" >/dev/null 2>&1 || ok=0
done
if [ "$ok" = 1 ]; then
t1=$(date +%s)
log "产物已是最新(脚本与版本未变),跳过编译"
info "输出目录: $BIN_DIR"
info "用时 $((t1 - t0))s,联网 $DOWNLOADS 次"
return 0
local bin_ok=1 mf_ok=0 n
for n in $TOOL_NAMES; do
[ -f "$BIN_DIR/$n" ] && verify_binary "$BIN_DIR/$n" >/dev/null 2>&1 || bin_ok=0
done
# 交付完整性也要一并作为 skip 条件:MANIFEST 缺失或与 bin 不符时宁可重建
if [ -f "$OUT_DIR/MANIFEST.txt" ] && verify_manifest >/dev/null 2>&1; then
mf_ok=1
fi
if [ "$FORCE" = 0 ] && [ "$cur_stamp" = "$old_stamp" ] && [ "$bin_ok" = 1 ] && [ "$mf_ok" = 1 ]; then
# P1 回归点:--with-sources 必须在这里就兑现,不能因为“跳过编译”而漏掉源码。
# (曾经把这段放在 return 之后,结果 stamp 命中时静默不产出 src/,退出码仍是 0。)
if [ "$WITH_SOURCES" = 1 ]; then
if sources_complete; then
info "源码包已就绪: $OUT_DIR/src"
else
stage_sources
fi
elif [ -d "$OUT_DIR/src" ] && ! sources_complete; then
info "注意: $OUT_DIR/src 与当前版本/脚本不一致;若要分发请用 --with-sources 重建"
fi
t1=$(date +%s)
log "产物已是最新(脚本与版本未变),跳过编译"
info "输出目录: $BIN_DIR"
info "用时 $((t1 - t0))s,联网 $DOWNLOADS 次"
return 0
fi
log "取源码(固定版本 + sha256 校验)"
@@ -458,11 +561,11 @@ main() {
write_meta
if [ "$WITH_SOURCES" = 1 ]; then
log "复制源码包到 $OUT_DIR/src(GPL 合规用)"
mkdir -p "$OUT_DIR/src"
cp "$SRC_DIR/ncurses-${NCURSES_VER}.tar.gz" "$SRC_DIR/procps-ng-${PROCPS_VER}.tar.xz" \
"$SRC_DIR/lsof-${LSOF_VER}.tar.xz" "$OUT_DIR/src/"
info "共 $(du -sh "$OUT_DIR/src" | cut -f1)"
stage_sources
elif [ -d "$OUT_DIR/src" ]; then
# 上次 --with-sources 的残留可能与本次二进制不对应;留着会被误当“合规源码”
info "删除上次残留的 $OUT_DIR/src(本次未请求源码,避免与二进制错配)"
rm -rf "$OUT_DIR/src"
fi
printf '%s\n' "$cur_stamp" > "$WORK/stamp"
@@ -471,17 +574,49 @@ main() {
info "输出: $OUT_DIR"
info "体积合计: $(du -sh "$BIN_DIR" | cut -f1)"
info "用时 $((t1 - t0))s,本次联网下载 $DOWNLOADS 次"
if [ "$WITH_SOURCES" = 1 ]; then
info "源码包: $OUT_DIR/src($(du -sh "$OUT_DIR/src" | cut -f1))"
fi
}
collect_verify_existing() {
local n bad=0
for n in vmstat slabtop free ps pmap lsof; do
for n in $TOOL_NAMES; do
if [ ! -f "$BIN_DIR/$n" ]; then
info "缺 $BIN_DIR/$n"; bad=$((bad + 1)); continue
fi
verify_binary "$BIN_DIR/$n" && info "OK $n $(wc -c < "$BIN_DIR/$n") B" || bad=$((bad + 1))
done
verify_manifest || bad=$((bad + 1))
FAIL=$bad
}
# MANIFEST.txt 的逐条 sha256/体积比对:只查“是不是静态 MIPS”看不出内容漂移
# (旧版脚本/旧工具链编出来的同样是 static/MIPS)。这里把 MANIFEST 当权威对账。
verify_manifest() {
local n line got_sha got_size bad=0
local mf="$OUT_DIR/MANIFEST.txt"
if [ ! -f "$mf" ]; then
info "FAIL 缺 $mf(无法核对产物内容)"
return 1
fi
for n in $TOOL_NAMES; do
# 只认表格行:$1=工具名,$(NF-1)=纯数字体积,$NF=64 位 sha。
# (MANIFEST 末尾「源码」小节里也有以 lsof 开头的行,不加这两个约束会把两行都收进来。)
line="$(awk -v k="$n" '$1 == k && $(NF-1) ~ /^[0-9]+$/ && length($NF) == 64' "$mf")"
if [ -z "$line" ]; then
info "FAIL MANIFEST 缺 $n 的表格行"; bad=$((bad + 1)); continue
fi
got_sha="$(printf '%s\n' "$line" | awk '{print $NF}')"
got_size="$(printf '%s\n' "$line" | awk '{print $(NF-1)}')"
if [ "$got_sha" != "$(sha256sum "$BIN_DIR/$n" | cut -d' ' -f1)" ]; then
info "FAIL $n sha256 与 MANIFEST 不一致(产物被替换/旧版残留?)"; bad=$((bad + 1))
elif [ "$got_size" != "$(wc -c < "$BIN_DIR/$n")" ]; then
info "FAIL $n 体积与 MANIFEST 不一致($got_size vs $(wc -c < "$BIN_DIR/$n"))"; bad=$((bad + 1))
fi
done
[ "$bad" = 0 ] && info "OK MANIFEST.txt 与 bin/ 六项 sha256/体积逐条一致"
return $((bad > 0))
}
main "$@"