22using System ;
33using System . Collections . Concurrent ;
44using System . Collections . Generic ;
5+ using System . Diagnostics ;
56using System . IO ;
7+ using System . Linq ;
68using Java . Interop . Tools . Cecil ;
79using Microsoft . Android . Build . Tasks ;
810using Microsoft . Build . Framework ;
@@ -20,67 +22,127 @@ public class GenerateTypeMappings : AndroidTask
2022
2123 public bool Debug { get ; set ; }
2224
25+ public bool EnableMarshalMethods { get ; set ; }
26+
27+ [ Output ]
28+ public ITaskItem [ ] GeneratedBinaryTypeMaps { get ; set ; } = [ ] ;
29+
2330 [ Required ]
2431 public string IntermediateOutputDirectory { get ; set ; } = "" ;
2532
2633 public bool SkipJniAddNativeMethodRegistrationAttributeScan { get ; set ; }
2734
2835 [ Required ]
29- public string TypemapOutputDirectory { get ; set ; } = "" ;
36+ public ITaskItem [ ] ResolvedAssemblies { get ; set ; } = [ ] ;
3037
31- [ Output ]
32- public ITaskItem [ ] GeneratedBinaryTypeMaps { get ; set ; } = [ ] ;
38+ // This property is temporary and is used to ensure that the new "linker step"
39+ // JLO scanning produces the same results as the old process. It will be removed
40+ // once the process is complete.
41+ public bool RunCheckedBuild { get ; set ; }
42+
43+ [ Required ]
44+ public string [ ] SupportedAbis { get ; set ; } = [ ] ;
3345
3446 public string TypemapImplementation { get ; set ; } = "llvm-ir" ;
3547
48+ [ Required ]
49+ public string TypemapOutputDirectory { get ; set ; } = "" ;
50+
3651 AndroidRuntime androidRuntime ;
3752
3853 public override bool RunTask ( )
3954 {
55+ var useMarshalMethods = ! Debug && EnableMarshalMethods ;
56+
4057 androidRuntime = MonoAndroidHelper . ParseAndroidRuntime ( AndroidRuntime ) ;
58+ if ( androidRuntime == Xamarin . Android . Tasks . AndroidRuntime . NativeAOT ) {
59+ // NativeAOT typemaps are generated in `Microsoft.Android.Sdk.ILLink.TypeMappingStep`
60+ Log . LogDebugMessage ( "Skipping type maps for NativeAOT." ) ;
61+ return ! Log . HasLoggedErrors ;
62+ }
4163
42- // Retrieve the stored NativeCodeGenState
43- var nativeCodeGenStates = BuildEngine4 . GetRegisteredTaskObjectAssemblyLocal < ConcurrentDictionary < AndroidTargetArch , NativeCodeGenState > > (
44- MonoAndroidHelper . GetProjectBuildSpecificTaskObjectKey ( GenerateJavaStubs . NativeCodeGenStateRegisterTaskKey , WorkingDirectory , IntermediateOutputDirectory ) ,
45- RegisteredTaskObjectLifetime . Build
46- ) ;
64+ // If using marshal methods, we cannot use the .typemap.xml files currently because
65+ // the type token ids were changed by the marshal method rewriter.
66+ if ( ! useMarshalMethods )
67+ GenerateAllTypeMappings ( ) ;
4768
48- NativeCodeGenState ? templateCodeGenState = null ;
69+ // Temporarily used to ensure we still generate the same as the old code
70+ if ( RunCheckedBuild || useMarshalMethods ) {
71+ // Retrieve the stored NativeCodeGenState
72+ var nativeCodeGenStates = BuildEngine4 . GetRegisteredTaskObjectAssemblyLocal < ConcurrentDictionary < AndroidTargetArch , NativeCodeGenState > > (
73+ MonoAndroidHelper . GetProjectBuildSpecificTaskObjectKey ( GenerateJavaStubs . NativeCodeGenStateRegisterTaskKey , WorkingDirectory , IntermediateOutputDirectory ) ,
74+ RegisteredTaskObjectLifetime . Build
75+ ) ;
4976
50- foreach ( var kvp in nativeCodeGenStates ) {
51- NativeCodeGenState state = kvp . Value ;
52- templateCodeGenState = state ;
53- WriteTypeMappings ( state ) ;
54- }
77+ NativeCodeGenState ? templateCodeGenState = null ;
78+
79+ foreach ( var kvp in nativeCodeGenStates ) {
80+ NativeCodeGenState state = kvp . Value ;
81+ templateCodeGenState = state ;
82+ WriteTypeMappingsFromNativeState ( state , useMarshalMethods ) ;
83+ }
84+
85+ if ( templateCodeGenState is null )
86+ throw new InvalidOperationException ( $ "Internal error: no native code generator state defined") ;
5587
56- if ( templateCodeGenState is null )
57- throw new InvalidOperationException ( $ "Internal error: no native code generator state defined" ) ;
88+ // Set for use by <GeneratePackageManagerJava/> task later
89+ NativeCodeGenState . TemplateJniAddNativeMethodRegistrationAttributePresent = templateCodeGenState . JniAddNativeMethodRegistrationAttributePresent ;
5890
59- // Set for use by <GeneratePackageManagerJava/> task later
60- NativeCodeGenState . TemplateJniAddNativeMethodRegistrationAttributePresent = templateCodeGenState . JniAddNativeMethodRegistrationAttributePresent ;
91+ return ! Log . HasLoggedErrors ;
92+ }
6193
6294 return ! Log . HasLoggedErrors ;
6395 }
6496
65- void WriteTypeMappings ( NativeCodeGenState state )
97+ void GenerateAllTypeMappings ( )
98+ {
99+ var allAssembliesPerArch = MonoAndroidHelper . GetPerArchAssemblies ( ResolvedAssemblies , SupportedAbis , validate : true ) ;
100+
101+ foreach ( var set in allAssembliesPerArch )
102+ GenerateTypeMap ( set . Key , set . Value . Values . ToList ( ) ) ;
103+ }
104+
105+ void GenerateTypeMap ( AndroidTargetArch arch , List < ITaskItem > assemblies )
106+ {
107+ Log . LogDebugMessage ( $ "Generating type maps for architecture '{ arch } '") ;
108+
109+ var state = TypeMapObjectsFileAdapter . Create ( arch , assemblies , Log ) ;
110+
111+ // An error was already logged to Log.LogError
112+ if ( state is null )
113+ return ;
114+
115+ var tmg = new TypeMapGenerator ( Log , state , androidRuntime ) ;
116+ tmg . Generate ( Debug , SkipJniAddNativeMethodRegistrationAttributeScan , TypemapOutputDirectory ) ;
117+
118+ AddOutputTypeMaps ( tmg , state . TargetArch ) ;
119+ }
120+
121+ void WriteTypeMappingsFromNativeState ( NativeCodeGenState state , bool useMarshalMethods )
66122 {
67123 if ( androidRuntime == Xamarin . Android . Tasks . AndroidRuntime . NativeAOT ) {
68124 // NativeAOT typemaps are generated in `Microsoft.Android.Sdk.ILLink.TypeMappingStep`
69125 Log . LogDebugMessage ( "Skipping type maps for NativeAOT." ) ;
70126 return ;
71127 }
72- Log . LogDebugMessage ( $ "Generating type maps for architecture '{ state . TargetArch } '") ;
128+ Log . LogDebugMessage ( $ "Generating type maps from native state for architecture '{ state . TargetArch } ' (RunCheckedBuild = { RunCheckedBuild } ) ") ;
73129
74130 if ( TypemapImplementation != "llvm-ir" ) {
75131 Log . LogDebugMessage ( $ "TypemapImplementation='{ TypemapImplementation } ' will write an empty native typemap.") ;
76132 state = new NativeCodeGenState ( state . TargetArch , new TypeDefinitionCache ( ) , state . Resolver , [ ] , [ ] , state . Classifier ) ;
77133 }
78134
79- var tmg = new TypeMapGenerator ( Log , state , androidRuntime ) ;
135+ var tmg = new TypeMapGenerator ( Log , new NativeCodeGenStateAdapter ( state ) , androidRuntime ) { RunCheckedBuild = RunCheckedBuild && ! useMarshalMethods } ;
80136 tmg . Generate ( Debug , SkipJniAddNativeMethodRegistrationAttributeScan , TypemapOutputDirectory ) ;
81137
82- string abi = MonoAndroidHelper . ArchToAbi ( state . TargetArch ) ;
138+ AddOutputTypeMaps ( tmg , state . TargetArch ) ;
139+ }
140+
141+ void AddOutputTypeMaps ( TypeMapGenerator tmg , AndroidTargetArch arch )
142+ {
143+ string abi = MonoAndroidHelper . ArchToAbi ( arch ) ;
83144 var items = new List < ITaskItem > ( ) ;
145+
84146 foreach ( string file in tmg . GeneratedBinaryTypeMaps ) {
85147 var item = new TaskItem ( file ) ;
86148 string fileName = Path . GetFileName ( file ) ;
@@ -90,6 +152,6 @@ void WriteTypeMappings (NativeCodeGenState state)
90152 items . Add ( item ) ;
91153 }
92154
93- GeneratedBinaryTypeMaps = items . ToArray ( ) ;
155+ GeneratedBinaryTypeMaps = GeneratedBinaryTypeMaps . Concat ( items ) . ToArray ( ) ;
94156 }
95157}
0 commit comments