From 013ef97b4d997baa9310fe94464370895ccafe30 Mon Sep 17 00:00:00 2001 From: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Date: Tue, 22 Feb 2022 08:20:11 -0800 Subject: [PATCH 01/20] Improve formatting of host test errors 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. --- .../DependencyResolutionCommandResultExtensions.cs | 1 + .../tests/TestUtils/Assertions/CommandResultAssertions.cs | 1 + .../tests/TestUtils/Assertions/DirectoryInfoAssertions.cs | 1 + 3 files changed, 3 insertions(+) diff --git a/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs b/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs index 149eead4c7ac7f..f2ab034a94a91f 100644 --- a/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs +++ b/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.IO; +using TestUtils.Assertions; namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.DependencyResolution { diff --git a/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs b/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs index 305f7aca468355..20b0e34967069e 100644 --- a/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs +++ b/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs @@ -6,6 +6,7 @@ using FluentAssertions; using FluentAssertions.Execution; using Microsoft.DotNet.Cli.Build.Framework; +using TestUtils.Assertions; namespace Microsoft.DotNet.CoreSetup.Test { diff --git a/src/installer/tests/TestUtils/Assertions/DirectoryInfoAssertions.cs b/src/installer/tests/TestUtils/Assertions/DirectoryInfoAssertions.cs index d4915024c00c21..1fa3049e12a033 100644 --- a/src/installer/tests/TestUtils/Assertions/DirectoryInfoAssertions.cs +++ b/src/installer/tests/TestUtils/Assertions/DirectoryInfoAssertions.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using TestUtils.Assertions; namespace Microsoft.DotNet.CoreSetup.Test { From bc16363b4494c443db24e0a36e890d4461e9fd96 Mon Sep 17 00:00:00 2001 From: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Date: Tue, 22 Feb 2022 10:50:06 -0800 Subject: [PATCH 02/20] Fixes --- .../DependencyResolutionCommandResultExtensions.cs | 1 - .../tests/TestUtils/Assertions/CommandResultAssertions.cs | 1 - .../tests/TestUtils/Assertions/DirectoryInfoAssertions.cs | 1 - 3 files changed, 3 deletions(-) diff --git a/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs b/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs index f2ab034a94a91f..149eead4c7ac7f 100644 --- a/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs +++ b/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; using System.IO; -using TestUtils.Assertions; namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.DependencyResolution { diff --git a/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs b/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs index 20b0e34967069e..305f7aca468355 100644 --- a/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs +++ b/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs @@ -6,7 +6,6 @@ using FluentAssertions; using FluentAssertions.Execution; using Microsoft.DotNet.Cli.Build.Framework; -using TestUtils.Assertions; namespace Microsoft.DotNet.CoreSetup.Test { diff --git a/src/installer/tests/TestUtils/Assertions/DirectoryInfoAssertions.cs b/src/installer/tests/TestUtils/Assertions/DirectoryInfoAssertions.cs index 1fa3049e12a033..d4915024c00c21 100644 --- a/src/installer/tests/TestUtils/Assertions/DirectoryInfoAssertions.cs +++ b/src/installer/tests/TestUtils/Assertions/DirectoryInfoAssertions.cs @@ -7,7 +7,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using TestUtils.Assertions; namespace Microsoft.DotNet.CoreSetup.Test { From 0cff6bf82321424af759867a81c1e1c4bdbca4ab Mon Sep 17 00:00:00 2001 From: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Date: Tue, 22 Feb 2022 08:20:11 -0800 Subject: [PATCH 03/20] Improve formatting of host test errors 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. --- ...ndencyResolutionCommandResultExtensions.cs | 1 + .../Assertions/AssertionScopeExtensions.cs | 20 ------------------- .../Assertions/CommandResultAssertions.cs | 1 + .../Assertions/DirectoryInfoAssertions.cs | 1 + 4 files changed, 3 insertions(+), 20 deletions(-) diff --git a/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs b/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs index 149eead4c7ac7f..f2ab034a94a91f 100644 --- a/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs +++ b/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.IO; +using TestUtils.Assertions; namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.DependencyResolution { diff --git a/src/installer/tests/TestUtils/Assertions/AssertionScopeExtensions.cs b/src/installer/tests/TestUtils/Assertions/AssertionScopeExtensions.cs index 24a8e58ed8b8b0..e69de29bb2d1d6 100644 --- a/src/installer/tests/TestUtils/Assertions/AssertionScopeExtensions.cs +++ b/src/installer/tests/TestUtils/Assertions/AssertionScopeExtensions.cs @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using FluentAssertions.Execution; - -namespace Microsoft.DotNet.CoreSetup.Test -{ - public static class AssertionScopeExtensions - { - public static Continuation FailWithPreformatted(this AssertionScope assertionScope, string message) - { - if (!assertionScope.Succeeded) - { - assertionScope.AddFailure(message); - } - - return new Continuation(assertionScope, assertionScope.Succeeded); - } - } -} diff --git a/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs b/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs index 305f7aca468355..20b0e34967069e 100644 --- a/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs +++ b/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs @@ -6,6 +6,7 @@ using FluentAssertions; using FluentAssertions.Execution; using Microsoft.DotNet.Cli.Build.Framework; +using TestUtils.Assertions; namespace Microsoft.DotNet.CoreSetup.Test { diff --git a/src/installer/tests/TestUtils/Assertions/DirectoryInfoAssertions.cs b/src/installer/tests/TestUtils/Assertions/DirectoryInfoAssertions.cs index d4915024c00c21..1fa3049e12a033 100644 --- a/src/installer/tests/TestUtils/Assertions/DirectoryInfoAssertions.cs +++ b/src/installer/tests/TestUtils/Assertions/DirectoryInfoAssertions.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using TestUtils.Assertions; namespace Microsoft.DotNet.CoreSetup.Test { From 0a31cb388ac74856b6416e89d7c8149e4ec82f72 Mon Sep 17 00:00:00 2001 From: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Date: Tue, 22 Feb 2022 10:50:06 -0800 Subject: [PATCH 04/20] Fixes --- .../DependencyResolutionCommandResultExtensions.cs | 1 - .../tests/TestUtils/Assertions/CommandResultAssertions.cs | 1 - .../tests/TestUtils/Assertions/DirectoryInfoAssertions.cs | 1 - 3 files changed, 3 deletions(-) diff --git a/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs b/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs index f2ab034a94a91f..149eead4c7ac7f 100644 --- a/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs +++ b/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; using System.IO; -using TestUtils.Assertions; namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.DependencyResolution { diff --git a/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs b/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs index 20b0e34967069e..305f7aca468355 100644 --- a/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs +++ b/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs @@ -6,7 +6,6 @@ using FluentAssertions; using FluentAssertions.Execution; using Microsoft.DotNet.Cli.Build.Framework; -using TestUtils.Assertions; namespace Microsoft.DotNet.CoreSetup.Test { diff --git a/src/installer/tests/TestUtils/Assertions/DirectoryInfoAssertions.cs b/src/installer/tests/TestUtils/Assertions/DirectoryInfoAssertions.cs index 1fa3049e12a033..d4915024c00c21 100644 --- a/src/installer/tests/TestUtils/Assertions/DirectoryInfoAssertions.cs +++ b/src/installer/tests/TestUtils/Assertions/DirectoryInfoAssertions.cs @@ -7,7 +7,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using TestUtils.Assertions; namespace Microsoft.DotNet.CoreSetup.Test { From c7d94d0ba73a13ad6eb9dd41cf7a7513395f7c63 Mon Sep 17 00:00:00 2001 From: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Date: Thu, 3 Mar 2022 06:40:04 -0800 Subject: [PATCH 05/20] Enable some tests --- .../FrameworkResolution/MultipleHives.cs | 8 ++++---- .../Assertions/AssertionScopeExtensions.cs | 20 +++++++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/installer/tests/HostActivation.Tests/FrameworkResolution/MultipleHives.cs b/src/installer/tests/HostActivation.Tests/FrameworkResolution/MultipleHives.cs index 1ae3df06f1631f..c92c636327cc29 100644 --- a/src/installer/tests/HostActivation.Tests/FrameworkResolution/MultipleHives.cs +++ b/src/installer/tests/HostActivation.Tests/FrameworkResolution/MultipleHives.cs @@ -37,11 +37,11 @@ public MultipleHives(SharedTestState sharedState) [InlineData("5.0.0", "net6.0", true, "5.1.2")] [InlineData("5.0.0", "net6.0", null, "5.1.2")] [InlineData("5.0.0", "net6.0", false, "5.2.0")] - [InlineData("7.0.0", "net7.0", true, "7.0.1")] - [InlineData("7.0.0", "net7.0", null, "7.0.1")] + [InlineData("7.0.0", "net7.0", true, "7.1.2")] // MLL disabled by default - setting it doesn't change anything + [InlineData("7.0.0", "net7.0", null, "7.1.2")] // MLL disabled by default [InlineData("7.0.0", "net7.0", false, "7.1.2")] - [InlineData("7.0.0", "net8.0", true, "7.0.1")] - [InlineData("7.0.0", "net8.0", null, "7.0.1")] + [InlineData("7.0.0", "net8.0", true, "7.1.2")] // MLL disabled by default - setting it doesn't change anything + [InlineData("7.0.0", "net8.0", null, "7.1.2")] // MLL disabled by default [InlineData("7.0.0", "net8.0", false, "7.1.2")] // MLL where main hive has a better match [InlineData("6.0.0", "net6.0", true, "6.1.4")] // Global hive with better version (higher patch) diff --git a/src/installer/tests/TestUtils/Assertions/AssertionScopeExtensions.cs b/src/installer/tests/TestUtils/Assertions/AssertionScopeExtensions.cs index e69de29bb2d1d6..5dcc970d16fd16 100644 --- a/src/installer/tests/TestUtils/Assertions/AssertionScopeExtensions.cs +++ b/src/installer/tests/TestUtils/Assertions/AssertionScopeExtensions.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using FluentAssertions.Execution; + +namespace Microsoft.DotNet.CoreSetup.Test +{ + public static class AssertionScopeExtensions + { + public static Continuation FailWithPreformatted(this AssertionScope assertionScope, string message) + { + if (!assertionScope.Succeeded) + { + assertionScope.AddFailure(message); + } + + return new Continuation(assertionScope, assertionScope.Succeeded); + } + } +} From 03a3158ac14787b2b461bb1c73fcb2ab927fde03 Mon Sep 17 00:00:00 2001 From: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Date: Thu, 3 Mar 2022 08:44:13 -0800 Subject: [PATCH 06/20] Disable MLL per design rules --- src/native/corehost/fxr/framework_info.cpp | 5 ++- src/native/corehost/fxr/framework_info.h | 1 + src/native/corehost/fxr/fx_muxer.cpp | 4 +- src/native/corehost/fxr/fx_resolver.cpp | 15 ++++--- src/native/corehost/fxr/fx_resolver.h | 5 ++- .../corehost/fxr/fx_resolver.messages.cpp | 7 ++-- src/native/corehost/fxr/hostfxr.cpp | 2 +- src/native/corehost/fxr/sdk_info.cpp | 2 +- src/native/corehost/fxr/sdk_resolver.cpp | 2 +- src/native/corehost/hostmisc/utils.cpp | 4 +- src/native/corehost/hostmisc/utils.h | 2 +- src/native/corehost/runtime_config.cpp | 40 +++++++++++++++++++ src/native/corehost/runtime_config.h | 4 ++ 13 files changed, 73 insertions(+), 20 deletions(-) diff --git a/src/native/corehost/fxr/framework_info.cpp b/src/native/corehost/fxr/framework_info.cpp index 0859f6e57c9d7e..439cde6e3980eb 100644 --- a/src/native/corehost/fxr/framework_info.cpp +++ b/src/native/corehost/fxr/framework_info.cpp @@ -35,10 +35,11 @@ bool compare_by_name_and_version(const framework_info &a, const framework_info & /*static*/ void framework_info::get_all_framework_infos( const pal::string_t& own_dir, const pal::string_t& fx_name, + bool disable_multilevel_lookup, std::vector* framework_infos) { std::vector hive_dir; - get_framework_and_sdk_locations(own_dir, &hive_dir); + get_framework_and_sdk_locations(own_dir, disable_multilevel_lookup, &hive_dir); int32_t hive_depth = 0; @@ -97,7 +98,7 @@ bool compare_by_name_and_version(const framework_info &a, const framework_info & /*static*/ bool framework_info::print_all_frameworks(const pal::string_t& own_dir, const pal::string_t& leading_whitespace) { std::vector framework_infos; - get_all_framework_infos(own_dir, _X(""), &framework_infos); + get_all_framework_infos(own_dir, _X(""), /*disable_multilevel_lookup*/ true, &framework_infos); for (framework_info info : framework_infos) { trace::println(_X("%s%s %s [%s]"), leading_whitespace.c_str(), info.name.c_str(), info.version.as_str().c_str(), info.path.c_str()); diff --git a/src/native/corehost/fxr/framework_info.h b/src/native/corehost/fxr/framework_info.h index e4eea09cb14c22..8e95bdea0cc61a 100644 --- a/src/native/corehost/fxr/framework_info.h +++ b/src/native/corehost/fxr/framework_info.h @@ -18,6 +18,7 @@ struct framework_info static void get_all_framework_infos( const pal::string_t& own_dir, const pal::string_t& fx_name, + bool disable_multilevel_lookup, std::vector* framework_infos); static bool print_all_frameworks(const pal::string_t& own_dir, const pal::string_t& leading_whitespace); diff --git a/src/native/corehost/fxr/fx_muxer.cpp b/src/native/corehost/fxr/fx_muxer.cpp index 8647130c18dc10..bfacf2d6f027a8 100644 --- a/src/native/corehost/fxr/fx_muxer.cpp +++ b/src/native/corehost/fxr/fx_muxer.cpp @@ -466,7 +466,7 @@ namespace } else { - rc = fx_resolver_t::resolve_frameworks_for_app(host_info, override_settings, app_config, fx_definitions); + rc = fx_resolver_t::resolve_frameworks_for_app(host_info, app_config.get_is_multilevel_lookup_disabled(), override_settings, app_config, fx_definitions); if (rc != StatusCode::Success) { return rc; @@ -620,7 +620,7 @@ namespace return StatusCode::InvalidConfigFile; } - rc = fx_resolver_t::resolve_frameworks_for_app(host_info, override_settings, app_config, fx_definitions); + rc = fx_resolver_t::resolve_frameworks_for_app(host_info, app_config.get_is_multilevel_lookup_disabled(), override_settings, app_config, fx_definitions); if (rc != StatusCode::Success) return rc; diff --git a/src/native/corehost/fxr/fx_resolver.cpp b/src/native/corehost/fxr/fx_resolver.cpp index 5a5fef7aac0ec8..c79602da3a9142 100644 --- a/src/native/corehost/fxr/fx_resolver.cpp +++ b/src/native/corehost/fxr/fx_resolver.cpp @@ -190,7 +190,8 @@ namespace fx_definition_t* resolve_framework_reference( const fx_reference_t & fx_ref, const pal::string_t & oldest_requested_version, - const pal::string_t & dotnet_dir) + const pal::string_t & dotnet_dir, + const bool disable_multilevel_lookup) { #if defined(DEBUG) assert(!fx_ref.get_fx_name().empty()); @@ -205,7 +206,7 @@ namespace fx_ref.get_fx_name().c_str(), fx_ref.get_fx_version().c_str()); std::vector hive_dir; - get_framework_and_sdk_locations(dotnet_dir, &hive_dir); + get_framework_and_sdk_locations(dotnet_dir, disable_multilevel_lookup, &hive_dir); pal::string_t selected_fx_dir; pal::string_t selected_fx_version; @@ -394,6 +395,7 @@ void fx_resolver_t::update_newest_references( // InvalidConfigFile - reading of a runtime config for some of the processed frameworks has failed. StatusCode fx_resolver_t::read_framework( const host_startup_info_t & host_info, + bool disable_multilevel_lookup, const runtime_config_t::settings_t& override_settings, const runtime_config_t & config, const fx_reference_t * effective_parent_fx_ref, @@ -439,10 +441,10 @@ StatusCode fx_resolver_t::read_framework( m_effective_fx_references[fx_name] = new_effective_fx_ref; // Resolve the effective framework reference against the the existing physical framework folders - fx_definition_t* fx = resolve_framework_reference(new_effective_fx_ref, m_oldest_fx_references[fx_name].get_fx_version(), host_info.dotnet_root); + fx_definition_t* fx = resolve_framework_reference(new_effective_fx_ref, m_oldest_fx_references[fx_name].get_fx_version(), host_info.dotnet_root, disable_multilevel_lookup); if (fx == nullptr) { - display_missing_framework_error(fx_name, new_effective_fx_ref.get_fx_version(), pal::string_t(), host_info.dotnet_root); + display_missing_framework_error(fx_name, new_effective_fx_ref.get_fx_version(), pal::string_t(), host_info.dotnet_root, disable_multilevel_lookup); return FrameworkMissingFailure; } @@ -471,7 +473,7 @@ StatusCode fx_resolver_t::read_framework( return StatusCode::InvalidConfigFile; } - rc = read_framework(host_info, override_settings, new_config, &new_effective_fx_ref, fx_definitions); + rc = read_framework(host_info, disable_multilevel_lookup, override_settings, new_config, &new_effective_fx_ref, fx_definitions); if (rc) { break; // Error case @@ -511,6 +513,7 @@ fx_resolver_t::fx_resolver_t() StatusCode fx_resolver_t::resolve_frameworks_for_app( const host_startup_info_t & host_info, + bool disable_multilevel_lookup, const runtime_config_t::settings_t& override_settings, const runtime_config_t & app_config, fx_definition_vector_t & fx_definitions) @@ -523,7 +526,7 @@ StatusCode fx_resolver_t::resolve_frameworks_for_app( do { fx_definitions.resize(1); // Erase any existing frameworks for re-try - rc = resolver.read_framework(host_info, override_settings, app_config, /*effective_parent_fx_ref*/ nullptr, fx_definitions); + rc = resolver.read_framework(host_info, disable_multilevel_lookup, override_settings, app_config, /*effective_parent_fx_ref*/ nullptr, fx_definitions); } while (rc == StatusCode::FrameworkCompatRetry && retry_count++ < Max_Framework_Resolve_Retries); assert(retry_count < Max_Framework_Resolve_Retries); diff --git a/src/native/corehost/fxr/fx_resolver.h b/src/native/corehost/fxr/fx_resolver.h index 1db5a7d1ebab35..1d0e001fe617c6 100644 --- a/src/native/corehost/fxr/fx_resolver.h +++ b/src/native/corehost/fxr/fx_resolver.h @@ -16,6 +16,7 @@ class fx_resolver_t public: static StatusCode resolve_frameworks_for_app( const host_startup_info_t& host_info, + bool disable_multilevel_lookup, const runtime_config_t::settings_t& override_settings, const runtime_config_t& app_config, fx_definition_vector_t& fx_definitions); @@ -31,6 +32,7 @@ class fx_resolver_t const runtime_config_t& config); StatusCode read_framework( const host_startup_info_t& host_info, + bool disable_multilevel_lookup, const runtime_config_t::settings_t& override_settings, const runtime_config_t& config, const fx_reference_t * effective_parent_fx_ref, @@ -49,7 +51,8 @@ class fx_resolver_t const pal::string_t& fx_name, const pal::string_t& fx_version, const pal::string_t& fx_dir, - const pal::string_t& dotnet_root); + const pal::string_t& dotnet_root, + bool disable_multilevel_lookup); static void display_incompatible_framework_error( const pal::string_t& higher, const fx_reference_t& lower); diff --git a/src/native/corehost/fxr/fx_resolver.messages.cpp b/src/native/corehost/fxr/fx_resolver.messages.cpp index 9f64a793c16cb7..ddf3fdf521a6c4 100644 --- a/src/native/corehost/fxr/fx_resolver.messages.cpp +++ b/src/native/corehost/fxr/fx_resolver.messages.cpp @@ -93,21 +93,22 @@ void fx_resolver_t::display_missing_framework_error( const pal::string_t& fx_name, const pal::string_t& fx_version, const pal::string_t& fx_dir, - const pal::string_t& dotnet_root) + const pal::string_t& dotnet_root, + bool disable_multilevel_lookup) { std::vector framework_infos; pal::string_t fx_ver_dirs; if (fx_dir.length()) { fx_ver_dirs = fx_dir; - framework_info::get_all_framework_infos(get_directory(fx_dir), fx_name, &framework_infos); + framework_info::get_all_framework_infos(get_directory(fx_dir), fx_name, disable_multilevel_lookup, &framework_infos); } else { fx_ver_dirs = dotnet_root; } - framework_info::get_all_framework_infos(dotnet_root, fx_name, &framework_infos); + framework_info::get_all_framework_infos(dotnet_root, fx_name, disable_multilevel_lookup, &framework_infos); // Display the error message about missing FX. if (fx_version.length()) diff --git a/src/native/corehost/fxr/hostfxr.cpp b/src/native/corehost/fxr/hostfxr.cpp index 4011dbc4397856..8790083e9885d0 100644 --- a/src/native/corehost/fxr/hostfxr.cpp +++ b/src/native/corehost/fxr/hostfxr.cpp @@ -425,7 +425,7 @@ SHARED_API int32_t HOSTFXR_CALLTYPE hostfxr_get_dotnet_environment_info( } std::vector framework_infos; - framework_info::get_all_framework_infos(dotnet_dir, _X(""), &framework_infos); + framework_info::get_all_framework_infos(dotnet_dir, _X(""), /*disable_multilevel_lookup*/ true, &framework_infos); std::vector environment_framework_infos; std::vector framework_versions; diff --git a/src/native/corehost/fxr/sdk_info.cpp b/src/native/corehost/fxr/sdk_info.cpp index 1c2f37a7d079c2..af52b41c5690b0 100644 --- a/src/native/corehost/fxr/sdk_info.cpp +++ b/src/native/corehost/fxr/sdk_info.cpp @@ -45,7 +45,7 @@ void sdk_info::get_all_sdk_infos( std::vector* sdk_infos) { std::vector hive_dir; - get_framework_and_sdk_locations(own_dir, &hive_dir); + get_framework_and_sdk_locations(own_dir, /*disable_multilevel_lookup*/ true, &hive_dir); int32_t hive_depth = 0; diff --git a/src/native/corehost/fxr/sdk_resolver.cpp b/src/native/corehost/fxr/sdk_resolver.cpp index abc47c7f17c39d..af91bb831fbdc2 100644 --- a/src/native/corehost/fxr/sdk_resolver.cpp +++ b/src/native/corehost/fxr/sdk_resolver.cpp @@ -61,7 +61,7 @@ pal::string_t sdk_resolver::resolve(const pal::string_t& dotnet_root, bool print fx_ver_t resolved_version; vector locations; - get_framework_and_sdk_locations(dotnet_root, &locations); + get_framework_and_sdk_locations(dotnet_root, /*disable_multilevel_lookup*/ true, &locations); for (auto&& dir : locations) { diff --git a/src/native/corehost/hostmisc/utils.cpp b/src/native/corehost/hostmisc/utils.cpp index 17d2660d9be7a1..35865a5d1ec582 100644 --- a/src/native/corehost/hostmisc/utils.cpp +++ b/src/native/corehost/hostmisc/utils.cpp @@ -284,9 +284,9 @@ bool multilevel_lookup_enabled() return multilevel_lookup; } -void get_framework_and_sdk_locations(const pal::string_t& dotnet_dir, std::vector* locations) +void get_framework_and_sdk_locations(const pal::string_t& dotnet_dir, const bool disable_multilevel_lookup, std::vector* locations) { - bool multilevel_lookup = multilevel_lookup_enabled(); + bool multilevel_lookup = disable_multilevel_lookup ? false : multilevel_lookup_enabled(); // Multi-level lookup will look for the most appropriate version in several locations // by following the priority rank below: diff --git a/src/native/corehost/hostmisc/utils.h b/src/native/corehost/hostmisc/utils.h index b4e31b599fac1a..dd8d1a32abb8c2 100644 --- a/src/native/corehost/hostmisc/utils.h +++ b/src/native/corehost/hostmisc/utils.h @@ -39,7 +39,7 @@ pal::string_t get_current_runtime_id(bool use_fallback); bool get_env_shared_store_dirs(std::vector* dirs, const pal::string_t& arch, const pal::string_t& tfm); bool get_global_shared_store_dirs(std::vector* dirs, const pal::string_t& arch, const pal::string_t& tfm); bool multilevel_lookup_enabled(); -void get_framework_and_sdk_locations(const pal::string_t& dotnet_dir, std::vector* locations); +void get_framework_and_sdk_locations(const pal::string_t& dotnet_dir, const bool disable_multilevel_lookup, std::vector* locations); bool get_file_path_from_env(const pal::char_t* env_key, pal::string_t* recv); size_t index_of_non_numeric(const pal::string_t& str, size_t i); bool try_stou(const pal::string_t& str, unsigned* num); diff --git a/src/native/corehost/runtime_config.cpp b/src/native/corehost/runtime_config.cpp index a6bb064ba7151d..9154b24481cad7 100644 --- a/src/native/corehost/runtime_config.cpp +++ b/src/native/corehost/runtime_config.cpp @@ -429,6 +429,46 @@ const pal::string_t& runtime_config_t::get_tfm() const return m_tfm; } +const int runtime_config_t::get_compat_major_version_from_tfm() const +{ + assert(m_valid); + + // TFM is in form + // - netcoreapp#.# for <= 3.1 + // - net#.# for >= 5.0 + // In theory it could contain a suffix like `net6.0-windows` (or more than one) + // or it may lack the minor version like `net6`. SDK will normalize this, but the runtime should not 100% rely on it + + if (m_tfm.empty()) + return runtime_config_t::unknown_version; + + int majorVersionStartIndex; + if (m_tfm.rfind(_X("netcoreapp"), 0) == 0) // this is StartsWith("netcoreapp") in std speak + { + majorVersionStartIndex = 10; // "netcoreapp".Length + } + else + { + majorVersionStartIndex = 3; // "net".Length + } + + if (majorVersionStartIndex >= m_tfm.length()) + return runtime_config_t::unknown_version; + + int majorVersionEndIndex = index_of_non_numeric (m_tfm, majorVersionStartIndex); + if (majorVersionEndIndex == pal::string_t::npos || majorVersionEndIndex == majorVersionStartIndex) + return runtime_config_t::unknown_version; + + return std::stoul(m_tfm.substr(majorVersionStartIndex, majorVersionEndIndex - majorVersionStartIndex)); +} + +bool runtime_config_t::get_is_multilevel_lookup_disabled() const +{ + // Starting with .NET 7 multi-level lookup is fully disabled + int compat_major_version = get_compat_major_version_from_tfm(); + return (compat_major_version >= 7 || compat_major_version == runtime_config_t::unknown_version); +} + bool runtime_config_t::get_is_framework_dependent() const { return m_is_framework_dependent; diff --git a/src/native/corehost/runtime_config.h b/src/native/corehost/runtime_config.h index f093dda3602e18..8457d146523709 100644 --- a/src/native/corehost/runtime_config.h +++ b/src/native/corehost/runtime_config.h @@ -33,6 +33,8 @@ class runtime_config_t const pal::string_t& get_path() const { return m_path; } const pal::string_t& get_dev_path() const { return m_dev_path; } const pal::string_t& get_tfm() const; + const int get_compat_major_version_from_tfm() const; + bool get_is_multilevel_lookup_disabled() const; const std::list& get_probe_paths() const; bool get_is_framework_dependent() const; bool parse_opts(const json_parser_t::value_t& opts); @@ -41,6 +43,8 @@ class runtime_config_t const fx_reference_vector_t& get_included_frameworks() const { return m_included_frameworks; } void set_fx_version(pal::string_t version); + static constexpr int unknown_version = std::numeric_limits::max(); + private: bool ensure_parsed(); //todo: const runtime_config_t* defaults bool ensure_dev_config_parsed(); From b6930f1ef38b74bacd41718d33ca62857ebbeadc Mon Sep 17 00:00:00 2001 From: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Date: Tue, 22 Feb 2022 08:20:11 -0800 Subject: [PATCH 07/20] Improve formatting of host test errors 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. --- .../DependencyResolutionCommandResultExtensions.cs | 1 + .../tests/TestUtils/Assertions/CommandResultAssertions.cs | 1 + .../tests/TestUtils/Assertions/DirectoryInfoAssertions.cs | 1 + 3 files changed, 3 insertions(+) diff --git a/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs b/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs index 149eead4c7ac7f..f2ab034a94a91f 100644 --- a/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs +++ b/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.IO; +using TestUtils.Assertions; namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.DependencyResolution { diff --git a/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs b/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs index 305f7aca468355..20b0e34967069e 100644 --- a/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs +++ b/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs @@ -6,6 +6,7 @@ using FluentAssertions; using FluentAssertions.Execution; using Microsoft.DotNet.Cli.Build.Framework; +using TestUtils.Assertions; namespace Microsoft.DotNet.CoreSetup.Test { diff --git a/src/installer/tests/TestUtils/Assertions/DirectoryInfoAssertions.cs b/src/installer/tests/TestUtils/Assertions/DirectoryInfoAssertions.cs index d4915024c00c21..1fa3049e12a033 100644 --- a/src/installer/tests/TestUtils/Assertions/DirectoryInfoAssertions.cs +++ b/src/installer/tests/TestUtils/Assertions/DirectoryInfoAssertions.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using TestUtils.Assertions; namespace Microsoft.DotNet.CoreSetup.Test { From 3faf7ae1a58e4041afbfc8fbd830cef928ea7613 Mon Sep 17 00:00:00 2001 From: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Date: Tue, 22 Feb 2022 10:50:06 -0800 Subject: [PATCH 08/20] Fixes --- .../DependencyResolutionCommandResultExtensions.cs | 1 - .../tests/TestUtils/Assertions/CommandResultAssertions.cs | 1 - .../tests/TestUtils/Assertions/DirectoryInfoAssertions.cs | 1 - 3 files changed, 3 deletions(-) diff --git a/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs b/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs index f2ab034a94a91f..149eead4c7ac7f 100644 --- a/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs +++ b/src/installer/tests/HostActivation.Tests/DependencyResolution/DependencyResolutionCommandResultExtensions.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; using System.IO; -using TestUtils.Assertions; namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.DependencyResolution { diff --git a/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs b/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs index 20b0e34967069e..305f7aca468355 100644 --- a/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs +++ b/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs @@ -6,7 +6,6 @@ using FluentAssertions; using FluentAssertions.Execution; using Microsoft.DotNet.Cli.Build.Framework; -using TestUtils.Assertions; namespace Microsoft.DotNet.CoreSetup.Test { diff --git a/src/installer/tests/TestUtils/Assertions/DirectoryInfoAssertions.cs b/src/installer/tests/TestUtils/Assertions/DirectoryInfoAssertions.cs index 1fa3049e12a033..d4915024c00c21 100644 --- a/src/installer/tests/TestUtils/Assertions/DirectoryInfoAssertions.cs +++ b/src/installer/tests/TestUtils/Assertions/DirectoryInfoAssertions.cs @@ -7,7 +7,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using TestUtils.Assertions; namespace Microsoft.DotNet.CoreSetup.Test { From 5ee6e7fce2966e11509041b811530910284f4bdd Mon Sep 17 00:00:00 2001 From: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Date: Thu, 17 Mar 2022 13:26:40 -0700 Subject: [PATCH 09/20] Update framework resolution error message Update tests to match the new MLL behavior --- .../FrameworkResolution/MultipleHives.cs | 25 ++-- .../MultilevelSDKLookup.cs | 64 ++++------ .../HostActivation.Tests/NativeHostApis.cs | 118 +++--------------- .../corehost/fxr/fx_resolver.messages.cpp | 5 +- 4 files changed, 57 insertions(+), 155 deletions(-) diff --git a/src/installer/tests/HostActivation.Tests/FrameworkResolution/MultipleHives.cs b/src/installer/tests/HostActivation.Tests/FrameworkResolution/MultipleHives.cs index c92c636327cc29..6cf5cf5ed42ff4 100644 --- a/src/installer/tests/HostActivation.Tests/FrameworkResolution/MultipleHives.cs +++ b/src/installer/tests/HostActivation.Tests/FrameworkResolution/MultipleHives.cs @@ -82,7 +82,7 @@ public void FrameworkHiveSelection_CurrentDirectoryIsIgnored() [InlineData("6.1.4", "net6.0", true, "6.1.4", true)] [InlineData("6.1.4", "net6.0", null, "6.1.4", true)] [InlineData("6.1.4", "net6.0", false, ResolvedFramework.NotFound, false)] - [InlineData("6.1.4", "net7.0", true, "6.1.4", true)] + [InlineData("6.1.4", "net7.0", true, ResolvedFramework.NotFound, false)] // MLL disabled for net7.0 [InlineData("7.1.2", "net6.0", true, "7.1.2", false)] // 7.1.2 is in both main and global hives - the main should always win with exact match [InlineData("7.1.2", "net6.0", null, "7.1.2", false)] [InlineData("7.1.2", "net6.0", false, "7.1.2", false)] @@ -154,7 +154,7 @@ public void ListRuntimes(bool? multiLevelLookup) string expectedOutput = string.Join( string.Empty, - GetExpectedFrameworks(multiLevelLookup) + GetExpectedFrameworks(false) // MLL Is always disabled for dotnet --list-runtimes .Select(t => $"{MicrosoftNETCoreApp} {t.Version} [{Path.Combine(t.Path, "shared", MicrosoftNETCoreApp)}]{Environment.NewLine}")); // !!IMPORTANT!!: This test verifies the exact match of the entire output of the command (not a substring!) @@ -179,7 +179,7 @@ public void DotnetInfo(bool? multiLevelLookup) string expectedOutput = $".NET runtimes installed:{Environment.NewLine}" + string.Join(string.Empty, - GetExpectedFrameworks(multiLevelLookup) + GetExpectedFrameworks(false) // MLL is always disabled for dotnet --info .Select(t => $" {MicrosoftNETCoreApp} {t.Version} [{Path.Combine(t.Path, "shared", MicrosoftNETCoreApp)}]{Environment.NewLine}")); RunTest( @@ -190,13 +190,13 @@ public void DotnetInfo(bool? multiLevelLookup) } [Theory] - [InlineData("net5.0", true)] - [InlineData("net5.0", null)] - [InlineData("net5.0", false)] - [InlineData("net7.0", true)] - [InlineData("net7.0", null)] - [InlineData("net7.0", false)] - public void FrameworkResolutionError(string tfm, bool? multiLevelLookup) + [InlineData("net5.0", true, true)] + [InlineData("net5.0", null, true)] + [InlineData("net5.0", false, false)] + [InlineData("net7.0", true, false)] + [InlineData("net7.0", null, false)] + [InlineData("net7.0", false, false)] + public void FrameworkResolutionError(string tfm, bool? multiLevelLookup, bool effectiveMultiLevelLookup) { // Multi-level lookup is only supported on Windows. if (!OperatingSystem.IsWindows() && multiLevelLookup != false) @@ -205,7 +205,7 @@ public void FrameworkResolutionError(string tfm, bool? multiLevelLookup) string expectedOutput = $"The following frameworks were found:{Environment.NewLine}" + string.Join(string.Empty, - GetExpectedFrameworks(multiLevelLookup) + GetExpectedFrameworks(effectiveMultiLevelLookup) .Select(t => $" {t.Version} at [{Path.Combine(t.Path, "shared", MicrosoftNETCoreApp)}]{Environment.NewLine}")); RunTest( @@ -214,7 +214,8 @@ public void FrameworkResolutionError(string tfm, bool? multiLevelLookup) .WithFramework(MicrosoftNETCoreApp, "9999.9.9"), multiLevelLookup) .Should().Fail() - .And.HaveStdErrContaining(expectedOutput); + .And.HaveStdErrContaining(expectedOutput) + .And.HaveStdErrContaining("https://aka.ms/dotnet/app-launch-failed"); } private CommandResult RunTest(Func runtimeConfig, bool? multiLevelLookup = true) diff --git a/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs b/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs index 96d7ca63d691ad..c6f6f07ca0046e 100644 --- a/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs +++ b/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs @@ -139,7 +139,7 @@ public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup() // Expected: 9999.3.5-dummy from reg dir RunTest() .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.3.5-dummy", _dotnetSdkDllMessageTerminator)); + .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.4", _dotnetSdkDllMessageTerminator)); // Add SDK versions AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.600"); @@ -151,7 +151,7 @@ public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup() // Expected: 9999.3.5-dummy from reg dir RunTest() .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.3.5-dummy", _dotnetSdkDllMessageTerminator)); + .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.4", _dotnetSdkDllMessageTerminator)); // Add SDK versions AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.4-global-dummy"); @@ -171,10 +171,8 @@ public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup() .And.HaveStdOutContaining("9999.3.4-dummy") .And.HaveStdOutContaining("9999.3.4-global-dummy") .And.HaveStdOutContaining("9999.4.1") - .And.HaveStdOutContaining("9999.3.3") .And.HaveStdOutContaining("9999.3.4") - .And.HaveStdOutContaining("9999.3.600") - .And.HaveStdOutContaining("9999.3.5-dummy"); + .And.HaveStdOutContaining("9999.3.600"); } [Fact] @@ -224,10 +222,10 @@ public void SdkMultilevelLookup_Global_Json_Two_Part_Patch_Rollup() // Cwd: empty // Exe: 9999.3.300, 9999.7.304-global-dummy // Reg: 9999.3.57, 9999.3.4-dummy, 9999.3.304 - // Expected: 9999.3.304 from reg dir + // Expected: no compatible version and specific error message RunTest() - .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.3.304", _dotnetSdkDllMessageTerminator)); + .Should().Fail() + .And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version"); // Add SDK versions AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.399", "9999.3.399-dummy", "9999.3.400"); @@ -264,22 +262,18 @@ public void SdkMultilevelLookup_Global_Json_Two_Part_Patch_Rollup() // Expected: 9999.3.304-global-dummy from reg dir RunTest() .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.3.304-global-dummy", _dotnetSdkDllMessageTerminator)); + .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.399", _dotnetSdkDllMessageTerminator)); // Verify we have the expected SDK versions RunTest("--list-sdks") .Should().Pass() - .And.HaveStdOutContaining("9999.3.57") - .And.HaveStdOutContaining("9999.3.4-dummy") .And.HaveStdOutContaining("9999.3.300") .And.HaveStdOutContaining("9999.7.304-global-dummy") .And.HaveStdOutContaining("9999.3.399") .And.HaveStdOutContaining("9999.3.399-dummy") .And.HaveStdOutContaining("9999.3.400") .And.HaveStdOutContaining("9999.3.2400") - .And.HaveStdOutContaining("9999.3.3004") - .And.HaveStdOutContaining("9999.3.304") - .And.HaveStdOutContaining("9999.3.304-global-dummy"); + .And.HaveStdOutContaining("9999.3.3004"); } [Fact] @@ -295,10 +289,10 @@ public void SdkMultilevelLookup_Precedential_Order() // Cwd: empty // Exe: empty // Reg: 9999.0.4 - // Expected: 9999.0.4 from reg dir + // Expected: no compatible version and a specific error message RunTest() - .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.4", _dotnetSdkDllMessageTerminator)); + .Should().Fail() + .And.HaveStdErrContaining("compatible .NET SDK is not installed"); // Add SDK versions AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.4"); @@ -348,8 +342,8 @@ public void SdkMultilevelLookup_RegistryAccess() .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride) .EnableTracingAndCaptureOutputs() .Execute() - .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.4", _dotnetSdkDllMessageTerminator)); + .Should().Fail() + .And.HaveStdErrContaining("compatible .NET SDK is not installed"); } } @@ -366,10 +360,10 @@ public void SdkMultilevelLookup_Must_Pick_The_Highest_Semantic_Version() // Cwd: empty // Exe: empty // Reg: 9999.0.0, 9999.0.3-dummy - // Expected: 9999.0.3-dummy from reg dir + // Expected: no compatible version and a specific error message RunTest() - .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.3-dummy", _dotnetSdkDllMessageTerminator)); + .Should().Fail() + .And.HaveStdErrContaining("compatible .NET SDK is not installed"); // Add SDK versions AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.3"); @@ -394,7 +388,7 @@ public void SdkMultilevelLookup_Must_Pick_The_Highest_Semantic_Version() // Expected: 9999.0.100 from reg dir RunTest() .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.100", _dotnetSdkDllMessageTerminator)); + .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.3", _dotnetSdkDllMessageTerminator)); // Add SDK versions AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.80"); @@ -406,7 +400,7 @@ public void SdkMultilevelLookup_Must_Pick_The_Highest_Semantic_Version() // Expected: 9999.0.100 from reg dir RunTest() .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.100", _dotnetSdkDllMessageTerminator)); + .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.80", _dotnetSdkDllMessageTerminator)); // Add SDK versions AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.5500000"); @@ -430,21 +424,17 @@ public void SdkMultilevelLookup_Must_Pick_The_Highest_Semantic_Version() // Expected: 9999.0.52000000 from reg dir RunTest() .Should().Pass() - .And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.52000000", _dotnetSdkDllMessageTerminator)); + .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.5500000", _dotnetSdkDllMessageTerminator)); // Verify we have the expected SDK versions RunTest("--list-sdks") .Should().Pass() - .And.HaveStdOutContaining("9999.0.0") - .And.HaveStdOutContaining("9999.0.3-dummy") .And.HaveStdOutContaining("9999.0.3") - .And.HaveStdOutContaining("9999.0.100") .And.HaveStdOutContaining("9999.0.80") - .And.HaveStdOutContaining("9999.0.5500000") - .And.HaveStdOutContaining("9999.0.52000000"); + .And.HaveStdOutContaining("9999.0.5500000"); } - private List<(string version, string rootPath)> AddSdkVersionsAndGetExpectedList(bool? multiLevelLookup) + private List<(string version, string rootPath)> AddSdkVersionsAndGetExpectedList() { AddAvailableSdkVersions(_exeSdkBaseDir, "5.0.2"); AddAvailableSdkVersions(_exeSdkBaseDir, "6.1.1"); @@ -457,11 +447,7 @@ public void SdkMultilevelLookup_Must_Pick_The_Highest_Semantic_Version() expectedList.Add(("5.0.2", _exeSdkBaseDir)); expectedList.Add(("6.1.1", _exeSdkBaseDir)); expectedList.Add(("7.1.2", _exeSdkBaseDir)); - if (multiLevelLookup is null || multiLevelLookup == true) - { - expectedList.Add(("6.2.0", _regSdkBaseDir)); - expectedList.Add(("7.0.1", _regSdkBaseDir)); - } + // MLL is always disabled for SDK resolution, so only the "exe" SDKs are listed expectedList.Sort((a, b) => { if (!Version.TryParse(a.version, out var aVersion)) @@ -485,7 +471,7 @@ public void ListSdks(bool? multiLevelLookup) if (!OperatingSystem.IsWindows() && multiLevelLookup != false) return; - var expectedList = AddSdkVersionsAndGetExpectedList(multiLevelLookup); + var expectedList = AddSdkVersionsAndGetExpectedList(); string expectedOutput = string.Join(string.Empty, expectedList.Select(t => $"{t.version} [{t.rootPath}]{Environment.NewLine}")); // !!IMPORTANT!!: This test verifies the exact match of the entire output of the command (not a substring!) @@ -509,7 +495,7 @@ public void SdkResolutionError(bool? multiLevelLookup) SetGlobalJsonVersion("SingleDigit-global.json"); // When we fail to resolve SDK version, we print out all available SDKs - var expectedList = AddSdkVersionsAndGetExpectedList(multiLevelLookup); + var expectedList = AddSdkVersionsAndGetExpectedList(); string expectedOutput = string.Join(string.Empty, expectedList.Select(t => $" {t.version} [{t.rootPath}]{Environment.NewLine}")); RunTest("help", multiLevelLookup) @@ -527,7 +513,7 @@ public void DotnetInfo(bool? multiLevelLookup) if (!OperatingSystem.IsWindows() && multiLevelLookup != false) return; - var expectedList = AddSdkVersionsAndGetExpectedList(multiLevelLookup); + var expectedList = AddSdkVersionsAndGetExpectedList(); string expectedOutput = $".NET SDKs installed:{Environment.NewLine}" + string.Join(string.Empty, expectedList.Select(t => $" {t.version} [{t.rootPath}]{Environment.NewLine}")); diff --git a/src/installer/tests/HostActivation.Tests/NativeHostApis.cs b/src/installer/tests/HostActivation.Tests/NativeHostApis.cs index d9feb540d70bce..869c264239e2f5 100644 --- a/src/installer/tests/HostActivation.Tests/NativeHostApis.cs +++ b/src/installer/tests/HostActivation.Tests/NativeHostApis.cs @@ -128,24 +128,18 @@ public SdkResolutionFixture(SharedTestState state) } [Fact] - [PlatformSpecific(TestPlatforms.Windows)] // Multi-level lookup is only supported on Windows. + [PlatformSpecific(TestPlatforms.Windows)] // The test setup only works on Windows (and MLL was Windows-only anyway) public void Hostfxr_get_available_sdks_with_multilevel_lookup() { var f = new SdkResolutionFixture(sharedTestState); - // With multi-level lookup (windows only): get local and global sdks sorted by ascending version, - // with global sdk coming before local sdk when versions are equal + // Starting with .NET 7, multi-level lookup is completely disabled for hostfxr API calls. + // This test is still valuable to validate that it is in fact disabled string expectedList = string.Join(';', new[] { Path.Combine(f.LocalSdkDir, "0.1.2"), - Path.Combine(f.ProgramFilesGlobalSdkDir, "1.2.3"), Path.Combine(f.LocalSdkDir, "1.2.3"), - Path.Combine(f.ProgramFilesGlobalSdkDir, "2.3.4-preview"), - Path.Combine(f.SelfRegisteredGlobalSdkDir, "3.0.0"), - Path.Combine(f.ProgramFilesGlobalSdkDir, "4.5.6"), Path.Combine(f.LocalSdkDir, "5.6.7-preview"), - Path.Combine(f.SelfRegisteredGlobalSdkDir, "5.6.7"), - Path.Combine(f.SelfRegisteredGlobalSdkDir, "15.1.4-preview"), }); using (TestOnlyProductBehavior.Enable(f.Dotnet.GreatestVersionHostFxrFilePath)) @@ -317,7 +311,7 @@ public void Hostfxr_get_dotnet_environment_info_dotnet_root_only() } [Fact] - [PlatformSpecific(TestPlatforms.Windows)] // Multi-level lookup is only supported on Windows. + [PlatformSpecific(TestPlatforms.Windows)] // The test setup only works on Windows (and MLL was Windows-only anyway) public void Hostfxr_get_dotnet_environment_info_with_multilevel_lookup_with_dotnet_root() { var f = new SdkResolutionFixture(sharedTestState); @@ -325,33 +319,18 @@ public void Hostfxr_get_dotnet_environment_info_with_multilevel_lookup_with_dotn { "0.1.2", "1.2.3", - "1.2.3", - "2.3.4-preview", - "3.0.0", - "4.5.6", "5.6.7-preview", - "5.6.7", - "15.1.4-preview" }); string expectedSdkPaths = string.Join(';', new[] { Path.Combine(f.LocalSdkDir, "0.1.2"), - Path.Combine(f.ProgramFilesGlobalSdkDir, "1.2.3"), Path.Combine(f.LocalSdkDir, "1.2.3"), - Path.Combine(f.ProgramFilesGlobalSdkDir, "2.3.4-preview"), - Path.Combine(f.SelfRegisteredGlobalSdkDir, "3.0.0"), - Path.Combine(f.ProgramFilesGlobalSdkDir, "4.5.6"), Path.Combine(f.LocalSdkDir, "5.6.7-preview"), - Path.Combine(f.SelfRegisteredGlobalSdkDir, "5.6.7"), - Path.Combine(f.SelfRegisteredGlobalSdkDir, "15.1.4-preview"), }); string expectedFrameworkNames = string.Join(';', new[] { - "HostFxr.Test.A", - "HostFxr.Test.A", - "HostFxr.Test.B", "HostFxr.Test.B", "HostFxr.Test.B", "HostFxr.Test.C" @@ -359,20 +338,14 @@ public void Hostfxr_get_dotnet_environment_info_with_multilevel_lookup_with_dotn string expectedFrameworkVersions = string.Join(';', new[] { - "1.2.3", - "3.0.0", "4.0.0", "5.6.7-A", - "5.6.7-A", "3.0.0" }); string expectedFrameworkPaths = string.Join(';', new[] { - Path.Combine(f.ProgramFilesGlobalFrameworksDir, "HostFxr.Test.A"), - Path.Combine(f.ProgramFilesGlobalFrameworksDir, "HostFxr.Test.A"), Path.Combine(f.LocalFrameworksDir, "HostFxr.Test.B"), - Path.Combine(f.ProgramFilesGlobalFrameworksDir, "HostFxr.Test.B"), Path.Combine(f.LocalFrameworksDir, "HostFxr.Test.B"), Path.Combine(f.LocalFrameworksDir, "HostFxr.Test.C") }); @@ -396,51 +369,13 @@ public void Hostfxr_get_dotnet_environment_info_with_multilevel_lookup_with_dotn } [Fact] - [PlatformSpecific(TestPlatforms.Windows)] // Multi-level lookup is only supported on Windows. + [PlatformSpecific(TestPlatforms.Windows)] // The test setup only works on Windows (and MLL was Windows-only anyway) public void Hostfxr_get_dotnet_environment_info_with_multilevel_lookup_only() { var f = new SdkResolutionFixture(sharedTestState); - string expectedSdkVersions = string.Join(';', new[] - { - "1.2.3", - "2.3.4-preview", - "3.0.0", - "4.5.6", - "5.6.7", - "15.1.4-preview" - }); - - string expectedSdkPaths = string.Join(';', new[] - { - Path.Combine(f.ProgramFilesGlobalSdkDir, "1.2.3"), - Path.Combine(f.ProgramFilesGlobalSdkDir, "2.3.4-preview"), - Path.Combine(f.SelfRegisteredGlobalSdkDir, "3.0.0"), - Path.Combine(f.ProgramFilesGlobalSdkDir, "4.5.6"), - Path.Combine(f.SelfRegisteredGlobalSdkDir, "5.6.7"), - Path.Combine(f.SelfRegisteredGlobalSdkDir, "15.1.4-preview"), - }); - - string expectedFrameworkNames = string.Join(';', new[] - { - "HostFxr.Test.A", - "HostFxr.Test.A", - "HostFxr.Test.B", - }); - - string expectedFrameworkVersions = string.Join(';', new[] - { - "1.2.3", - "3.0.0", - "5.6.7-A", - }); - - string expectedFrameworkPaths = string.Join(';', new[] - { - Path.Combine(f.ProgramFilesGlobalFrameworksDir, "HostFxr.Test.A"), - Path.Combine(f.ProgramFilesGlobalFrameworksDir, "HostFxr.Test.A"), - Path.Combine(f.ProgramFilesGlobalFrameworksDir, "HostFxr.Test.B"), - }); + // Multi-level lookup is completely disabled on 7+ + // The test runs the API with the dotnet root directory set to a location which doesn't have any SDKs or frameworks using (TestOnlyProductBehavior.Enable(f.Dotnet.GreatestVersionHostFxrFilePath)) { // We pass f.WorkingDir so that we don't resolve dotnet_dir to the global installation @@ -453,41 +388,20 @@ public void Hostfxr_get_dotnet_environment_info_with_multilevel_lookup_only() .Execute() .Should().Pass() .And.HaveStdOutContaining("hostfxr_get_dotnet_environment_info:Success") - .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info sdk versions:[{expectedSdkVersions}]") - .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info sdk paths:[{expectedSdkPaths}]") - .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework names:[{expectedFrameworkNames}]") - .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework versions:[{expectedFrameworkVersions}]") - .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework paths:[{expectedFrameworkPaths}]"); + .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info sdk versions:[]") + .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info sdk paths:[]") + .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework names:[]") + .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework versions:[]") + .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework paths:[]"); } } [Fact] - [PlatformSpecific(TestPlatforms.Windows)] // Multi-level lookup is only supported on Windows. + [PlatformSpecific(TestPlatforms.Windows)] // The test setup only works on Windows (and MLL was Windows-only anyway) public void Hostfxr_get_dotnet_environment_info_with_multilevel_lookup_only_self_register_program_files() { var f = new SdkResolutionFixture(sharedTestState); - string expectedFrameworkNames = string.Join(';', new[] - { - "HostFxr.Test.A", - "HostFxr.Test.A", - "HostFxr.Test.B", - }); - - string expectedFrameworkVersions = string.Join(';', new[] - { - "1.2.3", - "3.0.0", - "5.6.7-A", - }); - - string expectedFrameworkPaths = string.Join(';', new[] - { - Path.Combine(f.ProgramFilesGlobalFrameworksDir, "HostFxr.Test.A"), - Path.Combine(f.ProgramFilesGlobalFrameworksDir, "HostFxr.Test.A"), - Path.Combine(f.ProgramFilesGlobalFrameworksDir, "HostFxr.Test.B"), - }); - using (TestOnlyProductBehavior.Enable(f.Dotnet.GreatestVersionHostFxrFilePath)) { // We pass f.WorkingDir so that we don't resolve dotnet_dir to the global installation @@ -501,9 +415,9 @@ public void Hostfxr_get_dotnet_environment_info_with_multilevel_lookup_only_self .Execute() .Should().Pass() .And.HaveStdOutContaining("hostfxr_get_dotnet_environment_info:Success") - .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework names:[{expectedFrameworkNames}]") - .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework versions:[{expectedFrameworkVersions}]") - .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework paths:[{expectedFrameworkPaths}]"); + .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework names:[]") + .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework versions:[]") + .And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework paths:[]"); } } diff --git a/src/native/corehost/fxr/fx_resolver.messages.cpp b/src/native/corehost/fxr/fx_resolver.messages.cpp index ddf3fdf521a6c4..b7f01eb264b3c0 100644 --- a/src/native/corehost/fxr/fx_resolver.messages.cpp +++ b/src/native/corehost/fxr/fx_resolver.messages.cpp @@ -135,9 +135,10 @@ void fx_resolver_t::display_missing_framework_error( pal::string_t url = get_download_url(fx_name.c_str(), fx_version.c_str()); trace::error(_X("")); - trace::error(_X("You can resolve the problem by installing the specified framework and/or SDK.")); + trace::error(_X("Learn about framework resolution:")); + trace::error(_X("https://aka.ms/dotnet/app-launch-failed")); trace::error(_X("")); - trace::error(_X("The specified framework can be found at:")); + trace::error(_X("Download framework:")); trace::error(_X(" - %s"), url.c_str()); } From 59007259f15c6fad1346c648bf9e6d826c7344c8 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Mon, 21 Mar 2022 13:35:39 -0700 Subject: [PATCH 10/20] Update dotnet --info message --- src/native/corehost/fxr/command_line.cpp | 14 +++++++++----- src/native/corehost/fxr/fx_muxer.cpp | 7 +++++++ src/native/corehost/hostmisc/utils.h | 4 ++++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/native/corehost/fxr/command_line.cpp b/src/native/corehost/fxr/command_line.cpp index deb80b38b7f522..4912c29d60509b 100644 --- a/src/native/corehost/fxr/command_line.cpp +++ b/src/native/corehost/fxr/command_line.cpp @@ -282,11 +282,12 @@ int command_line::parse_args_for_sdk_command( void command_line::print_muxer_info(const pal::string_t &dotnet_root) { trace::println(); - trace::println(_X("Host (useful for support):")); - trace::println(_X(" Version: %s"), _STRINGIFY(HOST_FXR_PKG_VER)); + trace::println(_X("Host:")); + trace::println(_X(" Version: %s"), _STRINGIFY(HOST_FXR_PKG_VER)); + trace::println(_X(" Architecture: %s"), get_arch()); pal::string_t commit = _STRINGIFY(REPO_COMMIT_HASH); - trace::println(_X(" Commit: %s"), commit.substr(0, 10).c_str()); + trace::println(_X(" Commit: %s"), commit.substr(0, 10).c_str()); trace::println(); trace::println(_X(".NET SDKs installed:")); @@ -303,9 +304,12 @@ void command_line::print_muxer_info(const pal::string_t &dotnet_root) } trace::println(); - trace::println(_X("To install additional .NET runtimes or SDKs:")); + trace::println(_X("Download .NET:")); trace::println(_X(" %s"), DOTNET_CORE_DOWNLOAD_URL); -} + + trace::println(); + trace::println(_X("Learn about .NET Runtimes and SDKs:")); + trace::println(_X(" %s"), DOTNET_INFO_URL);} void command_line::print_muxer_usage(bool is_sdk_present) { diff --git a/src/native/corehost/fxr/fx_muxer.cpp b/src/native/corehost/fxr/fx_muxer.cpp index bfacf2d6f027a8..bb5b4e375ec049 100644 --- a/src/native/corehost/fxr/fx_muxer.cpp +++ b/src/native/corehost/fxr/fx_muxer.cpp @@ -1122,6 +1122,13 @@ int fx_muxer_t::handle_cli( if (pal::strcasecmp(_X("--info"), argv[1]) == 0) { + if (!resolver.global_file_path().empty()) + { + trace::println(); + trace::println(_X("global.json file:")); + trace::println(_X(" %s"), resolver.global_file_path().c_str()); + } + command_line::print_muxer_info(host_info.dotnet_root); } diff --git a/src/native/corehost/hostmisc/utils.h b/src/native/corehost/hostmisc/utils.h index dd8d1a32abb8c2..c0e00d82228eda 100644 --- a/src/native/corehost/hostmisc/utils.h +++ b/src/native/corehost/hostmisc/utils.h @@ -19,6 +19,10 @@ #define DOTNET_CORE_DOWNLOAD_URL _X("https://aka.ms/dotnet-download") #define DOTNET_CORE_APPLAUNCH_URL _X("https://aka.ms/dotnet-core-applaunch") +#define DOTNET_INFO_URL _X("https://aka.ms/dotnet/runtimes-sdk-info") +#define DOTNET_APP_LAUNCH_FAILED_URL _X("https://aka.ms/dotnet/app-launch-failed") +#define DOTNET_SDK_NOT_FOUND_URL _X("https://aka.ms/dotnet/sdk-not-found") + #define RUNTIME_STORE_DIRECTORY_NAME _X("store") bool ends_with(const pal::string_t& value, const pal::string_t& suffix, bool match_case); From 514210f6cb25b4922b5fd2c808c934943e24e645 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Mon, 21 Mar 2022 15:09:38 -0700 Subject: [PATCH 11/20] Update framework resolution error message and tests --- ...meworkResolutionCommandResultExtensions.cs | 4 +- .../FrameworkResolution/MultipleHives.cs | 16 +++--- .../PortableAppActivation.cs | 10 ++-- .../corehost/apphost/apphost.windows.cpp | 56 +++++++++++++------ src/native/corehost/fxr/fx_resolver.cpp | 2 +- .../corehost/fxr/fx_resolver.messages.cpp | 18 +++--- src/native/corehost/fxr_resolver.cpp | 4 +- src/native/corehost/hostmisc/utils.h | 3 + 8 files changed, 74 insertions(+), 39 deletions(-) diff --git a/src/installer/tests/HostActivation.Tests/FrameworkResolution/FrameworkResolutionCommandResultExtensions.cs b/src/installer/tests/HostActivation.Tests/FrameworkResolution/FrameworkResolutionCommandResultExtensions.cs index 017a601e994910..6705068a696e64 100644 --- a/src/installer/tests/HostActivation.Tests/FrameworkResolution/FrameworkResolutionCommandResultExtensions.cs +++ b/src/installer/tests/HostActivation.Tests/FrameworkResolution/FrameworkResolutionCommandResultExtensions.cs @@ -35,7 +35,7 @@ public static AndConstraint ShouldHaveResolvedFramework /// Constraint public static AndConstraint ShouldHaveResolvedFrameworkOrFailToFind(this CommandResult result, string resolvedFrameworkName, string resolvedFrameworkVersion, string resolvedFrameworkBasePath = null) { - if (resolvedFrameworkName == null || resolvedFrameworkVersion == null || + if (resolvedFrameworkName == null || resolvedFrameworkVersion == null || resolvedFrameworkVersion == FrameworkResolutionBase.ResolvedFramework.NotFound) { return result.ShouldFailToFindCompatibleFrameworkVersion(); @@ -48,7 +48,7 @@ public static AndConstraint ShouldHaveResolvedFramework public static AndConstraint DidNotFindCompatibleFrameworkVersion(this CommandResultAssertions assertion) { - return assertion.HaveStdErrContaining("It was not possible to find any compatible framework version"); + return assertion.HaveStdErrContaining("You must install or update .NET to run this application."); } public static AndConstraint ShouldFailToFindCompatibleFrameworkVersion(this CommandResult result) diff --git a/src/installer/tests/HostActivation.Tests/FrameworkResolution/MultipleHives.cs b/src/installer/tests/HostActivation.Tests/FrameworkResolution/MultipleHives.cs index 6cf5cf5ed42ff4..52236a4cc4076d 100644 --- a/src/installer/tests/HostActivation.Tests/FrameworkResolution/MultipleHives.cs +++ b/src/installer/tests/HostActivation.Tests/FrameworkResolution/MultipleHives.cs @@ -25,7 +25,7 @@ public MultipleHives(SharedTestState sharedState) [Theory] // MLL where global hive has a better match [InlineData("5.0.0", "net5.0", true, "5.1.2")] - [InlineData("5.0.0", "net5.0", null, "5.1.2")] // MLL is on by default, so same as true + [InlineData("5.0.0", "net5.0", null, "5.1.2")] // MLL is on by default before 7.0, so same as true [InlineData("5.0.0", "net5.0", false, "5.2.0")] // No global hive allowed // MLL (where global hive has better match) with various TFMs [InlineData("5.0.0", "netcoreapp3.0", true, "5.1.2")] @@ -37,11 +37,12 @@ public MultipleHives(SharedTestState sharedState) [InlineData("5.0.0", "net6.0", true, "5.1.2")] [InlineData("5.0.0", "net6.0", null, "5.1.2")] [InlineData("5.0.0", "net6.0", false, "5.2.0")] - [InlineData("7.0.0", "net7.0", true, "7.1.2")] // MLL disabled by default - setting it doesn't change anything - [InlineData("7.0.0", "net7.0", null, "7.1.2")] // MLL disabled by default + // MLL is disabled for 7.0+ + [InlineData("7.0.0", "net7.0", true, "7.1.2")] // MLL disabled for 7.0+ - setting it doesn't change anything + [InlineData("7.0.0", "net7.0", null, "7.1.2")] [InlineData("7.0.0", "net7.0", false, "7.1.2")] - [InlineData("7.0.0", "net8.0", true, "7.1.2")] // MLL disabled by default - setting it doesn't change anything - [InlineData("7.0.0", "net8.0", null, "7.1.2")] // MLL disabled by default + [InlineData("7.0.0", "net8.0", true, "7.1.2")] // MLL disabled for 7.0+ - setting it doesn't change anything + [InlineData("7.0.0", "net8.0", null, "7.1.2")] [InlineData("7.0.0", "net8.0", false, "7.1.2")] // MLL where main hive has a better match [InlineData("6.0.0", "net6.0", true, "6.1.4")] // Global hive with better version (higher patch) @@ -82,7 +83,7 @@ public void FrameworkHiveSelection_CurrentDirectoryIsIgnored() [InlineData("6.1.4", "net6.0", true, "6.1.4", true)] [InlineData("6.1.4", "net6.0", null, "6.1.4", true)] [InlineData("6.1.4", "net6.0", false, ResolvedFramework.NotFound, false)] - [InlineData("6.1.4", "net7.0", true, ResolvedFramework.NotFound, false)] // MLL disabled for net7.0 + [InlineData("6.1.4", "net7.0", true, ResolvedFramework.NotFound, false)] // MLL disabled for 7.0+ [InlineData("7.1.2", "net6.0", true, "7.1.2", false)] // 7.1.2 is in both main and global hives - the main should always win with exact match [InlineData("7.1.2", "net6.0", null, "7.1.2", false)] [InlineData("7.1.2", "net6.0", false, "7.1.2", false)] @@ -193,6 +194,7 @@ public void DotnetInfo(bool? multiLevelLookup) [InlineData("net5.0", true, true)] [InlineData("net5.0", null, true)] [InlineData("net5.0", false, false)] + // MLL is disabled for 7.0+ [InlineData("net7.0", true, false)] [InlineData("net7.0", null, false)] [InlineData("net7.0", false, false)] @@ -206,7 +208,7 @@ public void FrameworkResolutionError(string tfm, bool? multiLevelLookup, bool ef $"The following frameworks were found:{Environment.NewLine}" + string.Join(string.Empty, GetExpectedFrameworks(effectiveMultiLevelLookup) - .Select(t => $" {t.Version} at [{Path.Combine(t.Path, "shared", MicrosoftNETCoreApp)}]{Environment.NewLine}")); + .Select(t => $" {t.Version} at [{Path.Combine(t.Path, "shared", MicrosoftNETCoreApp)}]{Environment.NewLine}")); RunTest( runtimeConfig => runtimeConfig diff --git a/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs b/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs index b0b52d901efd2c..b8fced8d26ea69 100644 --- a/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs +++ b/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs @@ -474,8 +474,8 @@ public void AppHost_CLI_FrameworkDependent_MissingRuntimeFramework_ErrorReported .BinPath; expectedErrorCode = Constants.ErrorCode.FrameworkMissingFailure; - expectedStdErr = $"The framework '{Constants.MicrosoftNETCoreApp}', " + - $"version '{sharedTestState.RepoDirectories.MicrosoftNETCoreAppVersion}' ({fixture.RepoDirProvider.BuildArchitecture}) was not found."; + expectedStdErr = $"Framework: '{Constants.MicrosoftNETCoreApp}', " + + $"version '{sharedTestState.RepoDirectories.MicrosoftNETCoreAppVersion}' ({fixture.RepoDirProvider.BuildArchitecture})"; expectedUrlQuery = $"framework={Constants.MicrosoftNETCoreApp}&framework_version={sharedTestState.RepoDirectories.MicrosoftNETCoreAppVersion}"; } @@ -487,7 +487,7 @@ public void AppHost_CLI_FrameworkDependent_MissingRuntimeFramework_ErrorReported var result = command.WaitForExit(true); result.Should().Fail() - .And.HaveStdErrContaining($"- https://aka.ms/dotnet-core-applaunch?{expectedUrlQuery}") + .And.HaveStdErrContaining($"https://aka.ms/dotnet-core-applaunch?{expectedUrlQuery}") .And.HaveStdErrContaining(expectedStdErr); // Some Unix systems will have 8 bit exit codes. @@ -529,11 +529,13 @@ public void AppHost_GUI_FrameworkDependent_MissingRuntimeFramework_ErrorReported WindowsUtils.WaitForPopupFromProcess(command.Process); command.Process.Kill(); + string expectedMissingFramework = $"'{Constants.MicrosoftNETCoreApp}', version '{sharedTestState.RepoDirectories.MicrosoftNETCoreAppVersion}' ({fixture.RepoDirProvider.BuildArchitecture})"; var result = command.WaitForExit(true) .Should().Fail() .And.HaveStdErrContaining($"Showing error dialog for application: '{Path.GetFileName(appExe)}' - error code: 0x{expectedErrorCode}") .And.HaveStdErrContaining($"url: 'https://aka.ms/dotnet-core-applaunch?{expectedUrlQuery}") - .And.HaveStdErrContaining("&gui=true"); + .And.HaveStdErrContaining("&gui=true") + .And.HaveStdErrMatching($"dialog message: (?>.|\\s)*{System.Text.RegularExpressions.Regex.Escape(expectedMissingFramework)}"); } } diff --git a/src/native/corehost/apphost/apphost.windows.cpp b/src/native/corehost/apphost/apphost.windows.cpp index 7a3a94a960b398..26c7de9e729c87 100644 --- a/src/native/corehost/apphost/apphost.windows.cpp +++ b/src/native/corehost/apphost/apphost.windows.cpp @@ -50,6 +50,26 @@ namespace ::DeregisterEventSource(eventSource); } + bool try_get_url_from_line(const pal::string_t& line, pal::string_t& url) + { + const pal::string_t url_prefix = DOTNET_CORE_APPLAUNCH_URL _X("?"); + if (starts_with(line, url_prefix, true)) + { + url.assign(line); + return true; + } + + const pal::string_t url_prefix_before_7_0 = _X(" - ") DOTNET_CORE_APPLAUNCH_URL _X("?"); + if (starts_with(line, url_prefix_before_7_0, true)) + { + size_t offset = url_prefix_before_7_0.length() - pal::strlen(DOTNET_CORE_APPLAUNCH_URL) - 1; + url.assign(line.substr(offset, line.length() - offset)); + return true; + } + + return false; + } + void show_error_dialog(const pal::char_t *executable_name, int error_code) { pal::string_t gui_errors_disabled; @@ -58,17 +78,15 @@ namespace pal::string_t dialogMsg; pal::string_t url; - const pal::string_t url_prefix = _X(" - ") DOTNET_CORE_APPLAUNCH_URL _X("?"); if (error_code == StatusCode::CoreHostLibMissingFailure) { dialogMsg = pal::string_t(_X("To run this application, you must install .NET Desktop Runtime ")) + _STRINGIFY(COMMON_HOST_PKG_VER) + _X(" (") + get_arch() + _X(").\n\n"); pal::string_t line; pal::stringstream_t ss(g_buffered_errors); - while (std::getline(ss, line, _X('\n'))) { - if (starts_with(line, url_prefix, true)) + while (std::getline(ss, line, _X('\n'))) + { + if (try_get_url_from_line(line, url)) { - size_t offset = url_prefix.length() - pal::strlen(DOTNET_CORE_APPLAUNCH_URL) - 1; - url = line.substr(offset, line.length() - offset); break; } } @@ -77,14 +95,17 @@ namespace { // We don't have a great way of passing out different kinds of detailed error info across components, so // just match the expected error string. See fx_resolver.messages.cpp. - dialogMsg = pal::string_t(_X("To run this application, you must install missing frameworks for .NET.\n\n")); + dialogMsg = pal::string_t(INSTALL_OR_UPDATE_NET_ERROR_MESSAGE _X("\n\n")); pal::string_t line; pal::stringstream_t ss(g_buffered_errors); - while (std::getline(ss, line, _X('\n'))){ - const pal::string_t prefix = _X("The framework '"); - const pal::string_t suffix = _X("' was not found."); + while (std::getline(ss, line, _X('\n'))) + { + const pal::string_t prefix = _X("Framework: '"); + const pal::string_t prefix_before_7_0 = _X("The framework '"); + const pal::string_t suffix_before_7_0 = _X(" was not found."); const pal::string_t custom_prefix = _X(" _ "); - if (starts_with(line, prefix, true) && ends_with(line, suffix, true)) + if (starts_with(line, prefix, true) + || (starts_with(line, prefix_before_7_0, true) && ends_with(line, suffix_before_7_0, true))) { dialogMsg.append(line); dialogMsg.append(_X("\n\n")); @@ -95,10 +116,8 @@ namespace dialogMsg.append(line.substr(custom_prefix.length())); dialogMsg.append(_X("\n\n")); } - else if (starts_with(line, url_prefix, true)) + else if (try_get_url_from_line(line, url)) { - size_t offset = url_prefix.length() - pal::strlen(DOTNET_CORE_APPLAUNCH_URL) - 1; - url = line.substr(offset, line.length() - offset); break; } } @@ -107,7 +126,8 @@ namespace { pal::string_t line; pal::stringstream_t ss(g_buffered_errors); - while (std::getline(ss, line, _X('\n'))) { + while (std::getline(ss, line, _X('\n'))) + { if (starts_with(line, _X("Bundle header version compatibility check failed."), true)) { dialogMsg = pal::string_t(_X("To run this application, you must install .NET Desktop Runtime ")) + _STRINGIFY(COMMON_HOST_PKG_VER) + _X(" (") + get_arch() + _X(").\n\n"); @@ -121,15 +141,19 @@ namespace return; } else + { return; + } - dialogMsg.append(_X("Would you like to download it now?")); + dialogMsg.append(_X("Would you like to download it now?\n\n")); + dialogMsg.append(_X("Learn about framework resolution:\n")); + dialogMsg.append(DOTNET_APP_LAUNCH_FAILED_URL); assert(url.length() > 0); assert(is_gui_application()); url.append(_X("&gui=true")); - trace::verbose(_X("Showing error dialog for application: '%s' - error code: 0x%x - url: '%s'"), executable_name, error_code, url.c_str()); + trace::verbose(_X("Showing error dialog for application: '%s' - error code: 0x%x - url: '%s' - dialog message: %s"), executable_name, error_code, url.c_str(), dialogMsg.c_str()); if (::MessageBoxW(nullptr, dialogMsg.c_str(), executable_name, MB_ICONERROR | MB_YESNO) == IDYES) { // Open the URL in default browser diff --git a/src/native/corehost/fxr/fx_resolver.cpp b/src/native/corehost/fxr/fx_resolver.cpp index c79602da3a9142..d5e25d6b418f69 100644 --- a/src/native/corehost/fxr/fx_resolver.cpp +++ b/src/native/corehost/fxr/fx_resolver.cpp @@ -287,7 +287,6 @@ namespace if (selected_fx_dir.empty()) { - trace::error(_X("It was not possible to find any compatible framework version")); return nullptr; } @@ -444,6 +443,7 @@ StatusCode fx_resolver_t::read_framework( fx_definition_t* fx = resolve_framework_reference(new_effective_fx_ref, m_oldest_fx_references[fx_name].get_fx_version(), host_info.dotnet_root, disable_multilevel_lookup); if (fx == nullptr) { + trace::error(INSTALL_OR_UPDATE_NET_ERROR_MESSAGE); display_missing_framework_error(fx_name, new_effective_fx_ref.get_fx_version(), pal::string_t(), host_info.dotnet_root, disable_multilevel_lookup); return FrameworkMissingFailure; } diff --git a/src/native/corehost/fxr/fx_resolver.messages.cpp b/src/native/corehost/fxr/fx_resolver.messages.cpp index b7f01eb264b3c0..24e4175c1779e1 100644 --- a/src/native/corehost/fxr/fx_resolver.messages.cpp +++ b/src/native/corehost/fxr/fx_resolver.messages.cpp @@ -111,35 +111,39 @@ void fx_resolver_t::display_missing_framework_error( framework_info::get_all_framework_infos(dotnet_root, fx_name, disable_multilevel_lookup, &framework_infos); // Display the error message about missing FX. + trace::error(_X("")); if (fx_version.length()) { - trace::error(_X("The framework '%s', version '%s' (%s) was not found."), fx_name.c_str(), fx_version.c_str(), get_arch()); + trace::error(_X("Framework: '%s', version '%s' (%s)"), fx_name.c_str(), fx_version.c_str(), get_arch()); } else { - trace::error(_X("The framework '%s' (%s) was not found."), fx_name.c_str(), get_arch()); + trace::error(_X("Framework: '%s', (%s)"), fx_name.c_str(), get_arch()); } + trace::error(_X("Location: %s"), dotnet_root.c_str()); + trace::error(_X("")); + if (framework_infos.size()) { - trace::error(_X(" - The following frameworks were found:")); + trace::error(_X("The following frameworks were found:")); for (const framework_info& info : framework_infos) { - trace::error(_X(" %s at [%s]"), info.version.as_str().c_str(), info.path.c_str()); + trace::error(_X(" %s at [%s]"), info.version.as_str().c_str(), info.path.c_str()); } } else { - trace::error(_X(" - No frameworks were found.")); + trace::error(_X("No frameworks were found.")); } pal::string_t url = get_download_url(fx_name.c_str(), fx_version.c_str()); trace::error(_X("")); trace::error(_X("Learn about framework resolution:")); - trace::error(_X("https://aka.ms/dotnet/app-launch-failed")); + trace::error(DOTNET_APP_LAUNCH_FAILED_URL); trace::error(_X("")); trace::error(_X("Download framework:")); - trace::error(_X(" - %s"), url.c_str()); + trace::error(url.c_str()); } void fx_resolver_t::display_incompatible_loaded_framework_error( diff --git a/src/native/corehost/fxr_resolver.cpp b/src/native/corehost/fxr_resolver.cpp index b59c59f10fd80e..ef92136e80bb5d 100644 --- a/src/native/corehost/fxr_resolver.cpp +++ b/src/native/corehost/fxr_resolver.cpp @@ -112,8 +112,8 @@ bool fxr_resolver::try_get_path(const pal::string_t& root_path, pal::string_t* o dotnet_root_env_var_name.c_str(), self_registered_message.c_str()); trace::error(_X("")); - trace::error(_X("The .NET runtime can be found at:")); - trace::error(_X(" - %s&apphost_version=%s"), get_download_url().c_str(), _STRINGIFY(COMMON_HOST_PKG_VER)); + trace::error(_X("Download the .NET runtime:")); + trace::error(_X("%s&apphost_version=%s"), get_download_url().c_str(), _STRINGIFY(COMMON_HOST_PKG_VER)); return false; } diff --git a/src/native/corehost/hostmisc/utils.h b/src/native/corehost/hostmisc/utils.h index c0e00d82228eda..125ae32f8d0953 100644 --- a/src/native/corehost/hostmisc/utils.h +++ b/src/native/corehost/hostmisc/utils.h @@ -23,6 +23,9 @@ #define DOTNET_APP_LAUNCH_FAILED_URL _X("https://aka.ms/dotnet/app-launch-failed") #define DOTNET_SDK_NOT_FOUND_URL _X("https://aka.ms/dotnet/sdk-not-found") +// This message is defined here for consistency between errors on the command line and GUI (Windows apphost). +#define INSTALL_OR_UPDATE_NET_ERROR_MESSAGE _X("You must install or update .NET to run this application.") + #define RUNTIME_STORE_DIRECTORY_NAME _X("store") bool ends_with(const pal::string_t& value, const pal::string_t& suffix, bool match_case); From 92fceb4fa488e02f55351c3f61882652a7f027e7 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Mon, 21 Mar 2022 18:51:17 -0700 Subject: [PATCH 12/20] Update SDK resolution error message --- src/native/corehost/fxr/fx_muxer.cpp | 3 +- src/native/corehost/fxr/sdk_resolver.cpp | 42 ++++++++++++++++-------- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/native/corehost/fxr/fx_muxer.cpp b/src/native/corehost/fxr/fx_muxer.cpp index bb5b4e375ec049..e571c03d966bba 100644 --- a/src/native/corehost/fxr/fx_muxer.cpp +++ b/src/native/corehost/fxr/fx_muxer.cpp @@ -1070,8 +1070,7 @@ int fx_muxer_t::handle_cli( return StatusCode::Success; } - trace::error(_X("Could not execute because the application was not found or a compatible .NET SDK is not installed.")); - trace::error(_X("Possible reasons for this include:")); + trace::error(_X("The command could not be loaded, possibly because:")); trace::error(_X(" * You intended to execute a .NET program:")); trace::error(_X(" The application '%s' does not exist."), app_candidate.c_str()); trace::error(_X(" * You intended to execute a .NET SDK command:")); diff --git a/src/native/corehost/fxr/sdk_resolver.cpp b/src/native/corehost/fxr/sdk_resolver.cpp index af91bb831fbdc2..a8be7ca856d2e1 100644 --- a/src/native/corehost/fxr/sdk_resolver.cpp +++ b/src/native/corehost/fxr/sdk_resolver.cpp @@ -85,38 +85,52 @@ pal::string_t sdk_resolver::resolve(const pal::string_t& dotnet_root, bool print return {}; } -void sdk_resolver::print_resolution_error(const pal::string_t& dotnet_root, const pal::char_t *prefix) const +void sdk_resolver::print_resolution_error(const pal::string_t& dotnet_root, const pal::char_t *main_error_prefix) const { bool sdk_exists = false; - const pal::char_t *no_sdk_message = _X("It was not possible to find any installed .NET SDKs."); + const pal::char_t *no_sdk_message = _X("No .NET SDKs were found."); if (!version.is_empty()) { + trace::error(_X("%sA compatible .NET SDK was not found."), main_error_prefix); pal::string_t requested = version.as_str(); - if (!global_file.empty()) + + trace::error(_X("")); + trace::error(_X("Requested SDK version: %s"), requested.c_str()); + bool has_global_file = !global_file.empty(); + if (has_global_file) + trace::error(_X("global.json file: %s"), global_file.c_str()); + + trace::error(_X("")); + trace::error(_X("Installed SDKs:")); + sdk_exists = sdk_info::print_all_sdks(dotnet_root, _X("")); + if (!sdk_exists) + trace::error(no_sdk_message); + + trace::error(_X("")); + if (has_global_file) { - trace::error(_X("%sA compatible installed .NET SDK for global.json version [%s] from [%s] was not found."), prefix, requested.c_str(), global_file.c_str()); - trace::error(_X("%sInstall the [%s] .NET SDK or update [%s] with an installed .NET SDK:"), prefix, requested.c_str(), global_file.c_str()); + trace::error(_X("Install the [%s] .NET SDK or update [%s] to match an installed SDK."), requested.c_str(), global_file.c_str()); } else { - trace::error(_X("%sA compatible installed .NET SDK version [%s] was not found."), prefix, requested.c_str()); - trace::error(_X("%sInstall the [%s] .NET SDK or create a global.json file with an installed .NET SDK:"), prefix, requested.c_str()); + trace::error(_X("Install the [%s] .NET SDK or create a global.json file matching an installed SDK."), requested.c_str()); } - - sdk_exists = sdk_info::print_all_sdks(dotnet_root, pal::string_t{prefix}.append(_X(" "))); - if (!sdk_exists) - trace::error(_X("%s %s"), prefix, no_sdk_message); } else { - trace::error(_X("%s%s"), prefix, no_sdk_message); + trace::error(_X("%s%s"), main_error_prefix, no_sdk_message); } if (!sdk_exists) { - trace::error(_X("%sInstall a .NET SDK from:"), prefix); - trace::error(_X("%s %s"), prefix, DOTNET_CORE_DOWNLOAD_URL); + trace::error(_X("")); + trace::error(_X("Download a .NET SDK:")); + trace::error(DOTNET_CORE_DOWNLOAD_URL); } + + trace::error(_X("")); + trace::error(_X("Learn about SDK resolution:")); + trace::error(DOTNET_SDK_NOT_FOUND_URL); } sdk_resolver sdk_resolver::from_nearest_global_file(bool allow_prerelease) From a08a0f4ed91a25b5f7b035a0b5fa595d0304d0cc Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Mon, 21 Mar 2022 20:51:07 -0700 Subject: [PATCH 13/20] Update SDK resolution tests --- .../DotnetArgValidation.cs | 2 +- .../MultilevelSDKLookup.cs | 69 +++++++++++-------- .../tests/HostActivation.Tests/SDKLookup.cs | 59 ++++++++-------- .../SDKResolutionCommandResultExtensions.cs | 43 ++++++++++++ 4 files changed, 112 insertions(+), 61 deletions(-) create mode 100644 src/installer/tests/HostActivation.Tests/SDKResolutionCommandResultExtensions.cs diff --git a/src/installer/tests/HostActivation.Tests/DotnetArgValidation.cs b/src/installer/tests/HostActivation.Tests/DotnetArgValidation.cs index bffa811215b679..f7c0bdc468fcac 100644 --- a/src/installer/tests/HostActivation.Tests/DotnetArgValidation.cs +++ b/src/installer/tests/HostActivation.Tests/DotnetArgValidation.cs @@ -79,7 +79,7 @@ public void InvalidFileOrCommand_NoSDK_ListsPossibleIssues() .Execute(fExpectedToFail: true) .Should().Fail() .And.HaveStdErrContaining($"The application '{fileName}' does not exist") - .And.HaveStdErrContaining($"It was not possible to find any installed .NET SDKs"); + .And.FindAnySdk(false); } // Return a non-exisitent path that contains a mix of / and \ diff --git a/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs b/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs index c6f6f07ca0046e..ecbd4529694d35 100644 --- a/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs +++ b/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs @@ -81,17 +81,20 @@ public void Dispose() [PlatformSpecific(TestPlatforms.Windows)] // Multi-level lookup is only supported on Windows. public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup() { + // Multi-level lookup is disabled for 7.0+, so the resolved SDK should never be from the registered directory. + // Set specified SDK version = 9999.3.4-global-dummy - SetGlobalJsonVersion("SingleDigit-global.json"); + string globalJsonPath = SetGlobalJsonVersion("SingleDigit-global.json"); + string requestedVersion = "9999.3.4-global-dummy"; // Specified SDK version: 9999.3.4-global-dummy // Cwd: empty // Exe: empty // Reg: empty - // Expected: no compatible version and a specific error messages + // Expected: no compatible version RunTest() .Should().Fail() - .And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version"); + .And.NotFindCompatibleSdk(globalJsonPath, requestedVersion); // Add SDK versions AddAvailableSdkVersions(_exeSdkBaseDir, "9999.4.1", "9999.3.4-dummy"); @@ -100,10 +103,10 @@ public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup() // Cwd: empty // Exe: 9999.4.1, 9999.3.4-dummy // Reg: empty - // Expected: no compatible version and a specific error message + // Expected: no compatible version RunTest() .Should().Fail() - .And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version"); + .And.NotFindCompatibleSdk(globalJsonPath, requestedVersion); // Add SDK versions AddAvailableSdkVersions(_regSdkBaseDir, "9999.3.3"); @@ -112,10 +115,10 @@ public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup() // Cwd: empty // Exe: 9999.4.1, 9999.3.4-dummy // Reg: 9999.3.3 - // Expected: no compatible version and a specific error message + // Expected: no compatible version RunTest() .Should().Fail() - .And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version"); + .And.NotFindCompatibleSdk(globalJsonPath, requestedVersion); // Add SDK versions AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.4"); @@ -136,7 +139,7 @@ public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup() // Cwd: empty // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.4 // Reg: 9999.3.3, 9999.3.5-dummy - // Expected: 9999.3.5-dummy from reg dir + // Expected: 9999.3.4 from exe dir RunTest() .Should().Pass() .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.4", _dotnetSdkDllMessageTerminator)); @@ -148,7 +151,7 @@ public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup() // Cwd: empty // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.4, 9999.3.600 // Reg: 9999.3.3, 9999.3.5-dummy - // Expected: 9999.3.5-dummy from reg dir + // Expected: 9999.3.4-dummy from exe dir RunTest() .Should().Pass() .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.4", _dotnetSdkDllMessageTerminator)); @@ -179,17 +182,20 @@ public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup() [PlatformSpecific(TestPlatforms.Windows)] // Multi-level lookup is only supported on Windows. public void SdkMultilevelLookup_Global_Json_Two_Part_Patch_Rollup() { + // Multi-level lookup is disabled for 7.0+, so the resolved SDK should never be from the registered directory. + // Set specified SDK version = 9999.3.304-global-dummy - SetGlobalJsonVersion("TwoPart-global.json"); + string globalJsonPath = SetGlobalJsonVersion("TwoPart-global.json"); + string requestedVersion = "9999.3.304-global-dummy"; // Specified SDK version: 9999.3.304-global-dummy // Cwd: empty // Exe: empty // Reg: empty - // Expected: no compatible version and a specific error messages + // Expected: no compatible version RunTest() .Should().Fail() - .And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version"); + .And.NotFindCompatibleSdk(globalJsonPath, requestedVersion); // Add SDK versions AddAvailableSdkVersions(_regSdkBaseDir, "9999.3.57", "9999.3.4-dummy"); @@ -198,10 +204,10 @@ public void SdkMultilevelLookup_Global_Json_Two_Part_Patch_Rollup() // Cwd: empty // Exe: empty // Reg: 9999.3.57, 9999.3.4-dummy - // Expected: no compatible version and a specific error message + // Expected: no compatible version RunTest() .Should().Fail() - .And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version"); + .And.NotFindCompatibleSdk(globalJsonPath, requestedVersion); // Add SDK versions AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.300", "9999.7.304-global-dummy"); @@ -210,10 +216,10 @@ public void SdkMultilevelLookup_Global_Json_Two_Part_Patch_Rollup() // Cwd: empty // Exe: 9999.3.300, 9999.7.304-global-dummy // Reg: 9999.3.57, 9999.3.4-dummy - // Expected: no compatible version and a specific error message + // Expected: no compatible version RunTest() .Should().Fail() - .And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version"); + .And.NotFindCompatibleSdk(globalJsonPath, requestedVersion); // Add SDK versions AddAvailableSdkVersions(_regSdkBaseDir, "9999.3.304"); @@ -222,10 +228,10 @@ public void SdkMultilevelLookup_Global_Json_Two_Part_Patch_Rollup() // Cwd: empty // Exe: 9999.3.300, 9999.7.304-global-dummy // Reg: 9999.3.57, 9999.3.4-dummy, 9999.3.304 - // Expected: no compatible version and specific error message + // Expected: no compatible version RunTest() .Should().Fail() - .And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version"); + .And.NotFindCompatibleSdk(globalJsonPath, requestedVersion); // Add SDK versions AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.399", "9999.3.399-dummy", "9999.3.400"); @@ -259,7 +265,7 @@ public void SdkMultilevelLookup_Global_Json_Two_Part_Patch_Rollup() // Cwd: empty // Exe: 9999.3.300, 9999.7.304-global-dummy, 9999.3.399, 9999.3.399-dummy, 9999.3.400, 9999.3.2400, 9999.3.3004 // Reg: 9999.3.57, 9999.3.4-dummy, 9999.3.304, 9999.3.2400, 9999.3.3004, 9999.3.304-global-dummy - // Expected: 9999.3.304-global-dummy from reg dir + // Expected: 9999.3.399 from exe dir RunTest() .Should().Pass() .And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.399", _dotnetSdkDllMessageTerminator)); @@ -289,10 +295,10 @@ public void SdkMultilevelLookup_Precedential_Order() // Cwd: empty // Exe: empty // Reg: 9999.0.4 - // Expected: no compatible version and a specific error message + // Expected: no SDKs found RunTest() .Should().Fail() - .And.HaveStdErrContaining("compatible .NET SDK is not installed"); + .And.FindAnySdk(false); // Add SDK versions AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.4"); @@ -313,12 +319,12 @@ public void SdkMultilevelLookup_RegistryAccess() { // The purpose of this test is to verify that the product uses correct code to access // the registry to extract the path to search for SDKs. - // Most of our tests rely on a shortcut which is to set _DOTNET_TEST_SDK_SELF_REGISTERED_DIR env variable + // Most of our tests rely on a shortcut which is to set _DOTNET_TEST_GLOBALLY_REGISTERED_PATH env variable // which will skip the registry reading code in the product and simply use the specified value. // This test is different since it actually runs the registry reading code. // Normally the reg key the product uses is in HKEY_LOCAL_MACHINE which is only writable as admin // so we would require the tests to run as admin to modify that key (and it may introduce races with other code running on the machine). - // So instead the tests use _DOTENT_TEST_SDK_REGISTRY_PATH env variable to point to the produce to use + // So instead the tests use _DOTENT_TEST_REGISTRY_PATH env variable to point to the produce to use // different registry key, inside the HKEY_CURRENT_USER hive which is writable without admin. // Note that the test creates a unique key (based on PID) for every run, to avoid collisions between parallel running tests. @@ -335,7 +341,7 @@ public void SdkMultilevelLookup_RegistryAccess() // Cwd: empty // Exe: empty // Reg: 9999.0.4 - // Expected: 9999.0.4 from reg dir + // Expected: no SDKs found DotNet.Exec("help") .WorkingDirectory(_currentWorkingDir) .MultilevelLookup(true) @@ -343,7 +349,7 @@ public void SdkMultilevelLookup_RegistryAccess() .EnableTracingAndCaptureOutputs() .Execute() .Should().Fail() - .And.HaveStdErrContaining("compatible .NET SDK is not installed"); + .And.FindAnySdk(false); } } @@ -360,10 +366,10 @@ public void SdkMultilevelLookup_Must_Pick_The_Highest_Semantic_Version() // Cwd: empty // Exe: empty // Reg: 9999.0.0, 9999.0.3-dummy - // Expected: no compatible version and a specific error message + // Expected: no SDKs found RunTest() .Should().Fail() - .And.HaveStdErrContaining("compatible .NET SDK is not installed"); + .And.FindAnySdk(false); // Add SDK versions AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.3"); @@ -492,14 +498,16 @@ public void SdkResolutionError(bool? multiLevelLookup) return; // Set specified SDK version = 9999.3.4-global-dummy - such SDK doesn't exist - SetGlobalJsonVersion("SingleDigit-global.json"); + string globalJsonPath = SetGlobalJsonVersion("SingleDigit-global.json"); + string requestedVersion = "9999.3.4-global-dummy"; // When we fail to resolve SDK version, we print out all available SDKs var expectedList = AddSdkVersionsAndGetExpectedList(); - string expectedOutput = string.Join(string.Empty, expectedList.Select(t => $" {t.version} [{t.rootPath}]{Environment.NewLine}")); + string expectedOutput = string.Join(string.Empty, expectedList.Select(t => $"{t.version} [{t.rootPath}]{Environment.NewLine}")); RunTest("help", multiLevelLookup) .Should().Fail() + .And.NotFindCompatibleSdk(globalJsonPath, requestedVersion) .And.HaveStdOutContaining(expectedOutput); } @@ -561,13 +569,14 @@ private void AddAvailableSdkVersions(string sdkBaseDir, params string[] availabl } // Put a global.json file in the cwd in order to specify a CLI - private void SetGlobalJsonVersion(string globalJsonFileName) + private string SetGlobalJsonVersion(string globalJsonFileName) { string destFile = Path.Combine(_currentWorkingDir, "global.json"); string srcFile = Path.Combine(RepoDirectories.TestAssetsFolder, "TestUtils", "SDKLookup", globalJsonFileName); File.Copy(srcFile, destFile, true); + return destFile; } private void WriteGlobalJson(string contents) diff --git a/src/installer/tests/HostActivation.Tests/SDKLookup.cs b/src/installer/tests/HostActivation.Tests/SDKLookup.cs index a8ec31b413cce0..12093a70e94b7f 100644 --- a/src/installer/tests/HostActivation.Tests/SDKLookup.cs +++ b/src/installer/tests/HostActivation.Tests/SDKLookup.cs @@ -39,15 +39,16 @@ public SDKLookup(SharedTestState sharedState) public void SdkLookup_Global_Json_Single_Digit_Patch_Rollup() { // Set specified SDK version = 9999.3.4-global-dummy - CopyGlobalJson("SingleDigit-global.json"); + string globalJsonPath = CopyGlobalJson("SingleDigit-global.json"); + string requestedVersion = "9999.3.4-global-dummy"; // Specified SDK version: 9999.3.4-global-dummy // Exe: empty - // Expected: no compatible version and a specific error messages + // Expected: no compatible version, no SDKs found RunTest() .Should().Fail() - .And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version") - .And.HaveStdErrContaining("It was not possible to find any installed .NET SDKs") + .And.NotFindCompatibleSdk(globalJsonPath, requestedVersion) + .And.FindAnySdk(false) .And.HaveStdErrContaining("aka.ms/dotnet-download") .And.NotHaveStdErrContaining("Checking if resolved SDK dir"); @@ -56,22 +57,22 @@ public void SdkLookup_Global_Json_Single_Digit_Patch_Rollup() // Specified SDK version: 9999.3.4-global-dummy // Exe: 9999.4.1, 9999.3.4-dummy - // Expected: no compatible version and a specific error message + // Expected: no compatible version RunTest() .Should().Fail() - .And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version") - .And.NotHaveStdErrContaining("It was not possible to find any installed .NET SDKs"); + .And.NotFindCompatibleSdk(globalJsonPath, requestedVersion) + .And.FindAnySdk(true); // Add SDK versions AddAvailableSdkVersions("9999.3.3"); // Specified SDK version: 9999.3.4-global-dummy // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.3 - // Expected: no compatible version and a specific error message + // Expected: no compatible version RunTest() .Should().Fail() - .And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version") - .And.NotHaveStdErrContaining("It was not possible to find any installed .NET SDKs"); + .And.NotFindCompatibleSdk(globalJsonPath, requestedVersion) + .And.FindAnySdk(true); // Add SDK versions AddAvailableSdkVersions("9999.3.4"); @@ -129,37 +130,38 @@ public void SdkLookup_Global_Json_Single_Digit_Patch_Rollup() public void SdkLookup_Global_Json_Two_Part_Patch_Rollup() { // Set specified SDK version = 9999.3.304-global-dummy - CopyGlobalJson("TwoPart-global.json"); + string globalJsonPath = CopyGlobalJson("TwoPart-global.json"); + string requestedVersion = "9999.3.304-global-dummy"; // Specified SDK version: 9999.3.304-global-dummy // Exe: empty - // Expected: no compatible version and a specific error messages + // Expected: no compatible version, no SDKs found RunTest() .Should().Fail() - .And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version") - .And.HaveStdErrContaining("It was not possible to find any installed .NET SDKs"); + .And.NotFindCompatibleSdk(globalJsonPath, requestedVersion) + .And.FindAnySdk(false); // Add SDK versions AddAvailableSdkVersions("9999.3.57", "9999.3.4-dummy"); // Specified SDK version: 9999.3.304-global-dummy // Exe: 9999.3.57, 9999.3.4-dummy - // Expected: no compatible version and a specific error message + // Expected: no compatible version RunTest() .Should().Fail() - .And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version") - .And.NotHaveStdErrContaining("It was not possible to find any installed .NET SDKs"); + .And.NotFindCompatibleSdk(globalJsonPath, requestedVersion) + .And.FindAnySdk(true); // Add SDK versions AddAvailableSdkVersions("9999.3.300", "9999.7.304-global-dummy"); // Specified SDK version: 9999.3.304-global-dummy // Exe: 9999.3.57, 9999.3.4-dummy, 9999.3.300, 9999.7.304-global-dummy - // Expected: no compatible version and a specific error message + // Expected: no compatible version RunTest() .Should().Fail() - .And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version") - .And.NotHaveStdErrContaining("It was not possible to find any installed .NET SDKs"); + .And.NotFindCompatibleSdk(globalJsonPath, requestedVersion) + .And.FindAnySdk(true); // Add SDK versions AddAvailableSdkVersions("9999.3.304"); @@ -227,11 +229,10 @@ public void SdkLookup_Negative_Version() // Specified SDK version: none // Exe: -1.-1.-1 - // Expected: no compatible version and a specific error messages + // Expected: no compatible version, no SDKs found RunTest() .Should().Fail() - .And.HaveStdErrContaining("It was not possible to find any installed .NET SDKs") - .And.HaveStdErrContaining("Install a .NET SDK from"); + .And.FindAnySdk(false); // Add SDK versions AddAvailableSdkVersions("9999.0.4"); @@ -391,16 +392,13 @@ public void It_rolls_forward_as_expected(string policy, string requested, bool a if (expected == null) { result - .Should() - .Fail() - .And.HaveStdErrContaining($"A compatible installed .NET SDK for global.json version [{requested}] from [{globalJson}] was not found") - .And.HaveStdErrContaining($"Install the [{requested}] .NET SDK or update [{globalJson}] with an installed .NET SDK:"); + .Should().Fail() + .And.NotFindCompatibleSdk(globalJson, requested); } else { result - .Should() - .Pass() + .Should().Pass() .And.HaveStdErrContaining($"SDK path resolved to [{Path.Combine(ExecutableDotNet.BinPath, "sdk", expected)}]"); } } @@ -1001,12 +999,13 @@ private void AddAvailableSdkVersions(params string[] availableVersions) } // Put a global.json file in the cwd in order to specify a CLI - private void CopyGlobalJson(string globalJsonFileName) + private string CopyGlobalJson(string globalJsonFileName) { string destFile = Path.Combine(SharedState.CurrentWorkingDir, "global.json"); string srcFile = Path.Combine(SharedState.TestAssetsPath, globalJsonFileName); File.Copy(srcFile, destFile, true); + return destFile; } private static string FormatGlobalJson(string version = null, string policy = null, bool? allowPrerelease = null) diff --git a/src/installer/tests/HostActivation.Tests/SDKResolutionCommandResultExtensions.cs b/src/installer/tests/HostActivation.Tests/SDKResolutionCommandResultExtensions.cs new file mode 100644 index 00000000000000..322af9abf695d8 --- /dev/null +++ b/src/installer/tests/HostActivation.Tests/SDKResolutionCommandResultExtensions.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using FluentAssertions; +using Microsoft.DotNet.Cli.Build.Framework; + +namespace Microsoft.DotNet.CoreSetup.Test.HostActivation +{ + internal static class SDKResolutionCommandResultExtensions + { + public static AndConstraint FindAnySdk(this CommandResultAssertions assertion, bool shouldFindAnySdk) + { + string noSdkMessage = "No .NET SDKs were found"; + return shouldFindAnySdk + ? assertion.NotHaveStdErrContaining(noSdkMessage) + : assertion.HaveStdErrContaining(noSdkMessage) + .And.HaveStdErrContaining("Download a .NET SDK:"); + } + + public static AndConstraint NotFindCompatibleSdk(this CommandResultAssertions assertion, string globalJsonPath = null, string requestedVersion = null) + { + var constraint = assertion.HaveStdErrContaining("compatible .NET SDK was not found"); + + if (globalJsonPath is not null) + { + constraint = constraint.And.HaveStdErrContaining($"global.json file: {globalJsonPath}"); + } + + if (requestedVersion is not null) + { + constraint = constraint.And.HaveStdErrContaining($"Requested SDK version: {requestedVersion}"); + } + + if (globalJsonPath is not null && requestedVersion is not null) + { + constraint = constraint.And.HaveStdErrContaining($"Install the [{requestedVersion}] .NET SDK or update [{globalJsonPath}] to match an installed SDK."); + } + + return constraint; + } + } +} From 7fdb99f59ede35a66b7caca76abeda411248590f Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Tue, 22 Mar 2022 16:21:56 -0700 Subject: [PATCH 14/20] Update docs to point at design doc for disabling MLL --- .../features/framework-version-resolution.md | 2 +- docs/design/features/host-components.md | 2 +- docs/design/features/host-probing.md | 2 +- ...-sharedfx-lookup.md => sharedfx-lookup.md} | 30 +++++++++++++++---- src/native/corehost/hostmisc/utils.cpp | 5 +++- src/native/corehost/runtime_config.cpp | 2 +- 6 files changed, 32 insertions(+), 11 deletions(-) rename docs/design/features/{multilevel-sharedfx-lookup.md => sharedfx-lookup.md} (91%) diff --git a/docs/design/features/framework-version-resolution.md b/docs/design/features/framework-version-resolution.md index 178f41d2328e3d..3ab1d22d1049d4 100644 --- a/docs/design/features/framework-version-resolution.md +++ b/docs/design/features/framework-version-resolution.md @@ -1,7 +1,7 @@ # Framework version resolution This document describes .NET Core 3.0 version resolution behavior when the host resolves framework references for framework dependent apps. -It's just a part of the overall framework resolution scenario described in [multilevel-sharedfx-lookup](multilevel-sharedfx-lookup.md). +It's just a part of the overall framework resolution scenario described in [sharedfx-lookup](sharedfx-lookup.md). ## Framework references Application defines its framework dependencies in its `.runtimeconfig.json` file. Each framework then defines its dependencies in its copy of `.runtimeconfig.json`. Each dependency is expressed as a framework reference. Together these form a graph. The host must resolve the references by finding the actual frameworks which are available on the machine. It must also unify references if there are multi references to the same framework. diff --git a/docs/design/features/host-components.md b/docs/design/features/host-components.md index 59669d661d39e8..6c8def6efdd028 100644 --- a/docs/design/features/host-components.md +++ b/docs/design/features/host-components.md @@ -20,7 +20,7 @@ The entry-point typically does just one thing: it finds the `hostfxr` library an ## Host FXR This library finds and resolves the runtime and all the frameworks the app needs. Then it loads the `hostpolicy` library and transfers control to it. -The host FXR library reads the `.runtimeconfig.json` of the app (and all it's dependent frameworks) and resolves the frameworks. It implements the algorithm for framework resolution as described in [SharedFX Lookup](multilevel-sharedfx-lookup.md) and in [Framework version resolution](framework-version-resolution.md). +The host FXR library reads the `.runtimeconfig.json` of the app (and all it's dependent frameworks) and resolves the frameworks. It implements the algorithm for framework resolution as described in [SharedFX Lookup](sharedfx-lookup.md) and in [Framework version resolution](framework-version-resolution.md). In most cases the latest available version of `hostfxr` is used. Self-contained apps use `hostfxr` from the app folder. diff --git a/docs/design/features/host-probing.md b/docs/design/features/host-probing.md index 38df2347406b85..febdf24932a1b0 100644 --- a/docs/design/features/host-probing.md +++ b/docs/design/features/host-probing.md @@ -48,7 +48,7 @@ The list of probing paths ordered according to their priority. First path in the If the app (or framework) has dependencies on frameworks, these frameworks are used as probing paths. The order is from the higher level framework to lower level framework. The app is considered the highest level, it direct dependencies are next and so on. For assets from frameworks, only that framework and lower level frameworks are considered. - Note: These directories come directly out of the framework resolution process. Special note on Windows where global locations are always considered even if the app is not executed via the shared `dotnet.exe`. More details can be found in [Multi-level Shared FX Lookup](multilevel-sharedfx-lookup.md). + Note: These directories come directly out of the framework resolution process. Special note on Windows where global locations are always considered even if the app is not executed via the shared `dotnet.exe`. More details can be found in [Shared FX Lookup](sharedfx-lookup.md). * Shared store paths * `$DOTNET_SHARED_STORE/|arch|/|tfm|` - The environment variable `DOTNET_SHARED_STORE` can contain multiple paths, in which case each is appended with `|arch|/|tfm|` and used as a probing path. * If the app is executed through `dotnet.exe` then path relative to the directory with the `dotnet.exe` is used diff --git a/docs/design/features/multilevel-sharedfx-lookup.md b/docs/design/features/sharedfx-lookup.md similarity index 91% rename from docs/design/features/multilevel-sharedfx-lookup.md rename to docs/design/features/sharedfx-lookup.md index b729819efe8bff..8453350f13c1a6 100644 --- a/docs/design/features/multilevel-sharedfx-lookup.md +++ b/docs/design/features/sharedfx-lookup.md @@ -1,10 +1,10 @@ -# Multi-level SharedFX Lookup +# SharedFX Lookup ## Introduction -There are two possible ways of running .NET Core Applications: through dotnet.exe or through a custom executable appname.exe. The first one is used when the user wants to run a framework-dependent app or a .NET Core command while the second one is used for self-contained applications. Both executables share exactly the same source code. +There are two main ways of running .NET Applications: through `dotnet` or through the `apphost` executables. The executable is in charge of finding and loading `hostfxr`. `hostfxr`, in turn, must find and load `hostpolicy`. It is also responsible for searching for the SDK when running .NET SDK commands. Finally, `hostpolicy` must find and load the runtime (`coreclr`). See [host components](host-components.md) for details. -The executable is in charge of finding and loading the hostfxr.dll file. The hostfxr, in turn, must find and load the hostpolicy.dll file (it’s also responsible for searching for the SDK when running .NET commands). At last the coreclr.dll file must be found and loaded by the hostpolicy. Self-contained apps are supposed to keep all its dependencies in the same location as the executable. Framework-dependent apps must have the runtime files inside predefined folders. +An application can either be [framework-dependent](https://docs.microsoft.com/dotnet/core/deploying/#publish-framework-dependent) or [self-contained](https://docs.microsoft.com/dotnet/core/deploying/#publish-self-contained). Framework-dependent apps must have the runtime files inside predefined folders. Self-contained apps are expected to have their dependencies in the same location as the executable. ## Semantic Versioning @@ -219,6 +219,16 @@ In order to compare versions of an assembly, the assemblyVersion and fileVersion ## Global locations +Global install locations are described in the [install locations design](https://github.com/dotnet/designs/blob/main/accepted/2020/install-locations.md) document. + +**.NET 7.0 and above** + +When running `dotnet`, only the executable directory will be searched and global locations are not searched. For all other [entry-point hosts](host-components.md#entry-point-hosts), if the `DOTNET_ROOT` environment variable is set, that path is searched. If the environment variable is not set, the global location as described in [install locations](https://github.com/dotnet/designs/blob/main/accepted/2020/install-locations.md) is searched. + +See [disable multi-level lookup](https://github.com/dotnet/designs/blob/main/accepted/2022/disable-multi-level-lookup-by-default.md) for more details. + +**Before .NET 7.0** + In addition to searching the executable directory, the global .NET location is also searched. The global folders may vary depending on the running operational system. They are defined as follows: Global .NET location: @@ -271,11 +281,19 @@ To make sure that the changes are working correctly, the following behavior cond ### SDK search -Like the Framework search, the SDK is searched for a compatible version. Instead of looking for it only in relation to the executable directory, it is also searched in the folders specified above by following the same priority rank. +Like the Framework search, the SDK is searched for a compatible version. + +Unlike the Framework search, the SDK search does a roll-forward for pre-release versions when the patch version changes. For example, if you install v2.0.1-pre, it will be used over v2.0.0. + +**.NET 7.0 and above** + +Only the executable directory will be searched. See [disable multi-level lookup](https://github.com/dotnet/designs/blob/main/accepted/2022/disable-multi-level-lookup-by-default.md) for more details. + +**Before .NET 7.0** + +Aside from looking for it in relation to the executable directory, it is also searched in the folders specified above by following the same priority rank. The search is conducted as follows: 1. In relation to the executable directory: search for the specified version. If it cannot be found, choose the most appropriate available version. If there’s no available version, proceed to the next step. 2. In relation to the global location: search for the specified version. If it cannot be found, choose the most appropriate available version. If there’s no available version, then we were not able to find any version folder and an error message is returned. - -Unlike the Framework search, the SDK search does a roll-forward for pre-release versions when the patch version changes. For example, if you install v2.0.1-pre, it will be used over v2.0.0. diff --git a/src/native/corehost/hostmisc/utils.cpp b/src/native/corehost/hostmisc/utils.cpp index 35865a5d1ec582..049210088e2ee3 100644 --- a/src/native/corehost/hostmisc/utils.cpp +++ b/src/native/corehost/hostmisc/utils.cpp @@ -304,8 +304,11 @@ void get_framework_and_sdk_locations(const pal::string_t& dotnet_dir, const bool locations->push_back(dotnet_dir_temp); } + if (!multilevel_lookup) + return; + std::vector global_dirs; - if (multilevel_lookup && pal::get_global_dotnet_dirs(&global_dirs)) + if (pal::get_global_dotnet_dirs(&global_dirs)) { for (pal::string_t dir : global_dirs) { diff --git a/src/native/corehost/runtime_config.cpp b/src/native/corehost/runtime_config.cpp index 9154b24481cad7..91116acaa8f7d3 100644 --- a/src/native/corehost/runtime_config.cpp +++ b/src/native/corehost/runtime_config.cpp @@ -464,7 +464,7 @@ const int runtime_config_t::get_compat_major_version_from_tfm() const bool runtime_config_t::get_is_multilevel_lookup_disabled() const { - // Starting with .NET 7 multi-level lookup is fully disabled + // Starting with .NET 7, multi-level lookup is fully disabled int compat_major_version = get_compat_major_version_from_tfm(); return (compat_major_version >= 7 || compat_major_version == runtime_config_t::unknown_version); } From 4778a666ff525620873d947587ff2504337ebe3e Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Wed, 23 Mar 2022 09:01:13 -0700 Subject: [PATCH 15/20] Fix warnings --- src/native/corehost/runtime_config.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/native/corehost/runtime_config.cpp b/src/native/corehost/runtime_config.cpp index 91116acaa8f7d3..7e54b76b726a78 100644 --- a/src/native/corehost/runtime_config.cpp +++ b/src/native/corehost/runtime_config.cpp @@ -442,7 +442,7 @@ const int runtime_config_t::get_compat_major_version_from_tfm() const if (m_tfm.empty()) return runtime_config_t::unknown_version; - int majorVersionStartIndex; + size_t majorVersionStartIndex; if (m_tfm.rfind(_X("netcoreapp"), 0) == 0) // this is StartsWith("netcoreapp") in std speak { majorVersionStartIndex = 10; // "netcoreapp".Length @@ -455,7 +455,7 @@ const int runtime_config_t::get_compat_major_version_from_tfm() const if (majorVersionStartIndex >= m_tfm.length()) return runtime_config_t::unknown_version; - int majorVersionEndIndex = index_of_non_numeric (m_tfm, majorVersionStartIndex); + size_t majorVersionEndIndex = index_of_non_numeric(m_tfm, majorVersionStartIndex); if (majorVersionEndIndex == pal::string_t::npos || majorVersionEndIndex == majorVersionStartIndex) return runtime_config_t::unknown_version; From 62175fbd12387817c15311fe5cad67af8937e094 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Wed, 23 Mar 2022 17:32:28 -0700 Subject: [PATCH 16/20] PR feedback --- .../corehost/apphost/apphost.windows.cpp | 35 ++++++++++--------- src/native/corehost/fxr/fx_muxer.cpp | 10 +++--- src/native/corehost/fxr/fx_resolver.cpp | 1 + .../corehost/fxr/fx_resolver.messages.cpp | 17 ++++----- src/native/corehost/fxr/sdk_resolver.cpp | 26 ++++++++------ src/native/corehost/hostmisc/utils.cpp | 31 ++++++++++------ src/native/corehost/hostmisc/utils.h | 25 +++++++++++++ src/native/corehost/runtime_config.cpp | 11 +++--- src/native/corehost/runtime_config.h | 2 +- 9 files changed, 101 insertions(+), 57 deletions(-) diff --git a/src/native/corehost/apphost/apphost.windows.cpp b/src/native/corehost/apphost/apphost.windows.cpp index 26c7de9e729c87..bb113d84faa3bc 100644 --- a/src/native/corehost/apphost/apphost.windows.cpp +++ b/src/native/corehost/apphost/apphost.windows.cpp @@ -52,17 +52,17 @@ namespace bool try_get_url_from_line(const pal::string_t& line, pal::string_t& url) { - const pal::string_t url_prefix = DOTNET_CORE_APPLAUNCH_URL _X("?"); - if (starts_with(line, url_prefix, true)) + const pal::char_t url_prefix[] = DOTNET_CORE_APPLAUNCH_URL _X("?"); + if (utils::starts_with(line, url_prefix, true)) { url.assign(line); return true; } - const pal::string_t url_prefix_before_7_0 = _X(" - ") DOTNET_CORE_APPLAUNCH_URL _X("?"); - if (starts_with(line, url_prefix_before_7_0, true)) + const pal::char_t url_prefix_before_7_0[] = _X(" - ") DOTNET_CORE_APPLAUNCH_URL _X("?"); + if (utils::starts_with(line, url_prefix_before_7_0, true)) { - size_t offset = url_prefix_before_7_0.length() - pal::strlen(DOTNET_CORE_APPLAUNCH_URL) - 1; + size_t offset = utils::strlen(url_prefix_before_7_0) - utils::strlen(DOTNET_CORE_APPLAUNCH_URL) - 1; url.assign(line.substr(offset, line.length() - offset)); return true; } @@ -100,20 +100,20 @@ namespace pal::stringstream_t ss(g_buffered_errors); while (std::getline(ss, line, _X('\n'))) { - const pal::string_t prefix = _X("Framework: '"); - const pal::string_t prefix_before_7_0 = _X("The framework '"); - const pal::string_t suffix_before_7_0 = _X(" was not found."); - const pal::string_t custom_prefix = _X(" _ "); - if (starts_with(line, prefix, true) - || (starts_with(line, prefix_before_7_0, true) && ends_with(line, suffix_before_7_0, true))) + const pal::char_t prefix[] = _X("Framework: '"); + const pal::char_t prefix_before_7_0[] = _X("The framework '"); + const pal::char_t suffix_before_7_0[] = _X(" was not found."); + const pal::char_t custom_prefix[] = _X(" _ "); + if (utils::starts_with(line, prefix, true) + || (utils::starts_with(line, prefix_before_7_0, true) && utils::ends_with(line, suffix_before_7_0, true))) { dialogMsg.append(line); dialogMsg.append(_X("\n\n")); } - else if (starts_with(line, custom_prefix, true)) + else if (utils::starts_with(line, custom_prefix, true)) { dialogMsg.erase(); - dialogMsg.append(line.substr(custom_prefix.length())); + dialogMsg.append(line.substr(utils::strlen(custom_prefix))); dialogMsg.append(_X("\n\n")); } else if (try_get_url_from_line(line, url)) @@ -128,7 +128,7 @@ namespace pal::stringstream_t ss(g_buffered_errors); while (std::getline(ss, line, _X('\n'))) { - if (starts_with(line, _X("Bundle header version compatibility check failed."), true)) + if (utils::starts_with(line, _X("Bundle header version compatibility check failed."), true)) { dialogMsg = pal::string_t(_X("To run this application, you must install .NET Desktop Runtime ")) + _STRINGIFY(COMMON_HOST_PKG_VER) + _X(" (") + get_arch() + _X(").\n\n"); url = get_download_url(); @@ -145,9 +145,10 @@ namespace return; } - dialogMsg.append(_X("Would you like to download it now?\n\n")); - dialogMsg.append(_X("Learn about framework resolution:\n")); - dialogMsg.append(DOTNET_APP_LAUNCH_FAILED_URL); + dialogMsg.append( + _X("Would you like to download it now?\n\n") + _X("Learn about framework resolution:\n") + DOTNET_APP_LAUNCH_FAILED_URL); assert(url.length() > 0); assert(is_gui_application()); diff --git a/src/native/corehost/fxr/fx_muxer.cpp b/src/native/corehost/fxr/fx_muxer.cpp index e571c03d966bba..65f21100922305 100644 --- a/src/native/corehost/fxr/fx_muxer.cpp +++ b/src/native/corehost/fxr/fx_muxer.cpp @@ -1070,10 +1070,12 @@ int fx_muxer_t::handle_cli( return StatusCode::Success; } - trace::error(_X("The command could not be loaded, possibly because:")); - trace::error(_X(" * You intended to execute a .NET program:")); - trace::error(_X(" The application '%s' does not exist."), app_candidate.c_str()); - trace::error(_X(" * You intended to execute a .NET SDK command:")); + trace::error( + _X("The command could not be loaded, possibly because:\n") + _X(" * You intended to execute a .NET program:\n") + _X(" The application '%s' does not exist.\n") + _X(" * You intended to execute a .NET SDK command:"), + app_candidate.c_str()); resolver.print_resolution_error(host_info.dotnet_root, _X(" ")); return StatusCode::LibHostSdkFindFailure; diff --git a/src/native/corehost/fxr/fx_resolver.cpp b/src/native/corehost/fxr/fx_resolver.cpp index d5e25d6b418f69..ab898aec8a233d 100644 --- a/src/native/corehost/fxr/fx_resolver.cpp +++ b/src/native/corehost/fxr/fx_resolver.cpp @@ -287,6 +287,7 @@ namespace if (selected_fx_dir.empty()) { + trace::verbose(_X("It was not possible to find any compatible framework version")); return nullptr; } diff --git a/src/native/corehost/fxr/fx_resolver.messages.cpp b/src/native/corehost/fxr/fx_resolver.messages.cpp index 24e4175c1779e1..2a7c080d074009 100644 --- a/src/native/corehost/fxr/fx_resolver.messages.cpp +++ b/src/native/corehost/fxr/fx_resolver.messages.cpp @@ -121,8 +121,7 @@ void fx_resolver_t::display_missing_framework_error( trace::error(_X("Framework: '%s', (%s)"), fx_name.c_str(), get_arch()); } - trace::error(_X("Location: %s"), dotnet_root.c_str()); - trace::error(_X("")); + trace::error(_X("Location: %s\n"), dotnet_root.c_str()); if (framework_infos.size()) { @@ -138,12 +137,14 @@ void fx_resolver_t::display_missing_framework_error( } pal::string_t url = get_download_url(fx_name.c_str(), fx_version.c_str()); - trace::error(_X("")); - trace::error(_X("Learn about framework resolution:")); - trace::error(DOTNET_APP_LAUNCH_FAILED_URL); - trace::error(_X("")); - trace::error(_X("Download framework:")); - trace::error(url.c_str()); + trace::error( + _X("\n") + _X("Learn about framework resolution:\n") + DOTNET_APP_LAUNCH_FAILED_URL + _X("\n\n") + _X("Download framework:\n") + _X("%s"), + url.c_str()); } void fx_resolver_t::display_incompatible_loaded_framework_error( diff --git a/src/native/corehost/fxr/sdk_resolver.cpp b/src/native/corehost/fxr/sdk_resolver.cpp index a8be7ca856d2e1..1079b52e07e003 100644 --- a/src/native/corehost/fxr/sdk_resolver.cpp +++ b/src/native/corehost/fxr/sdk_resolver.cpp @@ -91,17 +91,19 @@ void sdk_resolver::print_resolution_error(const pal::string_t& dotnet_root, cons const pal::char_t *no_sdk_message = _X("No .NET SDKs were found."); if (!version.is_empty()) { - trace::error(_X("%sA compatible .NET SDK was not found."), main_error_prefix); pal::string_t requested = version.as_str(); + trace::error( + _X("%sA compatible .NET SDK was not found.\n") + _X("\n") + _X("Requested SDK version: %s"), + main_error_prefix, + requested.c_str()); - trace::error(_X("")); - trace::error(_X("Requested SDK version: %s"), requested.c_str()); bool has_global_file = !global_file.empty(); if (has_global_file) trace::error(_X("global.json file: %s"), global_file.c_str()); - trace::error(_X("")); - trace::error(_X("Installed SDKs:")); + trace::error(_X("\nInstalled SDKs:")); sdk_exists = sdk_info::print_all_sdks(dotnet_root, _X("")); if (!sdk_exists) trace::error(no_sdk_message); @@ -123,14 +125,16 @@ void sdk_resolver::print_resolution_error(const pal::string_t& dotnet_root, cons if (!sdk_exists) { - trace::error(_X("")); - trace::error(_X("Download a .NET SDK:")); - trace::error(DOTNET_CORE_DOWNLOAD_URL); + trace::error( + _X("\n") + _X("Download a .NET SDK:\n") + DOTNET_CORE_DOWNLOAD_URL); } - trace::error(_X("")); - trace::error(_X("Learn about SDK resolution:")); - trace::error(DOTNET_SDK_NOT_FOUND_URL); + trace::error( + _X("\n") + _X("Learn about SDK resolution:\n") + DOTNET_SDK_NOT_FOUND_URL); } sdk_resolver sdk_resolver::from_nearest_global_file(bool allow_prerelease) diff --git a/src/native/corehost/hostmisc/utils.cpp b/src/native/corehost/hostmisc/utils.cpp index 049210088e2ee3..977d3d0e4d53d7 100644 --- a/src/native/corehost/hostmisc/utils.cpp +++ b/src/native/corehost/hostmisc/utils.cpp @@ -29,23 +29,32 @@ bool coreclr_exists_in_dir(const pal::string_t& candidate) return pal::file_exists(test); } -bool ends_with(const pal::string_t& value, const pal::string_t& suffix, bool match_case) +bool utils::starts_with(const pal::string_t& value, const pal::char_t* prefix, size_t prefix_len, bool match_case) +{ + // Cannot start with an empty string. + if (prefix_len == 0) + return false; + + auto cmp = match_case ? pal::strncmp : pal::strncasecmp; + return (value.size() >= prefix_len) && + cmp(value.c_str(), prefix, prefix_len) == 0; +} + +bool utils::ends_with(const pal::string_t& value, const pal::char_t* suffix, size_t suffix_len, bool match_case) { auto cmp = match_case ? pal::strcmp : pal::strcasecmp; - return (value.size() >= suffix.size()) && - cmp(value.c_str() + value.size() - suffix.size(), suffix.c_str()) == 0; + return (value.size() >= suffix_len) && + cmp(value.c_str() + value.size() - suffix_len, suffix) == 0; +} + +bool ends_with(const pal::string_t& value, const pal::string_t& suffix, bool match_case) +{ + return utils::ends_with(value, suffix.c_str(), suffix.size(), match_case); } bool starts_with(const pal::string_t& value, const pal::string_t& prefix, bool match_case) { - if (prefix.empty()) - { - // Cannot start with an empty string. - return false; - } - auto cmp = match_case ? pal::strncmp : pal::strncasecmp; - return (value.size() >= prefix.size()) && - cmp(value.c_str(), prefix.c_str(), prefix.size()) == 0; + return utils::starts_with(value, prefix.c_str(), prefix.size(), match_case); } void append_path(pal::string_t* path1, const pal::char_t* path2) diff --git a/src/native/corehost/hostmisc/utils.h b/src/native/corehost/hostmisc/utils.h index 125ae32f8d0953..9abc6f3fc4874a 100644 --- a/src/native/corehost/hostmisc/utils.h +++ b/src/native/corehost/hostmisc/utils.h @@ -30,6 +30,31 @@ bool ends_with(const pal::string_t& value, const pal::string_t& suffix, bool match_case); bool starts_with(const pal::string_t& value, const pal::string_t& prefix, bool match_case); + +namespace utils +{ + template + inline constexpr size_t strlen(const pal::char_t(&)[L]) + { + return L - 1; + } + + bool ends_with(const pal::string_t& value, const pal::char_t *suffix, size_t suffix_len, bool match_case); + bool starts_with(const pal::string_t& value, const pal::char_t* prefix, size_t prefix_len, bool match_case); + + template + bool ends_with(const pal::string_t& value, const pal::char_t (&suffix)[L], bool match_case) + { + return ends_with(value, suffix, L - 1, match_case); + } + + template + bool starts_with(const pal::string_t& value, const pal::char_t (&prefix)[L], bool match_case) + { + return starts_with(value, prefix, L - 1, match_case); + } +} + pal::string_t strip_executable_ext(const pal::string_t& filename); pal::string_t get_directory(const pal::string_t& path); pal::string_t strip_file_ext(const pal::string_t& path); diff --git a/src/native/corehost/runtime_config.cpp b/src/native/corehost/runtime_config.cpp index 7e54b76b726a78..58432ebcc41840 100644 --- a/src/native/corehost/runtime_config.cpp +++ b/src/native/corehost/runtime_config.cpp @@ -429,7 +429,7 @@ const pal::string_t& runtime_config_t::get_tfm() const return m_tfm; } -const int runtime_config_t::get_compat_major_version_from_tfm() const +const unsigned long runtime_config_t::get_compat_major_version_from_tfm() const { assert(m_valid); @@ -443,13 +443,14 @@ const int runtime_config_t::get_compat_major_version_from_tfm() const return runtime_config_t::unknown_version; size_t majorVersionStartIndex; - if (m_tfm.rfind(_X("netcoreapp"), 0) == 0) // this is StartsWith("netcoreapp") in std speak + const pal::char_t netcoreapp_prefix[] = _X("netcoreapp"); + if (utils::starts_with(m_tfm, netcoreapp_prefix, true)) { - majorVersionStartIndex = 10; // "netcoreapp".Length + majorVersionStartIndex = utils::strlen(netcoreapp_prefix); } else { - majorVersionStartIndex = 3; // "net".Length + majorVersionStartIndex = utils::strlen(_X("net")); } if (majorVersionStartIndex >= m_tfm.length()) @@ -465,7 +466,7 @@ const int runtime_config_t::get_compat_major_version_from_tfm() const bool runtime_config_t::get_is_multilevel_lookup_disabled() const { // Starting with .NET 7, multi-level lookup is fully disabled - int compat_major_version = get_compat_major_version_from_tfm(); + unsigned long compat_major_version = get_compat_major_version_from_tfm(); return (compat_major_version >= 7 || compat_major_version == runtime_config_t::unknown_version); } diff --git a/src/native/corehost/runtime_config.h b/src/native/corehost/runtime_config.h index 8457d146523709..5e09255224704f 100644 --- a/src/native/corehost/runtime_config.h +++ b/src/native/corehost/runtime_config.h @@ -33,7 +33,7 @@ class runtime_config_t const pal::string_t& get_path() const { return m_path; } const pal::string_t& get_dev_path() const { return m_dev_path; } const pal::string_t& get_tfm() const; - const int get_compat_major_version_from_tfm() const; + const unsigned long get_compat_major_version_from_tfm() const; bool get_is_multilevel_lookup_disabled() const; const std::list& get_probe_paths() const; bool get_is_framework_dependent() const; From 25d8c8f7907049ead1c68b03d81dee930382563d Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Thu, 24 Mar 2022 12:24:25 -0700 Subject: [PATCH 17/20] Add App and Architecture to messages. Include global.json section when not found --- src/native/corehost/fxr/fx_muxer.cpp | 9 ++------- src/native/corehost/fxr/fx_resolver.cpp | 8 +++++++- src/native/corehost/fxr/fx_resolver.messages.cpp | 3 +-- src/native/corehost/fxr/sdk_resolver.cpp | 9 +++++++++ src/native/corehost/fxr/sdk_resolver.h | 1 + 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/native/corehost/fxr/fx_muxer.cpp b/src/native/corehost/fxr/fx_muxer.cpp index 65f21100922305..f084ece4d22ec8 100644 --- a/src/native/corehost/fxr/fx_muxer.cpp +++ b/src/native/corehost/fxr/fx_muxer.cpp @@ -1066,6 +1066,7 @@ int fx_muxer_t::handle_cli( } else if (pal::strcasecmp(_X("--info"), argv[1]) == 0) { + resolver.print_global_file_path(); command_line::print_muxer_info(host_info.dotnet_root); return StatusCode::Success; } @@ -1123,13 +1124,7 @@ int fx_muxer_t::handle_cli( if (pal::strcasecmp(_X("--info"), argv[1]) == 0) { - if (!resolver.global_file_path().empty()) - { - trace::println(); - trace::println(_X("global.json file:")); - trace::println(_X(" %s"), resolver.global_file_path().c_str()); - } - + resolver.print_global_file_path(); command_line::print_muxer_info(host_info.dotnet_root); } diff --git a/src/native/corehost/fxr/fx_resolver.cpp b/src/native/corehost/fxr/fx_resolver.cpp index ab898aec8a233d..a44d3e76c8768e 100644 --- a/src/native/corehost/fxr/fx_resolver.cpp +++ b/src/native/corehost/fxr/fx_resolver.cpp @@ -444,7 +444,13 @@ StatusCode fx_resolver_t::read_framework( fx_definition_t* fx = resolve_framework_reference(new_effective_fx_ref, m_oldest_fx_references[fx_name].get_fx_version(), host_info.dotnet_root, disable_multilevel_lookup); if (fx == nullptr) { - trace::error(INSTALL_OR_UPDATE_NET_ERROR_MESSAGE); + trace::error( + INSTALL_OR_UPDATE_NET_ERROR_MESSAGE + _X("\n\n") + _X("App: %s\n") + _X("Architecture: %s"), + host_info.host_path.c_str(), + get_arch()); display_missing_framework_error(fx_name, new_effective_fx_ref.get_fx_version(), pal::string_t(), host_info.dotnet_root, disable_multilevel_lookup); return FrameworkMissingFailure; } diff --git a/src/native/corehost/fxr/fx_resolver.messages.cpp b/src/native/corehost/fxr/fx_resolver.messages.cpp index 2a7c080d074009..578c2e6565c6a5 100644 --- a/src/native/corehost/fxr/fx_resolver.messages.cpp +++ b/src/native/corehost/fxr/fx_resolver.messages.cpp @@ -111,7 +111,6 @@ void fx_resolver_t::display_missing_framework_error( framework_info::get_all_framework_infos(dotnet_root, fx_name, disable_multilevel_lookup, &framework_infos); // Display the error message about missing FX. - trace::error(_X("")); if (fx_version.length()) { trace::error(_X("Framework: '%s', version '%s' (%s)"), fx_name.c_str(), fx_version.c_str(), get_arch()); @@ -121,7 +120,7 @@ void fx_resolver_t::display_missing_framework_error( trace::error(_X("Framework: '%s', (%s)"), fx_name.c_str(), get_arch()); } - trace::error(_X("Location: %s\n"), dotnet_root.c_str()); + trace::error(_X(".NET location: %s\n"), dotnet_root.c_str()); if (framework_infos.size()) { diff --git a/src/native/corehost/fxr/sdk_resolver.cpp b/src/native/corehost/fxr/sdk_resolver.cpp index 1079b52e07e003..3a1f01105b0b73 100644 --- a/src/native/corehost/fxr/sdk_resolver.cpp +++ b/src/native/corehost/fxr/sdk_resolver.cpp @@ -85,6 +85,15 @@ pal::string_t sdk_resolver::resolve(const pal::string_t& dotnet_root, bool print return {}; } +void sdk_resolver::print_global_file_path() +{ + trace::println( + _X("\n") + _X("global.json file:\n") + _X(" %s"), + global_file.empty() ? _X("Not found") : global_file.c_str()); +} + void sdk_resolver::print_resolution_error(const pal::string_t& dotnet_root, const pal::char_t *main_error_prefix) const { bool sdk_exists = false; diff --git a/src/native/corehost/fxr/sdk_resolver.h b/src/native/corehost/fxr/sdk_resolver.h index 40ac774d398e1e..15342603ab764a 100644 --- a/src/native/corehost/fxr/sdk_resolver.h +++ b/src/native/corehost/fxr/sdk_resolver.h @@ -41,6 +41,7 @@ class sdk_resolver pal::string_t resolve(const pal::string_t& dotnet_root, bool print_errors = true) const; + void print_global_file_path(); void print_resolution_error(const pal::string_t& dotnet_root, const pal::char_t *prefix) const; static sdk_resolver from_nearest_global_file(bool allow_prerelease = true); From 12d17a03f17d575ae896d9221d454e26db643c46 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Thu, 24 Mar 2022 12:25:02 -0700 Subject: [PATCH 18/20] Update some framework resolution tests to check we print framework info on error --- ...meworkResolutionCommandResultExtensions.cs | 18 +++++--- ...rdAndRollForwardOnNoCandidateFxSettings.cs | 5 +- .../RollForwardOnNoCandidateFx.cs | 46 +++++++++++-------- .../RollForwardOnNoCandidateFxSettings.cs | 16 +++---- .../RollForwardPreReleaseOnly.cs | 20 ++++---- .../RollForwardReleaseOnly.cs | 15 +++--- .../RollForwardSettings.cs | 8 ++-- 7 files changed, 74 insertions(+), 54 deletions(-) diff --git a/src/installer/tests/HostActivation.Tests/FrameworkResolution/FrameworkResolutionCommandResultExtensions.cs b/src/installer/tests/HostActivation.Tests/FrameworkResolution/FrameworkResolutionCommandResultExtensions.cs index 6705068a696e64..d7280481ddaff4 100644 --- a/src/installer/tests/HostActivation.Tests/FrameworkResolution/FrameworkResolutionCommandResultExtensions.cs +++ b/src/installer/tests/HostActivation.Tests/FrameworkResolution/FrameworkResolutionCommandResultExtensions.cs @@ -38,7 +38,7 @@ public static AndConstraint ShouldHaveResolvedFramework if (resolvedFrameworkName == null || resolvedFrameworkVersion == null || resolvedFrameworkVersion == FrameworkResolutionBase.ResolvedFramework.NotFound) { - return result.ShouldFailToFindCompatibleFrameworkVersion(); + return result.ShouldFailToFindCompatibleFrameworkVersion(resolvedFrameworkName); } else { @@ -46,15 +46,21 @@ public static AndConstraint ShouldHaveResolvedFramework } } - public static AndConstraint DidNotFindCompatibleFrameworkVersion(this CommandResultAssertions assertion) + public static AndConstraint DidNotFindCompatibleFrameworkVersion(this CommandResultAssertions assertion, string frameworkName, string requestedVersion) { - return assertion.HaveStdErrContaining("You must install or update .NET to run this application."); + var constraint = assertion.HaveStdErrContaining("You must install or update .NET to run this application."); + if (frameworkName is not null) + { + constraint = constraint.And.HaveStdErrContaining($"Framework: '{frameworkName}', {(requestedVersion is null ? "" : $"version '{requestedVersion}'")}"); + } + + return constraint; } - public static AndConstraint ShouldFailToFindCompatibleFrameworkVersion(this CommandResult result) + public static AndConstraint ShouldFailToFindCompatibleFrameworkVersion(this CommandResult result, string frameworkName, string requestedVersion = null) { return result.Should().Fail() - .And.DidNotFindCompatibleFrameworkVersion(); + .And.DidNotFindCompatibleFrameworkVersion(frameworkName, requestedVersion); } public static AndConstraint FailedToReconcileFrameworkReference( @@ -96,7 +102,7 @@ public static AndConstraint ShouldHaveResolvedFramework } else if (resolvedVersion == FrameworkResolutionBase.ResolvedFramework.NotFound) { - return result.ShouldFailToFindCompatibleFrameworkVersion(); + return result.ShouldFailToFindCompatibleFrameworkVersion(frameworkName, null); } else { diff --git a/src/installer/tests/HostActivation.Tests/FrameworkResolution/RollForwardAndRollForwardOnNoCandidateFxSettings.cs b/src/installer/tests/HostActivation.Tests/FrameworkResolution/RollForwardAndRollForwardOnNoCandidateFxSettings.cs index b5fb6718fdb232..b12199a7bb842f 100644 --- a/src/installer/tests/HostActivation.Tests/FrameworkResolution/RollForwardAndRollForwardOnNoCandidateFxSettings.cs +++ b/src/installer/tests/HostActivation.Tests/FrameworkResolution/RollForwardAndRollForwardOnNoCandidateFxSettings.cs @@ -101,10 +101,11 @@ public void Precedence( SettingLocation rollForwardOnNoCandidateFxLocation, bool passes) { + string requestedVersion = "5.0.0"; CommandResult result = RunTest( new TestSettings() .WithRuntimeConfigCustomizer(runtimeConfig => runtimeConfig - .WithFramework(MicrosoftNETCoreApp, "5.0.0")) + .WithFramework(MicrosoftNETCoreApp, requestedVersion)) .With(RollForwardSetting(rollForwardLocation, Constants.RollForwardSetting.Major)) .With(RollForwardOnNoCandidateFxSetting(rollForwardOnNoCandidateFxLocation, 0))); @@ -114,7 +115,7 @@ public void Precedence( } else { - result.Should().Fail().And.DidNotFindCompatibleFrameworkVersion(); + result.ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion); } } diff --git a/src/installer/tests/HostActivation.Tests/FrameworkResolution/RollForwardOnNoCandidateFx.cs b/src/installer/tests/HostActivation.Tests/FrameworkResolution/RollForwardOnNoCandidateFx.cs index 935d4122fa7d33..dda6a1dd6d185a 100644 --- a/src/installer/tests/HostActivation.Tests/FrameworkResolution/RollForwardOnNoCandidateFx.cs +++ b/src/installer/tests/HostActivation.Tests/FrameworkResolution/RollForwardOnNoCandidateFx.cs @@ -8,7 +8,7 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.FrameworkResolution { - public class RollForwardOnNoCandidateFx : + public class RollForwardOnNoCandidateFx : FrameworkResolutionBase, IClassFixture { @@ -113,18 +113,19 @@ public void RollForwardToLatestPatch_RollForwardOnNoCandidateFx(int? rollForward [InlineData(2, false, true)] public void RollForwardOnMinor_RollForwardOnNoCandidateFx(int? rollForwardOnNoCandidateFx, bool? applyPatches, bool passes) { + string requestedVersion = "5.0.0"; CommandResult result = RunTestWithOneFramework( runtimeConfig => runtimeConfig .WithRollForwardOnNoCandidateFx(rollForwardOnNoCandidateFx) .WithApplyPatches(applyPatches) - .WithFramework(MicrosoftNETCoreApp, "5.0.0")); + .WithFramework(MicrosoftNETCoreApp, requestedVersion)); if (passes) { result.ShouldHaveResolvedFramework(MicrosoftNETCoreApp, "5.1.3"); } else { - result.ShouldFailToFindCompatibleFrameworkVersion(); + result.ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion); } } @@ -140,18 +141,19 @@ public void RollForwardOnMinor_RollForwardOnNoCandidateFx(int? rollForwardOnNoCa [InlineData(2, false, true)] public void RollForwardOnMajor_RollForwardOnNoCandidateFx(int? rollForwardOnNoCandidateFx, bool? applyPatches, bool passes) { + string requestedVersion = "4.1.0"; CommandResult result = RunTestWithOneFramework( runtimeConfig => runtimeConfig .WithRollForwardOnNoCandidateFx(rollForwardOnNoCandidateFx) .WithApplyPatches(applyPatches) - .WithFramework(MicrosoftNETCoreApp, "4.1.0")); + .WithFramework(MicrosoftNETCoreApp, requestedVersion)); if (passes) { result.ShouldHaveResolvedFramework(MicrosoftNETCoreApp, "5.1.3"); } else { - result.ShouldFailToFindCompatibleFrameworkVersion(); + result.ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion); } } @@ -165,12 +167,13 @@ public void RollForwardOnMajor_RollForwardOnNoCandidateFx(int? rollForwardOnNoCa [InlineData(2, false)] public void NeverRollBackOnRelease(int? rollForwardOnNoCandidateFx, bool? applyPatches) { + string requestedVersion = "5.1.4"; RunTestWithOneFramework( runtimeConfig => runtimeConfig .WithRollForwardOnNoCandidateFx(rollForwardOnNoCandidateFx) .WithApplyPatches(applyPatches) - .WithFramework(MicrosoftNETCoreApp, "5.1.4")) - .ShouldFailToFindCompatibleFrameworkVersion(); + .WithFramework(MicrosoftNETCoreApp, requestedVersion)) + .ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion); } // Verifies that if both rollForwardOnNoCandidateFx=0 and applyPatches=0 there will be no rolling forward. @@ -203,14 +206,14 @@ public void RollForwardDisabledOnCandidateFxAndDisabledApplyPatches_MatchesExact [Fact] public void RollForwardOnMinorDisabledOnNoCandidateFx_FailsToRoll() { + string requestedVersion = "5.0.0"; RunTestWithOneFramework( runtimeConfig => runtimeConfig .WithRollForwardOnNoCandidateFx(0) - .WithFramework(MicrosoftNETCoreApp, "5.0.0")) + .WithFramework(MicrosoftNETCoreApp, requestedVersion)) // Will still attempt roll forward to latest patch - .Should().Fail() - .And.HaveStdErrContaining("Attempting FX roll forward") - .And.DidNotFindCompatibleFrameworkVersion(); + .ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion) + .And.HaveStdErrContaining("Attempting FX roll forward"); } // 3.0 change: In 2.* pre-release never rolled to release. In 3.* it will follow normal roll-forward rules. @@ -313,22 +316,23 @@ public void RollForwardToPreReleaseLatestPatch_RollForwardOnNoCandidateFx(int? r [InlineData(2, null, true)] [InlineData(2, false, true)] public void RollForwardToPreReleaseOnMinor_RollForwardOnNoCandidateFx( - int? rollForwardOnNoCandidateFx, - bool? applyPatches, + int? rollForwardOnNoCandidateFx, + bool? applyPatches, bool passes) { + string requestedVersion = "5.0.0"; CommandResult result = RunTestWithPreReleaseFramework( runtimeConfig => runtimeConfig .WithRollForwardOnNoCandidateFx(rollForwardOnNoCandidateFx) .WithApplyPatches(applyPatches) - .WithFramework(MicrosoftNETCoreApp, "5.0.0")); + .WithFramework(MicrosoftNETCoreApp, requestedVersion)); if (passes) { result.ShouldHaveResolvedFramework(MicrosoftNETCoreApp, "5.1.3-preview.2"); } else { - result.ShouldFailToFindCompatibleFrameworkVersion(); + result.ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion); } } @@ -346,18 +350,19 @@ public void RollForwardToPreReleaseOnMajor_RollForwardOnNoCandidateFx( bool? applyPatches, bool passes) { + string requestedVersion = "4.1.0"; CommandResult result = RunTestWithPreReleaseFramework( runtimeConfig => runtimeConfig .WithRollForwardOnNoCandidateFx(rollForwardOnNoCandidateFx) .WithApplyPatches(applyPatches) - .WithFramework(MicrosoftNETCoreApp, "4.1.0")); + .WithFramework(MicrosoftNETCoreApp, requestedVersion)); if (passes) { result.ShouldHaveResolvedFramework(MicrosoftNETCoreApp, "5.1.3-preview.2"); } else { - result.ShouldFailToFindCompatibleFrameworkVersion(); + result.ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion); } } @@ -371,12 +376,13 @@ public void RollForwardToPreReleaseOnMajor_RollForwardOnNoCandidateFx( [InlineData(2, false)] public void NeverRollBackOnPreRelease(int? rollForwardOnNoCandidateFx, bool? applyPatches) { + string requestedVersion = "5.1.3-preview.9"; RunTestWithPreReleaseFramework( runtimeConfig => runtimeConfig .WithRollForwardOnNoCandidateFx(rollForwardOnNoCandidateFx) .WithApplyPatches(applyPatches) - .WithFramework(MicrosoftNETCoreApp, "5.1.3-preview.9")) - .ShouldFailToFindCompatibleFrameworkVersion(); + .WithFramework(MicrosoftNETCoreApp, requestedVersion)) + .ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion); } private CommandResult RunTestWithPreReleaseFramework(Func runtimeConfig) @@ -623,7 +629,7 @@ public void RollForwardToPreReleaseToClosestPreRelease_FromRelease( [InlineData(2, false, "2.3.2")] // Pre-release is ignored, roll forward to closest release available public void RollForwardToClosestReleaseWithPreReleaseAvailable_FromRelease( int? rollForwardOnNoCandidateFx, - bool? applyPatches, + bool? applyPatches, string resolvedFramework) { RunTestWithManyVersions( diff --git a/src/installer/tests/HostActivation.Tests/FrameworkResolution/RollForwardOnNoCandidateFxSettings.cs b/src/installer/tests/HostActivation.Tests/FrameworkResolution/RollForwardOnNoCandidateFxSettings.cs index 3258653cdf40bd..b18b8c8131c6b1 100644 --- a/src/installer/tests/HostActivation.Tests/FrameworkResolution/RollForwardOnNoCandidateFxSettings.cs +++ b/src/installer/tests/HostActivation.Tests/FrameworkResolution/RollForwardOnNoCandidateFxSettings.cs @@ -24,12 +24,12 @@ public RollForwardOnNoCandidateFxSettings(SharedTestState sharedState) [Fact] public void Default() { + string requestedVersion = "4.0.0"; RunTest( new TestSettings() .WithRuntimeConfigCustomizer(runtimeConfig => runtimeConfig - .WithFramework(MicrosoftNETCoreApp, "4.0.0"))) - .Should().Fail() - .And.DidNotFindCompatibleFrameworkVersion(); + .WithFramework(MicrosoftNETCoreApp, requestedVersion))) + .ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion); RunTest( new TestSettings() @@ -122,7 +122,7 @@ public void EnvironmentPriority(SettingLocation settingLocation, bool envVariabl // Verifies interaction between variour and inner framework reference setting [Theory] // settingLocation innerReferenceWins // Command line overrides everything - even inner framework references - [InlineData(SettingLocation.CommandLine, false)] + [InlineData(SettingLocation.CommandLine, false)] [InlineData(SettingLocation.RuntimeOptions, true)] [InlineData(SettingLocation.FrameworkReference, true)] [InlineData(SettingLocation.Environment, true)] @@ -149,7 +149,7 @@ public void InnerFrameworkReference(SettingLocation settingLocation, bool innerR // RuntimeOptions and FrameworkReference settings are not inherited to inner reference [InlineData(SettingLocation.FrameworkReference, false)] // Since none is specified for the inner reference, environment is used - [InlineData(SettingLocation.Environment, true)] + [InlineData(SettingLocation.Environment, true)] public void NoInheritance_MoreRelaxed(SettingLocation settingLocation, bool appWins) { RunTest( @@ -172,7 +172,7 @@ public void NoInheritance_MoreRelaxed(SettingLocation settingLocation, bool appW // RuntimeOptions and FrameworkReference settings are not inherited to inner reference [InlineData(SettingLocation.FrameworkReference, false)] // Since none is specified for the inner reference, environment is used - [InlineData(SettingLocation.Environment, true)] + [InlineData(SettingLocation.Environment, true)] public void NoInheritance_MoreRestrictive(SettingLocation settingLocation, bool appWins) { RunTest( @@ -186,7 +186,7 @@ public void NoInheritance_MoreRestrictive(SettingLocation settingLocation, bool .ShouldHaveResolvedFrameworkOrFailToFind(MicrosoftNETCoreApp, appWins ? null : "5.1.3"); } - private CommandResult RunTest(TestSettings testSettings) => + private CommandResult RunTest(TestSettings testSettings) => RunTest(SharedState.DotNetWithFrameworks, SharedState.FrameworkReferenceApp, testSettings); public class SharedTestState : SharedTestStateBase @@ -202,7 +202,7 @@ public SharedTestState() .AddMicrosoftNETCoreAppFrameworkMockHostPolicy("2.5.5") .AddMicrosoftNETCoreAppFrameworkMockHostPolicy("5.1.3") .AddFramework( - MiddleWare, "2.1.2", + MiddleWare, "2.1.2", runtimeConfig => runtimeConfig.WithFramework(MicrosoftNETCoreApp, "5.1.3")) .Build(); diff --git a/src/installer/tests/HostActivation.Tests/FrameworkResolution/RollForwardPreReleaseOnly.cs b/src/installer/tests/HostActivation.Tests/FrameworkResolution/RollForwardPreReleaseOnly.cs index b3b6cf8a935367..9e35ed8ff2a630 100644 --- a/src/installer/tests/HostActivation.Tests/FrameworkResolution/RollForwardPreReleaseOnly.cs +++ b/src/installer/tests/HostActivation.Tests/FrameworkResolution/RollForwardPreReleaseOnly.cs @@ -122,11 +122,12 @@ public void RollForwardOnMajor_FromReleaseToPreRelease(string rollForward, bool? [InlineData(Constants.RollForwardSetting.LatestPatch, false)] public void NeverRollBackOnPreRelease_PreReleaseOnly(string rollForward, bool? applyPatches) { + string requestedVersion = "5.1.2-preview.3"; RunTest( - "5.1.2-preview.3", + requestedVersion, rollForward, applyPatches) - .ShouldFailToFindCompatibleFrameworkVersion(); + .ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion); } // Verifies that rollForward settings won't roll back (on patch). @@ -139,11 +140,12 @@ public void NeverRollBackOnPreRelease_PreReleaseOnly(string rollForward, bool? a [InlineData(Constants.RollForwardSetting.LatestPatch, false)] public void NeverRollBackOnPatch_PreReleaseOnly(string rollForward, bool? applyPatches) { + string requestedVersion = "5.1.3-preview.1"; RunTest( - "5.1.3-preview.1", + requestedVersion, rollForward, applyPatches) - .ShouldFailToFindCompatibleFrameworkVersion(); + .ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion); } // Verifies that rollForward settings won't roll back (on minor). @@ -157,11 +159,12 @@ public void NeverRollBackOnPatch_PreReleaseOnly(string rollForward, bool? applyP [InlineData(Constants.RollForwardSetting.LatestMinor, false)] public void NeverRollBackOnMinor_PreReleaseOnly(string rollForward, bool? applyPatches) { + string requestedVersion = "5.3.0-preview.1"; RunTest( - "5.3.0-preview.1", + requestedVersion, rollForward, applyPatches) - .ShouldFailToFindCompatibleFrameworkVersion(); + .ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion); } // Verifies that rollForward settings won't roll back (on major). @@ -175,11 +178,12 @@ public void NeverRollBackOnMinor_PreReleaseOnly(string rollForward, bool? applyP [InlineData(Constants.RollForwardSetting.LatestMinor, false)] public void NeverRollBackOnMajor_PreReleaseOnly(string rollForward, bool? applyPatches) { + string requestedVersion = "7.1.0-preview.1"; RunTest( - "7.1.0-preview.1", + requestedVersion, rollForward, applyPatches) - .ShouldFailToFindCompatibleFrameworkVersion(); + .ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion); } // Verifies that rollForward settings behave as expected starting with framework reference diff --git a/src/installer/tests/HostActivation.Tests/FrameworkResolution/RollForwardReleaseOnly.cs b/src/installer/tests/HostActivation.Tests/FrameworkResolution/RollForwardReleaseOnly.cs index 1f438a9d96d596..9899c4364a6cbc 100644 --- a/src/installer/tests/HostActivation.Tests/FrameworkResolution/RollForwardReleaseOnly.cs +++ b/src/installer/tests/HostActivation.Tests/FrameworkResolution/RollForwardReleaseOnly.cs @@ -166,11 +166,12 @@ public void RollForwardOnMajor_ReleaseOnly(string rollForward, bool? applyPatche [InlineData(Constants.RollForwardSetting.LatestPatch, false)] public void NeverRollBackOnPatch_ReleaseOnly(string rollForward, bool? applyPatches) { + string requestedVersion = "2.1.4"; RunTest( - "2.1.4", + requestedVersion, rollForward, applyPatches) - .ShouldFailToFindCompatibleFrameworkVersion(); + .ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion); } // Verify that rollForward settings will never roll back to lower minor version. @@ -183,11 +184,12 @@ public void NeverRollBackOnPatch_ReleaseOnly(string rollForward, bool? applyPatc [InlineData(Constants.RollForwardSetting.LatestMinor, false)] public void NeverRollBackOnMinor_ReleaseOnly(string rollForward, bool? applyPatches) { + string requestedVersion = "2.5.0"; RunTest( - "2.5.0", + requestedVersion, rollForward, applyPatches) - .ShouldFailToFindCompatibleFrameworkVersion(); + .ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion); } // Verify that rollForward settings will never roll back to lower major version. @@ -200,11 +202,12 @@ public void NeverRollBackOnMinor_ReleaseOnly(string rollForward, bool? applyPatc [InlineData(Constants.RollForwardSetting.LatestMinor, false)] public void NeverRollBackOnMajor_ReleaseOnly(string rollForward, bool? applyPatches) { + string requestedVersion = "4.1.0"; RunTest( - "4.1.0", + requestedVersion, rollForward, applyPatches) - .ShouldFailToFindCompatibleFrameworkVersion(); + .ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion); } private CommandResult RunTest( diff --git a/src/installer/tests/HostActivation.Tests/FrameworkResolution/RollForwardSettings.cs b/src/installer/tests/HostActivation.Tests/FrameworkResolution/RollForwardSettings.cs index 7b35129c0577d8..b5e278c74ccabc 100644 --- a/src/installer/tests/HostActivation.Tests/FrameworkResolution/RollForwardSettings.cs +++ b/src/installer/tests/HostActivation.Tests/FrameworkResolution/RollForwardSettings.cs @@ -24,12 +24,12 @@ public RollForwardSettings(SharedTestState sharedState) [Fact] public void Default() { + string requestedVersion = "4.0.0"; RunTest( new TestSettings() .WithRuntimeConfigCustomizer(runtimeConfig => runtimeConfig .WithFramework(MicrosoftNETCoreApp, "4.0.0"))) - .Should().Fail() - .And.DidNotFindCompatibleFrameworkVersion(); + .ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion); RunTest( new TestSettings() @@ -138,7 +138,7 @@ private void ValidateValueIgnoresCase(SettingLocation settingLocation, string ro // RuntimeOptions and FrameworkReference settings are not inherited to inner reference [InlineData(SettingLocation.FrameworkReference, false)] // Since none is specified for the inner reference, environment is used - [InlineData(SettingLocation.Environment, true)] + [InlineData(SettingLocation.Environment, true)] public void NoInheritance_MoreRelaxed(SettingLocation settingLocation, bool appWins) { RunTest( @@ -161,7 +161,7 @@ public void NoInheritance_MoreRelaxed(SettingLocation settingLocation, bool appW // RuntimeOptions and FrameworkReference settings are not inherited to inner reference [InlineData(SettingLocation.FrameworkReference, false)] // Since none is specified for the inner reference, environment is used - [InlineData(SettingLocation.Environment, true)] + [InlineData(SettingLocation.Environment, true)] public void NoInheritance_MoreRestrictive(SettingLocation settingLocation, bool appWins) { RunTest( From e563ef5134a5befb8aadc3ebdb0607acd9ef19f2 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Thu, 24 Mar 2022 17:53:14 -0700 Subject: [PATCH 19/20] PR feedback --- src/native/corehost/runtime_config.cpp | 4 ++-- src/native/corehost/runtime_config.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/native/corehost/runtime_config.cpp b/src/native/corehost/runtime_config.cpp index 58432ebcc41840..bbfd8f89653ae8 100644 --- a/src/native/corehost/runtime_config.cpp +++ b/src/native/corehost/runtime_config.cpp @@ -429,7 +429,7 @@ const pal::string_t& runtime_config_t::get_tfm() const return m_tfm; } -const unsigned long runtime_config_t::get_compat_major_version_from_tfm() const +const uint32_t runtime_config_t::get_compat_major_version_from_tfm() const { assert(m_valid); @@ -460,7 +460,7 @@ const unsigned long runtime_config_t::get_compat_major_version_from_tfm() const if (majorVersionEndIndex == pal::string_t::npos || majorVersionEndIndex == majorVersionStartIndex) return runtime_config_t::unknown_version; - return std::stoul(m_tfm.substr(majorVersionStartIndex, majorVersionEndIndex - majorVersionStartIndex)); + return static_cast(std::stoul(m_tfm.substr(majorVersionStartIndex, majorVersionEndIndex - majorVersionStartIndex))); } bool runtime_config_t::get_is_multilevel_lookup_disabled() const diff --git a/src/native/corehost/runtime_config.h b/src/native/corehost/runtime_config.h index 5e09255224704f..c7b01d40baa12f 100644 --- a/src/native/corehost/runtime_config.h +++ b/src/native/corehost/runtime_config.h @@ -33,7 +33,6 @@ class runtime_config_t const pal::string_t& get_path() const { return m_path; } const pal::string_t& get_dev_path() const { return m_dev_path; } const pal::string_t& get_tfm() const; - const unsigned long get_compat_major_version_from_tfm() const; bool get_is_multilevel_lookup_disabled() const; const std::list& get_probe_paths() const; bool get_is_framework_dependent() const; @@ -46,6 +45,7 @@ class runtime_config_t static constexpr int unknown_version = std::numeric_limits::max(); private: + const uint32_t get_compat_major_version_from_tfm() const; bool ensure_parsed(); //todo: const runtime_config_t* defaults bool ensure_dev_config_parsed(); From 0ab5f74c07a00c0e95d7da2697091ef8635cacda Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Fri, 25 Mar 2022 09:53:42 -0700 Subject: [PATCH 20/20] Update messages --- src/native/corehost/fxr/fx_muxer.cpp | 4 ++-- src/native/corehost/fxr/fx_resolver.cpp | 12 +++++++----- src/native/corehost/fxr/fx_resolver.h | 6 ++++-- src/native/corehost/fxr/fx_resolver.messages.cpp | 2 +- src/native/corehost/host_startup_info.h | 4 ++-- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/native/corehost/fxr/fx_muxer.cpp b/src/native/corehost/fxr/fx_muxer.cpp index f084ece4d22ec8..a97da2f90f7d6f 100644 --- a/src/native/corehost/fxr/fx_muxer.cpp +++ b/src/native/corehost/fxr/fx_muxer.cpp @@ -466,7 +466,7 @@ namespace } else { - rc = fx_resolver_t::resolve_frameworks_for_app(host_info, app_config.get_is_multilevel_lookup_disabled(), override_settings, app_config, fx_definitions); + rc = fx_resolver_t::resolve_frameworks_for_app(host_info, app_config.get_is_multilevel_lookup_disabled(), override_settings, app_config, fx_definitions, mode == host_mode_t::muxer ? app_candidate.c_str() : nullptr); if (rc != StatusCode::Success) { return rc; @@ -1073,7 +1073,7 @@ int fx_muxer_t::handle_cli( trace::error( _X("The command could not be loaded, possibly because:\n") - _X(" * You intended to execute a .NET program:\n") + _X(" * You intended to execute a .NET application:\n") _X(" The application '%s' does not exist.\n") _X(" * You intended to execute a .NET SDK command:"), app_candidate.c_str()); diff --git a/src/native/corehost/fxr/fx_resolver.cpp b/src/native/corehost/fxr/fx_resolver.cpp index a44d3e76c8768e..df93428b74ed6a 100644 --- a/src/native/corehost/fxr/fx_resolver.cpp +++ b/src/native/corehost/fxr/fx_resolver.cpp @@ -399,7 +399,8 @@ StatusCode fx_resolver_t::read_framework( const runtime_config_t::settings_t& override_settings, const runtime_config_t & config, const fx_reference_t * effective_parent_fx_ref, - fx_definition_vector_t & fx_definitions) + fx_definition_vector_t & fx_definitions, + const pal::char_t* app_display_name) { // This reconciles duplicate references to minimize the number of resolve retries. update_newest_references(config); @@ -449,7 +450,7 @@ StatusCode fx_resolver_t::read_framework( _X("\n\n") _X("App: %s\n") _X("Architecture: %s"), - host_info.host_path.c_str(), + app_display_name != nullptr ? app_display_name : host_info.host_path.c_str(), get_arch()); display_missing_framework_error(fx_name, new_effective_fx_ref.get_fx_version(), pal::string_t(), host_info.dotnet_root, disable_multilevel_lookup); return FrameworkMissingFailure; @@ -480,7 +481,7 @@ StatusCode fx_resolver_t::read_framework( return StatusCode::InvalidConfigFile; } - rc = read_framework(host_info, disable_multilevel_lookup, override_settings, new_config, &new_effective_fx_ref, fx_definitions); + rc = read_framework(host_info, disable_multilevel_lookup, override_settings, new_config, &new_effective_fx_ref, fx_definitions, app_display_name); if (rc) { break; // Error case @@ -523,7 +524,8 @@ StatusCode fx_resolver_t::resolve_frameworks_for_app( bool disable_multilevel_lookup, const runtime_config_t::settings_t& override_settings, const runtime_config_t & app_config, - fx_definition_vector_t & fx_definitions) + fx_definition_vector_t & fx_definitions, + const pal::char_t* app_display_name) { fx_resolver_t resolver; @@ -533,7 +535,7 @@ StatusCode fx_resolver_t::resolve_frameworks_for_app( do { fx_definitions.resize(1); // Erase any existing frameworks for re-try - rc = resolver.read_framework(host_info, disable_multilevel_lookup, override_settings, app_config, /*effective_parent_fx_ref*/ nullptr, fx_definitions); + rc = resolver.read_framework(host_info, disable_multilevel_lookup, override_settings, app_config, /*effective_parent_fx_ref*/ nullptr, fx_definitions, app_display_name); } while (rc == StatusCode::FrameworkCompatRetry && retry_count++ < Max_Framework_Resolve_Retries); assert(retry_count < Max_Framework_Resolve_Retries); diff --git a/src/native/corehost/fxr/fx_resolver.h b/src/native/corehost/fxr/fx_resolver.h index 1d0e001fe617c6..35c6fd250af5ad 100644 --- a/src/native/corehost/fxr/fx_resolver.h +++ b/src/native/corehost/fxr/fx_resolver.h @@ -19,7 +19,8 @@ class fx_resolver_t bool disable_multilevel_lookup, const runtime_config_t::settings_t& override_settings, const runtime_config_t& app_config, - fx_definition_vector_t& fx_definitions); + fx_definition_vector_t& fx_definitions, + const pal::char_t* app_display_name = nullptr); static bool is_config_compatible_with_frameworks( const runtime_config_t& config, @@ -36,7 +37,8 @@ class fx_resolver_t const runtime_config_t::settings_t& override_settings, const runtime_config_t& config, const fx_reference_t * effective_parent_fx_ref, - fx_definition_vector_t& fx_definitions); + fx_definition_vector_t& fx_definitions, + const pal::char_t* app_display_name); static StatusCode reconcile_fx_references_helper( const fx_reference_t& lower_fx_ref, diff --git a/src/native/corehost/fxr/fx_resolver.messages.cpp b/src/native/corehost/fxr/fx_resolver.messages.cpp index 578c2e6565c6a5..e67344d9449dab 100644 --- a/src/native/corehost/fxr/fx_resolver.messages.cpp +++ b/src/native/corehost/fxr/fx_resolver.messages.cpp @@ -141,7 +141,7 @@ void fx_resolver_t::display_missing_framework_error( _X("Learn about framework resolution:\n") DOTNET_APP_LAUNCH_FAILED_URL _X("\n\n") - _X("Download framework:\n") + _X("To install missing framework, download:\n") _X("%s"), url.c_str()); } diff --git a/src/native/corehost/host_startup_info.h b/src/native/corehost/host_startup_info.h index 9571d1b4902ccd..98d22942f65e74 100644 --- a/src/native/corehost/host_startup_info.h +++ b/src/native/corehost/host_startup_info.h @@ -27,8 +27,8 @@ struct host_startup_info_t static int get_host_path(int argc, const pal::char_t* argv[], pal::string_t* host_path); pal::string_t host_path; // The path to the current hosting binary. - pal::string_t dotnet_root; // The path to the framework. - pal::string_t app_path; // For apphost, the path to the app dll; for muxer, not applicable as this information is not yet parsed. + pal::string_t dotnet_root; // The path to the .NET install. + pal::string_t app_path; // For apphost, the path to the app dll. For muxer, this is invalid and does not point to the app (the app path is not yet parsed). }; #endif // __HOST_STARTUP_INFO_H_