Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disable multi-level lookup by default #67022

Merged
merged 20 commits into from
Mar 25, 2022
Merged

Conversation

elinor-fung
Copy link
Member

@elinor-fung elinor-fung commented Mar 23, 2022

Design doc: Disable multi-level lookup by default

  • Framework resolution
    • Applications targeting .NET 7.0 and above: multi-level lookup is disabled. There is no configuration to enable it.
    • Applications targeting .NET 6.0 and below: maintain existing behaviour. Multi-level lookup (Window-only) is enabled by default and can be configured via the DOTNET_MULTILEVEL_LOOKUP environment variable
    • Failure message includes new doc link: https://aka.ms/dotnet/app-launch-failed
  • SDK resolution
    • Multi-level lookup is disabled when running a .NET 7.0+ dotnet. SDKs are only searched for relative to the location of dotnet being run.
    • Failure message includes new doc link: https://aka.ms/dotnet/sdk-not-found
  • dotnet commands: --info, --list-runtimes, --list-sdks
    • Multi-level lookup is disabled when running a .NET 7.0+ dotnet. Runtimes and SDKs are only searched for relative to the location of dotnet being run.
    • --info message includes architecture and new doc link: https://aka.ms/dotnet/runtimes-sdk-info

Actual documentation is not yet written. The links currently redirect to the closest related existing docs I found.

Vast majority of this is @vitek-karas' work.

@richlander - the current messaging for resolution failures and dotnet --info looks like:

Framework resolution failure

Console application

See #67022 (comment)

Windows GUI application

image

SDK resolution failure

No compatible SDK for version in global.json

The command could not be loaded, possibly because:
  * You intended to execute a .NET program:
      The application 'help' does not exist.
  * You intended to execute a .NET SDK command:
      A compatible .NET SDK was not found.

Requested SDK version: 7.0.100
global.json file: D:\helloworld\global.json

Installed SDKs:
7.0.100-preview.2.22153.17 [D:\.dotnet.install\7.0.100-preview.2.22153.17-win-x64\sdk]

Install the [7.0.100] .NET SDK or update [D:\helloworld\global.json] to match an installed SDK.

Learn about SDK resolution:
https://aka.ms/dotnet/sdk-not-found

No SDKs

The command could not be loaded, possibly because:
  * You intended to execute a .NET program:
      The application 'help' does not exist.
  * You intended to execute a .NET SDK command:
      No .NET SDKs were found.

Download a .NET SDK:
https://aka.ms/dotnet-download

Learn about SDK resolution:
https://aka.ms/dotnet/sdk-not-found

dotnet --info

With SDK

.NET SDK (reflecting any global.json):
 Version:   7.0.100-preview.2.22153.17
 Commit:    9c52c56c13

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.19044
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   D:\.dotnet.install\7.0.100-preview.2.22153.17-win-x64\sdk\7.0.100-preview.2.22153.17\

global.json file:
  D:\helloworld\global.json

Host:
  Version:      7.0.0-dev
  Architecture: x64
  Commit:       N/A

