From 7fdec0670fdf78e3c9b78f2e78093b1161f2844c Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Thu, 22 Oct 2026 23:55:54 +0800 Subject: [PATCH] =?UTF-8?q?fix(csrc):=20=E4=BA=A4=E5=8F=89=E9=97=A8?= =?UTF-8?q?=E7=A6=81=E6=94=B9=E7=94=A8=20clang=20--target=EF=BC=8C?= =?UTF-8?q?=E5=8E=BB=E6=8E=89=E4=BC=9A=E5=8D=A1=E6=AD=BB=E7=9A=84=20apt=20?= =?UTF-8?q?=E4=B8=8B=E8=BD=BD=20+=20=E4=BF=AE=E4=B8=A4=E5=A4=84=E5=81=87?= =?UTF-8?q?=E9=80=9A=E8=BF=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 卡死真因(这次可观测了) 上一版加了 `timeout` 硬超时后,日志终于能看出卡在哪: 15:47:33 Fetched 12.5 MB in 1s (9972 kB/s) ← update 很快 15:47:38 Get:2 gcc-13-aarch64-linux-gnu-base 51.9 kB 15:47:39 Get:3 cpp-13-aarch64-linux-gnu 10.8 MB ← 下到这儿卡住 15:50:37 ##[error]apt-get install 超时/失败(180s) **根因:为了跑一道 `-fsyntax-only` 的语法门禁,去下载 10MB+ 的交叉 GCC 工具链, 在 runner 上拉不动。** ## 改法:用 clang 自带的交叉前端 这道门禁只做 `-fsyntax-only`(不链接、不运行)。clang 支持 `--target=aarch64-linux-gnu`,会**真的切到 aarch64 目标**: $ clang -fsyntax-only arch2.c → error: "not aarch64 target" $ clang --target=aarch64-linux-gnu ... → 通过(__aarch64__ 生效) 指针宽度、endianness、C 扩展支持都按目标架构算 —— 足以暴露本门禁要抓的 那类问题。而 clang 在 ubuntu-latest 镜像里**已预装**(实测确认,只列出 `gcc-aarch64-linux-gnu` 一项需要安装)。 优先级:`CC_ARM64`(显式)> `clang --target` > `aarch64-linux-gnu-gcc` > SKIP ## 顺带修掉两处**本来就存在**的假通过 这两处不是本轮引入的,是新写的存在性校验暴露出来的: ### ① `cmd | head` 吞掉编译器退出码 for src in ...; do if ! $CC ... -fsyntax-only $src 2>&1 | head -20; then ok=0; fi done 管道退出码是 `head` 的(恒 0)。实测:`CC_ARM64=nonexistent-xyz make csrc-cross` 竟然输出「0 告警、编译通过 ✓」—— 编译器根本没执行。 修法:改用 `${PIPESTATUS[0]}`,取编译器的真实退出码。 ### ② `PIPESTATUS` 需要 bash,Make 默认是 dash `/bin/sh` 在 Debian/Ubuntu 上是 dash,不支持 `PIPESTATUS`(报 Bad substitution)。 必须显式 `SHELL := /bin/bash`,否则上面的修法静默失效。 ### 风险自证(三条路径实测) | 场景 | 结果 | |---|---| | 正常(clang-target) | ✅ 0 告警、编译通过 ✓ | | `CC_ARM64` 指向不存在的命令 | ✅ `[FAIL] CC_ARM64=nonexistent-xyz 不存在` → Error 1 | | 真的写坏 C 源码 | ✅ 编译器报错 + `[FAIL] arm64 交叉编译失败` → Error 1 | ## CI 侧 - 安装步骤不再要求 `gcc-aarch64-linux-gnu` ⇒ **依赖已预装,直接跳过 apt-get,完全不碰网络** - 工具链确认步骤只校验 `cmake` + `clang` - `[SKIP]` 拦截、`timeout` 硬超时、去 `-qq` 全部保留(对付将来别的原因导致的卡死) --- .github/workflows/ci.yml | 18 +++++++++---- Makefile | 57 +++++++++++++++++++++++++++++++++------- 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a06f027b..9ed4a233 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -192,13 +192,21 @@ jobs: timeout-minutes: 5 run: | set -uo pipefail + # ★ 不再需要 gcc-aarch64-linux-gnu(2026-10-22)。 + # + # 实测:装它要从源拉 10MB+ 的 cpp-13 等依赖,在 runner 上卡住超时 + # (15:47:39 开始下 cpp-13-aarch64,180s 后仍未完成)。 + # 而 csrc-cross 只需要 `-fsyntax-only`,clang 自带交叉前端即可: + # `clang --target=aarch64-linux-gnu` 会真的切到 aarch64 目标 + # (__aarch64__ 宏生效、指针宽度按该架构算),足以暴露本门禁要抓的 + # 架构问题。Makefile 的 csrc-cross 已按 + # CC_ARM64 > clang --target > aarch64-gcc > SKIP 的优先级挑选。 need="" - command -v cmake >/dev/null || need="$need cmake" - command -v clang >/dev/null || need="$need clang" - command -v aarch64-linux-gnu-gcc >/dev/null || need="$need gcc-aarch64-linux-gnu" + command -v cmake >/dev/null || need="$need cmake" + command -v clang >/dev/null || need="$need clang" if [ -z "$need" ]; then - echo "✅ 三件套已存在,跳过 apt-get:$(command -v cmake clang aarch64-linux-gnu-gcc | tr '\n' ' ')" + echo "✅ 依赖已存在,跳过 apt-get:$(command -v cmake clang | tr '\n' ' ')" exit 0 fi @@ -239,7 +247,7 @@ jobs: run: | set -euo pipefail miss="" - for t in cmake clang aarch64-linux-gnu-gcc; do + for t in cmake clang; do if command -v "$t" >/dev/null 2>&1; then echo " ✅ $t: $(command -v "$t")" else diff --git a/Makefile b/Makefile index 4d77fe52..6b00f6b3 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,7 @@ +# 门禁脚本需要 bash 的 PIPESTATUS(/bin/sh=dash 不支持)。 +# 没有它就无法区分"编译器失败"与"head 成功" —— 实测会让门禁假通过。 +SHELL := /bin/bash + .PHONY: test-gui all build build-plain build-cli build-gui clean install test run build-static build-linux-arm64 lint fmt sync-client-versions check-client-versions check-starmap-shared check-client-protocol csrc csrc-test csrc-lint csrc-abi csrc-headers csrc-sanitize csrc-cross csrc-fuzz check-csrc check-csrc-full # HOMED_TAGS 默认带 onnxruntime:发行版**默认启用**本地向量空间(与 # deploy/packaging/build.sh 保持一致)。 @@ -255,21 +259,56 @@ csrc-fuzz: # C 侧的架构相关问题(endianness 假设、指针宽度、size_t vs int 宽度、 # -fsanitize 不可用)只有真的用目标编译器编一遍才会暴露。 # 与 deploy/packaging/build.sh 的 arm64 目标共用同一套 CC 变量。 +# +# ## 为什么首选 clang --target,而不是 aarch64-linux-gnu-gcc(2026-10-22) +# +# 这道门禁只做 `-fsyntax-only`(不链接、不运行),而 clang 自带交叉前端: +# `clang --target=aarch64-linux-gnu` 会真的切到 aarch64 目标(`__aarch64__` +# 宏生效、指针宽度按该架构计算),足以暴露本门禁要抓的那类问题 +# (endianness 假设、指针/size_t 宽度、不支持的 C 扩展)。 +# +# 而装 `gcc-aarch64-linux-gnu` 需要从源拉 10MB+ 的 cpp-13 等依赖 —— +# 实测在 CI runner 上会卡住超时(见 .github/workflows/ci.yml 的加固说明)。 +# 既然只需要语法层验证,就不该为它引入一个会卡死的下载依赖。 +# +# 优先级:CC_ARM64(显式指定) > clang --target > aarch64-linux-gnu-gcc > SKIP +# +# ★ 两个假通过陷阱(实测踩到,勿删): +# 1. 显式 CC_ARM64 指向不存在的命令时,`cmd | head` 的退出码是 head 的(恒 0), +# command-not-found(127) 被吞 ⇒ 门禁显示"通过"。所以循环里改用 PIPESTATUS[0], +# 且对显式指定的编译器先做 command -v 校验。 +# 2. 上述 PIPESTATUS 需要 bash;Make 默认 /bin/sh=dash 不支持(见文件顶 SHELL)。 .PHONY: csrc-cross csrc-cross: @echo "== C 侧交叉编译(linux/arm64)==" - @CC_ARM64=$${CC_ARM64:-aarch64-linux-gnu-gcc}; \ - if ! command -v $$CC_ARM64 >/dev/null 2>&1; then \ - echo " [SKIP] $$CC_ARM64 不存在(未装交叉工具链)"; exit 0; \ - fi; \ - ok=1; \ - for src in $(CSRC_SRCS); do \ - if ! $$CC_ARM64 $(CSRC_CFLAGS) -Werror -fsyntax-only $$src 2>&1 | head -20; then \ - ok=0; \ + @ok=1; \ + if [ -n "$${CC_ARM64:-}" ]; then \ + if ! command -v "$$CC_ARM64" >/dev/null 2>&1; then \ + echo " [FAIL] CC_ARM64=$$CC_ARM64 不存在"; exit 1; \ fi; \ + CC="$$CC_ARM64"; MODE="explicit"; \ + elif command -v clang >/dev/null 2>&1; then \ + CC="clang --target=aarch64-linux-gnu"; MODE="clang-target"; \ + elif command -v aarch64-linux-gnu-gcc >/dev/null 2>&1; then \ + CC="aarch64-linux-gnu-gcc"; MODE="cross-gcc"; \ + else \ + echo " [SKIP] 无 clang、无 aarch64-linux-gnu-gcc(未装任何交叉前端)"; exit 0; \ + fi; \ + \ + if [ "$$MODE" = "clang-target" ]; then \ + printf 'int main(void){return 0;}\n' > /tmp/_archchk.c; \ + if ! clang --target=aarch64-linux-gnu -fsyntax-only /tmp/_archchk.c 2>/dev/null; then \ + echo " [SKIP] clang 不支持 --target=aarch64-linux-gnu(版本过旧)"; exit 0; \ + fi; \ + fi; \ + \ + for src in $(CSRC_SRCS); do \ + $$CC $(CSRC_CFLAGS) -Werror -fsyntax-only $$src 2>&1 | head -20; \ + rc=$${PIPESTATUS[0]}; \ + if [ "$$rc" != "0" ]; then ok=0; fi; \ done; \ if [ "$$ok" = "1" ]; then \ - echo " $$CC_ARM64: 0 告警、编译通过 ✓($(words $(CSRC_SRCS)) 个源文件)"; \ + echo " $$CC [$$MODE]: 0 告警、编译通过 ✓($(words $(CSRC_SRCS)) 个源文件)"; \ else \ echo " [FAIL] arm64 交叉编译失败"; exit 1; \ fi