Skip to content

Commit 8650e83

Browse files
committed
[XABT] Move JLO scanning needed for typemap generation to a linker step.
1 parent b54ec05 commit 8650e83

13 files changed

+714
-99
lines changed

src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/AddKeepAlivesStep.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@ protected override void ProcessAssembly (AssemblyDefinition assembly)
2929
}
3030

3131
#if !ILLINK
32-
public bool ProcessAssembly (AssemblyDefinition assembly, StepContext context)
32+
public void ProcessAssembly (AssemblyDefinition assembly, StepContext context)
3333
{
3434
// Only run this step on user Android assemblies
3535
if (!context.IsAndroidUserAssembly)
36-
return false;
36+
return;
3737

38-
return AddKeepAlives (assembly);
38+
context.IsAssemblyModified |= AddKeepAlives (assembly);
3939
}
4040
#endif // !ILLINK
4141

src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FindJavaObjectsStep.cs

+1-5
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,15 @@ public class FindJavaObjectsStep : BaseStep, IAssemblyModifierPipelineStep
2929

3030
public FindJavaObjectsStep (TaskLoggingHelper log) => Log = log;
3131

32-
public bool ProcessAssembly (AssemblyDefinition assembly, StepContext context)
32+
public void ProcessAssembly (AssemblyDefinition assembly, StepContext context)
3333
{
3434
var destinationJLOXml = JavaObjectsXmlFile.GetJavaObjectsXmlFilePath (context.Destination.ItemSpec);
3535
var scanned = ScanAssembly (assembly, context, destinationJLOXml);
3636

3737
if (!scanned) {
3838
// We didn't scan for Java objects, so write an empty .xml file for later steps
3939
JavaObjectsXmlFile.WriteEmptyFile (destinationJLOXml, Log);
40-
return false;
4140
}
42-
43-
// This step does not change the assembly
44-
return false;
4541
}
4642

4743
public bool ScanAssembly (AssemblyDefinition assembly, StepContext context, string destinationJLOXml)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#nullable enable
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using Microsoft.Android.Build.Tasks;
6+
using Microsoft.Build.Utilities;
7+
using Mono.Cecil;
8+
using Mono.Linker.Steps;
9+
using Xamarin.Android.Tasks;
10+
11+
namespace MonoDroid.Tuner;
12+
13+
/// <summary>
14+
/// Scans an assembly for JLOs that need to be in the typemap and writes them to an XML file.
15+
/// </summary>
16+
public class FindTypeMapObjectsStep : BaseStep, IAssemblyModifierPipelineStep
17+
{
18+
public bool Debug { get; set; }
19+
20+
public bool ErrorOnCustomJavaObject { get; set; }
21+
22+
public TaskLoggingHelper Log { get; set; }
23+
24+
public FindTypeMapObjectsStep (TaskLoggingHelper log) => Log = log;
25+
26+
public void ProcessAssembly (AssemblyDefinition assembly, StepContext context)
27+
{
28+
var destinationTypeMapXml = TypeMapObjectsXmlFile.GetTypeMapObjectsXmlFilePath (context.Destination.ItemSpec);
29+
30+
// We only care about assemblies that can contains JLOs
31+
if (!context.IsAndroidAssembly) {
32+
Log.LogDebugMessage ($"Skipping assembly '{assembly.Name.Name}' because it is not an Android assembly");
33+
TypeMapObjectsXmlFile.WriteEmptyFile (destinationTypeMapXml, Log);
34+
return;
35+
}
36+
37+
var types = ScanForJavaTypes (assembly);
38+
39+
var xml = new TypeMapObjectsXmlFile {
40+
AssemblyName = assembly.Name.Name,
41+
};
42+
43+
if (Debug) {
44+
var (javaToManaged, managedToJava) = TypeMapCecilAdapter.GetDebugNativeEntries (types, Context, out var foundJniNativeRegistration);
45+
46+
xml.JavaToManagedDebugEntries.AddRange (javaToManaged);
47+
xml.ManagedToJavaDebugEntries.AddRange (managedToJava);
48+
xml.FoundJniNativeRegistration = foundJniNativeRegistration;
49+
50+
if (!xml.HasDebugEntries) {
51+
Log.LogDebugMessage ($"No Java types found in '{assembly.Name.Name}'");
52+
TypeMapObjectsXmlFile.WriteEmptyFile (destinationTypeMapXml, Log);
53+
return;
54+
}
55+
} else {
56+
var genState = TypeMapCecilAdapter.GetReleaseGenerationState (types, Context, out var foundJniNativeRegistration);
57+
xml.ModuleReleaseData = genState.TempModules.SingleOrDefault ().Value;
58+
59+
if (xml.ModuleReleaseData == null) {
60+
Log.LogDebugMessage ($"No Java types found in '{assembly.Name.Name}'");
61+
TypeMapObjectsXmlFile.WriteEmptyFile (destinationTypeMapXml, Log);
62+
return;
63+
}
64+
}
65+
66+
xml.Export (destinationTypeMapXml);
67+
68+
Log.LogDebugMessage ($"Wrote '{destinationTypeMapXml}', {xml.JavaToManagedDebugEntries.Count} JavaToManagedDebugEntries, {xml.ManagedToJavaDebugEntries.Count} ManagedToJavaDebugEntries, FoundJniNativeRegistration: {xml.FoundJniNativeRegistration}");
69+
}
70+
71+
List<TypeDefinition> ScanForJavaTypes (AssemblyDefinition assembly)
72+
{
73+
var types = new List<TypeDefinition> ();
74+
75+
var scanner = new XAJavaTypeScanner (Xamarin.Android.Tools.AndroidTargetArch.None, Log, Context) {
76+
ErrorOnCustomJavaObject = ErrorOnCustomJavaObject
77+
};
78+
79+
foreach (ModuleDefinition md in assembly.Modules) {
80+
foreach (TypeDefinition td in md.Types) {
81+
scanner.AddJavaType (td, types);
82+
}
83+
}
84+
85+
return types;
86+
}
87+
}

