Skip to content

Commit a28230c

Browse files
authored
[Xamarin.Android.Build.Tasks] GenerateJavaStubs typemap marshalmethod (#9850)
Context: #9827 Continue breaking down the `<GenerateJavaStubs>` task into smaller, more manageable pieces by moving the marshal methods and type map generation steps into separate tasks. TODO: start moving steps that require Cecil assembly scanning into linker steps (or equivalent), eventually saving a Cecil assembly scan per build.
1 parent 8f6945f commit a28230c

File tree

4 files changed

+197
-115
lines changed

4 files changed

+197
-115
lines changed

src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs

Lines changed: 0 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,8 @@ public class GenerateJavaStubs : AndroidTask
4040
[Required]
4141
public string [] SupportedAbis { get; set; }
4242

43-
[Required]
44-
public string TypemapOutputDirectory { get; set; }
45-
46-
[Required]
47-
public bool GenerateNativeAssembly { get; set; }
48-
4943
public string IntermediateOutputDirectory { get; set; }
50-
public bool LinkingEnabled { get; set; }
51-
public bool HaveMultipleRIDs { get; set; }
5244
public bool EnableMarshalMethods { get; set; }
53-
public bool EnableManagedMarshalMethodsLookup { get; set; }
5445

5546
public bool Debug { get; set; }
5647

@@ -63,27 +54,15 @@ public class GenerateJavaStubs : AndroidTask
6354

6455
public string ApplicationJavaClass { get; set; }
6556

66-
public bool SkipJniAddNativeMethodRegistrationAttributeScan { get; set; }
67-
68-
public ITaskItem[] Environments { get; set; }
69-
70-
[Output]
71-
public ITaskItem[] GeneratedBinaryTypeMaps { get; set; }
72-
73-
[Required]
74-
public string AndroidRuntime { get; set; } = "";
75-
7657
public string CodeGenerationTarget { get; set; } = "";
7758

78-
AndroidRuntime androidRuntime;
7959
JavaPeerStyle codeGenerationTarget;
8060

8161
internal const string AndroidSkipJavaStubGeneration = "AndroidSkipJavaStubGeneration";
8262

8363
public override bool RunTask ()
8464
{
8565
try {
86-
androidRuntime = MonoAndroidHelper.ParseAndroidRuntime (AndroidRuntime);
8766
codeGenerationTarget = MonoAndroidHelper.ParseCodeGenerationTarget (CodeGenerationTarget);
8867
bool useMarshalMethods = !Debug && EnableMarshalMethods;
8968
Run (useMarshalMethods);
@@ -202,54 +181,6 @@ void Run (bool useMarshalMethods)
202181
}
203182
JCWGenerator.EnsureAllArchitecturesAreIdentical (Log, nativeCodeGenStates);
204183

205-
if (useMarshalMethods) {
206-
// We need to parse the environment files supplied by the user to see if they want to use broken exception transitions. This information is needed
207-
// in order to properly generate wrapper methods in the marshal methods assembly rewriter.
208-
// We don't care about those generated by us, since they won't contain the `XA_BROKEN_EXCEPTION_TRANSITIONS` variable we look for.
209-
var environmentParser = new EnvironmentFilesParser ();
210-
bool brokenExceptionTransitionsEnabled = environmentParser.AreBrokenExceptionTransitionsEnabled (Environments);
211-
212-
foreach (var kvp in nativeCodeGenStates) {
213-
NativeCodeGenState state = kvp.Value;
214-
if (!EnableManagedMarshalMethodsLookup) {
215-
RewriteMarshalMethods (state, brokenExceptionTransitionsEnabled);
216-
state.Classifier.AddSpecialCaseMethods ();
217-
} else {
218-
// We need to run `AddSpecialCaseMethods` before `RewriteMarshalMethods` so that we can see the special case
219-
// methods (such as TypeManager.n_Activate_mm) when generating the managed lookup tables.
220-
state.Classifier.AddSpecialCaseMethods ();
221-
state.ManagedMarshalMethodsLookupInfo = new ManagedMarshalMethodsLookupInfo (Log);
222-
RewriteMarshalMethods (state, brokenExceptionTransitionsEnabled);
223-
}
224-
225-
Log.LogDebugMessage ($"[{state.TargetArch}] Number of generated marshal methods: {state.Classifier.MarshalMethods.Count}");
226-
if (state.Classifier.RejectedMethodCount > 0) {
227-
Log.LogWarning ($"[{state.TargetArch}] Number of methods in the project that will be registered dynamically: {state.Classifier.RejectedMethodCount}");
228-
}
229-
230-
if (state.Classifier.WrappedMethodCount > 0) {
231-
// TODO: change to LogWarning once the generator can output code which requires no non-blittable wrappers
232-
Log.LogDebugMessage ($"[{state.TargetArch}] Number of methods in the project that need marshal method wrappers: {state.Classifier.WrappedMethodCount}");
233-
}
234-
}
235-
}
236-
237-
bool typemapsAreAbiAgnostic = Debug && !GenerateNativeAssembly;
238-
bool first = true;
239-
foreach (var kvp in nativeCodeGenStates) {
240-
if (!first && typemapsAreAbiAgnostic) {
241-
Log.LogDebugMessage ("Typemaps: it's a debug build and type maps are ABI-agnostic, not processing more ABIs");
242-
break;
243-
}
244-
245-
NativeCodeGenState state = kvp.Value;
246-
first = false;
247-
WriteTypeMappings (state);
248-
}
249-
250-
// Set for use by <GeneratePackageManagerJava/> task later
251-
NativeCodeGenState.TemplateJniAddNativeMethodRegistrationAttributePresent = templateCodeGenState.JniAddNativeMethodRegistrationAttributePresent;
252-
253184
// Save NativeCodeGenState for later tasks
254185
Log.LogDebugMessage ($"Saving {nameof (NativeCodeGenState)} to {nameof (NativeCodeGenStateRegisterTaskKey)}");
255186
BuildEngine4.RegisterTaskObjectAssemblyLocal (MonoAndroidHelper.GetProjectBuildSpecificTaskObjectKey (NativeCodeGenStateRegisterTaskKey, WorkingDirectory, IntermediateOutputDirectory), nativeCodeGenStates, RegisteredTaskObjectLifetime.Build);
@@ -309,42 +240,5 @@ internal static Dictionary<string, ITaskItem> MaybeGetArchAssemblies (Dictionary
309240

310241
return (allJavaTypes, javaTypesForJCW);
311242
}
312-
313-
void RewriteMarshalMethods (NativeCodeGenState state, bool brokenExceptionTransitionsEnabled)
314-
{
315-
if (state.Classifier == null) {
316-
return;
317-
}
318-
319-
var rewriter = new MarshalMethodsAssemblyRewriter (Log, state.TargetArch, state.Classifier, state.Resolver, state.ManagedMarshalMethodsLookupInfo);
320-
rewriter.Rewrite (brokenExceptionTransitionsEnabled);
321-
}
322-
323-
void WriteTypeMappings (NativeCodeGenState state)
324-
{
325-
if (androidRuntime == Xamarin.Android.Tasks.AndroidRuntime.NativeAOT) {
326-
// NativeAOT typemaps are generated in `Microsoft.Android.Sdk.ILLink.TypeMappingStep`
327-
return;
328-
}
329-
330-
Log.LogDebugMessage ($"Generating type maps for architecture '{state.TargetArch}'");
331-
var tmg = new TypeMapGenerator (Log, state, androidRuntime);
332-
if (!tmg.Generate (Debug, SkipJniAddNativeMethodRegistrationAttributeScan, TypemapOutputDirectory, GenerateNativeAssembly)) {
333-
throw new XamarinAndroidException (4308, Properties.Resources.XA4308);
334-
}
335-
336-
string abi = MonoAndroidHelper.ArchToAbi (state.TargetArch);
337-
var items = new List<ITaskItem> ();
338-
foreach (string file in tmg.GeneratedBinaryTypeMaps) {
339-
var item = new TaskItem (file);
340-
string fileName = Path.GetFileName (file);
341-
item.SetMetadata ("DestinationSubPath", $"{abi}/{fileName}");
342-
item.SetMetadata ("DestinationSubDirectory", $"{abi}/");
343-
item.SetMetadata ("Abi", abi);
344-
items.Add (item);
345-
}
346-
347-
GeneratedBinaryTypeMaps = items.ToArray ();
348-
}
349243
}
350244
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#nullable enable
2+
using System;
3+
using System.Collections.Concurrent;
4+
using System.Collections.Generic;
5+
using System.IO;
6+
using Java.Interop.Tools.Diagnostics;
7+
using Microsoft.Android.Build.Tasks;
8+
using Microsoft.Build.Framework;
9+
using Microsoft.Build.Utilities;
10+
using Xamarin.Android.Tools;
11+
12+
namespace Xamarin.Android.Tasks;
13+
14+
public class GenerateTypeMappings : AndroidTask
15+
{
16+
public override string TaskPrefix => "GTM";
17+
18+
[Required]
19+
public string AndroidRuntime { get; set; } = "";
20+
21+
public bool Debug { get; set; }
22+
23+
[Required]
24+
public bool GenerateNativeAssembly { get; set; }
25+
26+
[Required]
27+
public string IntermediateOutputDirectory { get; set; } = "";
28+
29+
public bool SkipJniAddNativeMethodRegistrationAttributeScan { get; set; }
30+
31+
[Required]
32+
public string TypemapOutputDirectory { get; set; } = "";
33+
34+
[Output]
35+
public ITaskItem [] GeneratedBinaryTypeMaps { get; set; } = [];
36+
37+
AndroidRuntime androidRuntime;
38+
39+
public override bool RunTask ()
40+
{
41+
androidRuntime = MonoAndroidHelper.ParseAndroidRuntime (AndroidRuntime);
42+
43+
// Retrieve the stored NativeCodeGenState
44+
var nativeCodeGenStates = BuildEngine4.GetRegisteredTaskObjectAssemblyLocal<ConcurrentDictionary<AndroidTargetArch, NativeCodeGenState>> (
45+
MonoAndroidHelper.GetProjectBuildSpecificTaskObjectKey (GenerateJavaStubs.NativeCodeGenStateRegisterTaskKey, WorkingDirectory, IntermediateOutputDirectory),
46+
RegisteredTaskObjectLifetime.Build
47+
);
48+
49+
NativeCodeGenState? templateCodeGenState = null;
50+
bool typemapsAreAbiAgnostic = Debug && !GenerateNativeAssembly;
51+
bool first = true;
52+
53+
foreach (var kvp in nativeCodeGenStates) {
54+
if (!first && typemapsAreAbiAgnostic) {
55+
Log.LogDebugMessage ("Typemaps: it's a debug build and type maps are ABI-agnostic, not processing more ABIs");
56+
break;
57+
}
58+
59+
NativeCodeGenState state = kvp.Value;
60+
templateCodeGenState = state;
61+
first = false;
62+
WriteTypeMappings (state);
63+
}
64+
65+
if (templateCodeGenState is null)
66+
throw new InvalidOperationException ($"Internal error: no native code generator state defined");
67+
68+
// Set for use by <GeneratePackageManagerJava/> task later
69+
NativeCodeGenState.TemplateJniAddNativeMethodRegistrationAttributePresent = templateCodeGenState.JniAddNativeMethodRegistrationAttributePresent;
70+
71+
return !Log.HasLoggedErrors;
72+
}
73+
74+
void WriteTypeMappings (NativeCodeGenState state)
75+
{
76+
if (androidRuntime == Xamarin.Android.Tasks.AndroidRuntime.NativeAOT) {
77+
// NativeAOT typemaps are generated in `Microsoft.Android.Sdk.ILLink.TypeMappingStep`
78+
return;
79+
}
80+
if (androidRuntime == Xamarin.Android.Tasks.AndroidRuntime.CoreCLR) {
81+
// TODO: CoreCLR typemaps will be emitted later
82+
return;
83+
}
84+
Log.LogDebugMessage ($"Generating type maps for architecture '{state.TargetArch}'");
85+
var tmg = new TypeMapGenerator (Log, state, androidRuntime);
86+
if (!tmg.Generate (Debug, SkipJniAddNativeMethodRegistrationAttributeScan, TypemapOutputDirectory, GenerateNativeAssembly)) {
87+
throw new XamarinAndroidException (4308, Properties.Resources.XA4308);
88+
}
89+
90+
string abi = MonoAndroidHelper.ArchToAbi (state.TargetArch);
91+
var items = new List<ITaskItem> ();
92+
foreach (string file in tmg.GeneratedBinaryTypeMaps) {
93+
var item = new TaskItem (file);
94+
string fileName = Path.GetFileName (file);
95+
item.SetMetadata ("DestinationSubPath", $"{abi}/{fileName}");
96+
item.SetMetadata ("DestinationSubDirectory", $"{abi}/");
97+
item.SetMetadata ("Abi", abi);
98+
items.Add (item);
99+
}
100+
101+
GeneratedBinaryTypeMaps = items.ToArray ();
102+
}
103+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#nullable enable
2+
using System.Collections.Concurrent;
3+
using Microsoft.Android.Build.Tasks;
4+
using Microsoft.Build.Framework;
5+
using Xamarin.Android.Tools;
6+
7+
namespace Xamarin.Android.Tasks;
8+
9+
public class RewriteMarshalMethods : AndroidTask
10+
{
11+
public override string TaskPrefix => "RMM";
12+
13+
public bool EnableManagedMarshalMethodsLookup { get; set; }
14+
15+
public ITaskItem [] Environments { get; set; } = [];
16+
17+
[Required]
18+
public string IntermediateOutputDirectory { get; set; } = "";
19+
20+
public override bool RunTask ()
21+
{
22+
// Retrieve the stored NativeCodeGenState
23+
var nativeCodeGenStates = BuildEngine4.GetRegisteredTaskObjectAssemblyLocal<ConcurrentDictionary<AndroidTargetArch, NativeCodeGenState>> (
24+
MonoAndroidHelper.GetProjectBuildSpecificTaskObjectKey (GenerateJavaStubs.NativeCodeGenStateRegisterTaskKey, WorkingDirectory, IntermediateOutputDirectory),
25+
RegisteredTaskObjectLifetime.Build
26+
);
27+
28+
// We need to parse the environment files supplied by the user to see if they want to use broken exception transitions. This information is needed
29+
// in order to properly generate wrapper methods in the marshal methods assembly rewriter.
30+
// We don't care about those generated by us, since they won't contain the `XA_BROKEN_EXCEPTION_TRANSITIONS` variable we look for.
31+
var environmentParser = new EnvironmentFilesParser ();
32+
bool brokenExceptionTransitionsEnabled = environmentParser.AreBrokenExceptionTransitionsEnabled (Environments);
33+
34+
foreach (var kvp in nativeCodeGenStates) {
35+
NativeCodeGenState state = kvp.Value;
36+
37+
if (state.Classifier is null) {
38+
Log.LogError ("state.Classifier cannot be null if marshal methods are enabled");
39+
return false;
40+
}
41+
42+
if (!EnableManagedMarshalMethodsLookup) {
43+
RewriteMethods (state, brokenExceptionTransitionsEnabled);
44+
state.Classifier.AddSpecialCaseMethods ();
45+
} else {
46+
// We need to run `AddSpecialCaseMethods` before `RewriteMarshalMethods` so that we can see the special case
47+
// methods (such as TypeManager.n_Activate_mm) when generating the managed lookup tables.
48+
state.Classifier.AddSpecialCaseMethods ();
49+
state.ManagedMarshalMethodsLookupInfo = new ManagedMarshalMethodsLookupInfo (Log);
50+
RewriteMethods (state, brokenExceptionTransitionsEnabled);
51+
}
52+
53+
Log.LogDebugMessage ($"[{state.TargetArch}] Number of generated marshal methods: {state.Classifier.MarshalMethods.Count}");
54+
if (state.Classifier.RejectedMethodCount > 0) {
55+
Log.LogWarning ($"[{state.TargetArch}] Number of methods in the project that will be registered dynamically: {state.Classifier.RejectedMethodCount}");
56+
}
57+
58+
if (state.Classifier.WrappedMethodCount > 0) {
59+
// TODO: change to LogWarning once the generator can output code which requires no non-blittable wrappers
60+
Log.LogDebugMessage ($"[{state.TargetArch}] Number of methods in the project that need marshal method wrappers: {state.Classifier.WrappedMethodCount}");
61+
}
62+
}
63+
64+
return !Log.HasLoggedErrors;
65+
}
66+
67+
void RewriteMethods (NativeCodeGenState state, bool brokenExceptionTransitionsEnabled)
68+
{
69+
if (state.Classifier == null) {
70+
return;
71+
}
72+
73+
var rewriter = new MarshalMethodsAssemblyRewriter (Log, state.TargetArch, state.Classifier, state.Resolver, state.ManagedMarshalMethodsLookupInfo);
74+
rewriter.Rewrite (brokenExceptionTransitionsEnabled);
75+
}
76+
}

src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
7171
<UsingTask TaskName="Xamarin.Android.Tasks.GenerateJavaStubs" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
7272
<UsingTask TaskName="Xamarin.Android.Tasks.GenerateMainAndroidManifest" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
7373
<UsingTask TaskName="Xamarin.Android.Tasks.GeneratePackageManagerJava" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
74+
<UsingTask TaskName="Xamarin.Android.Tasks.GenerateTypeMappings" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
7475
<UsingTask TaskName="Xamarin.Android.Tasks.GetAndroidDefineConstants" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
7576
<UsingTask TaskName="Xamarin.Android.Tasks.GetAppSettingsDirectory" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
7677
<UsingTask TaskName="Xamarin.Android.Tasks.GetConvertedJavaLibraries" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
@@ -90,6 +91,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
9091
<UsingTask TaskName="Xamarin.Android.Tasks.RemoveDirFixed" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
9192
<UsingTask TaskName="Xamarin.Android.Tasks.RemoveRegisterAttribute" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
9293
<UsingTask TaskName="Xamarin.Android.Tasks.ReadAndroidManifest" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
94+
<UsingTask TaskName="Xamarin.Android.Tasks.RewriteMarshalMethods" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
9395
<UsingTask TaskName="Xamarin.Android.Tasks.GetExtraPackages" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
9496
<UsingTask TaskName="Xamarin.Android.Tasks.CopyGeneratedJavaResourceClasses" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
9597
<UsingTask TaskName="Xamarin.Android.Tasks.ReadJavaStubsCache" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
@@ -1529,29 +1531,36 @@ because xbuild doesn't support framework reference assemblies.
15291531
</ItemGroup>
15301532

15311533
<GenerateJavaStubs
1532-
AndroidRuntime="$(_AndroidRuntime)"
15331534
CodeGenerationTarget="$(_AndroidJcwCodegenTarget)"
15341535
ResolvedAssemblies="@(_ResolvedAssemblies)"
15351536
ResolvedUserAssemblies="@(_ResolvedUserMonoAndroidAssemblies)"
15361537
ErrorOnCustomJavaObject="$(AndroidErrorOnCustomJavaObject)"
15371538
Debug="$(AndroidIncludeDebugSymbols)"
15381539
AndroidSdkPlatform="$(_AndroidApiLevel)"
15391540
OutputDirectory="$(IntermediateOutputPath)android"
1540-
TypemapOutputDirectory="$(_NativeAssemblySourceDir)"
1541-
GenerateNativeAssembly="$(_AndroidGenerateNativeAssembly)"
15421541
PackageNamingPolicy="$(AndroidPackageNamingPolicy)"
15431542
ApplicationJavaClass="$(AndroidApplicationJavaClass)"
15441543
FrameworkDirectories="$(_XATargetFrameworkDirectories);$(_XATargetFrameworkDirectories)Facades"
15451544
SupportedAbis="@(_BuildTargetAbis)"
1546-
SkipJniAddNativeMethodRegistrationAttributeScan="$(_SkipJniAddNativeMethodRegistrationAttributeScan)"
15471545
EnableMarshalMethods="$(_AndroidUseMarshalMethods)"
1548-
EnableManagedMarshalMethodsLookup="$(_AndroidUseManagedMarshalMethodsLookup)"
1549-
LinkingEnabled="$(_LinkingEnabled)"
1550-
HaveMultipleRIDs="$(_HaveMultipleRIDs)"
1551-
IntermediateOutputDirectory="$(IntermediateOutputPath)"
1552-
Environments="@(_EnvironmentFiles)">
1546+
IntermediateOutputDirectory="$(IntermediateOutputPath)">
15531547
</GenerateJavaStubs>
15541548

1549+
<RewriteMarshalMethods
1550+
Condition=" '$(_AndroidUseMarshalMethods)' == 'true' And '$(AndroidIncludeDebugSymbols)' == 'false' "
1551+
EnableManagedMarshalMethodsLookup="$(_AndroidUseManagedMarshalMethodsLookup)"
1552+
Environments="@(_EnvironmentFiles)"
1553+
IntermediateOutputDirectory="$(IntermediateOutputPath)">
1554+
</RewriteMarshalMethods>
1555+
1556+
<GenerateTypeMappings
1557+
AndroidRuntime="$(_AndroidRuntime)"
1558+
Debug="$(AndroidIncludeDebugSymbols)"
1559+
GenerateNativeAssembly="$(_AndroidGenerateNativeAssembly)"
1560+
IntermediateOutputDirectory="$(IntermediateOutputPath)"
1561+
SkipJniAddNativeMethodRegistrationAttributeScan="$(_SkipJniAddNativeMethodRegistrationAttributeScan)"
1562+
TypemapOutputDirectory="$(_NativeAssemblySourceDir)">
1563+
</GenerateTypeMappings>
15551564

15561565
<GenerateACWMap
15571566
AcwMapFile="$(_AcwMapFile)"

0 commit comments

Comments
 (0)