Skip to content

Conversation

jonathanpeppers
Copy link
Member

@jonathanpeppers jonathanpeppers commented Jun 4, 2020

To mirror what we have for $(AndroidSupportedAbis) in the existing
Xamarin.Android world, we need a way for .NET 6 apps to support
multiple architectures.

A HelloAndroid.csproj targeting all architectures might look like:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net5.0-android</TargetFramework>
    <RuntimeIdentifiers>android.21-x86;android.21-x64;android.21-arm;android.21-arm64</RuntimeIdentifiers>
    <OutputType>Exe</OutputType>
  </PropertyGroup>
</Project>

To achieve this, we can do a nested <MSBuild/> call:

<ItemGroup>
  <_RIDs Include="$(RuntimeIdentifiers)" />
</ItemGroup>
<MSBuild
    Projects="$(MSBuildProjectFile)"
    Targets="_ComputeFilesToPublishForRuntimeIdentifiers"
    Properties="RuntimeIdentifier=%(_RIDs.Identity)">
  <Output TaskParameter="TargetOutputs" ItemName="ResolvedFileToPublish" />
</MSBuild>

For a new target:

<Target Name="_ComputeFilesToPublishForRuntimeIdentifiers"
    DependsOnTargets="ResolveReferences;ComputeFilesToPublish"
    Returns="@(ResolvedFileToPublish)">
</Target>

This is equivalent to what would happen in the legacy
_ResolveAssemblies target.

The linker then outputs files to:

obj\Release\net5.0\$(RuntimeIdentifier)\linked\

One problem with this, is you receive multiple BCL assemblies for each
architecture:

obj\Release\net5.0\android.21-arm\linked\System.Linq.dll
obj\Release\net5.0\android.21-arm64\linked\System.Linq.dll
obj\Release\net5.0\android.21-x86\linked\System.Linq.dll
obj\Release\net5.0\android.21-x64\linked\System.Linq.dll

I created a <ProcessAssemblies/> MSBuild task that checks the mvid
of each assembly to detect duplicates. Non-duplicates will be placed
in a sub-directory within the .apk file, such as:

  • assemblies\HelloAndroid.dll
  • assemblies\System.Linq.dll
  • assemblies\arm64-v8a\System.Private.CoreLib.dll
  • assemblies\armeabi-v7a\System.Private.CoreLib.dll
  • assemblies\x86\System.Private.CoreLib.dll
  • assemblies\x86_64\System.Private.CoreLib.dll

I had to make a few build & runtime changes for these ABI
sub-directories to work.

Other notes:

  • We must pass @(IntermediateAssembly) from the outer build to the
    nested <MSBuild/> calls. Otherwise, the inner calls look for
    obj\Release\net5.0\android.21-arm\HelloAndroid.dll instead of
    obj\Release\net5.0\HelloAndroid.dll that actually exists.
  • _ComputeFilesToPublishForRuntimeIdentifiers was running some extra
    targets causing <ResolveSdks/> to run 5 times for 4 ABIs. I had to
    make changes in BuildOrder.targets to fix this.
  • <ResolveAssemblies/> now sets %(IntermediateLinkerOutput) on
    each assembly item. This allows .NET 6 builds to support multiple
    linker directories.

@jonathanpeppers jonathanpeppers force-pushed the dotnet-multiple-rids branch 7 times, most recently from 7521cc0 to 0d9b251 Compare June 8, 2020 20:49
@jonathanpeppers jonathanpeppers force-pushed the dotnet-multiple-rids branch 3 times, most recently from a915c1a to 635e99f Compare June 11, 2020 14:53
@jonathanpeppers jonathanpeppers marked this pull request as ready for review June 11, 2020 14:54
@dellis1972
Copy link
Contributor

@jonathanpeppers its green!

