Beispiel #1
0
        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);
        }
        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);
        }
Beispiel #3
0
        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);
            }
        }
Beispiel #4
0
        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");
        }
Beispiel #5
0
        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);
        }
Beispiel #6
0
        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);
        }
Beispiel #7
0
        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");
        }
Beispiel #8
0
        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);
        }