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()