perf: compile-check reads the project's directories once per run

Merged
HA
2d ago
+831-106

Summary

  • uloop compile-check reads the project's directories once per run instead of three times, which shortens every run on a large project, including the runs where nothing changed.
  • Which assemblies it selects, which sources it compiles, and which errors stop it do not change.

User Impact

  • Before: on a project of about 28,000 sources and 680 assembly definitions, planning took about 1.6 s of a 1.7 s no-change run, almost all of it file-system calls. The assembly definition index, the assembly reference index, and every assembly's source re-glob each walked the same directories again, and every recorded source was checked with its own stat.
  • After: one walk lists Assets, Packages, the package cache, and local packages at the start of the run, and the indexes and source re-globs read that listing. Measurements on a large project are pending.

Changes

  • A new listing of the index roots (project_tree.go), taken by one filepath.WalkDir per root in root order. It keeps the rules the separate walks had: names Unity ignores are left out, symbolic links are listed but not followed, and the walk stops at the first root that fails, keeping what that root listed before the error. A failed root's directories are not used for lookups.
  • The .asmdef index reads each root's .asmdef files in walk order, then reports that root's walk error, so a duplicate assembly name still resolves to the first file in root and walk order (not path order), and errors come in the same order as before. The .asmref index is still sorted by path.
  • The source re-glob (in the source rebuild and in the .asmref check), the check for a nested .asmdef, and the check that a recorded source still exists read the listing, which the owner index carries.
  • The listing only answers yes. A name it lacks, a path outside it, or an entry that is a symbolic link is checked on the disk as before, because a file system that ignores case or Unicode normalization finds a differently spelled name, and a dangling link is not a file. A directory outside the listing is globbed from the disk.

Verification

  • cli/dispatcher: golangci-lint fmt --diff, go vet ./..., golangci-lint run (0 issues), the complexity config (0 issues), and go test ./... pass. Coverage with the CI exclusions: 94.24% (baseline 94.2).
  • cli/release-automation: go test ./... passes, including the architecture test on file length. The file-length check passes.
  • Existing tests keep their expectations; only their calls changed to pass the listing, and the source rebuild tests now go through it.
  • New tests pin: duplicate names across siblings, a directory beside a same-named .asmdef, and across roots; error order between a broken .asmdef and an unreadable directory in either root order and within one root; package-cache and .asmdef-less assemblies; sources behind a linked directory, a dangling link, a case-only rename (with and without an .asmdef), another Unicode normalization (macOS), and sources outside the roots; ignored files; that the disk glob and the listing glob agree, including on a directory named like an .asmdef; and that recording an entry whose parent was never listed does not panic.
  • Mutation checks, each restored afterwards: answering a missing name from the listing fails the dangling-link, case-rename, and normalization tests; sorting .asmdef paths before indexing and reversing the root order each fail the duplicate test; reporting walk errors before reads and walking on past a failed root, and returning a root's walk error before its reads each fail an error-order test; counting a directory named like an .asmdef as one fails the glob agreement test.