Tooling Recipes
Things that are hard to work out from scratch and needed more than once. Paths are macOS and assume
the Unity version this project pins (6000.5.4f1).
Decompile the engine
The most reliable way to settle a question about UI Toolkit, URP or any other engine assembly is to read it. Decompiling the whole assembly once and grepping it beats fishing for type names:
dotnet tool install -g ilspycmd # once; lands in ~/.dotnet/tools, not on PATH
cd "/Applications/Unity/Hub/Editor/6000.5.4f1/Unity.app/Contents/Resources/Scripting/Managed/UnityEngine"
~/.dotnet/tools/ilspycmd -o /tmp/uitk -p UnityEngine.UIElementsModule.dll # whole assembly, ~1070 files
~/.dotnet/tools/ilspycmd -t UnityEngine.UIElements.MaterialDefinition UnityEngine.UIElementsModule.dll
Type and member names found this way are authoritative; line numbers are not — they shift with the decompiler version, so cite the type and member, never a line.
The shader libraries are plain text and need no decompiling:
-
UI Toolkit’s own shader library:
/Applications/Unity/Hub/Editor/6000.5.4f1/Unity.app/Contents/Resources/CGIncludes/Internal/UnityUIE.cginc -
Shader Graph’s UITK target, useful as a worked example of the vertex contract:
…/BuiltInPackages/com.unity.shadergraph/Editor/Generation/Targets/UITK/
Compile C# while Unity holds the project lock
Bee leaves a full response file per assembly — every define, every reference, every source file — from
the last successful build, so Roslyn can be run on it directly without waiting for the editor. Nothing
is written into the project: point -out at a scratch path.
cd <project>
csc=/Applications/Unity/Hub/Editor/6000.5.4f1/Unity.app/Contents/Resources/Scripting/DotNetSdk/sdk/8.0.318/Roslyn/bincore/csc.dll
# ...EDbg.dag = editor defines (UNITY_EDITOR), ...P.dag = player defines. Check both.
sed 's#-out:.*#-out:"/tmp/csc/Promethist.LiquidGlass.dll"#; s#-refout:.*#-refout:"/tmp/csc/Promethist.LiquidGlass.ref.dll"#' \
Library/Bee/artifacts/2000b0aEDbg.dag/Promethist.LiquidGlass.rsp > /tmp/lg.rsp
dotnet "$csc" @/tmp/lg.rsp # silence means it compiled
Two ways this bites:
-
The source list is from the last build. A file added since will not be in it and has to be appended (
"$PWD/<path>", one per line); a deleted file has to begrep -v’d out. Diff the `.rspagainstfind <dir> -name '*.cs'rather than assuming. -
Give each define set its own output directory. The player build of an assembly has no
UNITY_EDITOR, so it contains none of the assembly’s editor-onlyinternalmembers. Building both configs to one path leaves the player output in place, and the matching*.Editorassembly then fails against it — with exactly theCS1061wall described below, from a compile that was fine. -
Keep the assembly’s own filename and redirect only the directory. Roslyn takes the assembly identity from the output filename, so
-out:/tmp/out.dllbuilds an assembly calledout, and[assembly: InternalsVisibleTo("Promethist.Something.Editor")]then does not match — every editor-onlyinternalmember reads as missing, surfacing as a wall of plausible-lookingCS1061errors that look exactly like a real break in the code just changed. For the same reason, point an editor assembly’s-r:at the runtime assembly just built rather than at the stale one inLibrary/Bee/artifacts.
This does not cover shaders, which only the editor compiles.