diff --git a/src/installer/tests/HostActivation.Tests/InstallLocation.cs b/src/installer/tests/HostActivation.Tests/InstallLocation.cs index cb2a15dd0a5836..7dcbeed1d343db 100644 --- a/src/installer/tests/HostActivation.Tests/InstallLocation.cs +++ b/src/installer/tests/HostActivation.Tests/InstallLocation.cs @@ -322,6 +322,51 @@ public void RegisteredInstallLocation_DotNetInfo_ListOtherArchitectures() } } + [Fact] + public void NotFound() + { + TestApp app = sharedTestState.TestBehaviourEnabledApp; + + // Ensure no install locations are registered + using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(app.AppExe)) + { + string defaultLocation = Path.GetTempPath(); + string registeredLocationOverride = OperatingSystem.IsWindows() // Host uses short form of base key for Windows + ? registeredInstallLocationOverride.PathValueOverride.Replace(Microsoft.Win32.Registry.CurrentUser.Name, "HKCU") + : registeredInstallLocationOverride.PathValueOverride; + Command.Create(app.AppExe) + .CaptureStdOut() + .CaptureStdErr() + .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride) + .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.DefaultInstallPath, defaultLocation) + .DotNetRoot(null) + .Execute() + .Should().Fail() + .And.HaveStdErrContaining("The following locations were searched:") + .And.HaveStdErrContaining( + $""" + Application directory: + {app.Location} + """) + .And.HaveStdErrContaining( + $""" + Environment variable: + DOTNET_ROOT_{TestContext.BuildArchitecture.ToUpper()} = + DOTNET_ROOT = + """) + .And.HaveStdErrMatching( + $""" + Registered location: + {System.Text.RegularExpressions.Regex.Escape(registeredLocationOverride)}.*{TestContext.BuildArchitecture}.* = + """) + .And.HaveStdErrContaining( + $""" + Default location: + {defaultLocation} + """); + } + } + [Theory] [InlineData(SearchLocation.AppLocal)] [InlineData(SearchLocation.AppRelative)] diff --git a/src/native/corehost/apphost/standalone/hostfxr_resolver.cpp b/src/native/corehost/apphost/standalone/hostfxr_resolver.cpp index e3f10e2b21dcfd..0fd8bc01269130 100644 --- a/src/native/corehost/apphost/standalone/hostfxr_resolver.cpp +++ b/src/native/corehost/apphost/standalone/hostfxr_resolver.cpp @@ -121,6 +121,7 @@ hostfxr_resolver_t::hostfxr_resolver_t(const pal::string_t& app_root) else if (!pal::is_path_fully_qualified(m_fxr_path)) { // We should always be loading hostfxr from an absolute path + trace::error(_X("Path to %s must be fully qualified: [%s]"), LIBFXR_NAME, m_fxr_path.c_str()); m_status_code = StatusCode::CoreHostLibMissingFailure; } else if (pal::load_library(&m_fxr_path, &m_hostfxr_dll)) diff --git a/src/native/corehost/corehost.cpp b/src/native/corehost/corehost.cpp index c186318d5f3cbb..aeae151be08770 100644 --- a/src/native/corehost/corehost.cpp +++ b/src/native/corehost/corehost.cpp @@ -212,10 +212,7 @@ int exe_start(const int argc, const pal::char_t* argv[]) // Obtain the entrypoints. int rc = fxr.status_code(); if (rc != StatusCode::Success) - { - trace::error(_X("Failed to resolve %s [%s]. Error code: 0x%x"), LIBFXR_NAME, fxr.fxr_path().empty() ? _X("not found") : fxr.fxr_path().c_str(), rc); return rc; - } #if defined(FEATURE_APPHOST) if (bundle_marker_t::is_bundle()) diff --git a/src/native/corehost/fxr_resolver.cpp b/src/native/corehost/fxr_resolver.cpp index 0d1cbddf9deb5c..dbc1d625f8a14c 100644 --- a/src/native/corehost/fxr_resolver.cpp +++ b/src/native/corehost/fxr_resolver.cpp @@ -93,7 +93,6 @@ bool fxr_resolver::try_get_path( bool search_app_relative = (search & search_location_app_relative) != 0 && app_relative_dotnet_root != nullptr && !app_relative_dotnet_root->empty(); bool search_env = (search & search_location_environment_variable) != 0; bool search_global = (search & search_location_global) != 0; - pal::string_t default_install_location; pal::string_t dotnet_root_env_var_name; if (search_app_relative && pal::fullpath(app_relative_dotnet_root)) { @@ -111,10 +110,11 @@ bool fxr_resolver::try_get_path( } else if (search_global) { - if (pal::get_dotnet_self_registered_dir(&default_install_location) || pal::get_default_installation_dir(&default_install_location)) + pal::string_t global_install_location; + if (pal::get_dotnet_self_registered_dir(&global_install_location) || pal::get_default_installation_dir(&global_install_location)) { - trace::info(_X("Using global install location [%s] as runtime location."), default_install_location.c_str()); - out_dotnet_root->assign(default_install_location); + trace::info(_X("Using global install location [%s] as runtime location."), global_install_location.c_str()); + out_dotnet_root->assign(global_install_location); } else { @@ -130,35 +130,7 @@ bool fxr_resolver::try_get_path( return get_latest_fxr(std::move(fxr_dir), out_fxr_path); // Failed to find hostfxr - if (trace::is_enabled()) - { - trace::verbose(_X("The required library %s could not be found. Search location options [0x%x]"), LIBFXR_NAME, search); - if (search_app_local) - trace::verbose(_X(" app-local: [%s]"), root_path.c_str()); - - if (search_app_relative) - trace::verbose(_X(" app-relative: [%s]"), app_relative_dotnet_root->c_str()); - - if (search_env) - trace::verbose(_X(" environment variable: [%s]"), dotnet_root_env_var_name.c_str()); - - if (search_global) - { - if (default_install_location.empty()) - { - pal::get_dotnet_self_registered_dir(&default_install_location); - } - if (default_install_location.empty()) - { - pal::get_default_installation_dir(&default_install_location); - } - - pal::string_t self_registered_config_location = pal::get_dotnet_self_registered_config_location(get_current_arch()); - trace::verbose(_X(" global install location [%s]\n self-registered config location [%s]"), - default_install_location.c_str(), - self_registered_config_location.c_str()); - } - } + trace::verbose(_X("The required library %s could not be found. Search location options [0x%x]"), LIBFXR_NAME, search); pal::string_t host_path; pal::get_own_executable_path(&host_path); @@ -187,6 +159,66 @@ bool fxr_resolver::try_get_path( } } + pal::string_t searched_locations = _X("The following locations were searched:"); + if (search_app_local && !root_path.empty()) + { + searched_locations.append(_X("\n Application directory:\n ")); + searched_locations.append(root_path); + } + + if (search_app_relative) + { + searched_locations.append(_X("\n App-relative location:\n ")); + searched_locations.append(*app_relative_dotnet_root); + } + + if (search_env) + { + searched_locations.append(_X("\n Environment variable:\n ")); + if (dotnet_root_env_var_name.empty()) + { + searched_locations.append(get_dotnet_root_env_var_for_arch(get_current_arch())); + searched_locations.append(_X(" = \n ")); + searched_locations.append(DOTNET_ROOT_ENV_VAR _X(" = ")); + } + else + { + searched_locations.append(dotnet_root_env_var_name); + searched_locations.append(_X(" = ")); + searched_locations.append(*out_dotnet_root); + } + } + + // Global locations are only searched if environment variables are not set + if (search_global && dotnet_root_env_var_name.empty()) + { + searched_locations.append(_X("\n Registered location:\n ")); + searched_locations.append(pal::get_dotnet_self_registered_config_location(get_current_arch())); + + pal::string_t self_registered_dir; + if (pal::get_dotnet_self_registered_dir(&self_registered_dir) && !self_registered_dir.empty()) + { + searched_locations.append(_X(" = ")); + searched_locations.append(self_registered_dir); + } + else + { + searched_locations.append(_X(" = ")); + } + + // Default install location is only searched if self-registered location is not set + if (self_registered_dir.empty()) + { + pal::string_t default_install_location; + pal::get_default_installation_dir(&default_install_location); + searched_locations.append(_X("\n Default location:\n ")); + searched_locations.append(default_install_location); + } + } + + location.append(_X("\n\n")); + location.append(searched_locations); + trace::error( MISSING_RUNTIME_ERROR_FORMAT, INSTALL_NET_ERROR_MESSAGE, diff --git a/src/native/corehost/hostmisc/utils.cpp b/src/native/corehost/hostmisc/utils.cpp index 664d56d6ae61f3..a48cd2d39f9bd0 100644 --- a/src/native/corehost/hostmisc/utils.cpp +++ b/src/native/corehost/hostmisc/utils.cpp @@ -360,23 +360,33 @@ pal::string_t get_dotnet_root_env_var_for_arch(pal::architecture arch) bool get_dotnet_root_from_env(pal::string_t* dotnet_root_env_var_name, pal::string_t* recv) { - *dotnet_root_env_var_name = get_dotnet_root_env_var_for_arch(get_current_arch()); - if (get_file_path_from_env(dotnet_root_env_var_name->c_str(), recv)) + pal::string_t env_var_name = get_dotnet_root_env_var_for_arch(get_current_arch()); + if (get_file_path_from_env(env_var_name.c_str(), recv)) + { + *dotnet_root_env_var_name = env_var_name; return true; + } #if defined(WIN32) if (pal::is_running_in_wow64()) { - *dotnet_root_env_var_name = _X("DOTNET_ROOT(x86)"); - if (get_file_path_from_env(dotnet_root_env_var_name->c_str(), recv)) + if (get_file_path_from_env(_X("DOTNET_ROOT(x86)"), recv)) + { + *dotnet_root_env_var_name = _X("DOTNET_ROOT(x86)"); return true; + } } #endif // If no architecture-specific environment variable was set // fallback to the default DOTNET_ROOT. - *dotnet_root_env_var_name = DOTNET_ROOT_ENV_VAR; - return get_file_path_from_env(dotnet_root_env_var_name->c_str(), recv); + if (get_file_path_from_env(DOTNET_ROOT_ENV_VAR, recv)) + { + *dotnet_root_env_var_name = DOTNET_ROOT_ENV_VAR; + return true; + } + + return false; } /**