@@ -70,23 +70,17 @@ public class GenerateJavaStubs : Task
7070
7171 public override bool Execute ( )
7272 {
73- var temp = Path . Combine ( Path . GetTempPath ( ) , Path . GetRandomFileName ( ) ) ;
7473 try {
75- Directory . CreateDirectory ( temp ) ;
76-
7774 // We're going to do 3 steps here instead of separate tasks so
7875 // we can share the list of JLO TypeDefinitions between them
7976 using ( var res = new DirectoryAssemblyResolver ( this . CreateTaskLogger ( ) , loadDebugSymbols : true ) ) {
80- Run ( res , temp ) ;
77+ Run ( res ) ;
8178 }
82- } catch ( XamarinAndroidException e ) {
79+ }
80+ catch ( XamarinAndroidException e ) {
8381 Log . LogCodedError ( string . Format ( "XA{0:0000}" , e . Code ) , e . MessageWithoutCode ) ;
8482 if ( MonoAndroidHelper . LogInternalExceptions )
8583 Log . LogMessage ( e . ToString ( ) ) ;
86- } finally {
87- // Delete our temp directory
88- if ( Directory . Exists ( temp ) )
89- Directory . Delete ( temp , true ) ;
9084 }
9185
9286 if ( Log . HasLoggedErrors ) {
@@ -101,10 +95,12 @@ public override bool Execute ()
10195 return ! Log . HasLoggedErrors ;
10296 }
10397
104- void Run ( DirectoryAssemblyResolver res , string temp )
98+ void Run ( DirectoryAssemblyResolver res )
10599 {
106100 PackageNamingPolicy pnp ;
107101 JavaNativeTypeManager . PackageNamingPolicy = Enum . TryParse ( PackageNamingPolicy , out pnp ) ? pnp : PackageNamingPolicyEnum . LowercaseHash ;
102+ var temp = Path . Combine ( Path . GetTempPath ( ) , Path . GetRandomFileName ( ) ) ;
103+ Directory . CreateDirectory ( temp ) ;
108104
109105 foreach ( var dir in FrameworkDirectories ) {
110106 if ( Directory . Exists ( dir . ItemSpec ) )
@@ -115,10 +111,9 @@ void Run (DirectoryAssemblyResolver res, string temp)
115111
116112 // Put every assembly we'll need in the resolver
117113 foreach ( var assembly in ResolvedAssemblies ) {
118- var assemblyFullPath = Path . GetFullPath ( assembly . ItemSpec ) ;
119- res . Load ( assemblyFullPath ) ;
114+ res . Load ( Path . GetFullPath ( assembly . ItemSpec ) ) ;
120115 if ( MonoAndroidHelper . FrameworkAttributeLookupTargets . Any ( a => Path . GetFileName ( assembly . ItemSpec ) == a ) )
121- selectedWhitelistAssemblies . Add ( assemblyFullPath ) ;
116+ selectedWhitelistAssemblies . Add ( Path . GetFullPath ( assembly . ItemSpec ) ) ;
122117 }
123118
124119 // However we only want to look for JLO types in user code
@@ -138,62 +133,72 @@ void Run (DirectoryAssemblyResolver res, string temp)
138133 var java_types = all_java_types . Where ( t => ! JavaTypeScanner . ShouldSkipJavaCallableWrapperGeneration ( t ) ) ;
139134
140135 // Step 2 - Generate Java stub code
141- var success = Generator . CreateJavaSources (
136+ var keep_going = Generator . CreateJavaSources (
142137 Log ,
143138 java_types ,
144139 temp ,
145140 ApplicationJavaClass ,
146141 UseSharedRuntime ,
147142 int . Parse ( AndroidSdkPlatform ) <= 10 ,
148143 ResolvedAssemblies . Any ( assembly => Path . GetFileName ( assembly . ItemSpec ) == "Mono.Android.Export.dll" ) ) ;
149- if ( ! success )
150- return ;
144+
145+ var temp_map_file = Path . Combine ( temp , "acw-map.temp" ) ;
151146
152147 // We need to save a map of .NET type -> ACW type for resource file fixups
153148 var managed = new Dictionary < string , TypeDefinition > ( ) ;
154149 var java = new Dictionary < string , TypeDefinition > ( ) ;
155- using ( var stream = new MemoryStream ( ) )
156- using ( var acw_map = new StreamWriter ( stream ) ) {
157- foreach ( var type in java_types ) {
158- string managedKey = type . FullName . Replace ( '/' , '.' ) ;
159- string javaKey = JavaNativeTypeManager . ToJniName ( type ) . Replace ( '/' , '.' ) ;
160-
161- acw_map . WriteLine ( "{0};{1}" , type . GetPartialAssemblyQualifiedName ( ) , javaKey ) ;
162-
163- TypeDefinition conflict ;
164- if ( managed . TryGetValue ( managedKey , out conflict ) ) {
165- Log . LogWarning (
166- "Duplicate managed type found! Mappings between managed types and Java types must be unique. " +
167- "First Type: '{0}'; Second Type: '{1}'." ,
168- conflict . GetAssemblyQualifiedName ( ) ,
169- type . GetAssemblyQualifiedName ( ) ) ;
170- Log . LogWarning (
171- "References to the type '{0}' will refer to '{1}'." ,
172- managedKey , conflict . GetAssemblyQualifiedName ( ) ) ;
173- continue ;
174- }
175- if ( java . TryGetValue ( javaKey , out conflict ) ) {
176- Log . LogError (
177- "Duplicate Java type found! Mappings between managed types and Java types must be unique. " +
178- "First Type: '{0}'; Second Type: '{1}'" ,
179- conflict . GetAssemblyQualifiedName ( ) ,
180- type . GetAssemblyQualifiedName ( ) ) ;
181- success = false ;
182- continue ;
183- }
184- managed . Add ( managedKey , type ) ;
185- java . Add ( javaKey , type ) ;
186- acw_map . WriteLine ( "{0};{1}" , managedKey , javaKey ) ;
187- acw_map . WriteLine ( "{0};{1}" , JavaNativeTypeManager . ToCompatJniName ( type ) . Replace ( '/' , '.' ) , javaKey ) ;
150+ var acw_map = new StreamWriter ( temp_map_file ) ;
151+
152+ foreach ( var type in java_types ) {
153+ string managedKey = type . FullName . Replace ( '/' , '.' ) ;
154+ string javaKey = JavaNativeTypeManager . ToJniName ( type ) . Replace ( '/' , '.' ) ;
155+
156+ acw_map . WriteLine ( "{0};{1}" , type . GetPartialAssemblyQualifiedName ( ) , javaKey ) ;
157+
158+ TypeDefinition conflict ;
159+ if ( managed . TryGetValue ( managedKey , out conflict ) ) {
160+ Log . LogWarning (
161+ "Duplicate managed type found! Mappings between managed types and Java types must be unique. " +
162+ "First Type: '{0}'; Second Type: '{1}'." ,
163+ conflict . GetAssemblyQualifiedName ( ) ,
164+ type . GetAssemblyQualifiedName ( ) ) ;
165+ Log . LogWarning (
166+ "References to the type '{0}' will refer to '{1}'." ,
167+ managedKey , conflict . GetAssemblyQualifiedName ( ) ) ;
168+ continue ;
169+ }
170+ if ( java . TryGetValue ( javaKey , out conflict ) ) {
171+ Log . LogError (
172+ "Duplicate Java type found! Mappings between managed types and Java types must be unique. " +
173+ "First Type: '{0}'; Second Type: '{1}'" ,
174+ conflict . GetAssemblyQualifiedName ( ) ,
175+ type . GetAssemblyQualifiedName ( ) ) ;
176+ keep_going = false ;
177+ continue ;
188178 }
179+ managed . Add ( managedKey , type ) ;
180+ java . Add ( javaKey , type ) ;
181+ acw_map . WriteLine ( "{0};{1}" , managedKey , javaKey ) ;
182+ acw_map . WriteLine ( "{0};{1}" , JavaNativeTypeManager . ToCompatJniName ( type ) . Replace ( '/' , '.' ) , javaKey ) ;
183+ }
184+
185+ acw_map . Close ( ) ;
189186
190- acw_map . Flush ( ) ;
191- MonoAndroidHelper . CopyIfStreamChanged ( stream , AcwMapFile ) ;
187+ //The previous steps found an error, so we must abort and not generate any further output
188+ //We must do so subsequent unchanged builds fail too.
189+ if ( ! keep_going ) {
190+ File . Delete ( temp_map_file ) ;
191+ return ;
192192 }
193193
194+ MonoAndroidHelper . CopyIfChanged ( temp_map_file , AcwMapFile ) ;
195+
196+ try { File . Delete ( temp_map_file ) ; } catch ( Exception ) { }
197+
194198 // Only overwrite files if the contents actually changed
195199 foreach ( var file in Directory . GetFiles ( temp , "*" , SearchOption . AllDirectories ) ) {
196- var dest = Path . Combine ( OutputDirectory , "src" , file . Substring ( temp . Length + 1 ) ) ;
200+ var dest = Path . GetFullPath ( Path . Combine ( OutputDirectory , "src" , file . Substring ( temp . Length + 1 ) ) ) ;
201+
197202 MonoAndroidHelper . CopyIfChanged ( file , dest ) ;
198203 }
199204
@@ -214,24 +219,38 @@ void Run (DirectoryAssemblyResolver res, string temp)
214219
215220 var additionalProviders = manifest . Merge ( all_java_types , selectedWhitelistAssemblies , ApplicationJavaClass , EmbedAssemblies , BundledWearApplicationName , MergedManifestDocuments ) ;
216221
217- using ( var stream = new MemoryStream ( ) ) {
218- manifest . Save ( stream ) ;
222+ var temp_manifest = Path . Combine ( temp , "AndroidManifest.xml" ) ;
223+ var real_manifest = Path . GetFullPath ( MergedAndroidManifestOutput ) ;
219224
220- // Only write the new manifest if it actually changed
221- MonoAndroidHelper . CopyIfStreamChanged ( stream , MergedAndroidManifestOutput ) ;
222- }
225+ manifest . Save ( temp_manifest ) ;
226+
227+ // Only write the new manifest if it actually changed
228+ MonoAndroidHelper . CopyIfChanged ( temp_manifest , real_manifest ) ;
223229
224230 // Create additional runtime provider java sources.
225231 string providerTemplateFile = UseSharedRuntime ? "MonoRuntimeProvider.Shared.java" : "MonoRuntimeProvider.Bundled.java" ;
226- string providerTemplate = GetResource < JavaCallableWrapperGenerator > ( providerTemplateFile ) ;
232+ string providerTemplate = new StreamReader ( typeof ( JavaCallableWrapperGenerator ) . Assembly . GetManifestResourceStream ( providerTemplateFile ) ) . ReadToEnd ( ) ;
227233
228234 foreach ( var provider in additionalProviders ) {
229- var contents = providerTemplate . Replace ( "MonoRuntimeProvider" , provider ) ;
230- var real_provider = Path . Combine ( OutputDirectory , "src" , "mono" , provider + ".java" ) ;
231- MonoAndroidHelper . CopyIfStringChanged ( contents , real_provider ) ;
235+ var temp_provider = Path . Combine ( temp , provider + ".java" ) ;
236+ File . WriteAllText ( temp_provider , providerTemplate . Replace ( "MonoRuntimeProvider" , provider ) ) ;
237+ var real_provider_dir = Path . GetFullPath ( Path . Combine ( OutputDirectory , "src" , "mono" ) ) ;
238+ Directory . CreateDirectory ( real_provider_dir ) ;
239+ var real_provider = Path . Combine ( real_provider_dir , provider + ".java" ) ;
240+ MonoAndroidHelper . CopyIfChanged ( temp_provider , real_provider ) ;
232241 }
233242
234243 // Create additional application java sources.
244+
245+ Action < string , string , string , Func < string , string > > save = ( resource , filename , destDir , applyTemplate ) => {
246+ string temp_file = Path . Combine ( temp , filename ) ;
247+ string template = applyTemplate ( new StreamReader ( typeof ( GenerateJavaStubs ) . Assembly . GetManifestResourceStream ( resource ) ) . ReadToEnd ( ) ) ;
248+ File . WriteAllText ( temp_file , template ) ;
249+ Directory . CreateDirectory ( destDir ) ;
250+ var real_file = Path . Combine ( destDir , filename ) ;
251+ MonoAndroidHelper . CopyIfChanged ( temp_file , real_file ) ;
252+ } ;
253+
235254 StringWriter regCallsWriter = new StringWriter ( ) ;
236255 regCallsWriter . WriteLine ( "\t \t // Application and Instrumentation ACWs must be registered first." ) ;
237256 foreach ( var type in java_types ) {
@@ -243,28 +262,17 @@ void Run (DirectoryAssemblyResolver res, string temp)
243262 }
244263 regCallsWriter . Close ( ) ;
245264
246- var real_app_dir = Path . Combine ( OutputDirectory , "src" , "mono" , "android" , "app" ) ;
265+ var real_app_dir = Path . GetFullPath ( Path . Combine ( OutputDirectory , "src" , "mono" , "android" , "app" ) ) ;
247266 string applicationTemplateFile = "ApplicationRegistration.java" ;
248- SaveResource ( applicationTemplateFile , applicationTemplateFile , real_app_dir ,
267+ save ( applicationTemplateFile , applicationTemplateFile , real_app_dir ,
249268 template => template . Replace ( "// REGISTER_APPLICATION_AND_INSTRUMENTATION_CLASSES_HERE" , regCallsWriter . ToString ( ) ) ) ;
250269
251270 // Create NotifyTimeZoneChanges java sources.
252271 string notifyTimeZoneChangesFile = "NotifyTimeZoneChanges.java" ;
253- SaveResource ( notifyTimeZoneChangesFile , notifyTimeZoneChangesFile , real_app_dir , template => template ) ;
254- }
255-
256- string GetResource < T > ( string resource )
257- {
258- using ( var stream = typeof ( T ) . Assembly . GetManifestResourceStream ( resource ) )
259- using ( var reader = new StreamReader ( stream ) )
260- return reader . ReadToEnd ( ) ;
261- }
262-
263- void SaveResource ( string resource , string filename , string destDir , Func < string , string > applyTemplate )
264- {
265- string template = GetResource < GenerateJavaStubs > ( resource ) ;
266- template = applyTemplate ( template ) ;
267- MonoAndroidHelper . CopyIfStringChanged ( template , Path . Combine ( destDir , filename ) ) ;
272+ save ( notifyTimeZoneChangesFile , notifyTimeZoneChangesFile , real_app_dir , template => template ) ;
273+
274+ // Delete our temp directory
275+ try { Directory . Delete ( temp , true ) ; } catch ( Exception ) { }
268276 }
269277
270278 void WriteTypeMappings ( List < TypeDefinition > types )
@@ -279,10 +287,11 @@ void WriteTypeMappings (List<TypeDefinition> types)
279287
280288 void UpdateWhenChanged ( string path , Action < Stream > generator )
281289 {
282- using ( var stream = new MemoryStream ( ) ) {
283- generator ( stream ) ;
284- MonoAndroidHelper . CopyIfStreamChanged ( stream , path ) ;
285- }
290+ var np = path + ".new" ;
291+ using ( var o = File . OpenWrite ( np ) )
292+ generator ( o ) ;
293+ MonoAndroidHelper . CopyIfChanged ( np , path ) ;
294+ File . Delete ( np ) ;
286295 }
287296 }
288297}
0 commit comments