11using System ;
22using System . Collections . Generic ;
3-
3+ using System . IO ;
4+ using System . Linq ;
5+ using System . Xml . Linq ;
46using Java . Interop . Tools . Cecil ;
57using Java . Interop . Tools . TypeNameMappings ;
68using Microsoft . Android . Build . Tasks ;
@@ -33,7 +35,7 @@ public bool Generate (NativeCodeGenState codeGenState, string acwMapFile)
3335 bool success = true ;
3436
3537 using var acw_map = MemoryStreamPool . Shared . CreateStreamWriter ( ) ;
36- foreach ( TypeDefinition type in javaTypes ) {
38+ foreach ( TypeDefinition type in javaTypes . OrderBy ( t => t . FullName . Replace ( '/' , '.' ) ) ) {
3739 string managedKey = type . FullName . Replace ( '/' , '.' ) ;
3840 string javaKey = JavaNativeTypeManager . ToJniName ( type , cache ) . Replace ( '/' , '.' ) ;
3941
@@ -79,7 +81,19 @@ public bool Generate (NativeCodeGenState codeGenState, string acwMapFile)
7981 }
8082
8183 acw_map . Flush ( ) ;
82- Files . CopyIfStreamChanged ( acw_map . BaseStream , acwMapFile ) ;
84+
85+ // If there's conflicts, the "new way" file never got written, and will show up as
86+ // "changed" in our comparison test, so skip it.
87+ if ( javaConflicts . Count > 0 ) {
88+ return false ;
89+ }
90+
91+ if ( Files . HasStreamChanged ( acw_map . BaseStream , acwMapFile ) ) {
92+ log . LogError ( $ "ACW map file '{ acwMapFile } ' changed") ;
93+ Files . CopyIfStreamChanged ( acw_map . BaseStream , acwMapFile + "2" ) ;
94+ } else {
95+ log . LogDebugMessage ( $ "ACW map file '{ acwMapFile } ' unchanged") ;
96+ }
8397
8498 foreach ( var kvp in managedConflicts ) {
8599 log . LogCodedWarning ( "XA4214" , Properties . Resources . XA4214 , kvp . Key , string . Join ( ", " , kvp . Value ) ) ;
@@ -99,4 +113,121 @@ public bool Generate (NativeCodeGenState codeGenState, string acwMapFile)
99113
100114 return success ;
101115 }
116+
117+ public void Generate ( List < ACWMapEntry > javaTypes , string acwMapFile )
118+ {
119+ // We need to save a map of .NET type -> ACW type for resource file fixups
120+ var managed = new Dictionary < string , ACWMapEntry > ( javaTypes . Count , StringComparer . Ordinal ) ;
121+ var java = new Dictionary < string , ACWMapEntry > ( javaTypes . Count , StringComparer . Ordinal ) ;
122+
123+ var managedConflicts = new Dictionary < string , List < string > > ( 0 , StringComparer . Ordinal ) ;
124+ var javaConflicts = new Dictionary < string , List < string > > ( 0 , StringComparer . Ordinal ) ;
125+
126+ using var acw_map = MemoryStreamPool . Shared . CreateStreamWriter ( ) ;
127+
128+ foreach ( var type in javaTypes . OrderBy ( t => t . ManagedKey ) ) {
129+ string managedKey = type . ManagedKey ;
130+ string javaKey = type . JavaKey ;
131+
132+ acw_map . Write ( type . PartialAssemblyQualifiedName ) ;
133+ acw_map . Write ( ';' ) ;
134+ acw_map . Write ( javaKey ) ;
135+ acw_map . WriteLine ( ) ;
136+
137+ ACWMapEntry conflict ;
138+ bool hasConflict = false ;
139+
140+ if ( managed . TryGetValue ( managedKey , out conflict ) ) {
141+ if ( ! conflict . ModuleName . Equals ( type . ModuleName ) ) {
142+ if ( ! managedConflicts . TryGetValue ( managedKey , out var list ) )
143+ managedConflicts . Add ( managedKey , list = new List < string > { conflict . PartialAssemblyName } ) ;
144+ list . Add ( type . PartialAssemblyName ) ;
145+ }
146+ hasConflict = true ;
147+ }
148+
149+ if ( java . TryGetValue ( javaKey , out conflict ) ) {
150+ if ( ! conflict . ModuleName . Equals ( type . ModuleName ) ) {
151+ if ( ! javaConflicts . TryGetValue ( javaKey , out var list ) )
152+ javaConflicts . Add ( javaKey , list = new List < string > { conflict . AssemblyQualifiedName } ) ;
153+ list . Add ( type . AssemblyQualifiedName ) ;
154+ }
155+ hasConflict = true ;
156+ }
157+
158+ if ( ! hasConflict ) {
159+ managed . Add ( managedKey , type ) ;
160+ java . Add ( javaKey , type ) ;
161+
162+ acw_map . Write ( managedKey ) ;
163+ acw_map . Write ( ';' ) ;
164+ acw_map . Write ( javaKey ) ;
165+ acw_map . WriteLine ( ) ;
166+
167+ acw_map . Write ( type . CompatJniName ) ;
168+ acw_map . Write ( ';' ) ;
169+ acw_map . Write ( javaKey ) ;
170+ acw_map . WriteLine ( ) ;
171+ }
172+ }
173+
174+ acw_map . Flush ( ) ;
175+
176+ foreach ( var kvp in managedConflicts ) {
177+ log . LogCodedWarning ( "XA4214" , Properties . Resources . XA4214 , kvp . Key , string . Join ( ", " , kvp . Value ) ) ;
178+ log . LogCodedWarning ( "XA4214" , Properties . Resources . XA4214_Result , kvp . Key , kvp . Value [ 0 ] ) ;
179+ }
180+
181+ foreach ( var kvp in javaConflicts ) {
182+ log . LogCodedError ( "XA4215" , Properties . Resources . XA4215 , kvp . Key ) ;
183+
184+ foreach ( var typeName in kvp . Value ) {
185+ log . LogCodedError ( "XA4215" , Properties . Resources . XA4215_Details , kvp . Key , typeName ) ;
186+ }
187+ }
188+
189+ // Don't write the output file if there are any errors so that
190+ // future incremental builds will try again.
191+ if ( javaConflicts . Count > 0 )
192+ return ;
193+
194+ Files . CopyIfStreamChanged ( acw_map . BaseStream , acwMapFile ) ;
195+ }
196+ }
197+
198+ class ACWMapEntry
199+ {
200+ public string AssemblyQualifiedName { get ; set ; }
201+ public string CompatJniName { get ; set ; }
202+ public string JavaKey { get ; set ; }
203+ public string ManagedKey { get ; set ; }
204+ public string ModuleName { get ; set ; }
205+ public string PartialAssemblyName { get ; set ; }
206+ public string PartialAssemblyQualifiedName { get ; set ; }
207+
208+ public static ACWMapEntry Create ( TypeDefinition type , TypeDefinitionCache cache )
209+ {
210+ return new ACWMapEntry {
211+ AssemblyQualifiedName = type . GetAssemblyQualifiedName ( cache ) ,
212+ CompatJniName = JavaNativeTypeManager . ToCompatJniName ( type , cache ) . Replace ( '/' , '.' ) ,
213+ JavaKey = JavaNativeTypeManager . ToJniName ( type , cache ) . Replace ( '/' , '.' ) ,
214+ ManagedKey = type . FullName . Replace ( '/' , '.' ) ,
215+ ModuleName = type . Module . Name ,
216+ PartialAssemblyName = type . GetPartialAssemblyName ( cache ) ,
217+ PartialAssemblyQualifiedName = type . GetPartialAssemblyQualifiedName ( cache ) ,
218+ } ;
219+ }
220+
221+ public static ACWMapEntry Create ( XElement type , string partialAssemblyName , string moduleName )
222+ {
223+ return new ACWMapEntry {
224+ AssemblyQualifiedName = type . GetAttributeOrDefault ( "assembly-qualified-name" , string . Empty ) ,
225+ CompatJniName = type . GetAttributeOrDefault ( "compat-jni-name" , string . Empty ) ,
226+ JavaKey = type . GetAttributeOrDefault ( "java-key" , string . Empty ) ,
227+ ManagedKey = type . GetAttributeOrDefault ( "managed-key" , string . Empty ) ,
228+ ModuleName = moduleName ,
229+ PartialAssemblyName = partialAssemblyName ,
230+ PartialAssemblyQualifiedName = type . GetAttributeOrDefault ( "partial-assembly-qualified-name" , string . Empty ) ,
231+ } ;
232+ }
102233}
0 commit comments