feat: compile-check picks up new scripts outside every assembly definition
Merged
HA
4d ago+528+528+012345678901234567890123456789-21-21-01234567890123456789
Summary
uloop compile-checknow compiles a new script created outside every assembly definition, in the predefined assembly Unity would put it in (Assembly-CSharp,Assembly-CSharp-Editor,Assembly-CSharp-firstpassorAssembly-CSharp-Editor-firstpass).
User Impact
- Before: the predefined assemblies kept the source list of the last Unity build, so a brand-new
.csfile outside every.asmdefwas not compiled until Unity imported it, and its errors were missed. - After: the new script is compiled with the right assembly. If the last build did not produce that assembly - the first editor script of a project that had none - the run stops with
COMPILE_CHECK_UNITY_BUILD_REQUIRED(<script> belongs to <assembly>, which the last Unity build did not produce) anduloop compileclears it.
Changes
- New
predefined_sources.go:predefinedAssemblyForfollows Unity'sEditorBuildRules.GetPredefinedTargetAssembly: a/editor/folder anywhere in the path picks an editor assembly, the first-pass folders at the top ofAssetspick a first-pass one, and an editor folder under a first-pass folder picksAssembly-CSharp-Editor-firstpass.globPredefinedSourceswalksAssetsonce per run with the same glob the assembly definitions use, so it stops where an.asmdefor.asmrefstarts another assembly and skips names Unity ignores. An.asmdefinAssetsitself yields nothing.- Scripts under
Assets/StreamingAssets,Assets/WebPlayerTemplates,Assets/WebGLTemplatesandAssets/Plugins/Androidare left out. Unity imports them without compiling them; that list lives in its native script importer, not in the C# build rules. - A script whose assembly has no response file is refused.
- The owner index carries the walk's result; a nil list (Assets never walked) makes
RebuildSourcesrefuse a predefined assembly instead of silently rebuilding it without its new scripts, while an empty map means the walk found none. RebuildSourcesappends the new scripts of a predefined assembly after its recorded ones, in ordinal order, the same order as for assemblies with an.asmdef.- The owner index now keeps an
.asmrefnaming an.asmdefthe build left out. Unity attaches the folder to that assembly and then drops it, so the folder's scripts belong to no assembly; before, they were globbed into the assembly around it. An.asmrefthat resolves to no.asmdefstill falls through to the enclosing assembly. Packages/is not walked: where a script in a package folder without an.asmdefgoes differs between Unity versions. ADR 0018 already lists that limitation.docs/compile-check.md(How it works, Limitations, a new Troubleshooting entry) and the skill are updated.
Verification
cli/dispatcher:gofmt -l .empty,go vet ./...,golangci-lint run ./...0 issues,go test ./... -count=1pass.- New tests cover the routing (editor segment, first-pass folders), the glob (owned scripts, hidden folders,
Assetsroot.asmdef, package folders,.asmrefto an unbuilt assembly / to no assembly / by an unknown name, and the.asmdefglob stopping at the same folder), the rebuild for each predefined assembly, a recorded script kept once, and the refusal throughBuildCompilePlan. - Mutations (each reverted):
- (a)
predefinedAssemblyForalways returnsAssembly-CSharp→ the routing and per-assembly rebuild tests fail. - (b) the glob ignores assembly boundaries →
TestGlobPredefinedSourcesLeavesOutAScriptAnAssemblyDefinitionOwnsfails. - (c) no refusal for an unbuilt assembly →
TestSelectChangedAssembliesRefusesAScriptForAPredefinedAssemblyTheLastBuildLacksfails. - (d) no early return for an
.asmdefinAssets→TestGlobPredefinedSourcesReturnsNothingWhenAnAssemblyDefinitionSitsInAssetsfails. - (e) the owner index skips
.asmreffiles of unbuilt assemblies again → both "stops at a reference to an unbuilt assembly" tests fail. - (f) name references always resolve →
TestGlobPredefinedSourcesDescendsIntoAReferenceByAnUnknownNamefails. - (g) drop the uncompiled-folder filter →
TestGlobPredefinedSourcesLeavesOutTheFoldersUnityNeverCompilesfails. - (h) drop the production assignment of the walk's result to the owner index →
TestBuildCompilePlanPicksUpANewScriptOutsideEveryAssemblyDefinitionfails. - (i) refuse a built predefined assembly too (
built && false) → the same plan-level test fails. - (j) drop the nil check in
RebuildSources→TestRebuildSourcesRefusesAPredefinedAssemblyWhenAssetsWasNeverWalkedfails.
- (a)
- Live run on this repository's Unity project with the Editor closed, using a
distbinary built from this branch:- Baseline: exit 0, nothing compiled (no predefined assembly gained a script it should not).
Assets/CompileCheckProbe.cswithint x = "s";: exit 1,CS0029in that file,CompiledAssemblies=[Assembly-CSharp]. Removed → exit 0.- The same in an
Editorfolder owned by no.asmdef: exit 1,CompiledAssemblies=[Assembly-CSharp-Editor]. Removed. git status --porcelainmatched the state before the run.Assets/StreamingAssets/Probe.cswith the same error: Unity's ownuloop compileexited 0 with no error and no response file listed it (the build-trace file moved, so Unity did build). Before the filter, compile-check put it intoAssembly-CSharpand failed withCS0029. After it, compile-check exits 0. Removed.
- The refusal for an unbuilt first-pass assembly is covered by unit tests only. The live check would have needed a new
Assets/Pluginsfolder.