Files
MeowOS-For-R3P2/scripts/build/build_devkit.sh
T
AskaEth b52e6a08bc feat(devkit): M24 静态 MIPS32r2 调试工具集 — vmstat/slabtop/free/ps/pmap/lsof
TF 卡 MeowOS/opt/devkit 用的交叉编译流水线:zig 0.17 musl 静态链接,
产物 fw_apps/devkit/{bin,MANIFEST.txt,licenses,README.txt}(gitignore 内)。

- build_devkit.sh:源码固定 版本+sha256+URL(ncurses 6.5 / procps-ng 4.0.4 /
  lsof 4.99.4),缓存命中零联网;冷启动 3 次下载约 80s,重跑 0 次联网约 65s,
  stamp 命中 1s 跳过;--force/--clean/--offline/--with-sources/--verify-only
- 自编静态 ncursesw 只为 slabtop(procps 的 slabtop 硬依赖 ncurses);
  terminfo 目录钉到设备 /usr/share/terminfo,交互式 slabtop 可用
- 逐项自检 file/readelf(static、ELF32、mips32r2、0 NEEDED),任一不过即失败
- 不交付 top(真机输出不可信)、htop(卡上已有 3.5.3 且我们的更大)
- DEVKIT.md:许可证留痕(procps=GPL-2+ 需提供源码、ncurses=MIT、lsof=Purdue)、
  可行性实测、上卡 PATH 与 busybox 同名关系、真机验证输出
2026-11-06 19:05:15 +08:00

