landsandboat/tools/codegen/cli.py

102 lines
3.8 KiB
Python

import argparse
import json
from pathlib import Path
from .common import ENUMS_DIR, ROOT, TEMPLATES_DIR, write_if_changed
from .data_codegen import SchemaWalker
from .enum_codegen import emit_pure_enum, emit_table_enum
from .validate import validate_data_yamls
def inputs_newer_than(stamp: Path) -> bool:
if not stamp.exists():
return True
stamp_mtime = stamp.stat().st_mtime
schemas_dir = ROOT / "data" / "schemas"
inputs = [
*Path(__file__).parent.glob("*.py"),
*TEMPLATES_DIR.glob("*.j2"),
*ENUMS_DIR.glob("*.yaml"),
*schemas_dir.glob("*.schema.json"),
*(schemas_dir / "enums").glob("*.codegen.json"),
*(ROOT / "data").glob("*.yaml"),
]
return any(p.stat().st_mtime > stamp_mtime for p in inputs)
def main():
"""No-op if stamp is newer than input files and `--validate` wasn't passed."""
parser = argparse.ArgumentParser()
parser.add_argument("build_dir")
parser.add_argument(
"--validate",
action="store_true",
help="Also run schema validation.",
)
args = parser.parse_args()
build_root = Path(args.build_dir)
stamp = build_root / "generated" / ".codegen.stamp"
if not inputs_newer_than(stamp) and not args.validate:
return
out_dir = build_root / "generated" / "data" / "enums"
data_out_dir = build_root / "generated" / "data"
lua_out_dir = ROOT / "scripts" / "enum"
keyset_dir = ROOT / "data" / "schemas" / "enums"
for d in (out_dir, data_out_dir, lua_out_dir, keyset_dir):
d.mkdir(parents=True, exist_ok=True)
# Enums: pure (data/enums/*.yaml) + table-derived (meta.enum: inside a data file).
enums: list[dict] = []
for p in sorted(ENUMS_DIR.glob("*.yaml")):
enums.append(emit_pure_enum(p))
for p in sorted((ROOT / "data").rglob("*.yaml")):
if p.is_relative_to(ENUMS_DIR):
continue
print(f"scanning {p.relative_to(ROOT).as_posix()} for embedded enum ...", flush=True)
result = emit_table_enum(p)
if result is not None:
enums.append(result)
for e in enums:
write_if_changed(out_dir / (e["name"] + ".h"), e["header"])
if e["lua"]:
write_if_changed(lua_out_dir / (e["lua"]["name"] + ".codegen.lua"), e["lua"]["content"])
# Keyset schema lets other schemas $ref the list of valid values for IDE completion + CI checks.
keyset = {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": f"enums/{e['name']}.codegen.json",
"description": f"GENERATED from {e['source_name']}. Do not edit.",
"type": "string",
"enum": list(e["values"].keys()),
}
write_if_changed(keyset_dir / (e["name"] + ".codegen.json"), json.dumps(keyset, indent=2) + "\n")
schemas_dir = ROOT / "data" / "schemas"
walker = SchemaWalker()
data_names: list[str] = []
for schema_path in sorted(schemas_dir.glob("*.schema.json")):
print(f"rendering {schema_path.relative_to(ROOT).as_posix()} ...", flush=True)
result = walker.emit(schema_path)
if result is None:
continue
write_if_changed(data_out_dir / (result["name"] + ".h"), result["pod"])
write_if_changed(data_out_dir / (result["name"] + "_populator.h"), result["populator"])
data_names.append(result["name"])
manifest_lines = ["// GENERATED by tools/codegen. Do not edit.", "#pragma once", ""]
for name in sorted(data_names):
manifest_lines.append(f'#include "data/{name}.h"')
manifest_lines.append(f'#include "data/{name}_populator.h"')
write_if_changed(data_out_dir / "all.h", "\n".join(manifest_lines) + "\n")
stamp.parent.mkdir(parents=True, exist_ok=True)
stamp.touch()
if args.validate:
validate_data_yamls(schemas_dir, keyset_dir, build_root)
if __name__ == "__main__":
main()