yams/scripts/generate_version_header.py
2026-07-04 09:28:59 -04:00

115 lines
3.6 KiB
Python

#!/usr/bin/env python3
import datetime as _dt
import os
import pathlib
import re
import subprocess
import sys
def _run_git(repo_root: str, git_exe: str, args: list[str]) -> str:
try:
proc = subprocess.run(
[git_exe, "-c", f"safe.directory={repo_root}", *args],
cwd=repo_root,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
text=True,
check=False,
)
except OSError:
return ""
if proc.returncode != 0:
return ""
return proc.stdout.strip()
def _compute_effective_version(
repo_root: str, git_exe: str, project_version: str, override_version: str
) -> str:
if override_version:
return override_version
tag = _run_git(
repo_root, git_exe, ["describe", "--tags", "--match", "v*", "--abbrev=0"]
)
if tag.startswith("v"):
return tag[1:]
if tag:
return tag
return project_version
def _now_timestamp() -> str:
epoch = os.environ.get("SOURCE_DATE_EPOCH")
if epoch:
try:
dt = _dt.datetime.fromtimestamp(int(epoch), _dt.timezone.utc)
except (OSError, OverflowError, ValueError):
dt = _dt.datetime.now(_dt.timezone.utc)
else:
dt = _dt.datetime.now(_dt.timezone.utc)
return dt.strftime("%Y-%m-%dT%H:%M:%SZ")
def _extract_define(text: str, name: str) -> str:
m = re.search(rf'#define\s+{re.escape(name)}\s+"([^"]*)"', text)
return m.group(1) if m else ""
def main() -> int:
if len(sys.argv) != 7:
print(
"usage: generate_version_header.py <template> <output> <repo_root> "
"<project_version> <override_version> <git_exe>",
file=sys.stderr,
)
return 2
template_path = pathlib.Path(sys.argv[1])
output_path = pathlib.Path(sys.argv[2])
repo_root = sys.argv[3]
project_version = sys.argv[4]
override_version = sys.argv[5]
git_exe = sys.argv[6]
template = template_path.read_text()
effective_version = _compute_effective_version(
repo_root, git_exe, project_version, override_version
)
git_describe = _run_git(
repo_root, git_exe, ["describe", "--tags", "--always", "--dirty"]
)
git_commit = _run_git(repo_root, git_exe, ["rev-parse", "--short=8", "HEAD"])
build_timestamp = _now_timestamp()
if output_path.exists():
existing = output_path.read_text()
if (
_extract_define(existing, "YAMS_EFFECTIVE_VERSION") == effective_version
and _extract_define(existing, "YAMS_PROJECT_VERSION") == project_version
and _extract_define(existing, "YAMS_OVERRIDE_VERSION") == override_version
and _extract_define(existing, "YAMS_GIT_DESCRIBE") == git_describe
and _extract_define(existing, "YAMS_GIT_COMMIT") == git_commit
):
preserved = _extract_define(existing, "YAMS_BUILD_TIMESTAMP")
if preserved:
build_timestamp = preserved
rendered = (
template.replace("@YAMS_EFFECTIVE_VERSION@", effective_version)
.replace("@YAMS_PROJECT_VERSION@", project_version)
.replace("@YAMS_OVERRIDE_VERSION@", override_version)
.replace("@YAMS_BUILD_TIMESTAMP@", build_timestamp)
.replace("@YAMS_GIT_DESCRIBE@", git_describe)
.replace("@YAMS_GIT_COMMIT@", git_commit)
)
output_path.parent.mkdir(parents=True, exist_ok=True)
if output_path.exists() and output_path.read_text() == rendered:
return 0
output_path.write_text(rendered)
return 0
if __name__ == "__main__":
raise SystemExit(main())