111 lines
3.2 KiB
Python
111 lines
3.2 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""Generate protocol/git_user_fw_ver.h from current git HEAD + branch.
|
||
|
||
用户固件版本 (01 0C) 线格式: {branch}_{short6}[optional '*']
|
||
例: develop_0aedb4 / feature-ui_57448b*
|
||
|
||
- short6 = git rev-parse --short=6 HEAD(小写 hex)
|
||
- branch 清洗为 [A-Za-z0-9.-],整串(不含 dirty '*') ≤ 24
|
||
- dirty working tree 时末尾追加 '*'
|
||
|
||
用法(IAR Pre-build):
|
||
python \"$PROJ_DIR$\\..\\..\\tools\\gen_git_user_fw_ver.py\"
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import os
|
||
import re
|
||
import subprocess
|
||
|
||
ROOT = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
|
||
OUT = os.path.join(ROOT, "protocol", "git_user_fw_ver.h")
|
||
BUILD_ID_MAX = 24 # without trailing dirty '*'
|
||
|
||
|
||
def _git(*args: str) -> str | None:
|
||
try:
|
||
return subprocess.check_output(
|
||
["git", *args],
|
||
cwd=ROOT,
|
||
stderr=subprocess.DEVNULL,
|
||
text=True,
|
||
).strip()
|
||
except (subprocess.CalledProcessError, FileNotFoundError, OSError):
|
||
return None
|
||
|
||
|
||
def sanitize_branch(name: str) -> str:
|
||
if not name or name == "HEAD":
|
||
return "DETACHED"
|
||
# feature/foo → feature-foo;去掉其它非法字符
|
||
s = name.replace("/", "-")
|
||
s = re.sub(r"[^A-Za-z0-9.-]+", "-", s)
|
||
s = re.sub(r"-{2,}", "-", s).strip("-.")
|
||
return s or "DETACHED"
|
||
|
||
|
||
def make_build_id(branch: str, short6: str) -> str:
|
||
"""Ensure '{branch}_{short6}' length ≤ BUILD_ID_MAX; keep commit suffix intact."""
|
||
short6 = short6.lower()[:6].ljust(6, "0")
|
||
suffix = "_" + short6
|
||
max_br = BUILD_ID_MAX - len(suffix)
|
||
if max_br < 1:
|
||
return short6[:BUILD_ID_MAX]
|
||
br = branch[:max_br]
|
||
return br + suffix
|
||
|
||
|
||
def main() -> int:
|
||
full = _git("rev-parse", "HEAD") or ("0" * 40)
|
||
if len(full) < 6 or not all(c in "0123456789abcdefABCDEF" for c in full):
|
||
full = "0" * 40
|
||
full = full.lower()
|
||
short6 = full[:6]
|
||
|
||
br_raw = _git("rev-parse", "--abbrev-ref", "HEAD") or "DETACHED"
|
||
branch = sanitize_branch(br_raw)
|
||
|
||
dirty = False
|
||
st = _git("status", "--porcelain")
|
||
if st:
|
||
dirty = True
|
||
|
||
build_id = make_build_id(branch, short6)
|
||
wire = build_id + ("*" if dirty else "")
|
||
# C string escape
|
||
wire_c = wire.replace("\\", "\\\\").replace('"', '\\"')
|
||
|
||
text = f"""/* Auto-generated by tools/gen_git_user_fw_ver.py — do not edit.
|
||
* git HEAD{' (dirty)' if dirty else ''}: {full}
|
||
* branch: {br_raw} → {branch}
|
||
* 01 0C wire: {wire}
|
||
*/
|
||
#ifndef GIT_USER_FW_VER_H
|
||
#define GIT_USER_FW_VER_H
|
||
|
||
#define GIT_COMMIT_ID_FULL "{full}"
|
||
#define GIT_BRANCH_NAME "{branch}"
|
||
#define GIT_COMMIT_SHORT6 "{short6}"
|
||
#define GIT_DIRTY ({1 if dirty else 0})
|
||
#define GIT_BUILD_ID "{wire_c}"
|
||
#define GIT_BUILD_ID_LEN {len(wire)}u
|
||
|
||
#endif /* GIT_USER_FW_VER_H */
|
||
"""
|
||
os.makedirs(os.path.dirname(OUT), exist_ok=True)
|
||
old = ""
|
||
if os.path.isfile(OUT):
|
||
with open(OUT, "r", encoding="utf-8") as f:
|
||
old = f.read()
|
||
if old != text:
|
||
with open(OUT, "w", encoding="utf-8", newline="\n") as f:
|
||
f.write(text)
|
||
print(f"gen_git_user_fw_ver: wrote {OUT} ({wire})")
|
||
else:
|
||
print(f"gen_git_user_fw_ver: up-to-date ({wire})")
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
raise SystemExit(main())
|