From 44ae238ab407a88b926ec2a789d27b145126855d Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Fri, 22 Dec 2017 21:48:55 -0600 Subject: [PATCH] [Xamarin.Android.Build.Tasks] fix classes-zip inception Fixes: https://bugzilla.xamarin.com/show_bug.cgi?id=61073 Context: a296784 When we added the new `classes.zip` functionality, there was one oddity present. On the first build, everything appears to be normal. On the second build, `classes.zip` appeared to contain itself inside itself??? To make things even stranger, I wrote a unit test that contains a `Java.Lang.Object` subclass in which its type and namespace names change on each write. This worked fine on macOS, even with the oddity of `classes.zip` continually zipping itself inside itself... The new java sources would appear at the bottom of the list within the zip file. Everything broke down when I tried my unit test on Windows, the case of `classes.zip` trying to compress itself inside itself was silently failing. It was not processing any further files after the failing zip entry, and so *in theory* this situation would cause many Java stubs to be missing from the zip. Solution? Let's just put `classes.zip` up one directory. So instead of: ``` obj/Debug/android/bin/classes/classes.zip ``` Move it to: ``` obj/Debug/android/bin/classes.zip ``` This prevents the `classes` directory from containing `classes.zip`, and our strange case of `classes.zip` inception. Merry Christmas! I'm going to go play Skylanders now. --- .../Tasks/CompileToDalvik.cs | 2 +- .../Tasks/CreateMultiDexMainDexClassList.cs | 4 +- .../Tasks/Javac.cs | 2 +- .../Tasks/Proguard.cs | 2 +- .../Xamarin.Android.Build.Tests/BuildTest.cs | 48 +++++++++++++++++++ 5 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/CompileToDalvik.cs b/src/Xamarin.Android.Build.Tasks/Tasks/CompileToDalvik.cs index 13123548d8e..b4ab6f5965b 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/CompileToDalvik.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/CompileToDalvik.cs @@ -119,7 +119,7 @@ protected override string GenerateCommandLineCommands () cmd.AppendFileNamesIfNotNull (AlternativeJarFiles, " "); } else { Log.LogDebugMessage (" processing ClassesOutputDirectory..."); - var zip = Path.GetFullPath (Path.Combine (ClassesOutputDirectory, "classes.zip")); + var zip = Path.GetFullPath (Path.Combine (ClassesOutputDirectory, "..", "classes.zip")); if (!File.Exists (zip)) { throw new FileNotFoundException ($"'{zip}' does not exist. Please rebuild the project."); } diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/CreateMultiDexMainDexClassList.cs b/src/Xamarin.Android.Build.Tasks/Tasks/CreateMultiDexMainDexClassList.cs index 66d473898fb..d0b3c45f0d9 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/CreateMultiDexMainDexClassList.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/CreateMultiDexMainDexClassList.cs @@ -76,7 +76,7 @@ protected override string GenerateCommandLineCommands () void GenerateProguardCommands (CommandLineBuilder cmd) { var enclosingChar = OS.IsWindows ? "\"" : string.Empty; - var jars = JavaLibraries.Select (i => i.ItemSpec).Concat (new string [] { Path.Combine (ClassesOutputDirectory, "classes.zip") }); + var jars = JavaLibraries.Select (i => i.ItemSpec).Concat (new string [] { Path.Combine (ClassesOutputDirectory, "..", "classes.zip") }); cmd.AppendSwitchIfNotNull ("-jar ", ProguardJarPath); cmd.AppendSwitchUnquotedIfNotNull ("-injars ", "\"'" + string.Join ($"'{ProguardInputJarFilter}{Path.PathSeparator}'", jars) + $"'{ProguardInputJarFilter}\""); cmd.AppendSwitch ("-dontwarn"); @@ -93,7 +93,7 @@ void GenerateMainDexListBuilderCommands(CommandLineBuilder cmd) { var enclosingDoubleQuote = OS.IsWindows ? "\"" : string.Empty; var enclosingQuote = OS.IsWindows ? string.Empty : "'"; - var jars = JavaLibraries.Select (i => i.ItemSpec).Concat (new string [] { Path.Combine (ClassesOutputDirectory, "classes.zip") }); + var jars = JavaLibraries.Select (i => i.ItemSpec).Concat (new string [] { Path.Combine (ClassesOutputDirectory, "..", "classes.zip") }); cmd.AppendSwitchIfNotNull ("-Djava.ext.dirs=", Path.Combine (AndroidSdkBuildToolsPath, "lib")); cmd.AppendSwitch ("com.android.multidex.MainDexListBuilder"); cmd.AppendSwitch ($"{enclosingDoubleQuote}{tempJar}{enclosingDoubleQuote}"); diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/Javac.cs b/src/Xamarin.Android.Build.Tasks/Tasks/Javac.cs index b5dd7c724cc..02982960874 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/Javac.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/Javac.cs @@ -35,7 +35,7 @@ public override bool Execute () if (!result) return result; // compress all the class files - using (var zip = new ZipArchiveEx (Path.Combine (ClassesOutputDirectory, "classes.zip"), FileMode.OpenOrCreate)) + using (var zip = new ZipArchiveEx (Path.Combine (ClassesOutputDirectory, "..", "classes.zip"), FileMode.OpenOrCreate)) zip.AddDirectory (ClassesOutputDirectory, ""); return result; } diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/Proguard.cs b/src/Xamarin.Android.Build.Tasks/Tasks/Proguard.cs index 7cd8998cc4d..c9152c75258 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/Proguard.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/Proguard.cs @@ -127,7 +127,7 @@ protected override string GenerateCommandLineCommands () if (!ClassesOutputDirectory.EndsWith (Path.DirectorySeparatorChar.ToString (), StringComparison.OrdinalIgnoreCase)) ClassesOutputDirectory += Path.DirectorySeparatorChar; - var classesZip = Path.Combine (ClassesOutputDirectory, "classes.zip"); + var classesZip = Path.Combine (ClassesOutputDirectory, "..", "classes.zip"); var acwLines = File.ReadAllLines (AcwMapFile); using (var appcfg = File.CreateText (ProguardGeneratedApplicationConfiguration)) for (int i = 0; i + 3 < acwLines.Length; i += 4) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs index 99e4e36e611..8f96a2c2193 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs @@ -202,6 +202,54 @@ public void BuildIncrementingAssemblyVersion () } } + [Test] + public void BuildIncrementingClassName () + { + int count = 0; + var source = new BuildItem ("Compile", "World.cs") { + TextContent = () => { + int current = ++count; + return $"namespace Hello{current} {{ public class World{current} : Java.Lang.Object {{ }} }}"; + } + }; + var proj = new XamarinAndroidApplicationProject (); + proj.Sources.Add (source); + + using (var b = CreateApkBuilder ("temp/BuildIncrementingClassName")) { + Assert.IsTrue (b.Build (proj), "Build should have succeeded."); + + var classesZipPath = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath, "android", "bin", "classes.zip"); + FileAssert.Exists (classesZipPath); + var expectedBuilder = new StringBuilder (); + using (var zip = ZipHelper.OpenZip (classesZipPath)) { + foreach (var file in zip) { + expectedBuilder.AppendLine (file.FullName); + } + } + var expectedZip = expectedBuilder.ToString (); + + source.Timestamp = null; //Force the file to re-save w/ new Timestamp + Assert.IsTrue (b.Build (proj), "Second build should have succeeded."); + + var actualBuilder = new StringBuilder (); + using (var zip = ZipHelper.OpenZip (classesZipPath)) { + foreach (var file in zip) { + actualBuilder.AppendLine (file.FullName); + } + } + var actualZip = actualBuilder.ToString (); + Assert.AreNotEqual (expectedZip, actualZip); + + //Build with no changes + Assert.IsTrue (b.Build (proj), "Third build should have succeeded."); + FileAssert.Exists (classesZipPath); + + //Clean + Assert.IsTrue (b.Clean (proj), "Clean should have succeeded."); + FileAssert.DoesNotExist (classesZipPath); + } + } + [Test] public void BuildMkBundleApplicationRelease () {