Skip to content

Supporting .NET with x64 emulation on Arm64 #17463

Closed
@richlander

Description

@richlander

Supporting .NET with x64 emulation on Arm64

x64 emulation has emerged as an important new scenario for Arm64 operating systems from Apple and Microsoft. This document defines the approach for supporting .NET with x64 emulation.

The overall intent of this proposal to create workflows that enable the intuitive use of the Arm64 SDK with x64 runtimes. This experience will limit the need for the x64 SDK to be used (and installed). For example, someone should be able to git clone a repository that builds and tests a .NET Core 3.1 app on an Arm64 machine, using the Arm64 dotnet that is on the path, and relying on natural use of the (installed) .NET Core 3.1 x64 runtime. The repo owner wouldn't need to do anything special to enable their build and test scripts to work correctly on Arm64 machines.

An earlier issue discussed enabling dotnet [Arm64 | x64] coexistence, in terms of a broad set of options. The initial feedback converged on providing Arm64 as a native architecture offering, just like x64 is offered on x64 machines, and as if x64 emulation on Arm64 did not exist. Conversely, the feedback suggested that x64 emulation should be provided as a secondary architecture offering, as a new scenario with a new set of gestures and experiences. This document expands on that approach.

For the purposes of this document, we will assume that the Apple and Microsoft approaches to x64 emulation are the same.

Terms

  • Native architecture: the instruction set that the operating system targets and supports.
    • For example, the native architecture of Windows x64 running on an x64 CPU is x64.
    • For example, the native architecture on Windows x86 running on either an x86 or x64 CPU is x86 (or IA32).
    • For example, the native architecture of macOS Arm64 running on an Apple Silicon CPU is Arm64.
  • Emulation: an alternate instruction set that is supported for some purpose, such as application compatibility.
    • For example, Windows x64 supports running x86 (or 32-bit) via its Wow64 subsystem.
    • For example, macOS Arm64 supports running x64 via its Rosetta 2 subsystem.

Principles

The approach is based on the following principles:

  • .NET will have a uniform model for supporting native architectures, with little or no difference across environments, such as macOS Arm64 and Windows x64.
  • .NET will have a uniform model for supporting x64 emulation on Arm64, with little or no difference across environments, such as macOS Arm64 and Windows Arm64.
  • It is OK for a given architecture to not be uniform across its native architecture and emulated presentations, for example .NET x64 on Windows x64 vs Windows Arm64.

These principles lead to the following propositions:

  • The native architecture offering is the primary use case and design target.
  • The emulated offering is a secondary use case and may require special gestures.

Those principles and propositions offer us both prioritization and degrees of freedom.

Overall approach

There are two primary challenges to solve to enable .NET code to run in x64 emulation:

  • x64 instruction emulation.
  • Enabling .NET Arm64 and x64 to coexist.

We see the first challenge as being the role of the operating system teams, from Apple (macOS) and Microsoft (Windows). A .NET x64 emulation issue could just as easily affect Java or Python, for example. Also, we don't want to regularly service multiple .NET versions to support x64 emulation on multiple operating systems. For example, Apple have been responsive to the bugs we have filed, and fixed multiple issues in public builds. It may take longer for x64 emulation issues to be resolved than native architecture issues as an inherent outcome of this approach.

.NET x64 builds are now working well in Rosetta 2 on macOS. The .NET team hasn't yet tested x64 emulation (at least at depth) on Windows Arm64. We have every reason to believe that these emulation systems are high quality and will work well for .NET x64 apps as a durable feature.

Note: We are not committed to resolving issues that the operating system vendor does not address in their emulation subsystem. This includes functional, performance, and security issues. You can call Microsoft support for .NET issues with x64 emulation, but that may result in an issue being filed with the operating system vendor.

For development, we recommend using the native architecture build of .NET and using emulation primarily for testing. You can rely on emulation for daily work, but it will be be a substandard experience (described later).

There are two primary reasons why you might be required to use emulation:

  • You rely on a .NET version that isn't built for Arm64 (for your OS).
  • You rely on native dependencies that are not built for Arm64 (for your OS).

Arm64 and x64 builds can coexist, however, only the Arm64 build will be automatically added to the PATH. That means that typing dotnet will result in using an Arm64 build of .NET, by default. It also means you will get an error if the .NET version you need isn't available for Arm64, but only via x64 emulation (even if it is installed). Going forward, we will apply the same policy to all environments, to only set the PATH for native architecture builds. For example, we intend to apply this policy to Windows WoW64 support (Windows x86 on Windows x64).

We consider emulated environments as secondary experiences and will require users to have more skill to configure and use them efficiently and correctly. PATH management can be confusing and frustrating, and we consider it an advanced experience. By only setting the PATH for one architecture, we can document the expected experience of using .NET without needing to clarify the order of installing Arm64 and x64 versions.

.NET x64 will be usable (via emulation) without any additional configuration for scenarios that do not rely on the PATH. These are documented later.

Both Arm64 and x64 installs must be serviceable. For example, if a user maintains .NET 6 installations for both Arm64 and x64, they must be able to install new Arm64 and x64 .NET builds in any order to safely update their machine. Also, Windows users should be able to rely on Microsoft Update to install both Arm64 and x64 .NET updates, as appropriate, for a given machine.

Native architecture

We have adopted the practice of installing .NET to the expected and idiomatic location on each operating system in the dotnet directory. That's what we've done for (native architecture) x64, and we'll follow that model for Arm64.

  • macOS: /usr/local/share/dotnet
  • Windows C:\Program Files\dotnet

The .NET installer for each operating system will update the machine-wide path to include the dotnet directory. This action enables developers to type dotnet and use the .NET platform.

On Windows, a registry key is also set by the installer to describe that .NET is installed. It will be set on Arm64 in an analogous way as x64. The format isn't discussed in this document.

x64 emulation

x64 emulation is a new scenario that requires a new model, including new experiences and gestures. Unlike the Windows Wow64 model, where the operating system establishes file and registry virtualization that provides effective guidance for platforms like .NET, x64 emulation has no similar model. A platform like .NET is on its own to choose a good model for Arm64 and x64 coexistence. It is quite likely there will be significant diversity between dev platforms on this topic, either via inherent need or the result of different and uncoordinated choices.

Install location