src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixAbstractMethodsStep.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,13 @@ internal bool FixAbstractMethods (AssemblyDefinition assembly)
8383
}
8484

8585
#if !ILLINK
86-
public bool ProcessAssembly (AssemblyDefinition assembly, StepContext context)
86+
public void ProcessAssembly (AssemblyDefinition assembly, StepContext context)
8787
{
8888
// Only run this step on non-main user Android assemblies
8989
if (context.IsMainAssembly || !context.IsAndroidUserAssembly)
90-
return false;
90+
return;
9191

92-
return FixAbstractMethods (assembly);
92+
context.IsAssemblyModified |= FixAbstractMethods (assembly);
9393
}
9494
#endif // !ILLINK
9595

src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixLegacyResourceDesignerStep.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,13 @@ protected override void LoadDesigner ()
7171
}
7272

7373
#if !ILLINK
74-
public bool ProcessAssembly (AssemblyDefinition assembly, Xamarin.Android.Tasks.StepContext context)
74+
public void ProcessAssembly (AssemblyDefinition assembly, Xamarin.Android.Tasks.StepContext context)
7575
{
7676
// Only run this step on non-main user Android assemblies
7777
if (context.IsMainAssembly || !context.IsAndroidUserAssembly)
78-
return false;
78+
return;
7979

80-
return ProcessAssemblyDesigner (assembly);
80+
context.IsAssemblyModified |= ProcessAssemblyDesigner (assembly);
8181
}
8282
#endif // !ILLINK
8383

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

+57-24
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using Java.Interop.Tools.TypeNameMappings;
99
using Microsoft.Android.Build.Tasks;
1010
using Microsoft.Build.Framework;
11+
using Microsoft.Build.Utilities;
1112
using Mono.Cecil;
1213
using MonoDroid.Tuner;
1314
using Xamarin.Android.Tools;
@@ -78,10 +79,6 @@ public override bool RunTask ()
7879
ReadSymbols = ReadSymbols,
7980
};
8081

81-
var writerParameters = new WriterParameters {
82-
DeterministicMvid = Deterministic,
83-
};
84-
8582
Dictionary<AndroidTargetArch, Dictionary<string, ITaskItem>> perArchAssemblies = MonoAndroidHelper.GetPerArchAssemblies (ResolvedAssemblies, Array.Empty<string> (), validate: false);
8683

8784
AssemblyPipeline? pipeline = null;
@@ -122,7 +119,7 @@ public override bool RunTask ()
122119

123120
Directory.CreateDirectory (Path.GetDirectoryName (destination.ItemSpec));
124121

125-
RunPipeline (pipeline!, source, destination, writerParameters);
122+
RunPipeline (pipeline!, source, destination);
126123
}
127124

