()
| 4142 | tmp_path = w.path + ".codegen.tmp" |
| 4143 | try: |
| 4144 | with open(tmp_path, "wb") as f: |
| 4145 | f.write(new_bytes) |
| 4146 | os.replace(tmp_path, w.path) |
| 4147 | except BaseException: |
| 4148 | if os.path.exists(tmp_path): |
| 4149 | try: |
| 4150 | os.remove(tmp_path) |
| 4151 | except OSError: |
| 4152 | pass |
| 4153 | raise |
| 4154 | print(f"WROTE {rel}") |
| 4155 | if check_only and changed: |
| 4156 | return 1 |
| 4157 | print(f"{'(dry-run) ' if dry_run else ''}files changed: {changed}") |
| 4158 | return 0 |
| 4159 | |
| 4160 | |
| 4161 | # Hand-written companion files that legitimately live in MuJoCo/Generated/ |
| 4162 | # (their banner explicitly says "NOT codegen-managed"). Listed here so the |
| 4163 | # orphan walk doesn't flag them — anything else in Generated/ that the |
| 4164 | # codegen doesn't plan to write IS a real orphan. |
| 4165 | _HAND_WRITTEN_IN_GENERATED = { |
| 4166 | "MjOptionGeneratedExtras.cpp", |
| 4167 | } |
| 4168 | |
| 4169 | |
| 4170 | def find_orphan_generated_files( |
| 4171 | writes: List[FileWrite], public_root: str, private_root: str, |
| 4172 | ) -> List[str]: |
| 4173 | """Walk ``MuJoCo/Generated/`` under public + private roots and return |
| 4174 | any file the codegen no longer plans to emit. A category removed from |
| 4175 | rules leaves a stale ``Generated/MjFoo.h`` that UBT will pick up — the |
| 4176 | user sees a confusing UHT error far from the JSON edit.""" |
| 4177 | expected = {os.path.normcase(os.path.abspath(w.path)) for w in writes} |
| 4178 | orphans: List[str] = [] |
| 4179 | for root in (public_root, private_root): |
| 4180 | gen_dir = os.path.join(root, "MuJoCo", "Generated") |
| 4181 | if not os.path.isdir(gen_dir): |
| 4182 | continue |
| 4183 | for dirpath, _dirs, files in os.walk(gen_dir): |
| 4184 | for fname in files: |
| 4185 | if not (fname.endswith(".h") or fname.endswith(".cpp")): |
| 4186 | continue |
| 4187 | if fname in _HAND_WRITTEN_IN_GENERATED: |
| 4188 | continue |
| 4189 | p = os.path.normcase(os.path.abspath(os.path.join(dirpath, fname))) |
| 4190 | if p not in expected: |
| 4191 | orphans.append(os.path.join(dirpath, fname)) |
| 4192 | return sorted(orphans) |
| 4193 | |
| 4194 | |
| 4195 | def main() -> int: |
| 4196 | parser = argparse.ArgumentParser(description="URLab UE component codegen.") |
| 4197 | parser.add_argument("--schema", default=DEFAULT_SCHEMA) |
| 4198 | parser.add_argument("--mjxmacro", default=DEFAULT_MJXMACRO) |
| 4199 | parser.add_argument("--introspect", default=DEFAULT_INTROSPECT, |
| 4200 | help="Clang-AST scrape carrying mjsX struct fields, " |
| 4201 | "mjt* enums, mjs_setTo* signatures, and EMj* " |
no test coverage detected