public override bool Execute() { Log.LogDebugTaskItems(" JavaSourceFiles:", JavaSourceFiles); Log.LogDebugTaskItems(" JavaLibraries:", JavaLibraries); Log.LogDebugTaskItems(" LibraryProjectJars:", LibraryProjectJars); 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 jarFilePaths = (LibraryProjectJars ?? new ITaskItem [0]).Concat(jarFiles ?? new ITaskItem [0]).Select(j => j.ItemSpec); // Remove duplicate identical jars by name, size and content, and reject any jars that conflicts by name (i.e. different content). var jars = MonoAndroidHelper.DistinctFilesByContent(jarFilePaths).ToArray(); var dups = MonoAndroidHelper.GetDuplicateFileNames(jars, new string [] { "classes.jar" }); if (dups.Any()) { Log.LogError("You have Jar libraries, {0}, that have the identical name with inconsistent file contents. Please make sure to remove any conflicting libraries in EmbeddedJar, InputJar and AndroidJavaLibrary.", String.Join(", ", dups.ToArray())); return(false); } return(true); }
private void GenerateResponseFile() { TemporarySourceListFile = Path.GetTempFileName(); using (var sw = new StreamWriter(path: TemporarySourceListFile, append: false, encoding: new UTF8Encoding(encoderShouldEmitUTF8Identifier: false))) { // Include any user .java files if (JavaSourceFiles != null) { foreach (var file in JavaSourceFiles.Where(p => Path.GetExtension(p.ItemSpec) == ".java")) { sw.WriteLine(string.Format("\"{0}\"", file.ItemSpec.Replace(@"\", @"\\"))); } } foreach (var file in Directory.GetFiles(StubSourceDirectory, "*.java", SearchOption.AllDirectories)) { // This makes sense. BAD sense. but sense. // Problem: // A perfectly sensible path like "E:\tmp\a.java" generates a // javac error that "E: mp.java" can't be found. // Cause: // javac uses java.io.StreamTokenizer to parse @response files, and // the docs for StreamTokenizer.quoteChar(int) [0] say: // The usual escape sequences such as "\n" and "\t" are recognized // and converted to single characters as the string is parsed. // i.e. '\' is an escape character! // Solution: // Since '\' is an escape character, we need to escape it. // [0] http://download.oracle.com/javase/1.4.2/docs/api/java/io/StreamTokenizer.html#quoteChar(int) sw.WriteLine(string.Format("\"{0}\"", file.Replace(@"\", @"\\").Normalize(NormalizationForm.FormC))); } } }
public override bool RunTask() { 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 jarFilePaths = (LibraryProjectJars ?? new ITaskItem [0]).Concat(jarFiles ?? new ITaskItem [0]).Select(j => j.ItemSpec); // Remove duplicate identical jars by name, size and content, and reject any jars that conflicts by name (i.e. different content). var jars = MonoAndroidHelper.DistinctFilesByContent(jarFilePaths).ToArray(); var dups = MonoAndroidHelper.GetDuplicateFileNames(jars, new string [] { "classes.jar" }); if (dups.Any()) { Log.LogCodedError("XA1014", Properties.Resources.XA1014, String.Join(", ", dups.ToArray())); return(false); } return(true); }
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); } }
void ExecuteWithAbi(string supportedAbis, string apkInputPath, string apkOutputPath) { ArchiveFileList files = new ArchiveFileList(); if (apkInputPath != null) { File.Copy(apkInputPath, apkOutputPath + "new", overwrite: true); } using (var apk = ZipFile.Open(apkOutputPath + "new", apkInputPath != null ? ZipArchiveMode.Update : ZipArchiveMode.Create)) { apk.AddEntry("NOTICE", Assembly.GetExecutingAssembly().GetManifestResourceStream("NOTICE.txt")); // Add classes.dx apk.AddFiles(DalvikClasses, string.Empty); if (EmbedAssemblies && !BundleAssemblies) { AddAssemblies(apk); } AddEnvironment(apk); AddRuntimeLibraries(apk, supportedAbis); AddNativeLibraries(files, supportedAbis); AddAdditionalNativeLibraries(files, supportedAbis); AddNativeLibrariesFromAssemblies(apk, supportedAbis); foreach (ITaskItem typemap in TypeMappings) { apk.AddFile(typemap.ItemSpec, directoryPathInZip: "", compressionLevel: CompressionLevel.NoCompression); } foreach (var file in files) { var item = Path.Combine(file.Item2, Path.GetFileName(file.Item1)) .Replace(Path.DirectorySeparatorChar, '/'); if (apk.ContainsEntry(item)) { Log.LogWarning(null, "XA4301", null, file.Item1, 0, 0, 0, 0, "Apk already contains the item {0}; ignoring.", item); continue; } apk.AddFile(file.Item1, file.Item2); } if (_Debug) { AddGdbservers(apk, files, supportedAbis, debugServer); } 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); foreach (var jarFile in jarFilePaths) { using (var jar = new ZipArchive(File.Open(jarFile, FileMode.Open), ZipArchiveMode.Read)) { foreach (var jarItem in jar.Entries.Where(ze => !ze.IsDirectory() && !ze.FullName.StartsWith("META-INF") && !ze.FullName.EndsWith(".class") && !ze.FullName.EndsWith(".java") && !ze.FullName.EndsWith("MANIFEST.MF"))) { byte[] data; using (var d = new System.IO.MemoryStream()) using (var i = jarItem.Open()) { i.CopyTo(d); data = d.ToArray(); } if (apk.Entries.Any(e => e.FullName == jarItem.FullName)) { Log.LogMessage("Warning: failed to add jar entry {0} from {1}: the same file already exists in the apk", jarItem.FullName, Path.GetFileName(jarFile)); } else { apk.AddEntry(jarItem.FullName, data); } } } } if (StubApplicationDataFile != null && File.Exists(StubApplicationDataFile)) { AddZipEntry(apk, StubApplicationDataFile, string.Empty); } } MonoAndroidHelper.CopyIfZipChanged(apkOutputPath + "new", apkOutputPath); File.Delete(apkOutputPath + "new"); }
public override bool Execute() { Log.LogDebugMessage("DetermineJavaLibrariesToCompile"); Log.LogDebugMessage(" EnableInstantRun: {0}", EnableInstantRun); Log.LogDebugMessage(" MonoPlatformJarPath: {0}", MonoPlatformJarPath); Log.LogDebugTaskItems(" JavaSourceFiles:", JavaSourceFiles); Log.LogDebugTaskItems(" JavaLibraries:", JavaLibraries); Log.LogDebugTaskItems(" ExternalJavaLibraries:", ExternalJavaLibraries); Log.LogDebugTaskItems(" LibraryProjectJars:", LibraryProjectJars); Log.LogDebugTaskItems(" AdditionalJavaLibraryReferences:", AdditionalJavaLibraryReferences); Log.LogDebugTaskItems(" DoNotPackageJavaLibraries:", DoNotPackageJavaLibraries); var jars = new List <ITaskItem> (); if (!EnableInstantRun) { jars.Add(new TaskItem(MonoPlatformJarPath)); } if (JavaSourceFiles != null) { foreach (var jar in JavaSourceFiles.Where(p => Path.GetExtension(p.ItemSpec) == ".jar")) { jars.Add(jar); } } if (JavaLibraries != null) { foreach (var jarfile in JavaLibraries) { jars.Add(jarfile); } } if (LibraryProjectJars != null) { foreach (var jar in LibraryProjectJars) { if (!MonoAndroidHelper.IsEmbeddedReferenceJar(jar.ItemSpec)) { jars.Add(jar); } } } if (AdditionalJavaLibraryReferences != null) { foreach (var jar in AdditionalJavaLibraryReferences.Distinct(TaskItemComparer.DefaultComparer)) { jars.Add(jar); } } var distinct = MonoAndroidHelper.DistinctFilesByContent(jars); jars = jars.Where(j => distinct.Contains(j)).ToList(); JavaLibrariesToCompile = jars.Where(j => !IsExcluded(j.ItemSpec)).ToArray(); ReferenceJavaLibraries = jars.Except(JavaLibrariesToCompile).ToArray(); Log.LogDebugTaskItems(" JavaLibrariesToCompile:", JavaLibrariesToCompile); Log.LogDebugTaskItems(" ReferenceJavaLibraries:", ReferenceJavaLibraries); return(true); }
public override bool RunTask() { var jars = new List <ITaskItem> (); if (!EnableInstantRun) { jars.AddRange(MonoPlatformJarPaths); } if (JavaSourceFiles != null) { foreach (var jar in JavaSourceFiles.Where(p => Path.GetExtension(p.ItemSpec) == ".jar")) { jars.Add(jar); } } if (JavaLibraries != null) { foreach (var jarfile in JavaLibraries) { jars.Add(jarfile); } } if (LibraryProjectJars != null) { foreach (var jar in LibraryProjectJars) { if (!MonoAndroidHelper.IsEmbeddedReferenceJar(jar.ItemSpec)) { jars.Add(jar); } } } var distinct = MonoAndroidHelper.DistinctFilesByContent(jars); var javaLibrariesToCompile = new List <ITaskItem> (); var referenceJavaLibraries = new List <ITaskItem> (); if (ExternalJavaLibraries != null) { referenceJavaLibraries.AddRange(ExternalJavaLibraries); } foreach (var item in distinct) { if (!HasClassFiles(item.ItemSpec)) { continue; } if (IsExcluded(item.ItemSpec)) { referenceJavaLibraries.Add(item); } else { javaLibrariesToCompile.Add(item); } } JavaLibrariesToCompile = javaLibrariesToCompile.ToArray(); ReferenceJavaLibraries = referenceJavaLibraries.ToArray(); Log.LogDebugTaskItems(" JavaLibrariesToCompile:", JavaLibrariesToCompile); Log.LogDebugTaskItems(" ReferenceJavaLibraries:", ReferenceJavaLibraries); return(true); }
void ExecuteWithAbi(string supportedAbis, string apkInputPath, string apkOutputPath) { ArchiveFileList files = new ArchiveFileList(); if (apkInputPath != null) { File.Copy(apkInputPath, apkOutputPath + "new", overwrite: true); } using (var apk = new ZipArchiveEx(apkOutputPath + "new", apkInputPath != null ? FileMode.Open : FileMode.Create)) { apk.Archive.AddEntry("NOTICE", Assembly.GetExecutingAssembly().GetManifestResourceStream("NOTICE.txt")); // Add classes.dx apk.Archive.AddFiles(DalvikClasses, useFileDirectories: false); if (EmbedAssemblies && !BundleAssemblies) { AddAssemblies(apk); } AddEnvironment(apk); AddRuntimeLibraries(apk, supportedAbis); apk.Flush(); AddNativeLibraries(files, supportedAbis); apk.Flush(); AddAdditionalNativeLibraries(files, supportedAbis); apk.Flush(); if (TypeMappings != null) { foreach (ITaskItem typemap in TypeMappings) { apk.Archive.AddFile(typemap.ItemSpec, Path.GetFileName(typemap.ItemSpec), compressionMethod: CompressionMethod.Store); } } int count = 0; foreach (var file in files) { var item = Path.Combine(file.Item2, Path.GetFileName(file.Item1)) .Replace(Path.DirectorySeparatorChar, '/'); if (apk.Archive.ContainsEntry(item)) { Log.LogCodedWarning("XA4301", file.Item1, 0, "Apk already contains the item {0}; ignoring.", item); continue; } apk.Archive.AddFile(file.Item1, item, compressionMethod: GetCompressionMethod(file.Item1)); count++; if (count == ZipArchiveEx.ZipFlushLimit) { 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 jar = ZipArchive.Open(File.OpenRead(jarFile))) { foreach (var jarItem in jar.Where(ze => !ze.IsDirectory && !ze.FullName.StartsWith("META-INF") && !ze.FullName.EndsWith(".class") && !ze.FullName.EndsWith(".java") && !ze.FullName.EndsWith("MANIFEST.MF"))) { byte [] data; using (var d = new System.IO.MemoryStream()) { jarItem.Extract(d); data = d.ToArray(); } if (apk.Archive.Any(e => e.FullName == jarItem.FullName)) { Log.LogMessage("Warning: failed to add jar entry {0} from {1}: the same file already exists in the apk", jarItem.FullName, Path.GetFileName(jarFile)); } else { apk.Archive.AddEntry(data, jarItem.FullName); } } } count++; if (count == ZipArchiveEx.ZipFlushLimit) { apk.Flush(); count = 0; } } } MonoAndroidHelper.CopyIfZipChanged(apkOutputPath + "new", apkOutputPath); File.Delete(apkOutputPath + "new"); }
void ExecuteWithAbi(string [] supportedAbis, string apkInputPath, string apkOutputPath) { var temp = apkOutputPath + "new"; ArchiveFileList files = new ArchiveFileList(); if (apkInputPath != null) { File.Copy(apkInputPath, temp, overwrite: true); } using (var notice = Assembly.GetExecutingAssembly().GetManifestResourceStream("NOTICE.txt")) using (var apk = new ZipArchiveEx(temp, apkInputPath != null ? FileMode.Open : FileMode.Create)) { apk.FixupWindowsPathSeparators((a, b) => Log.LogDebugMessage($"Fixing up malformed entry `{a}` -> `{b}`")); apk.Archive.AddEntry(RootPath + "NOTICE", notice); // Add classes.dx foreach (var dex in DalvikClasses) { string apkName = dex.GetMetadata("ApkName"); string dexPath = string.IsNullOrWhiteSpace(apkName) ? Path.GetFileName(dex.ItemSpec) : apkName; apk.Archive.AddFile(dex.ItemSpec, DalvikPath + dexPath); } if (EmbedAssemblies && !BundleAssemblies) { AddAssemblies(apk); } AddRuntimeLibraries(apk, supportedAbis); apk.Flush(); AddNativeLibraries(files, supportedAbis); apk.Flush(); AddAdditionalNativeLibraries(files, supportedAbis); apk.Flush(); if (TypeMappings != null) { foreach (ITaskItem typemap in TypeMappings) { apk.Archive.AddFile(typemap.ItemSpec, RootPath + Path.GetFileName(typemap.ItemSpec), compressionMethod: UncompressedMethod); } } int count = 0; foreach (var file in files) { var item = Path.Combine(file.Item2, Path.GetFileName(file.Item1)) .Replace(Path.DirectorySeparatorChar, '/'); if (apk.Archive.ContainsEntry(item)) { Log.LogCodedWarning("XA4301", file.Item1, 0, "Apk already contains the item {0}; ignoring.", item); continue; } apk.Archive.AddFile(file.Item1, item, compressionMethod: GetCompressionMethod(file.Item1)); count++; if (count == ZipArchiveEx.ZipFlushLimit) { 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 jar = ZipArchive.Open(File.OpenRead(jarFile))) { foreach (var jarItem in jar.Where(ze => !ze.IsDirectory && !ze.FullName.StartsWith("META-INF") && !ze.FullName.EndsWith(".class") && !ze.FullName.EndsWith(".java") && !ze.FullName.EndsWith("MANIFEST.MF"))) { byte [] data; using (var d = new System.IO.MemoryStream()) { jarItem.Extract(d); data = d.ToArray(); } var path = RootPath + jarItem.FullName; if (apk.Archive.Any(e => e.FullName == path)) { Log.LogMessage("Warning: failed to add jar entry {0} from {1}: the same file already exists in the apk", jarItem.FullName, Path.GetFileName(jarFile)); } else { apk.Archive.AddEntry(data, path); } } } count++; if (count == ZipArchiveEx.ZipFlushLimit) { apk.Flush(); count = 0; } } FixupArchive(apk); } if (MonoAndroidHelper.CopyIfZipChanged(temp, apkOutputPath)) { Log.LogDebugMessage($"Copied {temp} to {apkOutputPath}"); } else { Log.LogDebugMessage($"Skipped {apkOutputPath}: up to date"); } File.Delete(temp); }