From 27efea780b6fc9efbfdc830710829247a64f9a03 Mon Sep 17 00:00:00 2001 From: Dean Ellis Date: Wed, 14 Jul 2021 14:17:57 +0100 Subject: [PATCH] [One .NET] select defaults for App Bundles Fixes #6059 Users will probably want to target more than one App Store. Google is now requiring the `aab` format for Google Play Store uploads. Unfortunately this package format is not compatible with other stores. Users can build their app twice producing an `aab` for one build and `apk` for another, but we should try to make this a bit easier. `bundle-tool` has the ability to create a universal apk from the `aab` file. So lets make use of that to generate one along side the `aab`. We are introducing a new property `AndroidPackageFormats`. Under .net 6 this will have a value of `aab;apk` by default for Release builds, for Legacy it will be empty. If `AndroidPackageFormats` is specified and `AndroidPackageFormat` is empty we will use the values in `AndroidPackageFormats` to populate `AndroidPackageFormat`. If none of these values are provided the `AndroidPackageFormat` will still default to `apk` as it has always done. The following setting will produce both an `aab` and an `apk`. ``` aab;apk ``` This will produce just an `aab` ``` aab ``` and this will produce just an `apk` ``` apk ``` For .net 6 users this will be enabled by default for Release builds. For legacy users they will need to define the property manually. --- .../guides/building-apps/build-properties.md | 47 +++++++++++++-- build-tools/automation/azure-pipelines.yaml | 5 +- .../yaml-templates/apk-instrumentation.yaml | 4 +- .../Microsoft.Android.Sdk.BuildOrder.targets | 1 + ...soft.Android.Sdk.DefaultProperties.targets | 3 +- .../WorkloadManifest.targets | 2 +- .../Tasks/AndroidApkSigner.cs | 9 ++- .../Tasks/AndroidSignPackage.cs | 4 +- .../Tasks/BuildApk.cs | 6 +- .../Tasks/BuildApkSet.cs | 21 +++++-- .../Tasks/Unzip.cs | 19 +++++- .../Xamarin.Android.Build.Tests/AotTests.cs | 14 ++--- .../BuildAssetsTest.cs | 2 +- .../Xamarin.Android.Build.Tests/BuildTest.cs | 31 +++++----- .../CodeBehindTests.cs | 8 ++- .../EnvironmentContentTests.cs | 3 +- .../IncrementalBuildTest.cs | 4 +- .../ManifestTest.cs | 2 +- .../PackagingTest.cs | 24 ++++---- .../Tasks/LinkerTests.cs | 4 +- .../Xamarin.Android.Build.Tests/XASdkTests.cs | 58 +++++++++++++------ .../Xamarin.Android.Common.targets | 53 +++++++++++++++++ .../Xamarin.Android.Bcl-Tests.csproj | 1 + .../Tests/BundleToolTests.cs | 2 +- .../Tests/DebuggingTest.cs | 4 ++ .../Tests/InstallTests.cs | 6 +- .../Tests/InstantRunTest.cs | 2 +- 27 files changed, 252 insertions(+), 87 deletions(-) diff --git a/Documentation/guides/building-apps/build-properties.md b/Documentation/guides/building-apps/build-properties.md index 06e038b28df..43b6b537dad 100644 --- a/Documentation/guides/building-apps/build-properties.md +++ b/Documentation/guides/building-apps/build-properties.md @@ -172,8 +172,8 @@ Added in Xamarin.Android 10.2. ## AndroidBoundInterfacesContainConstants A boolean property that -determines whether binding constants on interfaces will be supported, -or the workaround of creating an `IMyInterfaceConsts` class +determines whether binding constants on interfaces will be supported, +or the workaround of creating an `IMyInterfaceConsts` class will be used. Defaults to `True` in .NET 6 and `False` for legacy. @@ -181,8 +181,8 @@ Defaults to `True` in .NET 6 and `False` for legacy. ## AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods A boolean property that -whether default and static members on interfaces will be supported, -or old workaround of creating a sibling class containing static +whether default and static members on interfaces will be supported, +or old workaround of creating a sibling class containing static members like `abstract class MyInterface`. Defaults to `True` in .NET 6 and `False` for legacy. @@ -190,7 +190,7 @@ Defaults to `True` in .NET 6 and `False` for legacy. ## AndroidBoundInterfacesContainTypes A boolean property that -whether types nested in interfaces will be supported, or the workaround +whether types nested in interfaces will be supported, or the workaround of creating a non-nested type like `IMyInterfaceMyNestedClass`. Defaults to `True` in .NET 6 and `False` for legacy. @@ -908,6 +908,43 @@ properties are set, which are required for Android App Bundles: [apk]: https://en.wikipedia.org/wiki/Android_application_package [bundle]: https://developer.android.com/platform/technology/app-bundle +This property will be deprecated for .net 6. Users should switch over to +the newer [`AndroidPackageFormats`](~/android/deploy-test/building-apps/build-properties.md#androidpackageformats). + +## AndroidPackageFormats + +A semi-colon delimited property with valid values of `apk` and `aab`. +This indicates if you want to package the Android application as +an [APK file][apk] or [Android App Bundle][bundle]. App Bundles +are a new format for `Release` builds that are intended for +submission on Google Play. + +When building a Release build you might want to generate both +and `aab` and an `apk` for distribution to various stores. + +Setting `AndroidPackageFormats` to `aab;apk` will result in both +being generated. Setting `AndroidPackageFormats` to either `aab` +or `apk` will generate only one file. + +For .net 6 `AndroidPackageFormats` will be set to `aab;apk` for +`Release` builds only. It is recommended that you continue to use +just `apk` for debugging. + +For Legacy Xamarin.Android this value currently defaults to `""`. +As a result Legacy Xamarin.Android will NOT by default produce +both as part of a release build. If a user wants to produce both +outputs they will need to define the following in their `Release` +configuration. + +``` +aab;apk +``` + +You will also need to remove the existing `AndroidPackageFormat` for +that configuration if you have it. + +Added in Xamarin.Android 11.5. + ## AndroidPackageNamingPolicy An enum-style property for diff --git a/build-tools/automation/azure-pipelines.yaml b/build-tools/automation/azure-pipelines.yaml index fabd51a8fdd..d046ee7e168 100644 --- a/build-tools/automation/azure-pipelines.yaml +++ b/build-tools/automation/azure-pipelines.yaml @@ -771,6 +771,7 @@ stages: testName: Mono.Android.NET_Tests project: tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj testResultsFiles: TestResult-Mono.Android.NET_Tests-$(XA.Build.Configuration).xml + extraBuildArgs: /p:AndroidPackageFormat=apk artifactSource: bin/Test$(XA.Build.Configuration)/net6.0-android/Mono.Android.NET_Tests-Signed.apk artifactFolder: net6-Default useDotNet: true @@ -793,7 +794,7 @@ stages: testName: Mono.Android.NET_Tests-Interpreter project: tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj testResultsFiles: TestResult-Mono.Android.NET_Tests-$(XA.Build.Configuration)Interpreter.xml - extraBuildArgs: /p:TestsFlavor=Interpreter /p:UseInterpreter=True + extraBuildArgs: /p:TestsFlavor=Interpreter /p:UseInterpreter=True /p:AndroidPackageFormat=apk artifactSource: bin/Test$(XA.Build.Configuration)/net6.0-android/Mono.Android.NET_Tests-Signed.apk artifactFolder: net6-Interpreter useDotNet: true @@ -804,7 +805,7 @@ stages: testName: Mono.Android.NET_Tests-Aot project: tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj testResultsFiles: TestResult-Mono.Android.NET_Tests-$(XA.Build.Configuration)Aot.xml - extraBuildArgs: /p:TestsFlavor=Aot /p:RunAOTCompilation=true + extraBuildArgs: /p:TestsFlavor=Aot /p:RunAOTCompilation=true /p:AndroidPackageFormat=apk artifactSource: bin/Test$(XA.Build.Configuration)/net6.0-android/Mono.Android.NET_Tests-Signed.apk artifactFolder: net6-aot useDotNet: true diff --git a/build-tools/automation/yaml-templates/apk-instrumentation.yaml b/build-tools/automation/yaml-templates/apk-instrumentation.yaml index b2c87e6deb5..c1b355b3755 100644 --- a/build-tools/automation/yaml-templates/apk-instrumentation.yaml +++ b/build-tools/automation/yaml-templates/apk-instrumentation.yaml @@ -19,7 +19,7 @@ steps: solution: ${{ parameters.project }} configuration: ${{ parameters.configuration }} msbuildArguments: >- - /restore + /restore /v:diag /t:AcquireAndroidTarget,SignAndroidPackage,DeployTest${{ parameters.packageType }}s,CheckAndRecordApkSizes,RunTestApks,UndeployTestApks,RenameApkTestCases,ReportComponentFailures /bl:$(System.DefaultWorkingDirectory)/bin/Test${{ parameters.configuration }}/run-${{ parameters.testName }}.binlog ${{ parameters.extraBuildArgs }} @@ -34,7 +34,7 @@ steps: arguments: >- -t:AcquireAndroidTarget,SignAndroidPackage,DeployTest${{ parameters.packageType }}s,CheckAndRecordApkSizes,RunTestApks,UndeployTestApks,RenameApkTestCases,ReportComponentFailures -bl:$(System.DefaultWorkingDirectory)/bin/Test${{ parameters.configuration }}/run-${{ parameters.testName }}.binlog - -v:n -c ${{ parameters.configuration }} ${{ parameters.extraBuildArgs }} + -v:d -c ${{ parameters.configuration }} ${{ parameters.extraBuildArgs }} condition: ${{ parameters.condition }} - script: > diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets index a39d1c89d32..9531431b31a 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets @@ -37,6 +37,7 @@ projects, these properties are set in Xamarin.Android.Legacy.targets. $(BuildDependsOn); _CopyPackage; _Sign; + _CreateUniversalApkFromBundle; _PrepareAssemblies; diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets index 709ebebf4ce..0f6f690793c 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets @@ -18,7 +18,7 @@ false <_GetChildProjectCopyToPublishDirectoryItems>false @@ -46,6 +46,7 @@ true true + aab;apk diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.NET.Sdk.Android/WorkloadManifest.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.NET.Sdk.Android/WorkloadManifest.targets index 1ac6e36f993..1fb7efd8cd1 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.NET.Sdk.Android/WorkloadManifest.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.NET.Sdk.Android/WorkloadManifest.targets @@ -2,7 +2,7 @@ + Condition=" '$(TargetPlatformIdentifier)' == 'android' " /> diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/AndroidApkSigner.cs b/src/Xamarin.Android.Build.Tasks/Tasks/AndroidApkSigner.cs index 8293376333f..30fa4ba04e2 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/AndroidApkSigner.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/AndroidApkSigner.cs @@ -54,13 +54,16 @@ public class AndroidApkSigner : JavaToolTask void AddStorePass (CommandLineBuilder cmd, string cmdLineSwitch, string value) { + string pass = value.Replace ("env:", string.Empty) + .Replace ("file:", string.Empty) + .Replace ("pass:", string.Empty); if (value.StartsWith ("env:", StringComparison.Ordinal)) { - cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch} ", value); + cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch} env:", pass); } else if (value.StartsWith ("file:", StringComparison.Ordinal)) { - cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch} file:", value.Replace ("file:", string.Empty)); + cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch} file:", pass); } else { - cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch} pass:", value); + cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch} pass:", pass); } } diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/AndroidSignPackage.cs b/src/Xamarin.Android.Build.Tasks/Tasks/AndroidSignPackage.cs index ee8e2937389..00680879c8f 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/AndroidSignPackage.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/AndroidSignPackage.cs @@ -71,7 +71,9 @@ public class AndroidSignPackage : AndroidRunToolTask void AddStorePass (CommandLineBuilder cmd, string cmdLineSwitch, string value) { - string pass = value.Replace ("env:", string.Empty).Replace ("file:", string.Empty); + string pass = value.Replace ("env:", string.Empty) + .Replace ("file:", string.Empty) + .Replace ("pass:", string.Empty); if (value.StartsWith ("env:", StringComparison.Ordinal)) { cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch}:env ", pass); } diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs b/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs index be43b151a0e..b85c80dbf0d 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs @@ -244,6 +244,10 @@ void ExecuteWithAbi (string [] supportedAbis, string apkInputPath, string apkOut Log.LogDebugMessage ($"Skipping {path} as the archive file is up to date."); continue; } + if (string.Compare (Path.GetFileName (name), "AndroidManifest.xml", StringComparison.OrdinalIgnoreCase) == 0) { + Log.LogDebugMessage ("Ignoring jar entry {0} from {1}: the same file already exists in the apk", name, Path.GetFileName (jarFile)); + 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; @@ -253,7 +257,7 @@ void ExecuteWithAbi (string [] supportedAbis, string apkInputPath, string apkOut jarItem.Extract (d); data = d.ToArray (); } - Log.LogDebugMessage ($"Adding {path} as the archive file is out of date."); + Log.LogDebugMessage ($"Adding {path} from {jarFile} as the archive file is out of date."); apk.Archive.AddEntry (data, path); } } diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/BuildApkSet.cs b/src/Xamarin.Android.Build.Tasks/Tasks/BuildApkSet.cs index 898c55661f7..b59728d50aa 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/BuildApkSet.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/BuildApkSet.cs @@ -42,6 +42,8 @@ public class BuildApkSet : BundleToolAdbTask public string ExtraArgs { get; set; } + public bool GenerateUniversalApkSet { get; set; } = false; + public override bool RunTask () { //NOTE: bundletool will not overwrite @@ -55,12 +57,15 @@ public override bool RunTask () void AddStorePass (CommandLineBuilder cmd, string cmdLineSwitch, string value) { + string pass = value.Replace ("env:", string.Empty) + .Replace ("file:", string.Empty) + .Replace ("pass:", string.Empty); if (value.StartsWith ("env:", StringComparison.Ordinal)) { - cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch} ", value); + cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch} pass:", Environment.GetEnvironmentVariable (pass)); } else if (value.StartsWith ("file:", StringComparison.Ordinal)) { - cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch} file:", value.Replace ("file:", string.Empty)); + cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch} file:", pass); } else { - cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch} pass:", value); + cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch} pass:", pass); } } @@ -69,11 +74,15 @@ internal override CommandLineBuilder GetCommandLineBuilder () var aapt2 = string.IsNullOrEmpty (Aapt2ToolExe) ? Aapt2ToolName : Aapt2ToolExe; var cmd = base.GetCommandLineBuilder (); cmd.AppendSwitch ("build-apks"); - cmd.AppendSwitch ("--connected-device"); + if (GenerateUniversalApkSet) { + cmd.AppendSwitchIfNotNull ("--mode ", "universal"); + } else { + cmd.AppendSwitch ("--connected-device"); + cmd.AppendSwitchIfNotNull ("--mode ", "default"); + AppendAdbOptions (cmd); + } cmd.AppendSwitchIfNotNull ("--bundle ", AppBundle); cmd.AppendSwitchIfNotNull ("--output ", Output); - cmd.AppendSwitchIfNotNull ("--mode ", "default"); - AppendAdbOptions (cmd); cmd.AppendSwitchIfNotNull ("--aapt2 ", Path.Combine (Aapt2ToolPath, aapt2)); cmd.AppendSwitchIfNotNull ("--ks ", KeyStore); cmd.AppendSwitchIfNotNull ("--ks-key-alias ", KeyAlias); diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/Unzip.cs b/src/Xamarin.Android.Build.Tasks/Tasks/Unzip.cs index b14f8ab9a1b..aab4efe7d14 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/Unzip.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/Unzip.cs @@ -14,14 +14,29 @@ public class Unzip : AndroidTask public ITaskItem [] Sources { get; set; } public ITaskItem [] DestinationDirectories { get; set; } + public ITaskItem [] Files { get; set; } public override bool RunTask () { foreach (var pair in Sources.Zip (DestinationDirectories, (s, d) => new { Source = s, Destination = d })) { if (!Directory.Exists (pair.Destination.ItemSpec)) Directory.CreateDirectory (pair.Destination.ItemSpec); - using (var z = ZipArchive.Open (pair.Source.ItemSpec, FileMode.Open)) - z.ExtractAll (pair.Destination.ItemSpec); + using (var z = ZipArchive.Open (pair.Source.ItemSpec, FileMode.Open)) { + if (Files == null || Files.Length == 0) { + z.ExtractAll (pair.Destination.ItemSpec); + } else { + foreach (var file in Files) { + ZipEntry entry = z.ReadEntry (file.ItemSpec); + if (entry == null) { + Log.LogDebugMessage ($"Skipping not existant file {file.ItemSpec}"); + continue; + } + string destinationFileName = file.GetMetadata ("DestinationFileName"); + Log.LogDebugMessage ($"Extracting {file.ItemSpec} to {destinationFileName ?? file.ItemSpec}"); + entry.Extract (pair.Destination.ItemSpec, destinationFileName ?? file.ItemSpec); + } + } + } } return true; diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AotTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AotTests.cs index b0ea6906eb6..6aab2e78996 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AotTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AotTests.cs @@ -204,14 +204,14 @@ public void BuildAotApplicationAndÜmläüts (string supportedAbis, bool enableL "aot", abi, "libaot-UnnamedProject.dll.so"); Assert.IsTrue (File.Exists (assemblies), "{0} libaot-UnnamedProject.dll.so does not exist", abi); var apk = Path.Combine (Root, b.ProjectDirectory, - proj.IntermediateOutputPath, "android", "bin", $"{proj.PackageName}.apk"); + proj.OutputPath, $"{proj.PackageName}-Signed.apk"); using (var zipFile = ZipHelper.OpenZip (apk)) { Assert.IsNotNull (ZipHelper.ReadFileFromZip (zipFile, string.Format ("lib/{0}/libaot-UnnamedProject.dll.so", abi)), - $"lib/{0}/libaot-UnnamedProject.dll.so should be in the {proj.PackageName}.apk", abi); + $"lib/{0}/libaot-UnnamedProject.dll.so should be in the {proj.PackageName}-Signed.apk", abi); Assert.IsNotNull (ZipHelper.ReadFileFromZip (zipFile, "assemblies/UnnamedProject.dll"), - $"UnnamedProject.dll should be in the {proj.PackageName}.apk"); + $"UnnamedProject.dll should be in the {proj.PackageName}-Signed.apk"); } } Assert.AreEqual (expectedResult, b.Build (proj), "Second Build should have {0}.", expectedResult ? "succeeded" : "failed"); @@ -259,14 +259,14 @@ public void BuildAotApplicationAndBundleAndÜmläüts (string supportedAbis, boo "aot", abi, "libaot-UnnamedProject.dll.so"); Assert.IsTrue (File.Exists (assemblies), "{0} libaot-UnnamedProject.dll.so does not exist", abi); var apk = Path.Combine (Root, b.ProjectDirectory, - proj.IntermediateOutputPath, "android", "bin", $"{proj.PackageName}.apk"); + proj.OutputPath, $"{proj.PackageName}-Signed.apk"); using (var zipFile = ZipHelper.OpenZip (apk)) { Assert.IsNotNull (ZipHelper.ReadFileFromZip (zipFile, string.Format ("lib/{0}/libaot-UnnamedProject.dll.so", abi)), - $"lib/{0}/libaot-UnnamedProject.dll.so should be in the {proj.PackageName}.apk", abi); + $"lib/{0}/libaot-UnnamedProject.dll.so should be in the {proj.PackageName}-Signed.apk", abi); Assert.IsNull (ZipHelper.ReadFileFromZip (zipFile, "assemblies/UnnamedProject.dll"), - $"UnnamedProject.dll should not be in the {proj.PackageName}.apk"); + $"UnnamedProject.dll should not be in the {proj.PackageName}-Signed.apk"); } } Assert.AreEqual (expectedResult, b.Build (proj), "Second Build should have {0}.", expectedResult ? "succeeded" : "failed"); @@ -406,7 +406,7 @@ public void HybridAOT ([Values ("armeabi-v7a;arm64-v8a", "armeabi-v7a", "arm64-v b.Build (proj); - var apk = Path.Combine (Root, b.ProjectDirectory, proj.OutputPath, $"{proj.PackageName}.apk"); + var apk = Path.Combine (Root, b.ProjectDirectory, proj.OutputPath, $"{proj.PackageName}-Signed.apk"); FileAssert.Exists (apk); using (var zip = ZipHelper.OpenZip (apk)) { var entry = zip.ReadEntry ($"assemblies/{proj.ProjectName}.dll"); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildAssetsTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildAssetsTest.cs index 190ce7f8c8c..7e6150ab203 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildAssetsTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildAssetsTest.cs @@ -113,7 +113,7 @@ public void CheckAssetsAreIncludedInAPK ([Values (true, false)] bool useAapt2) Assert.IsTrue (libb.Build (libproj), "{0} should have built successfully.", libproj.ProjectName); using (var b = CreateApkBuilder (Path.Combine (projectPath, proj.ProjectName))) { Assert.IsTrue (b.Build (proj), "{0} should have built successfully.", proj.ProjectName); - using (var apk = ZipHelper.OpenZip (Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath, "android", "bin", $"{proj.PackageName}.apk"))) { + using (var apk = ZipHelper.OpenZip (Path.Combine (Root, b.ProjectDirectory, proj.OutputPath, $"{proj.PackageName}-Signed.apk"))) { foreach (var a in libproj.OtherBuildItems.Where (x => x is AndroidItem.AndroidAsset)) { var item = a.Include ().ToLower ().Replace ("\\", "/"); if (item.EndsWith ("/")) 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 c53e2366002..34b43a62d57 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 @@ -1000,14 +1000,14 @@ public void BuildMkBundleApplicationRelease () "bundles", "armeabi-v7a", "libmonodroid_bundle_app.so"); Assert.IsTrue (File.Exists (libapp), "libmonodroid_bundle_app.so does not exist"); var apk = Path.Combine (Root, b.ProjectDirectory, - proj.IntermediateOutputPath, "android", "bin", $"{proj.PackageName}.apk"); + proj.OutputPath, $"{proj.PackageName}-Signed.apk"); using (var zipFile = ZipHelper.OpenZip (apk)) { Assert.IsNotNull (ZipHelper.ReadFileFromZip (zipFile, "lib/armeabi-v7a/libmonodroid_bundle_app.so"), - $"lib/armeabi-v7a/libmonodroid_bundle_app.so should be in the {proj.PackageName}.apk"); + $"lib/armeabi-v7a/libmonodroid_bundle_app.so should be in the {proj.PackageName}-Signed.apk"); Assert.IsNull (ZipHelper.ReadFileFromZip (zipFile, Path.Combine ("assemblies", "UnnamedProject.dll")), - $"UnnamedProject.dll should not be in the {proj.PackageName}.apk"); + $"UnnamedProject.dll should not be in the {proj.PackageName}-Signed.apk"); } } } @@ -1028,14 +1028,14 @@ public void BuildMkBundleApplicationReleaseAllAbi () "bundles", abi, "libmonodroid_bundle_app.so"); Assert.IsTrue (File.Exists (libapp), abi + " libmonodroid_bundle_app.so does not exist"); var apk = Path.Combine (Root, b.ProjectDirectory, - proj.IntermediateOutputPath, "android", "bin", $"{proj.PackageName}.apk"); + proj.OutputPath, $"{proj.PackageName}-Signed.apk"); using (var zipFile = ZipHelper.OpenZip (apk)) { Assert.IsNotNull (ZipHelper.ReadFileFromZip (zipFile, "lib/" + abi + "/libmonodroid_bundle_app.so"), - $"lib/{0}/libmonodroid_bundle_app.so should be in the {proj.PackageName}.apk", abi); + $"lib/{0}/libmonodroid_bundle_app.so should be in the {proj.PackageName}-Signed.apk", abi); Assert.IsNull (ZipHelper.ReadFileFromZip (zipFile, Path.Combine ("assemblies", "UnnamedProject.dll")), - $"UnnamedProject.dll should not be in the {proj.PackageName}.apk"); + $"UnnamedProject.dll should not be in the {proj.PackageName}-Signed.apk"); } } } @@ -1174,7 +1174,7 @@ public void BuildAfterMultiDexIsNotRequired ([Values ("dx", "d8")] string dexToo using (var b = CreateApkBuilder ()) { string intermediateDir = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath); string androidBinDir = Path.Combine (intermediateDir, "android", "bin"); - string apkPath = Path.Combine (androidBinDir, $"{proj.PackageName}.apk"); + string apkPath = Path.Combine (Root, b.ProjectDirectory, proj.OutputPath, $"{proj.PackageName}-Signed.apk"); Assert.IsTrue (b.Build (proj), "Build should have succeeded."); FileAssert.Exists (Path.Combine (androidBinDir, "classes.dex")); @@ -1901,7 +1901,7 @@ public void BuildApplicationWithJavaSource (bool isRelease, bool expectedResult) var runtimeInfo = b.GetSupportedRuntimes (); Assert.IsTrue (b.Build (proj), "Build should have succeeded."); var apkPath = Path.Combine (Root, b.ProjectDirectory, - proj.IntermediateOutputPath,"android", "bin", $"{proj.PackageName}.apk"); + proj.OutputPath, $"{proj.PackageName}-Signed.apk"); using (var apk = ZipHelper.OpenZip (apkPath)) { var runtime = runtimeInfo.FirstOrDefault (x => x.Abi == supportedAbi && x.Runtime == expectedRuntime); Assert.IsNotNull (runtime, "Could not find the expected runtime."); @@ -1945,13 +1945,13 @@ public void BuildApplicationWithJavaSource (bool isRelease, bool expectedResult) Assert.Ignore ("Cross compiler was not available"); Assert.IsTrue (b.Build (proj), "Build should have succeeded."); var apk = Path.Combine (Root, b.ProjectDirectory, - proj.IntermediateOutputPath, "android", "bin", $"{proj.PackageName}.apk"); + proj.OutputPath, $"{proj.PackageName}-Signed.apk"); var msymarchive = Path.Combine (Root, b.ProjectDirectory, proj.OutputPath, proj.PackageName + ".apk.mSYM"); using (var zipFile = ZipHelper.OpenZip (apk)) { var mdbExits = ZipHelper.ReadFileFromZip (zipFile, "assemblies/UnnamedProject.dll.mdb") != null || ZipHelper.ReadFileFromZip (zipFile, "assemblies/UnnamedProject.pdb") != null; Assert.AreEqual (embedMdb, mdbExits, - $"assemblies/UnnamedProject.dll.mdb or assemblies/UnnamedProject.pdb should{0}be in the {proj.PackageName}.apk", embedMdb ? " " : " not "); + $"assemblies/UnnamedProject.dll.mdb or assemblies/UnnamedProject.pdb should{0}be in the {proj.PackageName}-Signed.apk", embedMdb ? " " : " not "); if (aotAssemblies) { foreach (var abi in abis) { var assemblies = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath, @@ -1970,10 +1970,10 @@ public void BuildApplicationWithJavaSource (bool isRelease, bool expectedResult) Assert.IsTrue (File.Exists (assemblies), "{0} libaot-UnnamedProject.dll.so does not exist", abi); Assert.IsNotNull (ZipHelper.ReadFileFromZip (zipFile, string.Format ("lib/{0}/libaot-UnnamedProject.dll.so", abi)), - $"lib/{0}/libaot-UnnamedProject.dll.so should be in the {proj.PackageName}.apk", abi); + $"lib/{0}/libaot-UnnamedProject.dll.so should be in the {proj.PackageName}-Signed.apk", abi); Assert.IsNotNull (ZipHelper.ReadFileFromZip (zipFile, "assemblies/UnnamedProject.dll"), - $"UnnamedProject.dll should be in the {proj.PackageName}.apk"); + $"UnnamedProject.dll should be in the {proj.PackageName}-Signed.apk"); } } var runtimeInfo = b.GetSupportedRuntimes (); @@ -2059,7 +2059,8 @@ public void BuildWithNativeLibraries ([Values (true, false)] bool isRelease) using (var builder = CreateApkBuilder (Path.Combine (path, proj.ProjectName))) { Assert.IsTrue (builder.Build (proj), "Build should have succeeded."); var apk = Path.Combine (Root, builder.ProjectDirectory, - proj.IntermediateOutputPath, "android", "bin", $"{proj.PackageName}.apk"); + proj.OutputPath, $"{proj.PackageName}-Signed.apk"); + FileAssert.Exists (apk); Assert.IsTrue (StringAssertEx.ContainsText (builder.LastBuildOutput, "warning XA4301: APK already contains the item lib/armeabi-v7a/libRSSupport.so; ignoring."), "warning about skipping libRSSupport.so should have been raised"); using (var zipFile = ZipHelper.OpenZip (apk)) { @@ -2810,7 +2811,7 @@ public void BuildBasicApplicationCheckPdb () File.Exists (Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath, "android", "assets", "NetStandard16.pdb")), "NetStandard16.pdb must be copied to Intermediate directory"); var apk = Path.Combine (Root, b.ProjectDirectory, - proj.IntermediateOutputPath, "android", "bin", $"{proj.PackageName}.apk"); + proj.OutputPath, $"{proj.PackageName}-Signed.apk"); using (var zipFile = ZipHelper.OpenZip (apk)) { Assert.IsNotNull (ZipHelper.ReadFileFromZip (zipFile, "assemblies/NetStandard16.pdb"), @@ -3966,7 +3967,7 @@ public void KotlinServiceLoader ([Values ("apk", "aab")] string packageFormat) using (var b = CreateApkBuilder ()) { Assert.IsTrue (b.Build (proj), "build should have succeeded."); var archive = Path.Combine (Root, b.ProjectDirectory, - proj.IntermediateOutputPath, "android", "bin", $"{proj.PackageName}.{packageFormat}"); + proj.OutputPath, $"{proj.PackageName}.{packageFormat}"); var prefix = packageFormat == "apk" ? "" : "base/root/"; var expectedFiles = new [] { prefix + "META-INF/maven/com.google.code.gson/gson/pom.xml", diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/CodeBehindTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/CodeBehindTests.cs index 8a73f093a94..66106922b97 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/CodeBehindTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/CodeBehindTests.cs @@ -273,8 +273,14 @@ static CodeBehindTests () $"{ProjectName}.dll", "CommonSampleLibrary.dll", $"{PackageName}-Signed.apk", - $"{PackageName}.apk", }; + + if (!Builder.UseDotNet) { + produced_binaries.Add ($"{PackageName}.apk"); + } else { + produced_binaries.Add ($"{PackageName}.aab"); + produced_binaries.Add ($"{PackageName}-Signed.aab"); + } } [Test] diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/EnvironmentContentTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/EnvironmentContentTests.cs index d70ae029b15..aab5121ed82 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/EnvironmentContentTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/EnvironmentContentTests.cs @@ -248,7 +248,8 @@ public void BuildWithTlsProvider (string androidTlsProvider, bool isRelease, boo proj.SetProperty ("AndroidTlsProvider", androidTlsProvider); Assert.IsTrue (b.Build (proj), "Build should have succeeded."); var intermediateOutputDir = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath); - var apk = Path.Combine (intermediateOutputDir, "android", "bin", $"{proj.PackageName}.apk"); + var outpath = Path.Combine (Root, b.ProjectDirectory, proj.OutputPath); + var apk = Path.Combine (outpath, $"{proj.PackageName}-Signed.apk"); using (var zipFile = ZipHelper.OpenZip (apk)) { foreach (var abi in supportedAbis) { if (expected) { diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs index 465f674ac8e..d904609796c 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs @@ -1271,7 +1271,7 @@ public void AndroidAssetChange () TextContent = () => text }); using (var b = CreateApkBuilder ()) { - var apk = Path.Combine (Root, b.ProjectDirectory, proj.OutputPath, $"{proj.PackageName}.apk"); + var apk = Path.Combine (Root, b.ProjectDirectory, proj.OutputPath, $"{proj.PackageName}-Signed.apk"); Assert.IsTrue (b.Build (proj), "first build should succeed"); AssertAssetContents (apk); @@ -1312,7 +1312,7 @@ public void AndroidAssetMissing () using (var b = CreateApkBuilder ()) { Assert.IsTrue (b.Build (proj), "first build should succeed"); - var apk = Path.Combine (Root, b.ProjectDirectory, proj.OutputPath, $"{proj.PackageName}.apk"); + var apk = Path.Combine (Root, b.ProjectDirectory, proj.OutputPath, $"{proj.PackageName}-Signed.apk"); FileAssert.Exists (apk); using (var zip = ZipHelper.OpenZip (apk)) { Assert.IsTrue (zip.ContainsEntry ("assets/foo/bar.txt"), "bar.txt should exist in apk!"); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ManifestTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ManifestTest.cs index 14694bc24ce..50d07d1062d 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ManifestTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ManifestTest.cs @@ -518,7 +518,7 @@ public void ManifestPlaceholders ([Values ("legacy", "manifestmerger.jar")] stri Assert.IsTrue (manifest.Contains (" android:label=\"val1\""), "#1"); Assert.IsTrue (manifest.Contains (" x=\"a=b\\c\"".Replace ('\\', Path.DirectorySeparatorChar)), "#2"); Assert.IsTrue (manifest.Contains ("package=\"com.foo.bar\""), "PackageName should have been replaced with 'com.foo.bar'"); - var apk = Path.Combine (Root, builder.ProjectDirectory, proj.OutputPath, $"com.foo.bar.apk"); + var apk = Path.Combine (Root, builder.ProjectDirectory, proj.OutputPath, $"com.foo.bar-Signed.apk"); FileAssert.Exists (apk, $"'{apk}' should have been created."); } } 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 2da49dffd01..77fca11d4be 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 @@ -106,7 +106,7 @@ public void CheckIncludedAssemblies () using (var b = CreateApkBuilder ()) { Assert.IsTrue (b.Build (proj), "build should have succeeded."); var apk = Path.Combine (Root, b.ProjectDirectory, - proj.IntermediateOutputPath, "android", "bin", $"{proj.PackageName}.apk"); + proj.OutputPath, $"{proj.PackageName}-Signed.apk"); using (var zip = ZipHelper.OpenZip (apk)) { var existingFiles = zip.Where (a => a.FullName.StartsWith ("assemblies/", StringComparison.InvariantCultureIgnoreCase)); var missingFiles = expectedFiles.Where (x => !zip.ContainsEntry ("assemblies/" + Path.GetFileName (x))); @@ -138,7 +138,7 @@ public void CheckClassesDexIsIncluded ([Values ("dx", "d8", "invalid")] string d Assert.IsTrue (b.Build (proj), "build failed"); var apk = Path.Combine (Root, b.ProjectDirectory, - proj.IntermediateOutputPath, "android", "bin", $"{proj.PackageName}.apk"); + proj.OutputPath, $"{proj.PackageName}-Signed.apk"); using (var zip = ZipHelper.OpenZip (apk)) { Assert.IsTrue (zip.ContainsEntry ("classes.dex"), "Apk should contain classes.dex"); } @@ -161,7 +161,7 @@ public void CheckIncludedNativeLibraries ([Values (true, false)] bool compressNa b.ThrowOnBuildFailure = false; Assert.IsTrue (b.Build (proj), "build failed"); var apk = Path.Combine (Root, b.ProjectDirectory, - proj.IntermediateOutputPath, "android", "bin", $"{proj.PackageName}.apk"); + proj.OutputPath, $"{proj.PackageName}-Signed.apk"); CompressionMethod method = compressNativeLibraries ? CompressionMethod.Deflate : CompressionMethod.Store; using (var zip = ZipHelper.OpenZip (apk)) { var libFiles = zip.Where (x => x.FullName.StartsWith("lib/") && !x.FullName.Equals("lib/", StringComparison.InvariantCultureIgnoreCase)); @@ -192,7 +192,7 @@ public void EmbeddedDSOs () AssertExtractNativeLibs (manifest, extractNativeLibs: false); var apk = Path.Combine (Root, b.ProjectDirectory, - proj.IntermediateOutputPath, "android", "bin", $"{proj.PackageName}.apk"); + proj.OutputPath, $"{proj.PackageName}-Signed.apk"); AssertEmbeddedDSOs (apk); //Delete the apk & build again @@ -241,7 +241,7 @@ public void IncrementalCompression () Assert.IsTrue (b.Build (proj), "first build should have succeeded"); var apk = Path.Combine (Root, b.ProjectDirectory, - proj.IntermediateOutputPath, "android", "bin", $"{proj.PackageName}.apk"); + proj.OutputPath, $"{proj.PackageName}-Signed.apk"); FileAssert.Exists (apk); using (var zip = ZipHelper.OpenZip (apk)) { foreach (var entry in zip) { @@ -698,7 +698,7 @@ protected override void OnCreate (Bundle bundle) Assert.IsTrue (builder.Build (netStandardProject), "XamFormsSample should have built."); Assert.IsTrue (ab.Build (app), "App should have built."); var apk = Path.Combine (Root, ab.ProjectDirectory, - app.IntermediateOutputPath, "android", "bin", $"{app.PackageName}.apk"); + app.OutputPath, $"{app.PackageName}-Signed.apk"); using (var zip = ZipHelper.OpenZip (apk)) { var existingFiles = zip.Where (a => a.FullName.StartsWith ("assemblies/", StringComparison.InvariantCultureIgnoreCase)); var missingFiles = expectedFiles.Where (x => !zip.ContainsEntry ("assemblies/" + Path.GetFileName (x))); @@ -808,7 +808,7 @@ public void MissingDexFile () 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", $"{proj.PackageName}.apk"); + proj.OutputPath, $"{proj.PackageName}-Signed.apk"); using (var zip = ZipHelper.OpenZip (apk)) { Assert.IsTrue (zip.ContainsEntry ("classes.dex"), "Apk should contain classes.dex"); } @@ -845,7 +845,7 @@ public void MissingSatelliteAssemblyInLibrary () Assert.IsTrue (appBuilder.Build (app), "App SignAndroidPackage should have succeeded."); var apk = Path.Combine (Root, appBuilder.ProjectDirectory, - app.IntermediateOutputPath, "android", "bin", $"{app.PackageName}.apk"); + app.OutputPath, $"{app.PackageName}-Signed.apk"); using (var zip = ZipHelper.OpenZip (apk)) { Assert.IsTrue (zip.ContainsEntry ($"assemblies/es/{lib.ProjectName}.resources.dll"), "Apk should contain satellite assemblies!"); } @@ -874,7 +874,7 @@ public void MissingSatelliteAssemblyInApp () Assert.IsTrue (b.Build (proj), "SignAndroidPackage should have succeeded."); var apk = Path.Combine (Root, b.ProjectDirectory, - proj.IntermediateOutputPath, "android", "bin", $"{proj.PackageName}.apk"); + proj.OutputPath, $"{proj.PackageName}-Signed.apk"); using (var zip = ZipHelper.OpenZip (apk)) { Assert.IsTrue (zip.ContainsEntry ($"assemblies/es/{proj.ProjectName}.resources.dll"), "Apk should contain satellite assemblies!"); } @@ -924,8 +924,8 @@ public class Test using (var b = CreateApkBuilder (Path.Combine (path, app.ProjectName))) { Assert.IsTrue (b.Build (app), "Build of jar should have succeeded."); var jar = Builder.UseDotNet ? "2965D0C9A2D5DB1E.jar" : "test.jar"; - string expected = $"Failed to add jar entry AndroidManifest.xml from {jar}: the same file already exists in the apk"; - Assert.IsTrue (b.LastBuildOutput.ContainsText (expected), $"AndroidManifest.xml for test.jar should have been ignored."); + string expected = $"Ignoring jar entry AndroidManifest.xml from {jar}: the same file already exists in the apk"; + Assert.IsTrue (b.LastBuildOutput.ContainsText (expected), $"AndroidManifest.xml for {jar} should have been ignored."); } } } @@ -947,7 +947,7 @@ public void ExtractNativeLibsTrue () // All .so files should be compressed var apk = Path.Combine (Root, b.ProjectDirectory, - proj.IntermediateOutputPath, "android", "bin", $"{proj.PackageName}.apk"); + proj.OutputPath, $"{proj.PackageName}-Signed.apk"); using (var zip = ZipHelper.OpenZip (apk)) { foreach (var entry in zip) { if (entry.FullName.EndsWith (".so")) { diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs index e8cc12135bf..9786e8a3762 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs @@ -203,7 +203,7 @@ public void RemoveDesigner () string assemblyName = proj.ProjectName; using (var b = CreateApkBuilder ()) { Assert.IsTrue (b.Build (proj), "build should have succeeded."); - var apk = Path.Combine (Root, b.ProjectDirectory, proj.OutputPath, $"{proj.PackageName}.apk"); + var apk = Path.Combine (Root, b.ProjectDirectory, proj.OutputPath, $"{proj.PackageName}-Signed.apk"); FileAssert.Exists (apk); using (var zip = ZipHelper.OpenZip (apk)) { Assert.IsTrue (zip.ContainsEntry ($"assemblies/{assemblyName}.dll"), $"{assemblyName}.dll should exist in apk!"); @@ -252,7 +252,7 @@ public void LinkDescription () Assert.IsTrue (b.Build (proj, doNotCleanupOnUpdate: true), "second build should have succeeded."); - var apk = Path.Combine (Root, b.ProjectDirectory, proj.OutputPath, $"{proj.PackageName}.apk"); + var apk = Path.Combine (Root, b.ProjectDirectory, proj.OutputPath, $"{proj.PackageName}-Signed.apk"); FileAssert.Exists (apk); using (var zip = ZipHelper.OpenZip (apk)) { var entry = zip.ReadEntry ($"assemblies/{assembly_name}.dll"); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs index 492df957658..8b1012e1edf 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Xml.Linq; @@ -162,8 +163,8 @@ public Foo () var appBuilder = CreateDotNetBuilder (appA, Path.Combine (path, appA.ProjectName)); Assert.IsTrue (appBuilder.Build (), $"{appA.ProjectName} should succeed"); - // Check .apk for assets, res, and native libraries - var apkPath = Path.Combine (FullProjectDirectory, appA.OutputPath, $"{appA.PackageName}.apk"); + // Check .apk/.aab for assets, res, and native libraries + var apkPath = Path.Combine (FullProjectDirectory, appA.OutputPath, $"{appA.PackageName}-Signed.apk"); FileAssert.Exists (apkPath); using (var apk = ZipHelper.OpenZip (apkPath)) { apk.AssertContainsEntry (apkPath, "assets/foo/foo.txt"); @@ -443,17 +444,34 @@ public void DotNetBuild (string runtimeIdentifiers, bool isRelease, bool aot) var files = Directory.EnumerateFileSystemEntries (outputPath) .Select (Path.GetFileName) - .OrderBy (f => f) + .OrderBy (f => f, StringComparer.OrdinalIgnoreCase) .ToArray (); - var expectedFiles = new[]{ - $"{proj.PackageName}.apk", - $"{proj.PackageName}-Signed.apk", - "es", - $"{proj.ProjectName}.dll", - $"{proj.ProjectName}.pdb", - $"{proj.ProjectName}.runtimeconfig.json", - $"{proj.ProjectName}.xml", - }; + IEnumerable expectedFiles; + if (isRelease) { + expectedFiles = new string[] { + $"{proj.PackageName}.aab", + $"{proj.PackageName}-Signed.aab", + $"{proj.PackageName}-Signed.apk", + "es", + $"{proj.ProjectName}.dll", + $"{proj.ProjectName}.pdb", + $"{proj.ProjectName}.runtimeconfig.json", + $"{proj.ProjectName}.xml", + }; + } else { + expectedFiles = new string[] { + $"{proj.PackageName}.apk", + $"{proj.PackageName}-Signed.apk", + "es", + $"{proj.ProjectName}.dll", + $"{proj.ProjectName}.pdb", + $"{proj.ProjectName}.runtimeconfig.json", + $"{proj.ProjectName}.xml", + }; + } + + expectedFiles = expectedFiles.OrderBy(f => f, StringComparer.OrdinalIgnoreCase); + CollectionAssert.AreEqual (expectedFiles, files, $"Expected: {string.Join (";", expectedFiles)}\n Found: {string.Join (";", files)}"); var assemblyPath = Path.Combine (outputPath, $"{proj.ProjectName}.dll"); @@ -484,7 +502,7 @@ public void DotNetBuild (string runtimeIdentifiers, bool isRelease, bool aot) Assert.AreEqual ("30", uses_sdk.Attribute (ns + "targetSdkVersion").Value); bool expectEmbeddedAssembies = !(CommercialBuildAvailable && !isRelease); - var apkPath = Path.Combine (outputPath, $"{proj.PackageName}.apk"); + var apkPath = Path.Combine (outputPath, $"{proj.PackageName}-Signed.apk"); FileAssert.Exists (apkPath); using (var apk = ZipHelper.OpenZip (apkPath)) { apk.AssertContainsEntry (apkPath, $"assemblies/{proj.ProjectName}.dll", shouldContainEntry: expectEmbeddedAssembies); @@ -552,16 +570,20 @@ public void DotNetPublish ([Values (false, true)] bool isRelease) Assert.IsTrue (dotnet.Publish (), "first `dotnet publish` should succeed"); var publishDirectory = Path.Combine (FullProjectDirectory, proj.OutputPath, runtimeIdentifier, "publish"); - var apk = Path.Combine (publishDirectory, $"{proj.PackageName}.apk"); - var apkSigned = Path.Combine (publishDirectory, $"{proj.PackageName}-Signed.apk"); + string ext = isRelease ? "aab" : "apk"; + var apk = Path.Combine (publishDirectory, $"{proj.PackageName}.{ext}"); + var apkSigned = Path.Combine (publishDirectory, $"{proj.PackageName}-Signed.{ext}"); FileAssert.Exists (apk); FileAssert.Exists (apkSigned); Assert.IsTrue (dotnet.Publish (parameters: new [] { "AndroidPackageFormat=aab" }), $"second `dotnet publish` should succeed"); var aab = Path.Combine (publishDirectory, $"{proj.PackageName}.aab"); var aabSigned = Path.Combine (publishDirectory, $"{proj.PackageName}-Signed.aab"); - FileAssert.DoesNotExist (apk); - FileAssert.DoesNotExist (apkSigned); + if (isRelease) { + FileAssert.Exists (apkSigned); + } else { + FileAssert.DoesNotExist (apkSigned); + } FileAssert.Exists (aab); FileAssert.Exists (aabSigned); } @@ -594,7 +616,7 @@ void CreateEmptyFile (params string [] paths) Assert.IsTrue (dotnet.Build (), "`dotnet build` should succeed"); - var apkPath = Path.Combine (FullProjectDirectory, proj.OutputPath, $"{proj.PackageName}.apk"); + var apkPath = Path.Combine (FullProjectDirectory, proj.OutputPath, $"{proj.PackageName}-Signed.apk"); FileAssert.Exists (apkPath); using (var apk = ZipHelper.OpenZip (apkPath)) { apk.AssertContainsEntry (apkPath, "res/raw/foo.txt"); diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index c9cde7bd350..9397383a0b9 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -91,6 +91,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. + + aab + <_AndroidAdditionalPackageFormats Condition=" $(AndroidPackageFormats.Contains('apk')) And '$(AndroidPackageFormat)' == 'aab' ">apk apk True False @@ -529,6 +534,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. <_BaseZipIntermediate>$(IntermediateOutputPath)android\bin\base.zip <_AppBundleIntermediate>$(IntermediateOutputPath)android\bin\$(_AndroidPackage).aab <_ApkSetIntermediate>$(IntermediateOutputPath)android\bin\$(_AndroidPackage).apks + <_UniversalApkSetIntermediate>$(IntermediateOutputPath)android\bin\$(_AndroidPackage)-Universal.apks $(OutDir)$(_AndroidPackage).apk $(OutDir)$(_AndroidPackage)-Signed.apk <_AabFile>$(OutDir)$(_AndroidPackage).aab @@ -2300,11 +2306,47 @@ because xbuild doesn't support framework reference assemblies. + + + + + + + <_FilestoExtract Include="universal.apk" > + $(_AndroidPackage)-Signed.apk + + + + + + + Build; Package; _Sign; + _CreateUniversalApkFromBundle; @@ -2315,6 +2357,7 @@ because xbuild doesn't support framework reference assemblies. CreateSatelliteAssemblies; _CopyPackage; _Sign; + _CreateUniversalApkFromBundle; @@ -2490,6 +2533,16 @@ because xbuild doesn't support framework reference assemblies. KeyPass="$(_ApkKeyPass)" StorePass="$(_ApkStorePass)" ExtraArgs="$(AndroidBundleToolExtraArgs)" + Condition="!Exists('$(_ApkSetIntermediate)')" + /> + + <_UninstallCommand>"$(AdbToolPath)adb" $(AdbTarget) uninstall -k "$(_AndroidPackage)" + + Resources Assets armeabi-v7a;x86 + apk diff --git a/tests/MSBuildDeviceIntegration/Tests/BundleToolTests.cs b/tests/MSBuildDeviceIntegration/Tests/BundleToolTests.cs index fb4f4b541df..9c0deb624fc 100644 --- a/tests/MSBuildDeviceIntegration/Tests/BundleToolTests.cs +++ b/tests/MSBuildDeviceIntegration/Tests/BundleToolTests.cs @@ -316,7 +316,7 @@ public void BuildApkSetCommand () string aapt2 = Path.Combine (task.Aapt2ToolPath, task.Aapt2ToolExe); string adb = Path.Combine (task.AdbToolPath, task.AdbToolExe); string cmd = task.GetCommandLineBuilder ().ToString (); - Assert.AreEqual ($"build-apks --connected-device --bundle foo.aab --output foo.apks --mode default --adb \"{adb}\" --device-id emulator-5554 --aapt2 \"{aapt2}\" --ks foo.keystore --ks-key-alias alias --key-pass pass:keypass --ks-pass pass:storepass", cmd); + Assert.AreEqual ($"build-apks --connected-device --mode default --adb \"{adb}\" --device-id emulator-5554 --bundle foo.aab --output foo.apks --aapt2 \"{aapt2}\" --ks foo.keystore --ks-key-alias alias --key-pass pass:keypass --ks-pass pass:storepass", cmd); } [Test] diff --git a/tests/MSBuildDeviceIntegration/Tests/DebuggingTest.cs b/tests/MSBuildDeviceIntegration/Tests/DebuggingTest.cs index 2ed567d90a3..48ed3c61daa 100755 --- a/tests/MSBuildDeviceIntegration/Tests/DebuggingTest.cs +++ b/tests/MSBuildDeviceIntegration/Tests/DebuggingTest.cs @@ -54,6 +54,10 @@ public void ApplicationRunsWithoutDebugger ([Values (false, true)] bool isReleas proj.SetAndroidSupportedAbis ("armeabi-v7a", "x86"); } proj.SetDefaultTargetDevice (); + if (Builder.UseDotNet && isRelease) { + // bundle tool does NOT support embeddedDex files it seems. + useEmbeddedDex = false; + } using (var b = CreateApkBuilder (Path.Combine ("temp", TestName))) { SetTargetFrameworkAndManifest (proj, b); proj.AndroidManifest = proj.AndroidManifest.Replace (" l.Contains ("Building target \"_BuildApkFastDev\" completely.") ||