.NET SDKs installed:
  7.0.100-preview.2.22153.17 [D:\.dotnet.install\7.0.100-preview.2.22153.17-win-x64\sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 7.0.0-preview.2.22153.2 [D:\.dotnet.install\7.0.100-preview.2.22153.17-win-x64\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 7.0.0-preview.2.22152.2 [D:\.dotnet.install\7.0.100-preview.2.22153.17-win-x64\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 7.0.0-preview.2.22153.5 [D:\.dotnet.install\7.0.100-preview.2.22153.17-win-x64\shared\Microsoft.WindowsDesktop.App]

Download .NET:
  https://aka.ms/dotnet-download

Learn about .NET Runtimes and SDKs:
  https://aka.ms/dotnet/runtimes-sdk-info

No SDKs

Host:
  Version:      7.0.0-dev
  Architecture: x64
  Commit:       N/A

.NET SDKs installed:
  No SDKs were found.

.NET runtimes installed:
  Microsoft.AspNetCore.App 7.0.0-preview.2.22153.2 [D:\.dotnet.install\7.0.100-preview.2.22153.17-win-x64\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 7.0.0-preview.2.22152.2 [D:\.dotnet.install\7.0.100-preview.2.22153.17-win-x64\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 7.0.0-preview.2.22153.5 [D:\.dotnet.install\7.0.100-preview.2.22153.17-win-x64\shared\Microsoft.WindowsDesktop.App]

Download .NET:
  https://aka.ms/dotnet-download

Learn about .NET Runtimes and SDKs:
  https://aka.ms/dotnet/runtimes-sdk-info

@elinor-fung elinor-fung added this to the 7.0.0 milestone Mar 23, 2022
@ghost ghost assigned elinor-fung Mar 23, 2022
@ghost
Copy link

ghost commented Mar 23, 2022

Tagging subscribers to this area: @vitek-karas, @agocke, @VSadov
See info in area-owners.md if you want to be subscribed.

Issue Details

Design doc: Disable multi-level lookup by default

  • Framework resolution
    • Applications targeting .NET 7.0 and above: multi-level lookup is disabled. There is no configuration to enable it.
    • Applications targeting .NET 6.0 and below: maintain existing behaviour. Multi-level lookup (Window-only) is enabled by default and can be configured via the DOTNET_MULTILEVEL_LOOKUP environment variable
    • Failure message includes new doc link: https://aka.ms/dotnet/app-launch-failed
  • SDK resolution
    • Multi-level lookup is disabled when running a .NET 7.0+ dotnet. SDKs are only searched for relative to the location of dotnet being run.
    • Failure message includes new doc link: https://aka.ms/dotnet/sdk-not-found
  • dotnet commands: --info, --list-runtimes, --list-sdks
    • Multi-level lookup is disabled when running a .NET 7.0+ dotnet. Runtimes and SDKs are only searched for relative to the location of dotnet being run.
    • --info message includes architecture and new doc link: https://aka.ms/dotnet/runtimes-sdk-info

Actual documentation is not yet written. The links currently redirect to the closest related existing docs I found.

Vast majority of this is @vitek-karas' work.

@richlander - the current messaging for resolution failures and dotnet --info looks like:

Framework resolution failure

Console application

You must install or update .NET to run this application.

Framework: 'Microsoft.NETCore.App', version '7.0.0' (x64)
Location: D:\.dotnet.install\7.0.100-preview.2.22153.17-win-x64

The following frameworks were found:
  7.0.0-preview.2.22152.2 at [D:\.dotnet.install\7.0.100-preview.2.22153.17-win-x64\shared\Microsoft.NETCore.App]

Learn about framework resolution:
https://aka.ms/dotnet/app-launch-failed

Download framework:
https://aka.ms/dotnet-core-applaunch?framework=Microsoft.NETCore.App&framework_version=7.0.0&arch=x64&rid=win10-x64

Windows GUI application

image

SDK resolution failure

No compatible SDK for version in global.json

The command could not be loaded, possibly because:
  * You intended to execute a .NET program:
      The application 'help' does not exist.
  * You intended to execute a .NET SDK command:
      A compatible .NET SDK was not found.

Requested SDK version: 7.0.100
global.json file: D:\helloworld\global.json

Installed SDKs:
7.0.100-preview.2.22153.17 [D:\.dotnet.install\7.0.100-preview.2.22153.17-win-x64\sdk]

Install the [7.0.100] .NET SDK or update [D:\helloworld\global.json] to match an installed SDK.

Learn about SDK resolution:
https://aka.ms/dotnet/sdk-not-found

No SDKs

The command could not be loaded, possibly because:
  * You intended to execute a .NET program:
      The application 'help' does not exist.
  * You intended to execute a .NET SDK command:
      No .NET SDKs were found.

Download a .NET SDK:
https://aka.ms/dotnet-download

Learn about SDK resolution:
https://aka.ms/dotnet/sdk-not-found

dotnet --info

.NET SDK (reflecting any global.json):
 Version:   7.0.100-preview.2.22153.17
 Commit:    9c52c56c13

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.19044
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   D:\.dotnet.install\7.0.100-preview.2.22153.17-win-x64\sdk\7.0.100-preview.2.22153.17\

global.json file:
  D:\helloworld\global.json

Host:
  Version:      7.0.0-dev
  Architecture: x64
  Commit:       N/A

.NET SDKs installed:
  7.0.100-preview.2.22153.17 [D:\.dotnet.install\7.0.100-preview.2.22153.17-win-x64\sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 7.0.0-preview.2.22153.2 [D:\.dotnet.install\7.0.100-preview.2.22153.17-win-x64\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 7.0.0-preview.2.22152.2 [D:\.dotnet.install\7.0.100-preview.2.22153.17-win-x64\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 7.0.0-preview.2.22153.5 [D:\.dotnet.install\7.0.100-preview.2.22153.17-win-x64\shared\Microsoft.WindowsDesktop.App]

Download .NET:
  https://aka.ms/dotnet-download

Learn about .NET Runtimes and SDKs:
  https://aka.ms/dotnet/runtimes-sdk-info
Author: elinor-fung
Assignees: -
Labels:

area-Host

Milestone: 7.0.0

vitek-karas and others added 15 commits March 23, 2022 08:52
The host tests use FluentAssertions which has its own implementation of string formatting, for things like `Print("Some {0}", "message\r\nline")`. Instead of taking the string replacement as-is, it formats it by wrapping it in double quotes and replaces newlines with escaped values, so the above would output:
```
Some "message\r\nline"
```

This makes the test output quite hard to read without formatting it back (by unescaping the newlines).

Unfortunately FluentAssertions tries hard to always run the value through its own formatter. So even if we preformat everything and instead call `Print($"Some {"message\r\nline"})` it only works sometimes. The string is still parsed and the parser may fail if it finds for example only an open `{` without a closing one. This makes it nest to useless as we would have to make sure to somehow escape everything we feed in.

So this change introduces a new extension method FailWithPreformatted which circumvents the automatic formatting and uses the underlying AddFailure method which will not format anything.
The host tests use FluentAssertions which has its own implementation of string formatting, for things like `Print("Some {0}", "message\r\nline")`. Instead of taking the string replacement as-is, it formats it by wrapping it in double quotes and replaces newlines with escaped values, so the above would output:
```
Some "message\r\nline"
```

This makes the test output quite hard to read without formatting it back (by unescaping the newlines).

Unfortunately FluentAssertions tries hard to always run the value through its own formatter. So even if we preformat everything and instead call `Print($"Some {"message\r\nline"})` it only works sometimes. The string is still parsed and the parser may fail if it finds for example only an open `{` without a closing one. This makes it nest to useless as we would have to make sure to somehow escape everything we feed in.

So this change introduces a new extension method FailWithPreformatted which circumvents the automatic formatting and uses the underlying AddFailure method which will not format anything.
The host tests use FluentAssertions which has its own implementation of string formatting, for things like `Print("Some {0}", "message\r\nline")`. Instead of taking the string replacement as-is, it formats it by wrapping it in double quotes and replaces newlines with escaped values, so the above would output:
```
Some "message\r\nline"
```

This makes the test output quite hard to read without formatting it back (by unescaping the newlines).

Unfortunately FluentAssertions tries hard to always run the value through its own formatter. So even if we preformat everything and instead call `Print($"Some {"message\r\nline"})` it only works sometimes. The string is still parsed and the parser may fail if it finds for example only an open `{` without a closing one. This makes it nest to useless as we would have to make sure to somehow escape everything we feed in.

So this change introduces a new extension method FailWithPreformatted which circumvents the automatic formatting and uses the underlying AddFailure method which will not format anything.
Update tests to match the new MLL behavior
src/native/corehost/apphost/apphost.windows.cpp Outdated Show resolved Hide resolved
src/native/corehost/apphost/apphost.windows.cpp Outdated Show resolved Hide resolved
src/native/corehost/apphost/apphost.windows.cpp Outdated Show resolved Hide resolved
src/native/corehost/fxr/framework_info.cpp Show resolved Hide resolved
src/native/corehost/fxr/fx_resolver.cpp Show resolved Hide resolved
src/native/corehost/fxr/fx_resolver.messages.cpp Outdated Show resolved Hide resolved
src/native/corehost/fxr/sdk_resolver.cpp Outdated Show resolved Hide resolved
src/native/corehost/runtime_config.cpp Outdated Show resolved Hide resolved
src/native/corehost/runtime_config.cpp Outdated Show resolved Hide resolved
@richlander
Copy link
Member

richlander commented Mar 24, 2022

Looks really good.

You must install or update .NET to run this application.

Framework: 'Microsoft.NETCore.App', version '7.0.0' (x64)
Location: D:.dotnet.install\7.0.100-preview.2.22153.17-win-x64

How about this small update:

You must install or update .NET to run this application.

App: C:\dotnet\is\awesome.exe
Framework: 'Microsoft.NETCore.App', version '7.0.0' (x64)
.NET location: D:.dotnet.install\7.0.100-preview.2.22153.17-win-x64

In some cases, "App" would be dotnet.exe and would duplication information with ".NET location". That's OK, and in fact good.

In cases where apphost cannot find DOTNET_ROOT (or the equivalent), then we could print "Unknown" or "Not found" for ".NET location".

(reflecting any global.json)

Can we remove that? I don't think it is helpful. If it was only shown when global.json is used, I'd be game, but that's not the case.

global.json file:
D:\helloworld\global.json

What happens when one isn't found? I think we should keep this section (not elide) it and instead print "Not found", just like we'd do above for the ".NET location" field.

@richlander
Copy link
Member

Ah, I forgot. We should also show architecture in these error messages. Like this.

You must install or update .NET to run this application.

App: C:\dotnet\is\awesome.exe
Architecture: x64
Framework: 'Microsoft.NETCore.App', version '7.0.0' (x64)
.NET location: D:.dotnet.install\7.0.100-preview.2.22153.17-win-x64

It duplicates "Framework" except when it doesn't.

@elinor-fung
Copy link
Member Author

Updated to always include the global.json section.

With updates to the framework resolution failure message:

apphost

You must install or update .NET to run this application.

App: D:\helloworld\bin\Debug\net7.0\helloworld.exe
Architecture: x64
Framework: 'Microsoft.NETCore.App', version '7.0.0' (x64)
.NET location: D:\.dotnet.install\7.0.100-preview.2.22153.17-win-x64\

The following frameworks were found:
  7.0.0-preview.2.22152.2 at [D:\.dotnet.install\7.0.100-preview.2.22153.17-win-x64\shared\Microsoft.NETCore.App]

Learn about framework resolution:
https://aka.ms/dotnet/app-launch-failed

Download framework:
https://aka.ms/dotnet-core-applaunch?framework=Microsoft.NETCore.App&framework_version=7.0.0&arch=x64&rid=win10-x64

dotnet

You must install or update .NET to run this application.

App: D:\.dotnet.install\7.0.100-preview.2.22153.17-win-x64\dotnet.exe
Architecture: x64
Framework: 'Microsoft.NETCore.App', version '7.0.0' (x64)
.NET location: D:\.dotnet.install\7.0.100-preview.2.22153.17-win-x64\

The following frameworks were found:
  7.0.0-preview.2.22152.2 at [D:\.dotnet.install\7.0.100-preview.2.22153.17-win-x64\shared\Microsoft.NETCore.App]

Learn about framework resolution:
https://aka.ms/dotnet/app-launch-failed

Download framework:
https://aka.ms/dotnet-core-applaunch?framework=Microsoft.NETCore.App&framework_version=7.0.0&arch=x64&rid=win10-x64

@elinor-fung
Copy link
Member Author

(reflecting any global.json)

Can we remove that? I don't think it is helpful.

Opened dotnet/sdk#24541 to remove it.

@danmoseley
Copy link
Member

FWIW, looking at this without following the issue,

I read this as what the app is requesting

Architecture: x64
Framework: 'Microsoft.NETCore.App', version '7.0.0' (x64)

and this is clearly what was found:

The following frameworks were found:
  7.0.0-preview.2.22152.2 at [D:\.dotnet.install\7.0.100-preview.2.22153.17-win-x64\shared\Microsoft.NETCore.App]

but what is this part? is it "this is where the framework found is installed" -- because that's already in the output. this needs clarification

.NET location: D:\.dotnet.install\7.0.100-preview.2.22153.17-win-x64\

@danmoseley
Copy link
Member

Is 'framework' an important word to use here? (as opposed eg to 'runtime' or '.NET runtime'). In my head I think, what kind of framework -- oh, it's the .NET framework -- confusion. 'Shared framework' would be unambiguous, but I don't think most app users will know what that is.

@elinor-fung
Copy link
Member Author

Yes - where 'real' means using default location (not custom registration or private install).

The host messages use 'framework' to refer to the defined groupings of libraries like Microsoft.AspNetCore.App/Microsoft.NETCore.App/ Microsoft.WindowsDesktop.App and 'runtime'/'.NET runtime' as what you get if you download a '.NET runtime' from the downloads page (so more specific to just coreclr/Microsoft.NETCore.App).

'framework' is certainly... overloaded. But I think the description we have in the glossary does match our usage. We do have 'shared framework' that glossary, but I'd agree that it is less common and less likely that app users will know what that is (I also don't use it much). We do also try hard to never have .NET framework (and definitely not .NET Framework) in a message.

@danmoseley
Copy link
Member

So if I understand correctly the Download framework: will not always point to a URL with ?framework=Microsoft.NETCore.App , but rather in some cases point to a download link for eg Microsoft.AspNetCore.App specifically (if there is such a thing)?

If not -- it seems it is reasonable to say it is just downloading the "runtime", perhaps

@elinor-fung
Copy link
Member Author

Yes, it would not always be ?framework=Microsoft.NETCore.App. For example, if it is a version of Microsoft.AspNetCore.App that is missing:

You must install or update .NET to run this application.

App: D:\helloaspnet\bin\Debug\net7.0\helloaspnet.exe
Architecture: x64
Framework: 'Microsoft.AspNetCore.App', version '7.0.0' (x64)
.NET location: C:\Program Files\dotnet\

The following frameworks were found:
  6.0.2 at [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  7.0.0-preview.1.22109.13 at [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]

Learn about framework resolution:
https://aka.ms/dotnet/app-launch-failed

Download framework:
https://aka.ms/dotnet-core-applaunch?framework=Microsoft.AspNetCore.App&framework_version=7.0.0&arch=x64&rid=win10-x64

@danmoseley
Copy link
Member

Got it. OK, my only remaining suggestion is to make it clearer the action required -- these are app users, not developers, eg

To fix this, install the framework from here:
https://aka.ms/dotnet-core-applaunch?framework=Microsoft.AspNetCore.App&framework_version=7.0.0&arch=x64&rid=win10-x64

src/native/corehost/runtime_config.cpp Outdated Show resolved Hide resolved
src/native/corehost/runtime_config.cpp Outdated Show resolved Hide resolved
src/native/corehost/runtime_config.cpp Show resolved Hide resolved
src/native/corehost/runtime_config.h Outdated Show resolved Hide resolved
@elinor-fung
Copy link
Member Author

elinor-fung commented Mar 25, 2022

@richlander - thoughts on #67022 (comment) or something like it (Download the framework to fix this:, Download the required framework:, To fix this, download the framework:...)?
May be a middle ground between just 'Download framework:' and the multiple sentences we currently have without this change.

@vitek-karas
Copy link
Member

What is the scenario where dotnet itself fails with missing framework error? Is that effectively dotnet exec? If that is the case, the App: printed out should not point to dotnet but instead to the app.dll.

I can't think of a case where running dotnet on its own without trying to execute an app would fail with missing framework... (ignoring corrupted installs).

Regarding the terminology in the errors. Unfortunately the dot.net website uses "runtime" pretty much for everything. It's a ".NET Runtime" or "ASP.NET Core Runtime" or ".NET Desktop Runtime". So I can see the confusion. I think the "error" part should stick to the "framework" term, since that's what is used in the low-level, details version of the docs. But the "action" part may want to use the website terminology - since it leads there. So maybe "To fix this, download .NET runtime from:".

Copy link
Member

@vitek-karas vitek-karas left a comment

Choose a reason for hiding this comment

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

Aside from the reported "app path" this looks great. Thanks a lot for all the cleanup and making it all pretty.

src/native/corehost/fxr/fx_muxer.cpp Outdated Show resolved Hide resolved
src/native/corehost/fxr/fx_resolver.cpp Outdated Show resolved Hide resolved
src/native/corehost/hostmisc/utils.h Show resolved Hide resolved
@richlander
Copy link
Member

@elinor-fung, @danmoseley

I propose "To install missing framework, download:"

Very few end-users will ever see this UX, since very few end-users run console apps. I'd like to keep the text as terse as possible.

This is the UX that end-users see (borrowed from Elinor above):

image

The UX of the console and GUI are not the same. I'd argue that the proposal makes them as similar as possible.

@richlander
Copy link
Member

richlander commented Mar 25, 2022

@vitek-karas

What is the scenario where dotnet itself fails with missing framework error? Is that effectively dotnet exec? If that is the case, the App: printed out should not point to dotnet but instead to the app.dll.

I would expect that dotnet app.dll would report this same error in the case that the required framework was missing. No? Or am I misunderstanding your question?

@elinor-fung
Copy link
Member Author

dotnet failing with missing framework error is effectively dotnet exec myapp.dll - same thing as dotnet myapp.dl

I believe that @vitek-karas' comment is that the message for running dotnet myapp.dll should be updated to:

- App: D:\.dotnet.install\7.0.100-preview.2.22153.17-win-x64\dotnet.exe
+ App: <full_path_to>\myapp.dll
Architecture: x64
Framework: 'Microsoft.NETCore.App', version '7.0.0' (x64)
.NET location: D:\.dotnet.install\7.0.100-preview.2.22153.17-win-x64\

(And leave running through apphost the way it is currently)

@vitek-karas
Copy link
Member

Exactly what @elinor-fung said (I was just too slow to type it). There's little value in showing the path to dotnet in that case (since it's already there anyway), and there's no way to tell what was the app in question (unlike the case with apphost).

@richlander
Copy link
Member

richlander commented Mar 25, 2022

Ah. I didn't pick that up. +1 on that proposal.

This is almost frightening. These improvements are going to be quite useful.

@richlander
Copy link
Member

Woohoo. So glad to see this change in.

@richlander
Copy link
Member

Just checking. It doesn't appear that we wrote up a breaking change notice for this. Is that right?

@elinor-fung @baronfel

@elinor-fung
Copy link
Member Author

elinor-fung commented Jun 9, 2022

We have https://docs.microsoft.com/dotnet/core/compatibility/deployment/7.0/multilevel-lookup
Corresponding issue in docs repo: dotnet/docs#28836
Is that the beraking change notice you are looking for?

@richlander
Copy link
Member

Works. Thanks!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants