From 5678683561e1a3c0668405b73d81c12820c92f91 Mon Sep 17 00:00:00 2001 From: AskaEth Date: Fri, 6 Nov 2026 11:59:31 +0800 Subject: [PATCH] =?UTF-8?q?fix(release):=20M140=20r1=20=E5=A4=8D=E5=AE=A1?= =?UTF-8?q?=E4=B8=89=E6=9D=A1=20p2=20=E2=80=94=E2=80=94=20vswhere=20-find?= =?UTF-8?q?=20=E5=8D=95=E5=80=BC=E3=80=81=E6=A0=B9=E5=9B=A0=E6=8E=AA?= =?UTF-8?q?=E8=BE=9E=E3=80=81SDK=20=E5=8F=8C=E6=A0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit P2-1(真缺陷):`-find` 是单值参数,老写法 `... "${find_args[@]}"` 一次传 4 个 `-find`,官方定位器只认其中一个(复现:last-wins 下取到 Hostx64), 所以 round-1 新加的「ARM64 优先 → Hostarm64」在 vswhere 分支上从未执行。 改成每个 pattern 各起一次定位器,不赌取值规则;注释同步为真话。 P2-2(措辞):README §4 不再声称 v0.2.1 那次是「x64 镜像缺库」——windows-2025 镜像清单里 VC.Tools.ARM64 与 SDK 26100 都在,根因未定论,更像自检的路径探测; §7.4 的 dumpbin 门禁补一句覆盖 windows-latest 与 windows-11-arm 两个镜像。 P2-3(放宽):arm64 库体检的 SDK 侧也改成两个安装根(Program Files (x86) 与 Program Files),把「路径猜错 → fail-closed 误报」这个正是本次事故的形态再降一档。 行数:release.yml 400 / README 400(均 ≤400);PUBLISH_SLOTS 未动。 --- .github/workflows/release.yml | 41 ++++++++++++++++++----------------- packaging/README.md | 10 ++++----- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 169070e..16d09a0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -60,9 +60,8 @@ jobs: python3 - "$ALL_SLOTS" >> "$GITHUB_OUTPUT" <<'PY' import json, sys # 槽位表(唯一真相源)。mode:"ci" = 默认在 CI 里构建;"probe" = 未验证,仅 all_slots=true 时构建。 - # **没有一个槽位交叉编译**(M140):每槽都在**自己的架构**上原生构建,故不设 `target` 键、产物一律 - # 在 target/release。哪天再加交叉槽位,把 `target` 键、`build_dir=target//release` 与 - # 「`rustup target add` + cargo 的 `--target`」两处一起加回来(现在它们已随最后一个交叉槽位删掉)。 + # **没有交叉槽位**(M140):每槽在自己的架构上原生构建,故无 `target` 键、产物一律在 target/release。 + # 要加回交叉槽位:`target` 键 + `build_dir=target//release` + `rustup target add` 与 cargo 的 `--target`。 SLOTS = [ # Windows x64:MSVC + 静态 CRT(+crt-static)→ exe 不依赖 VC++ 可再发行包,双击即用的单文件。 dict(slot="windows-x64", os="windows-latest", build_dir="target/release", rustflags="-C target-feature=+crt-static", mode="ci"), @@ -110,31 +109,30 @@ jobs: - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - # (M140)windows-arm64 跑在**原生** `windows-11-arm` 上,所以本步查的是"这台机器上的 ARM64 库齐不齐": - # `+crt-static` 要 VC 的 `lib/arm64/libcmt.lib`,链接要 SDK 的 `um/arm64/kernel32.lib`。 - # 门禁不降级(缺了就 fail),但**失败必须能自己定位**:把实际探测到的根目录 / lib 目录全打出来 - # ——原来只报"缺失",2026-11-06 第一次真跑就卡在这里,而单看日志无法判断是镜像没装还是探测写错了。 - # 两个探测还都改宽容了:VS 两个根都扫;SDK 不再只认 `tail -n1` 挑出的那一个版本,全部版本里找。 + # (M140)windows-arm64 跑在**原生** `windows-11-arm` 上,本步查"这台机器上的 ARM64 库齐不齐": + # `+crt-static` 要 VC 的 `lib/arm64/libcmt.lib`,链接要 SDK 的 `um/arm64/kernel32.lib`。门禁不降级 + # (缺了就 fail),但失败必须能自己定位:把探测到的根目录 / lib 目录全打出来——原来只报"缺失", + # 2026-11-06 首次真跑就卡在这里而看不出原因。宽容化:VS 与 SDK **各扫两个安装根**,SDK 全版本找。 - name: Windows ARM64 工具链体检 if: matrix.slot == 'windows-arm64' shell: bash run: | set -euo pipefail vs_roots=("C:/Program Files/Microsoft Visual Studio" "C:/Program Files (x86)/Microsoft Visual Studio") - sdk_root="C:/Program Files (x86)/Windows Kits/10/Lib" + # SDK 同样两个根:老安装器落在 `(x86)`,别只猜一个 —— 猜错路径就是这次事故的形态(fail-closed 误报)。 + sdk_roots=("C:/Program Files (x86)/Windows Kits/10/Lib" "C:/Program Files/Windows Kits/10/Lib") echo "── 探测的根目录 ──" - for r in "${vs_roots[@]}"; do + for r in "${vs_roots[@]}" "${sdk_roots[@]}"; do if [ -d "$r" ]; then echo " 有 $r"; else echo " 无 $r"; fi done - if [ -d "$sdk_root" ]; then echo " 有 $sdk_root"; else echo " 无 $sdk_root"; fi vc_dirs=$(find "${vs_roots[@]}" -maxdepth 9 -type d -path '*/VC/Tools/MSVC/*/lib/arm64' 2>/dev/null || true) echo "── VC 的 arm64 lib 目录(*/VC/Tools/MSVC/*/lib/arm64)──" echo "${vc_dirs:- <无>}" - sdk_dirs=$(find "$sdk_root" -maxdepth 3 -type d -path '*/um/arm64' 2>/dev/null || true) + sdk_dirs=$(find "${sdk_roots[@]}" -maxdepth 3 -type d -path '*/um/arm64' 2>/dev/null || true) echo "── SDK 的 um/arm64 目录 ──" echo "${sdk_dirs:- <无>}" vc_lib=$(find "${vs_roots[@]}" -maxdepth 11 -type f -path '*/VC/Tools/MSVC/*/lib/arm64/libcmt.lib' 2>/dev/null | head -n1 || true) - sdk_lib=$(find "$sdk_root" -maxdepth 4 -type f -path '*/um/arm64/kernel32.lib' 2>/dev/null | head -n1 || true) + sdk_lib=$(find "${sdk_roots[@]}" -maxdepth 4 -type f -path '*/um/arm64/kernel32.lib' 2>/dev/null | head -n1 || true) echo "libcmt.lib: ${vc_lib:-<缺失>}" echo "kernel32.lib: ${sdk_lib:-<缺失>}" if [ -z "$vc_lib" ] || [ -z "$sdk_lib" ]; then @@ -169,8 +167,7 @@ jobs: # ① vswhere(VS 官方定位器,安装器路径固定)② find 兜底(两个 VS 根都扫,Build Tools 在 # x86 根)③ PATH(镜像预置则直接可用;排最后是刻意的:PATH 最不可控,官方定位器优先)。 # dumpbin 在 MSVC 的**宿主**工具目录里:x64 runner 是 `Hostx64/x64`,arm64 runner(M140)是 - # `Hostarm64/arm64`(x64 那族作为兜底,ARM64 Windows 靠模拟层跑得起来)。反向不成立 —— - # 在 x64 runner 上挑 `Hostarm64` 只会拿到跑不起来的 arm64 二进制,所以按 RUNNER_ARCH 选。 + # `Hostarm64/arm64`(x64 那族兜底,靠模拟层能跑);反向不行——x64 上挑 Hostarm64 是跑不起来的二进制。 pats=('VC/Tools/MSVC/*/bin/Hostx64/x64/dumpbin.exe') if [ "${RUNNER_ARCH:-X64}" = "ARM64" ]; then pats=('VC/Tools/MSVC/*/bin/Hostarm64/arm64/dumpbin.exe' "${pats[@]}") @@ -179,11 +176,15 @@ jobs: DUMPBIN="" VIA="" if [ -x "$VSWHERE" ]; then - # -find 的模式先用反斜杠(vswhere 官方文档即此形式),再试正斜杠以规避分隔符差异。 - find_args=() - for p in "${pats[@]}"; do find_args+=(-find "${p//\//\\}" -find "$p"); done - hit=$("$VSWHERE" -latest -products '*' "${find_args[@]}" 2>/dev/null | head -n1 || true) - if [ -n "$hit" ]; then DUMPBIN=${hit//\\//}; VIA="vswhere"; fi + # `-find` 是**单值**参数:一次传多个 pattern 只有一个生效(3.x 取最后一个 —— 老写法里 Hostarm64 + # 因此永远轮不到)。所以每个 pattern 各起一次定位器,不赌它取哪个。 + # 反斜杠形态优先(vswhere 官方文档即此形式),再试正斜杠以规避分隔符差异。 + for p in "${pats[@]}"; do + for pat in "${p//\//\\}" "$p"; do + hit=$("$VSWHERE" -latest -products '*' -find "$pat" 2>/dev/null | head -n1 || true) + if [ -n "$hit" ]; then DUMPBIN=${hit//\\//}; VIA="vswhere"; break 2; fi + done + done fi if [ -z "$DUMPBIN" ]; then # VS 根形如 <年>/<版次>(2022/Enterprise、2022/BuildTools……),固定层数的 glob 会踩空,按模式扫。 diff --git a/packaging/README.md b/packaging/README.md index b1cf7e9..73d4a1d 100644 --- a/packaging/README.md +++ b/packaging/README.md @@ -135,10 +135,10 @@ Windows 为什么用 MSVC 静态 CRT 而不是本机这套 GNU 工具链:两 产物形态与门禁跟 x64 一模一样(裸 exe + 静态 CRT + dumpbin 导入表硬门禁),差别只在**怎么编**: **M140 起在原生 `windows-11-arm` 上编**(GitHub 文档 2026 的 Standard runners 表明列该标签:4C/16G、 公开仓库免费),产物落在 `target/release`(`matrix.build_dir` 注入,脚本不硬编码路径)。原路线是在 -`windows-latest` 上交叉编译到 `aarch64-pc-windows-msvc`,但**CI 上从未成功过一次**(v0.2.1 首次真跑即 -红在「ARM64 工具链体检」:x64 镜像上探不到 VC/SDK 的 ARM64 库),"双 Windows 共用一套镜像"的惯性理由 -因此不再成立。原生的附赠好处:宿主=产物架构,`ull --version` 冒烟**真跑**;M50 的 dumpbin 定位链补了 -`Hostarm64/arm64` 分支,并按 `RUNNER_ARCH` 只挑本机能执行的那族(x64 上不会挑到跑不起来的 arm64 二进制)。 +`windows-latest` 上交叉编译到 `aarch64-pc-windows-msvc`,但**CI 上从未成功过一次**(v0.2.1 首次真跑即红在 +「ARM64 工具链体检」)。那次**根因未定论**:`windows-2025` 镜像清单里 `VC.Tools.ARM64` 与 SDK 26100 都在, +更像自检自己的路径探测有问题;无论哪种,交叉都多一层"在 x64 宿主上猜 ARM64 库路径"。原生的附赠好处: +宿主=产物架构,`ull --version` 冒烟**真跑**;dumpbin 链补了 `Hostarm64/arm64` 分支并按 `RUNNER_ARCH` 选族。 运行时依赖与 x64 同表(UCRT 是 Win11 ARM 的系统组件,`+crt-static` 静态链 CRT,不需要可再发行包)。 游戏侧如实说:**启动器原生运行**;Temurin 没有 windows-aarch64 产物,Microsoft OpenJDK 有该 @@ -282,7 +282,7 @@ uxtheme/uiautomationcore/opengl32/ws2_32/bcrypt/bcryptprimitives/ntdll` + 一组 `libgcc_s_*.dll` 之类 MinGW 运行时 —— 即"拷走一个 exe 就能跑"(Win10+ 自带 UCRT)。 CI 的 MSVC `+crt-static` 产物同理(静态 CRT);`release.yml` 里用 `dumpbin -dependents` 把这条变成硬门禁, 两侧都 fail、没有降级分支:**出现 VCRUNTIME/MSVCP/ucrtbase 就 fail**,**找不到 dumpbin 同样 fail** -(`windows-latest` 自带 VS 工具链,基本找得到;"找不到就跳过"等于让门禁在最需要它的时候失效)。 +(`windows-latest` 与 `windows-11-arm` 两个镜像都自带 VS 工具链,基本找得到;跳过等于让门禁在最需要它时失效)。 总之不允许"悄悄要用户装可再发行包"。 注意:本机只装了 GNU 工具链,MSVC 那个槽位的产物本身**没有在本机验证过**,由 CI 首跑验证。