The x64 build will be installed within the dotnet directory in an x64 folder. Within that folder, the folder and file layout will be identical to what one would find in a native architecture installation. For example, the x64 dotnet executable will be installed to /usr/local/share/dotnet/x64/dotnet on macOS. The same model will be applied to Windows.

The major advantage of this approach is that the .NET product requires very limited updates to support this system since the structure within the x64 directory is exactly as expected, because it is identical to a native architecture installation.

Any .NET version can be installed within the x64 structure. For example, you can have .NET 6 installed concurrently for Arm64 and x64. They will be concurrently usable according to experiences that will be defined shortly.

On Windows, the x64 installer will need to write a value that does not overwrite Arm64 values. The format isn't discussed in this document.

macOS Arm64 has already been released and people are already installing .NET builds on it. Inherent in this proposal is a major breaking change. If users have installed any x64 builds on macOS Arm64, they will be required to delete the dotnet directory on their machine and re-install .NET from scratch to ensure x64 builds are installed correctly (per this proposal).

Windows Arm64 will have a similar problem if we don't implement this proposal in time. We need to talk to the Windows team about their schedule. If Microsoft releases a Windows build with x64 emulation before .NET 6, we will need to update .NET 5 to have the correct behavior to align with that release date so that Windows users don't have to deal with the same problems as macOS users (remove existing installs).

Another related but separate breaking change is that we will need to change x64 installers to have different behaviors on Arm64 (different install location; not setting the path; different reg keys on Windows). That means that old x64 installers will not be supported on Arm64. This includes both old builds for a supported release like .NET Core 3.1, and all builds for a no longer supported release like .NET Core 3.0. That's not likely to affect many people, but will need to be documented.

Applications

There are two different aspects of apps that determine the way in which they are launched.

  • The host that is used to launch the app.
  • The runtime ID that was used to build the app.

There are three host options:

  1. The app is self-contained and launched with its executable apphost, such as myapp.exe or ./myapp.
  2. The app is framework-dependent and launched with its executable apphost, such as myapp.exe or ./myapp.
  3. The app is framework-dependent and launched with the dotnet host, such as dotnet myapp.dll.

Note: The second case is the default experience on Windows, while the third one is the default experience on macOS (due to notarization requirements).

In the first case, the apphost is able to launch the runtime and app in the same directory, and the application is started. As long as the app was published for osx-arm64 or osx-x64, it will work. This case does not rely on the PATH.

In the second case, the apphost will attempt to find the required .NET architecture and version installed to known locations such as C:\Program Files\dotnet (native architecture) or C:\Program Files\dotnet\x64 (x64 emulation). The apphost for the app will be chosen and generated at build-time based on the implicit runtime ID of the SDK or explicitly defined via a CLI or msbuild property. For example dotnet build will produce an apphost that matches the operating system and architecture of the SDK, whereas dotnet publish -r osx-arm64 --self-contained false will produce a framework-dependent app with an app host for macOS Arm64. In the case that the apphost cannot find a compatible .NET version in the known location for the given architecture, it will produce an error. A user can set DOTNET_ROOT to an alternative location. This case does not rely on the PATH.

We will need to create a DOTNET_ROOT_X64 or DOTNET_ROOT(x64) ENV, to enable DOTNET_ROOT to apply to the native architecture. x64 apps that are already published will have no concept of a DOTNET_ROOT_X64, so will not work correctly.

We have two choices on how to handle that:

  • Require developers to rebuild apps to behave correct in the x64 emulated environment on Arm64. Rebuilding would result in using a newer apphost that understands to look for the DOTNET_ROOT_X64 ENV.
  • Consider DOTNET_ROOT as x64-specific, and instead create a DOTNET_ROOT_Arm64 ENV for Arm64 apps.

The first option isn't great, but is workable. The second option would be a very unfortunate long-term choice. This choice is a microcosm of the installation directory discussion that is at the heart of this document. We rejected dotnet-x64 and dotnet-arm64 directory names for the .NET product installation. We should also reject needing to specify an architecture for DOTNET_ROOT for the native architecture.

Note: I'm not a fan of DOTNET_ROOT(x64). It's hard to type (requires me to hold shift and look at my keyboard), looks inspired by Program Files(x86) from Windows, and generally looks funny. Please, let's adopt DOTNET_ROOT_X64.

In the third and last case, the dotnet host is used to directly launch an app via its managed entry point. In this case, the architecture has been chosen by virtue of the given dotnet used, either for the native or emulated architecture. The dotnet host (and related infrastructure) will attempt to find a compatible .NET version based on that architecture. In the case that the dotnet host cannot find be a compatible .NET version, it will produce an error. This case relies on the PATH, unless an absolute path to dotnet is used, such as C:\Program Files\dotnet\x64\dotnet or D:\MyPrivateDotnet\dotnet.

In the third case, an architecture is specified in deps.json, but it is not consulted/honored. That makes the experience for the second and third cases asymmetric. That's not attractive. Also, the app may reference native dependencies that are incompatible with the architecture of the given dotnet. One challenge is that dotnet build produces portable assets by default. As a result, looking to deps.json for an architecture isn't correct.

We can improve on the first case by writing the architecture to runtimeconfig.json. That's the correct file to describe fundamental scalar values about the app. This value would enable dotnet to either produce errors if it was an incompatible architecture for the app, or to proxy to a dotnet of another architecture. We should only write this value for apps that target a specific (and single) architecture, not for portable apps. As is mentioned multiple times in this document, there is no good gesture for that today. In fact, we should reconsider portable apps as a default experience.

SDK

The SDK offers many commands and scenarios and includes multiple components that are launched in varying ways. It supports cross-version and cross-architecture targeting for some scenarios and for others requires using a matching SDK and/or runtime for the version and architecture you are targeting. It is expected that this section of the document is the least complete as a result of the more complicated experiences.

