新增 cubemx-regen、board-test-suite、new-machine-setup、mem-budget 四个项目级 skill
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""env_check.py - 换机/新克隆环境自检(纯标准库,只读不修)。
|
||||
|
||||
用法(项目根目录,任一 python3 ≥3.8):
|
||||
python <skill>/scripts/env_check.py [--venv .venv]
|
||||
|
||||
检查项: venv 与关键包 / DTU_* 工具路径变量(会话 vs 用户级)/ UV4 /
|
||||
ST-LINK_CLI / COM 口在位 / pre-commit。每行 PASS|WARN|FAIL,FAIL 行附
|
||||
可直接执行的修复命令。退出码: 0=可用(无 FAIL) / 1=有 FAIL。
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def line(status, name, detail=""):
|
||||
print("{:4} {:24} {}".format(status, name, detail))
|
||||
return status
|
||||
|
||||
|
||||
def env_user_scope(name):
|
||||
"""读用户级环境变量(Windows 注册表,标准库 winreg;其他平台回退 os.environ)。"""
|
||||
if os.name != "nt":
|
||||
return os.environ.get(name)
|
||||
try:
|
||||
import winreg
|
||||
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Environment") as k:
|
||||
v, _ = winreg.QueryValueEx(k, name)
|
||||
return v
|
||||
except OSError:
|
||||
return None
|
||||
|
||||
|
||||
def check_tool_env(name, venv_py):
|
||||
"""工具路径变量:会话值 > 用户级值;区分'没设'与'设了没继承'。"""
|
||||
sess = os.environ.get(name)
|
||||
if sess:
|
||||
if Path(sess).is_file():
|
||||
return line("PASS", name, sess)
|
||||
return line("FAIL", name, "已设但路径无效: {}(改正值本身)".format(sess))
|
||||
user = env_user_scope(name)
|
||||
if user:
|
||||
return line("WARN", name, "用户级已设({}),本会话未继承——注入: $env:{} = '{}'".format(
|
||||
user, name, user))
|
||||
return None # 未设:由调用方决定 WARN/FAIL
|
||||
|
||||
|
||||
def py_import_ok(py, mod):
|
||||
if not py or not Path(py).is_file():
|
||||
return False
|
||||
r = subprocess.run([str(py), "-c", "import " + mod], capture_output=True, timeout=30)
|
||||
return r.returncode == 0
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
for s in (sys.stdout, sys.stderr):
|
||||
try:
|
||||
s.reconfigure(encoding="utf-8", errors="replace")
|
||||
except Exception:
|
||||
pass
|
||||
ap = argparse.ArgumentParser(description="environment self-check (read-only)")
|
||||
ap.add_argument("--venv", default=".venv", help="venv dir (default ./.venv)")
|
||||
args = ap.parse_args(argv)
|
||||
|
||||
fails = 0
|
||||
root = Path.cwd()
|
||||
|
||||
# 1) venv 与关键包
|
||||
venv_py = root / args.venv / "Scripts" / "python.exe"
|
||||
if not venv_py.is_file():
|
||||
venv_py = root / args.venv / "bin" / "python" # *nix
|
||||
if line("FAIL" if not venv_py.is_file() else "PASS", "venv",
|
||||
str(venv_py) if venv_py.is_file() else
|
||||
"缺失——& <任一 python ≥3.10> -m venv {}; & '{}\\Scripts\\python.exe' -m pip install -r requirements.txt".format(
|
||||
args.venv, args.venv)) == "FAIL":
|
||||
fails += 1
|
||||
else:
|
||||
for mod, pipname in (("serial", "pyserial"), ("fontTools", "fontTools"),
|
||||
("pre_commit", "pre-commit")):
|
||||
if line("PASS" if py_import_ok(venv_py, mod) else "FAIL",
|
||||
"pkg:" + mod, "" if py_import_ok(venv_py, mod) else
|
||||
"缺失——& '{}' -m pip install {}".format(venv_py, pipname)) == "FAIL":
|
||||
fails += 1
|
||||
|
||||
# 2) 工具路径变量
|
||||
r = check_tool_env("DTU_KEIL_UV4", venv_py)
|
||||
if r is None:
|
||||
w = shutil.which("UV4.exe") or shutil.which("UV4")
|
||||
if line("FAIL" if not w else "WARN", "UV4 可达",
|
||||
w or "DTU_KEIL_UV4 未设且 PATH 无 UV4——[Environment]::SetEnvironmentVariable('DTU_KEIL_UV4','<本机>\\UV4.exe','User')"):
|
||||
fails += 1
|
||||
r = check_tool_env("DTU_STLINK_CLI", venv_py)
|
||||
if r is None:
|
||||
w = shutil.which("ST-LINK_CLI.exe") or shutil.which("ST-LINK_CLI")
|
||||
line("PASS" if w else "WARN", "ST-LINK_CLI 可达",
|
||||
w or "未设 DTU_STLINK_CLI 且 PATH 无(烧录机需二者之一)")
|
||||
|
||||
# 3) COM 口在位(用 venv 的 pyserial,不碰 daemon)
|
||||
if py_import_ok(venv_py, "serial.tools.list_ports"):
|
||||
r = subprocess.run([str(venv_py), "-c",
|
||||
"import serial.tools.list_ports as p;"
|
||||
"print(';'.join(x.device for x in p.comports()))"],
|
||||
capture_output=True, timeout=30)
|
||||
ports = (r.stdout or b"").decode("utf-8", "replace").strip()
|
||||
if line("PASS" if ports else "FAIL", "COM 口",
|
||||
ports or "无任何 COM 设备——装 CH340 驱动 / 检查 USB"):
|
||||
if not ports:
|
||||
fails += 1
|
||||
else:
|
||||
line("WARN", "COM 口", "跳过(pyserial 不可用)")
|
||||
|
||||
# 4) 项目配置存在
|
||||
cfg = root / ".serialtest" / "config.json"
|
||||
if line("PASS" if cfg.is_file() else "FAIL", "串口配置档案",
|
||||
str(cfg) if cfg.is_file() else
|
||||
"缺失——建 .serialtest/config.json(portName/baudRate,见 .dsh/skills/serial-boot-test/config.example.json)"):
|
||||
if not cfg.is_file():
|
||||
fails += 1
|
||||
|
||||
print("SUMMARY: {}".format("OK - 环境可用(WARN 项按提示处理)" if fails == 0
|
||||
else "{} 项 FAIL - 按上面修复命令处理".format(fails)))
|
||||
return 0 if fails == 0 else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user