void AddAssemblyConfigEntry(ZipArchiveEx apk, string assemblyPath, string configFile) { string inArchivePath = assemblyPath + Path.GetFileName(configFile); existingEntries.Remove(inArchivePath); if (!File.Exists(configFile)) { return; } CompressionMethod compressionMethod = UncompressedMethod; if (apk.SkipExistingFile(configFile, inArchivePath, compressionMethod)) { Log.LogDebugMessage($"Skipping {configFile} as the archive file is up to date."); return; } Log.LogDebugMessage($"Adding {configFile} as the archive file is out of date."); using (var source = File.OpenRead(configFile)) { var dest = new MemoryStream(); source.CopyTo(dest); dest.WriteByte(0); dest.Position = 0; apk.Archive.AddEntry(inArchivePath, dest, compressionMethod); } }
bool AddFileToArchiveIfNewer (ZipArchiveEx apk, string file, string inArchivePath, CompressionMethod compressionMethod = CompressionMethod.Default) { existingEntries.Remove (inArchivePath); if (apk.SkipExistingFile (file, inArchivePath, compressionMethod)) { Log.LogDebugMessage ($"Skipping {file} as the archive file is up to date."); return false; } Log.LogDebugMessage ($"Adding {file} as the archive file is out of date."); apk.Archive.AddFile (file, inArchivePath, compressionMethod: compressionMethod); return true; }
void AddNativeLibraryToArchive (ZipArchiveEx apk, string abi, string filesystemPath, string inArchiveFileName) { string archivePath = $"lib/{abi}/{inArchiveFileName}"; existingEntries.Remove (archivePath); CompressionMethod compressionMethod = GetCompressionMethod (archivePath); if (apk.SkipExistingFile (filesystemPath, archivePath, compressionMethod)) { Log.LogDebugMessage ($"Skipping {filesystemPath} (APK path: {archivePath}) as it is up to date."); return; } Log.LogDebugMessage ($"Adding native library: {filesystemPath} (APK path: {archivePath})"); apk.Archive.AddEntry (archivePath, File.OpenRead (filesystemPath), compressionMethod); }
void ExecuteWithAbi(string [] supportedAbis, string apkInputPath, string apkOutputPath, bool debug, bool compress, IDictionary <string, CompressedAssemblyInfo> compressedAssembliesInfo) { if (InterpreterEnabled) { foreach (string abi in supportedAbis) { if (String.Compare("x86", abi, StringComparison.OrdinalIgnoreCase) == 0) { Log.LogCodedError("XA0124", Properties.Resources.XA0124); return; } } } ArchiveFileList files = new ArchiveFileList(); bool refresh = true; if (apkInputPath != null && File.Exists(apkInputPath) && !File.Exists(apkOutputPath)) { Log.LogDebugMessage($"Copying {apkInputPath} to {apkInputPath}"); File.Copy(apkInputPath, apkOutputPath, overwrite: true); refresh = false; } using (var apk = new ZipArchiveEx(apkOutputPath, File.Exists(apkOutputPath) ? FileMode.Open : FileMode.Create)) { if (refresh) { for (long i = 0; i < apk.Archive.EntryCount; i++) { ZipEntry e = apk.Archive.ReadEntry((ulong)i); Log.LogDebugMessage($"Registering item {e.FullName}"); existingEntries.Add(e.FullName); } } if (apkInputPath != null && File.Exists(apkInputPath) && refresh) { var lastWriteOutput = File.Exists(apkOutputPath) ? File.GetLastWriteTimeUtc(apkOutputPath) : DateTime.MinValue; var lastWriteInput = File.GetLastWriteTimeUtc(apkInputPath); using (var packaged = new ZipArchiveEx(apkInputPath, FileMode.Open)) { foreach (var entry in packaged.Archive) { // NOTE: aapt2 is creating zip entries on Windows such as `assets\subfolder/asset2.txt` var entryName = entry.FullName; if (entryName.Contains("\\")) { entryName = entryName.Replace('\\', '/'); Log.LogDebugMessage($"Fixing up malformed entry `{entry.FullName}` -> `{entryName}`"); } Log.LogDebugMessage($"Deregistering item {entryName}"); existingEntries.Remove(entryName); if (lastWriteInput <= lastWriteOutput) { continue; } if (apk.Archive.ContainsEntry(entryName)) { ZipEntry e = apk.Archive.ReadEntry(entryName); // check the CRC values as the ModifiedDate is always 01/01/1980 in the aapt generated file. if (entry.CRC == e.CRC && entry.CompressedSize == e.CompressedSize) { Log.LogDebugMessage($"Skipping {entryName} from {apkInputPath} as its up to date."); continue; } } var ms = new MemoryStream(); entry.Extract(ms); Log.LogDebugMessage($"Refreshing {entryName} from {apkInputPath}"); apk.Archive.AddStream(ms, entryName, compressionMethod: entry.CompressionMethod); } } } apk.FixupWindowsPathSeparators((a, b) => Log.LogDebugMessage($"Fixing up malformed entry `{a}` -> `{b}`")); // Add classes.dx foreach (var dex in DalvikClasses) { string apkName = dex.GetMetadata("ApkName"); string dexPath = string.IsNullOrWhiteSpace(apkName) ? Path.GetFileName(dex.ItemSpec) : apkName; AddFileToArchiveIfNewer(apk, dex.ItemSpec, DalvikPath + dexPath); } if (EmbedAssemblies && !BundleAssemblies) { AddAssemblies(apk, debug, compress, compressedAssembliesInfo); } AddRuntimeLibraries(apk, supportedAbis); apk.Flush(); AddNativeLibraries(files, supportedAbis); AddAdditionalNativeLibraries(files, supportedAbis); if (TypeMappings != null) { foreach (ITaskItem typemap in TypeMappings) { AddFileToArchiveIfNewer(apk, typemap.ItemSpec, RootPath + Path.GetFileName(typemap.ItemSpec), compressionMethod: UncompressedMethod); } } int count = 0; foreach (var file in files) { var item = Path.Combine(file.archivePath.Replace(Path.DirectorySeparatorChar, '/')); existingEntries.Remove(item); CompressionMethod compressionMethod = GetCompressionMethod(file.filePath); if (apk.SkipExistingFile(file.filePath, item, compressionMethod)) { Log.LogDebugMessage($"Skipping {file.filePath} as the archive file is up to date."); continue; } Log.LogDebugMessage("\tAdding {0}", file.filePath); apk.Archive.AddFile(file.filePath, item, compressionMethod: compressionMethod); count++; if (count >= ZipArchiveEx.ZipFlushFilesLimit) { apk.Flush(); count = 0; } } var jarFiles = (JavaSourceFiles != null) ? JavaSourceFiles.Where(f => f.ItemSpec.EndsWith(".jar")) : null; if (jarFiles != null && JavaLibraries != null) { jarFiles = jarFiles.Concat(JavaLibraries); } else if (JavaLibraries != null) { jarFiles = JavaLibraries; } var libraryProjectJars = MonoAndroidHelper.ExpandFiles(LibraryProjectJars) .Where(jar => !MonoAndroidHelper.IsEmbeddedReferenceJar(jar)); var jarFilePaths = libraryProjectJars.Concat(jarFiles != null ? jarFiles.Select(j => j.ItemSpec) : Enumerable.Empty <string> ()); jarFilePaths = MonoAndroidHelper.DistinctFilesByContent(jarFilePaths); count = 0; foreach (var jarFile in jarFilePaths) { using (var stream = File.OpenRead(jarFile)) using (var jar = ZipArchive.Open(stream)) { foreach (var jarItem in jar) { if (jarItem.IsDirectory) { continue; } var name = jarItem.FullName; if (!PackagingUtils.CheckEntryForPackaging(name)) { continue; } var path = RootPath + name; existingEntries.Remove(path); if (apk.SkipExistingEntry(jarItem, path)) { Log.LogDebugMessage($"Skipping {path} as the archive file is up to date."); continue; } if (apk.Archive.Any(e => e.FullName == path)) { Log.LogDebugMessage("Failed to add jar entry {0} from {1}: the same file already exists in the apk", name, Path.GetFileName(jarFile)); continue; } byte [] data; using (var d = new MemoryStream()) { jarItem.Extract(d); data = d.ToArray(); } Log.LogDebugMessage($"Adding {path} as the archive file is out of date."); apk.Archive.AddEntry(data, path); } } count++; if (count >= ZipArchiveEx.ZipFlushFilesLimit) { apk.Flush(); count = 0; } } // Clean up Removed files. foreach (var entry in existingEntries) { Log.LogDebugMessage($"Removing {entry} as it is not longer required."); apk.Archive.DeleteEntry(entry); } apk.Flush(); FixupArchive(apk); } }