Files
STM32F4-Base/.dsh/skills/new-machine-setup/scripts/env_check.py
T

132 lines
5.2 KiB
Python
Raw 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.
#!/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())