124 lines
4.8 KiB
Python
124 lines
4.8 KiB
Python
"""
|
||||
|
|
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("✅ 全部通过")
|