488 lines
24 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# ============================================================================
# build_devkit.sh — 交叉编译静态 MIPS32r2 调试工具集(TF 卡 MeowOS/opt/devkit)
#
# 为什么:设备(HiBy R3 Pro II / Ingenic X1600 / Linux 4.4.94 / MIPS32r2)上
# busybox 提供的 free/ps/lsof 字段不全(都是 busybox 软链),且没有 vmstat/slabtop。
# 内存分析时最缺 slabtop(拆 Slab 占用)与 vmstat(系统级趋势)。本脚本用 zig 自带
# musl 交叉编出完全静态的二进制,放进 TF 卡供开发调试。
#
# 产物(全部静态链接、strip 过、无 NEEDED):
# fw_apps/devkit/bin/{vmstat,slabtop,free,ps,pmap,lsof}
# fw_apps/devkit/MANIFEST.txt 版本/许可证/体积/校验摘要
# fw_apps/devkit/licenses/ 各上游 COPYING(分发留痕)
# fw_apps/devkit/README.txt 上卡后怎么加 PATH、和 busybox 的同名关系
# (--with-sources 时另有 fw_apps/devkit/src/ 源码包,GPL 合规用)
#
# 源码固定为「版本 + sha256 + 固定 URL」,全部校验后才编译;缓存命中则零联网。
#
# 工具链:zig cc --target=mipsel-linux-musleabi(需 zig 0.17.x)
# 不依赖 ~/.tools 下的私有脚本,自己生成 wrapper($WORK/bin/zigcc)。
# 不需要内核头(musl 头足够;只有 lsof 缺 11 个 TCP 状态宏,本脚本 -D 补上)。
#
# 许可证(二进制将来随卡分发时请保留此节):
# procps-ng 4.0.4 GPL-2.0-or-later(COPYING=GPLv2;library/ 为 LGPL-2.1+)
# *** 分发二进制必须提供对应源码(GPLv2 §3)***
# → 用 --with-sources 把源码包一起放上卡,或随卡附书面要约。
# ncurses 6.5 MIT 风格(X11 许可),保留版权声明即可
# lsof 4.99.4 Purdue Research Foundation 许可(BSD 风格 4 条),
# 要求文档与源码中保留作者与 Purdue 的署名(COPYING 第 2 条)
# 本脚本链接的 ncurses 只作为 slabtop 的静态依赖进入产物,不单独分发。
#
# 用法:
# 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 --offline # 只用已缓存的源码包(CI/离线)
# bash scripts/build/build_devkit.sh --with-sources
# bash scripts/build/build_devkit.sh --verify-only # 只对现有产物做静态/架构自检
#
# 环境变量:
# MEOW_DEVKIT_WORK 工作区(源码包/编译树/prefix),默认 ~/.cache/meow-devkit
# MEOW_DEVKIT_JOBS 并行度,默认 nproc
# ============================================================================
set -eu
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
OUT_DIR="$ROOT/fw_apps/devkit"
BIN_DIR="$OUT_DIR/bin"
WORK="${MEOW_DEVKIT_WORK:-$HOME/.cache/meow-devkit}"
SRC_DIR="$WORK/src"
BUILD_DIR="$WORK/build"
PREFIX="$WORK/prefix"
WRAP_DIR="$WORK/bin"
JOBS="${MEOW_DEVKIT_JOBS:-$(nproc 2>/dev/null || echo 4)}"
ZIG_TARGET="mipsel-linux-musleabi"
ZIG_WANT="0.17" # 已验证;0.18+ 参数可能变,见 check_toolchain
NCURSES_VER="6.5"
PROCPS_VER="4.0.4"
LSOF_VER="4.99.4"
# 设备内核版本:写进 lsof 的 LINUXV,让它按目标内核(4.4)选代码路径
TARGET_KERNEL_VSTR="4.4.94"
# name|url|sha256 (sha256 是“哪份源码”的唯一权威,URL 只是取法之一)
SRC_NCURSES="ncurses-${NCURSES_VER}.tar.gz|https://mirrors.tuna.tsinghua.edu.cn/gnu/ncurses/ncurses-${NCURSES_VER}.tar.gz|136d91bc269a9a5785e5f9e980bc76ab57428f604ce3e5a5a90cebc767971cc6"
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"
FORCE=0
CLEAN=0
OFFLINE=0
WITH_SOURCES=0
VERIFY_ONLY=0
DOWNLOADS=0
log() { printf '\n== %s\n' "$*"; }
info() { printf ' %s\n' "$*"; }
die() { printf '\n!!! %s\n' "$*" >&2; exit 1; }
usage() {
sed -n '3,40p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
}
while [ $# -gt 0 ]; do
case "$1" in
-f|--force) FORCE=1 ;;
--clean) CLEAN=1 ;;
--offline) OFFLINE=1 ;;
--with-sources) WITH_SOURCES=1 ;;
--verify-only) VERIFY_ONLY=1 ;;
-h|--help) usage; exit 0 ;;
*) die "未知参数: $1(-h 看用法)" ;;
esac
shift
done
# ---------------------------------------------------------------------------
# 0) 工具链与宿主依赖
# ---------------------------------------------------------------------------
check_toolchain() {
local zig_bin zig_ver 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%.*}"
[ "$major_minor" = "$ZIG_WANT" ] || \
die "zig 版本是 $zig_ver,本脚本只在 $ZIG_WANT.x 上验证过(target/静态链接行为可能变)"
mkdir -p "$WRAP_DIR"
cat > "$WRAP_DIR/zigcc" <<EOF
#!/bin/sh
exec "$zig_bin" cc --target=$ZIG_TARGET "\$@"
EOF
chmod +x "$WRAP_DIR/zigcc"
# 统一用 PATH 上的裸名 "zigcc" 当 CC:这样二进制里不会嵌进绝对工具链路径
# (lsof 会把 ${CC} 写进 version.h),换机器/换工作区也能逐字节复现。
PATH="$WRAP_DIR:$PATH"; export PATH
ZIGCC="zigcc"
info "zig $zig_ver → $WRAP_DIR/zigcc"
for t in make curl tar sha256sum file readelf; do
command -v "$t" >/dev/null 2>&1 || die "缺宿主工具: $t"
done
# strip 必须能处理 ELF32/MIPS:优先 llvm-strip(zig 自带同源 llvm),
# GNU strip 来自 binutils 一般也支持,但 Android 自带的那份不保证。
if command -v llvm-strip >/dev/null 2>&1; then
STRIP=llvm-strip
elif command -v "$zig_bin" >/dev/null 2>&1 && [ -x "$(dirname "$zig_bin")/llvm-strip" ]; then
STRIP="$(dirname "$zig_bin")/llvm-strip"
else
STRIP=strip
fi
info "strip → $STRIP"
}
# ---------------------------------------------------------------------------
# 1) 源码获取(固定版本 + sha256;缓存命中不联网)
# ---------------------------------------------------------------------------
fetch_src() {
local spec="$1" name url sha got
name="${spec%%|*}"; spec="${spec#*|}"
url="${spec%%|*}"; sha="${spec#*|}"
if [ -f "$SRC_DIR/$name" ]; then
got="$(sha256sum "$SRC_DIR/$name" | cut -d' ' -f1)"
if [ "$got" = "$sha" ]; then
info "源码已缓存: $name"
return 0
fi
info "缓存校验不符,删除重下: $name"
rm -f "$SRC_DIR/$name"
fi
[ "$OFFLINE" = 1 ] && die "离线模式但缺少源码: $SRC_DIR/$name"
info "下载 $name"
curl -fL --retry 3 --connect-timeout 20 -o "$SRC_DIR/$name.part" "$url" \
|| { rm -f "$SRC_DIR/$name.part"; die "下载失败: $url"; }
got="$(sha256sum "$SRC_DIR/$name.part" | cut -d' ' -f1)"
[ "$got" = "$sha" ] || { rm -f "$SRC_DIR/$name.part"; die "sha256 不符: $name(期望 $sha 实得 $got)"; }
mv "$SRC_DIR/$name.part" "$SRC_DIR/$name"
DOWNLOADS=$((DOWNLOADS + 1))
}
unpack() { # unpack <tarball> <destdir>
rm -rf "$2"; mkdir -p "$2"
tar xf "$SRC_DIR/$1" -C "$2" --strip-components=1
}
# ---------------------------------------------------------------------------
# 2) ncurses:只为 slabtop 提供静态 ncursesw(不单独分发)
# procps 的 slabtop/top 硬依赖 ncurses(configure 里 --without-ncurses 会把
# 它们整个跳过),所以这是编 slabtop 的必要成本,不是为 htop 顺手加的。
# terminfo 目录必须指向设备真实路径(/usr/share/terminfo),否则交互式
# slabtop 会 "Error opening terminal":默认值是编译时 prefix,设备上没有。
# slabtop -o(一次性输出)不初始化 curses,本来不受影响。
# ---------------------------------------------------------------------------
build_ncurses() {
log "编译 ncurses $NCURSES_VER(静态 widec,无 progs/man/tests)"
unpack "ncurses-${NCURSES_VER}.tar.gz" "$BUILD_DIR/ncurses-src"
rm -rf "$BUILD_DIR/ncurses"; mkdir -p "$BUILD_DIR/ncurses"
(
cd "$BUILD_DIR/ncurses"
CC="$ZIGCC" BUILD_CC=cc CFLAGS="-O2 -ffile-prefix-map=$BUILD_DIR/ncurses-src=/ncurses-$NCURSES_VER" \
"$BUILD_DIR/ncurses-src/configure" \
--host="$ZIG_TARGET" --prefix="$PREFIX/ncurses" \
--without-shared --with-normal --without-debug --without-profile \
--without-manpages --without-tests --without-ada \
--without-cxx --without-cxx-binding --without-progs --without-tack \
--enable-widec --disable-db-install \
--enable-pc-files --with-pkg-config-libdir="$PREFIX/ncurses/lib/pkgconfig" \
--with-default-terminfo-dir=/usr/share/terminfo \
--with-terminfo-dirs="/usr/share/terminfo:/etc/terminfo:/lib/terminfo" \
> "$BUILD_DIR/ncurses-configure.log" 2>&1 \
|| { tail -20 "$BUILD_DIR/ncurses-configure.log"; die "ncurses configure 失败"; }
make -j"$JOBS" > "$BUILD_DIR/ncurses-make.log" 2>&1 \
|| { tail -20 "$BUILD_DIR/ncurses-make.log"; die "ncurses make 失败"; }
make install > "$BUILD_DIR/ncurses-install.log" 2>&1 \
|| { tail -20 "$BUILD_DIR/ncurses-install.log"; die "ncurses install 失败"; }
)
info "libncursesw.a 就绪: $PREFIX/ncurses/lib"
}
# ---------------------------------------------------------------------------
# 3) procps-ng:vmstat / slabtop / free / ps / top / pmap
# ---------------------------------------------------------------------------
build_procps() {
log "编译 procps-ng $PROCPS_VER"
unpack "procps-ng-${PROCPS_VER}.tar.xz" "$BUILD_DIR/procps-src"
rm -rf "$BUILD_DIR/procps"; mkdir -p "$BUILD_DIR/procps"
(
cd "$BUILD_DIR/procps"
# PKG_CONFIG_LIBDIR 必须收紧:否则会捡到宿主(Termux/aarch64)的 ncursesw.pc,
# 结果是一边用 musl 头一边混进 bionic 头,watch/top 直接编不过。
# 注意:下面这段环境变量赋值必须写成“无注释的连续续行”,
# 注释行会截断续行链,导致变量只作用于一条空命令。
#
# ac_cv_func_malloc_0_nonnull / realloc:交叉编译时 autoconf 的运行期探测
# 无法执行,不缓存这两个值会生成 rpl_malloc/rpl_realloc 引用而链接失败。
PKG_CONFIG_PATH="$PREFIX/ncurses/lib/pkgconfig" \
PKG_CONFIG_LIBDIR="$PREFIX/ncurses/lib/pkgconfig" \
ac_cv_func_malloc_0_nonnull=yes ac_cv_func_realloc_0_nonnull=yes \
CC="$ZIGCC" CFLAGS="-O2 -ffunction-sections -fdata-sections -ffile-prefix-map=$BUILD_DIR/procps-src=/procps-ng-$PROCPS_VER" \
LDFLAGS="-static -Wl,--gc-sections" \
"$BUILD_DIR/procps-src/configure" \
--host="$ZIG_TARGET" --prefix="$PREFIX/procps" \
--disable-shared --enable-static --disable-nls --disable-kill \
> "$BUILD_DIR/procps-configure.log" 2>&1 \
|| { tail -20 "$BUILD_DIR/procps-configure.log"; die "procps configure 失败"; }
# 防呆:确认 ncurses 来自本 devkit 的 prefix,而不是宿主 Termux 的那份
grep -q "$PREFIX/ncurses" "$BUILD_DIR/procps/Makefile" \
|| die "procps 的 NCURSES_CFLAGS/LIBS 没指向 devkit prefix(pkg-config 串到宿主了)"
make -j"$JOBS" > "$BUILD_DIR/procps-make.log" 2>&1 \
|| { tail -20 "$BUILD_DIR/procps-make.log"; die "procps make 失败"; }
)
}
# ---------------------------------------------------------------------------
# 4) lsof:真版 lsof(busybox lsof 只有 pid/fd/设备号,没有 size/offset/协议)
# lsof 4.99.4 自带老式 Configure(不需要 autoconf),但版本探测读的是宿主
# /usr/include/linux/version.h + uname,在 Termux/aarch64 上拿不到目标内核版本,
# 于是 Makefile 里退化成 -DLINUXV=00000 -DLSOF_VSTR="0.0.0"(Configure 会打
# "unsupported dialect or version")。这两个宏在 linux dialect 里只影响
# `lsof -v` 的显示,不影响代码分支 —— 但仍显式覆盖成目标内核 4.4.94,便于事后对账。
# zig 的 musl 头没有 glibc 侧 linux/tcp.h 间接带来的 TCP 状态宏(TCP_ESTABLISHED
# 等 11 个),这里按 linux/tcp.h 的定义补齐;不补则 dsock.c 编译失败。
# ---------------------------------------------------------------------------
build_lsof() {
log "编译 lsof $LSOF_VER"
rm -rf "$BUILD_DIR/lsof"
mkdir -p "$BUILD_DIR/lsof"
tar xf "$SRC_DIR/lsof-${LSOF_VER}.tar.xz" -C "$BUILD_DIR/lsof" --strip-components=1
(
cd "$BUILD_DIR/lsof"
CC="$ZIGCC" ./Configure -n linux > "$BUILD_DIR/lsof-configure.log" 2>&1 \
|| { tail -20 "$BUILD_DIR/lsof-configure.log"; die "lsof Configure 失败"; }
grep -q 'unsupported dialect' "$BUILD_DIR/lsof-configure.log" \
&& info "注意:Configure 报 unsupported dialect(宿主 uname 6.x),版本宏下面用 -D 覆盖"
# lsof 的 Makefile 里 CFLAGS = CDEFS INCL DEP DEBUG;额外宏走 DEBUG 注入。
# -O2 必须显式给:zig cc 无 -O 时按 Debug 编,会插入 UBSan 桩导致链接失败。
make -j"$JOBS" CC="$ZIGCC" \
DEBUG="-O2 -DLINUXV=44094 -DLSOF_VSTR=\"$TARGET_KERNEL_VSTR\" -DTCP_ESTABLISHED=1 -DTCP_SYN_SENT=2 -DTCP_SYN_RECV=3 -DTCP_FIN_WAIT1=4 -DTCP_FIN_WAIT2=5 -DTCP_TIME_WAIT=6 -DTCP_CLOSE=7 -DTCP_CLOSE_WAIT=8 -DTCP_LAST_ACK=9 -DTCP_LISTEN=10 -DTCP_CLOSING=11" \
> "$BUILD_DIR/lsof-make.log" 2>&1 \
|| { tail -20 "$BUILD_DIR/lsof-make.log"; die "lsof make 失败"; }
)
}
# ---------------------------------------------------------------------------
# 5) 收集 + strip + 逐项验证
# ---------------------------------------------------------------------------
# 产出清单:<源路径>|<目标名>|<出处>|<许可证>
# 只放「真机实测能跑且真有价值」的: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「未交付清单」。
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/lsof/lsof|lsof|lsof $LSOF_VER|Purdue/BSD-style
"
FAIL=0
collect_and_verify() {
log "收集产物并验证"
rm -rf "$BIN_DIR"; mkdir -p "$BIN_DIR"
printf '%-8s %10s %s\n' "工具" "体积(B)" "静态/架构"
printf '%-8s %10s %s\n' "--------" "----------" "--------------------"
while IFS='|' read -r src name origin lic; do
[ -n "$src" ] || continue
[ -f "$src" ] || die "缺产物: $src(编译没成功?)"
cp "$src" "$BIN_DIR/$name"
chmod 755 "$BIN_DIR/$name"
"$STRIP" --strip-all "$BIN_DIR/$name"
if verify_binary "$BIN_DIR/$name"; then
printf '%-8s %10s %s\n' "$name" "$(wc -c < "$BIN_DIR/$name")" "OK: static/ELF32/MIPS32r2/0 NEEDED"
else
FAIL=$((FAIL + 1))
fi
done <<EOF
$(printf '%s\n' "$TOOLS" | sed '/^$/d')
EOF
}
verify_binary() {
local f="$1" desc cls mach flags needed
desc="$(file -b "$f")"
cls="$(readelf -h "$f" | sed -n 's/^ *Class: *//p')"
mach="$(readelf -h "$f" | sed -n 's/^ *Machine: *//p')"
flags="$(readelf -h "$f" | sed -n 's/^ *Flags: *//p')"
needed="$(readelf -d "$f" 2>/dev/null | grep -c NEEDED || true)"
case "$desc" in
*"statically linked"*) ;;
*) info "FAIL $(basename "$f"): 非静态 → $desc"; return 1 ;;
esac
[ "$cls" = "ELF32" ] || { info "FAIL $(basename "$f"): Class=$cls"; return 1; }
case "$mach" in *MIPS*) ;; *) info "FAIL $(basename "$f"): Machine=$mach"; return 1 ;; esac
case "$flags" in *mips32r2*) ;; *) info "FAIL $(basename "$f"): Flags=$flags"; return 1 ;; esac
[ "$needed" = "0" ] || { info "FAIL $(basename "$f"): 有 $needed 条 NEEDED"; return 1; }
case "$desc" in *"not stripped"*) info "警告 $(basename "$f"): 未 strip";; esac
return 0
}
# ---------------------------------------------------------------------------
# 6) MANIFEST / README / 许可证 / (可选)源码
# ---------------------------------------------------------------------------
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
{
echo "MeowOS devkit — 静态 MIPS32r2 调试工具集"
echo "生成时间: $(date -u '+%Y-%m-%dT%H:%M:%SZ') zig: $("${ZIGCC}" --version 2>/dev/null | head -1) 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
[ -n "$src" ] || continue
printf '%-8s %-18s %-20s %10s %s\n' "$name" "$origin" "$lic" \
"$(wc -c < "$BIN_DIR/$name")" "$(sha256sum "$BIN_DIR/$name" | cut -d' ' -f1)"
done <<EOF
$(printf '%s\n' "$TOOLS" | sed '/^$/d')
EOF
echo
echo "源码(可复现性 + GPL 合规):"
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 " lsof $LSOF_VER Purdue/BSD sha256 $(printf '%s' "$SRC_LSOF" | awk -F'|' '{print $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 " 或随卡附书面要约(有效期 ≥3 年)。仅给出下载 URL 不构成合规。"
} > "$OUT_DIR/MANIFEST.txt"
cat > "$OUT_DIR/README.txt" <<'EOS'
MeowOS devkit — 设备端调试工具(静态 MIPS32r2)
================================================
加到 PATH(SSH 会话,临时):
export PATH=/usr/data/mnt/sd_0/MeowOS/opt/devkit/bin:$PATH
持久化(rootfs 的 /etc/profile.d/meow_card_path.sh 已把 MeowOS/opt/bin 前插,
但 devkit 是独立目录,需要再加一行):
if [ -d /usr/data/mnt/sd_0/MeowOS/opt/devkit/bin ]; then
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/*
与 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 里的软链:
rootfs 是 ubifs,随固件回刷会被还原,而且 busybox 软链是基线的一部分)。
已知限制:
- pmap 只能读 /proc/<pid>/maps;本机 CONFIG_PROC_PAGE_MONITOR=n,
/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)。
EOS
}
# ---------------------------------------------------------------------------
# 主流程
# ---------------------------------------------------------------------------
stamp_value() {
{
sha256sum "${BASH_SOURCE[0]}"
echo "$NCURSES_VER $PROCPS_VER $LSOF_VER $ZIG_TARGET $TARGET_KERNEL_VSTR"
} | 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 即可
collect_verify_existing
[ "$FAIL" = 0 ] || die "$FAIL 个产物未通过自检"
return 0
fi
check_toolchain
local cur_stamp old_stamp=""
cur_stamp="$(stamp_value)"
[ -f "$WORK/stamp" ] && old_stamp="$(cat "$WORK/stamp")"
if [ "$CLEAN" = 1 ]; then
log "清理工作区 $WORK"
rm -rf "$BUILD_DIR" "$PREFIX"
mkdir -p "$BUILD_DIR" "$PREFIX"
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
fi
fi
log "取源码(固定版本 + sha256 校验)"
fetch_src "$SRC_NCURSES"
fetch_src "$SRC_PROCPS"
fetch_src "$SRC_LSOF"
build_ncurses
build_procps
build_lsof
collect_and_verify
[ "$FAIL" = 0 ] || die "$FAIL 个产物未通过静态/架构自检(见上)"
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)"
fi
printf '%s\n' "$cur_stamp" > "$WORK/stamp"
t1=$(date +%s)
log "完成"
info "输出: $OUT_DIR"
info "体积合计: $(du -sh "$BIN_DIR" | cut -f1)"
info "用时 $((t1 - t0))s,本次联网下载 $DOWNLOADS 次"
}
collect_verify_existing() {
local n bad=0
for n in vmstat slabtop free ps pmap lsof; 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
FAIL=$bad
}
main "$@"