1313
1414using Java . Interop . Tools . Cecil ;
1515
16- using ArchiveFileList = System . Collections . Generic . List < System . Tuple < string , string > > ;
16+ using ArchiveFileList = System . Collections . Generic . List < ( string filePath , string archivePath ) > ;
1717using Mono . Cecil ;
1818using Xamarin . Android . Tools ;
1919using Xamarin . Tools . Zip ;
@@ -109,22 +109,61 @@ bool _Debug {
109109
110110 protected virtual void FixupArchive ( ZipArchiveEx zip ) { }
111111
112+ List < string > existingEntries = new List < string > ( ) ;
113+
112114 void ExecuteWithAbi ( string [ ] supportedAbis , string apkInputPath , string apkOutputPath )
113115 {
114- var temp = apkOutputPath + "new" ;
115116 ArchiveFileList files = new ArchiveFileList ( ) ;
116- if ( apkInputPath != null )
117- File . Copy ( apkInputPath , temp , overwrite : true ) ;
117+ bool refresh = true ;
118+ if ( apkInputPath != null && File . Exists ( apkInputPath ) && ! File . Exists ( apkOutputPath ) ) {
119+ Log . LogDebugMessage ( $ "Copying { apkInputPath } to { apkInputPath } ") ;
120+ File . Copy ( apkInputPath , apkOutputPath , overwrite : true ) ;
121+ refresh = false ;
122+ }
118123 using ( var notice = Assembly . GetExecutingAssembly ( ) . GetManifestResourceStream ( "NOTICE.txt" ) )
119- using ( var apk = new ZipArchiveEx ( temp , apkInputPath != null ? FileMode . Open : FileMode . Create ) ) {
124+ using ( var apk = new ZipArchiveEx ( apkOutputPath , File . Exists ( apkOutputPath ) ? FileMode . Open : FileMode . Create ) ) {
125+ if ( refresh ) {
126+ for ( long i = 0 ; i < apk . Archive . EntryCount ; i ++ ) {
127+ ZipEntry e = apk . Archive . ReadEntry ( ( ulong ) i ) ;
128+ Log . LogDebugMessage ( $ "Registering item { e . FullName } ") ;
129+ existingEntries . Add ( e . FullName ) ;
130+ }
131+ }
132+ if ( apkInputPath != null && File . Exists ( apkInputPath ) && refresh ) {
133+ var lastWriteOutput = File . Exists ( apkOutputPath ) ? File . GetLastWriteTimeUtc ( apkOutputPath ) : DateTime . MinValue ;
134+ var lastWriteInput = File . GetLastWriteTimeUtc ( apkInputPath ) ;
135+ using ( var packaged = new ZipArchiveEx ( apkInputPath , FileMode . Open ) ) {
136+ foreach ( var entry in packaged . Archive ) {
137+ Log . LogDebugMessage ( $ "Deregistering item { entry . FullName } ") ;
138+ existingEntries . Remove ( entry . FullName ) ;
139+ if ( lastWriteInput <= lastWriteOutput )
140+ continue ;
141+ if ( apk . Archive . ContainsEntry ( entry . FullName ) ) {
142+ ZipEntry e = apk . Archive . ReadEntry ( entry . FullName ) ;
143+ // check the CRC values as the ModifiedDate is always 01/01/1980 in the aapt generated file.
144+ if ( entry . CRC == e . CRC ) {
145+ Log . LogDebugMessage ( $ "Skipping { entry . FullName } from { apkInputPath } as its up to date.") ;
146+ continue ;
147+ }
148+ }
149+ var ms = new MemoryStream ( ) ;
150+ entry . Extract ( ms ) ;
151+ Log . LogDebugMessage ( $ "Refreshing { entry . FullName } from { apkInputPath } ") ;
152+ apk . Archive . AddStream ( ms , entry . FullName , compressionMethod : entry . CompressionMethod ) ;
153+ }
154+ }
155+ }
120156 apk . FixupWindowsPathSeparators ( ( a , b ) => Log . LogDebugMessage ( $ "Fixing up malformed entry `{ a } ` -> `{ b } `") ) ;
121- apk . Archive . AddEntry ( RootPath + "NOTICE" , notice ) ;
157+ string noticeName = RootPath + "NOTICE" ;
158+ existingEntries . Remove ( noticeName ) ;
159+ if ( ! apk . Archive . ContainsEntry ( noticeName ) )
160+ apk . Archive . AddEntry ( noticeName , notice ) ;
122161
123162 // Add classes.dx
124163 foreach ( var dex in DalvikClasses ) {
125164 string apkName = dex . GetMetadata ( "ApkName" ) ;
126165 string dexPath = string . IsNullOrWhiteSpace ( apkName ) ? Path . GetFileName ( dex . ItemSpec ) : apkName ;
127- apk . Archive . AddFile ( dex . ItemSpec , DalvikPath + dexPath ) ;
166+ AddFileToArchiveIfNewer ( apk , dex . ItemSpec , DalvikPath + dexPath ) ;
128167 }
129168
130169 if ( EmbedAssemblies && ! BundleAssemblies )
@@ -133,25 +172,25 @@ void ExecuteWithAbi (string [] supportedAbis, string apkInputPath, string apkOut
133172 AddRuntimeLibraries ( apk , supportedAbis ) ;
134173 apk . Flush ( ) ;
135174 AddNativeLibraries ( files , supportedAbis ) ;
136- apk . Flush ( ) ;
137175 AddAdditionalNativeLibraries ( files , supportedAbis ) ;
138- apk . Flush ( ) ;
139176
140177 if ( TypeMappings != null ) {
141178 foreach ( ITaskItem typemap in TypeMappings ) {
142- apk . Archive . AddFile ( typemap . ItemSpec , RootPath + Path . GetFileName ( typemap . ItemSpec ) , compressionMethod : UncompressedMethod ) ;
179+ AddFileToArchiveIfNewer ( apk , typemap . ItemSpec , RootPath + Path . GetFileName ( typemap . ItemSpec ) , compressionMethod : UncompressedMethod ) ;
143180 }
144181 }
145182
146183 int count = 0 ;
147184 foreach ( var file in files ) {
148- var item = Path . Combine ( file . Item2 , Path . GetFileName ( file . Item1 ) )
185+ var item = Path . Combine ( file . archivePath , Path . GetFileName ( file . filePath ) )
149186 . Replace ( Path . DirectorySeparatorChar , '/' ) ;
150- if ( apk . Archive . ContainsEntry ( item ) ) {
151- Log . LogCodedWarning ( "XA4301" , file . Item1 , 0 , "Apk already contains the item {0}; ignoring." , item ) ;
187+ existingEntries . Remove ( item ) ;
188+ if ( apk . SkipExistingFile ( file . filePath , item ) ) {
189+ Log . LogDebugMessage ( $ "Skipping { file . filePath } as the archive file is up to date.") ;
152190 continue ;
153191 }
154- apk . Archive . AddFile ( file . Item1 , item , compressionMethod : GetCompressionMethod ( file . Item1 ) ) ;
192+ Log . LogDebugMessage ( "\t Adding {0}" , file . filePath ) ;
193+ apk . Archive . AddFile ( file . filePath , item , compressionMethod : GetCompressionMethod ( file . filePath ) ) ;
155194 count ++ ;
156195 if ( count == ZipArchiveEx . ZipFlushLimit ) {
157196 apk . Flush ( ) ;
@@ -181,16 +220,19 @@ void ExecuteWithAbi (string [] supportedAbis, string apkInputPath, string apkOut
181220 if ( ! PackagingUtils . CheckEntryForPackaging ( name ) ) {
182221 continue ;
183222 }
223+ var path = RootPath + name ;
224+ existingEntries . Remove ( path ) ;
225+ if ( apk . SkipExistingEntry ( jarItem , path ) ) {
226+ Log . LogDebugMessage ( $ "Skipping { path } as the archive file is up to date.") ;
227+ continue ;
228+ }
184229 byte [ ] data ;
185- using ( var d = new System . IO . MemoryStream ( ) ) {
230+ using ( var d = new MemoryStream ( ) ) {
186231 jarItem . Extract ( d ) ;
187232 data = d . ToArray ( ) ;
188233 }
189- var path = RootPath + name ;
190- if ( apk . Archive . Any ( e => e . FullName == path ) )
191- Log . LogMessage ( "Warning: failed to add jar entry {0} from {1}: the same file already exists in the apk" , name , Path . GetFileName ( jarFile ) ) ;
192- else
193- apk . Archive . AddEntry ( data , path ) ;
234+ Log . LogDebugMessage ( $ "Adding { path } as the archive file is out of date.") ;
235+ apk . Archive . AddEntry ( data , path ) ;
194236 }
195237 }
196238 count ++ ;
@@ -199,14 +241,14 @@ void ExecuteWithAbi (string [] supportedAbis, string apkInputPath, string apkOut
199241 count = 0 ;
200242 }
201243 }
244+ // Clean up Removed files.
245+ foreach ( var entry in existingEntries ) {
246+ Log . LogDebugMessage ( $ "Removing { entry } as it is not longer required.") ;
247+ apk . Archive . DeleteEntry ( entry ) ;
248+ }
249+ apk . Flush ( ) ;
202250 FixupArchive ( apk ) ;
203251 }
204- if ( MonoAndroidHelper . CopyIfZipChanged ( temp , apkOutputPath ) ) {
205- Log . LogDebugMessage ( $ "Copied { temp } to { apkOutputPath } ") ;
206- } else {
207- Log . LogDebugMessage ( $ "Skipped { apkOutputPath } : up to date") ;
208- }
209- File . Delete ( temp ) ;
210252 }
211253
212254 public override bool RunTask ( )
@@ -220,10 +262,12 @@ public override bool RunTask ()
220262 var outputFiles = new List < string > ( ) ;
221263 uncompressedFileExtensions = UncompressedFileExtensions ? . Split ( new char [ ] { ';' , ',' } , StringSplitOptions . RemoveEmptyEntries ) ?? new string [ 0 ] ;
222264
265+ existingEntries . Clear ( ) ;
223266 ExecuteWithAbi ( SupportedAbis , ApkInputPath , ApkOutputPath ) ;
224267 outputFiles . Add ( ApkOutputPath ) ;
225268 if ( CreatePackagePerAbi && SupportedAbis . Length > 1 ) {
226269 foreach ( var abi in SupportedAbis ) {
270+ existingEntries . Clear ( ) ;
227271 var path = Path . GetDirectoryName ( ApkOutputPath ) ;
228272 var apk = Path . GetFileNameWithoutExtension ( ApkOutputPath ) ;
229273 ExecuteWithAbi ( new [ ] { abi } , String . Format ( "{0}-{1}" , ApkInputPath , abi ) ,
@@ -254,7 +298,7 @@ private void AddAssemblies (ZipArchiveEx apk)
254298 Log . LogCodedWarning ( "XA0107" , assembly . ItemSpec , 0 , "{0} is a Reference Assembly!" , assembly . ItemSpec ) ;
255299 }
256300 // Add assembly
257- apk . Archive . AddFile ( assembly . ItemSpec , GetTargetDirectory ( assembly . ItemSpec ) + "/" + Path . GetFileName ( assembly . ItemSpec ) , compressionMethod : UncompressedMethod ) ;
301+ AddFileToArchiveIfNewer ( apk , assembly . ItemSpec , GetTargetDirectory ( assembly . ItemSpec ) + "/" + Path . GetFileName ( assembly . ItemSpec ) , compressionMethod : UncompressedMethod ) ;
258302
259303 // Try to add config if exists
260304 var config = Path . ChangeExtension ( assembly . ItemSpec , "dll.config" ) ;
@@ -265,12 +309,12 @@ private void AddAssemblies (ZipArchiveEx apk)
265309 var symbols = Path . ChangeExtension ( assembly . ItemSpec , "dll.mdb" ) ;
266310
267311 if ( File . Exists ( symbols ) )
268- apk . Archive . AddFile ( symbols , AssembliesPath + Path . GetFileName ( symbols ) , compressionMethod : UncompressedMethod ) ;
312+ AddFileToArchiveIfNewer ( apk , symbols , AssembliesPath + Path . GetFileName ( symbols ) , compressionMethod : UncompressedMethod ) ;
269313
270314 symbols = Path . ChangeExtension ( assembly . ItemSpec , "pdb" ) ;
271315
272316 if ( File . Exists ( symbols ) )
273- apk . Archive . AddFile ( symbols , AssembliesPath + Path . GetFileName ( symbols ) , compressionMethod : UncompressedMethod ) ;
317+ AddFileToArchiveIfNewer ( apk , symbols , AssembliesPath + Path . GetFileName ( symbols ) , compressionMethod : UncompressedMethod ) ;
274318 }
275319 count ++ ;
276320 if ( count == ZipArchiveEx . ZipFlushLimit ) {
@@ -292,20 +336,20 @@ private void AddAssemblies (ZipArchiveEx apk)
292336 if ( MonoAndroidHelper . IsReferenceAssembly ( assembly . ItemSpec ) ) {
293337 Log . LogCodedWarning ( "XA0107" , assembly . ItemSpec , 0 , "{0} is a Reference Assembly!" , assembly . ItemSpec ) ;
294338 }
295- apk . Archive . AddFile ( assembly . ItemSpec , AssembliesPath + Path . GetFileName ( assembly . ItemSpec ) , compressionMethod : UncompressedMethod ) ;
339+ AddFileToArchiveIfNewer ( apk , assembly . ItemSpec , AssembliesPath + Path . GetFileName ( assembly . ItemSpec ) , compressionMethod : UncompressedMethod ) ;
296340 var config = Path . ChangeExtension ( assembly . ItemSpec , "dll.config" ) ;
297341 AddAssemblyConfigEntry ( apk , config ) ;
298342 // Try to add symbols if Debug
299343 if ( debug ) {
300344 var symbols = Path . ChangeExtension ( assembly . ItemSpec , "dll.mdb" ) ;
301345
302346 if ( File . Exists ( symbols ) )
303- apk . Archive . AddFile ( symbols , AssembliesPath + Path . GetFileName ( symbols ) , compressionMethod : UncompressedMethod ) ;
347+ AddFileToArchiveIfNewer ( apk , symbols , AssembliesPath + Path . GetFileName ( symbols ) , compressionMethod : UncompressedMethod ) ;
304348
305349 symbols = Path . ChangeExtension ( assembly . ItemSpec , "pdb" ) ;
306350
307351 if ( File . Exists ( symbols ) )
308- apk . Archive . AddFile ( symbols , AssembliesPath + Path . GetFileName ( symbols ) , compressionMethod : UncompressedMethod ) ;
352+ AddFileToArchiveIfNewer ( apk , symbols , AssembliesPath + Path . GetFileName ( symbols ) , compressionMethod : UncompressedMethod ) ;
309353 }
310354 count ++ ;
311355 if ( count == ZipArchiveEx . ZipFlushLimit ) {
@@ -315,17 +359,38 @@ private void AddAssemblies (ZipArchiveEx apk)
315359 }
316360 }
317361
362+ bool AddFileToArchiveIfNewer ( ZipArchiveEx apk , string file , string inArchivePath , CompressionMethod compressionMethod = CompressionMethod . Default )
363+ {
364+ existingEntries . Remove ( inArchivePath ) ;
365+ if ( apk . SkipExistingFile ( file , inArchivePath ) ) {
366+ Log . LogDebugMessage ( $ "Skipping { file } as the archive file is up to date.") ;
367+ return false ;
368+ }
369+ Log . LogDebugMessage ( $ "Adding { file } as the archive file is out of date.") ;
370+ apk . Archive . AddFile ( file , inArchivePath , compressionMethod : compressionMethod ) ;
371+ return true ;
372+ }
373+
318374 void AddAssemblyConfigEntry ( ZipArchiveEx apk , string configFile )
319375 {
376+ string inArchivePath = AssembliesPath + Path . GetFileName ( configFile ) ;
377+ existingEntries . Remove ( inArchivePath ) ;
378+
320379 if ( ! File . Exists ( configFile ) )
321380 return ;
322381
382+ if ( apk . SkipExistingFile ( configFile , inArchivePath ) ) {
383+ Log . LogDebugMessage ( $ "Skipping { configFile } as the archive file is up to date.") ;
384+ return ;
385+ }
386+
387+ Log . LogDebugMessage ( $ "Adding { configFile } as the archive file is out of date.") ;
323388 using ( var source = File . OpenRead ( configFile ) ) {
324389 var dest = new MemoryStream ( ) ;
325390 source . CopyTo ( dest ) ;
326391 dest . WriteByte ( 0 ) ;
327392 dest . Position = 0 ;
328- apk . Archive . AddEntry ( AssembliesPath + Path . GetFileName ( configFile ) , dest , compressionMethod : UncompressedMethod ) ;
393+ apk . Archive . AddEntry ( inArchivePath , dest , compressionMethod : UncompressedMethod ) ;
329394 }
330395 }
331396
@@ -380,6 +445,11 @@ CompressionMethod GetCompressionMethod (string fileName)
380445 void AddNativeLibraryToArchive ( ZipArchiveEx apk , string abi , string filesystemPath , string inArchiveFileName )
381446 {
382447 string archivePath = $ "lib/{ abi } /{ inArchiveFileName } ";
448+ existingEntries . Remove ( archivePath ) ;
449+ if ( apk . SkipExistingFile ( filesystemPath , archivePath ) ) {
450+ Log . LogDebugMessage ( $ "Skipping { filesystemPath } (APK path: { archivePath } ) as it is up to date.") ;
451+ return ;
452+ }
383453 Log . LogDebugMessage ( $ "Adding native library: { filesystemPath } (APK path: { archivePath } )") ;
384454 apk . Archive . AddEntry ( archivePath , File . OpenRead ( filesystemPath ) , compressionMethod : GetCompressionMethod ( archivePath ) ) ;
385455 }
@@ -484,8 +554,14 @@ private void AddAdditionalNativeLibraries (ArchiveFileList files, string [] supp
484554
485555 void AddNativeLibrary ( ArchiveFileList files , string path , string abi )
486556 {
487- Log . LogMessage ( MessageImportance . Low , "\t Adding {0}" , path ) ;
488- files . Add ( new Tuple < string , string > ( path , string . Format ( "lib/{0}" , abi ) ) ) ;
557+ var item = ( filePath : path , archivePath : $ "lib/{ abi } ") ;
558+ string filename = "/" + Path . GetFileName ( item . filePath ) ;
559+ string inArchivePath = item . archivePath + filename ;
560+ if ( files . Any ( x => ( x . archivePath + "/" + Path . GetFileName ( x . filePath ) ) == inArchivePath ) ) {
561+ Log . LogCodedWarning ( "XA4301" , path , 0 , $ "Apk already contains the item { inArchivePath } ; ignoring.") ;
562+ return ;
563+ }
564+ files . Add ( item ) ;
489565 }
490566 }
491567}
0 commit comments