119 lines
4.0 KiB
Python
119 lines
4.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Build a deterministic inventory for the GMOCCAPY source and case assets."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
OUT = Path(__file__).resolve().parent
|
|
|
|
|
|
def sha256(path: Path) -> str:
|
|
return hashlib.sha256(path.read_bytes()).hexdigest()
|
|
|
|
|
|
def classify(rel: str) -> str:
|
|
path = Path(rel)
|
|
if "/configs/sim/gmoccapy/" in rel:
|
|
if path.suffix == ".ini":
|
|
return "simulation_ini"
|
|
if path.suffix in {".hal", ".tbl", ".para", ".var", ".pref"}:
|
|
return "simulation_runtime_data"
|
|
if path.suffix in {".ngc", ".py", ".ui", ".glade", ".svg", ".txt", ".README"}:
|
|
return "simulation_resource"
|
|
return "simulation_asset"
|
|
if "/src/emc/usr_intf/gmoccapy/" in rel or "/lib/python/gmoccapy/" in rel:
|
|
return "implementation"
|
|
if "/share/gmoccapy/" in rel:
|
|
if "icons/" in rel:
|
|
return "icon_asset"
|
|
return "installed_ui_asset"
|
|
if "/src/po/gmoccapy/" in rel:
|
|
return "translation"
|
|
if "/docs/src/gui/images/gmoccapy_" in rel or "/docs/src/user/images/gmoccapy_" in rel:
|
|
return "documentation_asset"
|
|
if "/docs/src/gui/gmoccapy" in rel or "/docs/man/man1/gmoccapy" in rel:
|
|
return "documentation"
|
|
return "cross_reference"
|
|
|
|
|
|
def collect(paths: list[Path]) -> list[dict[str, object]]:
|
|
files: list[Path] = []
|
|
for base in paths:
|
|
if base.is_file():
|
|
files.append(base)
|
|
elif base.is_dir():
|
|
files.extend(p for p in base.rglob("*") if p.is_file())
|
|
rows = []
|
|
for path in sorted(set(files)):
|
|
rel = path.relative_to(ROOT).as_posix()
|
|
rows.append(
|
|
{
|
|
"path": rel,
|
|
"role": classify(rel),
|
|
"bytes": path.stat().st_size,
|
|
"sha256": sha256(path),
|
|
}
|
|
)
|
|
return rows
|
|
|
|
|
|
def main() -> None:
|
|
rows = collect(
|
|
[
|
|
ROOT / "linuxcnc/configs/sim/gmoccapy",
|
|
ROOT / "linuxcnc/src/emc/usr_intf/gmoccapy",
|
|
ROOT / "linuxcnc/lib/python/gmoccapy",
|
|
ROOT / "linuxcnc/share/gmoccapy",
|
|
ROOT / "linuxcnc/src/po/gmoccapy",
|
|
ROOT / "linuxcnc/docs/src/gui/gmoccapy.adoc",
|
|
ROOT / "linuxcnc/docs/man/man1/gmoccapy.1",
|
|
*sorted((ROOT / "linuxcnc/docs/src/gui/images").glob("gmoccapy_*")),
|
|
*sorted((ROOT / "linuxcnc/docs/src/user/images").glob("gmoccapy_*")),
|
|
]
|
|
)
|
|
summary: dict[str, int] = {}
|
|
for row in rows:
|
|
role = str(row["role"])
|
|
summary[role] = summary.get(role, 0) + 1
|
|
payload = {
|
|
"schema": "gmoccapy-file-inventory.v1",
|
|
"scope": [
|
|
"linuxcnc/configs/sim/gmoccapy/**",
|
|
"linuxcnc/src/emc/usr_intf/gmoccapy/**",
|
|
"linuxcnc/lib/python/gmoccapy/**",
|
|
"linuxcnc/share/gmoccapy/**",
|
|
"linuxcnc/src/po/gmoccapy/**",
|
|
"linuxcnc/docs/src/gui/gmoccapy.adoc",
|
|
"linuxcnc/docs/man/man1/gmoccapy.1",
|
|
"linuxcnc/docs/src/gui/images/gmoccapy_*",
|
|
"linuxcnc/docs/src/user/images/gmoccapy_*",
|
|
],
|
|
"file_count": len(rows),
|
|
"role_counts": dict(sorted(summary.items())),
|
|
"files": rows,
|
|
}
|
|
(OUT / "gmoccapy-file-inventory.json").write_text(
|
|
json.dumps(payload, ensure_ascii=False, sort_keys=True, indent=2) + "\n",
|
|
encoding="utf-8",
|
|
)
|
|
lines = [
|
|
"# GMOCCAPY 文件清单",
|
|
"",
|
|
f"清单范围内共 `{len(rows)}` 个文件;每项包含相对路径、角色、字节数和 SHA-256。",
|
|
"",
|
|
"| 角色 | 数量 |",
|
|
"|---|---:|",
|
|
]
|
|
lines.extend(f"| `{role}` | {count} |" for role, count in sorted(summary.items()))
|
|
lines.extend(["", "完整逐文件数据见 [`gmoccapy-file-inventory.json`](gmoccapy-file-inventory.json)。", ""])
|
|
(OUT / "gmoccapy-file-inventory.md").write_text("\n".join(lines), encoding="utf-8")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|