Skip to content

Commit

Permalink
[One .NET] support building net6.0-android apps in .NET 7 (#6988)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jonathanpeppers authored May 25, 2022
1 parent 90aec41 commit fd47b02
Show file tree
Hide file tree
Showing 10 changed files with 85 additions and 29 deletions.
1 change: 1 addition & 0 deletions Configuration.props
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
<DebugType Condition=" '$(DebugType)' == '' ">portable</DebugType>
<Deterministic Condition=" '$(Deterministic)' == '' ">True</Deterministic>
<LangVersion Condition=" '$(LangVersion)' == '' ">latest</LangVersion>
<AndroidNet6Version Condition=" '$(AndroidNet6Version)' == '' ">32.0.301</AndroidNet6Version>
</PropertyGroup>
<PropertyGroup Condition=" '$(HostOS)' == '' ">
<HostOS Condition="$([MSBuild]::IsOSPlatform('windows'))">Windows</HostOS>
Expand Down
2 changes: 2 additions & 0 deletions build-tools/create-packs/Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@
</PropertyGroup>
<ItemGroup>
<_NuGetSources Include="$(OutputPath.TrimEnd('\'))" />
<!-- This allows us to install our Android .NET 6 packs -->
<_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' " />
Expand Down
12 changes: 9 additions & 3 deletions build-tools/create-packs/Microsoft.NET.Sdk.Android.proj
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,24 @@ about the various Microsoft.Android workloads.
DependsOnTargets="_GetDefaultPackageVersion;_GetLicense">
<PropertyGroup>
<WorkloadManifestJsonPath>$(OutputPath)workload-manifest\WorkloadManifest.json</WorkloadManifestJsonPath>
<WorkloadManifestTargetsPath>$(OutputPath)workload-manifest\WorkloadManifest.targets</WorkloadManifestTargetsPath>
</PropertyGroup>

<MakeDir Directories="$([System.IO.Path]::GetDirectoryName ($(WorkloadManifestJsonPath)))" />
<MakeDir Directories="$(OutputPath)workload-manifest" />
<ReplaceFileContents
SourceFile="$(XamarinAndroidSourcePath)src\Xamarin.Android.Build.Tasks\Microsoft.NET.Sdk.Android\WorkloadManifest.in.json"
DestinationFile="$(WorkloadManifestJsonPath)"
Replacements="@WORKLOAD_VERSION@=$(AndroidPackVersionLong)">
Replacements="@WORKLOAD_VERSION@=$(AndroidPackVersionLong);@NET6_VERSION@=$(AndroidNet6Version)">
</ReplaceFileContents>
<ReplaceFileContents
SourceFile="$(XamarinAndroidSourcePath)src\Xamarin.Android.Build.Tasks\Microsoft.NET.Sdk.Android\WorkloadManifest.in.targets"
DestinationFile="$(WorkloadManifestTargetsPath)"
Replacements="@NET6_VERSION@=$(AndroidNet6Version)">
</ReplaceFileContents>

<ItemGroup>
<_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" />
</ItemGroup>
</Target>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,6 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
<_AndroidRuntimePackId Condition=" '$(_AndroidRuntimePackId)' == '' ">$(_AndroidTargetingPackId)</_AndroidRuntimePackId>
</PropertyGroup>
<ItemGroup>
<!-- 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 -->
<KnownFrameworkReference
Include="Microsoft.Android"
TargetFramework="net6.0"
RuntimeFrameworkName="Microsoft.Android"
LatestRuntimeFrameworkVersion="32.0.300-rc.1.4"
TargetingPackName="Microsoft.Android.Ref.$(_AndroidTargetingPackId)"
TargetingPackVersion="32.0.300-rc.1.4"
RuntimePackNamePatterns="Microsoft.Android.Runtime.$(_AndroidRuntimePackId).**RID**"
RuntimePackRuntimeIdentifiers="android-arm;android-arm64;android-x86;android-x64"
Profile="Android"
/>
<KnownFrameworkReference
Include="Microsoft.Android"
TargetFramework="@DOTNET_TARGET_FRAMEWORK@"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"description": ".NET SDK Workload for building Android applications.",
"packs": [
"Microsoft.Android.Sdk",
"Microsoft.Android.Sdk.NET6",
"Microsoft.Android.Ref.32",
"Microsoft.Android.Runtime.32.android-arm",
"Microsoft.Android.Runtime.32.android-arm64",
Expand Down Expand Up @@ -40,6 +41,17 @@
"linux-x64": "Microsoft.Android.Sdk.Linux"
}
},
"Microsoft.Android.Sdk.NET6": {
"kind": "framework",
"version": "@NET6_VERSION@",
"alias-to": {
"osx-x64": "Microsoft.Android.Sdk.Darwin",
"osx-arm64": "Microsoft.Android.Sdk.Darwin",
"win-x86": "Microsoft.Android.Sdk.Windows",
"win-x64": "Microsoft.Android.Sdk.Windows",
"linux-x64": "Microsoft.Android.Sdk.Linux"
}
},
"Microsoft.Android.Ref.32": {
"kind": "framework",
"version": "@WORKLOAD_VERSION@"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Project>
<ImportGroup Condition=" '$(TargetPlatformIdentifier)' == 'android' ">
<Import Project="Sdk.targets" Sdk="Microsoft.Android.Sdk"
Condition=" $([MSBuild]::VersionEquals($(TargetFrameworkVersion), '7.0')) " />
<Import Project="Sdk.targets" Sdk="Microsoft.Android.Sdk.NET6"
Condition=" $([MSBuild]::VersionEquals($(TargetFrameworkVersion), '6.0')) " />
</ImportGroup>

<ItemGroup Condition=" '$(TargetPlatformIdentifier)' == 'android' and $([MSBuild]::VersionEquals($(TargetFrameworkVersion), '6.0')) ">
<KnownFrameworkReference
Update="Microsoft.Android"
LatestRuntimeFrameworkVersion="@NET6_VERSION@"
TargetingPackVersion="@NET6_VERSION@"
/>
<!--
HACK:
The .NET 7 SDK specifies 6.0.3 for .NET 6, but our .NET 6 libmonodroid.so depends on 6.0.5.
We should be able to remove this when we get a newer .NET 7 SDK.
-->
<KnownRuntimePack Update="Microsoft.NETCore.App" LatestRuntimeFrameworkVersion="6.0.5" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFrameworkIdentifier)' == '.NETCoreApp' and $([MSBuild]::VersionGreaterThanOrEquals($(TargetFrameworkVersion), '6.0')) ">
<SdkSupportedTargetPlatformIdentifier Include="android" DisplayName="Android" />
</ItemGroup>
</Project>

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
17 changes: 15 additions & 2 deletions tests/MSBuildDeviceIntegration/Tests/XASdkDeployTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 ();

Expand All @@ -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 ());
}
Expand Down Expand Up @@ -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);

Expand Down

0 comments on commit fd47b02

Please sign in to comment.