<SelfContained Condition=" '$(SelfContained)' == '' And '$(AndroidApplication)' == 'true' ">true</SelfContained>
<RuntimeIdentifier Condition=" '$(RuntimeIdentifier)' == '' And '$(AndroidApplication)' == 'true' ">android.21-x86</RuntimeIdentifier>
<!-- Prefer $(RuntimeIdentifiers) plural -->
<RuntimeIdentifiers Condition=" '$(RuntimeIdentifier)' == '' And '$(RuntimeIdentifiers)' == '' And '$(AndroidApplication)' == 'true' ">android.21-arm;android.21-arm64</RuntimeIdentifiers>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default $(RuntimeIdentifiers) are arm32 & arm64? Why? Especially considering that the previous default was x86, and the "normal" Xamarin.Android default -- via xamarin/monodroid, for commercial users -- is "all the ABIs!" arm64-v8a;armeabi-v7a;x86;x86_64.

Given that (1) we don't know what the "commercial integration" looks like, and (2) it would be Really Really Handy to be able to run the .apks on x86(_64?) emulators, this default seems "weird" to me.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This now defaults to android.21-arm64;android.21-x86.

@jonathanpeppers jonathanpeppers force-pushed the dotnet-multiple-rids branch 2 times, most recently from 2d69d65 to 0decf45 Compare June 16, 2020 21:35
public override bool RunTask ()
{
if (!string.IsNullOrEmpty (RuntimeIdentifier)) {
if (RuntimeIdentifiers != null && RuntimeIdentifiers.Length > 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implicit to this is that $(RuntimeIdentifiers) takes precedence over $(AndroidSupportedAbis). Is this desirable/intentional? What will be the fallout to this decision?

For example, if I have an existing App.csproj which overrides $(AndroidSupportedAbis), and then I migrate to .NET 5/6 and don't touch $(RuntimeIdentifiers)… What Happens?

Given that this PR always provides a default $(RuntimeIdentifiers) value, what I think will happen is that we'll effectively ignore $(AndroidSupportedAbis), replacing the "original overridden value" with the defaults that we provide.

Is this useful or desirable? (It might be! I don't know! I am wondering if there's been any consideration here, particularly as the commit message doesn't mention this semantic.)

Meanwhile, is the opposite possible? If $(AndroidSupportedAbis) is overridden, and $(RuntimeIdentifiers) isn't -- though making this determination may itself be difficult! -- would it be at all possible to populate $(RuntimeIdentifiers) based on the value of $(AndroidSupportedAbis)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eventually, I think that RuntimeIdentifier and/or RuntimeIdentifiers will be implicitly set via:

<TargetFrameworkVersion>net5.0-android</TargetFrameworkVersion>

And so, it seems like $(AndroidSupportedAbis) would always be ignored going forward? Is it worth trying to support both?

Either way, I should document something about $(RuntimeIdentifiers) being preferred in DotNet5.md.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If $(AndroidSupportedAbis) will be ignored going forward, then we should consider adding a warning if the property is set at all, as having something set which doesn't do anything will be a source of confusion.

This may complicate migrating from "long-form" to "short-form" projects, i.e. it'll be a property that needs to be removed as part of the migration, but this may be for the best.

Alternatively, we would need to ensure that if $(AndroidSupportedAbis) is set, the values within it contribute to $(RuntimeIdentifiers), so that when it's set, the specified ABIs will still appear in the resulting .apk/.aab.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since $(RuntimeIdentifiers) is needed for Restore to even work... I think we really should require it over $(AndroidSupportedAbis).

I added XA0036 if $(AndroidSupportedAbis) is present, @brendanzagaeski you might review what I added for this, thanks.

Comment on lines 25 to 31
See the Microsoft documentation on [runtime identifiers][rids] for more
information.

[rids]: https://docs.microsoft.com/dotnet/core/rid-catalog
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably it was already in mind when adding this info, but this link might need to be updated if the eventual page that talks about Android RIDs ends up being elsewhere. I think what's written here is good for now. I'm mostly just thinking out loud about the future.

Copy link
Contributor

@brendanzagaeski brendanzagaeski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved on the new warning and error messages, with one last optional idea for a small change in the XA0036 message.

The generated Resources.Designer.cs and .xlf files are all set too.

To mirror what we have for `$(AndroidSupportedAbis)` in the existing
Xamarin.Android world, we need a way for .NET 6 apps to support
multiple architectures.

A `HelloAndroid.csproj` targeting all architectures might look like:

    <Project Sdk="Microsoft.NET.Sdk">
      <PropertyGroup>
        <TargetFramework>net5.0-android</TargetFramework>
        <RuntimeIdentifiers>android.21-x86;android.21-x64;android.21-arm;android.21-arm64</RuntimeIdentifiers>
        <OutputType>Exe</OutputType>
      </PropertyGroup>
    </Project>

To achieve this, we can do a nested `<MSBuild/>` call:

    <ItemGroup>
      <_RIDs Include="$(RuntimeIdentifiers)" />
    </ItemGroup>
    <MSBuild
        Projects="$(MSBuildProjectFile)"
        Targets="_ComputeFilesToPublishForRuntimeIdentifiers"
        Properties="RuntimeIdentifier=%(_RIDs.Identity)">
      <Output TaskParameter="TargetOutputs" ItemName="ResolvedFileToPublish" />
    </MSBuild>

For a new target:

    <Target Name="_ComputeFilesToPublishForRuntimeIdentifiers"
        DependsOnTargets="ResolveReferences;ComputeFilesToPublish"
        Returns="@(ResolvedFileToPublish)">
    </Target>

This is equivalent to what would happen in the legacy
`_ResolveAssemblies` target.

The linker then outputs files to:

    obj\Release\net5.0\$(RuntimeIdentifier)\linked\

One problem with this, is you receive multiple BCL assemblies for each
architecture:

    obj\Release\net5.0\android.21-arm\linked\System.Linq.dll
    obj\Release\net5.0\android.21-arm64\linked\System.Linq.dll
    obj\Release\net5.0\android.21-x86\linked\System.Linq.dll
    obj\Release\net5.0\android.21-x64\linked\System.Linq.dll

I created a `<ProcessAssemblies/>` MSBuild task that checks the mvid
of each assembly to detect duplicates. Non-duplicates will be placed
in a sub-directory within the `.apk` file, such as:

* `assemblies\HelloAndroid.dll`
* `assemblies\System.Linq.dll`
* `assemblies\arm64-v8a\System.Private.CoreLib.dll`
* `assemblies\armeabi-v7a\System.Private.CoreLib.dll`
* `assemblies\x86\System.Private.CoreLib.dll`
* `assemblies\x86_64\System.Private.CoreLib.dll`

I had to make a few build & runtime changes for these ABI
sub-directories to work.

Other notes:

* We must pass `@(IntermediateAssembly)` from the outer build to the
  nested `<MSBuild/>` calls. Otherwise, the inner calls look for
  `obj\Release\net5.0\android.21-arm\HelloAndroid.dll` instead of
  `obj\Release\net5.0\HelloAndroid.dll` that actually exists.
* `_ComputeFilesToPublishForRuntimeIdentifiers` was running some extra
  targets causing `<ResolveSdks/>` to run 5 times for 4 ABIs. I had to
  make changes in `BuildOrder.targets` to fix this.
* `<ResolveAssemblies/>` now sets `%(IntermediateLinkerOutput)` on
  each assembly item. This allows .NET 6 builds to support multiple
  linker directories.
Example message should say 'warning'.
@jonpryor
Copy link
Contributor

Squash-and-merge commit message:

For .NET 5, obsolete the `$(AndroidSupportedAbis)` MSBuild property
and replace with the `$(RuntimeIdentifiers)` MSBuild property.

The `$(AndroidSupportedAbis)` MSBuild property has long been used to
control which Android ABIs are included in a `.apk` or `.aab` file,
unless `$(AndroidCreatePackagePerAbi)`=True.

The .NET Core analogue is the [`$(RuntimeIdentifiers)` property][0],
which is a `;`-separated list of "RID" values from the
[.NET Core RID catalog][1].

The mapping between `$(AndroidSupportedAbis)` and
`$(RuntimeIdentifiers)` values is as follows:

  * armeabi-v7a: android.21-arm
  *   arm64-v8a: android.21-arm64
  *         x86: android.21-x86
  *      x86_64: android.21-x64

Within a .NET 5+/`dotnet build` context, the `$(AndroidSupportedAbis)`
property is *no longer supported*; `$(AndroidSupportedAbis)` will not
be supported in .NET 5 because `$(RuntimeIdentifiers)` may be
implicitly set based on the `$(TargetFrameworks)` property; it may not
be possible to override `$(RuntimeIdentifiers)` "early enough" to take
`$(AndroidSupportedAbis)` into consideration.

If the `$(AndroidSupportedAbis)` property present, an XA0036 warning
will be generated:

	warning XA0036: The 'AndroidSupportedAbis' MSBuild property is no longer supported.
	Edit the project file in a text editor, remove any uses of 'AndroidSupportedAbis',
	and use the 'RuntimeIdentifiers' MSBuild property instead.\

A `HelloAndroid.csproj` targeting all architectures might look like:

	<Project Sdk="Microsoft.NET.Sdk">
	  <PropertyGroup>
	    <TargetFramework>net5.0-android</TargetFramework>
	    <RuntimeIdentifiers>android.21-x86;android.21-x64;android.21-arm;android.21-arm64</RuntimeIdentifiers>
	    <OutputType>Exe</OutputType>
	  </PropertyGroup>
	</Project>

To achieve this, we can do a nested `<MSBuild/>` call:

	<ItemGroup>
	  <_RIDs Include="$(RuntimeIdentifiers)" />
	</ItemGroup>
	<MSBuild
	    Projects="$(MSBuildProjectFile)"
	    Targets="_ComputeFilesToPublishForRuntimeIdentifiers"
	    Properties="RuntimeIdentifier=%(_RIDs.Identity)">
	  <Output TaskParameter="TargetOutputs" ItemName="ResolvedFileToPublish" />
	</MSBuild>

For a new target:

	<Target Name="_ComputeFilesToPublishForRuntimeIdentifiers"
	    DependsOnTargets="ResolveReferences;ComputeFilesToPublish"
	    Returns="@(ResolvedFileToPublish)">
	</Target>

This is equivalent to what would happen in the legacy
`_ResolveAssemblies` target.

The linker then outputs files to:

	obj\Release\net5.0\$(RuntimeIdentifier)\linked\

One problem with this is that you receive multiple BCL assemblies for
each architecture:

	obj\Release\net5.0\android.21-arm\linked\System.Linq.dll
	obj\Release\net5.0\android.21-arm64\linked\System.Linq.dll
	obj\Release\net5.0\android.21-x86\linked\System.Linq.dll
	obj\Release\net5.0\android.21-x64\linked\System.Linq.dll

I created a `<ProcessAssemblies/>` MSBuild task that checks the MVID
of each assembly to detect duplicates.  Non-duplicates will be placed
in a sub-directory within the `.apk` file, such as:

  * `assemblies\HelloAndroid.dll`
  * `assemblies\System.Linq.dll`
  * `assemblies\arm64-v8a\System.Private.CoreLib.dll`
  * `assemblies\armeabi-v7a\System.Private.CoreLib.dll`
  * `assemblies\x86\System.Private.CoreLib.dll`
  * `assemblies\x86_64\System.Private.CoreLib.dll`

I had to make a few build & runtime changes for these ABI
sub-directories to work.

Other notes:

  * We must pass `@(IntermediateAssembly)` from the outer build to the
    nested `<MSBuild/>` calls.  Otherwise, the inner calls look for
    `obj\Release\net5.0\android.21-arm\HelloAndroid.dll` instead of
    `obj\Release\net5.0\HelloAndroid.dll` that actually exists.

  * `_ComputeFilesToPublishForRuntimeIdentifiers` was running some
    extra targets causing `<ResolveSdks/>` to run 5 times for 4 ABIs.
    I had to make changes in `BuildOrder.targets` to fix this.

  * `<ResolveAssemblies/>` now sets `%(IntermediateLinkerOutput)` on
    each assembly item.  This allows .NET 6 builds to support multiple
    linker directories.

[0]: https://docs.microsoft.com/en-us/dotnet/core/tools/csproj#runtimeidentifiers
[1]: https://docs.microsoft.com/en-us/dotnet/core/rid-catalog

@jonpryor jonpryor merged commit 51fb93e into dotnet:master Jun 22, 2020
@jonathanpeppers jonathanpeppers deleted the dotnet-multiple-rids branch June 22, 2020 17:12
@github-actions github-actions bot locked and limited conversation to collaborators Jan 26, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants