""" Nuitka 打包资源档位 / 链接器注入 测试(不触发真实编译) 覆盖 build.py 里 nuitka 打包的三处防坑逻辑: 1. 链接器:装了 ld.lld 时自动经 LDFLAGS 注入 -fuse-ld=lld(默认 GNU ld 链接大工程 峰值数 GB,小内存机器/WSL 会被 OOM 杀掉:"clang: unable to execute command: Killed"); 已有 LDFLAGS 时不覆盖(用户在 CHONGMING_NUITKA_PGO 之外自行指定链接器优先) 2. 内存档位:可用内存 ≥ NUITKA_LTO_MIN_GB 完整优化(LTO);低于则去 LTO(LTO 链接 峰值 3 GB+,实测 ld/lld 都被 OOM 杀过);更低(或 CHONGMING_NUITKA_LOW_MEM=1) 进一步 --low-memory + 降并发;CHONGMING_NUITKA_FULL=1 强制完整优化 3. PGO 缺省关闭:--pgo-c 与 standalone/onefile 不兼容且 profiling 运行会等程序退出 (常驻服务卡死),只能由 CHONGMING_NUITKA_PGO=1 显式打开—— 这里用源码断言兜住"默认关"这条(防止回潮) 运行: uv run python test/test_nuitka_pack_flags.py """ import io import contextlib import os import shutil import sys from pathlib import Path ROOT = Path(__file__).parent.parent sys.path.insert(0, str(ROOT / "utils" / "cli" / "src")) from chongming_cli import build as b # noqa: E402 failures = [] def check(name, cond, detail=""): status = "PASS" if cond else "FAIL" print(f"[{status}] {name}" + (f" — {detail}" if detail else "")) if not cond: failures.append(name) def quiet_note(): """调 _nuitka_env_and_mem_note 时屏蔽它打印的降档提示。""" with contextlib.redirect_stdout(io.StringIO()): return b._nuitka_env_and_mem_note() saved = {k: os.environ.pop(k, None) for k in (b.NUITKA_LOW_MEM_ENV, b.NUITKA_FULL_ENV, b.NUITKA_PGO_ENV)} try: # ========== 1. 链接器注入 ========== saved_ldflags = os.environ.pop("LDFLAGS", None) env, note, profile = quiet_note() if sys.platform.startswith("linux") and shutil.which("ld.lld"): check("装了 lld 自动注入 -fuse-ld=lld", env.get("LDFLAGS", "").endswith("-fuse-ld=lld"), env.get("LDFLAGS")) check("成功信息回显链接器", "lld" in note, note) else: check("无 lld 时不注入(本机无 lld,跳过)", "-fuse-ld" not in (env.get("LDFLAGS") or "")) os.environ["LDFLAGS"] = "-Wl,--as-needed" env, _, _ = quiet_note() check("已有 LDFLAGS 时追加而不覆盖", env["LDFLAGS"].startswith("-Wl,--as-needed"), env["LDFLAGS"]) if saved_ldflags is None: os.environ.pop("LDFLAGS", None) else: os.environ["LDFLAGS"] = saved_ldflags # ========== 2. 内存档位 ========== mem = b._mem_available_gb() check("_mem_available_gb 可读(Linux)", mem > 0 or not sys.platform.startswith("linux"), f"{mem:.1f} GB") env, note, profile = quiet_note() if mem >= 0: expect = ( "squeeze" if mem < b.NUITKA_SQUEEZE_GB else "no-lto" if mem < b.NUITKA_LTO_MIN_GB else "full" ) check( "默认档位与内存阈值一致", profile == expect, f"{mem:.1f} GB → {profile}(阈值 {b.NUITKA_SQUEEZE_GB}/{b.NUITKA_LTO_MIN_GB} GB)", ) check( "非 full 档在提示里说明原因", (profile == "full" and "LTO" not in note) or ( profile == "no-lto" and "未启用 LTO" in note ) or (profile == "squeeze" and "--low-memory" in note), note, ) os.environ[b.NUITKA_LOW_MEM_ENV] = "1" env, note, profile = quiet_note() check(f"{b.NUITKA_LOW_MEM_ENV}=1 强制 squeeze 档", profile == "squeeze" and "--low-memory" in note, note) os.environ.pop(b.NUITKA_LOW_MEM_ENV) os.environ[b.NUITKA_FULL_ENV] = "1" env, note, profile = quiet_note() check(f"{b.NUITKA_FULL_ENV}=1 强制完整优化(覆盖内存自动判断)", profile == "full" and "--low-memory" not in note, note) os.environ.pop(b.NUITKA_FULL_ENV) # ========== 3. PGO 缺省关闭(源码兜底,防回潮) ========== src = (ROOT / "utils" / "cli" / "src" / "chongming_cli" / "build.py").read_text(encoding="utf-8") check( "PGO 参数只在 NUITKA_PGO_ENV 开关下出现", f'os.environ.get(NUITKA_PGO_ENV) == "1"' in src and b.NUITKA_PGO_ENV == "CHONGMING_NUITKA_PGO", ) check( "Nuitka 缺 patchelf 时提前失败(链接后才检查,白跑一整轮)", "patchelf" in src and "缺少 patchelf" in src, ) check( "Nuitka 失败会抛错而不是静默返回(构建要非零退出)", 'raise RuntimeError("Nuitka 编译失败' in src, ) finally: for k, v in saved.items(): if v is not None: os.environ[k] = v print("\n" + ("=" * 40)) if failures: print(f"❌ {len(failures)} 项未通过: {failures}") sys.exit(1) print("✅ 全部通过")