From fd47b02761eb30443bf04ca793ac27b12355748e Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Wed, 25 May 2022 15:33:12 -0500 Subject: [PATCH] [One .NET] support building net6.0-android apps in .NET 7 (#6988) We currently have a hard dependency between: * `Xamarin.Android.Build.Tasks.dll` * `libmonodroid.so` The design for building a `net6.0-android` app with a .NET 7 SDK results in us using a .NET 7 `Xamarin.Android.Builds.Tasks.dll` and a .NET 6 `libmonodroid.so`. This crashes with: E AndroidRuntime: java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "java_name_width" referenced by "/data/app/~~8CHtXY8wK4g7s9VcOLTQEg==/com.microsoft.net6.helloandroid-cUUhwuQls7TRb6JdBsbPdg==/split_config.arm64_v8a.apk!/lib/arm64-v8a/libmonodroid.so"... To solve this problem: * Define `$(AndroidNet6Version)` to specify our .NET 6 GA `32.0.301` build that is released. * Add an alias for a `Microsoft.Android.Sdk.$(HostOS).NET6` workload pack that loads a .NET 6 version of `Microsoft.Android.Sdk.$(HostOS)`. * Include both the .NET 6 and .NET 7 versions of these packs in the workload. * Check `$(TargetFrameworkVersion)` and import the .NET 7 or .NET 6 MSBuild targets when appropriate. * Update `@(KnownFrameworkReference)` to the version of .NET 6 that our .NET 7 packs know about. * We have a matching .NET 6 `Xamarin.Android.Build.Tasks.dll` and `libmonodroid.so`! This obviously increases our install footprint of `Microsoft.Android.Sdk.$(HostOS)` by 2x. We might be able to address this by splitting out some files into a `Microsoft.Android.Sdk.Tooling` pack that can be shared between .NET 6 and .NET 7. (Perhaps files like `bundletool.jar` and `r8.jar` could be shared?) One hack I had to put in place was to avoid the crash: E AndroidRuntime: java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "mono_opt_aot_lazy_assembly_load" referenced by "/data/app/~~Fp4gEr_9sxU1qU6PoI-v6Q==/com.companyname.foo-EKEtL67XJI-d0H17_3oz3g==/lib/arm64/libmonodroid.so"... The .NET 7 SDK we are on does not yet know about .NET runtime 6.0.5 (it uses 6.0.3), so I manually fixed this version number for now. We should be able to remove this eventually when the .NET 7 SDK provides 6.0.5 by default. A second hack I put in place: Make `Microsoft.Android.Sdk.NET6` a "framework" and not an "sdk" within `WorkloadManifest.json`. This prevents *both* the .NET 6 `AutoImport.props` *and* .NET 7 `AutoImport.props` files from being imported, causing duplication of item groups/etc. This seems very weird, but this still works: We can remove this if I condition everything in `AutoImport.props` behind a current `$(TargetFrameworkVersion)`. We would then need to ship this in a .NET 6 release. --- Configuration.props | 1 + .../create-packs/Directory.Build.targets | 2 ++ .../Microsoft.NET.Sdk.Android.proj | 12 ++++++--- ...oft.Android.Sdk.BundledVersions.in.targets | 12 --------- .../WorkloadManifest.in.json | 12 +++++++++ .../WorkloadManifest.in.targets | 26 +++++++++++++++++++ .../WorkloadManifest.targets | 8 ------ .../Xamarin.Android.Build.Tests/XASdkTests.cs | 6 ++--- .../Common/TestEnvironment.cs | 18 ++++++++++++- .../Tests/XASdkDeployTests.cs | 17 ++++++++++-- 10 files changed, 85 insertions(+), 29 deletions(-) create mode 100644 src/Xamarin.Android.Build.Tasks/Microsoft.NET.Sdk.Android/WorkloadManifest.in.targets delete mode 100644 src/Xamarin.Android.Build.Tasks/Microsoft.NET.Sdk.Android/WorkloadManifest.targets diff --git a/Configuration.props b/Configuration.props index 0f4d1ab5222..5cd6189b89f 100644 --- a/Configuration.props +++ b/Configuration.props @@ -47,6 +47,7 @@ portable True latest + 32.0.301 Windows diff --git a/build-tools/create-packs/Directory.Build.targets b/build-tools/create-packs/Directory.Build.targets index da6907d6b0f..b15fe39a488 100644 --- a/build-tools/create-packs/Directory.Build.targets +++ b/build-tools/create-packs/Directory.Build.targets @@ -123,6 +123,8 @@ <_NuGetSources Include="$(OutputPath.TrimEnd('\'))" /> + + <_NuGetSources Include="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet6/nuget/v3/index.json" /> <_PreviewPacks Condition=" '$(AndroidLatestStableApiLevel)' != '$(AndroidLatestUnstableApiLevel)' " Include="$(XamarinAndroidSourcePath)bin\Build$(Configuration)\nuget-unsigned\Microsoft.Android.Ref.$(AndroidLatestUnstableApiLevel).*.nupkg" /> <_InstallArguments Include="android" /> <_InstallArguments Include="android-$(AndroidLatestUnstableApiLevel)" Condition=" '@(_PreviewPacks->Count())' != '0' " /> diff --git a/build-tools/create-packs/Microsoft.NET.Sdk.Android.proj b/build-tools/create-packs/Microsoft.NET.Sdk.Android.proj index b3ef2ac1a85..767a2563c48 100644 --- a/build-tools/create-packs/Microsoft.NET.Sdk.Android.proj +++ b/build-tools/create-packs/Microsoft.NET.Sdk.Android.proj @@ -28,18 +28,24 @@ about the various Microsoft.Android workloads. DependsOnTargets="_GetDefaultPackageVersion;_GetLicense"> $(OutputPath)workload-manifest\WorkloadManifest.json + $(OutputPath)workload-manifest\WorkloadManifest.targets - + + Replacements="@WORKLOAD_VERSION@=$(AndroidPackVersionLong);@NET6_VERSION@=$(AndroidNet6Version)"> + + - <_PackageFiles Include="$(XamarinAndroidSourcePath)src\Xamarin.Android.Build.Tasks\Microsoft.NET.Sdk.Android\WorkloadManifest.targets" PackagePath="data" /> <_PackageFiles Include="$(WorkloadManifestJsonPath)" PackagePath="data" /> + <_PackageFiles Include="$(WorkloadManifestTargetsPath)" PackagePath="data" /> diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/in/Microsoft.Android.Sdk.BundledVersions.in.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/in/Microsoft.Android.Sdk.BundledVersions.in.targets index f78f80bd585..607626dc3b1 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/in/Microsoft.Android.Sdk.BundledVersions.in.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/in/Microsoft.Android.Sdk.BundledVersions.in.targets @@ -18,18 +18,6 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and <_AndroidRuntimePackId Condition=" '$(_AndroidRuntimePackId)' == '' ">$(_AndroidTargetingPackId) - - + + + + + + + + + + + + + + + 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 deleted file mode 100644 index fcbee9812bf..00000000000 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.NET.Sdk.Android/WorkloadManifest.targets +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - 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 1562bc9d3c9..2f1fbc095e6 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 @@ -233,12 +233,12 @@ public void DotNetNew ([Values ("android", "androidlib", "android-bindinglib")] new object[] { "net6.0", "android", - XABuildConfig.AndroidDefaultTargetDotnetApiLevel, + 31, }, new object[] { "net6.0", - $"android{XABuildConfig.AndroidDefaultTargetDotnetApiLevel}", - XABuildConfig.AndroidDefaultTargetDotnetApiLevel, + "android31", + 31, }, new object[] { "net7.0", diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/TestEnvironment.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/TestEnvironment.cs index 4bd432cb75f..d367c949209 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/TestEnvironment.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/TestEnvironment.cs @@ -108,10 +108,26 @@ public static string DotNetAndroidSdkDirectory { IsWindows ? "Microsoft.Android.Sdk.Windows" : "Microsoft.Android.Sdk.Linux"; - return _dotNetAndroidSdkDirectory = Directory.GetDirectories (Path.Combine (AndroidSdkResolver.GetDotNetPreviewPath (), "packs", sdkName)).LastOrDefault (); + var directories = from d in Directory.GetDirectories (Path.Combine (AndroidSdkResolver.GetDotNetPreviewPath (), "packs", sdkName)) + let version = ParseVersion (d) + orderby version descending + select d; + return _dotNetAndroidSdkDirectory = directories.FirstOrDefault (); } } + static Version ParseVersion (string path) + { + var folderName = Path.GetFileName (path); + var index = folderName.IndexOf ('-'); + if (index != -1) { + folderName = folderName.Substring (0, index); + } + if (Version.TryParse (folderName, out var v)) + return v; + return new Version (0, 0); + } + public static string DotNetAndroidSdkToolsDirectory { get { return Path.Combine (DotNetAndroidSdkDirectory, "tools"); diff --git a/tests/MSBuildDeviceIntegration/Tests/XASdkDeployTests.cs b/tests/MSBuildDeviceIntegration/Tests/XASdkDeployTests.cs index b0afcfe2663..da486f9e553 100644 --- a/tests/MSBuildDeviceIntegration/Tests/XASdkDeployTests.cs +++ b/tests/MSBuildDeviceIntegration/Tests/XASdkDeployTests.cs @@ -23,32 +23,43 @@ public class XASdkDeployTests : DeviceTest /* isRelease */ false, /* xamarinForms */ false, /* publishTrimmed */ default (bool?), + /* targetFramework*/ "net7.0-android", }, new object[] { /* isRelease */ true, /* xamarinForms */ false, /* publishTrimmed */ default (bool?), + /* targetFramework*/ "net7.0-android", }, new object[] { /* isRelease */ false, /* xamarinForms */ true, /* publishTrimmed */ default (bool?), + /* targetFramework*/ "net7.0-android", }, new object[] { /* isRelease */ true, /* xamarinForms */ true, /* publishTrimmed */ default (bool?), + /* targetFramework*/ "net7.0-android", }, new object[] { /* isRelease */ true, /* xamarinForms */ false, /* publishTrimmed */ false, + /* targetFramework*/ "net7.0-android", + }, + new object[] { + /* isRelease */ true, + /* xamarinForms */ true, + /* publishTrimmed */ true, + /* targetFramework*/ "net6.0-android", }, }; [Test] [TestCaseSource (nameof (DotNetInstallAndRunSource))] - public void DotNetInstallAndRun (bool isRelease, bool xamarinForms, bool? publishTrimmed) + public void DotNetInstallAndRun (bool isRelease, bool xamarinForms, bool? publishTrimmed, string targetFramework) { AssertHasDevices (); @@ -62,6 +73,7 @@ public void DotNetInstallAndRun (bool isRelease, bool xamarinForms, bool? publis IsRelease = isRelease }; } + proj.TargetFramework = targetFramework; if (publishTrimmed != null) { proj.SetProperty (KnownProperties.PublishTrimmed, publishTrimmed.ToString ()); } @@ -137,12 +149,13 @@ public void TypeAndMemberRemapping ([Values (false, true)] bool isRelease) [Test] [Category ("Debugger"), Category ("Node-4")] - public void DotNetDebug () + public void DotNetDebug ([Values("net6.0-android", "net7.0-android")] string targetFramework) { AssertCommercialBuild (); AssertHasDevices (); var proj = new XASdkProject (); + proj.TargetFramework = targetFramework; proj.SetRuntimeIdentifier (DeviceAbi); string runtimeId = proj.GetProperty (KnownProperties.RuntimeIdentifier);