com.elestrago.unity.generated-code-dump 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/CHANGELOG.md.meta +7 -0
  3. package/Documentation~/api.md +583 -0
  4. package/Documentation~/examples/dump-in-batch-mode.md +43 -0
  5. package/Documentation~/examples/dump-now.md +29 -0
  6. package/Documentation~/examples/read-the-dump.md +63 -0
  7. package/Documentation~/examples/turn-on-the-dump.md +33 -0
  8. package/Documentation~/examples.md +24 -0
  9. package/Documentation~/manual.md +618 -0
  10. package/Editor/AssemblyInfo.cs +3 -0
  11. package/Editor/AssemblyInfo.cs.meta +2 -0
  12. package/Editor/GeneratedCodeDump.Editor.asmdef +16 -0
  13. package/Editor/GeneratedCodeDump.Editor.asmdef.meta +7 -0
  14. package/Editor/GeneratedCodeDumpArgs.cs +113 -0
  15. package/Editor/GeneratedCodeDumpArgs.cs.meta +2 -0
  16. package/Editor/GeneratedCodeDumpFiles.cs +120 -0
  17. package/Editor/GeneratedCodeDumpFiles.cs.meta +2 -0
  18. package/Editor/GeneratedCodeDumpFinisher.cs +103 -0
  19. package/Editor/GeneratedCodeDumpFinisher.cs.meta +2 -0
  20. package/Editor/GeneratedCodeDumpIdentity.cs +40 -0
  21. package/Editor/GeneratedCodeDumpIdentity.cs.meta +2 -0
  22. package/Editor/GeneratedCodeDumpJobs.cs +242 -0
  23. package/Editor/GeneratedCodeDumpJobs.cs.meta +2 -0
  24. package/Editor/GeneratedCodeDumpMigration.cs +112 -0
  25. package/Editor/GeneratedCodeDumpMigration.cs.meta +2 -0
  26. package/Editor/GeneratedCodeDumpNames.cs +104 -0
  27. package/Editor/GeneratedCodeDumpNames.cs.meta +2 -0
  28. package/Editor/GeneratedCodeDumpOutput.cs +449 -0
  29. package/Editor/GeneratedCodeDumpOutput.cs.meta +2 -0
  30. package/Editor/GeneratedCodeDumpPathBudget.cs +67 -0
  31. package/Editor/GeneratedCodeDumpPathBudget.cs.meta +2 -0
  32. package/Editor/GeneratedCodeDumpPaths.cs +145 -0
  33. package/Editor/GeneratedCodeDumpPaths.cs.meta +2 -0
  34. package/Editor/GeneratedCodeDumpProcess.cs +119 -0
  35. package/Editor/GeneratedCodeDumpProcess.cs.meta +2 -0
  36. package/Editor/GeneratedCodeDumpRsp.cs +107 -0
  37. package/Editor/GeneratedCodeDumpRsp.cs.meta +2 -0
  38. package/Editor/GeneratedCodeDumpSession.cs +65 -0
  39. package/Editor/GeneratedCodeDumpSession.cs.meta +2 -0
  40. package/Editor/GeneratedCodeDumpSettings.cs +111 -0
  41. package/Editor/GeneratedCodeDumpSettings.cs.meta +2 -0
  42. package/Editor/GeneratedCodeDumpSettingsFile.cs +231 -0
  43. package/Editor/GeneratedCodeDumpSettingsFile.cs.meta +2 -0
  44. package/Editor/GeneratedCodeDumpSettingsProvider.cs +93 -0
  45. package/Editor/GeneratedCodeDumpSettingsProvider.cs.meta +2 -0
  46. package/Editor/GeneratedCodeDumpSettingsWatcher.cs +199 -0
  47. package/Editor/GeneratedCodeDumpSettingsWatcher.cs.meta +2 -0
  48. package/Editor/GeneratedCodeDumper.cs +689 -0
  49. package/Editor/GeneratedCodeDumper.cs.meta +2 -0
  50. package/Editor/GeneratedCodeMenuItems.cs +46 -0
  51. package/Editor/GeneratedCodeMenuItems.cs.meta +2 -0
  52. package/Editor.meta +8 -0
  53. package/LICENSE.md +21 -0
  54. package/LICENSE.md.meta +7 -0
  55. package/README.md +164 -0
  56. package/README.md.meta +7 -0
  57. package/package.json +31 -0
  58. package/package.json.meta +7 -0
