fix: compile-check no longer recompiles dependents when a rebuilt assembly kept Unity's reference assembly

Merged
HA
20h ago
+410-42

Summary

  • When compile-check rebuilds an assembly and its reference assembly comes out byte-identical to Unity's, the assemblies that reference it now keep reading Unity's reference assembly. They can then reuse their recorded result or be checked on their changed files, instead of compiling in full.

User Impact

In a project with 561 assemblies, a dependent the run held for its own edits compiled in full (9 to 10 seconds) on two runs: the first run after starting to edit an assembly it references, and the run after putting that assembly back the way Unity built it. The rebuilt reference assembly was identical to Unity's, but the dependent was pointed at the check's own copy. That moved its input key, and the changed-files check fell back with inputs other than source contents changed: references.

Those runs now replay the dependent's recorded result, or check its changed files, the same way the runs that did not rebuild the assembly do. The same rule applies under --all, so the ordinary run after --all reuses what it compiled.

Changes

  • A refactor commit routes the surface comparison through the compiler, so the tests can count which units a run compares.
  • When the scheduler starts a unit, it gives the unit a copy of the names of its references that kept Unity's surface. All references are finished by then, so the copy is final. The csc response file, the changed-files check's response file, the input key and the non-source key all read that copy from the unit. So they cannot point at different reference assemblies.
  • rewriteReference keeps a reference on Unity's path when the copy names it. Such a reference is keyed by timestamp, like every reference the run did not rebuild. A reference that was rebuilt into a different surface is still keyed by content.
  • A unit's surface is compared only when the unit succeeded and something in the plan references it. Nothing reads the answer for any other unit.
  • docs/compile-check.md step 6 and its --all paragraph describe the new rule.

Verification

  • New scheduler-level tests:

    • Reuse of a dependent held for its own reason, after a compiled reference and after a replayed one.
    • Per-reference rewriting when one reference kept its surface and another changed (checked in the written response file).
    • --all followed by an ordinary run.
    • Changed-files check of such a dependent, including that the .fastcheck.rsp names Unity's path.
    • No comparison for a unit without dependents in the plan or a failed unit.
  • Before the fix, exactly these 6 tests failed and every existing test passed. The tests that pin today's rewriting of a changed surface are unchanged and still pass.

  • Mutations, each restored afterwards:

    MutationFails
    Ignore the copy and always rewritethe 5 reuse / rewrite / --all / changed-files tests
    The .fastcheck.rsp ignores the copythe changed-files test only
    The keys ignore the copythe 2 reuse tests, --all, changed-files
    Treat every reference as keptthe per-reference test, plus an existing blocked-dependent test
    Compare units without dependentsthe no-comparison test
    Compare failed unitsthe no-comparison test
  • cli/dispatcher:

    • golangci-lint fmt --diff is empty; go vet ./... passes; golangci-lint run and the complexity config both report 0 issues.
    • go test ./... passes; go test -race on the compile-check and dispatcher packages passes.
    • Coverage with the CI exclusions: 94.26% (baseline 94.2).
  • The file-length check and scripts/sync-tool-docs.sh --check pass.

  • Repository CI does not run on pull requests into this branch; the checks above were run locally.