Skip to content

Replace trimmable manifest LINQ XML generation with lightweight XML model#11488

Draft
Copilot wants to merge 6 commits into
mainfrom
copilot/replace-xdocument-xelement-manifest
Draft

Replace trimmable manifest LINQ XML generation with lightweight XML model#11488
Copilot wants to merge 6 commits into
mainfrom
copilot/replace-xdocument-xelement-manifest

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 25, 2026

The trimmable typemap manifest path used System.Xml.Linq for template parsing, read/modify/write merging, rooting, and final output. This PR moves production manifest generation to a purpose-built XML model that stores only the manifest elements and attributes needed by the trimmable pipeline while preserving existing manifest behavior.

  • Manifest model

    • Adds a small ManifestDocument/element/attribute model.
    • Parses templates with XmlReader.
    • Keeps a defined subset of XML data instead of a full generic XML node tree.
    • Writes merged manifests with XmlWriter.
  • Generation pipeline

    • Updates GenerateTrimmableTypeMap, TrimmableTypeMapGenerator, and GeneratedManifest to pass ManifestDocument instead of XDocument.
    • Keeps output flowing through Files.CopyIfStreamChanged(...).
  • Manifest merge helpers

    • Converts component, assembly-level, and property mapping helpers from XElement mutation to lightweight model mutation.
    • Adds helpers for common element/android-attribute queries.
    • Preserves placeholder replacement, compat-name rewriting, duplicate detection, runtime provider generation, application attributes, and assembly-level manifest elements.
  • Rooting

    • Roots manifest-referenced peers from the lightweight model instead of walking an LINQ-to-XML DOM.

Example API shift:

ManifestDocument? manifestTemplate = null;
if (!ManifestTemplate.IsNullOrEmpty () && File.Exists (ManifestTemplate)) {
	manifestTemplate = ManifestDocument.Load (ManifestTemplate);
}

Copilot AI and others added 2 commits May 25, 2026 15:57
Agent-Logs-Url: https://github.com/dotnet/android/sessions/7c90b070-d2df-4124-bea8-b0a5d9070cd1

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Agent-Logs-Url: https://github.com/dotnet/android/sessions/7c90b070-d2df-4124-bea8-b0a5d9070cd1

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Copilot AI changed the title [WIP] Refactor manifest generation to use lower-allocation APIs Replace trimmable manifest LINQ XML generation with lightweight XML model May 25, 2026
Copilot AI requested a review from simonrozsival May 25, 2026 16:01
@simonrozsival
Copy link
Copy Markdown
Member

@copilot build fails:

...

       "/mnt/vss/_work/1/s/android/Xamarin.Android.sln" (default target) (1:2) ->
       "/mnt/vss/_work/1/s/android/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj" (default target) (11:10) ->
       (CoreCompile target) -> 
         /mnt/vss/_work/1/s/android/src/Xamarin.Android.Build.Tasks/Tasks/GenerateTrimmableTypeMap.cs(149,41): error CS0117: 'ManifestDocument' does not contain a definition for 'Load' [/mnt/vss/_work/1/s/android/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj]
         /mnt/vss/_work/1/s/android/src/Xamarin.Android.Build.Tasks/Tasks/GenerateTrimmableTypeMap.cs(158,23): error CS1503: Argument 6: cannot convert from 'Xamarin.Android.Tasks.ManifestDocument' to 'Microsoft.Android.Sdk.TrimmableTypeMap.ManifestDocument?' [/mnt/vss/_work/1/s/android/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj]

    200 Warning(s)
    2 Error(s)

Agent-Logs-Url: https://github.com/dotnet/android/sessions/c005d968-85d0-4e7f-bea9-62a5ef0d74c9

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 25, 2026

@copilot build fails:

...

...

Fixed in e6721fd by aliasing the trimmable manifest model type in GenerateTrimmableTypeMap, so it no longer resolves to Xamarin.Android.Tasks.ManifestDocument. Verified with dotnet build src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj --no-restore -p:BuildProjectReferences=false and the trimmable typemap test project.

Copy link
Copy Markdown
Member

@simonrozsival simonrozsival left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot please address these comments

app.Add (new XElement ("uses-library",
new XAttribute (AttName, ul.Name),
new XAttribute (AndroidNs + "required", ul.Required ? "true" : "false")));
if (!app.Elements ("uses-library").Any (e => e.AndroidAttribute (ManifestConstants.AttributeName) == ul.Name)) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're implementing a new class, we can add helper methods for common operations, such as HasElementWithAndroidAttribute("uses-library", ManifestConstants.AttributeName, ul.Name)

{
var existingPermissions = new HashSet<string> (
manifest.Elements ("permission").Select (e => (string?)e.Attribute (AttName)).OfType<string> ());
manifest.Elements ("permission").Select (e => e.AndroidAttribute (ManifestConstants.AttributeName)).OfType<string> ());
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be a helper method GetElementAttributeValues("permission", ManifestConstants.AttributeName)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just wondering if this is actually more efficient than XElement... it would be interesting to see these two implementations in a few BDN microbenchmarks (no need to include them in the PR, just report the difference in performance).

Agent-Logs-Url: https://github.com/dotnet/android/sessions/730ad033-35a2-44fe-8072-0a2e1f387b4b

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 26, 2026

@copilot please address these comments

Addressed in 9d2e430 by adding GetElementAttributeValues() and HasElementWithAndroidAttribute() helpers and using them in AssemblyLevelElementBuilder. I also ran a temporary local BDN parse/write comparison: lightweight was faster (10 components: 16.21µs vs 18.15µs; 100 components: 134.06µs vs 141.51µs) but allocated slightly more (38.36KB vs 36.46KB; 210.67KB vs 192.34KB).

@simonrozsival
Copy link
Copy Markdown
Member

@copilot this added complexity does not justify the very minor perf improvement. I wonder if we really need the read-modify-write flow everywhere and if it cannot be simplified. I think the problem is that we're storing the full XML tree instead of keeping just a well defined subset of the data.

Agent-Logs-Url: https://github.com/dotnet/android/sessions/59bef1f8-44dc-4e30-a3cc-b0082dd1a6b4

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 26, 2026

@copilot this added complexity does not justify the very minor perf improvement. I wonder if we really need the read-modify-write flow everywhere and if it cannot be simplified. I think the problem is...

Addressed in 22c6763 by simplifying the model so it no longer stores a generic XML node tree: it now keeps only manifest elements and attributes, dropping the text/comment/CDATA node hierarchy. Targeted build and trimmable typemap tests pass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[TrimmableTypeMap] Replace XDocument/XElement manifest generation with lower-allocation APIs

2 participants