Skip to content

[question] supporting multiple $(RuntimeIdentifiers) in Xamarin #11975

@jonathanpeppers

Description

@jonathanpeppers

This might also need changes in dotnet/runtime, but I'll file here since it is mostly build related.

In dotnet/android#4767, I have an implementation that would enable:

<RuntimeIdentifiers>android.21-arm;android.21-arm64;android.21-x86;android.21-x64</RuntimeIdentifiers>

And you could build the project via:

> dotnet build HelloAndroid.csproj -p:Configuration=Release

This produces an Android HelloAndroid.apk file with contents such as:

assemblies\HelloAndroid.dll
assemblies\armeabi-v7a\System.Private.CoreLib.dll
assemblies\arm64-v8a\System.Private.CoreLib.dll
assemblies\x86\System.Private.CoreLib.dll
assemblies\x86_64\System.Private.CoreLib.dll
lib\armeabi-v7a\libmonosgen-2.0.so
lib\arm64-v8a\libmonosgen-2.0.so
lib\x86\libmonosgen-2.0.so
lib\x86_64\libmonosgen-2.0.so

You can install and run this .apk file on Android devices/emulators of any of the 4 architectures.

What I actually had to do to make this work, brings up several questions.

Is the mechanism I'm using to process multiple $(RuntimeIdentifier) the right idea?

At a high level, after Build we do something like:

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

Against a new target we define:

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

So in the example above with 4 architectures:

  1. Build occurs. Microsoft.Android.Sdk defaults $(AppendRuntimeIdentifierToOutputPath) to false. We only need a single HelloAndroid.dll and HelloAndroid.apk file.
  2. The <MSBuild/> task call runs for each RID.
  3. ILLink will run 4 times (once per RID). We get linker output into 4 distinct directories such as $(IntermediateOutputPath)$(RuntimeIdentifier)\linked\.
  4. --deterministic is passed to ILLink, so almost all of the BCL assemblies are duplicate across architectures.
  5. We have an MSBuild task that uses System.Reflection.Metadata to read the mvid of all assemblies. Any duplicates are removed, architecture-specific assemblies go in a sub-directory as described above.

Is this the right idea, in general? Is there a change in dotnet/sdk that could simplify this?

If we should deduplicate .NET assemblies, should the assemblies be identical across all runtime packs?

In a Debug build (or ILLink disabled), the deduplication does not work. The BCL assemblies in the runtime pack for each architecture are different. This results in a ~130MB .apk file.

But if I compare them:

$ monodis packages/microsoft.netcore.app.runtime.android-arm/5.0.0-preview.6.20264.1/runtimes/android-arm/lib/net5.0/System.IO.dll --module
Module Table (1..1)
1: System.IO.dll 1 {1D745C86-CC08-41A8-ABA5-F221F7BAD20D}
$ monodis packages/microsoft.netcore.app.runtime.android-arm64/5.0.0-preview.6.20264.1/runtimes/android-arm64/lib/net5.0/System.IO.dll --module
Module Table (1..1)
1: System.IO.dll 1 {B582D0AC-1AE7-4E41-8DF0-6938725B4ADB}

It looks like only the mvid differs, and none of the IL differs. Is this intentional or are these packages built with /p:Deterministic=false?

If a fix is needed for the runtime packs, I think this would be in dotnet/runtime.

/cc @dsplaisted @marek-safar @steveisok

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions