From 55046640dd31e8590ef27386aa1cfdb84598f86b Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Mon, 6 Apr 2020 14:30:44 -0500 Subject: [PATCH] [Xamarin.Android.Build.Tasks] trailing / causes missing classes.dex (#4505) Fixes: https://github.com/xamarin/xamarin-android/issues/4478 Fixes: https://github.com/xamarin/xamarin-android/issues/4486 We with the release of VS for Mac 8.5 going stable have been getting some reports of: error ADB0010: Deployment failed Mono.AndroidTools.InstallFailedException: Failure [INSTALL_FAILED_INVALID_APK: Package couldn't be installed in /data/app/com.contoso.androidapp1-RTZZFPyLkRI7Bk7VDgGkDg==: Package /data/app/com.contoso.androidapp1-RTZZFPyLkRI7Bk7VDgGkDg==/base.apk code is missing] at Mono.AndroidTools.Internal.AdbOutputParsing.CheckInstallSuccess (System.String output, System.String packageName) [0x00159] in <65710797f19f43cc9d8f9e05353b9615>:0 at Mono.AndroidTools.AndroidDevice+<>c__DisplayClass95_0.b__0 (System.Threading.Tasks.Task`1[TResult] t) [0x0001c] in <65710797f19f43cc9d8f9e05353b9615>:0 at System.Threading.Tasks.ContinuationTaskFromResultTask`1[TAntecedentResult].InnerInvoke () [0x00024] in :0 at System.Threading.Tasks.Task.Execute () [0x00000] in :0 --- End of stack trace from previous location where exception was thrown --- at AndroidDeviceExtensions.PushAndInstallPackage (Mono.AndroidTools.AndroidDevice device, System.String apkFile, System.String packageName, System.Boolean reinstall, Mono.AndroidTools.Adb.AdbProgressReporter notifyProgress, System.Threading.CancellationToken token) [0x001d3] in <81f66a65e9434daf9d06602cf6ac0bf9>:0 at AndroidDeviceExtensions.PushAndInstallPackage (Mono.AndroidTools.AndroidDevice device, System.String apkFile, System.String packageName, System.Boolean reinstall, Mono.AndroidTools.Adb.AdbProgressReporter notifyProgress, System.Threading.CancellationToken token) [0x00402] in <81f66a65e9434daf9d06602cf6ac0bf9>:0 at Xamarin.AndroidTools.AndroidDeploySession.InstallPackage () [0x003be] in <81f66a65e9434daf9d06602cf6ac0bf9>:0 at Xamarin.AndroidTools.AndroidDeploySession.RunAsync (System.Threading.CancellationToken token) [0x003f8] in <81f66a65e9434daf9d06602cf6ac0bf9>:0 at Xamarin.AndroidTools.AndroidDeploySession.RunLoggedAsync (System.Threading.CancellationToken token) [0x00077] in <81f66a65e9434daf9d06602cf6ac0bf9>:0 [/Users/macuser/Projects/AndroidApp1/AndroidApp1/AndroidApp1.csproj] The error means the `.apk` is missing `classes.dex`. It seems to affect apps where: * `$(AndroidDexTool)=dx`, this would need to be set explicitly or it would default to `d8`. * The issue appears to occur only on macOS. @brendanzagaeski [was able to reproduce the issue][0]: Using [`4478TestCase.zip`][1], there is an `obj` directory sitting next to the `.sln` file: * `obj` * `AndroidApp1` * `AndroidApp1/AndroidApp1.csproj` * `AndroidApp1/obj` * `AndroidApp1.sln` Note that the actual `$(BaseIntermediateOutputPath)` would be `AndroidApp1/obj`. Brendan just moved an `obj` directory next to the `.sln` file while trying to reproduce the issue. However, it appears the presence of this phantom `obj` triggers the issue! You can rename it to `obj2` to *solve* the issue. It is a directory with sub-directories, but no files. If you look at the build log, the main difference is: CompileToDalvik Parameters - ClassesOutputDirectory = obj/Release/android/bin/classes/ + ClassesOutputDirectory = obj/Release/android/bin/classes The presence of the trailing `/` breaks this code: cmd.AppendSwitchIfNotNull ("--output ", Path.GetDirectoryName (ClassesOutputDirectory)); `classes.dex` is output into `obj/Release/android/bin/classes` instead of `obj/Release/android/bin` and doesn't make it into the final `.apk`. Tracking back to what creates the input value of `ClassesOutputDirectory`, it is the MSBuild property `$(_AndroidIntermediateJavaClassDirectory)`, which is: <_AndroidIntermediateJavaClassDirectory>$(IntermediateOutputPath)android\bin\classes\ But then comparing broken vs working logs: Broken: _InitialBaseIntermediateOutputPath = obj/ BaseIntermediateOutputPath = obj/ IntermediateOutputPath = obj/Release/ _AndroidIntermediateJavaClassDirectory = obj/Release/android/bin/classes/ Working: _InitialBaseIntermediateOutputPath = obj\ BaseIntermediateOutputPath = obj\ IntermediateOutputPath = obj\Release\ _AndroidIntermediateJavaClassDirectory = obj\Release\android\bin\classes\ It appears the existence of this random `obj` directory influences MSBuild's path combining behavior? The trailing `\` works, but `/` does not, due to `TrimEnd`: `. We are unsure what is causing the strange MSBuild behavior. However, after reviewing ``, I think we should just remove the `Path.GetDirectoryName()` call. We should just pass in the directory name we want to use from MSBuild targets. I could add a test for this scenario by setting a global property: /p:_AndroidIntermediateJavaClassDirectory=obj/Debug/android/bin/classes/ Setting a global property could test the trailing `/` character regardless of MSBuild behavior. I also cleaned things up a bit by create a new `$(_AndroidIntermediateDexOutputDirectory)` property. [0]: https://github.com/xamarin/xamarin-android/issues/4478#issuecomment-607632589 [1]: https://github.com/xamarin/xamarin-android/files/4419061/4478TestCase.zip --- Documentation/release-notes/4505.md | 8 ++++++++ .../Tasks/CompileToDalvik.cs | 2 +- .../PackagingTest.cs | 18 ++++++++++++++++++ .../Xamarin.Android.Common.targets | 4 ++-- .../Xamarin.Android.D8.targets | 8 ++++---- .../Xamarin.Android.DX.targets | 6 +++--- 6 files changed, 36 insertions(+), 10 deletions(-) create mode 100644 Documentation/release-notes/4505.md diff --git a/Documentation/release-notes/4505.md b/Documentation/release-notes/4505.md new file mode 100644 index 00000000000..a4014900710 --- /dev/null +++ b/Documentation/release-notes/4505.md @@ -0,0 +1,8 @@ +#### Application and library build and deployment + + * [GitHub 4478](https://github.com/xamarin/xamarin-android/issues/4478), + [GitHub 4486](https://github.com/xamarin/xamarin-android/issues/4486): + On macOS, starting in Xamarin.Android 10.2, *\[INSTALL_FAILED_INVALID_APK: + ... base.apk code is missing\]* and *Module 'base' has no dex files* + prevented deploying successfully for certain projects configured to use the + DX DEX compiler. \ No newline at end of file diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/CompileToDalvik.cs b/src/Xamarin.Android.Build.Tasks/Tasks/CompileToDalvik.cs index 0b81c372a5c..bccfd9dcae3 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/CompileToDalvik.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/CompileToDalvik.cs @@ -114,7 +114,7 @@ protected override string GenerateCommandLineCommands () cmd.AppendSwitchIfNotNull ("--main-dex-list=", MultiDexMainDexListFile); } } - cmd.AppendSwitchIfNotNull ("--output ", Path.GetDirectoryName (ClassesOutputDirectory)); + cmd.AppendSwitchIfNotNull ("--output ", ClassesOutputDirectory); using (var sw = new StreamWriter (path: inputListFile, append: false, encoding: MonoAndroidHelper.UTF8withoutBOM)) { diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs index 82156b31de0..06b27b0777f 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs @@ -676,5 +676,23 @@ public void CheckTheCorrectRuntimeAssemblyIsUsedFromNuget () Assert.IsTrue (xab.LastBuildOutput.ContainsText (expected), $"Build should be using {expected}"); } } + + [Test] + public void MissingDexFile () + { + //NOTE: The trailing / was breaking in 16.5 + var parameters = new [] { "_AndroidIntermediateJavaClassDirectory=obj/Debug/android/bin/classes/" }; + var proj = new XamarinAndroidApplicationProject { + DexTool = "dx" + }; + using (var b = CreateApkBuilder ()) { + Assert.IsTrue (b.Build (proj, parameters: parameters), "Build should have succeeded."); + var apk = Path.Combine (Root, b.ProjectDirectory, + proj.IntermediateOutputPath, "android", "bin", "UnnamedProject.UnnamedProject.apk"); + using (var zip = ZipHelper.OpenZip (apk)) { + Assert.IsTrue (zip.ContainsEntry ("classes.dex"), "Apk should contain classes.dex"); + } + } + } } } diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index 2243126e149..ff7464114cc 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -1124,6 +1124,7 @@ because xbuild doesn't support framework reference assemblies. $(IntermediateOutputPath)generated $(IntermediateOutputPath)resourcecache <_AndroidIntermediateJavaSourceDirectory>$(IntermediateOutputPath)android\src\ + <_AndroidIntermediateDexOutputDirectory>$(IntermediateOutputPath)android\bin\ <_AndroidIntermediateJavaClassDirectory>$(IntermediateOutputPath)android\bin\classes\ <_AndroidIntermediateClassesZip>$(IntermediateOutputPath)android\bin\classes.zip <_AndroidAotBinDirectory>$(IntermediateOutputPath)aot @@ -2357,8 +2358,7 @@ because xbuild doesn't support framework reference assemblies. - <_DexFile Include="$(IntermediateOutputPath)android\bin\dex\*.dex" /> - <_DexFile Include="$(IntermediateOutputPath)android\bin\*.dex" /> + <_DexFile Include="$(_AndroidIntermediateDexOutputDirectory)*.dex" /> diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.D8.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.D8.targets index a92b6db9406..8744a3f43f4 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.D8.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.D8.targets @@ -36,7 +36,7 @@ Copyright (C) 2018 Xamarin. All rights reserved. - <_DexesToDelete Include="$(IntermediateOutputPath)android\bin\*.dex" /> + <_DexesToDelete Include="$(_AndroidIntermediateDexOutputDirectory)*.dex" /> @@ -52,7 +52,7 @@ Copyright (C) 2018 Xamarin. All rights reserved. JavaOptions="$(JavaOptions)" JarPath="$(AndroidR8JarPath)" AndroidManifestFile="$(IntermediateOutputPath)android\AndroidManifest.xml" - OutputDirectory="$(IntermediateOutputPath)android\bin" + OutputDirectory="$(_AndroidIntermediateDexOutputDirectory)" Debug="$(AndroidIncludeDebugSymbols)" JavaPlatformJarPath="$(JavaPlatformJarPath)" ClassesZip="$(_AndroidIntermediateClassesZip)" @@ -79,7 +79,7 @@ Copyright (C) 2018 Xamarin. All rights reserved. JavaOptions="$(JavaOptions)" JarPath="$(AndroidR8JarPath)" AndroidManifestFile="$(IntermediateOutputPath)android\AndroidManifest.xml" - OutputDirectory="$(IntermediateOutputPath)android\bin" + OutputDirectory="$(_AndroidIntermediateDexOutputDirectory)" Debug="$(AndroidIncludeDebugSymbols)" JavaPlatformJarPath="$(JavaPlatformJarPath)" ClassesZip="$(_AndroidIntermediateClassesZip)" @@ -92,7 +92,7 @@ Copyright (C) 2018 Xamarin. All rights reserved. - + diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.DX.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.DX.targets index 98159443d77..bb20d2b3aa5 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.DX.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.DX.targets @@ -71,7 +71,7 @@ Copyright (C) 2018 Xamarin. All rights reserved. - <_DexesToDelete Include="$(IntermediateOutputPath)android\bin\*.dex" /> + <_DexesToDelete Include="$(_AndroidIntermediateDexOutputDirectory)*.dex" /> @@ -82,7 +82,7 @@ Copyright (C) 2018 Xamarin. All rights reserved. JavaToolPath="$(JavaToolPath)" JavaMaximumHeapSize="$(JavaMaximumHeapSize)" JavaOptions="$(JavaOptions)" - ClassesOutputDirectory="$(_AndroidIntermediateJavaClassDirectory.TrimEnd('\'))" + ClassesOutputDirectory="$(_AndroidIntermediateDexOutputDirectory)" ClassesZip="$(_AndroidIntermediateClassesZip)" ToolPath="$(DxToolPath)" ToolExe="$(DxToolExe)" @@ -94,7 +94,7 @@ Copyright (C) 2018 Xamarin. All rights reserved. /> - +