Description
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:
- The app is self-contained and launched with its executable apphost, such as
myapp.exe
or./myapp
. - The app is framework-dependent and launched with its executable apphost, such as
myapp.exe
or./myapp
. - The app is framework-dependent and launched with the
dotnet
host, such asdotnet 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 aDOTNET_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
anddotnet 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
anddotnet 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 ofdotnet 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).
- For example, using the Arm64 SDK,
dotnet run
anddotnet 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
anddotnet 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 thedeps.json
file. The Arm64dotnet
could proxy to the x64dotnet
, and vice versa. Thedotnet
host is relevant on macOS, since it is used exclusively fordotnet run
anddotnet test
scenarios, due to notarization requirements. - We could simply extend
dotnet test
, for example, to accept a runtime ID, such asdotnet test -r osx-x64
. Ideally, we could infer the operating system, and specify only the architecture, such asdotnet 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 globaldotnet
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 thex64
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
anddotnet 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
anddotnet 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.