fix(devkit): M24 r2 评审 — 交付完整性闸门 + 补 sysctl + time64 文档更正

P2-1(与 P1 同类):licenses/ 与 README 缺失仍会假成功。
- 新增 meta_complete()/verify_meta():MANIFEST + README + 五份许可证副本
  (procps COPYING/COPYING.LIB、ncurses、lsof、musl-COPYRIGHT)缺一即不完整
- 纳入 skip 条件与 --verify-only;产物未变但 meta 缺失时就地 write_meta 补全(不重编),
  构建树不在则退回全量重建
- 回归测试新增 T6(删 licenses 后 --with-sources 必须恢复)、T7(--verify-only 必须拒绝)、
  T8(删 README 后恢复);共 20 项全过

P2-2:README.txt/MANIFEST.txt 的卡上路径全错(写成 ../src、scripts/build/…)。
- 全部改为卡内相对路径:src/、licenses/、MANIFEST.txt、src/DEVKIT.md、src/build_devkit.sh
- README 增加「目录内容」段并注明 src/ 是生成目录、勿手放文件

P2-3:valgrind 的排除理由(“无 MIPS32 后端”)是错的 —— 官方支持 MIPS32/Linux。
- 改为「未尝试」:交叉构建需 autotools + 目标 vDSO 细节、产物 10MB+,56MB 单核不实用

P2-4:busybox 1.31 实测有 pgrep/kill/killall,没有 pkill、没有 sysctl。
- 表格改准确;pgrep 保留(增量有限不交付);pkill 写明「busybox 无此 applet,
  killall 只能部分替代,本轮未做」
- 新增交付 sysctl(procps 4.0.4,148184 B,静态):MANIFEST/verify-only/真机
  (sysctl -a 428 键、kernel.hostname/panic 读取、-n)均已验证

P2-5:free -s <小数> 也受 time64 影响(usleep 亚秒被抹平),文档说反了。
- DEVKIT §9 增加一行(free -s 0.5 实测 0s、free -s 1 实测 1s);README 同步;
  “收益只有 vmstat -t 一列”改为包含 free -s

P3:
1. §9 机制改准:内核只写 8 字节 → tv_sec 高 32 位是内核写入的 nsec、offset 8 的
   tv_nsec 才是未初始化;nanosleep 是把 tv_sec 高半字当 nsec 读(0)
2. 删掉脚本里 top 的旧错误归因(时长溢出/59.4 GiB),与 DEVKIT 对齐
3. 测试耗时改为「热缓存约 15s,冷启动 1–2 分钟」(实测 28s 含多次构建/修复路径)
4. --verify-only 依赖清单写全(file/readelf/sha256sum/awk/sed/grep/wc/cut/basename)
5. licenses/ 增加 musl-COPYRIGHT(zig env → libc/musl/COPYRIGHT,取不到即失败),§6 补一行
6. htop 说明改准:亚秒刷新走 ncurses halfdelay()(select + 32 位 timeval),大概率不受
   time64 影响;受影响的是时钟显示与部分采样(仍注明未做运行时验证)
7. README/脚本注明 src/ 是生成目录
8. LGPL 摘要改为「源码(§6a) 或 共享库(§6b) 或 书面要约(§6c)」
9. ltrace/sysstat 从「不可行」改为「未做(成本高)」,与 perf 的硬阻塞区分

验证:test_build_devkit.sh 20/20;--verify-only 通过(7 个二进制 + MANIFEST + 5 份许可证);
独立工作区离线重建 64s/0 下载,七个 sha256 与前一次逐字节一致;设备实测 sysctl 与其余六项正常。
This commit is contained in:
AskaEth committed 2026-11-06 19:53:45 +08:00
1 parent 0531603779
commit adaed09f94
4 files changed
+195 -84

No files matched your search

