Skip to content

Commit c2bdc5b

Browse files
simonrozsivalCopilotjonathanpeppers
authored
[TrimmableTypeMap] Build pipeline: targets, stubs, manifest integration, feature switches (#11036)
* PR 4: Build pipeline changes for trimmable typemap - Update TrimmableTypeMapGenerator.Execute() with manifest generation, assembly manifest scanning, acw-map writing, and new optional parameters - Add ManifestConfig record to TrimmableTypeMapTypes.cs - Update TrimmableTypeMapResult with AdditionalProviderSources - Update GenerateTrimmableTypeMap MSBuild task with manifest/config properties - Create GenerateEmptyTypemapStub task for LLVM IR native typemap stubs - Create ApplicationRegistration.Trimmable.java (empty registerApplications) - Create Trimmable.CoreCLR.xml preserve list for JNIEnvInit.Initialize - Rewrite Trimmable.targets with full build pipeline (separate generation and _GenerateJavaStubs targets, native stub generation, manifest handling) - Rewrite Trimmable.CoreCLR.targets with ILLink integration and per-ABI assembly store support - Add TrimmableTypeMap=false feature switch to MonoVM and NativeAOT targets - Handle trimmed JNIEnvInit tokens in GenerateNativeApplicationConfigSources Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address review: remove #nullable enable, use IsNullOrEmpty extension - Remove redundant #nullable enable from 6 Generator files and task file - Convert string.IsNullOrEmpty to IsNullOrEmpty extension in task Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix _PrepareNativeAssemblySources Outputs typo (typemaps → typemap) The target declared Outputs as typemaps.{abi}.ll but GenerateEmptyTypemapStub writes typemap.{abi}.ll. The mismatch caused MSBuild to always rerun the target. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix CS1503: load XDocument from manifest path, guard GetDirectoryName null - Load manifestTemplatePath into XDocument before passing to ManifestGenerator.Generate() which expects XDocument? (not string) - Guard Path.GetDirectoryName() result with IsNullOrEmpty check before passing to Directory.CreateDirectory() Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Move all IO from TrimmableTypeMapGenerator to MSBuild task The previous PR (#11034) established the pattern that TTMG has no IO operations. The build pipeline PR regressed this by adding acw-map writing, manifest template loading, and manifest file saving into the generator. Move all IO back to GenerateTrimmableTypeMap (MSBuild task): - Load manifest template XDocument from file path - Write merged manifest to disk via XDocument.Save() - Write merged acw-map.txt via Files.CopyIfStreamChanged() Refactor ManifestGenerator.Generate to return (XDocument, IList<string>) instead of writing to disk. Add GeneratedManifest record to carry the in-memory result. Update ManifestGeneratorTests to work entirely in-memory (no temp dirs, no IDisposable). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Generate ApplicationRegistration.java with registerNatives for App/Instrumentation types Application and Instrumentation types skip the JCW static initializer block (CannotRegisterInStaticCtor) because libmonodroid.so isn't loaded when these classes are first instantiated by the Android framework. The legacy path deferred their registration to the generated ApplicationRegistration.registerApplications() method. The trimmable path incorrectly used an empty static file, meaning these types would never get their native methods registered → UnsatisfiedLinkError. Fix: dynamically generate ApplicationRegistration.java in the GenerateTrimmableTypeMap MSBuild task with mono.android.Runtime.registerNatives(MyApp.class); for each Application/Instrumentation type. This triggers the trimmable TrimmableTypeMap.OnRegisterNatives handler which dispatches to the UCO-generated IAndroidCallableWrapper.RegisterNatives. Call graph: MonoPackageManager.LoadApplication() → Runtime.initInternal() → ApplicationRegistration.registerApplications() → Runtime.registerNatives(MyApp.class) [generated] → OnRegisterNatives (JNI native) [TrimmableTypeMap.cs] → IAndroidCallableWrapper.RegisterNatives() [UCO IL] → Application.onCreate() → n_onCreate() [now registered] Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address trimmable typemap review feedback Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix CS0246/CS1503: add missing 'using System.Xml.Linq' to GenerateTrimmableTypeMap.cs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Restore pre-existing feature switches in NativeAOT.targets; fix null! in TrimmableTypeMapTypes - Restore IsMonoRuntime=false and IsCoreClrRuntime=false RuntimeHostConfigurationOption items accidentally removed from NativeAOT.targets - Change ApplicationRegistrationTypes parameter from null! to nullable (null?) to comply with the no null-forgiving operator rule Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address review: fix manifest save, remove dead PerAssemblyAcwMaps, style - Use Files.CopyIfStringChanged instead of XDocument.Save() to preserve manifest file timestamp when content is unchanged (fixes incremental builds) - Remove dead PerAssemblyAcwMapFiles [Output] property, GeneratePerAssemblyAcwMaps() method, and AcwMapDirectory [Required] property — the only consumer (_MergeAcwMaps) was removed in this PR; nothing reads the per-assembly acw-map files anymore - Add 'using System.Text' and use short StringBuilder form Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: use MemoryStream to preserve XML declaration in manifest output XDocument.ToString() drops the <?xml version="1.0" encoding="utf-8"?> declaration. Use XDocument.Save(Stream) + Files.CopyIfStreamChanged to preserve both the XML declaration and incremental-build safety. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update BuildReleaseArm64SimpleDotNet.MonoVM.apkdesc from CI Updated sizes from macOS > Tests > MSBuild 3 build artifacts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix indentation in GenerateTrimmableTypeMap.cs Address code review feedback: properly indent the entire file using tabs per the repo's .editorconfig convention (1 tab for class members, 2 tabs for method body, etc.). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Jonathan Peppers <jonathan.peppers@microsoft.com>
1 parent 77f88ea commit c2bdc5b

20 files changed

Lines changed: 735 additions & 329 deletions

src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/AcwMapWriter.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#nullable enable
21
using System;
32
using System.Collections.Generic;
43
using System.IO;
@@ -29,10 +28,10 @@ public static class AcwMapWriter
2928
public static void Write (TextWriter writer, IEnumerable<JavaPeerInfo> peers)
3029
{
3130
foreach (var peer in peers.OrderBy (p => p.ManagedTypeName, StringComparer.Ordinal)) {
32-
string javaKey = peer.JavaName.Replace ('/', '.');
31+
string javaKey = JniSignatureHelper.JniNameToJavaName (peer.JavaName);
3332
string managedKey = peer.ManagedTypeName;
3433
string partialAsmQualifiedName = $"{managedKey}, {peer.AssemblyName}";
35-
string compatJniName = peer.CompatJniName.Replace ('/', '.');
34+
string compatJniName = JniSignatureHelper.JniNameToJavaName (peer.CompatJniName);
3635

3736
// Line 1: PartialAssemblyQualifiedName;JavaKey
3837
writer.Write (partialAsmQualifiedName);

src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/AndroidEnumConverter.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
#nullable enable
2-
31
using System.Collections.Generic;
42

53
namespace Microsoft.Android.Sdk.TrimmableTypeMap;

src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ComponentElementBuilder.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
#nullable enable
2-
31
using System;
42
using System.Globalization;
53
using System.Linq;
@@ -157,7 +155,7 @@ internal static XElement CreateMetaDataElement (MetaDataInfo meta)
157155

158156
internal static void UpdateApplicationElement (XElement app, JavaPeerInfo peer)
159157
{
160-
string jniName = peer.JavaName.Replace ('/', '.');
158+
string jniName = JniSignatureHelper.JniNameToJavaName (peer.JavaName);
161159
app.SetAttributeValue (AttName, jniName);
162160

163161
var component = peer.ComponentAttribute;
@@ -169,7 +167,7 @@ internal static void UpdateApplicationElement (XElement app, JavaPeerInfo peer)
169167

170168
internal static void AddInstrumentation (XElement manifest, JavaPeerInfo peer)
171169
{
172-
string jniName = peer.JavaName.Replace ('/', '.');
170+
string jniName = JniSignatureHelper.JniNameToJavaName (peer.JavaName);
173171
var element = new XElement ("instrumentation",
174172
new XAttribute (AttName, jniName));
175173

src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/JcwJavaSourceGenerator.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,8 @@ static void WriteStaticInitializer (JavaPeerInfo type, TextWriter writer)
124124
{
125125
// Application and Instrumentation types cannot call registerNatives in their
126126
// static initializer — the native library isn't loaded yet at that point.
127-
// Their registration is deferred to ApplicationRegistration.registerApplications().
127+
// Their registerNatives call is emitted in the generated
128+
// ApplicationRegistration.registerApplications() method instead.
128129
if (type.CannotRegisterInStaticConstructor) {
129130
return;
130131
}

src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ManifestConstants.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
#nullable enable
2-
31
using System.Xml.Linq;
42

53
namespace Microsoft.Android.Sdk.TrimmableTypeMap;

src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ManifestGenerator.cs

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.IO;
43
using System.Linq;
54
using System.Xml.Linq;
65

@@ -23,9 +22,9 @@ class ManifestGenerator
2322
public string ApplicationLabel { get; set; } = "";
2423
public string VersionCode { get; set; } = "";
2524
public string VersionName { get; set; } = "";
26-
public string MinSdkVersion { get; set; } = "21";
27-
public string TargetSdkVersion { get; set; } = "36";
28-
public string AndroidRuntime { get; set; } = "coreclr";
25+
public string MinSdkVersion { get; set; } = "";
26+
public string TargetSdkVersion { get; set; } = "";
27+
public string RuntimeProviderJavaName { get; set; } = "";
2928
public bool Debug { get; set; }
3029
public bool NeedsInternet { get; set; }
3130
public bool EmbedAssemblies { get; set; }
@@ -39,11 +38,10 @@ class ManifestGenerator
3938
/// Generates the merged manifest from an optional pre-loaded template and writes it to <paramref name="outputPath"/>.
4039
/// Returns the list of additional content provider names (for ApplicationRegistration.java).
4140
/// </summary>
42-
public IList<string> Generate (
41+
public (XDocument Document, IList<string> ProviderNames) Generate (
4342
XDocument? manifestTemplate,
4443
IReadOnlyList<JavaPeerInfo> allPeers,
45-
AssemblyManifestInfo assemblyInfo,
46-
string outputPath)
44+
AssemblyManifestInfo assemblyInfo)
4745
{
4846
var doc = manifestTemplate ?? CreateDefaultManifest ();
4947
var manifest = doc.Root;
@@ -79,7 +77,7 @@ public IList<string> Generate (
7977
continue;
8078
}
8179

82-
string jniName = peer.JavaName.Replace ('/', '.');
80+
string jniName = JniSignatureHelper.JniNameToJavaName (peer.JavaName);
8381
if (existingTypes.Contains (jniName)) {
8482
continue;
8583
}
@@ -123,14 +121,7 @@ public IList<string> Generate (
123121
ApplyPlaceholders (doc, placeholders);
124122
}
125123

126-
// Write output
127-
var outputDir = Path.GetDirectoryName (outputPath);
128-
if (outputDir is not null) {
129-
Directory.CreateDirectory (outputDir);
130-
}
131-
doc.Save (outputPath);
132-
133-
return providerNames;
124+
return (doc, providerNames);
134125
}
135126

136127
XDocument CreateDefaultManifest ()
@@ -162,6 +153,12 @@ void EnsureManifestAttributes (XElement manifest)
162153

163154
// Add <uses-sdk>
164155
if (!manifest.Elements ("uses-sdk").Any ()) {
156+
if (MinSdkVersion.IsNullOrEmpty ()) {
157+
throw new InvalidOperationException ("MinSdkVersion must be provided by MSBuild.");
158+
}
159+
if (TargetSdkVersion.IsNullOrEmpty ()) {
160+
throw new InvalidOperationException ("TargetSdkVersion must be provided by MSBuild.");
161+
}
165162
manifest.AddFirst (new XElement ("uses-sdk",
166163
new XAttribute (AndroidNs + "minSdkVersion", MinSdkVersion),
167164
new XAttribute (AndroidNs + "targetSdkVersion", TargetSdkVersion)));
@@ -185,16 +182,19 @@ XElement EnsureApplicationElement (XElement manifest)
185182

186183
IList<string> AddRuntimeProviders (XElement app)
187184
{
188-
string packageName = "mono";
189-
string className = "MonoRuntimeProvider";
190-
191-
if (string.Equals (AndroidRuntime, "nativeaot", StringComparison.OrdinalIgnoreCase)) {
192-
packageName = "net.dot.jni.nativeaot";
193-
className = "NativeAotRuntimeProvider";
185+
if (RuntimeProviderJavaName.IsNullOrEmpty ()) {
186+
throw new InvalidOperationException ("RuntimeProviderJavaName must be provided by MSBuild.");
194187
}
188+
int lastDot = RuntimeProviderJavaName.LastIndexOf ('.');
189+
if (lastDot < 0 || lastDot == RuntimeProviderJavaName.Length - 1) {
190+
throw new InvalidOperationException ($"RuntimeProviderJavaName must be a fully-qualified Java type name: '{RuntimeProviderJavaName}'.");
191+
}
192+
193+
string packageName = RuntimeProviderJavaName.Substring (0, lastDot);
194+
string className = RuntimeProviderJavaName.Substring (lastDot + 1);
195195

196196
// Check if runtime provider already exists in template
197-
string runtimeProviderName = $"{packageName}.{className}";
197+
string runtimeProviderName = RuntimeProviderJavaName;
198198
if (!app.Elements ("provider").Any (p => {
199199
var name = (string?)p.Attribute (ManifestConstants.AttName);
200200
return name == runtimeProviderName ||

src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/PropertyMapper.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
#nullable enable
2-
31
using System;
42
using System.Collections.Generic;
53
using System.Globalization;

src/Microsoft.Android.Sdk.TrimmableTypeMap/TrimmableTypeMapGenerator.cs

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.IO;
44
using System.Linq;
55
using System.Reflection.PortableExecutable;
6+
using System.Xml.Linq;
67

78
namespace Microsoft.Android.Sdk.TrimmableTypeMap;
89

@@ -15,16 +16,23 @@ public TrimmableTypeMapGenerator (Action<string> log)
1516
this.log = log ?? throw new ArgumentNullException (nameof (log));
1617
}
1718

19+
/// <summary>
20+
/// Runs the full generation pipeline: scan assemblies, generate typemap
21+
/// assemblies, generate JCW Java sources, and optionally generate a merged manifest.
22+
/// No file IO is performed — all results are returned in memory.
23+
/// </summary>
1824
public TrimmableTypeMapResult Execute (
1925
IReadOnlyList<(string Name, PEReader Reader)> assemblies,
2026
Version systemRuntimeVersion,
21-
HashSet<string> frameworkAssemblyNames)
27+
HashSet<string> frameworkAssemblyNames,
28+
ManifestConfig? manifestConfig = null,
29+
XDocument? manifestTemplate = null)
2230
{
2331
_ = assemblies ?? throw new ArgumentNullException (nameof (assemblies));
2432
_ = systemRuntimeVersion ?? throw new ArgumentNullException (nameof (systemRuntimeVersion));
2533
_ = frameworkAssemblyNames ?? throw new ArgumentNullException (nameof (frameworkAssemblyNames));
2634

27-
var allPeers = ScanAssemblies (assemblies);
35+
var (allPeers, assemblyManifestInfo) = ScanAssemblies (assemblies);
2836
if (allPeers.Count == 0) {
2937
log ("No Java peer types found, skipping typemap generation.");
3038
return new TrimmableTypeMapResult ([], [], allPeers);
@@ -36,15 +44,66 @@ public TrimmableTypeMapResult Execute (
3644
|| p.JavaName.StartsWith ("mono/", StringComparison.Ordinal)).ToList ();
3745
log ($"Generating JCW files for {jcwPeers.Count} types (filtered from {allPeers.Count} total).");
3846
var generatedJavaSources = GenerateJcwJavaSources (jcwPeers);
39-
return new TrimmableTypeMapResult (generatedAssemblies, generatedJavaSources, allPeers);
47+
48+
// Collect Application/Instrumentation types that need deferred registerNatives
49+
var appRegTypes = allPeers
50+
.Where (p => p.CannotRegisterInStaticConstructor && !p.IsAbstract)
51+
.Select (p => JniSignatureHelper.JniNameToJavaName (p.JavaName))
52+
.ToList ();
53+
if (appRegTypes.Count > 0) {
54+
log ($"Found {appRegTypes.Count} Application/Instrumentation types for deferred registration.");
55+
}
56+
57+
var manifest = manifestConfig is not null
58+
? GenerateManifest (allPeers, assemblyManifestInfo, manifestConfig, manifestTemplate)
59+
: null;
60+
61+
return new TrimmableTypeMapResult (generatedAssemblies, generatedJavaSources, allPeers, manifest, appRegTypes);
62+
}
63+
64+
GeneratedManifest GenerateManifest (List<JavaPeerInfo> allPeers, AssemblyManifestInfo assemblyManifestInfo,
65+
ManifestConfig config, XDocument? manifestTemplate)
66+
{
67+
string minSdk = config.SupportedOSPlatformVersion ?? throw new InvalidOperationException ("SupportedOSPlatformVersion must be provided by MSBuild.");
68+
if (Version.TryParse (minSdk, out var sopv)) {
69+
minSdk = sopv.Major.ToString (System.Globalization.CultureInfo.InvariantCulture);
70+
}
71+
72+
string targetSdk = config.AndroidApiLevel ?? throw new InvalidOperationException ("AndroidApiLevel must be provided by MSBuild.");
73+
if (Version.TryParse (targetSdk, out var apiVersion)) {
74+
targetSdk = apiVersion.Major.ToString (System.Globalization.CultureInfo.InvariantCulture);
75+
}
76+
77+
bool forceDebuggable = !config.CheckedBuild.IsNullOrEmpty ();
78+
79+
var generator = new ManifestGenerator {
80+
PackageName = config.PackageName,
81+
ApplicationLabel = config.ApplicationLabel ?? config.PackageName,
82+
VersionCode = config.VersionCode ?? "",
83+
VersionName = config.VersionName ?? "",
84+
MinSdkVersion = minSdk,
85+
TargetSdkVersion = targetSdk,
86+
RuntimeProviderJavaName = config.RuntimeProviderJavaName ?? throw new InvalidOperationException ("RuntimeProviderJavaName must be provided by MSBuild."),
87+
Debug = config.Debug,
88+
NeedsInternet = config.NeedsInternet,
89+
EmbedAssemblies = config.EmbedAssemblies,
90+
ForceDebuggable = forceDebuggable,
91+
ForceExtractNativeLibs = forceDebuggable,
92+
ManifestPlaceholders = config.ManifestPlaceholders,
93+
ApplicationJavaClass = config.ApplicationJavaClass,
94+
};
95+
96+
var (doc, providerNames) = generator.Generate (manifestTemplate, allPeers, assemblyManifestInfo);
97+
return new GeneratedManifest (doc, providerNames.Count > 0 ? providerNames.ToArray () : []);
4098
}
4199

42-
List<JavaPeerInfo> ScanAssemblies (IReadOnlyList<(string Name, PEReader Reader)> assemblies)
100+
(List<JavaPeerInfo> peers, AssemblyManifestInfo manifestInfo) ScanAssemblies (IReadOnlyList<(string Name, PEReader Reader)> assemblies)
43101
{
44102
using var scanner = new JavaPeerScanner ();
45103
var peers = scanner.Scan (assemblies);
104+
var manifestInfo = scanner.ScanAssemblyManifestInfo ();
46105
log ($"Scanned {assemblies.Count} assemblies, found {peers.Count} Java peer types.");
47-
return peers;
106+
return (peers, manifestInfo);
48107
}
49108

50109
List<GeneratedAssembly> GenerateTypeMapAssemblies (List<JavaPeerInfo> allPeers, Version systemRuntimeVersion)
Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,49 @@
11
using System.Collections.Generic;
22
using System.IO;
3+
using System.Xml.Linq;
34

45
namespace Microsoft.Android.Sdk.TrimmableTypeMap;
56

67
public record TrimmableTypeMapResult (
78
IReadOnlyList<GeneratedAssembly> GeneratedAssemblies,
89
IReadOnlyList<GeneratedJavaSource> GeneratedJavaSources,
9-
IReadOnlyList<JavaPeerInfo> AllPeers);
10+
IReadOnlyList<JavaPeerInfo> AllPeers,
11+
GeneratedManifest? Manifest = null,
12+
IReadOnlyList<string>? ApplicationRegistrationTypes = null)
13+
{
14+
/// <summary>
15+
/// Java class names (dot-separated) of Application/Instrumentation types
16+
/// that need deferred <c>Runtime.registerNatives()</c> calls in
17+
/// <c>ApplicationRegistration.registerApplications()</c>.
18+
/// </summary>
19+
public IReadOnlyList<string> ApplicationRegistrationTypes { get; init; } =
20+
ApplicationRegistrationTypes ?? [];
21+
}
1022

1123
public record GeneratedAssembly (string Name, MemoryStream Content);
1224

1325
public record GeneratedJavaSource (string RelativePath, string Content);
26+
27+
/// <summary>
28+
/// The in-memory result of manifest generation: the merged document and
29+
/// any additional content provider class names for ApplicationRegistration.java.
30+
/// </summary>
31+
public record GeneratedManifest (XDocument Document, string[] AdditionalProviderSources);
32+
33+
/// <summary>
34+
/// Configuration values for manifest generation. Passed from MSBuild properties.
35+
/// </summary>
36+
public record ManifestConfig (
37+
string PackageName,
38+
string? ApplicationLabel = null,
39+
string? VersionCode = null,
40+
string? VersionName = null,
41+
string? AndroidApiLevel = null,
42+
string? SupportedOSPlatformVersion = null,
43+
string? RuntimeProviderJavaName = null,
44+
bool Debug = false,
45+
bool NeedsInternet = false,
46+
bool EmbedAssemblies = false,
47+
string? ManifestPlaceholders = null,
48+
string? CheckedBuild = null,
49+
string? ApplicationJavaClass = null);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<linker>
3+
<assembly fullname="Mono.Android">
4+
<!-- Native entry point: called from host.cc via create_delegate -->
5+
<type fullname="Android.Runtime.JNIEnvInit">
6+
<method name="Initialize" />
7+
</type>
8+
</assembly>
9+
</linker>

0 commit comments

Comments
 (0)