()
| 329 | |
| 330 | |
| 331 | def main() -> int: |
| 332 | src = sys.argv[1] if len(sys.argv) > 1 else DEFAULT_MJXMACRO |
| 333 | dst = sys.argv[2] if len(sys.argv) > 2 else DEFAULT_OUTPUT |
| 334 | if not os.path.isfile(src): |
| 335 | print(f"error: mjxmacro.h not found at {src}", file=sys.stderr) |
| 336 | return 1 |
| 337 | snapshot = parse_mjxmacro(src) |
| 338 | # Track which source each struct-field block came from so a snapshot |
| 339 | # reader can tell mjmodel-side (mjxmacro.h) blocks apart from mjspec-side |
| 340 | # (mjspecmacro.h) blocks without re-parsing the headers. |
| 341 | block_sources: Dict[str, str] = {} |
| 342 | for block_name in snapshot["struct_fields"]: |
| 343 | block_sources[block_name] = snapshot["_meta"]["source"] |
| 344 | # Also fold in mjspecmacro.h (MJSCOMPILER_FIELDS + MJSPEC_FIELDS) from the |
| 345 | # installed MuJoCo headers — both live in the same ``struct_fields`` dict so |
| 346 | # synthetic_categories rules can reference either by block name. |
| 347 | if os.path.isfile(DEFAULT_MJSPECMACRO): |
| 348 | spec_snapshot = parse_mjxmacro(DEFAULT_MJSPECMACRO) |
| 349 | spec_rel = os.path.relpath(DEFAULT_MJSPECMACRO, PLUGIN_ROOT) |
| 350 | for block_name, entries in spec_snapshot["struct_fields"].items(): |
| 351 | snapshot["struct_fields"][block_name] = entries |
| 352 | block_sources[block_name] = spec_rel |
| 353 | else: |
| 354 | # Loud rather than silent — MJSCOMPILER_FIELDS / MJSPEC_FIELDS blocks |
| 355 | # WILL be missing if the installed headers predate mjspecmacro.h, and |
| 356 | # that surfaces only as a downstream KeyError much later in |
| 357 | # generate_ue_components.py. |
| 358 | print( |
| 359 | f"warning: mjspecmacro.h not found at {DEFAULT_MJSPECMACRO}; " |
| 360 | f"MJSCOMPILER_FIELDS + MJSPEC_FIELDS will be absent from the snapshot. " |
| 361 | f"Rebuild the MuJoCo install (needs a version that ships mjspecmacro.h).", |
| 362 | file=sys.stderr, |
| 363 | ) |
| 364 | snapshot["_meta"]["struct_field_sources"] = block_sources |
| 365 | # Stats for sanity. |
| 366 | total_model = sum(len(v) for v in snapshot["mjmodel_pointers"].values()) |
| 367 | total_data = sum(len(v) for v in snapshot["mjdata_pointers"].values()) |
| 368 | total_structs = sum(len(v) for v in snapshot["struct_fields"].values()) |
| 369 | print( |
| 370 | f"parsed mjxmacro.h: {len(snapshot['mjmodel_pointers'])} model blocks " |
| 371 | f"({total_model} entries), " |
| 372 | f"{len(snapshot['mjdata_pointers'])} data blocks ({total_data} entries), " |
| 373 | f"{len(snapshot['struct_fields'])} struct blocks ({total_structs} entries)" |
| 374 | ) |
| 375 | with open(dst, "w", encoding="utf-8") as f: |
| 376 | json.dump(snapshot, f, indent=2) |
| 377 | print(f"wrote {dst}") |
| 378 | return 0 |
| 379 | |
| 380 | |
| 381 | if __name__ == "__main__": |
no test coverage detected