+84 -24
View File
@@ -8,7 +8,7 @@
# musl 交叉编出完全静态的二进制,放进 TF 卡供开发调试。
#
# 产物(全部静态链接、strip 过、无 NEEDED):
# fw_apps/devkit/bin/{vmstat,slabtop,free,ps,pmap,lsof}
# fw_apps/devkit/bin/{vmstat,slabtop,free,ps,pmap,sysctl,lsof}
# fw_apps/devkit/MANIFEST.txt 版本/许可证/体积/校验摘要
# fw_apps/devkit/licenses/ 各上游 COPYING(分发留痕)
# fw_apps/devkit/README.txt 上卡后怎么加 PATH、和 busybox 的同名关系
@@ -26,7 +26,7 @@
# 许可证(二进制将来随卡分发时请保留此节):
# 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,
# procps library/ LGPL-2.1+:ps/vmstat/slabtop/free/pmap/sysctl 静态链接 libproc2,
# LGPL-2.1 §6 要求提供可重链接 object code 或书面要约(见 DEVKIT.md §6)
# ncurses 6.5 MIT 风格(X11 许可),保留版权声明即可
# lsof 4.99.4 Purdue Research Foundation 许可(BSD 风格 4 条),
@@ -83,7 +83,12 @@ SRC_LSOF="lsof-${LSOF_VER}.tar.xz|https://deb.debian.org/debian/pool/main/l/lsof
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"
TOOL_NAMES="vmstat slabtop free ps pmap sysctl lsof"
# licenses/ 下必须存在的许可证副本(缺一个都算交付不完整):
# procps 是 GPL-2.0+(COPYING)且 library/ 是 LGPL-2.1+(COPYING.LIB),
# ncurses/lsof 是 MIT 风格 / Purdue,musl 是 MIT 且七个产物都静态链入了它。
LICENSE_FILES="procps-ng-${PROCPS_VER}-COPYING procps-ng-${PROCPS_VER}-COPYING.LIB ncurses-${NCURSES_VER}-COPYING lsof-${LSOF_VER}-COPYING musl-COPYRIGHT"
FORCE=0
CLEAN=0
@@ -137,6 +142,13 @@ EOF
ZIGCC="zigcc"
info "zig $ZIG_VERSION → $WRAP_DIR/zigcc"
# 七个产物都静态链入了 zig 自带 musl(MIT),其 COPYRIGHT 要求随副本保留,
# 所以构建时必须能拿到它,拿不到就明确失败(不能交付“缺许可证”的包)。
ZIG_LIB_DIR="$("$zig_bin" env 2>/dev/null | sed -n 's/.*\.lib_dir *= *"\(.*\)".*/\1/p')"
MUSL_COPYRIGHT="${ZIG_LIB_DIR}/libc/musl/COPYRIGHT"
[ -s "$MUSL_COPYRIGHT" ] || die "找不到 musl 的 COPYRIGHT(zig lib_dir=$ZIG_LIB_DIR),无法留痕许可证"
info "musl COPYRIGHT → $MUSL_COPYRIGHT"
for t in make curl tar sha256sum file readelf; do
command -v "$t" >/dev/null 2>&1 || die "缺宿主工具: $t"
done
@@ -334,16 +346,17 @@ build_lsof() {
# ---------------------------------------------------------------------------
# 产出清单:<源路径>|<目标名>|<出处>|<许可证>
# 只放「真机实测能跑且真有价值」的:vmstat/slabtop(busybox 没有)、
# free/ps/pmap(busybox 版字段残缺)、lsof(busybox 版只有 pid/fd)。
# 不交付 top:本机(32 位 MIPS + 4.4.94)上 procps 4.0.4 的 top 表头/内存单位
# 输出明显错误(时长字段溢出、Mem 显示 59.4 GiB 而 MemTotal 只有 55 MB),
# 且卡上已有 htop 3.5.3(673KB),top 属重复且不可信 → 见 DEVKIT.md「未交付清单」。
# free/ps/pmap(busybox 版字段残缺)、sysctl(busybox 1.31 没有此 applet)、
# lsof(busybox 版只有 pid/fd)。
# 不交付 top:表头时钟被工具链 time64 打坏(不是 procps 的 bug,见 DEVKIT.md §9),
# 且卡上已有 htop 3.5.3(673KB)→ 属重复且不可信。
TOOLS="
$BUILD_DIR/procps/src/vmstat|vmstat|procps-ng $PROCPS_VER|GPL-2.0-or-later
$BUILD_DIR/procps/src/slabtop|slabtop|procps-ng $PROCPS_VER|GPL-2.0-or-later
$BUILD_DIR/procps/src/free|free|procps-ng $PROCPS_VER|GPL-2.0-or-later
$BUILD_DIR/procps/src/ps/pscommand|ps|procps-ng $PROCPS_VER|GPL-2.0-or-later
$BUILD_DIR/procps/src/pmap|pmap|procps-ng $PROCPS_VER|GPL-2.0-or-later
$BUILD_DIR/procps/src/sysctl|sysctl|procps-ng $PROCPS_VER|GPL-2.0-or-later
$BUILD_DIR/lsof/lsof|lsof|lsof $LSOF_VER|Purdue/BSD-style
"
@@ -404,6 +417,9 @@ write_meta() {
|| die "复制许可证失败: ncurses COPYING"
cp "$BUILD_DIR/lsof/COPYING" "$OUT_DIR/licenses/lsof-${LSOF_VER}-COPYING" \
|| die "复制许可证失败: lsof COPYING"
# musl:七个 procps/lsof 产物都静态链入了 zig 自带 musl(MIT),必须随副本留声明
cp "$MUSL_COPYRIGHT" "$OUT_DIR/licenses/musl-COPYRIGHT" \
|| die "复制许可证失败: musl COPYRIGHT($MUSL_COPYRIGHT)"
{
echo "MeowOS devkit — 静态 MIPS32r2 调试工具集"
@@ -423,14 +439,16 @@ EOF
echo " ncurses $NCURSES_VER MIT-style sha256 $(printf '%s' "$SRC_NCURSES" | 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 " musl zig $ZIG_VERSION 自带(MIT) 许可证原文见 licenses/musl-COPYRIGHT"
echo " (--with-sources 时)src/ 内还有 build_devkit.sh + DEVKIT.md(构建控制脚本,GPLv2 §3 要求)"
echo " 下载地址与重编方法: scripts/build/build_devkit.sh(--with-sources 把上述文件放进 src/)"
echo " 重编方法: src/build_devkit.sh(卡上路径;仓库里是 scripts/build/build_devkit.sh)"
echo " 许可证原文: licenses/*-COPYING / licenses/*-COPYRIGHT;说明见 src/DEVKIT.md"
echo
echo "注意: procps-ng 是 GPL-2.0-or-later。把本目录随卡交付给第三方时,"
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。"
echo " 另外 ps/vmstat/slabtop/free/pmap/sysctl 静态链接了 LGPL-2.1+ 的 libproc2:"
echo " LGPL-2.1 §6 要求「源码(§6a) 或 共享库(§6b) 或 书面要约(§6c)」三选一,详见 DEVKIT.md §6。"
} > "$OUT_DIR/MANIFEST.txt"
cat > "$OUT_DIR/README.txt" <<'EOS'
@@ -446,9 +464,13 @@ MeowOS devkit — 设备端调试工具(静态 MIPS32r2)
export PATH="/usr/data/mnt/sd_0/MeowOS/opt/devkit/bin:$PATH"
fi
源码与许可证:
若随卡分发了源码:源码(含构建脚本 build_devkit.sh、DEVKIT.md)在 ../src/;
各上游许可证原文在 ../licenses/;版本/体积/sha256 见 ../MANIFEST.txt。
目录内容(本文件所在目录就是 devkit 根):
bin/ 可执行文件(实体文件,非软链)
MANIFEST.txt 版本 / 许可证 / 体积 / sha256
licenses/ 上游许可证原文(procps GPLv2 + LGPL-2.1、ncurses、lsof、musl)
src/ 仅当随卡附带了源码(--with-sources)时存在:三份源码包 +
build_devkit.sh + DEVKIT.md。**src/ 是生成目录,请勿手放文件。**
说明文档:src/DEVKIT.md(若本源码未随附,见仓库 scripts/build/DEVKIT.md)
exFAT 没有 Unix 权限位(挂载 fmask=0022 → 新文件一律 0755),也没有符号链接。
本目录里都是实体可执行文件,拷到卡上即可执行,无需 chmod,也不能用软链。
@@ -456,8 +478,8 @@ exFAT 没有 Unix 权限位(挂载 fmask=0022 → 新文件一律 0755),
而不是反复 chmod —— fmask=0022 下 chmod 本身是 no-op。
与 busybox 同名命令的关系(devkit/bin 前插 PATH 时生效):
free ps lsof → 会被本目录版本覆盖(procps/lsof 字段更全)
vmstat slabtop pmap → busybox 没有,直接新增
free ps lsof → 会被本目录版本覆盖(procps/lsof 字段更全)
vmstat slabtop pmap sysctl → busybox 1.31 没有这些 applet,直接新增
要临时用回 busybox 版:busybox free / busybox ps / busybox lsof
注意:/bin/ps -> busybox,/usr/bin/{free,lsof,top} -> ../../bin/busybox,
所以“前插 devkit/bin”是唯一让新版本生效的方式(不要覆盖这些软链:
@@ -469,15 +491,16 @@ rootfs 是 ubifs,随固件回刷会被还原,而且 busybox 软链是基线
- slabtop -o(一次性输出)不需要 terminfo,也不要求 TTY;
交互模式走 /usr/share/terminfo(设备已有 xterm 等条目;已编进 ncurses)。
- 本工具链(zig 0.17 musl)在 4.4 内核上是 time64 ABI 错配:
`vmstat -t` 的时间戳列会是**空**(localtime 返回 NULL)。
vmstat 采样间隔、ps 的 lstart、slabtop/free/pmap/lsof 均正常。
原因与后续修法见 ../src/DEVKIT.md「已知限制:工具链 time64」。
* `vmstat -t` 的时间戳列会是**空**(localtime 返回 NULL)
* `free -s 0.5`(小数间隔)会**立即返回**(亚秒等待被抹平);
`free -s 1` 与 `vmstat 1 N` 的整秒间隔正常
* ps 的 lstart、slabtop/pmap/sysctl 不受影响
原因与后续修法见 src/DEVKIT.md「已知限制:工具链 time64」。
- lsof 不带 -n 时会尝试反向解析(编译进 musl 的 resolv.conf 路径是构建机的,
设备上不存在)→ 实测 `lsof -i` 约 3s 超时;请用 `lsof -nP`。
- 不提供 top:见 ../src/DEVKIT.md(同样是 time64 问题,表头时钟溢出)。
- 不提供 top:见 src/DEVKIT.md(同样是 time64 问题:表头时钟不可信)。
EOS
}
# ---------------------------------------------------------------------------
# 主流程
# ---------------------------------------------------------------------------
@@ -518,16 +541,29 @@ main() {
old_stamp=""
fi
local bin_ok=1 mf_ok=0 n
local bin_ok=1 mf_ok=0 meta_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 不符时宁可重建
# 交付完整性也要一并作为 skip 条件:MANIFEST/许可证/README 缺失或与 bin 不符时
# 宁可重建(与 P1 同类——不能因为“跳过编译”就让合规材料静默消失)。
if [ -f "$OUT_DIR/MANIFEST.txt" ] && verify_manifest >/dev/null 2>&1; then
mf_ok=1
fi
meta_complete && meta_ok=1
if [ "$FORCE" = 0 ] && [ "$cur_stamp" = "$old_stamp" ] && [ "$bin_ok" = 1 ] && [ "$mf_ok" = 1 ]; then
# 产物本身没问题、只是 MANIFEST/README/licenses 被删:就地补,不必重编
if [ "$FORCE" = 0 ] && [ "$cur_stamp" = "$old_stamp" ] && [ "$bin_ok" = 1 ] \
&& { [ "$mf_ok" = 0 ] || [ "$meta_ok" = 0 ]; } \
&& [ -d "$BUILD_DIR/procps-src" ] && [ -d "$BUILD_DIR/ncurses-src" ] && [ -d "$BUILD_DIR/lsof" ]; then
log "产物未变,但 MANIFEST/README/licenses 不全 → 就地补全"
write_meta
if verify_manifest >/dev/null 2>&1; then mf_ok=1; else mf_ok=0; fi
if meta_complete; then meta_ok=1; else meta_ok=0; fi
fi
if [ "$FORCE" = 0 ] && [ "$cur_stamp" = "$old_stamp" ] && [ "$bin_ok" = 1 ] \
&& [ "$mf_ok" = 1 ] && [ "$meta_ok" = 1 ]; then
# P1 回归点:--with-sources 必须在这里就兑现,不能因为“跳过编译”而漏掉源码。
# (曾经把这段放在 return 之后,结果 stamp 命中时静默不产出 src/,退出码仍是 0。)
if [ "$WITH_SOURCES" = 1 ]; then
@@ -588,6 +624,7 @@ collect_verify_existing() {
verify_binary "$BIN_DIR/$n" && info "OK $n $(wc -c < "$BIN_DIR/$n") B" || bad=$((bad + 1))
done
verify_manifest || bad=$((bad + 1))
verify_meta || bad=$((bad + 1))
FAIL=$bad
}
@@ -615,7 +652,30 @@ verify_manifest() {
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/体积逐条一致"
[ "$bad" = 0 ] && info "OK MANIFEST.txt 与 bin/ 逐条 sha256/体积一致"
return $((bad > 0))
}
# 交付完整性:MANIFEST + README + 五份许可证副本,缺一个都算不完整
# (和 P1 同类:这些是合规/可用性的一部分,不能因为“跳过编译”就静默缺失)
meta_complete() {
local f
[ -s "$OUT_DIR/MANIFEST.txt" ] || return 1
[ -s "$OUT_DIR/README.txt" ] || return 1
for f in $LICENSE_FILES; do
[ -s "$OUT_DIR/licenses/$f" ] || return 1
done
return 0
}
verify_meta() {
local f bad=0
[ -s "$OUT_DIR/MANIFEST.txt" ] || { info "FAIL 缺 MANIFEST.txt(版本/许可证/sha 无从核对)"; bad=$((bad + 1)); }
[ -s "$OUT_DIR/README.txt" ] || { info "FAIL 缺 README.txt(上卡说明)"; bad=$((bad + 1)); }
for f in $LICENSE_FILES; do
[ -s "$OUT_DIR/licenses/$f" ] || { info "FAIL 缺 licenses/$f(许可证留痕)"; bad=$((bad + 1)); }
done
[ "$bad" = 0 ] && info "OK MANIFEST/README/licenses($(printf '%s' "$LICENSE_FILES" | wc -w) 份)齐全"
return $((bad > 0))
}