128125
pipeline?.Dispose ();
@@ -141,9 +138,26 @@ protected virtual void BuildPipeline (AssemblyPipeline pipeline, MSBuildLinkCont
141138

142139
findJavaObjectsStep.Initialize (context);
143140
pipeline.Steps.Add (findJavaObjectsStep);
141+
142+
// SaveChangedAssemblyStep
143+
var writerParameters = new WriterParameters {
144+
DeterministicMvid = Deterministic,
145+
};
146+
147+
var saveChangedAssemblyStep = new SaveChangedAssemblyStep (Log, writerParameters);
148+
pipeline.Steps.Add (saveChangedAssemblyStep);
149+
150+
// FindTypeMapObjectsStep - this must be run after the assembly has been saved, as saving changes the MVID
151+
var findTypeMapObjectsStep = new FindTypeMapObjectsStep (Log) {
152+
ErrorOnCustomJavaObject = ErrorOnCustomJavaObject,
153+
Debug = Debug,
154+
};
155+
156+
findTypeMapObjectsStep.Initialize (context);
157+
pipeline.Steps.Add (findTypeMapObjectsStep);
144158
}
145159

146-
void RunPipeline (AssemblyPipeline pipeline, ITaskItem source, ITaskItem destination, WriterParameters writerParameters)
160+
void RunPipeline (AssemblyPipeline pipeline, ITaskItem source, ITaskItem destination)
147161
{
148162
var assembly = pipeline.Resolver.GetAssembly (source.ItemSpec);
149163

@@ -157,28 +171,47 @@ void RunPipeline (AssemblyPipeline pipeline, ITaskItem source, ITaskItem destina
157171
IsUserAssembly = ResolvedUserAssemblies.Any (a => a.ItemSpec == source.ItemSpec),
158172
};
159173

160-
var changed = pipeline.Run (assembly, context);
161-
162-
if (changed) {
163-
Log.LogDebugMessage ($"Saving modified assembly: {destination.ItemSpec}");
164-
Directory.CreateDirectory (Path.GetDirectoryName (destination.ItemSpec));
165-
writerParameters.WriteSymbols = assembly.MainModule.HasSymbols;
166-
assembly.Write (destination.ItemSpec, writerParameters);
167-
} else {
168-
// If we didn't write a modified file, copy the original to the destination
169-
CopyIfChanged (source, destination);
170-
}
174+
pipeline.Run (assembly, context);
171175
}
172176

173-
void CopyIfChanged (ITaskItem source, ITaskItem destination)
177+
class SaveChangedAssemblyStep : IAssemblyModifierPipelineStep
174178
{
175-
if (MonoAndroidHelper.CopyAssemblyAndSymbols (source.ItemSpec, destination.ItemSpec)) {
176-
Log.LogDebugMessage ($"Copied: {destination.ItemSpec}");
177-
} else {
178-
Log.LogDebugMessage ($"Skipped unchanged file: {destination.ItemSpec}");
179+
public TaskLoggingHelper Log { get; set; }
180+
181+
public WriterParameters WriterParameters { get; set; }
182+
183+
public SaveChangedAssemblyStep (TaskLoggingHelper log, WriterParameters writerParameters)
184+
{
185+
Log = log;
186+
WriterParameters = writerParameters;
187+
}
179188

180-
// NOTE: We still need to update the timestamp on this file, or this target would run again
181-
File.SetLastWriteTimeUtc (destination.ItemSpec, DateTime.UtcNow);
189+
public void ProcessAssembly (AssemblyDefinition assembly, StepContext context)
190+
{
191+
if (context.IsAssemblyModified) {
192+
Log.LogDebugMessage ($"Saving modified assembly: {context.Destination.ItemSpec}");
193+
Directory.CreateDirectory (Path.GetDirectoryName (context.Destination.ItemSpec));
194+
WriterParameters.WriteSymbols = assembly.MainModule.HasSymbols;
195+
assembly.Write (context.Destination.ItemSpec, WriterParameters);
196+
} else {
197+
// If we didn't write a modified file, copy the original to the destination
198+
CopyIfChanged (context.Source, context.Destination);
199+
}
200+
201+
// We just saved the assembly, so it is no longer modified
202+
context.IsAssemblyModified = false;
203+
}
204+
205+
void CopyIfChanged (ITaskItem source, ITaskItem destination)
206+
{
207+
if (MonoAndroidHelper.CopyAssemblyAndSymbols (source.ItemSpec, destination.ItemSpec)) {
208+
Log.LogDebugMessage ($"Copied: {destination.ItemSpec}");
209+
} else {
210+
Log.LogDebugMessage ($"Skipped unchanged file: {destination.ItemSpec}");
211+
212+
// NOTE: We still need to update the timestamp on this file, or this target would run again
213+
File.SetLastWriteTimeUtc (destination.ItemSpec, DateTime.UtcNow);
214+
}
182215
}
183216
}
184217
}

0 commit comments

Comments
 (0)