From 7d4906828d0892ac13ec3efabab3e94c53357bfd Mon Sep 17 00:00:00 2001 From: AskaEth Date: Wed, 4 Nov 2026 21:47:25 +0800 Subject: [PATCH] =?UTF-8?q?build(ncm):=20=E5=AE=BF=E4=B8=BB=E4=BA=A4?= =?UTF-8?q?=E5=8F=89=E9=80=82=E9=85=8D=E5=B1=82=20+=20=E5=85=A8=E9=87=8F?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E8=A1=A5=E8=B7=91=EF=BC=88M8=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 build_ncm_cross_host.sh:同构 HOME 复用容器脚本,串起 sdk/vendor/host-mbedtls/core/tests/ncm - libs_common.sh 工具链路径改为 MEOW_R3PROII_ROOT/$HOME 可覆盖,容器默认不变 - build_mbedtls.sh 递归清理 library/ 与 3rdparty/ 旧对象,修宿主/mipsel 混架构归档 - test_ncm_core.c 改用 $TMPDIR,Termux /tmp 不可写不再导致 store/cache/tag 用例失败 - REPORT §40:构建/测试/热更结果(ncm md5 1bbd2a55…,Music du 前后恒定) --- patch_v2/ncm_app/REPORT.md | 51 ++++++++ patch_v2/ncm_app/build_mbedtls.sh | 4 +- patch_v2/ncm_app/build_ncm_cross_host.sh | 149 +++++++++++++++++++++++ patch_v2/ncm_app/libs_common.sh | 6 +- patch_v2/ncm_app/test/test_ncm_core.c | 26 ++-- 5 files changed, 226 insertions(+), 10 deletions(-) create mode 100755 patch_v2/ncm_app/build_ncm_cross_host.sh diff --git a/patch_v2/ncm_app/REPORT.md b/patch_v2/ncm_app/REPORT.md index 21db7ca..393121e 100644 --- a/patch_v2/ncm_app/REPORT.md +++ b/patch_v2/ncm_app/REPORT.md @@ -3429,3 +3429,54 @@ MP3 ≤128k→标准,>128k→极高 HQ。列表显示**实际档**(如 hires 切到「极高」档再播同名曲,会按实际档新落一份 `song__exhigh.mp3`(一次性重下,旧文件不删, 列表按 (id,实际档,ext) 去重只显示一行,旧文件由 LRU/清空自然回收)。未对旧名做向上探测扫描 (避免每次 miss 扫目录 + 读多份文件头);如需「旧名跨档零重下」,可作为后续优化加一次目录级兼容探测。 + +## 40. M8:交叉编译 + 真机热更(2026-11-04) + +用户原话:「跑」(把 M5/M6 已合并代码交叉编译并热更上卡真机验证)。 + +### 40.1 宿主适配层(新增入库) + +| 文件 | 改动 | +|---|---| +| `build_ncm_cross_host.sh`(新) | 不改原脚本的容器路径约定:临时布局同构 `$HOME/Project/R3ProII`(`.tools/zig-mipsel-cc`、`mipsel-linux-muslsf-cross/mipsel-linux-muslsf` → 本机 `~/.tools/mipsel-linux-musleabi-sysroot`、`zig-env/bin/zig` → 系统 `ar` 包装)+ launcher 镜像(仓库头文件 + 取回的 `meow_font.a`),导出 `CC_MIPS`/`AR_MIPS`/`TMPDIR` 后按 `sdk → vendor → host-mbedtls → core → tests → ncm` 原样执行;每步日志落 `~/.cache/meow-ncm-build/logs/`。缺省 `all`,也可单步跑。 | +| `libs_common.sh` | `CC_MIPS`/`AR_MIPS` 由硬编码 `/home/Aska/...` 改为 `MEOW_R3PROII_ROOT`(缺省 `$HOME/Project/R3ProII`)+ 环境变量可覆盖;容器行为不变。 | +| `build_mbedtls.sh` | 清旧对象从 `library/*.o` 改为递归清 `library/` + `3rdparty/`(everest/p256-m 的对象在 3rdparty),否则混架构对象会被归档进库。 | +| `test/test_ncm_core.c` | 三处硬编码 `/tmp`(store/cache/tag)改为 `$TMPDIR` 优先,默认 data_dir 同改;Termux `/tmp` 不可写,否则 deviceId 持久化/机器密钥直接 FAIL。 | + +> 任务建议的 `build_ncm_host.sh` 名称已被「宿主 UI 截图/内存 harness」占用,适配层改用 `build_ncm_cross_host.sh`。 + +### 40.2 构建结果 + +| 项 | 结果 | +|---|---| +| `build_sdk_host.sh --verify` | 75 成员(31 OBJS + meow_font.o + 43 freetype),全 ELF32 MIPS32 rel2(o32);`meow_ui.a` 5,701,866 B;本机约 16s | +| mbedtls(裁剪 config,102 enabled) | mipsel:crypto text 267,933 / x509 39,074 / tls 112,485(合计 419,492 ≈ 410 KB) | +| `build_ncm.sh` | `ncm` **1,193,464 B**;text 1,084,494 / data 1,948 / bss 1,493,360;ELF32 MIPS32 rel2、静态链接、已 strip;**0 warning**(自有代码 -Wall -Wextra) | +| md5 | `1bbd2a5527dc992de60cdf4b0ba8656d` | +| 耗时 | 首次整链约 13s;清空 `out/app-mipsel` 后重编 39 个对象约 5s(zig cc 进程级缓存命中) | + +### 40.3 宿主全量测试(补齐 M5/M6 缺口) + +| 测试 | 结果 | +|---|---| +| `test/test_ncm_core_host`(全量,含 M5 `test_cache_tag` / M6 `test_cache_actual_naming`) | **139 PASS / 0 FAIL / 2 SKIP**(SKIP 均为 live 网络项:Termux 环境 unikey rc=-5;计数不含 `== RESULT: PASS ==` 横幅) | +| `test/test_ncm_tag_host`(FLAC/MP3 tag 嵌入/读回/分块/降级) | **52 PASS / 0 FAIL** | +| `test/test_ncm_cache_host`(实际档命名/命中下探/旧名兼容/LRU) | **63 PASS / 0 FAIL** | +| 告警 | core/tag/cache/ncm 各构建日志 `warning:` 计数均为 0 | +| mipsel 测试二进制 | `out/test_ncm_core_mipsel` text 545,098 链接成功;本机无 `qemu-mipsel`,未执行 | + +### 40.4 真机热更(白名单仅 MeowAPPs/dev.player.ncm/) + +- 设备 `root@10.76.208.50`,`app.json.exec=/usr/data/mnt/sd_0/MeowAPPs/dev.player.ncm/ncm`。 +- 旧件 md5 `276b6caaf3212389f7f9dab1c289ac27`(1,160,092 B)→ 备份 `ncm.bak.276b6caaf3212389f7f9dab1c289ac27`(同目录;原有 `ncm.bak` 保留)。 +- 新件经 `ssh 'cat > ncm.new'` 流式写入 → 卡上 md5 核对一致 → `chmod 755` + `mv` → `sync`。 +- 卡上 md5 `1bbd2a5527dc992de60cdf4b0ba8656d` == 本地;大小 1,193,464 B。 +- **红线校验**:`du -s /usr/data/mnt/sd_0/Music` 写前/写后均为 **199,164,288 B**,恒定;未触碰 Music/MeowData/固件,未重启设备。 +- 生效方式:launcher 直接执行 `MeowAPPs//` 二进制,替换文件即生效,app 列表无需 rescan。热更时 ncm 未在运行,用户重新打开 App 即加载新件(物理验收由用户做)。 + +### 40.5 遗留 + +- **mipsel 侧未运行**:本机无 `qemu-mipsel`,`out/test_ncm_core_mipsel` 只完成编译+链接;真机功能验证依赖用户打开 App(顶栏入口/列表)。 +- **live 网络测试跳过**:Termux 环境 HTTPS 到 music.163.com 返回 rc=-5(网络/证书被拒),2 项 SKIP;加密/缓存/API 解析等离线项全部 PASS。 +- **产物不入库**:`ncm` 二进制、`meow_ui.a`、vendor、`out/` 均只在本机产出;仓库中原本被跟踪的旧 `ncm`/`test_ncm_core_host` 已恢复为 HEAD 版本,避免把本机构建产物带进提交。 +- 卡上保留两份备份(`ncm.bak` 与 `ncm.bak.<旧md5>`),需要回滚可 `cp` 任一覆盖。 diff --git a/patch_v2/ncm_app/build_mbedtls.sh b/patch_v2/ncm_app/build_mbedtls.sh index b51f5c4..dc0f2be 100755 --- a/patch_v2/ncm_app/build_mbedtls.sh +++ b/patch_v2/ncm_app/build_mbedtls.sh @@ -51,7 +51,9 @@ touch "$MB/library/error.c" "$MB/library/version_features.c" \ "$MB/library/psa_crypto_driver_wrappers.h" \ "$MB/library/psa_crypto_driver_wrappers_no_static.c" # 关键:config 变更不会自动触发 .c 重编,务必清掉旧对象/库再重编,否则 PEM 等新启用模块不会进库。 -rm -f "$MB/library"/*.o "$MB/library"/*.a +# everest/ p256-m 的对象在 3rdparty/ 下,也要清,否则上一轮(如宿主 gcc)对象会被归档进 mipsel 库。 +find "$MB/library" "$MB/3rdparty" -name '*.o' -delete +find "$MB/library" "$MB/3rdparty" -name '*.a' -delete BM="$OUT/mbedtls/mipsel"; rm -rf "$BM"; mkdir -p "$BM" # 在 vendor 源码内建,输出到本 app 的 out 目录 diff --git a/patch_v2/ncm_app/build_ncm_cross_host.sh b/patch_v2/ncm_app/build_ncm_cross_host.sh new file mode 100755 index 0000000..8ca7d51 --- /dev/null +++ b/patch_v2/ncm_app/build_ncm_cross_host.sh @@ -0,0 +1,149 @@ +#!/bin/bash +# build_ncm_cross_host.sh — 在 Termux/本机(非容器)跑通 ncm 的容器交叉链路。 +# +# 原构建脚本假定容器布局:build_ncm.sh 用 $HOME/Project/R3ProII/.tools 与 +# .tools/mipsel-linux-muslsf-cross/mipsel-linux-muslsf(libz.a);libs_common.sh 指向 +# /home/Aska/...;build_sdk.sh 从 meow_launcherd 取 meow_font.a。本脚本不改这些脚本, +# 而是临时布局一个同构 $HOME(符号链接到本机真实工具链/仓库),再原样执行: +# 1) .tools/zig-mipsel-cc -> ~/.tools/zig-mipsel-cc +# 2) .tools/mipsel-linux-muslsf-cross/mipsel-linux-muslsf +# -> ~/.tools/mipsel-linux-musleabi-sysroot(含 lib/libz.a) +# 3) .tools/zig-env/bin/zig -> 包装器:`zig ar` 转系统 GNU ar(架构无关) +# 4) launcher 镜像目录:仓库 meow_launcherd 头文件 + 取回的 meow_font.a(*.a 不入库) +# +# 依赖(均不入库): +# ~/.tools/zig-mipsel-cc +# ~/.tools/mipsel-linux-musleabi-sysroot +# patch_v2/sdk/vendor/freetype-src FreeType 2.14.3 源 +# patch_v2/ncm_app/vendor/{mbedtls,jsmn,minimp3,qrcodegen,dr_flac} +# meow_font.a:默认找 patch_v2/meow_launcherd/meow_font.a,否则 /launcher/meow_font.a +# +# 用法: +# bash patch_v2/ncm_app/build_ncm_cross_host.sh [all|prep|sdk|vendor|host-mbedtls|core|tests|ncm] +# 缺省 all;各步日志写 /logs/.log(WORK 默认 ~/.cache/meow-ncm-build)。 +set -e + +SELF_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SELF_DIR/../.." && pwd)" +WORK="${NCM_CROSS_WORK:-$HOME/.cache/meow-ncm-build}" +LOGS="$WORK/logs" +HOMELAYOUT="$WORK/home" +PROJ="$HOMELAYOUT/Project/R3ProII" +TOOLS="${MEOW_TOOLS_DIR:-$HOME/.tools}" +SYSROOT="${MEOW_SYSROOT:-$TOOLS/mipsel-linux-musleabi-sysroot}" +FONT_A="${MEOW_FONT_A:-$REPO_ROOT/patch_v2/meow_launcherd/meow_font.a}" + +usage() { sed -n '2,26p' "$0" | sed 's/^# \{0,1\}//'; } + +prep() { + mkdir -p "$PROJ/.tools/mipsel-linux-muslsf-cross" "$PROJ/.tools/zig-env/bin" "$LOGS" "$WORK/tmp" + ln -sfn "$TOOLS/zig-mipsel-cc" "$PROJ/.tools/zig-mipsel-cc" + ln -sfn "$SYSROOT" "$PROJ/.tools/mipsel-linux-muslsf-cross/mipsel-linux-muslsf" + cat > "$PROJ/.tools/zig-env/bin/zig" <<'EOF' +#!/bin/sh +[ "$1" = ar ] && shift +exec ar "$@" +EOF + chmod +x "$PROJ/.tools/zig-env/bin/zig" + + mkdir -p "$WORK/launcher-mirror" + for f in "$REPO_ROOT"/patch_v2/meow_launcherd/*; do + b="$(basename "$f")" + [ "$b" = meow_font.a ] && continue + ln -sfn "$f" "$WORK/launcher-mirror/$b" + done + if [ -f "$FONT_A" ]; then + ln -sfn "$FONT_A" "$WORK/launcher-mirror/meow_font.a" + elif [ -f "$WORK/launcher/meow_font.a" ]; then + ln -sfn "$WORK/launcher/meow_font.a" "$WORK/launcher-mirror/meow_font.a" + else + echo "缺少 meow_font.a(放 patch_v2/meow_launcherd/ 或 $WORK/launcher/,或设 MEOW_FONT_A)" >&2 + exit 1 + fi + + [ -x "$PROJ/.tools/zig-mipsel-cc" ] || { echo "缺少交叉编译器: $TOOLS/zig-mipsel-cc" >&2; exit 1; } + [ -f "$PROJ/.tools/mipsel-linux-muslsf-cross/mipsel-linux-muslsf/lib/libz.a" ] || { + echo "缺少 sysroot libz.a: $SYSROOT/lib/libz.a" >&2; exit 1; } + echo "== prep OK:$HOMELAYOUT ==" +} + +# 各步共用:同构 HOME + 显式 CC/AR(libs_common.sh 支持覆盖)+ Termux 可写 TMPDIR +export HOME="$HOMELAYOUT" +export CC_MIPS="$PROJ/.tools/zig-mipsel-cc" +export AR_MIPS="$(command -v ar)" +export TMPDIR="$WORK/tmp" + +run() { + local name="$1"; shift + echo "== [$name] $*" + set +e + ( "$@" ) 2>&1 | tee "$LOGS/$name.log" + local rc=${PIPESTATUS[0]} + set -e + [ "$rc" -eq 0 ] || { echo "[FAIL] $name rc=$rc(日志 $LOGS/$name.log)" >&2; exit "$rc"; } +} + +step_sdk() { + run sdk env LAUNCHER_DIR="$WORK/launcher-mirror" bash "$REPO_ROOT/patch_v2/sdk/build_sdk_host.sh" --verify +} + +step_vendor() { + cd "$SELF_DIR" + run vendor bash ./build_mbedtls.sh +} + +# 宿主 mbedtls:复用 vendor 步骤已裁剪并应用的 config,gcc 重编同一套库给宿主测试。 +step_host_mbedtls() { + local MB="$SELF_DIR/vendor/mbedtls" BM="$SELF_DIR/out/mbedtls/host" + [ -f "$MB/include/mbedtls/mbedtls_config.h" ] || { echo "先跑 vendor 步骤(config 未就绪)" >&2; exit 1; } + # everest/ p256-m 的对象在 3rdparty/ 下(library/ 里只有 *.c 引子);只删 library/*.o 会留下 + # 上一轮的 mipsel 对象,归档后与宿主对象混架构(ld: incompatible with aarch64linux)。 + find "$MB/library" "$MB/3rdparty" -name '*.o' -delete + find "$MB/library" "$MB/3rdparty" -name '*.a' -delete + rm -rf "$BM"; mkdir -p "$BM" + run host-mbedtls make -C "$MB" lib CC=gcc AR=ar \ + CFLAGS="-O2 -Wno-unused-parameter -fno-stack-protector" + for a in libmbedcrypto.a libmbedx509.a libmbedtls.a; do + [ -s "$MB/library/$a" ] && cp "$MB/library/$a" "$BM/$a" + done + ls -la "$BM" +} + +step_core() { + cd "$SELF_DIR" + run core bash ./build_core.sh + local DATA="$WORK/ncm_test" + rm -rf "$DATA"; mkdir -p "$DATA" + run core-test ./test/test_ncm_core_host "$DATA" +} + +step_tests() { + cd "$SELF_DIR" + run tag-build bash ./build_ncm_tag_host.sh + run tag-test ./test/test_ncm_tag_host + run cache-build bash ./build_ncm_cache_host.sh + run cache-test ./test/test_ncm_cache_host +} + +step_ncm() { + cd "$SELF_DIR" + run ncm bash ./build_ncm.sh + # 只匹配编译器告警本身;build_ncm.sh 结尾的 "0 warning enforced" 总结不是告警。 + if grep -i "warning:" "$LOGS/ncm.log"; then + echo "[FAIL] ncm 构建出现告警(要求 0 warning)" >&2 + exit 1 + fi +} + +case "${1:-all}" in + prep) prep ;; + sdk) prep; step_sdk ;; + vendor) prep; step_vendor ;; + host-mbedtls) prep; step_host_mbedtls ;; + core) prep; step_core ;; + tests) prep; step_tests ;; + ncm) prep; step_ncm ;; + all) prep; step_sdk; step_vendor; step_host_mbedtls; step_core; step_tests; step_ncm ;; + -h|--help) usage ;; + *) echo "未知步骤: $1(all|prep|sdk|vendor|host-mbedtls|core|tests|ncm)" >&2; exit 2 ;; +esac diff --git a/patch_v2/ncm_app/libs_common.sh b/patch_v2/ncm_app/libs_common.sh index f45384f..e71930f 100755 --- a/patch_v2/ncm_app/libs_common.sh +++ b/patch_v2/ncm_app/libs_common.sh @@ -3,8 +3,10 @@ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" VENDOR="$ROOT/vendor" OUT="$ROOT/out" -CC_MIPS="/home/Aska/Project/R3ProII/.tools/zig-mipsel-cc" -AR_MIPS="/home/Aska/Project/R3ProII/.tools/zig-env/bin/zig ar" +# 容器默认 $HOME/Project/R3ProII(= /home/Aska/Project/R3ProII);宿主适配层可整组覆盖 +R3ROOT="${MEOW_R3PROII_ROOT:-$HOME/Project/R3ProII}" +CC_MIPS="${CC_MIPS:-$R3ROOT/.tools/zig-mipsel-cc}" +AR_MIPS="${AR_MIPS:-$R3ROOT/.tools/zig-env/bin/zig ar}" CC_HOST="gcc" SZ_HOST="size" # 交叉 bin 是 x86_64 的,本机(aarch64)跑不了;统一用 host `size`(可读任意 ELF)+ zig ar。 diff --git a/patch_v2/ncm_app/test/test_ncm_core.c b/patch_v2/ncm_app/test/test_ncm_core.c index 13e4565..2c0adb9 100644 --- a/patch_v2/ncm_app/test/test_ncm_core.c +++ b/patch_v2/ncm_app/test/test_ncm_core.c @@ -4,7 +4,7 @@ * 缓存 LRU、设备键/cookie 加解密、二维码矩阵、以及 * 网络接口(unikey 实拿 / 轮询 801 / 无凭据错误处理)。 * 网络不可用时网络项按 SKIP 处理,不判失败;内部逻辑错才 FAIL。 - * 用法:test_ncm_core [data_dir](缺省 /tmp/ncm_test) + * 用法:test_ncm_core [data_dir](缺省 $TMPDIR/ncm_test,未设则 /tmp/ncm_test) */ #include #include @@ -87,7 +87,11 @@ static void test_crypto(void) { static void test_store_cookie(void) { printf("== store/device/cookie ==\n"); ncm_store_t st; - ncm_store_init(&st, NULL); /* env MEOW_DATA_DIR 或 /tmp */ + /* 不传 NULL:默认回退 /tmp(Termux 不可写 → deviceId 需持久化、机器密钥需落盘,都会失败) */ + const char *tmpbase = getenv("TMPDIR"); + char sdir[256]; + snprintf(sdir, sizeof sdir, "%s/ncm_store_test", (tmpbase && tmpbase[0]) ? tmpbase : "/tmp"); + ncm_store_init(&st, sdir); char id1[NCM_STORE_ID_MAX], id2[NCM_STORE_ID_MAX]; ncm_store_get_device_id(&st, id1); ncm_store_get_device_id(&st, id2); @@ -108,10 +112,13 @@ static void test_store_cookie(void) { static void test_cache(void) { printf("== cache ==\n"); ncm_cache_t c; - char dir[] = "/tmp/ncm_cache_test"; + /* Termux /tmp 不可写:跟随 $TMPDIR(与 test_cache_actual_naming / tag、cache 单测一致) */ + const char *tmpbase = getenv("TMPDIR"); + char dir[256]; + snprintf(dir, sizeof dir, "%s/ncm_cache_test", (tmpbase && tmpbase[0]) ? tmpbase : "/tmp"); /* 清空目录(须在 init 前:init 会建 songs/covers 子目录,rm -rf 会一并删掉) */ { - char cmd[128]; + char cmd[320]; snprintf(cmd, sizeof cmd, "rm -rf %s; mkdir -p %s", dir, dir); if (system(cmd) != 0) { /* best-effort */ } } @@ -193,9 +200,11 @@ static void make_flac_bytes(unsigned char *buf, size_t *len, int bps) static void test_cache_tag(void) { printf("== cache tag embed + scan (M5) ==\n"); ncm_cache_t c; - char dir[] = "/tmp/ncm_tag_cache"; + const char *tmpbase = getenv("TMPDIR"); + char dir[256]; + snprintf(dir, sizeof dir, "%s/ncm_tag_cache", (tmpbase && tmpbase[0]) ? tmpbase : "/tmp"); { - char cmd[160]; + char cmd[320]; snprintf(cmd, sizeof cmd, "rm -rf %s; mkdir -p %s", dir, dir); if (system(cmd) != 0) { /* best-effort */ } } @@ -762,7 +771,10 @@ static void test_json_unescape(void) { } int main(int argc, char **argv) { - const char *data_dir = (argc > 1) ? argv[1] : "/tmp/ncm_test"; + const char *tmpbase = getenv("TMPDIR"); + char def_dir[256]; + snprintf(def_dir, sizeof def_dir, "%s/ncm_test", (tmpbase && tmpbase[0]) ? tmpbase : "/tmp"); + const char *data_dir = (argc > 1) ? argv[1] : def_dir; printf("ncm_core self-test, data_dir=%s\n", data_dir); ncm_ctx_t ctx; ncm_ctx_init(&ctx, data_dir);