diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/BuildArchive.cs b/src/Xamarin.Android.Build.Tasks/Tasks/BuildArchive.cs index d1fd2af0f67..4a752613c3c 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/BuildArchive.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/BuildArchive.cs @@ -158,16 +158,26 @@ public override bool RunTask () // ItemSpec for these will be "# // eg: "obj/myjar.jar#myfile.txt" var jar_file_path = disk_path.Substring (0, disk_path.Length - (jar_entry_name.Length + 1)); - - if (apk.ContainsEntry (apk_path)) { - Log.LogDebugMessage ("Failed to add jar entry {0} from {1}: the same file already exists in the apk", jar_entry_name, Path.GetFileName (jar_file_path)); - continue; - } + var wasExistingOutputEntry = existingEntries.Remove (apk_path); using (var stream = File.OpenRead (jar_file_path)) using (var jar = ZipArchive.Open (stream)) { var jar_item = jar.ReadEntry (jar_entry_name); + if (apk.ContainsEntry (apk_path)) { + if (!wasExistingOutputEntry) { + Log.LogDebugMessage ("Failed to add jar entry {0} from {1}: the same file already exists in the apk", jar_entry_name, Path.GetFileName (jar_file_path)); + continue; + } + + if (apk.GetEntry (apk_path).CRC == jar_item.CRC) { + Log.LogDebugMessage ("Skipping {0} from {1} as it is up to date.", jar_entry_name, jar_file_path); + continue; + } + + apk.DeleteEntry (apk_path); + } + byte [] data; var d = MemoryStreamPool.Shared.Rent (); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/BuildArchiveTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/BuildArchiveTests.cs new file mode 100644 index 00000000000..2fc98ba96a0 --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/BuildArchiveTests.cs @@ -0,0 +1,96 @@ +using System.Collections.Generic; +using System.IO; +using System.Text; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using NUnit.Framework; +using Xamarin.Android.Tasks; +using Xamarin.Tools.Zip; + +namespace Xamarin.Android.Build.Tests +{ + [TestFixture] + public class BuildArchiveTests + { + string tempDirectory; + + [SetUp] + public void Setup () + { + tempDirectory = Path.Combine (Path.GetTempPath (), Path.GetRandomFileName ()); + Directory.CreateDirectory (tempDirectory); + } + + [TearDown] + public void TearDown () + { + Directory.Delete (tempDirectory, recursive: true); + } + + [Test] + public void ExistingJavaArchiveEntriesAreUpdated () + { + var apk = Path.Combine (tempDirectory, "app.apk"); + var jar = Path.Combine (tempDirectory, "classes.jar"); + + CreateArchive (apk, ("commonMain/default/manifest", "existing"), ("stale.txt", "stale")); + CreateArchive (jar, ("commonMain/default/manifest", "current")); + + var item = new TaskItem ($"{jar}#commonMain/default/manifest"); + item.SetMetadata ("ArchivePath", "commonMain/default/manifest"); + item.SetMetadata ("JavaArchiveEntry", "commonMain/default/manifest"); + + var task = new BuildArchive { + BuildEngine = new MockBuildEngine (TestContext.Out), + ApkOutputPath = apk, + FilesToAddToArchive = new ITaskItem [] { item }, + }; + + Assert.IsTrue (task.RunTask (), "task should have succeeded"); + + using (var archive = ZipArchive.Open (apk, FileMode.Open)) { + archive.AssertEntryContents (apk, "commonMain/default/manifest", "current"); + archive.AssertDoesNotContainEntry (apk, "stale.txt"); + } + } + + [Test] + public void ExistingJavaArchiveEntriesAreSkippedWhenUpToDate () + { + var apk = Path.Combine (tempDirectory, "app.apk"); + var jar = Path.Combine (tempDirectory, "classes.jar"); + + CreateArchive (apk, ("commonMain/default/manifest", "current")); + CreateArchive (jar, ("commonMain/default/manifest", "current")); + + var item = new TaskItem ($"{jar}#commonMain/default/manifest"); + item.SetMetadata ("ArchivePath", "commonMain/default/manifest"); + item.SetMetadata ("JavaArchiveEntry", "commonMain/default/manifest"); + var messages = new List (); + + var task = new BuildArchive { + BuildEngine = new MockBuildEngine (TestContext.Out, messages: messages), + ApkOutputPath = apk, + FilesToAddToArchive = new ITaskItem [] { item }, + }; + + Assert.IsTrue (task.RunTask (), "task should have succeeded"); + + Assert.That (messages, Has.Some.Property (nameof (BuildMessageEventArgs.Message)).EqualTo ($"Skipping commonMain/default/manifest from {jar} as it is up to date.")); + + using (var archive = ZipArchive.Open (apk, FileMode.Open)) { + archive.AssertEntryContents (apk, "commonMain/default/manifest", "current"); + } + } + + static void CreateArchive (string path, params (string name, string contents) [] entries) + { + using (var stream = File.Create (path)) + using (var archive = ZipArchive.Create (stream)) { + foreach (var entry in entries) { + archive.AddEntry (entry.name, entry.contents, encoding: Encoding.UTF8); + } + } + } + } +}