@@ -0,0 +1,618 @@
1
+ # Generated Code Dump — Manual
2
+
3
+ This is the full guide to the package. The [README](../README.md) is the quick start, and the [examples](examples.md) walk through single tasks. The last section, [Internals](#internals), is for contributors.
4
+
5
+ ## What it does
6
+
7
+ After Unity compiles `Assembly-CSharp` in this package's example project, this file appears on disk:
8
+
9
+ ```text
10
+ Library/GeneratedCodeDump/Assembly-CSharp/GeneratedCodeDump.TestGenerators.Incremental/Example.ExampleBehaviour.Dumpable.g.cs
11
+ ```
12
+
13
+ It is the code a source generator added to that compile. A *source generator* is a plug-in for the C# compiler that writes extra source code while an assembly compiles. Unity runs source generators during its compile, but it never saves what they write.
14
+
15
+ Without the files on disk, a tool or an AI agent reading the project sees only the attributes and `partial` declarations. It does not see the members the generators add, so it has to guess them. This package writes the output of every Roslyn source generator in the project to `Library/GeneratedCodeDump/` after each compile, so tools and agents can read the real generated code.
16
+
17
+ To get the files, the package *replays* each assembly's compile. A *replay* runs Unity's own compiler again for one assembly, with the same inputs Unity used, but sends its outputs to the dump's own folders instead of Unity's. The package keeps the generated files and adds header lines that name the dump time, the assembly, the generator and the file's name.
18
+
19
+ - The package is Editor-only. Nothing from it ships in a player build.
20
+ - The dump goes under `Library/` and the switch under `UserSettings/`. Nothing is added to `Assets/` or to the compile.
21
+ - The package only reads Unity's build files. Unity's own compile outputs under `Library/Bee/` are never touched.
22
+ - You control it with a per-user settings file, the `Tools/Generated Code Dump/` menu and the `Preferences/Generated Code Dump` page. There is no C# API to call.
23
+
24
+ ## Turning it on and off
25
+
26
+ From the Unity project root, these two lines turn the dump on:
27
+
28
+ ```bash
29
+ mkdir -p UserSettings
30
+ printf '{"enabled": true}\n' > UserSettings/GeneratedCodeDumpSettings.json
31
+ ```
32
+
33
+ The dump is off by default. The switch belongs to one user in one project. There are three ways to flip it, and all three write the same file:
34
+
35
+ - **Preferences:** the `Preferences/Generated Code Dump` page, toggle `Dump generated code`.
36
+ - **Menu:** `Tools/Generated Code Dump/Enabled`, which shows a checkmark while the dump is on.
37
+ - **File:** write `UserSettings/GeneratedCodeDumpSettings.json` yourself, as above.
38
+
39
+ A step-by-step version is in [Turn on the dump](examples/turn-on-the-dump.md).
40
+
41
+ ### The settings file
42
+
43
+ The file holds one flat JSON object. `enabled` must be exactly `true` or `false`, and a missing `enabled` means off. Other keys may sit next to it if their values are plain: a string, a number, `true`, `false` or `null`.
44
+
45
+ A missing file, or one that is not valid, means off. An invalid file also logs a warning:
46
+
47
+ ```text
48
+ [Generated Code Dump] <full path of the file> is not valid: expected {"enabled": true} or false. The dump is off.
49
+ ```
50
+
51
+ The warning is not repeated while the file stays the same. If you edit the file and it is still invalid, it warns again.
52
+
53
+ ### When a change applies
54
+
55
+ - **Open Editor:** it watches the file, even while it is in the background, and applies an edit within about a second. A change from the menu or the Preferences page applies at once.
56
+ - **No `UserSettings/` folder yet:** the Editor watches the project root for that folder to appear. Watching creates nothing.
57
+ - **Closed Editor:** it reads the file at its next start.
58
+ - **Batch mode:** an Editor started with `-batchmode` on the same checkout reads the file when it starts.
59
+
60
+ ### One switch per developer
61
+
62
+ `UserSettings/` is Unity's per-user folder. The switch survives deleting `Library/`, and each developer turns the dump on for themselves.
63
+
64
+ Unity's standard `.gitignore` ignores `/[Uu]ser[Ss]ettings/`. If yours does not, add that line, or the switch gets committed for everyone.
65
+
66
+ ### Turning it off
67
+
68
+ Untick the toggle or the menu item, write `{"enabled": false}` to the file, or delete the file. Turning the dump off drops the replays that have not started yet. A compiler that is already running still finishes, and its files still land.
69
+
70
+ The existing `Library/GeneratedCodeDump/` folder is kept, but later compiles no longer refresh it. After that, nothing under `Library/GeneratedCodeDump/` is created while the dump is off, except by Dump Now or Open Folder (see [The other menu items](#the-other-menu-items)).
71
+
72
+ One more file can be written while the dump is off without you asking: the new settings file, when an upgrade from 1.0.0 moves a setting that was off. See [Upgrading from 1.0.0](#upgrading-from-100).
73
+
74
+ ### The other menu items
75
+
76
+ | Menu item | What it does |
77
+ | --- | --- |
78
+ | `Tools/Generated Code Dump/Dump Now` | Starts a full dump, which refreshes the whole dump (see [When a dump runs](#when-a-dump-runs)), whatever the setting. It also works while the dump is off, as a one-off refresh. |
79
+ | `Tools/Generated Code Dump/Open Folder` | Creates `Library/GeneratedCodeDump/` if needed and opens it in the system's file browser. |
80
+ | `Tools/Generated Code Dump/Preferences…` | Opens the `Preferences/Generated Code Dump` page. |
81
+
82
+ Dump Now and Open Folder are also buttons on the Preferences page. Neither of them turns the dump on. The settings file is the only switch; there is no command-line flag. See also [Dump Now and Open Folder](examples/dump-now.md).
83
+
84
+ ## When a dump runs
85
+
86
+ When you turn the dump on, the Console shows a line like this:
87
+
88
+ ```text
89
+ [Generated Code Dump] queued 11 assemblies, at most 6 compiling at once; output in Library/GeneratedCodeDump/
90
+ ```
91
+
92
+ That line starts a *full dump*: a replay of every assembly from Unity's latest compile that loads an analyzer. An *analyzer* is a plug-in DLL the compiler is told to load for an assembly. Source generators ship as analyzers, so an assembly that loads none has no generated files and is never replayed.
93
+
94
+ ### What starts a full dump
95
+
96
+ - You turn the dump on, in any of the three ways.
97
+ - An Editor starts with the dump on, batch mode included.
98
+ - A domain reload or a change of the setting finds `Library/GeneratedCodeDump/` missing while the dump is on. A *domain reload* is Unity reloading its scripts, which it does after every compile. So deleting that folder while the dump is on forces a full dump at the next reload.
99
+ - You choose Dump Now, even while the dump is off.
100
+
101
+ A full dump always starts with the `queued` line above. The number after `queued` counts the assemblies that load an analyzer. The "at most" number is explained in [Performance](#performance).
102
+
103
+ ### Editor start or domain reload?
104
+
105
+ When an Editor first finds the dump on, it writes a small marker file, `Library/GeneratedCodeDump/.work/enabled-session`, that names that Editor process. A domain reload in the same Editor finds its own marker and skips the full dump. A new Editor finds another process's marker, so it runs one.
106
+
107
+ ### After each compile
108
+
109
+ Once the full dump is done, every compile runs a *per-compile dump*. It replays only the assemblies rebuilt by that compile without errors, and of those only the ones that load an analyzer.
110
+
111
+ An assembly that failed to compile is skipped by the per-compile dump until it compiles again. A full dump does not check this: it replays every assembly of the newest build graph that loads an analyzer.
112
+
113
+ ### Jobs
114
+
115
+ Each requested replay is a *job*: one replay of one assembly. The dump writes its jobs to `Library/GeneratedCodeDump/.work/jobs.txt`, marked queued or running, until the output is in place. A dump cut short by a domain reload or an Editor restart therefore picks up where it stopped.
116
+
117
+ Each assembly has at most one job waiting. When a dump is requested for an assembly whose job is still queued, that job covers it, since a job reads Unity's compiler arguments only when it starts. When one is requested while the assembly's compiler is running, the assembly is replayed once more afterwards.
118
+
119
+ ## Reading the output
120
+
121
+ A real dump of this package's own example project looks like this. Its test generators live in the repository's `External/` folder and are not shipped:
122
+
123
+ ```text
124
+ Library/GeneratedCodeDump/
125
+ .work/
126
+ Assembly-CSharp/
127
+ GeneratedCodeDump.TestGenerators.Classic/
128
+ Aux_.g.cs
129
+ ExampleBehaviour.generated.cs
130
+ ExampleBehaviour~31a944ea.generated.cs
131
+ GeneratedCodeDump.TestGenerators.Incremental/
132
+ Example.ExampleBehaviour.Dumpable.g.cs
133
+ Summary.Info.g.cs
134
+ Unity.SourceGenerators/
135
+ AssemblyMonoScriptTypes.generated.cs
136
+ GeneratedCodeDump.Example.Module/
137
+ GeneratedCodeDump.TestGenerators.Classic/
138
+ Aux_.g.cs
139
+ DeepRecord.generated.cs
140
+ DeepRecord~5fee0560.generated.cs
141
+ ModuleService.generated.cs
142
+ GeneratedCodeDump.TestGenerators.Incremental/
143
+ GeneratedCodeDump.Example.Module.Deep.DeepRecord.Dumpable.g.cs
144
+ GeneratedCodeDump.Example.Module.ModuleService.Dumpable.g.cs
145
+ Summary.Info.g.cs
146
+ Unity.SourceGenerators/
147
+ AssemblyMonoScriptTypes.generated.cs
148
+ ...
149
+ ```
150
+
151
+ The `...` stands for the project's other assemblies, such as the test runner's. Each of them holds only `Unity.SourceGenerators/AssemblyMonoScriptTypes.generated.cs`.
152
+
153
+ ### Folder and file names
154
+
155
+ Every file sits at `Library/GeneratedCodeDump/<Assembly>/<GeneratorAssembly>/<hint name>`:
156
+
157
+ - `<Assembly>` is the compiled assembly: `Assembly-CSharp`, or an asmdef name such as `GeneratedCodeDump.Example.Module`.
158
+ - `<GeneratorAssembly>` is the *generator assembly*: the DLL the source generator ships in, named by its assembly name. Unity's own generator ships in `Unity.SourceGenerators`.
159
+ - `<hint name>` is the *hint name*: the file name the source generator gave the file, such as `Example.ExampleBehaviour.Dumpable.g.cs`. It is kept exactly as the generator gave it, as one flat file name, with no namespace folders.
160
+
161
+ Unity's `Unity.SourceGenerators` runs on most assemblies, so most assembly folders hold `AssemblyMonoScriptTypes.generated.cs`. In this package's example project it ran on every assembly.
162
+
163
+ ### Two generators, one hint name
164
+
165
+ `ExampleBehaviour.generated.cs` and `ExampleBehaviour~31a944ea.generated.cs` in the tree above come from two generators in `GeneratedCodeDump.TestGenerators.Classic`. Both gave the hint name `ExampleBehaviour.generated.cs`, and one folder cannot hold two files with the same name.
166
+
167
+ The generator whose full type name sorts first, in plain character order, keeps the name. Here that is `GeneratedCodeDump.TestGenerators.Classic.CollidingGenerator`. Two hint names that differ only in letter case also count as a shared hint name.
168
+
169
+ The other one, `GeneratedCodeDump.TestGenerators.Classic.LegacyGenerator`, gets a *short hash* between the name and its ending. A short hash is a `~` and 8 lowercase hex digits, such as `~31a944ea`, computed with the FNV-1a 32-bit hash over the UTF-8 bytes of a name. The same name always gives the same short hash, on every machine, so the renamed file cannot clash with its twin and keeps its name from one dump to the next.
170
+
171
+ For a shared hint name, the hashed name is `<generator full type name>/<hint name>`: here `GeneratedCodeDump.TestGenerators.Classic.LegacyGenerator/ExampleBehaviour.generated.cs`. The ending that stays after the short hash is `.generated.cs` or `.g.cs`, or else the file's extension.
172
+
173
+ ### Windows reserved names
174
+
175
+ `Aux.g.cs` becomes `Aux_.g.cs`. Windows refuses a file or folder whose name part is `CON`, `PRN`, `AUX`, `NUL`, `COM0` to `COM9` or `LPT0` to `LPT9`, in any letter case and whatever follows the first dot. The dump adds `_` after that name part, for folders and files, on every OS.
176
+
177
+ ### Empty and removed files
178
+
179
+ - An assembly whose generators produce nothing has no folder.
180
+ - Each replay syncs its whole assembly folder. A file its generators no longer produce is deleted.
181
+
182
+ ### The `.work/` folder
183
+
184
+ `.work/` sits next to the assembly folders. It is the dump's own bookkeeping: the job list, the session marker, and each assembly's last replay arguments and error log. It holds no generated code, so skip it when you search, with `grep --exclude-dir=.work`.
185
+
186
+ On Windows `.work/` is Hidden, so Open Folder shows only the assembly folders. Its leading dot keeps it apart from every assembly name. Its contents are described in [Internals](#file-formats).
187
+
188
+ ### Header lines
189
+
190
+ The top of `Assembly-CSharp/GeneratedCodeDump.TestGenerators.Incremental/Example.ExampleBehaviour.Dumpable.g.cs`:
191
+
192
+ ```csharp
193
+ // <auto-generated/>
194
+ // Dumped: 2026-09-12 10:15:03 UTC
195
+ // Assembly: Assembly-CSharp
196
+ // Generator: GeneratedCodeDump.TestGenerators.Incremental / GeneratedCodeDump.TestGenerators.Incremental.DumpableGenerator
197
+ // Hint: Example.ExampleBehaviour.Dumpable.g.cs
198
+ namespace Example
199
+ {
200
+ partial class ExampleBehaviour
201
+ ```
202
+
203
+ Every dumped file gets these four header lines. They always hold full names, even when the file or its folders got a shorter name (see [Long paths](#long-paths)):
204
+
205
+ - `Dumped: yyyy-MM-dd HH:mm:ss UTC`: when the replay's compiler started, in UTC.
206
+ - `Assembly: <Assembly>`: the compiled assembly.
207
+ - `Generator: <GeneratorAssembly> / <generator full type name>`.
208
+ - `Hint: <hint name>`: the hint name exactly as the generator gave it.
209
+
210
+ Where the lines go depends on how the generator starts its file:
211
+
212
+ - After a `// <auto-generated/>` line, as above.
213
+ - At the end of a `// <auto-generated>` … `// </auto-generated>` block, indented as `// Dumped: …`.
214
+ - Otherwise, as the file's first lines.
215
+
216
+ The dump looks for those markers in the first 12 lines. It keeps the file's byte order mark (BOM) and its line endings.
217
+
218
+ ### Line numbers are off by 4
219
+
220
+ The header adds 4 lines above the generator's code. So when a compiler message points at line 10 of a generated file, look at line 14 of the dumped file. Lines above the header, such as a `// <auto-generated>` block, keep their numbers.
221
+
222
+ ### Stale files and files held open
223
+
224
+ A file dumped before your last source edit is stale. Compare its `Dumped:` line with the time of your edit; the next compile of that assembly refreshes it while the dump is on.
225
+
226
+ A file held open by another program cannot be replaced. It keeps its older content and its older `Dumped:` line, a warning names it (see [Warnings and failures](#warnings-and-failures)), and the next dump tries again. [Read the dump](examples/read-the-dump.md) shows these files being searched.
227
+
228
+ ## Long paths
229
+
230
+ Take a Windows project with HDRP and UI Toolkit, whose root folder path is 103 characters long. One generated file of its HDRP editor assembly would get this path inside the project:
231
+
232
+ ```text
233
+ Library/GeneratedCodeDump/Unity.RenderPipelines.HighDefinition.Editor/Unity.UIToolkit.SourceGenerator/UnityEditor.Rendering.HighDefinition.Compositor.CompositionLayerUIElement.UxmlSerializedData.g.cs
234
+ ```
235
+
236
+ With the project path and a separator in front, that is 303 characters. Windows allows 259, so the dump shortens names, one step at a time, until the path fits:
237
+
238
+ 1. The assembly folder `Unity.RenderPipelines.HighDefinition.Editor` becomes `Unity.RenderPipe~45d4eb49`. The path is now 285 characters.
239
+ 2. The generator folder `Unity.UIToolkit.SourceGenerator` becomes `Unity.UIToolkit.~b9605758`: 279 characters.
240
+ 3. The file name is cut to the 77 characters left: `UnityEditor.Rendering.HighDefinition.Compositor.CompositionLaye~f7565493.g.cs`. The path is now exactly 259 characters.
241
+
242
+ ```text
243
+ Library/GeneratedCodeDump/Unity.RenderPipe~45d4eb49/Unity.UIToolkit.~b9605758/UnityEditor.Rendering.HighDefinition.Compositor.CompositionLaye~f7565493.g.cs
244
+ ```
245
+
246
+ A shortened name like these is a *cut name*: the start of the name, then the short hash of the full name, then, for a file, its `.g.cs` or `.generated.cs` ending.
247
+
248
+ ### The limits
249
+
250
+ | OS | Whole file path | Folder path | One name | Counted in |
251
+ | --- | --- | --- | --- | --- |
252
+ | Windows | 259 | 247 | 255 | UTF-16 characters |
253
+ | macOS, Linux | 1023 | 1023 | 255 | UTF-8 bytes |
254
+
255
+ The whole path counts, from the drive or the root, with the project's own path included.
256
+
257
+ ### The cut order
258
+
259
+ Names stay whole wherever the full path fits. When a path does not fit, the dump cuts names in this order until it does:
260
+
261
+ 1. The assembly folder becomes its first 16 characters plus the short hash of its full name. This applies to all of that assembly's files.
262
+ 2. Then a generator folder is cut the same way, for all of that folder's files.
263
+ 3. Then each file that still does not fit is cut to the room left: as much of its start as fits, the short hash of its full name, and its ending. `.generated.cs` or `.g.cs` (else the extension) stays whole.
264
+ 4. A file that still does not fit, because fewer than 8 characters of its start would remain, is skipped with a warning.
265
+
266
+ A folder name of 25 characters or fewer is never cut, since its cut name would be no shorter. A folder can switch between its full name and its cut name when the longest path inside it changes. Each dump deletes the other form, so only one of them exists.
267
+
268
+ The short hash keeps a cut name the same from one dump to the next. The `.work/<Assembly>/` folder is cut the same way when its `replay.rsp` or `errors.sarif` would be over the limit.
269
+
270
+ ### Finding a cut file
271
+
272
+ A cut name loses part of the type name, but the header lines always hold the full names. So search the header instead of the file names:
273
+
274
+ ```bash
275
+ grep -rl --exclude-dir=.work "Hint: .*ExampleBehaviour" Library/GeneratedCodeDump
276
+ grep -rl --exclude-dir=.work "Assembly: Assembly-CSharp" Library/GeneratedCodeDump
277
+ ```
278
+
279
+ ### Skipped files
280
+
281
+ A file that cannot fit even with every cut is left out, and the Console shows:
282
+
283
+ ```text
284
+ [Generated Code Dump] <Assembly>: <N> files skipped: their path is over the <limit>-character limit of this OS; move the project to a shorter path.
285
+ ```
286
+
287
+ The skipped paths follow, one per line. `<limit>` is 259 on Windows and 1023 on macOS and Linux, where it counts UTF-8 bytes although the message says "character". The fix is the one the message gives: move the project to a shorter path.
288
+
289
+ The dump does not rely on Windows' long-path support. It keeps every path within the limits above. If the project path is too deep for that, some files are skipped with this warning.
290
+
291
+ ## Batch mode and CI
292
+
293
+ This runs Unity once without a window, dumps everything and lists the result:
294
+
295
+ ```bash
296
+ PROJECT=/path/to/project
297
+ UNITY=~/Unity/Hub/Editor/6000.0.65f1/Editor/Unity # your Unity Editor binary
298
+
299
+ mkdir -p "$PROJECT/UserSettings"
300
+ printf '{"enabled": true}\n' > "$PROJECT/UserSettings/GeneratedCodeDumpSettings.json"
301
+
302
+ "$UNITY" -batchmode -nographics -projectPath "$PROJECT" -quit -logFile unity.log
303
+ grep '\[Generated Code Dump\]' unity.log
304
+ ls "$PROJECT/Library/GeneratedCodeDump"
305
+ ```
306
+
307
+ With the dump on, a `-batchmode` Editor start runs a full dump and waits until it is complete before it goes on. A CI job or an agent can therefore read the dump as soon as the Editor exits after `-quit`.
308
+
309
+ In batch mode there is no background thread. The Editor's own thread starts the compilers and waits for them. Every dump in a batch run waits like this, including a per-compile dump after the run compiles scripts, until a wait runs out (below).
310
+
311
+ Any batch run while the dump is on does the same full dump when it starts: `-runTests`, a build, or any other. The settings file is the only switch. Delete it after the run if later batch runs on that machine should skip the dump.
312
+
313
+ ### How long it waits
314
+
315
+ The wait is at most 300 seconds per round, where a round is as many assemblies as compile at once. With 11 assemblies and 6 compiling at once, that is 2 rounds, so at most 600 seconds.
316
+
317
+ Past that limit the Editor logs a warning and carries on:
318
+
319
+ ```text
320
+ [Generated Code Dump] stopped waiting after <seconds> s; the rest of the dump finishes in the next Editor session.
321
+ ```
322
+
323
+ The unfinished jobs stay in `Library/GeneratedCodeDump/.work/jobs.txt`, and the next Editor session dumps the remaining assemblies. After giving up once, the rest of that batch run no longer waits for dumps.
324
+
325
+ A single compiler that runs for more than 300 seconds, counted from its start, is stopped and its output is ignored. This applies in the Editor too, and it logs:
326
+
327
+ ```text
328
+ [Generated Code Dump] <Assembly>: the compiler ran for over 300 s, so it was stopped and its output is ignored.
329
+ ```
330
+
331
+ To check that a run finished the dump, look for leftover jobs:
332
+
333
+ ```bash
334
+ grep -q '^job' "$PROJECT/Library/GeneratedCodeDump/.work/jobs.txt" && echo "dump incomplete" || echo "dump complete"
335
+ ```
336
+
337
+ The full walkthrough is in [Dump in batch mode](examples/dump-in-batch-mode.md).
338
+
339
+ ## Performance
340
+
341
+ At most half the logical CPUs run a compiler at once, and never fewer than 1 or more than 8. A machine with 12 logical CPUs runs 6 at once; one with 2 runs 1; one with 32 runs 8. The Preferences page and the `queued` log line show the number.
342
+
343
+ Each replay is a separate compiler process. In the Editor, a background thread starts them and collects their output every 250 milliseconds. The Editor never waits for them, and the dump keeps going while the Editor is in the background.
344
+
345
+ No replay starts while Unity itself is compiling. Queued replays wait until Unity's compile ends.
346
+
347
+ Measured on this package's repository, with 12 logical CPUs (so 6 at once): a full dump queued 11 assemblies and never ran more than 6 compilers. Compiling took about 1.7 seconds of a 7.5-second batch run, which also covered Editor start, import and quit.
348
+
349
+ A full dump costs about one compile per assembly that loads an analyzer. Unity's own generator runs on most assemblies, so a large project's full dump grows with its assembly count. A per-compile dump replays only the assemblies rebuilt by that compile.
350
+
351
+ ## Warnings and failures
352
+
353
+ Here is what the Console shows when Unity's compiler is not where the dump expects it after a compile:
354
+
355
+ ```text
356
+ [Generated Code Dump] Unity's C# compiler was not found at <path>. The dump is off until the next domain reload.
357
+ ```
358
+
359
+ Every message the dump logs starts with `[Generated Code Dump]`. Every failure is a Console warning, never an error. The dump never breaks the project's compile: its failures stay warnings, and it never writes Unity's own outputs.
360
+
361
+ ### Warnings that only warn
362
+
363
+ Most failures only log a warning, and the dump carries on. A failed replay and a failed Dump Now are both of this kind. When a replay cannot run or is stopped, the assembly's folder keeps the files from the previous dump.
364
+
365
+ Within one replay, paths over the limit and other file errors are reported as two separate warnings. Each names the files it is about.
366
+
367
+ ### Warnings that also turn the dump off
368
+
369
+ A few failures also turn the dump off until the next domain reload. Their warning ends with `The dump is off until the next domain reload.` These are:
370
+
371
+ - a failure while the dump starts up, at Editor start or after a domain reload;
372
+ - a failure while checking the setting;
373
+ - a failure while handling Unity's compile events;
374
+ - Unity's compiler missing after a compile.
375
+
376
+ This does not change the settings file: the switch stays on. The next domain reload, which follows every compile, turns the dump back on and tries again. If the cause is still there, it warns again.
377
+
378
+ ### After a Unity update
379
+
380
+ The dump relies on Unity internals, not public API: the response files Unity writes under `Library/Bee/`, and the compiler and .NET runtime inside the Editor install (see [How it works](#how-it-works)). A Unity update can move or change any of them.
381
+
382
+ If that happens, the dump logs a warning and stops: after a compile it is off until the next domain reload, as above, and Dump Now queues nothing. Your project still compiles as usual, and the files already in `Library/GeneratedCodeDump/` stay as they were.
383
+
384
+ ### Messages
385
+
386
+ Each message below follows the `[Generated Code Dump] ` prefix.
387
+
388
+ | Message | What happened | What the dump does |
389
+ | --- | --- | --- |
390
+ | `<full path> is not valid: expected {"enabled": true} or false. The dump is off.` | The settings file is not valid JSON of the expected shape. | Stays off until the file is fixed. Warns again only when the file changes and is still invalid. |
391
+ | `<Assembly>: <N> files skipped: their path is over the <limit>-character limit of this OS; move the project to a shorter path.` | Some paths are too long even with every cut. See [Long paths](#long-paths). | Skips those files; the rest are written. |
392
+ | `<Assembly>: <N> files could not be replaced, most likely open in another program; they keep their older 'Dumped:' line and the next dump retries.` | Some files could not be written or deleted, usually because another program holds them open. | Keeps the older files; the next dump tries again. |
393
+ | `<Assembly>: the compiler ran for over 300 s, so it was stopped and its output is ignored.` | One replay took more than 300 seconds. | Keeps the assembly's previous files. |
394
+ | `<Assembly>: the compiler stopped before writing errors.sarif, so the previous dump is kept. Its arguments: Library/GeneratedCodeDump/.work/<Assembly>/replay.rsp` | The replay's compiler crashed or was stopped. | Keeps the assembly's previous files. |
395
+ | `<Assembly>: the replay reported compile errors, so the dump can be incomplete. See Library/GeneratedCodeDump/.work/<Assembly>/errors.sarif` | The replayed compile had errors. | Writes what the generators produced anyway. |
396
+ | `stopped waiting after <seconds> s; the rest of the dump finishes in the next Editor session.` | A batch run reached its wait limit. See [Batch mode and CI](#batch-mode-and-ci). | Leaves the rest to the next Editor session. |
397
+ | `Unity's C# compiler was not found at <path>.` | The compiler is missing from the Editor install. | Queues nothing. After a compile, also turns the dump off until the next domain reload. |
398
+ | `Unity's .NET runtime was not found at <path>.` | The runtime the compiler needs is missing. | The same as a missing compiler. |
399
+ | `Unity's build folder Library/Bee/artifacts was not found.` | Unity has not compiled this project yet, or its layout changed. | The same as a missing compiler. |
400
+ | `UserSettings/GeneratedCodeDumpSettings.json could not be written: <reason>` | The menu or Preferences toggle could not save the setting. | Leaves the setting as it was. |
401
+ | `watching UserSettings/GeneratedCodeDumpSettings.json failed: <reason> An edit to it made outside the Editor now applies on the next compile.` | The Editor could not watch the settings file. | Picks up file edits at the next compile instead of within a second. |
402
+ | `the background dump stopped: <reason> It restarts with the next dump.` | The Editor's background thread hit an error. | Starts again with the next dump request. |
403
+
404
+ The messages from the upgrade are listed in [Upgrading from 1.0.0](#upgrading-from-100).
405
+
406
+ ## Upgrading from 1.0.0
407
+
408
+ 1.0.0 kept its switch and its dump in `Library/com.elestrago.unity.generated-code-dump/`, with namespace folders in the dump. Its temporary output went to `<temp>/GCDump/<project hash>/`, where `<temp>` is the system temp folder and `<project hash>` the short hash of the project's root path, without the `~`.
409
+
410
+ The upgrade runs at every Editor start, batch mode included, before the setting is first read. The first start with this version does the work:
411
+
412
+ 1. `Library/com.elestrago.unity.generated-code-dump/Settings.json` moves unchanged to `UserSettings/GeneratedCodeDumpSettings.json`, unless that file already exists. If it does, the new file wins.
413
+ 2. The rest of the old folder is deleted. The folder is kept while its setting could not be moved.
414
+ 3. This project's old temp folder `<temp>/GCDump/<project hash>/` is deleted, and `<temp>/GCDump/` too once it is empty. Other projects still on 1.0.0 may use it until then.
415
+
416
+ When the old folder is gone, the Console shows:
417
+
418
+ ```text
419
+ [Generated Code Dump] Library/com.elestrago.unity.generated-code-dump/ is replaced by Library/GeneratedCodeDump/ and UserSettings/GeneratedCodeDumpSettings.json, so it was deleted.
420
+ ```
421
+
422
+ A step that fails logs a warning, most of them ending `The next Editor start retries.`, and is tried again at the next start. It never turns the dump off.
423
+
424
+ If the dump is on, that same start then runs a full dump into `Library/GeneratedCodeDump/`. Scripts that read the 1.0.0 paths must switch to the flat layout in [Reading the output](#reading-the-output).
425
+
426
+ Moving a setting that is off writes the new settings file while the dump is off.
427
+
428
+ ## How it works
429
+
430
+ Each replay goes through these steps:
431
+
432
+ 1. Unity's compile writes a *response file* for each assembly: a text file with the compiler arguments for that assembly, one per line, covering sources, references, analyzers and outputs. It sits at `Library/Bee/artifacts/<hash>.dag/<Assembly>.rsp`, sometimes with an `<Assembly>.rsp2` whose arguments come after it.
433
+ 2. The `<hash>.dag` folder is a *build graph*, Unity's record of a compile. Older build graphs can sit beside the latest one, so the newest response file wins, and a full dump takes its assemblies from the newest build graph.
434
+ 3. Only an assembly whose response file has at least one `-analyzer:` argument is replayed.
435
+ 4. The dump copies the arguments and drops every one that writes a file: `-out`, `-refout`, `-doc`, `-pdb`, `-errorlog` and `-generatedfilesout`. It adds its own `-out`, `-refout`, `-errorlog` and `-generatedfilesout`, and saves the result as `Library/GeneratedCodeDump/.work/<Assembly>/replay.rsp`.
436
+ 5. It runs the Roslyn compiler that ships inside the Unity Editor install: `DotNetSdkRoslyn/csc.dll`, run on `NetCoreRuntime/dotnet`. Both are in the Editor's contents folder: `Editor/Data/` on Windows and Linux, `Unity.app/Contents/` on macOS.
437
+ 6. When the compiler exits, the generated files are named by the rules in [Reading the output](#reading-the-output), given their header lines, and synced into `Library/GeneratedCodeDump/<Assembly>/`. Then the job's temp folder is deleted.
438
+
439
+ The new outputs in step 4 go here:
440
+
441
+ | Argument | Points at |
442
+ | --- | --- |
443
+ | `-generatedfilesout` | `<temp>/GenDump/<project hash>/<job id>/g/`, the generated files |
444
+ | `-out`, `-refout` | `<Assembly>.dll` and `<Assembly>.ref.dll` in `<temp>/GenDump/<project hash>/<job id>/o/` |
445
+ | `-errorlog` | `Library/GeneratedCodeDump/.work/<Assembly>/errors.sarif` |
446
+
447
+ The DLL keeps the assembly's name because the compiler takes the assembly name from that file name. `<project hash>` is the short hash of the project's root path, without the `~`. It keeps the temp path short wherever the project lives.
448
+
449
+ Unity's own outputs are never touched: the dump only reads the response files. The jobs are recorded in `.work/jobs.txt`, so a dump cut short by a domain reload or an Editor restart resumes.
450
+
451
+ ## Internals
452
+
453
+ For contributors. Every identifier, source file and API link lives in this section.
454
+
455
+ - Package name: `com.elestrago.unity.generated-code-dump`.
456
+ - Package source: `Assets/Package/GeneratedCodeDump`.
457
+ - Source files: `Assets/Package/GeneratedCodeDump/Editor/`.
458
+ - `file:line` pointers for every type are in the [API reference](api.md).
459
+
460
+ ### Assembly
461
+
462
+ | Assembly | Root namespace | Platforms | References | asmdef |
463
+ | --- | --- | --- | --- | --- |
464
+ | `GeneratedCodeDump.Editor` | `GeneratedCodeDump.Editor` | Editor | none | `Assets/Package/GeneratedCodeDump/Editor/GeneratedCodeDump.Editor.asmdef` |
465
+
466
+ - Every type is `internal`, in the one namespace `GeneratedCodeDump.Editor`.
467
+ - `AssemblyInfo.cs` holds `InternalsVisibleTo("GeneratedCodeDump.Editor.Tests")` for the test assembly.
468
+ - Extension points: none. No type is abstract, virtual, `Base`-suffixed or `partial`.
469
+ - No `[CreateAssetMenu]`, and no public static method on a public type.
470
+ - No `ScriptableObject` or `[Serializable]` type. All state is plain files: the switch in `UserSettings/`, the dump and `.work/` in `Library/GeneratedCodeDump/`, one temp folder per job.
471
+ - UnityEditor is used only by `GeneratedCodeDumper.cs`, `GeneratedCodeDumpSettingsProvider.cs` and `GeneratedCodeMenuItems.cs`.
472
+ - `GeneratedCodeDumpFinisher`, `GeneratedCodeDumpSettingsWatcher`, `GeneratedCodeDumpSettings` and `GeneratedCodeDumpMigration` use `UnityEngine.Debug` for warnings; `GeneratedCodeDumpPaths` reads `Application.dataPath` once.
473
+
474
+ ### Source files
475
+
476
+ | File | Role | API |
477
+ | --- | --- | --- |
478
+ | `GeneratedCodeDumper.cs` | `[InitializeOnLoad]` entry. Compile hooks, `DumpIfJustEnabled`, `DumpAll`, `SetEnabled`, `Enqueue`, `StartReplay`, finishing jobs, batch-mode wait (`FinishAllBlocking`, `BatchWaitSeconds`), `MarkUnavailable`. Holds `MaxConcurrent`, `JobTimeoutSeconds` (300), `StampFormat`, `SessionLock`, `Gate`. | [GeneratedCodeDumper](api.md#generatedcodedumper) |
479
+ | `GeneratedCodeDumpFinisher.cs` | Background thread `Generated code dump`: `GeneratedCodeDumper.Tick` every 250 ms while jobs exist. Stopped before each domain reload and on quit, joining for up to 2 s. Not used in batch mode. | [GeneratedCodeDumpFinisher](api.md#generatedcodedumpfinisher) |
480
+ | `GeneratedCodeDumpJobs.cs` | Job store `.work/jobs.txt`: pure `Format` / `Parse`, `Read` / `Write` under a file lock. Ids only go up, across restarts. | [GeneratedCodeDumpJobs](api.md#generatedcodedumpjobs), [GeneratedCodeDumpJob](api.md#generatedcodedumpjob), [GeneratedCodeDumpJobList](api.md#generatedcodedumpjoblist), [GeneratedCodeDumpJobState](api.md#generatedcodedumpjobstate) |
481
+ | `GeneratedCodeDumpSession.cs` | Session marker `.work/enabled-session` and the pure `ShouldDumpAll`. | [GeneratedCodeDumpSession](api.md#generatedcodedumpsession) |
482
+ | `GeneratedCodeDumpSettings.cs` | The switch. Thread-safe, re-read when the file's write time or length changes, atomic write, one warning per invalid version of the file. | [GeneratedCodeDumpSettings](api.md#generatedcodedumpsettings) |
483
+ | `GeneratedCodeDumpSettingsFile.cs` | Strict flat-JSON `Format` / `TryParse` of the settings file, no `JsonUtility`, so the watcher can read it off the main thread. | [GeneratedCodeDumpSettingsFile](api.md#generatedcodedumpsettingsfile) |
484
+ | `GeneratedCodeDumpSettingsWatcher.cs` | `FileSystemWatcher` with a 300 ms debounce after the last change, calling `DumpIfJustEnabled`. Watches `UserSettings/` for the file, or the project root for `UserSettings/` while it is missing (pure `Target`). Not used in batch mode. | [GeneratedCodeDumpSettingsWatcher](api.md#generatedcodedumpsettingswatcher) |
485
+ | `GeneratedCodeDumpMigration.cs` | 1.0.0 upgrade, run before the setting is first read. Pure `ShouldMoveSettings`; `Migrate` takes full paths. A failure is a warning, never `MarkUnavailable`. | [GeneratedCodeDumpMigration](api.md#generatedcodedumpmigration) |
486
+ | `GeneratedCodeDumpRsp.cs` | `Find` (newest `<Assembly>.rsp`), `ReadArguments` (plus `.rsp2`), `ListAssemblies` (the newest build graph, `.mvfrm.rsp` excluded). | [GeneratedCodeDumpRsp](api.md#generatedcodedumprsp) |
487
+ | `GeneratedCodeDumpArgs.cs` | `HasAnalyzer` (`-analyzer:` or `/analyzer:`, any case, non-empty value) and `Rewrite` (drops every output argument, appends the replay's own). | [GeneratedCodeDumpArgs](api.md#generatedcodedumpargs) |
488
+ | `GeneratedCodeDumpOutput.cs` | Compiler output to the flat layout (`Entries`, with the collision tag), the cut plan (`Plan`), header lines (`Header`, `Stamp`), per-file `Sync` that also removes the assembly folder's other form. | [GeneratedCodeDumpOutput](api.md#generatedcodedumpoutput), [GeneratedCodeDumpEntry](api.md#generatedcodedumpentry), [GeneratedCodeDumpLayout](api.md#generatedcodedumplayout), [GeneratedCodeDumpSyncResult](api.md#generatedcodedumpsyncresult) |
489
+ | `GeneratedCodeDumpNames.cs` | `Tag` (`~<fnv8>`), `CutFolder`, `CutFile` (null below 8 start characters), `Suffix`, `SafeSegment`. Constants `MaxUncutLength` 25, `CutFolderStart` 16, `MinCutFileStart` 8. | [GeneratedCodeDumpNames](api.md#generatedcodedumpnames) |
490
+ | `GeneratedCodeDumpPathBudget.cs` | Per-OS limits `Windows`, `Unix`, `Current`; `Length`, `Fits`. Passed in so tests use small budgets. No Unity API. | [GeneratedCodeDumpPathBudget](api.md#generatedcodedumppathbudget) |
491
+ | `GeneratedCodeDumpPaths.cs` | Every path, project-relative constants, the 1.0.0 ones as `Legacy*`, the `.work/<Assembly>/` cut (`WorkDirectory`), the temp folder keyed by `Fnv1a` of the project root. | [GeneratedCodeDumpPaths](api.md#generatedcodedumppaths) |
492
+ | `GeneratedCodeDumpIdentity.cs` | Every shown name: `PackageName`, `LibraryFolderName`, `LogPrefix`, `ThreadName`, `ScratchFolderName` (`GenDump`), `LegacyScratchFolderName` (`GCDump`), `MenuRoot`, `SettingsPath`. | [GeneratedCodeDumpIdentity](api.md#generatedcodedumpidentity) |
493
+ | `GeneratedCodeDumpProcess.cs` | Process identity by pid and start time (2 s tolerance, unknown start matches any): `IsRunning`, `TryKill`, the current Editor's id and start. | [GeneratedCodeDumpProcess](api.md#generatedcodedumpprocess) |
494
+ | `GeneratedCodeDumpFiles.cs` | `WriteAtomic` (`<path>.tmp`, then replace), `ReadTextOrNull`, `CreateHiddenDirectory` (Hidden `.work` on Windows), folder cleanup. | [GeneratedCodeDumpFiles](api.md#generatedcodedumpfiles) |
495
+ | `GeneratedCodeDumpSettingsProvider.cs` | The `Preferences/Generated Code Dump` page, user scope. | [GeneratedCodeDumpSettingsProvider](api.md#generatedcodedumpsettingsprovider) |
496
+ | `GeneratedCodeMenuItems.cs` | The `Tools/Generated Code Dump/` menu. | [GeneratedCodeMenuItems](api.md#generatedcodemenuitems) |
497
+ | `AssemblyInfo.cs` | `InternalsVisibleTo` for `GeneratedCodeDump.Editor.Tests`. | none |
498
+
499
+ ### Entry points
500
+
501
+ Menu items (`GeneratedCodeMenuItems`):
502
+
503
+ | Menu item | Priority | Method | Calls |
504
+ | --- | --- | --- | --- |
505
+ | `Tools/Generated Code Dump/Enabled` | 100 | `ToggleEnabled` | `GeneratedCodeDumper.SetEnabled(!IsEnabled)`; checkmark from the validator `ValidateEnabled` |
506
+ | `Tools/Generated Code Dump/Dump Now` | 111 | `DumpNow` | `GeneratedCodeDumper.DumpAll`, whatever the setting |
507
+ | `Tools/Generated Code Dump/Open Folder` | 112 | `OpenFolder` | `Directory.CreateDirectory`, then `EditorUtility.OpenWithDefaultApp` |
508
+ | `Tools/Generated Code Dump/Preferences…` | 123 | `OpenSettings` | `SettingsService.OpenUserPreferences("Preferences/Generated Code Dump")` |
509
+
510
+ Preferences page:
511
+
512
+ - `[SettingsProvider]` factory `GeneratedCodeDumpSettingsProvider.Create`, `SettingsScope.User`, path `Preferences/Generated Code Dump`.
513
+ - Search keywords: `Generated`, `Source Generator`, `Roslyn`, `Dump`.
514
+
515
+ Start-up (the `[InitializeOnLoad]` static constructor of `GeneratedCodeDumper`):
516
+
517
+ 1. Return at once in an asset import worker (`AssetDatabase.IsAssetImportWorkerProcess`).
518
+ 2. `CaptureMainThreadState`: project root, temp root, settings, current process, `Application.isBatchMode`, and the compiler paths from `EditorApplication.applicationContentsPath`.
519
+ 3. `GeneratedCodeDumpMigration.Run`.
520
+ 4. Hook `CompilationPipeline.compilationStarted`, `CompilationPipeline.assemblyCompilationFinished`, `CompilationPipeline.compilationFinished`, `AssemblyReloadEvents.beforeAssemblyReload` and `EditorApplication.quitting`. The last two call `Shutdown`: the finisher first, then the watcher.
521
+ 5. Start `GeneratedCodeDumpSettingsWatcher`, except in batch mode.
522
+ 6. `DumpIfJustEnabled`.
523
+ 7. If `jobs.txt` holds jobs left by the previous domain or Editor, `Kick`: start the finisher, or `FinishAllBlocking` in batch mode.
524
+ 8. Any exception: `MarkUnavailable`.
525
+
526
+ Compile hooks:
527
+
528
+ - `compilationStarted`: sets `_compiling`, so `StartQueued` starts no compiler until Unity's compile ends.
529
+ - `assemblyCompilationFinished`: while on, records the assembly unless a message has type `Error`.
530
+ - `compilationFinished`: clears `_compiling`; while on, `TryLocateCompiler` (else `MarkUnavailable`), then `Enqueue` and `Kick`.
531
+
532
+ `DumpIfJustEnabled` (holds `SessionLock`):
533
+
534
+ - Off: if the session marker exists, delete it and `DropQueued` (queued jobs removed, reruns cleared).
535
+ - On: write the marker unless it names this process; then `ShouldDumpAll(enabled, markerIsThisProcess, rootExists)`, true when on and either the marker was not this process's or the dump folder is missing, runs `DumpAll`.
536
+ - `DumpAll` and `Enqueue` create `Library/GeneratedCodeDump/` even when nothing is queued, so a project without analyzers does not run a full dump on every reload.
537
+ - `DumpAll` with the compiler or `Library/Bee/artifacts` missing: one warning, returns 0, no `MarkUnavailable`.
538
+
539
+ ### File formats
540
+
541
+ - `UserSettings/GeneratedCodeDumpSettings.json`:
542
+ - written by `GeneratedCodeDumpSettingsFile.Format` as `{`, ` "enabled": true`, `}` on three lines, UTF-8 without BOM, through `WriteAtomic`;
543
+ - parsed by `TryParse`: one flat object, string keys, values string, number, `true`, `false` or `null`; `enabled` exactly `true` or `false`; missing `enabled` means off; a leading BOM is allowed.
544
+ - `.work/jobs.txt`: a `next <id>` line, then one line per job, sorted by id, with 8 tab-separated fields:
545
+
546
+ | # | Field | Values |
547
+ | --- | --- | --- |
548
+ | 1 | tag | `job` |
549
+ | 2 | id | 1 or more; ids only go up, so a new job never reuses an old temp folder |
550
+ | 3 | state | `queued` or `running` |
551
+ | 4 | rerun | `0` or `1` |
552
+ | 5 | process id | the compiler's pid, `0` while queued |
553
+ | 6 | process start ticks | the compiler's start time, UTC ticks |
554
+ | 7 | started ticks | when the job started its compiler, UTC ticks: the timeout counts from it and it is the `Dumped:` time |
555
+ | 8 | assembly | the assembly name |
556
+
557
+ `Parse` skips a malformed line and a second job with an id or assembly already seen; `next` always ends above the highest job id.
558
+ - `.work/enabled-session`: `<process id> <start ticks>` and a newline, for the Editor that last found the dump on. A missing start time reads as 0 (unknown) and matches any process start.
559
+ - `.work/<Assembly>/`: only `replay.rsp` (UTF-8 without BOM) and `errors.sarif` of the last replay. It is deleted and created again when a replay starts. Its name is `SafeSegment(assembly)`, or `CutFolder(assembly)` when either file would be over the budget.
560
+ - `<temp>/GenDump/<fnv8 of the project root>/<job id>/`:
561
+ - `g/`: the compiler's `-generatedfilesout`, laid out as `<GeneratorAssembly>/<GeneratorType>/<hint>`;
562
+ - `o/`: `<Assembly>.dll` and `<Assembly>.ref.dll`;
563
+ - deleted after each job. The project folder is deleted whole when jobs are added to an empty list (a crash's leftovers), and `<temp>/GenDump/` once it is empty.
564
+ - 1.0.0 paths: `LegacyRoot` (`Library/com.elestrago.unity.generated-code-dump/`), `LegacySettingsFile` (its `Settings.json`), `LegacyScratchBase` (`<temp>/GCDump/`), `LegacyScratchRoot` (this project's folder in it).
565
+
566
+ ### Jobs in memory
567
+
568
+ - Type: `GeneratedCodeDumpJob`, one per line of `jobs.txt`.
569
+ - Held in: `GeneratedCodeDumpJobList`, with `NextId` (starts at 1).
570
+
571
+ | Field | Type | Meaning |
572
+ | --- | --- | --- |
573
+ | `Id` | `long` | Job id |
574
+ | `Assembly` | `string` | Assembly name |
575
+ | `State` | `GeneratedCodeDumpJobState` | `Queued` or `Running` |
576
+ | `ProcessId` | `int` | Compiler pid |
577
+ | `ProcessStartUtcTicks` | `long` | Compiler process start time |
578
+ | `StartedUtcTicks` | `long` | When the job started its compiler: the timeout counts from it, and it is the `Dumped:` time |
579
+ | `Rerun` | `bool` | A dump was asked while the compiler ran |
580
+
581
+ - `GeneratedCodeDumpJobList.DropNotStarted` removes queued jobs and clears every `Rerun`.
582
+ - `Enqueue` per assembly: no job adds a queued one; a running job gets `Rerun = true`; a queued job is left alone, since it reads the response file when it starts.
583
+
584
+ ### Threading and locks
585
+
586
+ - No Unity API off the main thread. The static constructor caches the project root, the compiler paths and the batch-mode flag.
587
+ - Threads: the main thread runs the hooks, the menu and the Preferences page; the finisher thread runs `Tick`; the watcher's debounce timer calls back on a thread-pool thread. Batch mode uses the main thread only.
588
+ - Lock order, outermost first:
589
+ 1. the settings watcher's `CallbackLock`;
590
+ 2. `GeneratedCodeDumper.SessionLock`;
591
+ 3. `GeneratedCodeDumper.Gate`;
592
+ 4. the finisher's `ThreadLock`;
593
+ 5. the job store's `FileLock`.
594
+ - The watcher's own `Sync` lock is held only briefly and never while calling out.
595
+ - Every read-modify-write of the jobs holds `Gate`; `Read` and `Write` also take `FileLock`.
596
+ - `StartQueued`: nothing while `_compiling`; starts jobs in id order while fewer than `MaxConcurrent` live compilers run, and writes `jobs.txt` after each start, so a reload never starts a second compiler for a job. A stopping finisher starts nothing.
597
+ - Compiler process: `dotnet exec csc.dll /nostdlib /noconfig -nologo @replay.rsp`, working directory the project root, `UseShellExecute = false`, `CreateNoWindow`, no redirected output (an unread pipe dies with the domain and can take the compiler with it).
598
+ - Finishing (`FinishExited`), per exited job, in this order:
599
+ 1. `SyncOutput` syncs the files, unless `Rerun` is set;
600
+ 2. `Complete` removes the job, re-reading under `Gate`, and queues the assembly again under a new id when `Rerun` is set;
601
+ 3. the job's temp folder is deleted.
602
+
603
+ Removing the job last means a domain reload mid-finish repeats the finish.
604
+ - Timeout: a running job past `JobTimeoutSeconds` is killed (`TryKill` checks pid and start time) and completed without a sync.
605
+ - `SyncOutput`: no `errors.sarif` keeps the previous dump; an `"level": "error"` entry warns but still syncs.
606
+ - `Sync` tries every listing, write and delete on its own. `PathTooLongException` goes to `TooLong`, any other I/O or access error to `Failed`, and each list gets its own warning.
607
+ - Batch mode: `Kick` calls `FinishAllBlocking`, which runs `Tick` every 50 ms until no job is left or `BatchWaitSeconds(jobs, MaxConcurrent, 300)` (300 × ceil(jobs / `MaxConcurrent`)) runs out. After giving up it does not wait again in that domain.
608
+
609
+ ### Preferences page
610
+
611
+ - IMGUI: a user-scope `SettingsProvider` drawn with `EditorGUILayout` in `OnGUI`. The package has no `.uxml` or `.uss` file and no `EditorWindow`, `PropertyDrawer` or custom inspector.
612
+ - Top to bottom:
613
+ 1. a help box on what the dump does and where it writes;
614
+ 2. the `Dump generated code` toggle, which calls `GeneratedCodeDumper.SetEnabled`;
615
+ 3. an info box: where the setting lives, that an edit applies within a second and batch mode follows it, and the `MaxConcurrent` number;
616
+ 4. an `Output` section with `Folder` `Library/GeneratedCodeDump/`;
617
+ 5. `Open Folder` and `Dump Now` buttons, which call `GeneratedCodeMenuItems.OpenFolder` and `GeneratedCodeMenuItems.DumpNow`.
618
+ - The help texts describe a full dump more broadly than the code runs it: only assemblies whose compile loads an analyzer are replayed.
@@ -0,0 +1,3 @@
1
+ using System.Runtime.CompilerServices;
2
+
3
+ [assembly: InternalsVisibleTo("GeneratedCodeDump.Editor.Tests")]
@@ -0,0 +1,2 @@
1
+ fileFormatVersion: 2
2
+ guid: fbbf082de05732428bb727f1d0428801