[TrimmableTypeMap] Extract TrimmableTypeMapGenerator from MSBuild task#11034
[TrimmableTypeMap] Extract TrimmableTypeMapGenerator from MSBuild task#11034simonrozsival wants to merge 12 commits intomainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR refactors the trimmable typemap generation pipeline by extracting orchestration logic from the GenerateTrimmableTypeMap MSBuild task into a standalone TrimmableTypeMapGenerator class, aiming to make the logic more testable and the task a thin adapter.
Changes:
- Introduces
TrimmableTypeMapGenerator+TrimmableTypeMapResultto host the scan → typemap → JCW → acw-map orchestration outside the MSBuild task. - Updates
GenerateTrimmableTypeMapto adapt MSBuild inputs/outputs to the new generator and adjusts tests accordingly (null → empty outputs, removedget_TargetTypeassertions). - Adds resource infrastructure and MSBuild package references to the TrimmableTypeMap project.
Reviewed changes
Copilot reviewed 10 out of 11 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.cs | Removes get_TargetType wrapper assertion. |
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.cs | Removes get_TargetType wrapper assertion. |
| src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/GenerateTrimmableTypeMapTests.cs | Updates expectations for empty outputs (null → empty). |
| src/Xamarin.Android.Build.Tasks/Tasks/GenerateTrimmableTypeMap.cs | Converts task into an adapter calling TrimmableTypeMapGenerator. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/TrimmableTypeMapTypes.cs | Adds TrimmableTypeMapResult record. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/TrimmableTypeMapGenerator.cs | Adds the extracted orchestration logic. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Properties/Resources.resx | Adds XA4212/13/17 resource strings. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Properties/Resources.Designer.cs | Adds strongly-typed resource accessors (auto-generated). |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/NullableExtensions.cs | Adds NRT-annotated string null/empty helpers for netstandard2.0. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Microsoft.Android.Sdk.TrimmableTypeMap.csproj | Adds MSBuild package refs and resource codegen configuration. |
| eng/Versions.props | Adds MSBuildPackageReferenceVersion. |
Files not reviewed (1)
- src/Microsoft.Android.Sdk.TrimmableTypeMap/Properties/Resources.Designer.cs: Language not supported
src/Microsoft.Android.Sdk.TrimmableTypeMap/TrimmableTypeMapGenerator.cs
Outdated
Show resolved
Hide resolved
src/Microsoft.Android.Sdk.TrimmableTypeMap/Properties/Resources.resx
Outdated
Show resolved
Hide resolved
src/Xamarin.Android.Build.Tasks/Tasks/GenerateTrimmableTypeMap.cs
Outdated
Show resolved
Hide resolved
src/Xamarin.Android.Build.Tasks/Tasks/GenerateTrimmableTypeMap.cs
Outdated
Show resolved
Hide resolved
ac38f79 to
90b195d
Compare
Extract the core generation pipeline (scan → typemaps → JCW → acw-map) from GenerateTrimmableTypeMap into a standalone TrimmableTypeMapGenerator class that takes Action<string> for logging, keeping the TrimmableTypeMap project free of Microsoft.Build.* dependencies. The MSBuild task becomes a thin adapter. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
90b195d to
21facfc
Compare
The per-assembly acw-map files are consumed by existing targets. Keep GeneratePerAssemblyAcwMaps in the task (it uses MSBuild types like MemoryStreamPool and Files) rather than moving the merged acw-map.txt logic to the generator, which is PR 4 work. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Restore GetJavaInteropAssemblyPaths (MonoAndroidHelper.IsMonoAndroidAssembly) filtering to match main's behavior — removing the filter is a build pipeline change that belongs in a later PR. - Add ArgumentNullException checks to TrimmableTypeMapGenerator.Execute(). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…d.Tests Move incremental build, parsing, and empty-input tests to TrimmableTypeMapGeneratorTests (xUnit, uses TestFixtures.dll). Keep MSBuild-specific tests (error handling, Mono.Android integration) in GenerateTrimmableTypeMapTests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The scanner needs all assemblies — not just those with HasMonoAndroidReference. Framework JCW pre-compilation is tracked by #10792; until then we generate JCWs for everything. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This is MSBuild input parsing, not core generator logic. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
6cf2c60 to
cd79c47
Compare
- Generator accepts (name, PEReader) pairs, returns in-memory content - MSBuild task owns all filesystem IO - Tests assert on in-memory content, no temp dirs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
6c20cd9 to
b70425f
Compare
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
b70425f to
7f8afd7
Compare
All IO is now exclusively in the MSBuild task. The generator library only exposes stream/content-based APIs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
24dd92f to
5a931c3
Compare
simonrozsival
left a comment
There was a problem hiding this comment.
🤖 AI Review Summary
Verdict:
Found 4 issues (0 errors, 2 warnings, 2 suggestions).
Overall this is a well-executed refactor. The generator is now a clean pure transformation and the IO boundary is clearly in the MSBuild task. The PEReader-based API, metadata-derived assembly names, and in-memory result types are all good design choices.
⚠️ Resource management:MemoryStreaminstances inGeneratedAssemblyare never disposed (TrimmableTypeMapGenerator.cs)⚠️ Performance: Incremental build optimization removed — every run now regenerates all assemblies (GenerateTrimmableTypeMap.cs)- 💡 Formatting: Indentation error on doc-comment line (
JcwJavaSourceGenerator.cs:63) - 💡 Code organization:
GenerateContentmissing XML doc-comment (JcwJavaSourceGenerator.cs:44)
👍 Good:
- Clean IO-free generator with pure transformation semantics
- Assembly name derived from PE metadata, not file stem — correct for cross-assembly resolution
- Result types use
record— good use of value equality Files.CopyIfStreamChangedused consistently for incremental-safe writes
Review generated by android-reviewer from review guidelines.
src/Xamarin.Android.Build.Tasks/Tasks/GenerateTrimmableTypeMap.cs
Outdated
Show resolved
Hide resolved
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/JcwJavaSourceGenerator.cs
Show resolved
Hide resolved
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/JcwJavaSourceGenerator.cs
Show resolved
Hide resolved
4fcdbd1 to
e3c2f79
Compare
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
e3c2f79 to
2e2461f
Compare
simonrozsival
left a comment
There was a problem hiding this comment.
🤖 AI Review Summary
Verdict:
Found 2 issues (0 errors, 1 warning, 1 suggestion):
⚠️ Testing: Several old task tests were removed but not replaced — incremental build tests, TFV parsing, no-peers-found (GenerateTrimmableTypeMapTests.cs)- 💡 CI: Linux build is failing (build 1358253) — Mac/Windows still pending. Worth investigating before merge.
👍 Positive callouts
- Clean IO separation — generator is fully in-memory, MSBuild task owns all filesystem operations
- No
null!anywhere PEReader-based API is a smart design choice — avoids stream ownership issuesFiles.CopyIfStreamChangedin the task prevents incremental build breakage- New unit tests exercise the generator directly with xUnit, no MSBuild ceremony
Review generated by android-reviewer from review guidelines.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TrimmableTypeMapGeneratorTests.cs
Show resolved
Hide resolved
- Restore Execute_SecondRun_OutputsAreUpToDate at task level (verifies Files.CopyIfStreamChanged does not rewrite unchanged outputs) - Restore Execute_ParsesTargetFrameworkVersion at task level - Add Execute_AssemblyWithNoPeers_ReturnsEmpty at generator level (uses test assembly itself which has no [Register] types) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
Extract the core generation pipeline (scan → typemaps → JCW → acw-map) from the
GenerateTrimmableTypeMapMSBuild task into a standaloneTrimmableTypeMapGeneratorclass. The generator is IO-free — it acceptsPEReaderinstances and returns in-memory content. The MSBuild task owns all filesystem IO.Motivation
The generator logic was tightly coupled to MSBuild types (
TaskLoggingHelper,ITaskItem) and performed filesystem IO directly (creating directories, checking timestamps, writing files). This made it hard to test without full MSBuild ceremony and temp directories. Extracting it with an IO-free API enables fast, focused unit tests using xUnit +TestFixtures.dllthat assert on in-memory content.Changes
New files
TrimmableTypeMapGenerator.cs— orchestrates scan → typemaps → JCW pipeline as a pure transformation:(name, PEReader)[]in →(GeneratedAssembly, GeneratedJavaSource)[]out. No filesystem IO.TrimmableTypeMapTypes.cs—TrimmableTypeMapResult,GeneratedAssembly(Name, MemoryStream),GeneratedJavaSource(RelativePath, Content)recordsNullableExtensions.cs— NRT-annotatedIsNullOrEmpty/IsNullOrWhiteSpacefor netstandard2.0TrimmableTypeMapGeneratorTests.cs— 6 xUnit tests covering the generator directly (empty input, no-peers output, null validation, PE validity, Java source structure). Tests are IO-free.Modified files
GenerateTrimmableTypeMap.cs— rewritten as IO adapter: createsPEReaderinstances from files, calls the generator, writes all outputs to disk usingFiles.CopyIfStreamChanged. Owns directory creation, assembly writing, Java source writing, acw-map generation, andMemoryStreamdisposal in afinallyblock.GenerateTrimmableTypeMapTests.cs— 5 task-level NUnit tests: empty list, invalid TFV, valid TFV parsing (v11.0/v10.0/11.0), Mono.Android integration, incremental up-to-date checkAssemblyIndex.cs— addedCreate(PEReader, string)overload, removedFilePathproperty, no longer ownsPEReaderdisposalJavaPeerScanner.cs— addedScan((string, PEReader)[])overload with sharedScanCore()JcwJavaSourceGenerator.cs— addedGenerateContent()returning(relativePath, content)pairs without filesystem writes, madeGenerate(type, TextWriter)publicPEAssemblyBuilder,RootTypeMapAssemblyGenerator,TypeMapAssemblyEmitter,TypeMapAssemblyGenerator— removed file-path writing overloads, stream-based output onlyDesign decisions
Action<string>instead ofTaskLoggingHelper— keeps TrimmableTypeMap project free of Microsoft.Build packagesPEReaderpairs (not file paths or raw streams) —PEReaderalready owns PE parsing; MSBuild task creates them from files, tests from in-memory bytesMemoryStreamoutputs are disposed in afinallyblock in the MSBuild taskFiles.CopyIfStreamChangedprevents unnecessary file writes (preserving downstream incrementality)