Skip to content

Commit fd47b02

Browse files
[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: <Import Project="Sdk.targets" Sdk="Microsoft.Android.Sdk" /> 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.
1 parent 90aec41 commit fd47b02

File tree

10 files changed

+85
-29
lines changed

10 files changed

+85
-29
lines changed

Configuration.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
<DebugType Condition=" '$(DebugType)' == '' ">portable</DebugType>
4848
<Deterministic Condition=" '$(Deterministic)' == '' ">True</Deterministic>
4949
<LangVersion Condition=" '$(LangVersion)' == '' ">latest</LangVersion>
50+
<AndroidNet6Version Condition=" '$(AndroidNet6Version)' == '' ">32.0.301</AndroidNet6Version>
5051
</PropertyGroup>
5152
<PropertyGroup Condition=" '$(HostOS)' == '' ">
5253
<HostOS Condition="$([MSBuild]::IsOSPlatform('windows'))">Windows</HostOS>

build-tools/create-packs/Directory.Build.targets

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@
123123
</PropertyGroup>
124124
<ItemGroup>
125125
<_NuGetSources Include="$(OutputPath.TrimEnd('\'))" />
126+
<!-- This allows us to install our Android .NET 6 packs -->
127+
<_NuGetSources Include="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet6/nuget/v3/index.json" />
126128
<_PreviewPacks Condition=" '$(AndroidLatestStableApiLevel)' != '$(AndroidLatestUnstableApiLevel)' " Include="$(XamarinAndroidSourcePath)bin\Build$(Configuration)\nuget-unsigned\Microsoft.Android.Ref.$(AndroidLatestUnstableApiLevel).*.nupkg" />
127129
<_InstallArguments Include="android" />
128130
<_InstallArguments Include="android-$(AndroidLatestUnstableApiLevel)" Condition=" '@(_PreviewPacks->Count())' != '0' " />

build-tools/create-packs/Microsoft.NET.Sdk.Android.proj

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,24 @@ about the various Microsoft.Android workloads.
2828
DependsOnTargets="_GetDefaultPackageVersion;_GetLicense">
2929
<PropertyGroup>
3030
<WorkloadManifestJsonPath>$(OutputPath)workload-manifest\WorkloadManifest.json</WorkloadManifestJsonPath>
31+
<WorkloadManifestTargetsPath>$(OutputPath)workload-manifest\WorkloadManifest.targets</WorkloadManifestTargetsPath>
3132
</PropertyGroup>
3233

33-
<MakeDir Directories="$([System.IO.Path]::GetDirectoryName ($(WorkloadManifestJsonPath)))" />
34+
<MakeDir Directories="$(OutputPath)workload-manifest" />
3435
<ReplaceFileContents
3536
SourceFile="$(XamarinAndroidSourcePath)src\Xamarin.Android.Build.Tasks\Microsoft.NET.Sdk.Android\WorkloadManifest.in.json"
3637
DestinationFile="$(WorkloadManifestJsonPath)"
37-
Replacements="@WORKLOAD_VERSION@=$(AndroidPackVersionLong)">
38+
Replacements="@WORKLOAD_VERSION@=$(AndroidPackVersionLong);@NET6_VERSION@=$(AndroidNet6Version)">
39+
</ReplaceFileContents>
40+
<ReplaceFileContents
41+
SourceFile="$(XamarinAndroidSourcePath)src\Xamarin.Android.Build.Tasks\Microsoft.NET.Sdk.Android\WorkloadManifest.in.targets"
42+
DestinationFile="$(WorkloadManifestTargetsPath)"
43+
Replacements="@NET6_VERSION@=$(AndroidNet6Version)">
3844
</ReplaceFileContents>
3945

4046
<ItemGroup>
41-
<_PackageFiles Include="$(XamarinAndroidSourcePath)src\Xamarin.Android.Build.Tasks\Microsoft.NET.Sdk.Android\WorkloadManifest.targets" PackagePath="data" />
4247
<_PackageFiles Include="$(WorkloadManifestJsonPath)" PackagePath="data" />
48+
<_PackageFiles Include="$(WorkloadManifestTargetsPath)" PackagePath="data" />
4349
</ItemGroup>
4450
</Target>
4551

src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/in/Microsoft.Android.Sdk.BundledVersions.in.targets

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,6 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
1818
<_AndroidRuntimePackId Condition=" '$(_AndroidRuntimePackId)' == '' ">$(_AndroidTargetingPackId)</_AndroidRuntimePackId>
1919
</PropertyGroup>
2020
<ItemGroup>
21-
<!-- TODO: NET7TODO: Is this the right way to support building net6.0 projects? Need to bump versions as packages are release to NuGet.org as well -->
22-
<KnownFrameworkReference
23-
Include="Microsoft.Android"
24-
TargetFramework="net6.0"
25-
RuntimeFrameworkName="Microsoft.Android"
26-
LatestRuntimeFrameworkVersion="32.0.300-rc.1.4"
27-
TargetingPackName="Microsoft.Android.Ref.$(_AndroidTargetingPackId)"
28-
TargetingPackVersion="32.0.300-rc.1.4"
29-
RuntimePackNamePatterns="Microsoft.Android.Runtime.$(_AndroidRuntimePackId).**RID**"
30-
RuntimePackRuntimeIdentifiers="android-arm;android-arm64;android-x86;android-x64"
31-
Profile="Android"
32-
/>
3321
<KnownFrameworkReference
3422
Include="Microsoft.Android"
3523
TargetFramework="@DOTNET_TARGET_FRAMEWORK@"

src/Xamarin.Android.Build.Tasks/Microsoft.NET.Sdk.Android/WorkloadManifest.in.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"description": ".NET SDK Workload for building Android applications.",
66
"packs": [
77
"Microsoft.Android.Sdk",
8+
"Microsoft.Android.Sdk.NET6",
89
"Microsoft.Android.Ref.32",
910
"Microsoft.Android.Runtime.32.android-arm",
1011
"Microsoft.Android.Runtime.32.android-arm64",
@@ -40,6 +41,17 @@
4041
"linux-x64": "Microsoft.Android.Sdk.Linux"
4142
}
4243
},
44+
"Microsoft.Android.Sdk.NET6": {
45+
"kind": "framework",
46+
"version": "@NET6_VERSION@",
47+
"alias-to": {
48+
"osx-x64": "Microsoft.Android.Sdk.Darwin",
49+
"osx-arm64": "Microsoft.Android.Sdk.Darwin",
50+
"win-x86": "Microsoft.Android.Sdk.Windows",
51+
"win-x64": "Microsoft.Android.Sdk.Windows",
52+
"linux-x64": "Microsoft.Android.Sdk.Linux"
53+
}
54+
},
4355
"Microsoft.Android.Ref.32": {
4456
"kind": "framework",
4557
"version": "@WORKLOAD_VERSION@"
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<Project>
2+
<ImportGroup Condition=" '$(TargetPlatformIdentifier)' == 'android' ">
3+
<Import Project="Sdk.targets" Sdk="Microsoft.Android.Sdk"
4+
Condition=" $([MSBuild]::VersionEquals($(TargetFrameworkVersion), '7.0')) " />
5+
<Import Project="Sdk.targets" Sdk="Microsoft.Android.Sdk.NET6"
6+
Condition=" $([MSBuild]::VersionEquals($(TargetFrameworkVersion), '6.0')) " />
7+
</ImportGroup>
8+
9+
<ItemGroup Condition=" '$(TargetPlatformIdentifier)' == 'android' and $([MSBuild]::VersionEquals($(TargetFrameworkVersion), '6.0')) ">
10+
<KnownFrameworkReference
11+
Update="Microsoft.Android"
12+
LatestRuntimeFrameworkVersion="@NET6_VERSION@"
13+
TargetingPackVersion="@NET6_VERSION@"
14+
/>
15+
<!--
16+
HACK:
17+
The .NET 7 SDK specifies 6.0.3 for .NET 6, but our .NET 6 libmonodroid.so depends on 6.0.5.
18+
We should be able to remove this when we get a newer .NET 7 SDK.
19+
-->
20+
<KnownRuntimePack Update="Microsoft.NETCore.App" LatestRuntimeFrameworkVersion="6.0.5" />
21+
</ItemGroup>
22+
23+
<ItemGroup Condition=" '$(TargetFrameworkIdentifier)' == '.NETCoreApp' and $([MSBuild]::VersionGreaterThanOrEquals($(TargetFrameworkVersion), '6.0')) ">
24+
<SdkSupportedTargetPlatformIdentifier Include="android" DisplayName="Android" />
25+
</ItemGroup>
26+
</Project>

src/Xamarin.Android.Build.Tasks/Microsoft.NET.Sdk.Android/WorkloadManifest.targets

Lines changed: 0 additions & 8 deletions
This file was deleted.

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,12 +233,12 @@ public void DotNetNew ([Values ("android", "androidlib", "android-bindinglib")]
233233
new object[] {
234234
"net6.0",
235235
"android",
236-
XABuildConfig.AndroidDefaultTargetDotnetApiLevel,
236+
31,
237237
},
238238
new object[] {
239239
"net6.0",
240-
$"android{XABuildConfig.AndroidDefaultTargetDotnetApiLevel}",
241-
XABuildConfig.AndroidDefaultTargetDotnetApiLevel,
240+
"android31",
241+
31,
242242
},
243243
new object[] {
244244
"net7.0",

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/TestEnvironment.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,26 @@ public static string DotNetAndroidSdkDirectory {
108108
IsWindows ? "Microsoft.Android.Sdk.Windows" :
109109
"Microsoft.Android.Sdk.Linux";
110110

111-
return _dotNetAndroidSdkDirectory = Directory.GetDirectories (Path.Combine (AndroidSdkResolver.GetDotNetPreviewPath (), "packs", sdkName)).LastOrDefault ();
111+
var directories = from d in Directory.GetDirectories (Path.Combine (AndroidSdkResolver.GetDotNetPreviewPath (), "packs", sdkName))
112+
let version = ParseVersion (d)
113+
orderby version descending
114+
select d;
115+
return _dotNetAndroidSdkDirectory = directories.FirstOrDefault ();
112116
}
113117
}
114118

119+
static Version ParseVersion (string path)
120+
{
121+
var folderName = Path.GetFileName (path);
122+
var index = folderName.IndexOf ('-');
123+
if (index != -1) {
124+
folderName = folderName.Substring (0, index);
125+
}
126+
if (Version.TryParse (folderName, out var v))
127+
return v;
128+
return new Version (0, 0);
129+
}
130+
115131
public static string DotNetAndroidSdkToolsDirectory {
116132
get {
117133
return Path.Combine (DotNetAndroidSdkDirectory, "tools");

tests/MSBuildDeviceIntegration/Tests/XASdkDeployTests.cs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,32 +23,43 @@ public class XASdkDeployTests : DeviceTest
2323
/* isRelease */ false,
2424
/* xamarinForms */ false,
2525
/* publishTrimmed */ default (bool?),
26+
/* targetFramework*/ "net7.0-android",
2627
},
2728
new object[] {
2829
/* isRelease */ true,
2930
/* xamarinForms */ false,
3031
/* publishTrimmed */ default (bool?),
32+
/* targetFramework*/ "net7.0-android",
3133
},
3234
new object[] {
3335
/* isRelease */ false,
3436
/* xamarinForms */ true,
3537
/* publishTrimmed */ default (bool?),
38+
/* targetFramework*/ "net7.0-android",
3639
},
3740
new object[] {
3841
/* isRelease */ true,
3942
/* xamarinForms */ true,
4043
/* publishTrimmed */ default (bool?),
44+
/* targetFramework*/ "net7.0-android",
4145
},
4246
new object[] {
4347
/* isRelease */ true,
4448
/* xamarinForms */ false,
4549
/* publishTrimmed */ false,
50+
/* targetFramework*/ "net7.0-android",
51+
},
52+
new object[] {
53+
/* isRelease */ true,
54+
/* xamarinForms */ true,
55+
/* publishTrimmed */ true,
56+
/* targetFramework*/ "net6.0-android",
4657
},
4758
};
4859

4960
[Test]
5061
[TestCaseSource (nameof (DotNetInstallAndRunSource))]
51-
public void DotNetInstallAndRun (bool isRelease, bool xamarinForms, bool? publishTrimmed)
62+
public void DotNetInstallAndRun (bool isRelease, bool xamarinForms, bool? publishTrimmed, string targetFramework)
5263
{
5364
AssertHasDevices ();
5465

@@ -62,6 +73,7 @@ public void DotNetInstallAndRun (bool isRelease, bool xamarinForms, bool? publis
6273
IsRelease = isRelease
6374
};
6475
}
76+
proj.TargetFramework = targetFramework;
6577
if (publishTrimmed != null) {
6678
proj.SetProperty (KnownProperties.PublishTrimmed, publishTrimmed.ToString ());
6779
}
@@ -137,12 +149,13 @@ public void TypeAndMemberRemapping ([Values (false, true)] bool isRelease)
137149

138150
[Test]
139151
[Category ("Debugger"), Category ("Node-4")]
140-
public void DotNetDebug ()
152+
public void DotNetDebug ([Values("net6.0-android", "net7.0-android")] string targetFramework)
141153
{
142154
AssertCommercialBuild ();
143155
AssertHasDevices ();
144156

145157
var proj = new XASdkProject ();
158+
proj.TargetFramework = targetFramework;
146159
proj.SetRuntimeIdentifier (DeviceAbi);
147160
string runtimeId = proj.GetProperty (KnownProperties.RuntimeIdentifier);
148161

0 commit comments

Comments
 (0)