Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 578
[TrimmableTypeMap] Extract TrimmableTypeMapGenerator from MSBuild task#11034
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
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
a399675bcde9df24d340ce3553d56b6ff3db9ce5c67a66696dc478cf4342ba88aaf126fab1cc26791efb5d596eaba00b017d93affFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -42,43 +42,26 @@ namespace Microsoft.Android.Sdk.TrimmableTypeMap; | ||
| public sealed class JcwJavaSourceGenerator | ||
| { | ||
| /// <summary> | ||
| /// Generates .java source files for all ACW types and writes them to the output directory. | ||
| /// Returns the list of generated file paths. | ||
| /// Generates .java source content for all ACW types and returns them as in-memory | ||
| /// (relativePath, content) pairs. No filesystem IO is performed. | ||
| /// </summary> | ||
| public IReadOnlyList<string> Generate (IReadOnlyList<JavaPeerInfo> types, string outputDirectory) | ||
| public IReadOnlyList<GeneratedJavaSource> GenerateContent (IReadOnlyList<JavaPeerInfo> types) | ||
| { | ||
| if (types is null) { | ||
| throw new ArgumentNullException (nameof (types)); | ||
| } | ||
| if (outputDirectory is null) { | ||
| throw new ArgumentNullException (nameof (outputDirectory)); | ||
| } | ||
| var generatedFiles = new List<string> (); | ||
| if (types is null) throw new ArgumentNullException (nameof (types)); | ||
| var results = new List<GeneratedJavaSource> (); | ||
| foreach (var type in types) { | ||
| if (type.DoNotGenerateAcw || type.IsInterface) { | ||
| continue; | ||
| } | ||
| string filePath = GetOutputFilePath (type, outputDirectory); | ||
| string? dir = Path.GetDirectoryName (filePath); | ||
| if (dir != null) { | ||
| Directory.CreateDirectory (dir); | ||
| } | ||
| using var writer = new StreamWriter (filePath); | ||
| if (type.DoNotGenerateAcw || type.IsInterface) continue; | ||
| using var writer = new StringWriter (); | ||
| Generate (type, writer); | ||
| generatedFiles.Add (filePath); | ||
| results.Add (new GeneratedJavaSource (GetRelativePath (type), writer.ToString ())); | ||
| } | ||
| return generatedFiles; | ||
| return results; | ||
| } | ||
| /// <summary> | ||
| /// Generates a single .java source file for the given type. | ||
| /// </summary> | ||
| internal void Generate (JavaPeerInfo type, TextWriter writer) | ||
| public void Generate (JavaPeerInfo type, TextWriter writer) | ||
| { | ||
| writer.NewLine = "\n"; | ||
| WritePackageDeclaration (type, writer); | ||
simonrozsival marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| @@ -91,13 +74,13 @@ internal void Generate (JavaPeerInfo type, TextWriter writer) | ||
| WriteClassClose (writer); | ||
| } | ||
| static string GetOutputFilePath (JavaPeerInfo type, string outputDirectory) | ||
| static string GetRelativePath (JavaPeerInfo type) | ||
| { | ||
| JniSignatureHelper.ValidateJniName (type.JavaName); | ||
| string relativePath = type.JavaName + ".java"; | ||
| return Path.Combine (outputDirectory, relativePath); | ||
| return type.JavaName + ".java"; | ||
| } | ||
| /// <summary> | ||
| /// Validates that the JNI name is well-formed: non-empty, each segment separated by '/' | ||
| /// contains only valid Java identifier characters (letters, digits, '_', '$'). | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| using System.Diagnostics.CodeAnalysis; | ||
| namespace Microsoft.Android.Sdk.TrimmableTypeMap; | ||
| // The static methods in System.String are not NRT annotated in netstandard2.0, | ||
| // so we need our own extension methods to make them nullable aware. | ||
| static class NullableExtensions | ||
| { | ||
| public static bool IsNullOrEmpty ([NotNullWhen (false)] this string? str) | ||
| { | ||
| return string.IsNullOrEmpty (str); | ||
| } | ||
| public static bool IsNullOrWhiteSpace ([NotNullWhen (false)] this string? str) | ||
| { | ||
| return string.IsNullOrWhiteSpace (str); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.IO; | ||
| using System.Linq; | ||
| using System.Reflection.PortableExecutable; | ||
| namespace Microsoft.Android.Sdk.TrimmableTypeMap; | ||
| public class TrimmableTypeMapGenerator | ||
| { | ||
| readonly Action<string> log; | ||
| public TrimmableTypeMapGenerator (Action<string> log) | ||
| { | ||
| this.log = log ?? throw new ArgumentNullException (nameof (log)); | ||
| } | ||
| public TrimmableTypeMapResult Execute ( | ||
| IReadOnlyList<(string Name, PEReader Reader)> assemblies, | ||
| Version systemRuntimeVersion, | ||
| HashSet<string> frameworkAssemblyNames) | ||
| { | ||
simonrozsival marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| _ = assemblies ?? throw new ArgumentNullException (nameof (assemblies)); | ||
| _ = systemRuntimeVersion ?? throw new ArgumentNullException (nameof (systemRuntimeVersion)); | ||
| _ = frameworkAssemblyNames ?? throw new ArgumentNullException (nameof (frameworkAssemblyNames)); | ||
| var allPeers = ScanAssemblies (assemblies); | ||
| if (allPeers.Count == 0) { | ||
| log ("No Java peer types found, skipping typemap generation."); | ||
| return new TrimmableTypeMapResult ([], [], allPeers); | ||
| } | ||
| var generatedAssemblies = GenerateTypeMapAssemblies (allPeers, systemRuntimeVersion); | ||
| var jcwPeers = allPeers.Where (p => | ||
| !frameworkAssemblyNames.Contains (p.AssemblyName) | ||
| || p.JavaName.StartsWith ("mono/", StringComparison.Ordinal)).ToList (); | ||
| log ($"Generating JCW files for {jcwPeers.Count} types (filtered from {allPeers.Count} total)."); | ||
| var generatedJavaSources = GenerateJcwJavaSources (jcwPeers); | ||
| return new TrimmableTypeMapResult (generatedAssemblies, generatedJavaSources, allPeers); | ||
| } | ||
| List<JavaPeerInfo> ScanAssemblies (IReadOnlyList<(string Name, PEReader Reader)> assemblies) | ||
| { | ||
| using var scanner = new JavaPeerScanner (); | ||
| var peers = scanner.Scan (assemblies); | ||
| log ($"Scanned {assemblies.Count} assemblies, found {peers.Count} Java peer types."); | ||
| return peers; | ||
| } | ||
| List<GeneratedAssembly> GenerateTypeMapAssemblies (List<JavaPeerInfo> allPeers, Version systemRuntimeVersion) | ||
| { | ||
| var peersByAssembly = allPeers.GroupBy (p => p.AssemblyName, StringComparer.Ordinal).OrderBy (g => g.Key, StringComparer.Ordinal); | ||
| var generatedAssemblies = new List<GeneratedAssembly> (); | ||
| var perAssemblyNames = new List<string> (); | ||
| var generator = new TypeMapAssemblyGenerator (systemRuntimeVersion); | ||
| foreach (var group in peersByAssembly) { | ||
| string assemblyName = $"_{group.Key}.TypeMap"; | ||
| perAssemblyNames.Add (assemblyName); | ||
| var peers = group.ToList (); | ||
| var stream = new MemoryStream (); | ||
| generator.Generate (peers, stream, assemblyName); | ||
| stream.Position = 0; | ||
| generatedAssemblies.Add (new GeneratedAssembly (assemblyName, stream)); | ||
| log ($" {assemblyName}: {peers.Count} types"); | ||
simonrozsival marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } | ||
| var rootStream = new MemoryStream (); | ||
| var rootGenerator = new RootTypeMapAssemblyGenerator (systemRuntimeVersion); | ||
| rootGenerator.Generate (perAssemblyNames, rootStream); | ||
| rootStream.Position = 0; | ||
| generatedAssemblies.Add (new GeneratedAssembly ("_Microsoft.Android.TypeMaps", rootStream)); | ||
| log ($" Root: {perAssemblyNames.Count} per-assembly refs"); | ||
| log ($"Generated {generatedAssemblies.Count} typemap assemblies."); | ||
| return generatedAssemblies; | ||
| } | ||
| List<GeneratedJavaSource> GenerateJcwJavaSources (List<JavaPeerInfo> allPeers) | ||
| { | ||
| var jcwGenerator = new JcwJavaSourceGenerator (); | ||
| var sources = jcwGenerator.GenerateContent (allPeers); | ||
| log ($"Generated {sources.Count} JCW Java source files."); | ||
| return sources.ToList (); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| using System.Collections.Generic; | ||
| using System.IO; | ||
| namespace Microsoft.Android.Sdk.TrimmableTypeMap; | ||
| public record TrimmableTypeMapResult ( | ||
| IReadOnlyList<GeneratedAssembly> GeneratedAssemblies, | ||
| IReadOnlyList<GeneratedJavaSource> GeneratedJavaSources, | ||
| IReadOnlyList<JavaPeerInfo> AllPeers); | ||
| public record GeneratedAssembly (string Name, MemoryStream Content); | ||
| public record GeneratedJavaSource (string RelativePath, string Content); |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.