In general, we expect the following:

  • dotnet build and dotnet publish support cross-targeting, across .NET version and CPU architecture. It doesn't matter which SDK you use to build or publish a given app provided that the SDK is the same or higher .NET version.
  • dotnet build and dotnet publish target the architecture of the SDK, by default. This is most relevant for producing the apphost for the app.
    • For example, using the Arm64 SDK, dotnet build will produce an executable that is Arm64 specific and that makes the app Arm64-specific (for practical purposes). Given another gesture (which in practice doesn't exist), you could target x64, making it easier to use the Arm64 SDK for development but test with the emulated x64 SDK.
    • It is inconvenient to use dotnet build to produce framework-dependent apps that target an explicit architecture/RID. See .NET CLI Compatibility for more context.
    • This was demonstrated earlier by virtue of using dotnet publish instead of dotnet build for this purpose.
    • We may need to improve this experience to make x64 emulation more easy to target. This problem may be less relevant for macOS (due to notarization requirements).
  • dotnet run and dotnet test launch an application, and require a compatible .NET runtime version for the architecture.
    • In absence of a compatible version being present, the given host will present an error.
    • We could consider enabling discovery and use of emulated versions of .NET when native architecture versions are not available, and vice versa.
    • This scenario would happen naturally if it was straightforward to target an alternate architecture/RID, with dotnet run and dotnet test, particularly on Windows where the apphost is used by default.
    • The dotnet host could be updated to do something similar, to make it honor the architecture present in the deps.json file. The Arm64 dotnet could proxy to the x64 dotnet, and vice versa. The dotnet host is relevant on macOS, since it is used exclusively for dotnet run and dotnet test scenarios, due to notarization requirements.
    • We could simply extend dotnet test, for example, to accept a runtime ID, such as dotnet test -r osx-x64. Ideally, we could infer the operating system, and specify only the architecture, such as dotnet test -a x64.

User Experience

We haven't built this experience, but we can simulate much of it, both to more concretely present what is being proposed and to evaluate if the UX is tenable.

I performed the following steps to create my simulation environments:

  • I installed .NET Core 3.1, .NET 5 and .NET 6, all as x64 version.
  • I moved them into an x64 directory within the global dotnet directory.
  • I installed .NET 6 Arm64.
  • I validated that both x64 and Arm64 versions were functional at that point.

I ran the following tests to demonstrate and evaluate the experience.

Validate Arm64 install

Validate that the Arm64 install looks good. It does.

rich@MacBook-Air ~ % dotnet --info
.NET SDK (reflecting any global.json):
 Version:   6.0.100-preview.3.21202.5
 Commit:    aee38a6dd4

Runtime Environment:
 OS Name:     Mac OS X
 OS Version:  11.3
 OS Platform: Darwin
 RID:         osx.11.0-arm64
 Base Path:   /usr/local/share/dotnet/sdk/6.0.100-preview.3.21202.5/

Host (useful for support):
  Version: 6.0.0-preview.3.21201.4
  Commit:  236cb21e3c

.NET SDKs installed:
  6.0.100-preview.3.21202.5 [/usr/local/share/dotnet/sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 6.0.0-preview.3.21201.13 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 6.0.0-preview.3.21201.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]

To install additional .NET runtimes or SDKs:
  https://aka.ms/dotnet-download
rich@MacBook-Air ~ % ls /usr/local/share/dotnet/          
LICENSE.txt		host			shared
ThirdPartyNotices.txt	packs			templates
dotnet			sdk			x64
rich@MacBook-Air ~ % file /usr/local/share/dotnet/dotnet  
/usr/local/share/dotnet/dotnet: Mach-O 64-bit executable arm64

Validate x64 install

Validate that the x64 install looks good. It does.

rich@MacBook-Air dotnetapp % /usr/local/share/dotnet/x64/dotnet --info
.NET SDK (reflecting any global.json):
 Version:   6.0.100-preview.3.21202.5
 Commit:    aee38a6dd4

Runtime Environment:
 OS Name:     Mac OS X
 OS Version:  11.3
 OS Platform: Darwin
 RID:         osx.11.0-x64
 Base Path:   /usr/local/share/dotnet/x64/sdk/6.0.100-preview.3.21202.5/

Host (useful for support):
  Version: 6.0.0-preview.3.21201.4
  Commit:  236cb21e3c

.NET SDKs installed:
  3.1.408 [/usr/local/share/dotnet/x64/sdk]
  5.0.202 [/usr/local/share/dotnet/x64/sdk]
  6.0.100-preview.3.21202.5 [/usr/local/share/dotnet/x64/sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 3.1.14 [/usr/local/share/dotnet/x64/shared/Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.5 [/usr/local/share/dotnet/x64/shared/Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 6.0.0-preview.3.21201.13 [/usr/local/share/dotnet/x64/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 3.1.14 [/usr/local/share/dotnet/x64/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.5 [/usr/local/share/dotnet/x64/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.0-preview.3.21201.4 [/usr/local/share/dotnet/x64/shared/Microsoft.NETCore.App]

To install additional .NET runtimes or SDKs:
  https://aka.ms/dotnet-download
rich@MacBook-Air ~ % ls /usr/local/share/dotnet/x64       
LICENSE.txt		host			shared
ThirdPartyNotices.txt	packs			templates
dotnet			sdk
rich@MacBook-Air ~ % file /usr/local/share/dotnet/x64/dotnet
/usr/local/share/dotnet/x64/dotnet: Mach-O 64-bit executable x86_64

Build and run .NET 5 app with Arm64

The premise of this test is that there is no Arm64 build of .NET 5 for macOS. The app builds and does not run, as expected.

rich@MacBook-Air dotnetapp % dotnet --version
6.0.100-preview.3.21202.5
rich@MacBook-Air dotnetapp % pwd
/Users/rich/git/dotnet-docker/samples/dotnetapp
rich@MacBook-Air dotnetapp % cat dotnetapp.csproj | grep Target
    <TargetFramework>net5.0</TargetFramework>
rich@MacBook-Air dotnetapp % dotnet build
Microsoft (R) Build Engine version 16.10.0-preview-21181-07+073022eb4 for .NET
Copyright (C) Microsoft Corporation. All rights reserved.

  Determining projects to restore...
  Restored /Users/rich/git/dotnet-docker/samples/dotnetapp/dotnetapp.csproj (in 150 ms).
  You are using a preview version of .NET. See: https://aka.ms/dotnet-core-preview
  dotnetapp -> /Users/rich/git/dotnet-docker/samples/dotnetapp/bin/Debug/net5.0/dotnetapp.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:02.03
rich@MacBook-Air dotnetapp % dotnet run
It was not possible to find any compatible framework version
The framework 'Microsoft.NETCore.App', version '5.0.0' was not found.
  - The following frameworks were found:
      6.0.0-preview.3.21201.4 at [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]

You can resolve the problem by installing the specified framework and/or SDK.

The specified framework can be found at:
  - https://aka.ms/dotnet-core-applaunch?framework=Microsoft.NETCore.App&framework_version=5.0.0&arch=arm64&rid=osx.11.0-arm64
rich@MacBook-Air dotnetapp % dotnet ./bin/Debug/net5.0/dotnetapp.dll
It was not possible to find any compatible framework version
The framework 'Microsoft.NETCore.App', version '5.0.0' was not found.
  - The following frameworks were found:
      6.0.0-preview.3.21201.4 at [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]

You can resolve the problem by installing the specified framework and/or SDK.

The specified framework can be found at:
  - https://aka.ms/dotnet-core-applaunch?framework=Microsoft.NETCore.App&framework_version=5.0.0&arch=arm64&rid=osx.11.0-arm64
rich@MacBook-Air dotnetapp

We can use roll-forward tricks to coerce the app to run, and it does, as expected.

rich@MacBook-Air dotnetapp % export DOTNET_ROLL_FORWARD=LatestMajor && export DOTNET_ROLL_FORWARD_TO_PRERELEASE=1
rich@MacBook-Air dotnetapp % dotnet run                               
         42
         42              ,d                             ,d
         42              42                             42
 ,adPPYb,42  ,adPPYba, MM42MMM 8b,dPPYba,   ,adPPYba, MM42MMM
a8"    `Y42 a8"     "8a  42    42P'   `"8a a8P_____42   42
8b       42 8b       d8  42    42       42 8PP"""""""   42
"8a,   ,d42 "8a,   ,a8"  42,   42       42 "8b,   ,aa   42,
 `"8bbdP"Y8  `"YbbdP"'   "Y428 42       42  `"Ybbd8"'   "Y428

.NET 6.0.0-preview.3.21201.4
Darwin 20.4.0 Darwin Kernel Version 20.4.0: Fri Mar  5 01:14:02 PST 2021; root:xnu-7195.101.1~3/RELEASE_ARM64_T8101

OSArchitecture: Arm64
ProcessorCount: 8

Build and run .NET 5 app with x64

The .NET 5 app should run successfully using emulation. It does.

rich@MacBook-Air dotnetapp % /usr/local/share/dotnet/x64/dotnet --version
6.0.100-preview.3.21202.5
rich@MacBook-Air dotnetapp % /usr/local/share/dotnet/x64/dotnet run
         42
         42              ,d                             ,d
         42              42                             42
 ,adPPYb,42  ,adPPYba, MM42MMM 8b,dPPYba,   ,adPPYba, MM42MMM
a8"    `Y42 a8"     "8a  42    42P'   `"8a a8P_____42   42
8b       42 8b       d8  42    42       42 8PP"""""""   42
"8a,   ,d42 "8a,   ,a8"  42,   42       42 "8b,   ,aa   42,
 `"8bbdP"Y8  `"YbbdP"'   "Y428 42       42  `"Ybbd8"'   "Y428

.NET 5.0.5
Darwin 20.4.0 Darwin Kernel Version 20.4.0: Fri Mar  5 01:14:02 PST 2021; root:xnu-7195.101.1~3/RELEASE_ARM64_T8101

OSArchitecture: X64
ProcessorCount: 8

Validate .NET 6 apps work

The previous example used a .NET 5 app. There are two concurrent .NET 6 versions installed (one for Arm64 and the other x64). Do I get Arm64 by default and can I opt to use x64? Yes.

rich@MacBook-Air ~ % dotnet new console -o app
Getting ready...
The template "Console Application" was created successfully.

Processing post-creation actions...
Running 'dotnet restore' on app/app.csproj...
  Determining projects to restore...
  Restored /Users/rich/app/app.csproj (in 111 ms).
Restore succeeded.

rich@MacBook-Air ~ % cd app
rich@MacBook-Air app % cat app.csproj| grep Target
    <TargetFramework>net6.0</TargetFramework>
rich@MacBook-Air app % dotnet run
Hello World!
rich@MacBook-Air app % rm -r bin obj
rich@MacBook-Air app % /usr/local/share/dotnet/x64/dotnet run
Hello World!

Unit testing

Unit testing is expected to require a compatible .NET version. It does.

Complexapp includes an app, libraries, and tests. Let's start by running the app, repeating the exercise from above.

rich@MacBook-Air complexapp % export DOTNET_ROLL_FORWARD= && export DOTNET_ROLL_FORWARD_TO_PRERELEASE=  
rich@MacBook-Air complexapp % pwd  
/Users/rich/git/dotnet-docker/samples/complexapp/complexapp
rich@MacBook-Air complexapp % dotnet run
It was not possible to find any compatible framework version
The framework 'Microsoft.NETCore.App', version '5.0.0' was not found.
  - The following frameworks were found:
      6.0.0-preview.3.21201.4 at [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]

You can resolve the problem by installing the specified framework and/or SDK.

The specified framework can be found at:
  - https://aka.ms/dotnet-core-applaunch?framework=Microsoft.NETCore.App&framework_version=5.0.0&arch=arm64&rid=osx.11.0-arm64
rich@MacBook-Air complexapp % /usr/local/share/dotnet/x64/dotnet run       
string: The quick brown fox jumps over the lazy dog
reversed: god yzal eht revo spmuj xof nworb kciuq ehT

Now let's run the tests.

rich@MacBook-Air complexapp % cd ..
rich@MacBook-Air complexapp % cd tests 
rich@MacBook-Air tests % pwd
/Users/rich/git/dotnet-docker/samples/complexapp/tests
rich@MacBook-Air tests % dotnet test
  Determining projects to restore...
  Restored /Users/rich/git/dotnet-docker/samples/complexapp/libfoo/libfoo.csproj (in 269 ms).
  Restored /Users/rich/git/dotnet-docker/samples/complexapp/libbar/libbar.csproj (in 269 ms).
  Restored /Users/rich/git/dotnet-docker/samples/complexapp/tests/tests.csproj (in 279 ms).
  You are using a preview version of .NET. See: https://aka.ms/dotnet-core-preview
  You are using a preview version of .NET. See: https://aka.ms/dotnet-core-preview
  libfoo -> /Users/rich/git/dotnet-docker/samples/complexapp/libfoo/bin/Debug/netstandard2.0/libfoo.dll
  libbar -> /Users/rich/git/dotnet-docker/samples/complexapp/libbar/bin/Debug/netstandard2.0/libbar.dll
  tests -> /Users/rich/git/dotnet-docker/samples/complexapp/tests/bin/Debug/net5.0/tests.dll
Test run for /Users/rich/git/dotnet-docker/samples/complexapp/tests/bin/Debug/net5.0/tests.dll (.NETCoreApp,Version=v5.0)
Microsoft (R) Test Execution Command Line Tool Version 16.10.0-preview-20210317-02
Copyright (c) Microsoft Corporation.  All rights reserved.

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
Testhost process exited with error: It was not possible to find any compatible framework version
The framework 'Microsoft.NETCore.App', version '5.0.0' was not found.
  - The following frameworks were found:
      6.0.0-preview.3.21201.4 at [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
You can resolve the problem by installing the specified framework and/or SDK.
The specified framework can be found at:
  - https://aka.ms/dotnet-core-applaunch?framework=Microsoft.NETCore.App&framework_version=5.0.0&arch=arm64&rid=osx.11.0-arm64
. Please check the diagnostic logs for more information.

Test Run Aborted.

As expected, the .NET 5-based tests fail with a .NET 6 only environment. Let's try with a .NET 5 environment.

rich@MacBook-Air tests % /usr/local/share/dotnet/x64/dotnet test
  Determining projects to restore...
  Restored /Users/rich/git/dotnet-docker/samples/complexapp/libbar/libbar.csproj (in 601 ms).
  Restored /Users/rich/git/dotnet-docker/samples/complexapp/libfoo/libfoo.csproj (in 601 ms).
  Restored /Users/rich/git/dotnet-docker/samples/complexapp/tests/tests.csproj (in 690 ms).
  You are using a preview version of .NET. See: https://aka.ms/dotnet-core-preview
  You are using a preview version of .NET. See: https://aka.ms/dotnet-core-preview
  libfoo -> /Users/rich/git/dotnet-docker/samples/complexapp/libfoo/bin/Debug/netstandard2.0/libfoo.dll
  libbar -> /Users/rich/git/dotnet-docker/samples/complexapp/libbar/bin/Debug/netstandard2.0/libbar.dll
  tests -> /Users/rich/git/dotnet-docker/samples/complexapp/tests/bin/Debug/net5.0/tests.dll
Test run for /Users/rich/git/dotnet-docker/samples/complexapp/tests/bin/Debug/net5.0/tests.dll (.NETCoreApp,Version=v5.0)
Microsoft (R) Test Execution Command Line Tool Version 16.10.0-preview-20210317-02
Copyright (c) Microsoft Corporation.  All rights reserved.

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.

Passed!  - Failed:     0, Passed:     2, Skipped:     0, Total:     2, Duration: 16 ms - /Users/rich/git/dotnet-docker/samples/complexapp/tests/bin/Debug/net5.0/tests.dll (net5.0)

That worked, as expected. Let's try the rollforward trick again.

rich@MacBook-Air tests % export DOTNET_ROLL_FORWARD=LatestMajor && export DOTNET_ROLL_FORWARD_TO_PRERELEASE=1
rich@MacBook-Air tests % dotnet test                            
  Determining projects to restore...
  Restored /Users/rich/git/dotnet-docker/samples/complexapp/libbar/libbar.csproj (in 260 ms).
  Restored /Users/rich/git/dotnet-docker/samples/complexapp/libfoo/libfoo.csproj (in 260 ms).
  Restored /Users/rich/git/dotnet-docker/samples/complexapp/tests/tests.csproj (in 273 ms).
  You are using a preview version of .NET. See: https://aka.ms/dotnet-core-preview
  You are using a preview version of .NET. See: https://aka.ms/dotnet-core-preview
  libfoo -> /Users/rich/git/dotnet-docker/samples/complexapp/libfoo/bin/Debug/netstandard2.0/libfoo.dll
  libbar -> /Users/rich/git/dotnet-docker/samples/complexapp/libbar/bin/Debug/netstandard2.0/libbar.dll
  tests -> /Users/rich/git/dotnet-docker/samples/complexapp/tests/bin/Debug/net5.0/tests.dll
Test run for /Users/rich/git/dotnet-docker/samples/complexapp/tests/bin/Debug/net5.0/tests.dll (.NETCoreApp,Version=v5.0)
Microsoft (R) Test Execution Command Line Tool Version 16.10.0-preview-20210317-02
Copyright (c) Microsoft Corporation.  All rights reserved.

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.

Passed!  - Failed:     0, Passed:     2, Skipped:     0, Total:     2, Duration: 2 ms - /Users/rich/git/dotnet-docker/samples/complexapp/tests/bin/Debug/net5.0/tests.dll (net5.0)
rich@MacBook-Air tests % 

Using Apphost

Let's see if we can get the combination of the CLI, apphost, DOTNET_ROOT to enable us to pivot executables to either the native or emulated architecture. This task requires a little extra finessing on macOS due to notarization. That step isn't required on Windows.

Let's start with Arm64.

rich@MacBook-Air dotnetapp % pwd
/Users/rich/git/dotnet-docker/samples/dotnetapp
rich@MacBook-Air dotnetapp % cat dotnetapp.csproj 
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <UseAppHost>true</UseAppHost>
  </PropertyGroup>

</Project>
rich@MacBook-Air dotnetapp % dotnet build
Microsoft (R) Build Engine version 16.10.0-preview-21181-07+073022eb4 for .NET
Copyright (C) Microsoft Corporation. All rights reserved.

  Determining projects to restore...
  Restored /Users/rich/git/dotnet-docker/samples/dotnetapp/dotnetapp.csproj (in 112 ms).
  You are using a preview version of .NET. See: https://aka.ms/dotnet-core-preview
  dotnetapp -> /Users/rich/git/dotnet-docker/samples/dotnetapp/bin/Debug/net6.0/dotnetapp.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:01.56
rich@MacBook-Air dotnetapp % codesign -s - bin/Debug/net6.0/dotnetapp
rich@MacBook-Air dotnetapp % ./bin/Debug/net6.0/dotnetapp
         42
         42              ,d                             ,d
         42              42                             42
 ,adPPYb,42  ,adPPYba, MM42MMM 8b,dPPYba,   ,adPPYba, MM42MMM
a8"    `Y42 a8"     "8a  42    42P'   `"8a a8P_____42   42
8b       42 8b       d8  42    42       42 8PP"""""""   42
"8a,   ,d42 "8a,   ,a8"  42,   42       42 "8b,   ,aa   42,
 `"8bbdP"Y8  `"YbbdP"'   "Y428 42       42  `"Ybbd8"'   "Y428

.NET 6.0.0-preview.3.21201.4
Darwin 20.4.0 Darwin Kernel Version 20.4.0: Fri Mar  5 01:14:02 PST 2021; root:xnu-7195.101.1~3/RELEASE_ARM64_T8101

OSArchitecture: Arm64
ProcessorCount: 8

Now x64. This is where the CLI forces us to use publish instead of build. Ughh.

rich@MacBook-Air dotnetapp % dotnet publish -o dotnetappx64 --self-contained false -r osx-x64
Microsoft (R) Build Engine version 16.10.0-preview-21181-07+073022eb4 for .NET
Copyright (C) Microsoft Corporation. All rights reserved.

  Determining projects to restore...
  Restored /Users/rich/git/dotnet-docker/samples/dotnetapp/dotnetapp.csproj (in 1.2 sec).
  You are using a preview version of .NET. See: https://aka.ms/dotnet-core-preview
  dotnetapp -> /Users/rich/git/dotnet-docker/samples/dotnetapp/bin/Debug/net6.0/osx-x64/dotnetapp.dll
  dotnetapp -> /Users/rich/git/dotnet-docker/samples/dotnetapp/dotnetappx64/
rich@MacBook-Air dotnetapp % codesign -s - dotnetappx64/dotnetapp
rich@MacBook-Air dotnetapp % export DOTNET_ROOT=/usr/local/share/dotnet/x64
rich@MacBook-Air dotnetapp % ./dotnetappx64/dotnetapp 
         42
         42              ,d                             ,d
         42              42                             42
 ,adPPYb,42  ,adPPYba, MM42MMM 8b,dPPYba,   ,adPPYba, MM42MMM
a8"    `Y42 a8"     "8a  42    42P'   `"8a a8P_____42   42
8b       42 8b       d8  42    42       42 8PP"""""""   42
"8a,   ,d42 "8a,   ,a8"  42,   42       42 "8b,   ,aa   42,
 `"8bbdP"Y8  `"YbbdP"'   "Y428 42       42  `"Ybbd8"'   "Y428

.NET 6.0.0-preview.3.21201.4
Darwin 20.4.0 Darwin Kernel Version 20.4.0: Fri Mar  5 01:14:02 PST 2021; root:xnu-7195.101.1~3/RELEASE_ARM64_T8101

OSArchitecture: X64
ProcessorCount: 8
rich@MacBook-Air dotnetapp % rm -r bin obj
rich@MacBook-Air dotnetapp % /usr/local/share/dotnet/x64/dotnet build
Microsoft (R) Build Engine version 16.10.0-preview-21181-07+073022eb4 for .NET
Copyright (C) Microsoft Corporation. All rights reserved.

  Determining projects to restore...
  Restored /Users/rich/git/dotnet-docker/samples/dotnetapp/dotnetapp.csproj (in 133 ms).
  You are using a preview version of .NET. See: https://aka.ms/dotnet-core-preview
  dotnetapp -> /Users/rich/git/dotnet-docker/samples/dotnetapp/bin/Debug/net6.0/dotnetapp.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:05.20

That worked. Once the x64 directory is established as a known location, the DOTNET_ROOT ENV won't need to be set by default.

Using .NET global Tools

.NET global tools are a specific apphost use case. Let's see how it plays out.

Let's validate that we're starting from a clean slate.

rich@MacBook-Air ~ % rm -r .dotnet/tools 
rich@MacBook-Air ~ % dotnet tool list -g
Package Id      Version      Commands
-------------------------------------

Now let's install and use my favorite tool with the Arm64 SDK.

rich@MacBook-Air ~ % dotnet tool install -g dotnet-runtimeinfo
You can invoke the tool using the following command: dotnet-runtimeinfo
Tool 'dotnet-runtimeinfo' (version '1.0.4') was successfully installed.
rich@MacBook-Air ~ % dotnet-runtimeinfo
zsh: killed     dotnet-runtimeinfo
rich@MacBook-Air ~ % codesign -s - ~/.dotnet/tools/dotnet-runtimeinfo 
rich@MacBook-Air ~ % dotnet-runtimeinfo 
**.NET information
Version: 6.0.0
FrameworkDescription: .NET 6.0.0-preview.3.21201.4
Libraries version: 6.0.0-preview.3.21201.4
Libraries hash: 236cb21e3c1992c8cee6935ce67e2125ac4687e8

**Environment information
OSDescription: Darwin 20.4.0 Darwin Kernel Version 20.4.0: Fri Mar  5 01:14:02 PST 2021; root:xnu-7195.101.1~3/RELEASE_ARM64_T8101
OSVersion: Unix 11.3.0
OSArchitecture: Arm64
ProcessorCount: 8

rich@MacBook-Air ~ % dotnet runtimeinfo
**.NET information
Version: 6.0.0
FrameworkDescription: .NET 6.0.0-preview.3.21201.4
Libraries version: 6.0.0-preview.3.21201.4
Libraries hash: 236cb21e3c1992c8cee6935ce67e2125ac4687e8

**Environment information
OSDescription: Darwin 20.4.0 Darwin Kernel Version 20.4.0: Fri Mar  5 01:14:02 PST 2021; root:xnu-7195.101.1~3/RELEASE_ARM64_T8101
OSVersion: Unix 11.3.0
OSArchitecture: Arm64
ProcessorCount: 8

It appears that .NET tools need to be notarized. That's a problem. After that, the tools work.

Given that the tools use apphost, let's see if we can start an Arm64 tools with the x64 SDK.

rich@MacBook-Air ~ % /usr/local/share/dotnet/x64/dotnet runtimeinfo
**.NET information
Version: 6.0.0
FrameworkDescription: .NET 6.0.0-preview.3.21201.4
Libraries version: 6.0.0-preview.3.21201.4
Libraries hash: 236cb21e3c1992c8cee6935ce67e2125ac4687e8

**Environment information
OSDescription: Darwin 20.4.0 Darwin Kernel Version 20.4.0: Fri Mar  5 01:14:02 PST 2021; root:xnu-7195.101.1~3/RELEASE_ARM64_T8101
OSVersion: Unix 11.3.0
OSArchitecture: Arm64
ProcessorCount: 8

We can. That's not necessarily super useful, but good to know it works. Naturally, the tool sticks to being an Arm64 tools.

Let's now try the opposite approach, with the x64 SDK.

rich@MacBook-Air ~ % dotnet tool uninstall -g dotnet-runtimeinfo     
Tool 'dotnet-runtimeinfo' (version '1.0.4') was successfully uninstalled.
rich@MacBook-Air ~ % /usr/local/share/dotnet/x64/dotnet tool install -g dotnet-runtimeinfo
You can invoke the tool using the following command: dotnet-runtimeinfo
Tool 'dotnet-runtimeinfo' (version '1.0.4') was successfully installed.
rich@MacBook-Air ~ % /usr/local/share/dotnet/x64/dotnet tool list -g           
Package Id              Version      Commands          
-------------------------------------------------------
dotnet-runtimeinfo      1.0.4        dotnet-runtimeinfo
rich@MacBook-Air ~ % dotnet tool list -g 
Package Id              Version      Commands          
-------------------------------------------------------
dotnet-runtimeinfo      1.0.4        dotnet-runtimeinfo
rich@MacBook-Air ~ % dotnet-runtimeinfo                             
Failed to load /usr/local/share/dotnet/host/fxr/6.0.0-preview.3.21201.4/libhostfxr.dylib, error: dlopen(/usr/local/share/dotnet/host/fxr/6.0.0-preview.3.21201.4/libhostfxr.dylib, 1): no suitable image found.  Did find:
	/usr/local/share/dotnet/host/fxr/6.0.0-preview.3.21201.4/libhostfxr.dylib: mach-o, but wrong architecture
	/usr/local/share/dotnet/host/fxr/6.0.0-preview.3.21201.4/libhostfxr.dylib: mach-o, but wrong architecture
The library libhostfxr.dylib was found, but loading it from /usr/local/share/dotnet/host/fxr/6.0.0-preview.3.21201.4/libhostfxr.dylib failed
  - Installing .NET prerequisites might help resolve this problem.
     https://go.microsoft.com/fwlink/?linkid=2063366
rich@MacBook-Air ~ % export DOTNET_ROOT=/usr/local/share/dotnet/x64
rich@MacBook-Air ~ % dotnet-runtimeinfo                            
**.NET information
Version: 5.0.5
FrameworkDescription: .NET 5.0.5
Libraries version: 5.0.5
Libraries hash: 2f740adc1457e8a28c1c072993b66f515977eb51

**Environment information
OSDescription: Darwin 20.4.0 Darwin Kernel Version 20.4.0: Fri Mar  5 01:14:02 PST 2021; root:xnu-7195.101.1~3/RELEASE_ARM64_T8101
OSVersion: Unix 11.3.0
OSArchitecture: X64
ProcessorCount: 8

rich@MacBook-Air ~ % /usr/local/share/dotnet/x64/dotnet runtimeinfo 
**.NET information
Version: 5.0.5
FrameworkDescription: .NET 5.0.5
Libraries version: 5.0.5
Libraries hash: 2f740adc1457e8a28c1c072993b66f515977eb51

**Environment information
OSDescription: Darwin 20.4.0 Darwin Kernel Version 20.4.0: Fri Mar  5 01:14:02 PST 2021; root:xnu-7195.101.1~3/RELEASE_ARM64_T8101
OSVersion: Unix 11.3.0
OSArchitecture: X64
ProcessorCount: 8

rich@MacBook-Air ~ % dotnet runtimeinfo                           
**.NET information
Version: 5.0.5
FrameworkDescription: .NET 5.0.5
Libraries version: 5.0.5
Libraries hash: 2f740adc1457e8a28c1c072993b66f515977eb51

**Environment information
OSDescription: Darwin 20.4.0 Darwin Kernel Version 20.4.0: Fri Mar  5 01:14:02 PST 2021; root:xnu-7195.101.1~3/RELEASE_ARM64_T8101
OSVersion: Unix 11.3.0
OSArchitecture: X64
ProcessorCount: 8

Interesting.

  • The basic experience is identical, except the opposite of using the Arm64 SDK. As demonstrated, dotnet-runtimeinfo is now an x64 tool, but can be launched in all the same ways.
  • Tools can be discovered and managed with either the x64 or Arm64 SDKs. There is no mention of their architecture. It seems there is a flaw or a missing feature(s) there. Not a huge concern.
  • The DOTNET_ROOT ENV needs to be set (unsurprisingly) to find the .NET runtime. Once the x64 directory is understood as a known location, that won't be necessary.
  • For some reason, the x64 apphost doesn't need to be notarized. That may be temporary or the permanent solution. Perhaps the x64 notarization requirements track closely to macOS x64 (native architecture).

Better x64 dotnet UX

All the example require using an absolute path: /usr/local/share/dotnet/x64/dotnet. That's pretty arduous. I had to use which dotnet several times to get the base dotnet path, as the Unix-based file system scheme is hard for me to remember. What about symbolic links or aliases?

Let's start with a symbolic link.

rich@MacBook-Air ~ % sudo ln -s /usr/local/share/dotnet/x64/dotnet /usr/local/bin/dotnetx64
rich@MacBook-Air ~ % dotnetx64

Usage: dotnet [options]
Usage: dotnet [path-to-application]

Options:
  -h|--help         Display help.
  --info            Display .NET information.
  --list-sdks       Display the installed SDKs.
  --list-runtimes   Display the installed runtimes.

path-to-application:
  The path to an application .dll file to execute.
rich@MacBook-Air ~ % dotnetx64 --info
.NET SDK (reflecting any global.json):
 Version:   6.0.100-preview.3.21202.5
 Commit:    aee38a6dd4

Runtime Environment:
 OS Name:     Mac OS X
 OS Version:  11.3
 OS Platform: Darwin
 RID:         osx.11.0-x64
 Base Path:   /usr/local/share/dotnet/x64/sdk/6.0.100-preview.3.21202.5/

Host (useful for support):
  Version: 6.0.0-preview.3.21201.4
  Commit:  236cb21e3c

.NET SDKs installed:
  3.1.408 [/usr/local/share/dotnet/x64/sdk]
  5.0.202 [/usr/local/share/dotnet/x64/sdk]
  6.0.100-preview.3.21202.5 [/usr/local/share/dotnet/x64/sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 3.1.14 [/usr/local/share/dotnet/x64/shared/Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.5 [/usr/local/share/dotnet/x64/shared/Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 6.0.0-preview.3.21201.13 [/usr/local/share/dotnet/x64/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 3.1.14 [/usr/local/share/dotnet/x64/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.5 [/usr/local/share/dotnet/x64/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.0-preview.3.21201.4 [/usr/local/share/dotnet/x64/shared/Microsoft.NETCore.App]

To install additional .NET runtimes or SDKs:
  https://aka.ms/dotnet-download
rich@MacBook-Air ~ % dotnetx64 new console -o app
The template "Console Application" was created successfully.

Processing post-creation actions...
Running 'dotnet restore' on app/app.csproj...
  Determining projects to restore...
  Restored /Users/rich/app/app.csproj (in 147 ms).
Restore succeeded.

rich@MacBook-Air ~ % cd app 
rich@MacBook-Air app % dotnetx64 run
Hello World!
rich@MacBook-Air app % cd  
rich@MacBook-Air ~ % cd git/dotnet-docker/samples/dotnetapp 
rich@MacBook-Air dotnetapp % dotnetx64 run
         42
         42              ,d                             ,d
         42              42                             42
 ,adPPYb,42  ,adPPYba, MM42MMM 8b,dPPYba,   ,adPPYba, MM42MMM
a8"    `Y42 a8"     "8a  42    42P'   `"8a a8P_____42   42
8b       42 8b       d8  42    42       42 8PP"""""""   42
"8a,   ,d42 "8a,   ,a8"  42,   42       42 "8b,   ,aa   42,
 `"8bbdP"Y8  `"YbbdP"'   "Y428 42       42  `"Ybbd8"'   "Y428

.NET 6.0.0-preview.3.21201.4
Darwin 20.4.0 Darwin Kernel Version 20.4.0: Fri Mar  5 01:14:02 PST 2021; root:xnu-7195.101.1~3/RELEASE_ARM64_T8101

OSArchitecture: X64
ProcessorCount: 8
rich@MacBook-Air dotnetapp % 

Let's try an alias. I'll just set it directly, but you would want to set it in .zshrc, .bashrc, or the equivalent if you wanted the setting to be durable across terminal windows and sessions. Windows has similar capabilities you can use.

rich@MacBook-Air ~ % sudo rm /usr/local/bin/dotnetx64 
Password:
rich@MacBook-Air ~ % dotnetx64
zsh: command not found: dotnetx64
rich@MacBook-Air ~ % alias dotnetx64="/usr/local/share/dotnet/x64/dotnet"
rich@MacBook-Air ~ % dotnetx64

Usage: dotnet [options]
Usage: dotnet [path-to-application]

Options:
  -h|--help         Display help.
  --info            Display .NET information.
  --list-sdks       Display the installed SDKs.
  --list-runtimes   Display the installed runtimes.

path-to-application:
  The path to an application .dll file to execute.
rich@MacBook-Air ~ % dotnetx64 --info
.NET SDK (reflecting any global.json):
 Version:   6.0.100-preview.3.21202.5
 Commit:    aee38a6dd4

Runtime Environment:
 OS Name:     Mac OS X
 OS Version:  11.3
 OS Platform: Darwin
 RID:         osx.11.0-x64
 Base Path:   /usr/local/share/dotnet/x64/sdk/6.0.100-preview.3.21202.5/

Host (useful for support):
  Version: 6.0.0-preview.3.21201.4
  Commit:  236cb21e3c

.NET SDKs installed:
  3.1.408 [/usr/local/share/dotnet/x64/sdk]
  5.0.202 [/usr/local/share/dotnet/x64/sdk]
  6.0.100-preview.3.21202.5 [/usr/local/share/dotnet/x64/sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 3.1.14 [/usr/local/share/dotnet/x64/shared/Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.5 [/usr/local/share/dotnet/x64/shared/Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 6.0.0-preview.3.21201.13 [/usr/local/share/dotnet/x64/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 3.1.14 [/usr/local/share/dotnet/x64/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.5 [/usr/local/share/dotnet/x64/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.0-preview.3.21201.4 [/usr/local/share/dotnet/x64/shared/Microsoft.NETCore.App]

To install additional .NET runtimes or SDKs:
  https://aka.ms/dotnet-download

I don't think we should configure the x64 dotnet in either of these ways by default. They just demonstrate that users can configure their machines in ways that make them more productive, while still using .NET in a correct and supported way.

Conclusions

The good news is that the vast majority of the proposed experiences works well, and they appears largely tenable.

We should do some combination of the following, in priority order:

  • Update the x64 installers to install to the proposed new location on macOS and Windows Arm64 machines.
  • Update apphost to treat the x64 directory as a known location (for the x64 apphost).
  • Enable discovery of .NET versions for all architectures on a machine, installed to known locations.
    • dotnet --info should provide information on another architecture being present on the machine.
  • Improve error messages to direct users to better outcomes.
    • Direct users to a required version that is already installed, but for another architecture.
    • Direct users to use the other architecture because the required version is known to not be available for the current architecture (for example .NET 5 on Apple Silicon).
    • Direct users to use dotnet run and dotnet test with architecture specific builds when cross architectures.
  • Implement the codesign technique used in the examples as a first class feature so that we can re-enable the apphost on macOS, for apps in general and global tools in particular.
  • Enable explicit flows from one architecture to the other, for example by enabling users to specify an alternate architecture to dotnet run and dotnet test.

The simulated example UX demonstrated above (assuming we productize it) is a great step forward compared to what we have today, but it isn't good enough to ship. We need to make the product easier to use. We know that x64 emulation will be important, at least at first. On Windows, it may be an important part of the developer experience for a long time. We need to prepare for that likely outcome, and for x64 on Arm64 machines being more relevant than x86 is on Windows x64 today.

There are developer experiences that were not tested. It is very likely that there are important experiences that still need to be considered.

Metadata

Metadata

Assignees

No one assigned

    Labels

    untriagedRequest triage from a team member

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions