Files
chongming-core/test/test_nuitka_pack_flags.py

124 lines
4.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
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("✅ 全部通过")