-
Notifications
You must be signed in to change notification settings - Fork 566
[TrimmableTypeMap] Root manifest-referenced types as unconditional #11037
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev/simonrozsival/trimmable-typemap-build-pipeline-v2
Are you sure you want to change the base?
Changes from all commits
ec076db
820f748
a36a8a0
266703c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,10 +10,12 @@ namespace Microsoft.Android.Sdk.TrimmableTypeMap; | |
| public class TrimmableTypeMapGenerator | ||
| { | ||
| readonly Action<string> log; | ||
| readonly Action<string>? warn; | ||
|
|
||
| public TrimmableTypeMapGenerator (Action<string> log) | ||
| public TrimmableTypeMapGenerator (Action<string> log, Action<string>? warn = null) | ||
| { | ||
| this.log = log ?? throw new ArgumentNullException (nameof (log)); | ||
| this.warn = warn; | ||
| } | ||
|
|
||
| /// <summary> | ||
|
|
@@ -38,6 +40,8 @@ public TrimmableTypeMapResult Execute ( | |
| return new TrimmableTypeMapResult ([], [], allPeers); | ||
| } | ||
|
|
||
| RootManifestReferencedTypes (allPeers, manifestTemplate); | ||
|
|
||
| var generatedAssemblies = GenerateTypeMapAssemblies (allPeers, systemRuntimeVersion); | ||
| var jcwPeers = allPeers.Where (p => | ||
| !frameworkAssemblyNames.Contains (p.AssemblyName) | ||
|
|
@@ -142,4 +146,76 @@ List<GeneratedJavaSource> GenerateJcwJavaSources (List<JavaPeerInfo> allPeers) | |
| log ($"Generated {sources.Count} JCW Java source files."); | ||
| return sources.ToList (); | ||
| } | ||
|
|
||
| internal void RootManifestReferencedTypes (List<JavaPeerInfo> allPeers, XDocument? doc) | ||
| { | ||
| if (doc?.Root is not { } root) { | ||
| return; | ||
| } | ||
|
|
||
| XNamespace androidNs = "http://schemas.android.com/apk/res/android"; | ||
| XName attName = androidNs + "name"; | ||
| var packageName = (string?) root.Attribute ("package") ?? ""; | ||
|
|
||
| var componentNames = new HashSet<string> (StringComparer.Ordinal); | ||
| foreach (var element in root.Descendants ()) { | ||
| switch (element.Name.LocalName) { | ||
| case "activity": | ||
| case "service": | ||
| case "receiver": | ||
| case "provider": | ||
| var name = (string?) element.Attribute (attName); | ||
| if (name is not null) { | ||
| componentNames.Add (ResolveManifestClassName (name, packageName)); | ||
| } | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| if (componentNames.Count == 0) { | ||
| return; | ||
| } | ||
|
|
||
| // Build lookup by dot-name, keeping '$' for nested types (manifests use '$' too). | ||
| var peersByDotName = new Dictionary<string, List<JavaPeerInfo>> (StringComparer.Ordinal); | ||
| foreach (var peer in allPeers) { | ||
| var dotName = peer.JavaName.Replace ('/', '.'); | ||
| if (!peersByDotName.TryGetValue (dotName, out var list)) { | ||
| list = []; | ||
| peersByDotName [dotName] = list; | ||
| } | ||
| list.Add (peer); | ||
| } | ||
|
Comment on lines
+180
to
+188
|
||
|
|
||
| foreach (var name in componentNames) { | ||
| if (peersByDotName.TryGetValue (name, out var peers)) { | ||
| foreach (var peer in peers) { | ||
| if (!peer.IsUnconditional) { | ||
| peer.IsUnconditional = true; | ||
| log ($"Rooting manifest-referenced type '{name}' ({peer.ManagedTypeName}) as unconditional."); | ||
| } | ||
| } | ||
| } else { | ||
| warn?.Invoke ($"Manifest-referenced type '{name}' was not found in any scanned assembly. It may be a framework type."); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Resolves an android:name value to a fully-qualified class name. | ||
| /// Names starting with '.' are relative to the package. Names with no '.' at all | ||
| /// are also treated as relative (Android tooling convention). | ||
| /// </summary> | ||
| static string ResolveManifestClassName (string name, string packageName) | ||
| { | ||
| if (name.StartsWith (".", StringComparison.Ordinal)) { | ||
| return packageName + name; | ||
| } | ||
|
|
||
| if (name.IndexOf ('.') < 0 && !packageName.IsNullOrEmpty ()) { | ||
| return packageName + "." + name; | ||
| } | ||
|
|
||
| return name; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤖 💡 API design — Changing
IsUnconditionalfrominittosetmakes the record mutable after construction. The doc-comment update explains the "why" well. Consider adding a brief note in the doc-comment that callers should only set this totrue(never back tofalse) to prevent accidental unrooting.Rule: Document array mutability semantics (Postmortem
#66)