Files

124 lines
4.8 KiB
Python
Raw Permalink Normal View History

"""
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("✅ 全部通过")