From 79fd332bfcbdc1e98e9b783c48aacf6e620f0296 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Thu, 13 Sep 2018 16:18:20 -0500 Subject: [PATCH] Add graph support for multiple shared frameworks (dotnet/core-setup#4538) Commit migrated from https://github.com/dotnet/core-setup/commit/8fe49b4211abaa649784966a4f6b55a38a237d9e --- src/installer/corehost/cli/deps_resolver.cpp | 8 +- src/installer/corehost/cli/fx_definition.cpp | 6 +- src/installer/corehost/cli/fx_definition.h | 3 +- src/installer/corehost/cli/fx_reference.cpp | 104 +++++++ src/installer/corehost/cli/fx_reference.h | 107 +++++++ src/installer/corehost/cli/fxr/CMakeLists.txt | 2 + src/installer/corehost/cli/fxr/framework_info.cpp | 12 +- src/installer/corehost/cli/fxr/framework_info.h | 1 - src/installer/corehost/cli/fxr/fx_muxer.cpp | 330 +++++++++++++++------ src/installer/corehost/cli/fxr/fx_muxer.h | 45 ++- .../corehost/cli/fxr/fx_muxer.messages.cpp | 102 +++++++ src/installer/corehost/cli/fxr/fx_ver.cpp | 5 + src/installer/corehost/cli/fxr/fx_ver.h | 3 + src/installer/corehost/cli/fxr/sdk_info.cpp | 2 +- src/installer/corehost/cli/fxr/sdk_resolver.cpp | 10 +- .../corehost/cli/hostpolicy/CMakeLists.txt | 1 + src/installer/corehost/cli/libhost.cpp | 4 +- .../cli/roll_fwd_on_no_candidate_fx_option.h | 16 + src/installer/corehost/cli/runtime_config.cpp | 265 +++++++---------- src/installer/corehost/cli/runtime_config.h | 96 +----- src/installer/corehost/corehost.cpp | 6 +- src/installer/corehost/error_codes.h | 2 + .../GivenThatICareAboutLightupAppActivation.cs | 4 +- ...areAboutMultilevelSharedFxLookup.DepsVersion.cs | 2 +- .../GivenThatICareAboutMultilevelSharedFxLookup.cs | 260 ++++++++++++---- .../test/HostActivationTests/SharedFramework.cs | 21 +- 26 files changed, 965 insertions(+), 452 deletions(-) create mode 100644 src/installer/corehost/cli/fx_reference.cpp create mode 100644 src/installer/corehost/cli/fx_reference.h create mode 100644 src/installer/corehost/cli/fxr/fx_muxer.messages.cpp create mode 100644 src/installer/corehost/cli/roll_fwd_on_no_candidate_fx_option.h diff --git a/src/installer/corehost/cli/deps_resolver.cpp b/src/installer/corehost/cli/deps_resolver.cpp index 0d4bb8d..c2e0669 100644 --- a/src/installer/corehost/cli/deps_resolver.cpp +++ b/src/installer/corehost/cli/deps_resolver.cpp @@ -629,8 +629,8 @@ void deps_resolver_t::resolve_additional_deps(const hostpolicy_init_t& init) { for (int i = 1; i < m_fx_definitions.size(); ++i) { - fx_ver_t most_compatible_deps_folder_version(-1, -1, -1); - fx_ver_t framework_found_version(-1, -1, -1); + fx_ver_t most_compatible_deps_folder_version; + fx_ver_t framework_found_version; fx_ver_t::parse(m_fx_definitions[i]->get_found_version(), &framework_found_version); // We'll search deps directories in 'base_dir'/shared/fx_name/ for closest compatible patch version @@ -643,7 +643,7 @@ void deps_resolver_t::resolve_additional_deps(const hostpolicy_init_t& init) for (pal::string_t dir : deps_dirs) { - fx_ver_t ver(-1, -1, -1); + fx_ver_t ver; if (fx_ver_t::parse(dir, &ver)) { if (ver > most_compatible_deps_folder_version && @@ -656,7 +656,7 @@ void deps_resolver_t::resolve_additional_deps(const hostpolicy_init_t& init) } } - if (most_compatible_deps_folder_version == fx_ver_t(-1, -1, -1)) + if (most_compatible_deps_folder_version == fx_ver_t()) { trace::verbose(_X("No additional deps directory less than or equal to [%s] found with same major and minor version."), framework_found_version.as_str().c_str()); } diff --git a/src/installer/corehost/cli/fx_definition.cpp b/src/installer/corehost/cli/fx_definition.cpp index 64e1487..342a38c 100644 --- a/src/installer/corehost/cli/fx_definition.cpp +++ b/src/installer/corehost/cli/fx_definition.cpp @@ -26,11 +26,11 @@ fx_definition_t::fx_definition_t( void fx_definition_t::parse_runtime_config( const pal::string_t& path, const pal::string_t& dev_path, - const runtime_config_t* higher_layer_config, - const runtime_config_t* app_config + const fx_reference_t& fx_ref, + const fx_reference_t& override_settings ) { - m_runtime_config.parse(path, dev_path, higher_layer_config, app_config); + m_runtime_config.parse(path, dev_path, fx_ref, override_settings); } void fx_definition_t::parse_deps() diff --git a/src/installer/corehost/cli/fx_definition.h b/src/installer/corehost/cli/fx_definition.h index c4d38ac..4237bf1 100644 --- a/src/installer/corehost/cli/fx_definition.h +++ b/src/installer/corehost/cli/fx_definition.h @@ -12,7 +12,6 @@ class fx_definition_t { public: fx_definition_t(); - fx_definition_t( const pal::string_t& name, const pal::string_t& dir, @@ -24,7 +23,7 @@ public: const pal::string_t& get_found_version() const { return m_found_version; } const pal::string_t& get_dir() const { return m_dir; } const runtime_config_t& get_runtime_config() const { return m_runtime_config; } - void parse_runtime_config(const pal::string_t& path, const pal::string_t& dev_path, const runtime_config_t* higher_layer_config, const runtime_config_t* app_config); + void parse_runtime_config(const pal::string_t& path, const pal::string_t& dev_path, const fx_reference_t& fx_ref, const fx_reference_t& override_settings); const pal::string_t& get_deps_file() const { return m_deps_file; } void set_deps_file(const pal::string_t value) { m_deps_file = value; } diff --git a/src/installer/corehost/cli/fx_reference.cpp b/src/installer/corehost/cli/fx_reference.cpp new file mode 100644 index 0000000..906d3f7 --- /dev/null +++ b/src/installer/corehost/cli/fx_reference.cpp @@ -0,0 +1,104 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "pal.h" +#include "fx_ver.h" +#include "fx_reference.h" +#include "roll_fwd_on_no_candidate_fx_option.h" + +bool fx_reference_t::is_roll_forward_compatible(const fx_ver_t& other) const +{ + // We expect the version to be < + assert(get_fx_version_number() < other); + + if (get_fx_version_number() == other) + { + return true; + } + + if (get_use_exact_version()) + { + return false; + } + + // Verify major roll forward + if (get_fx_version_number().get_major() != other.get_major() + && roll_fwd_on_no_candidate_fx != roll_fwd_on_no_candidate_fx_option::major) + { + return false; + } + + // Verify minor roll forward + if (get_fx_version_number().get_minor() != other.get_minor() + && roll_fwd_on_no_candidate_fx != roll_fwd_on_no_candidate_fx_option::major + && roll_fwd_on_no_candidate_fx != roll_fwd_on_no_candidate_fx_option::minor) + { + return false; + } + + // Verify patch roll forward + // We do not distinguish here whether a previous framework reference found a patch version based on: + // - initial reference matching a patch version, + // - or roll_fwd_on_no_candidate_fx_option=major\minor finding a compatible patch version as initial framework, + // - or applyPatches=true finding a newer patch version + if (get_fx_version_number().get_patch() != other.get_patch() + && patch_roll_fwd == false + && roll_fwd_on_no_candidate_fx != roll_fwd_on_no_candidate_fx_option::major + && roll_fwd_on_no_candidate_fx != roll_fwd_on_no_candidate_fx_option::minor) + { + return false; + } + + // Release cannot roll forward to pre-release + if (!get_fx_version_number().is_prerelease() && other.is_prerelease()) + { + return false; + } + + return true; +} + +void fx_reference_t::apply_settings_from(const fx_reference_t& from) +{ + if (from.get_fx_version().length() > 0) + { + set_fx_version(from.get_fx_version()); + } + + const roll_fwd_on_no_candidate_fx_option* from_rollfwd = from.get_roll_fwd_on_no_candidate_fx(); + if (from_rollfwd != nullptr) + { + set_roll_fwd_on_no_candidate_fx(*from_rollfwd); + } + + const bool* from_patch = from.get_patch_roll_fwd(); + if (from_patch != nullptr) + { + set_patch_roll_fwd(*from_patch); + } +} + +void fx_reference_t::merge_roll_forward_settings_from(const fx_reference_t& from) +{ + const roll_fwd_on_no_candidate_fx_option* from_rollfwd = from.get_roll_fwd_on_no_candidate_fx(); + if (from_rollfwd != nullptr) + { + const roll_fwd_on_no_candidate_fx_option* to_rollfwd = get_roll_fwd_on_no_candidate_fx(); + if (to_rollfwd == nullptr || + *from_rollfwd < *to_rollfwd) + { + set_roll_fwd_on_no_candidate_fx(*from_rollfwd); + } + } + + const bool* from_patch = from.get_patch_roll_fwd(); + if (from_patch != nullptr) + { + const bool* to_patch = get_patch_roll_fwd(); + if (to_patch == nullptr || + *from_patch == false) + { + set_patch_roll_fwd(*from_patch); + } + } +} diff --git a/src/installer/corehost/cli/fx_reference.h b/src/installer/corehost/cli/fx_reference.h new file mode 100644 index 0000000..2fbb501 --- /dev/null +++ b/src/installer/corehost/cli/fx_reference.h @@ -0,0 +1,107 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef __FX_REFERENCE_H__ +#define __FX_REFERENCE_H__ + +#include +#include "pal.h" +#include "fx_ver.h" +#include "roll_fwd_on_no_candidate_fx_option.h" + +class fx_reference_t +{ +public: + fx_reference_t() + : fx_name(_X("")) + , fx_version(_X("")) + , fx_version_number() + , has_patch_roll_fwd(false) + , patch_roll_fwd(false) + , has_roll_fwd_on_no_candidate_fx(false) + , use_exact_version(false) + , roll_fwd_on_no_candidate_fx((roll_fwd_on_no_candidate_fx_option)0) + { } + + const pal::string_t& get_fx_name() const + { + return fx_name; + } + void set_fx_name(const pal::string_t& value) + { + fx_name = value; + } + + const pal::string_t& get_fx_version() const + { + return fx_version; + } + void set_fx_version(const pal::string_t& value) + { + fx_version = value; + + fx_ver_t::parse(fx_version, &fx_version_number); + } + + const fx_ver_t& get_fx_version_number() const + { + return fx_version_number; + } + + const bool* get_patch_roll_fwd() const + { + return (has_patch_roll_fwd ? &patch_roll_fwd : nullptr); + } + void set_patch_roll_fwd(bool value) + { + has_patch_roll_fwd = true; + patch_roll_fwd = value; + } + + const bool get_use_exact_version() const + { + return use_exact_version; + } + void set_use_exact_version(bool value) + { + use_exact_version = value; + } + + const roll_fwd_on_no_candidate_fx_option* get_roll_fwd_on_no_candidate_fx() const + { + return (has_roll_fwd_on_no_candidate_fx ? &roll_fwd_on_no_candidate_fx : nullptr); + } + void set_roll_fwd_on_no_candidate_fx(roll_fwd_on_no_candidate_fx_option value) + { + has_roll_fwd_on_no_candidate_fx = true; + roll_fwd_on_no_candidate_fx = value; + } + + // Is the current version compatible with another instance with roll-forward semantics. + bool is_roll_forward_compatible(const fx_ver_t& other) const; + + // Copy over any non-null values + void apply_settings_from(const fx_reference_t& from); + + // Apply the most restrictive settings + void merge_roll_forward_settings_from(const fx_reference_t& from); + +private: + bool has_patch_roll_fwd; + bool patch_roll_fwd; + + bool has_roll_fwd_on_no_candidate_fx; + roll_fwd_on_no_candidate_fx_option roll_fwd_on_no_candidate_fx; + + bool use_exact_version; + + pal::string_t fx_name; + + pal::string_t fx_version; + fx_ver_t fx_version_number; +}; + +typedef std::vector fx_reference_vector_t; +typedef std::unordered_map fx_name_to_fx_reference_map_t; + +#endif // __FX_REFERENCE_H__ diff --git a/src/installer/corehost/cli/fxr/CMakeLists.txt b/src/installer/corehost/cli/fxr/CMakeLists.txt index 3d554bb..9d02b8a 100644 --- a/src/installer/corehost/cli/fxr/CMakeLists.txt +++ b/src/installer/corehost/cli/fxr/CMakeLists.txt @@ -39,10 +39,12 @@ set(SOURCES ../json/casablanca/src/json/json_serialization.cpp ../json/casablanca/src/utilities/asyncrt_utils.cpp ../fx_definition.cpp + ../fx_reference.cpp ../version.cpp ./hostfxr.cpp ./fx_ver.cpp ./fx_muxer.cpp + ./fx_muxer.messages.cpp ./framework_info.cpp ./sdk_info.cpp ./sdk_resolver.cpp diff --git a/src/installer/corehost/cli/fxr/framework_info.cpp b/src/installer/corehost/cli/fxr/framework_info.cpp index e4de608..74a9008 100644 --- a/src/installer/corehost/cli/fxr/framework_info.cpp +++ b/src/installer/corehost/cli/fxr/framework_info.cpp @@ -23,18 +23,10 @@ bool compare_by_name_and_version(const framework_info &a, const framework_info & } /*static*/ void framework_info::get_all_framework_infos( - host_mode_t mode, const pal::string_t& own_dir, const pal::string_t& fx_name, std::vector* framework_infos) { - // No FX resolution for mixed apps - if (mode == host_mode_t::split_fx) - { - trace::verbose(_X("Split/FX mode detected. Not gathering shared FX locations")); - return; - } - std::vector global_dirs; bool multilevel_lookup = multilevel_lookup_enabled(); @@ -89,7 +81,7 @@ bool compare_by_name_and_version(const framework_info &a, const framework_info & for (const auto& ver : versions) { // Make sure we filter out any non-version folders. - fx_ver_t parsed(-1, -1, -1); + fx_ver_t parsed; if (fx_ver_t::parse(ver, &parsed, false)) { trace::verbose(_X("Found FX version [%s]"), ver.c_str()); @@ -109,7 +101,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(host_mode_t::muxer, own_dir, _X(""), &framework_infos); + get_all_framework_infos(own_dir, _X(""), &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/installer/corehost/cli/fxr/framework_info.h b/src/installer/corehost/cli/fxr/framework_info.h index 9aaea39..d502f95 100644 --- a/src/installer/corehost/cli/fxr/framework_info.h +++ b/src/installer/corehost/cli/fxr/framework_info.h @@ -14,7 +14,6 @@ struct framework_info , version(version) { } static void get_all_framework_infos( - host_mode_t mode, const pal::string_t& own_dir, const pal::string_t& fx_name, std::vector* framework_infos); diff --git a/src/installer/corehost/cli/fxr/fx_muxer.cpp b/src/installer/corehost/cli/fxr/fx_muxer.cpp index 27bda32..fb40f30 100644 --- a/src/installer/corehost/cli/fxr/fx_muxer.cpp +++ b/src/installer/corehost/cli/fxr/fx_muxer.cpp @@ -9,6 +9,7 @@ #include "framework_info.h" #include "fx_definition.h" #include "fx_muxer.h" +#include "fx_reference.h" #include "fx_ver.h" #include "host_startup_info.h" #include "libhost.h" @@ -23,8 +24,7 @@ * When the framework is not found, display detailed error message * about available frameworks and installation of new framework. */ -void handle_missing_framework_error( - host_mode_t mode, +void fx_muxer_t::display_missing_framework_error( const pal::string_t& fx_name, const pal::string_t& fx_version, const pal::string_t& fx_dir, @@ -35,14 +35,14 @@ void handle_missing_framework_error( if (fx_dir.length()) { fx_ver_dirs = fx_dir; - framework_info::get_all_framework_infos(mode, get_directory(fx_dir), fx_name, &framework_infos); + framework_info::get_all_framework_infos(get_directory(fx_dir), fx_name, &framework_infos); } else { fx_ver_dirs = dotnet_root; } - framework_info::get_all_framework_infos(mode, dotnet_root, fx_name, &framework_infos); + framework_info::get_all_framework_infos(dotnet_root, fx_name, &framework_infos); // Display the error message about missing FX. if (fx_version.length()) @@ -64,7 +64,7 @@ void handle_missing_framework_error( // Gather the list of versions installed at the shared FX location. bool is_print_header = true; - for (framework_info info : framework_infos) + for (const framework_info& info : framework_infos) { // Print banner only once before printing the versions if (is_print_header) @@ -274,7 +274,6 @@ bool fx_muxer_t::resolve_hostpolicy_dir( const fx_definition_vector_t& fx_definitions, const pal::string_t& app_candidate, const pal::string_t& specified_deps_file, - const pal::string_t& specified_fx_version, const std::vector& probe_realpaths, pal::string_t* impl_dir) { @@ -345,7 +344,7 @@ bool fx_muxer_t::resolve_hostpolicy_dir( trace::error(_X("Failed to run as a self-contained app. If this should be a framework-dependent app, add the %s file specifying the appropriate framework."), get_app(fx_definitions).get_runtime_config().get_path().c_str()); } - else if (get_app(fx_definitions).get_runtime_config().get_fx_name().empty()) + else if (get_app(fx_definitions).get_name().empty()) { trace::error(_X("Failed to run as a self-contained app. If this should be a framework-dependent app, specify the appropriate framework in %s."), get_app(fx_definitions).get_runtime_config().get_path().c_str()); @@ -367,7 +366,7 @@ fx_ver_t fx_muxer_t::resolve_framework_version(const std::vector& vers { if (roll_fwd_on_no_candidate_fx != roll_fwd_on_no_candidate_fx_option::disabled) { - fx_ver_t next_lowest(-1, -1, -1); + fx_ver_t next_lowest; // Look for the least production version trace::verbose(_X("'Roll forward on no candidate fx' enabled with value [%d]. Looking for the least production greater than or equal to [%s]"), @@ -385,11 +384,11 @@ fx_ver_t fx_muxer_t::resolve_framework_version(const std::vector& vers continue; } } - next_lowest = (next_lowest == fx_ver_t(-1, -1, -1)) ? ver : std::min(next_lowest, ver); + next_lowest = (next_lowest == fx_ver_t()) ? ver : std::min(next_lowest, ver); } } - if (next_lowest == fx_ver_t(-1, -1, -1)) + if (next_lowest == fx_ver_t()) { // Look for the least preview version trace::verbose(_X("No production greater than or equal to [%s] found. Looking for the least preview greater than [%s]"), @@ -407,12 +406,12 @@ fx_ver_t fx_muxer_t::resolve_framework_version(const std::vector& vers continue; } } - next_lowest = (next_lowest == fx_ver_t(-1, -1, -1)) ? ver : std::min(next_lowest, ver); + next_lowest = (next_lowest == fx_ver_t()) ? ver : std::min(next_lowest, ver); } } } - if (next_lowest == fx_ver_t(-1, -1, -1)) + if (next_lowest == fx_ver_t()) { trace::verbose(_X("No preview greater than or equal to [%s] found."), fx_ver.c_str()); } @@ -442,11 +441,11 @@ fx_ver_t fx_muxer_t::resolve_framework_version(const std::vector& vers } else { + // pre-release has its own roll forward rules and ignores roll_fwd_on_no_candidate_fx and patch_roll_fwd for (const auto& ver : version_list) { trace::verbose(_X("Inspecting version... [%s]"), ver.as_str().c_str()); - //both production and prerelease. if (ver.is_prerelease() && // prevent roll forward to production. ver.get_major() == specified.get_major() && ver.get_minor() == specified.get_minor() && @@ -463,26 +462,21 @@ fx_ver_t fx_muxer_t::resolve_framework_version(const std::vector& vers } fx_definition_t* fx_muxer_t::resolve_fx( - host_mode_t mode, - const runtime_config_t& config, - const pal::string_t& dotnet_dir, - const pal::string_t& specified_fx_version + const fx_reference_t& fx_ref, + const pal::string_t& oldest_requested_version, + const pal::string_t& dotnet_dir ) { - // If invoking using FX dotnet.exe, use own directory. - if (mode == host_mode_t::split_fx) - { - return new fx_definition_t(config.get_fx_name(), dotnet_dir, pal::string_t(), pal::string_t()); - } - - assert(!config.get_fx_name().empty()); - assert(!config.get_fx_version().empty()); + assert(!fx_ref.get_fx_name().empty()); + assert(!fx_ref.get_fx_version().empty()); + assert(fx_ref.get_patch_roll_fwd() != nullptr); + assert(fx_ref.get_roll_fwd_on_no_candidate_fx() != nullptr); trace::verbose(_X("--- Resolving FX directory, name '%s' version '%s'"), - config.get_fx_name().c_str(), config.get_fx_version().c_str()); + fx_ref.get_fx_name().c_str(), fx_ref.get_fx_version().c_str()); - const auto fx_ver = specified_fx_version.empty() ? config.get_fx_version() : specified_fx_version; - fx_ver_t specified(-1, -1, -1); + const auto fx_ver = fx_ref.get_fx_version(); + fx_ver_t specified; if (!fx_ver_t::parse(fx_ver, &specified, false)) { trace::error(_X("The specified framework version '%s' could not be parsed"), fx_ver.c_str()); @@ -518,7 +512,7 @@ fx_definition_t* fx_muxer_t::resolve_fx( pal::string_t selected_fx_dir; pal::string_t selected_fx_version; - fx_ver_t selected_ver(-1, -1, -1); + fx_ver_t selected_ver; for (pal::string_t dir : hive_dir) { @@ -526,15 +520,15 @@ fx_definition_t* fx_muxer_t::resolve_fx( trace::verbose(_X("Searching FX directory in [%s]"), fx_dir.c_str()); append_path(&fx_dir, _X("shared")); - append_path(&fx_dir, config.get_fx_name().c_str()); + append_path(&fx_dir, fx_ref.get_fx_name().c_str()); bool do_roll_forward = false; - if (specified_fx_version.empty()) + if (!fx_ref.get_use_exact_version()) { if (!specified.is_prerelease()) { // If production and no roll forward use given version. - do_roll_forward = (config.get_patch_roll_fwd()) || (config.get_roll_fwd_on_no_candidate_fx() != roll_fwd_on_no_candidate_fx_option::disabled); + do_roll_forward = (*(fx_ref.get_patch_roll_fwd())) || (*(fx_ref.get_roll_fwd_on_no_candidate_fx()) != roll_fwd_on_no_candidate_fx_option::disabled); } else { @@ -547,8 +541,8 @@ fx_definition_t* fx_muxer_t::resolve_fx( if (!do_roll_forward) { - trace::verbose(_X("Did not roll forward because specified version='%s', patch_roll_fwd=%d, roll_fwd_on_no_candidate_fx=%d, chose [%s]"), - specified_fx_version.c_str(), config.get_patch_roll_fwd(), config.get_roll_fwd_on_no_candidate_fx(), fx_ver.c_str()); + trace::verbose(_X("Did not roll forward because patch_roll_fwd=%d, roll_fwd_on_no_candidate_fx=%d, use_exact_version=%d chose [%s]"), + *(fx_ref.get_patch_roll_fwd()), *(fx_ref.get_roll_fwd_on_no_candidate_fx()), fx_ref.get_use_exact_version(), fx_ver.c_str()); append_path(&fx_dir, fx_ver.c_str()); if (pal::directory_exists(fx_dir)) @@ -566,27 +560,27 @@ fx_definition_t* fx_muxer_t::resolve_fx( for (const auto& version : list) { - fx_ver_t ver(-1, -1, -1); + fx_ver_t ver; if (fx_ver_t::parse(version, &ver, false)) { version_list.push_back(ver); } } - fx_ver_t resolved_ver = resolve_framework_version(version_list, fx_ver, specified, config.get_patch_roll_fwd(), config.get_roll_fwd_on_no_candidate_fx()); + fx_ver_t resolved_ver = resolve_framework_version(version_list, fx_ver, specified, *(fx_ref.get_patch_roll_fwd()), *(fx_ref.get_roll_fwd_on_no_candidate_fx())); pal::string_t resolved_ver_str = resolved_ver.as_str(); append_path(&fx_dir, resolved_ver_str.c_str()); if (pal::directory_exists(fx_dir)) { - if (selected_ver != fx_ver_t(-1, -1, -1)) + if (selected_ver != fx_ver_t()) { // Compare the previous hive_dir selection with the current hive_dir to see which one is the better match std::vector version_list; version_list.push_back(resolved_ver); version_list.push_back(selected_ver); - resolved_ver = resolve_framework_version(version_list, fx_ver, specified, config.get_patch_roll_fwd(), config.get_roll_fwd_on_no_candidate_fx()); + resolved_ver = resolve_framework_version(version_list, fx_ver, specified, *(fx_ref.get_patch_roll_fwd()), *(fx_ref.get_roll_fwd_on_no_candidate_fx())); } if (resolved_ver != selected_ver) @@ -608,7 +602,7 @@ fx_definition_t* fx_muxer_t::resolve_fx( trace::verbose(_X("Chose FX version [%s]"), selected_fx_dir.c_str()); - return new fx_definition_t(config.get_fx_name(), selected_fx_dir, fx_ver, selected_fx_version); + return new fx_definition_t(fx_ref.get_fx_name(), selected_fx_dir, oldest_requested_version, selected_fx_version); } bool is_sdk_dir_present(const pal::string_t& dotnet_root) @@ -828,7 +822,8 @@ int fx_muxer_t::parse_args( int read_config( fx_definition_t& app, const pal::string_t& app_candidate, - pal::string_t& runtime_config + pal::string_t& runtime_config, + const fx_reference_t& override_settings ) { if (!runtime_config.empty() && !pal::realpath(&runtime_config)) @@ -850,7 +845,7 @@ int read_config( get_runtime_config_paths_from_arg(runtime_config, &config_file, &dev_config_file); } - app.parse_runtime_config(config_file, dev_config_file, nullptr, nullptr); + app.parse_runtime_config(config_file, dev_config_file, fx_reference_t(), override_settings); if (!app.get_runtime_config().is_valid()) { trace::error(_X("Invalid runtimeconfig.json [%s] [%s]"), app.get_runtime_config().get_path().c_str(), app.get_runtime_config().get_dev_path().c_str()); @@ -860,6 +855,168 @@ int read_config( return 0; } +int fx_muxer_t::soft_roll_forward_helper( + const fx_reference_t& newer, + const fx_reference_t& older, + bool older_is_hard_roll_forward, + fx_name_to_fx_reference_map_t& newest_references, + fx_name_to_fx_reference_map_t& oldest_references) +{ + const pal::string_t& fx_name = newer.get_fx_name(); + fx_reference_t updated_newest = newer; + + if (older.get_fx_version_number() == newer.get_fx_version_number()) + { + updated_newest.merge_roll_forward_settings_from(older); + newest_references[fx_name] = updated_newest; + return 0; + } + + if (older.is_roll_forward_compatible(newer.get_fx_version_number())) + { + updated_newest.merge_roll_forward_settings_from(older); + newest_references[fx_name] = updated_newest; + + auto oldest = oldest_references[fx_name]; + if (older.get_fx_version_number() < oldest.get_fx_version_number()) + { + oldest_references[fx_name] = older; + } + + if (older_is_hard_roll_forward) + { + display_retry_framework_trace(older, newer); + return FrameworkCompatRetry; + } + + display_compatible_framework_trace(newer.get_fx_version(), older); + return 0; + } + + // Error condition - not compatible with the other reference + display_incompatible_framework_error(newer.get_fx_version(), older); + return FrameworkCompatFailure; +} + +// Peform a "soft" roll-forward meaning we don't read any physical framework folders +// and just check if the older reference is compatible with the newer reference +// with respect to roll-forward\applypatches. +int fx_muxer_t::soft_roll_forward( + const fx_reference_t fx_ref, //byval to avoid side-effects with mutable newest_references and oldest_references + bool current_is_hard_roll_forward, // true if reference was obtained from a "real" roll-forward meaning it probed the disk to find the most compatible version + fx_name_to_fx_reference_map_t& newest_references, + fx_name_to_fx_reference_map_t& oldest_references) +{ + /*byval*/ fx_reference_t current_ref = newest_references[fx_ref.get_fx_name()]; + + // Perform soft "in-memory" roll-forwards + if (fx_ref.get_fx_version_number() >= current_ref.get_fx_version_number()) + { + return soft_roll_forward_helper(fx_ref, current_ref, current_is_hard_roll_forward, newest_references, oldest_references); + } + + assert(fx_ref.get_fx_version_number() < current_ref.get_fx_version_number()); + return soft_roll_forward_helper(current_ref, fx_ref, false, newest_references, oldest_references); +} + +int fx_muxer_t::read_framework( + const host_startup_info_t& host_info, + const fx_reference_t& override_settings, + const runtime_config_t& config, + fx_name_to_fx_reference_map_t& newest_references, + fx_name_to_fx_reference_map_t& oldest_references, + fx_definition_vector_t& fx_definitions) +{ + // Loop through each reference and update the list of newest references before we resolve_fx. + // This reconciles duplicate references to minimize the number of resolve retries. + for (const fx_reference_t& fx_ref : config.get_frameworks()) + { + const pal::string_t& fx_name = fx_ref.get_fx_name(); + auto temp_ref = newest_references.find(fx_name); + if (temp_ref == newest_references.end()) + { + newest_references.insert({fx_name, fx_ref}); + oldest_references.insert({fx_name, fx_ref}); + } + } + + int rc = 0; + + // Loop through each reference and resolve the framework + for (const fx_reference_t& fx_ref : config.get_frameworks()) + { + const pal::string_t& fx_name = fx_ref.get_fx_name(); + + auto existing_framework = std::find_if( + fx_definitions.begin(), + fx_definitions.end(), + [&](const std::unique_ptr& fx) { return fx_name == fx->get_name(); }); + + if (existing_framework == fx_definitions.end()) + { + // Perform a "soft" roll-forward meaning we don't read any physical framework folders yet + rc = soft_roll_forward(fx_ref, false, newest_references, oldest_references); + if (rc) + { + break; // Error case + } + + const pal::string_t& oldest_requested_version = oldest_references[fx_name].get_fx_version(); + fx_reference_t& newest_ref = newest_references[fx_name]; + + // Resolve the framwork against the the existing physical framework folders + fx_definition_t* fx = resolve_fx(newest_ref, oldest_requested_version, host_info.dotnet_root); + if (fx == nullptr) + { + display_missing_framework_error(fx_name, newest_ref.get_fx_version(), pal::string_t(), host_info.dotnet_root); + return FrameworkMissingFailure; + } + + // Update the newest version based on the hard version found + newest_ref.set_fx_version(fx->get_found_version()); + + fx_definitions.push_back(std::unique_ptr(fx)); + + // Recursively process the base frameworks + pal::string_t config_file; + pal::string_t dev_config_file; + get_runtime_config_paths(fx->get_dir(), fx_name, &config_file, &dev_config_file); + fx->parse_runtime_config(config_file, dev_config_file, newest_ref, override_settings); + + runtime_config_t new_config = fx->get_runtime_config(); + if (!new_config.is_valid()) + { + trace::error(_X("Invalid framework config.json [%s]"), new_config.get_path().c_str()); + return StatusCode::InvalidConfigFile; + } + + rc = read_framework(host_info, override_settings, new_config, newest_references, oldest_references, fx_definitions); + if (rc) + { + break; // Error case + } + } + else + { + // Perform a "soft" roll-forward meaning we don't read any physical framework folders yet + rc = soft_roll_forward(fx_ref, true, newest_references, oldest_references); + if (rc) + { + break; // Error or retry case + } + + fx_reference_t& newest_ref = newest_references[fx_name]; + if (fx_ref.get_fx_version_number() == newest_ref.get_fx_version_number()) + { + // Success but move it to the back (without calling dtors) so that lower-level frameworks come last including Microsoft.NetCore.App + std::rotate(existing_framework, existing_framework + 1, fx_definitions.end()); + } + } + } + + return rc; +} + int fx_muxer_t::read_config_and_execute( const pal::string_t& host_command, const host_startup_info_t& host_info, @@ -879,7 +1036,6 @@ int fx_muxer_t::read_config_and_execute( pal::string_t opts_additional_deps = _X("--additional-deps"); pal::string_t opts_runtime_config = _X("--runtimeconfig"); - pal::string_t fx_version_specified; pal::string_t roll_fwd_on_no_candidate_fx; pal::string_t deps_file = get_last_known_arg(opts, opts_deps_file, _X("")); pal::string_t additional_deps; @@ -892,12 +1048,26 @@ int fx_muxer_t::read_config_and_execute( return StatusCode::InvalidArgFailure; } + fx_reference_t override_settings; + + // 'Roll forward on no candidate fx' is set to 1 (roll_fwd_on_no_candidate_fx_option::minor) by default. It can be changed through: + // 1. Command line argument (--roll-forward-on-no-candidate-fx). + // 2. Runtimeconfig json file ('rollForwardOnNoCandidateFx' property in "framework" section). + // 3. Runtimeconfig json file ('rollForwardOnNoCandidateFx' property in a referencing "frameworks" section). + // 4. DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX env var. + // The conflicts will be resolved by following the priority rank described above (from 1 to 4). + // The env var condition is verified in the config file processing + roll_fwd_on_no_candidate_fx = get_last_known_arg(opts, opts_roll_fwd_on_no_candidate_fx, _X("")); + if (roll_fwd_on_no_candidate_fx.length() > 0) + { + override_settings.set_roll_fwd_on_no_candidate_fx(static_cast(pal::xtoi(roll_fwd_on_no_candidate_fx.c_str()))); + } + // Read config fx_definition_vector_t fx_definitions; auto app = new fx_definition_t(); fx_definitions.push_back(std::unique_ptr(app)); - - int rc = read_config(*app, app_candidate, runtime_config); + int rc = read_config(*app, app_candidate, runtime_config, override_settings); if (rc) { return rc; @@ -906,28 +1076,18 @@ int fx_muxer_t::read_config_and_execute( auto app_config = app->get_runtime_config(); bool is_framework_dependent = app_config.get_is_framework_dependent(); - // These settings are only valid for framework-dependent apps + // Apply the --fx-version option to the first framework if (is_framework_dependent) { - fx_version_specified = get_last_known_arg(opts, opts_fx_version, _X("")); - roll_fwd_on_no_candidate_fx = get_last_known_arg(opts, opts_roll_fwd_on_no_candidate_fx, _X("")); - additional_deps = get_last_known_arg(opts, opts_additional_deps, _X("")); - } - - // 'Roll forward on no candidate fx' is set to 1 (roll_fwd_on_no_candidate_fx_option::minor) by default. It can be changed through: - // 1. Command line argument (--roll-forward-on-no-candidate-fx). - // 2. Runtimeconfig json file ('rollForwardOnNoCandidateFx' property in "framework" section:). - // 3. Runtimeconfig json file ('rollForwardOnNoCandidateFx' property), which is used as a default for lower level frameworks if they don't specify a value. - // 4. DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX env var. Only defaults the app's config. - // The conflicts will be resolved by following the priority rank described above (from 1 to 4). - // The env var condition is verified in the config file processing - if (!roll_fwd_on_no_candidate_fx.empty()) - { - app_config.force_roll_fwd_on_no_candidate_fx(static_cast(pal::xtoi(roll_fwd_on_no_candidate_fx.c_str()))); + pal::string_t fx_version_specified = get_last_known_arg(opts, opts_fx_version, _X("")); + if (fx_version_specified.length() > 0) + { + // This will also set roll forward defaults on the ref + app_config.set_fx_version(fx_version_specified); + } } - auto config = app_config; - + additional_deps = get_last_known_arg(opts, opts_additional_deps, _X("")); pal::string_t additional_deps_serialized; if (is_framework_dependent) { @@ -939,34 +1099,34 @@ int fx_muxer_t::read_config_and_execute( pal::getenv(_X("DOTNET_ADDITIONAL_DEPS"), &additional_deps_serialized); } - // Obtain frameworks\platforms - auto version = fx_version_specified; - while (!config.get_fx_name().empty() && !config.get_fx_version().empty()) + // If invoking using FX dotnet.exe, use own directory. + if (mode == host_mode_t::split_fx) { - fx_definition_t* fx = resolve_fx(mode, config, host_info.dotnet_root, version); - if (fx == nullptr) - { - pal::string_t searched_version = fx_version_specified.empty() ? config.get_fx_version() : fx_version_specified; - handle_missing_framework_error(mode, config.get_fx_name(), searched_version, pal::string_t(), host_info.dotnet_root); - return FrameworkMissingFailure; - } - + auto fx = new fx_definition_t(app_config.get_frameworks()[0].get_fx_name(), host_info.dotnet_root, pal::string_t(), pal::string_t()); fx_definitions.push_back(std::unique_ptr(fx)); + } + else + { + fx_name_to_fx_reference_map_t newest_references; + fx_name_to_fx_reference_map_t oldest_references; - pal::string_t config_file; - pal::string_t dev_config_file; - get_runtime_config_paths(fx->get_dir(), config.get_fx_name(), &config_file, &dev_config_file); - fx->parse_runtime_config(config_file, dev_config_file, &config, &app_config); + // Read the shared frameworks; retry is necessary when a framework is already resolved, but then a newer compatible version is processed. + int rc = 0; + int retry_count = 0; + do + { + fx_definitions.resize(1); // Erase any existing frameworks for re-try + rc = read_framework(host_info, override_settings, app_config, newest_references, oldest_references, fx_definitions); + } while (rc == FrameworkCompatRetry && retry_count++ < Max_Framework_Resolve_Retries); + + assert(retry_count < Max_Framework_Resolve_Retries); - config = fx->get_runtime_config(); - if (!config.is_valid()) + if (rc) { - trace::error(_X("Invalid framework config.json [%s]"), config.get_path().c_str()); - return StatusCode::InvalidConfigFile; + return rc; } - // Only the first framework can have a specified version (through --fx-version) - version.clear(); + display_summary_of_frameworks(fx_definitions, newest_references); } } @@ -991,10 +1151,10 @@ int fx_muxer_t::read_config_and_execute( } trace::verbose(_X("Executing as a %s app as per config file [%s]"), - (is_framework_dependent ? _X("framework-dependent") : _X("self-contained")), config.get_path().c_str()); + (is_framework_dependent ? _X("framework-dependent") : _X("self-contained")), app_config.get_path().c_str()); pal::string_t impl_dir; - if (!resolve_hostpolicy_dir(mode, host_info.dotnet_root, fx_definitions, app_candidate, deps_file, fx_version_specified, probe_realpaths, &impl_dir)) + if (!resolve_hostpolicy_dir(mode, host_info.dotnet_root, fx_definitions, app_candidate, deps_file, probe_realpaths, &impl_dir)) { return CoreHostLibMissingFailure; } diff --git a/src/installer/corehost/cli/fxr/fx_muxer.h b/src/installer/corehost/cli/fxr/fx_muxer.h index 3ad5c77..9d0a0b76 100644 --- a/src/installer/corehost/cli/fxr/fx_muxer.h +++ b/src/installer/corehost/cli/fxr/fx_muxer.h @@ -9,6 +9,8 @@ struct host_startup_info_t; #include "libhost.h" +const int Max_Framework_Resolve_Retries = 100; + int execute_app( const pal::string_t& impl_dll_dir, corehost_init_t* init, @@ -92,7 +94,6 @@ private: const fx_definition_vector_t& fx_definitions, const pal::string_t& app_candidate, const pal::string_t& specified_deps_file, - const pal::string_t& specified_fx_version, const std::vector& probe_realpaths, pal::string_t* impl_dir); static fx_ver_t resolve_framework_version( @@ -101,10 +102,44 @@ private: const fx_ver_t& specified, bool patch_roll_fwd, roll_fwd_on_no_candidate_fx_option roll_fwd_on_no_candidate_fx); - static fx_definition_t* resolve_fx( - host_mode_t mode, + static int read_framework( + const host_startup_info_t& host_info, + const fx_reference_t& override_settings, const runtime_config_t& config, - const pal::string_t& dotnet_dir, - const pal::string_t& specified_fx_version); + fx_name_to_fx_reference_map_t& newest_references, + fx_name_to_fx_reference_map_t& oldest_references, + fx_definition_vector_t& fx_definitions); + static fx_definition_t* resolve_fx( + const fx_reference_t& config, + const pal::string_t& oldest_requested_version, + const pal::string_t& dotnet_dir); static void muxer_usage(bool is_sdk_present); + static int soft_roll_forward_helper( + const fx_reference_t& newer, + const fx_reference_t& older, + bool older_is_hard_roll_forward, + fx_name_to_fx_reference_map_t& newest_references, + fx_name_to_fx_reference_map_t& oldest_references); + static int soft_roll_forward( + const fx_reference_t existing_ref, + bool current_is_hard_roll_forward, + fx_name_to_fx_reference_map_t& newest_references, + fx_name_to_fx_reference_map_t& oldest_references); + static void 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); + static void display_incompatible_framework_error( + const pal::string_t& higher, + const fx_reference_t& lower); + static void display_compatible_framework_trace( + const pal::string_t& higher, + const fx_reference_t& lower); + static void display_retry_framework_trace( + const fx_reference_t& fx_existing, + const fx_reference_t& fx_new); + static void display_summary_of_frameworks( + const fx_definition_vector_t& fx_definitions, + const fx_name_to_fx_reference_map_t& newest_references); }; diff --git a/src/installer/corehost/cli/fxr/fx_muxer.messages.cpp b/src/installer/corehost/cli/fxr/fx_muxer.messages.cpp new file mode 100644 index 0000000..5a39879 --- /dev/null +++ b/src/installer/corehost/cli/fxr/fx_muxer.messages.cpp @@ -0,0 +1,102 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#include "framework_info.h" +#include "fx_definition.h" +#include "fx_muxer.h" +#include "fx_reference.h" +#include "fx_ver.h" +#include "pal.h" +#include "trace.h" + +/** +* When the framework is referenced more than once in a non-compatible way, display detailed error message +* about available frameworks and installation of new framework. +*/ +void fx_muxer_t::display_incompatible_framework_error( + const pal::string_t& higher, + const fx_reference_t& lower) +{ + assert(lower.get_patch_roll_fwd() != nullptr); + assert(lower.get_roll_fwd_on_no_candidate_fx() != nullptr); + + trace::error(_X("The specified framework '%s', version '%s', patch_roll_fwd=%d, roll_fwd_on_no_candidate_fx=%d cannot roll-forward to the previously referenced version '%s'."), + lower.get_fx_name().c_str(), + lower.get_fx_version().c_str(), + *lower.get_patch_roll_fwd(), + *lower.get_roll_fwd_on_no_candidate_fx(), + higher.c_str()); +} + +void fx_muxer_t::display_compatible_framework_trace( + const pal::string_t& higher, + const fx_reference_t& lower) +{ + if (trace::is_enabled()) + { + assert(lower.get_patch_roll_fwd() != nullptr); + assert(lower.get_roll_fwd_on_no_candidate_fx() != nullptr); + + trace::verbose(_X("--- The specified framework '%s', version '%s', patch_roll_fwd=%d, roll_fwd_on_no_candidate_fx=%d is compatible with the previously referenced version '%s'."), + lower.get_fx_name().c_str(), + lower.get_fx_version().c_str(), + *lower.get_patch_roll_fwd(), + *lower.get_roll_fwd_on_no_candidate_fx(), + higher.c_str()); + } +} + +void fx_muxer_t::display_retry_framework_trace( + const fx_reference_t& fx_existing, + const fx_reference_t& fx_new) +{ + if (trace::is_enabled()) + { + assert(fx_new.get_patch_roll_fwd() != nullptr); + assert(fx_new.get_roll_fwd_on_no_candidate_fx() != nullptr); + + trace::verbose(_X("--- Restarting all framework resolution because the previously resolved framework '%s', version '%s' must be re-resolved with the new version '%s', patch_roll_fwd=%d, roll_fwd_on_no_candidate_fx=%d ."), + fx_existing.get_fx_name().c_str(), + fx_existing.get_fx_version().c_str(), + fx_new.get_fx_version().c_str(), + *fx_new.get_patch_roll_fwd(), + *fx_new.get_roll_fwd_on_no_candidate_fx()); + } +} + +void fx_muxer_t::display_summary_of_frameworks( + const fx_definition_vector_t& fx_definitions, + const fx_name_to_fx_reference_map_t& newest_references +) +{ + if (trace::is_enabled()) + { + trace::verbose(_X("--- Summary of all frameworks:")); + + bool is_app = true; + for (const auto& fx : fx_definitions) + { + if (is_app) + { + is_app = false; // skip the app + } + else + { + auto newest_ref = newest_references.find(fx->get_name()); + assert(newest_ref != newest_references.end()); + assert(newest_ref->second.get_fx_version() == fx->get_found_version()); + assert(newest_ref->second.get_patch_roll_fwd() != nullptr); + assert(newest_ref->second.get_roll_fwd_on_no_candidate_fx() != nullptr); + + trace::verbose(_X(" framework:'%s', lowest requested version='%s', found version='%s', patch_roll_fwd=%d, roll_fwd_on_no_candidate_fx=%d, folder=%s"), + fx->get_name().c_str(), + fx->get_requested_version().c_str(), + fx->get_found_version().c_str(), + *newest_ref->second.get_patch_roll_fwd(), + *newest_ref->second.get_roll_fwd_on_no_candidate_fx(), + fx->get_dir().c_str()); + } + } + } +} diff --git a/src/installer/corehost/cli/fxr/fx_ver.cpp b/src/installer/corehost/cli/fxr/fx_ver.cpp index 8d9f19a..60bc2ee 100644 --- a/src/installer/corehost/cli/fxr/fx_ver.cpp +++ b/src/installer/corehost/cli/fxr/fx_ver.cpp @@ -25,6 +25,11 @@ fx_ver_t::fx_ver_t(int major, int minor, int patch) { } +fx_ver_t::fx_ver_t() + : fx_ver_t(-1, -1, -1, _X(""), _X("")) +{ +} + bool fx_ver_t::operator ==(const fx_ver_t& b) const { return compare(*this, b) == 0; diff --git a/src/installer/corehost/cli/fxr/fx_ver.h b/src/installer/corehost/cli/fxr/fx_ver.h index b59b325..6331af6 100644 --- a/src/installer/corehost/cli/fxr/fx_ver.h +++ b/src/installer/corehost/cli/fxr/fx_ver.h @@ -10,6 +10,7 @@ // compare multiple dot separated identifiers individually.) ex: 1.0.0-beta.2 vs. 1.0.0-beta.11 struct fx_ver_t { + fx_ver_t(); fx_ver_t(int major, int minor, int patch); fx_ver_t(int major, int minor, int patch, const pal::string_t& pre); fx_ver_t(int major, int minor, int patch, const pal::string_t& pre, const pal::string_t& build); @@ -24,6 +25,8 @@ struct fx_ver_t bool is_prerelease() const { return !m_pre.empty(); } + bool is_empty() const { return m_major == -1; } + pal::string_t as_str() const; pal::string_t prerelease_glob() const; pal::string_t patch_glob() const; diff --git a/src/installer/corehost/cli/fxr/sdk_info.cpp b/src/installer/corehost/cli/fxr/sdk_info.cpp index d927f13..78c2b20 100644 --- a/src/installer/corehost/cli/fxr/sdk_info.cpp +++ b/src/installer/corehost/cli/fxr/sdk_info.cpp @@ -81,7 +81,7 @@ void sdk_info::get_all_sdk_infos( for (const auto& ver : versions) { // Make sure we filter out any non-version folders. - fx_ver_t parsed(-1, -1, -1); + fx_ver_t parsed; if (fx_ver_t::parse(ver, &parsed, false)) { trace::verbose(_X("Found SDK version [%s]"), ver.c_str()); diff --git a/src/installer/corehost/cli/fxr/sdk_resolver.cpp b/src/installer/corehost/cli/fxr/sdk_resolver.cpp index 61b19e4..86e01ea 100644 --- a/src/installer/corehost/cli/fxr/sdk_resolver.cpp +++ b/src/installer/corehost/cli/fxr/sdk_resolver.cpp @@ -66,7 +66,7 @@ pal::string_t resolve_cli_version(const pal::string_t& global_json) pal::string_t resolve_sdk_version(pal::string_t sdk_path, bool disallow_prerelease, pal::string_t global_cli_version) { - fx_ver_t specified(-1, -1, -1); + fx_ver_t specified; // Validate the global cli version if specified if (!global_cli_version.empty()) @@ -90,12 +90,12 @@ pal::string_t resolve_sdk_version(pal::string_t sdk_path, bool disallow_prerelea std::vector versions; pal::readdir_onlydirectories(sdk_path, &versions); - fx_ver_t max_ver(-1, -1, -1); + fx_ver_t max_ver; for (const auto& version : versions) { trace::verbose(_X("Considering version... [%s]"), version.c_str()); - fx_ver_t ver(-1, -1, -1); + fx_ver_t ver; if (fx_ver_t::parse(version, &ver, disallow_prerelease)) { if (global_cli_version.empty() || @@ -140,8 +140,8 @@ bool higher_sdk_version(const pal::string_t& new_version, pal::string_t* version { bool disallow_prerelease = false; bool retval = false; - fx_ver_t ver(-1, -1, -1); - fx_ver_t new_ver(-1, -1, -1); + fx_ver_t ver; + fx_ver_t new_ver; if (fx_ver_t::parse(new_version, &new_ver, disallow_prerelease)) { diff --git a/src/installer/corehost/cli/hostpolicy/CMakeLists.txt b/src/installer/corehost/cli/hostpolicy/CMakeLists.txt index 21af6d6..50ca121 100644 --- a/src/installer/corehost/cli/hostpolicy/CMakeLists.txt +++ b/src/installer/corehost/cli/hostpolicy/CMakeLists.txt @@ -45,6 +45,7 @@ set(SOURCES ../deps_format.cpp ../deps_entry.cpp ../fx_definition.cpp + ../fx_reference.cpp ../version.cpp ) diff --git a/src/installer/corehost/cli/libhost.cpp b/src/installer/corehost/cli/libhost.cpp index 82c4c33..3f7d1a5 100644 --- a/src/installer/corehost/cli/libhost.cpp +++ b/src/installer/corehost/cli/libhost.cpp @@ -94,7 +94,7 @@ void try_patch_roll_forward_in_dir(const pal::string_t& cur_dir, const fx_ver_t& pal::readdir_onlydirectories(path, maj_min_star, &list); fx_ver_t max_ver = start_ver; - fx_ver_t ver(-1, -1, -1); + fx_ver_t ver; for (const auto& str : list) { trace::verbose(_X("Considering patch roll forward candidate version [%s]"), str.c_str()); @@ -128,7 +128,7 @@ void try_prerelease_roll_forward_in_dir(const pal::string_t& cur_dir, const fx_v pal::readdir_onlydirectories(path, maj_min_pat_star, &list); fx_ver_t max_ver = start_ver; - fx_ver_t ver(-1, -1, -1); + fx_ver_t ver; for (const auto& str : list) { trace::verbose(_X("Considering prerelease roll forward candidate version [%s]"), str.c_str()); diff --git a/src/installer/corehost/cli/roll_fwd_on_no_candidate_fx_option.h b/src/installer/corehost/cli/roll_fwd_on_no_candidate_fx_option.h new file mode 100644 index 0000000..9050500 --- /dev/null +++ b/src/installer/corehost/cli/roll_fwd_on_no_candidate_fx_option.h @@ -0,0 +1,16 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef __ROLL_FWD_ON_NO_CANDIDATE_FX_OPTION_H_ +#define __ROLL_FWD_ON_NO_CANDIDATE_FX_OPTION_H_ + +// Specifies the roll forward capability for finding the closest (most compatible) framework +// Note that the "applyPatches" bool option is separate from this and occurs after roll forward. +enum class roll_fwd_on_no_candidate_fx_option +{ + disabled = 0, + minor, // also inludes patch + major // also inludes minor and patch +}; + +#endif // __ROLL_FWD_ON_NO_CANDIDATE_FX_OPTION_H_ diff --git a/src/installer/corehost/cli/runtime_config.cpp b/src/installer/corehost/cli/runtime_config.cpp index 9a441f9..bfff336 100644 --- a/src/installer/corehost/cli/runtime_config.cpp +++ b/src/installer/corehost/cli/runtime_config.cpp @@ -11,57 +11,38 @@ // The semantics of applying the runtimeconfig.json values follows, in the following steps from // first to last, where last always wins. These steps are also annotated in the code here. -// 1a) If the app, apply the default values from the environment -// 1b) If the framework, apply the default values from the higher framework -// 2) Apply the values in the current "framework" section; use these as defaults for current layer -// 3) Apply the values in the app's "additionalFrameworks" section for the targeted framework -// 4) Apply the readonly values which are the settings that can't be changed by lower layers +// 1) Apply the environment settings +// 2) Apply the values in the current "runtimeOptions" section +// 3) Apply the values in the referenced "frameworks" section +// 4) Apply the overrides (from command line or other) runtime_config_t::runtime_config_t() - : m_patch_roll_fwd(true) - , m_roll_fwd_on_no_candidate_fx(roll_fwd_on_no_candidate_fx_option::minor) - , m_is_framework_dependent(false) + : m_is_framework_dependent(false) , m_valid(false) { } -void runtime_config_t::parse(const pal::string_t& path, const pal::string_t& dev_path, const runtime_config_t* higher_layer_config, const runtime_config_t* app_config) +void runtime_config_t::parse(const pal::string_t& path, const pal::string_t& dev_path, const fx_reference_t& fx_ref, const fx_reference_t& override_settings) { m_path = path; m_dev_path = dev_path; + m_fx_ref = fx_ref; + m_fx_overrides = override_settings; - // Step #1: apply the defaults from the environment (for the app) or previous\higher layer (for a framework) - if (higher_layer_config != nullptr) - { - // Copy the previous defaults so we can default the next framework; these may be changed by the current fx - copy_framework_settings_to(higher_layer_config->m_fx_global, m_fx_global); + // Step #1: set the defaults from the environment + m_fx_defaults.set_patch_roll_fwd(true); - // Apply the defaults - set_effective_values(m_fx_global); - } - else + roll_fwd_on_no_candidate_fx_option roll_fwd_option = roll_fwd_on_no_candidate_fx_option::minor; + pal::string_t env_no_candidate; + if (pal::getenv(_X("DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX"), &env_no_candidate)) { - // Since there is no previous config, this is the app's config, so default m_roll_fwd_on_no_candidate_fx from the env variable. - // The value will be overwritten during parsing if the setting exists in the config file. - pal::string_t env_no_candidate; - if (pal::getenv(_X("DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX"), &env_no_candidate)) - { - m_roll_fwd_on_no_candidate_fx = static_cast(pal::xtoi(env_no_candidate.c_str())); - m_fx_global.set_roll_fwd_on_no_candidate_fx(m_roll_fwd_on_no_candidate_fx); - } + roll_fwd_option = static_cast(pal::xtoi(env_no_candidate.c_str())); } - m_valid = ensure_parsed(app_config); + m_fx_defaults.set_roll_fwd_on_no_candidate_fx(roll_fwd_option); - if (m_valid) - { - // Step #4: apply the readonly values - if (app_config != nullptr) - { - m_tfm = app_config->m_tfm; - set_effective_values(app_config->m_fx_readonly); - } - } + // Parse the file + m_valid = ensure_parsed(); trace::verbose(_X("Runtime config [%s] is valid=[%d]"), path.c_str(), m_valid); } @@ -106,18 +87,18 @@ bool runtime_config_t::parse_opts(const json_value& opts) } } + // Step #2: set the defaults from the "runtimeOptions" auto patch_roll_fwd = opts_obj.find(_X("applyPatches")); if (patch_roll_fwd != opts_obj.end()) { - m_patch_roll_fwd = patch_roll_fwd->second.as_bool(); - m_fx_global.set_patch_roll_fwd(m_patch_roll_fwd); + m_fx_defaults.set_patch_roll_fwd(patch_roll_fwd->second.as_bool()); } auto roll_fwd_on_no_candidate_fx = opts_obj.find(_X("rollForwardOnNoCandidateFx")); if (roll_fwd_on_no_candidate_fx != opts_obj.end()) { - m_roll_fwd_on_no_candidate_fx = static_cast(roll_fwd_on_no_candidate_fx->second.as_integer()); - m_fx_global.set_roll_fwd_on_no_candidate_fx(m_roll_fwd_on_no_candidate_fx); + auto val = static_cast(roll_fwd_on_no_candidate_fx->second.as_integer()); + m_fx_defaults.set_roll_fwd_on_no_candidate_fx(val); } auto tfm = opts_obj.find(_X("tfm")); @@ -126,85 +107,68 @@ bool runtime_config_t::parse_opts(const json_value& opts) m_tfm = tfm->second.as_string(); } - // Step #2: apply the "framework" section - + // Step #3: read the "framework" and "frameworks" section + bool rc = true; auto framework = opts_obj.find(_X("framework")); - if (framework == opts_obj.end()) + if (framework != opts_obj.end()) { - return true; - } - - m_is_framework_dependent = true; - - const auto& fx_obj = framework->second.as_object(); + m_is_framework_dependent = true; - m_fx_name = fx_obj.at(_X("name")).as_string(); + const auto& framework_obj = framework->second.as_object(); - bool rc = parse_framework(fx_obj); - if (rc) - { - set_effective_values(m_fx); + fx_reference_t fx_out; + rc = parse_framework(framework_obj, fx_out); + if (rc) + { + m_frameworks.push_back(fx_out); + } } - return rc; -} - -void runtime_config_t::set_effective_values(const runtime_config_framework_t& overrides) -{ - if (overrides.get_fx_ver() != nullptr) + if (rc) { - m_fx_ver = *overrides.get_fx_ver(); - } + auto iter = opts_obj.find(_X("frameworks")); + if (iter != opts_obj.end()) + { + m_is_framework_dependent = true; - if (overrides.get_roll_fwd_on_no_candidate_fx() != nullptr) - { - m_roll_fwd_on_no_candidate_fx = *overrides.get_roll_fwd_on_no_candidate_fx(); + const auto& frameworks_obj = iter->second.as_array(); + rc = read_framework_array(frameworks_obj); + } } - if (overrides.get_patch_roll_fwd() != nullptr) - { - m_patch_roll_fwd = *overrides.get_patch_roll_fwd(); - } + return rc; } -/*static*/ void runtime_config_t::copy_framework_settings_to(const runtime_config_framework_t& from, runtime_config_framework_t& to) +bool runtime_config_t::parse_framework(const json_object& fx_obj, fx_reference_t& fx_out) { - if (from.get_fx_ver() != nullptr) - { - to.set_fx_ver(*from.get_fx_ver()); - } + fx_out.apply_settings_from(m_fx_defaults); - if (from.get_roll_fwd_on_no_candidate_fx() != nullptr) + auto fx_name= fx_obj.find(_X("name")); + if (fx_name != fx_obj.end()) { - to.set_roll_fwd_on_no_candidate_fx(*from.get_roll_fwd_on_no_candidate_fx()); + fx_out.set_fx_name(fx_name->second.as_string()); } - if (from.get_patch_roll_fwd() != nullptr) - { - to.set_patch_roll_fwd(*from.get_patch_roll_fwd()); - } -} - -bool runtime_config_t::parse_framework(const json_object& fx_obj) -{ auto fx_ver = fx_obj.find(_X("version")); if (fx_ver != fx_obj.end()) { - m_fx.set_fx_ver(fx_ver->second.as_string()); + fx_out.set_fx_version(fx_ver->second.as_string()); } auto patch_roll_fwd = fx_obj.find(_X("applyPatches")); if (patch_roll_fwd != fx_obj.end()) { - m_fx.set_patch_roll_fwd(patch_roll_fwd->second.as_bool()); + fx_out.set_patch_roll_fwd(patch_roll_fwd->second.as_bool()); } auto roll_fwd_on_no_candidate_fx = fx_obj.find(_X("rollForwardOnNoCandidateFx")); if (roll_fwd_on_no_candidate_fx != fx_obj.end()) { - m_fx.set_roll_fwd_on_no_candidate_fx(static_cast(roll_fwd_on_no_candidate_fx->second.as_integer())); + fx_out.set_roll_fwd_on_no_candidate_fx(static_cast(roll_fwd_on_no_candidate_fx->second.as_integer())); } + fx_out.apply_settings_from(m_fx_overrides); + return true; } @@ -252,7 +216,46 @@ bool runtime_config_t::ensure_dev_config_parsed() return true; } -bool runtime_config_t::ensure_parsed(const runtime_config_t* app_config) +bool runtime_config_t::read_framework_array(web::json::array frameworks_json) +{ + bool rc = true; + + for (const auto& fx_json : frameworks_json) + { + const auto& fx_obj = fx_json.as_object(); + + fx_reference_t fx_out; + rc = parse_framework(fx_obj, fx_out); + if (!rc) + { + break; + } + + if (fx_out.get_fx_name().length() == 0) + { + trace::verbose(_X("No framework name specified.")); + rc = false; + break; + } + + if (std::find_if( + m_frameworks.begin(), + m_frameworks.end(), + [&](const fx_reference_t& item) { return fx_out.get_fx_name() == item.get_fx_name(); }) + != m_frameworks.end()) + { + trace::verbose(_X("Framework %s already specified."), fx_out.get_fx_name().c_str()); + rc = false; + break; + } + + m_frameworks.push_back(fx_out); + } + + return rc; +} + +bool runtime_config_t::ensure_parsed() { trace::verbose(_X("Attempting to read runtime config: %s"), m_path.c_str()); if (!ensure_dev_config_parsed()) @@ -288,51 +291,6 @@ bool runtime_config_t::ensure_parsed(const runtime_config_t* app_config) if (iter != json.end()) { rc = parse_opts(iter->second); - - if (rc) - { - if (app_config == nullptr) - { - // If there is no app_config yet, then we are the app - // Read the additionalFrameworks section so we can apply later when each framework's runtimeconfig is read - const auto& opts_obj = iter->second.as_object(); - const auto iter = opts_obj.find(_X("additionalFrameworks")); - if (iter != opts_obj.end()) - { - const auto& additional_frameworks = iter->second.as_array(); - for (const auto& fx : additional_frameworks) - { - runtime_config_t fx_overrides; - const auto& fx_obj = fx.as_object(); - fx_overrides.m_fx_name = fx_obj.at(_X("name")).as_string(); - if (fx_overrides.m_fx_name.length() == 0) - { - trace::verbose(_X("No framework name in additionalFrameworks section.")); - rc = false; - break; - } - - rc = fx_overrides.parse_framework(fx_obj); - if (!rc) - { - break; - } - - m_additional_frameworks[fx_overrides.m_fx_name] = fx_overrides.m_fx; - } - } - - // Follow through to step #3 in case the framework is also specified in the additionalFrameworks section - app_config = this; - } - - // Step #3: apply the values from "additionalFrameworks" - auto overrides = app_config->m_additional_frameworks.find(m_fx_name); - if (overrides != app_config->m_additional_frameworks.end()) - { - set_effective_values(overrides->second); - } - } } } catch (const std::exception& je) @@ -352,37 +310,6 @@ const pal::string_t& runtime_config_t::get_tfm() const return m_tfm; } -const pal::string_t& runtime_config_t::get_fx_name() const -{ - assert(m_valid); - return m_fx_name; -} - -const pal::string_t& runtime_config_t::get_fx_version() const -{ - assert(m_valid); - return m_fx_ver; -} - -bool runtime_config_t::get_patch_roll_fwd() const -{ - assert(m_valid); - return m_patch_roll_fwd; -} - -roll_fwd_on_no_candidate_fx_option runtime_config_t::get_roll_fwd_on_no_candidate_fx() const -{ - assert(m_valid); - return m_roll_fwd_on_no_candidate_fx; -} - -void runtime_config_t::force_roll_fwd_on_no_candidate_fx(roll_fwd_on_no_candidate_fx_option value) -{ - assert(m_valid); - m_roll_fwd_on_no_candidate_fx = value; - m_fx_readonly.set_roll_fwd_on_no_candidate_fx(value); -} - bool runtime_config_t::get_is_framework_dependent() const { return m_is_framework_dependent; @@ -405,3 +332,13 @@ void runtime_config_t::combine_properties(std::unordered_map 0); + + m_frameworks[0].set_fx_version(version); + m_frameworks[0].set_patch_roll_fwd(false); + m_frameworks[0].set_roll_fwd_on_no_candidate_fx(roll_fwd_on_no_candidate_fx_option::disabled); + m_frameworks[0].set_use_exact_version(true); +} diff --git a/src/installer/corehost/cli/runtime_config.h b/src/installer/corehost/cli/runtime_config.h index a1753e7..9604cfa 100644 --- a/src/installer/corehost/cli/runtime_config.h +++ b/src/installer/corehost/cli/runtime_config.h @@ -8,110 +8,41 @@ #include "pal.h" #include "cpprest/json.h" +#include "fx_reference.h" typedef web::json::value json_value; typedef web::json::object json_object; -enum class roll_fwd_on_no_candidate_fx_option -{ - disabled = 0, - minor, - major_or_minor -}; - -class runtime_config_framework_t -{ -public: - // Uses a "nullable" pattern until we add such a type - - runtime_config_framework_t() - : has_fx_ver(false) - , has_roll_fwd_on_no_candidate_fx(false) - , has_patch_roll_fwd(false) - , fx_ver(_X("")) - , patch_roll_fwd(false) - , roll_fwd_on_no_candidate_fx((roll_fwd_on_no_candidate_fx_option)0) - { } - - const pal::string_t* get_fx_ver() const - { - return (has_fx_ver ? &fx_ver : nullptr); - } - void set_fx_ver(pal::string_t value) - { - has_fx_ver = true; - fx_ver = value; - } - - const bool* get_patch_roll_fwd() const - { - return (has_patch_roll_fwd ? &patch_roll_fwd : nullptr); - } - void set_patch_roll_fwd(bool value) - { - has_patch_roll_fwd = true; - patch_roll_fwd = value; - } - - const roll_fwd_on_no_candidate_fx_option* get_roll_fwd_on_no_candidate_fx() const - { - return (has_roll_fwd_on_no_candidate_fx ? &roll_fwd_on_no_candidate_fx : nullptr); - } - void set_roll_fwd_on_no_candidate_fx(roll_fwd_on_no_candidate_fx_option value) - { - has_roll_fwd_on_no_candidate_fx = true; - roll_fwd_on_no_candidate_fx = value; - } - -private: - bool has_fx_ver; - bool has_patch_roll_fwd; - bool has_roll_fwd_on_no_candidate_fx; - - pal::string_t fx_ver; - bool patch_roll_fwd; - roll_fwd_on_no_candidate_fx_option roll_fwd_on_no_candidate_fx; -}; - class runtime_config_t { public: runtime_config_t(); - void parse(const pal::string_t& path, const pal::string_t& dev_path, const runtime_config_t* higher_layer_config, const runtime_config_t* app_config); + void parse(const pal::string_t& path, const pal::string_t& dev_path, const fx_reference_t& fx_ref, const fx_reference_t& override_settings); bool is_valid() const { return m_valid; } 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_fx_version() const; - const pal::string_t& get_fx_name() const; const pal::string_t& get_tfm() const; const std::list& get_probe_paths() const; - bool get_patch_roll_fwd() const; - roll_fwd_on_no_candidate_fx_option get_roll_fwd_on_no_candidate_fx() const; - void force_roll_fwd_on_no_candidate_fx(roll_fwd_on_no_candidate_fx_option value); bool get_is_framework_dependent() const; bool parse_opts(const json_value& opts); void combine_properties(std::unordered_map& combined_properties) const; + const fx_reference_vector_t& get_frameworks() const { return m_frameworks; } + void set_fx_version(pal::string_t version); private: - bool ensure_parsed(const runtime_config_t* defaults); + bool ensure_parsed(); //todo: const runtime_config_t* defaults bool ensure_dev_config_parsed(); std::unordered_map m_properties; - std::unordered_map m_additional_frameworks; - runtime_config_framework_t m_fx_global; // the settings that will be applied to the next lower layer; does not include version (Step #1) - runtime_config_framework_t m_fx; // the settings in the current "framework" section (Step #3) - runtime_config_framework_t m_fx_readonly; // the settings that can't be changed by lower layers (Step #4) + fx_reference_vector_t m_frameworks; + fx_reference_t m_fx_defaults; // the default settings (Steps #1 and #2) + fx_reference_t m_fx_ref; // the settings from the referenced "frameworks" section (Step #3) + fx_reference_t m_fx_overrides; // the settings that can't be changed (Step #4) std::vector m_prop_keys; std::vector m_prop_values; std::list m_probe_paths; pal::string_t m_tfm; - pal::string_t m_fx_name; - - // These are the effective settings - pal::string_t m_fx_ver; - bool m_patch_roll_fwd; - roll_fwd_on_no_candidate_fx_option m_roll_fwd_on_no_candidate_fx; pal::string_t m_dev_path; pal::string_t m_path; @@ -119,9 +50,8 @@ private: bool m_valid; private: - bool parse_framework(const json_object& fx_obj); - void set_effective_values(const runtime_config_framework_t& overrides); - static void copy_framework_settings_to(const runtime_config_framework_t& from, runtime_config_framework_t& to); - + bool parse_framework(const json_object& fx_obj, fx_reference_t& fx_out); + bool read_framework_array(web::json::array frameworks); + static void copy_framework_settings_to(const fx_reference_t& from, fx_reference_t& to); }; -#endif // __RUNTIME_CONFIG_H__ \ No newline at end of file +#endif // __RUNTIME_CONFIG_H__ diff --git a/src/installer/corehost/corehost.cpp b/src/installer/corehost/corehost.cpp index 476ec89..96c5adb 100644 --- a/src/installer/corehost/corehost.cpp +++ b/src/installer/corehost/corehost.cpp @@ -143,21 +143,21 @@ bool resolve_fxr_path(const pal::string_t& host_path, const pal::string_t& app_r std::vector list; pal::readdir_onlydirectories(fxr_dir, &list); - fx_ver_t max_ver(-1, -1, -1); + fx_ver_t max_ver; for (const auto& dir : list) { trace::info(_X("Considering fxr version=[%s]..."), dir.c_str()); pal::string_t ver = get_filename(dir); - fx_ver_t fx_ver(-1, -1, -1); + fx_ver_t fx_ver; if (fx_ver_t::parse(ver, &fx_ver, false)) { max_ver = std::max(max_ver, fx_ver); } } - if (max_ver == fx_ver_t(-1, -1, -1)) + if (max_ver == fx_ver_t()) { trace::error(_X("A fatal error occurred, the folder [%s] does not contain any version-numbered child folders"), fxr_dir.c_str()); return false; diff --git a/src/installer/corehost/error_codes.h b/src/installer/corehost/error_codes.h index 995c193..f84d959 100644 --- a/src/installer/corehost/error_codes.h +++ b/src/installer/corehost/error_codes.h @@ -33,5 +33,7 @@ enum StatusCode LibHostUnknownCommand = 0x80008099, LibHostAppRootFindFailure = 0x8000809a, SdkResolverResolveFailure = 0x8000809b, + FrameworkCompatFailure = 0x8000809c, + FrameworkCompatRetry = 0x8000809d, }; #endif // __ERROR_CODES_H__ diff --git a/src/installer/test/HostActivationTests/GivenThatICareAboutLightupAppActivation.cs b/src/installer/test/HostActivationTests/GivenThatICareAboutLightupAppActivation.cs index 9b4de2a..7167590 100644 --- a/src/installer/test/HostActivationTests/GivenThatICareAboutLightupAppActivation.cs +++ b/src/installer/test/HostActivationTests/GivenThatICareAboutLightupAppActivation.cs @@ -426,7 +426,7 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.LightupApp // Add versions in the exe folder SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _fxBaseDir, "9999.0.0"); - SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _uberFxBaseDir, "9999.0.0", null, "7777.0.0"); + SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _uberFxBaseDir, "9999.0.0", "7777.0.0"); // Copy NetCoreApp's copy of the assembly to the app location string netcoreAssembly = Path.Combine(_fxBaseDir, "9999.0.0", "System.Collections.Immutable.dll"); @@ -492,7 +492,7 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.LightupApp // Add versions in the exe folder SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _fxBaseDir, "9999.0.0"); - SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _uberFxBaseDir, "9999.0.0", null, "7777.0.0"); + SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _uberFxBaseDir, "9999.0.0", "7777.0.0"); // Copy NetCoreApp's copy of the assembly to the app location string netcoreAssembly = Path.Combine(_fxBaseDir, "9999.0.0", "System.Collections.Immutable.dll"); diff --git a/src/installer/test/HostActivationTests/GivenThatICareAboutMultilevelSharedFxLookup.DepsVersion.cs b/src/installer/test/HostActivationTests/GivenThatICareAboutMultilevelSharedFxLookup.DepsVersion.cs index cc4b4ba..7427ad2 100644 --- a/src/installer/test/HostActivationTests/GivenThatICareAboutMultilevelSharedFxLookup.DepsVersion.cs +++ b/src/installer/test/HostActivationTests/GivenThatICareAboutMultilevelSharedFxLookup.DepsVersion.cs @@ -121,7 +121,7 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSharedFxLooku // Add versions in the exe folder SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.0.0"); - SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.0.0", null, uberFxProductVersion); + SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.0.0", uberFxProductVersion); // Copy NetCoreApp's copy of the assembly to the app location netcoreAssembly = Path.Combine(_exeSharedFxBaseDir, "9999.0.0", "System.Collections.Immutable.dll"); diff --git a/src/installer/test/HostActivationTests/GivenThatICareAboutMultilevelSharedFxLookup.cs b/src/installer/test/HostActivationTests/GivenThatICareAboutMultilevelSharedFxLookup.cs index 9fe33c2..919acf1 100644 --- a/src/installer/test/HostActivationTests/GivenThatICareAboutMultilevelSharedFxLookup.cs +++ b/src/installer/test/HostActivationTests/GivenThatICareAboutMultilevelSharedFxLookup.cs @@ -9,6 +9,9 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSharedFxLooku { public partial class GivenThatICareAboutMultilevelSharedFxLookup : IDisposable { + private const string SystemCollectionsImmutableFileVersion = "88.2.3.4"; + private const string SystemCollectionsImmutableAssemblyVersion = "88.0.1.2"; + private RepoDirectoriesProvider RepoDirectories; private TestProjectFixture PreviouslyBuiltAndRestoredPortableTestProjectFixture; @@ -100,6 +103,7 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSharedFxLooku _sharedFxVersion = (new DirectoryInfo(greatestVersionSharedFxPath)).Name; _builtSharedFxDir = Path.Combine(_builtDotnet, "shared", "Microsoft.NETCore.App", _sharedFxVersion); _builtSharedUberFxDir = Path.Combine(_builtDotnet, "shared", "Microsoft.UberFramework", _sharedFxVersion); + SharedFramework.CreateUberFrameworkArtifacts(_builtSharedFxDir, _builtSharedUberFxDir, SystemCollectionsImmutableAssemblyVersion, SystemCollectionsImmutableFileVersion); // Trace messages used to identify from which folder the framework was picked _hostPolicyDllName = Path.GetFileName(fixture.TestProject.HostPolicyDll); @@ -647,7 +651,7 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSharedFxLooku // Add versions in the exe folders SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.0.0"); - SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.0.0", null, "7777.0.0"); + SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.0.0", "7777.0.0"); // Version: NetCoreApp 9999.0.0 // UberFramework 7777.0.0 @@ -672,7 +676,7 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSharedFxLooku // Add a newer version to verify roll-forward SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.0.1"); - SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.0.0", null, "7777.0.1"); + SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.0.0", "7777.0.1"); // Version: NetCoreApp 9999.0.0 // UberFramework 7777.0.0 @@ -712,7 +716,7 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSharedFxLooku } [Fact] - public void Multiple_SharedFxLookup_Propagated_Global_RuntimeConfig_Values() + public void Multiple_SharedFxLookup_Do_Not_Propagate() { var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture .Copy(); @@ -725,7 +729,7 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSharedFxLooku // Add versions in the exe folders SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.1.0"); - SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.0.0", "UberValue", "7777.0.0"); + SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.0.0", "7777.0.0"); // Version: NetCoreApp 9999.0.0 // UberFramework 7777.0.0 @@ -745,65 +749,160 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSharedFxLooku .And .HaveStdErrContaining("It was not possible to find any compatible framework version"); - // Enable rollForwardOnNoCandidateFx on app's config, which will be used as the default for Uber's config - SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", rollFwdOnNoCandidateFx: 1, testConfigPropertyValue: null, useUberFramework: true); + // Enable rollForwardOnNoCandidateFx on app's config, which will not be used as the default for Uber's config + SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", rollFwdOnNoCandidateFx: 1, useUberFramework: true); // Version: NetCoreApp 9999.0.0 // UberFramework 7777.0.0 // 'Roll forward on no candidate fx' enabled through config // Exe: NetCoreApp 9999.1.0 // UberFramework 7777.0.0 - // Expected: 9999.1.0 - // 7777.0.0 + // Expected: no compatible version dotnet.Exec(appDll) .WorkingDirectory(_currentWorkingDir) .EnvironmentVariable("COREHOST_TRACE", "1") .EnvironmentVariable("DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX", "0") .CaptureStdOut() .CaptureStdErr() + .Execute(fExpectedToFail: true) + .Should() + .Fail() + .And + .HaveStdErrContaining("It was not possible to find any compatible framework version"); + } + + [Fact] + public void Multiple_Fx_References_Cant_Roll_Forward_Because_Incompatible_Config() + { + var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture + .Copy(); + + var dotnet = fixture.BuiltDotnet; + var appDll = fixture.TestProject.AppDll; + + string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json"); + + var additionalfxs = new JArray(); + additionalfxs.Add(GetAdditionalFramework("Microsoft.NETCore.App", "9999.1.0", applyPatches: false, rollForwardOnNoCandidateFx: 0)); + SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", null, useUberFramework: true, frameworks: additionalfxs); + + // Add versions in the exe folders + SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.1.0", "9999.5.5"); + SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.5.5", "7777.0.0"); + + // Verify that both 9999.1.0 and 9999.5.5 can't be selected with roll-forward disabled + // Version: NetCoreApp 9999.5.5 (in framework section) + // NetCoreApp 9999.1.0 (in frameworks section) + // UberFramework 7777.0.0 + // Exe: NetCoreApp 9999.1.0 rollForwardOnNoCandidateFx:0 applyPatches:false + // NetCoreApp 9999.5.5 + // UberFramework 7777.0.0 + // Expected: no compatible version + dotnet.Exec(appDll) + .WorkingDirectory(_currentWorkingDir) + .EnvironmentVariable("COREHOST_TRACE", "1") + .CaptureStdOut() + .CaptureStdErr() + .Execute(fExpectedToFail: true) + .Should() + .Fail() + .And + .HaveStdErrContaining("cannot roll-forward to the previously referenced version '9999.5.5"); + } + + [Fact] + public void Multiple_Fx_References_Can_Roll_Forward_Without_Retry() + { + var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture + .Copy(); + + var dotnet = fixture.BuiltDotnet; + var appDll = fixture.TestProject.AppDll; + + string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json"); + + var additionalfxs = new JArray(); + additionalfxs.Add(GetAdditionalFramework("Microsoft.NETCore.App", "9999.1.1", applyPatches: false, rollForwardOnNoCandidateFx: 1)); + SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", null, useUberFramework: true, frameworks: additionalfxs); + + // Add versions in the exe folders + SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.1.0", "9999.5.5"); + SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.5.5", "7777.0.0"); + + // Version: NetCoreApp 9999.5.5 (in framework section) + // NetCoreApp 9999.1.0 (in frameworks section) + // UberFramework 7777.0.0 + // Exe: NetCoreApp 9999.1.0 rollForwardOnNoCandidateFx:1 applyPatches:false + // NetCoreApp 9999.5.5 + // UberFramework 7777.0.0 + // Expected: 9999.5.5 + // 7777.0.0 + dotnet.Exec(appDll) + .WorkingDirectory(_currentWorkingDir) + .EnvironmentVariable("COREHOST_TRACE", "1") + .CaptureStdOut() + .CaptureStdErr() .Execute() .Should() .Pass() .And - .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.1.0")) + .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.5.5")) .And - .HaveStdOutContaining("Framework Version:9999.1.0") + .HaveStdOutContaining("Framework Version:9999.5.5") .And .HaveStdErrContaining(Path.Combine(_exeFoundUberFxMessage, "7777.0.0")) .And - .HaveStdErrContaining("Property TestProperty = UberValue"); + .NotHaveStdErrContaining("Restarting all framework resolution"); + } - // Change the app's TestProperty value which should override the uber's config value - SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", rollFwdOnNoCandidateFx: 1, testConfigPropertyValue: "AppValue", useUberFramework: true); + [Fact] + public void Multiple_Fx_References_Can_Roll_Forward_With_Retry() + { + var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture + .Copy(); - // Version: NetCoreApp 9999.0.0 - // UberFramework 7777.0.0 - // 'Roll forward on no candidate fx' enabled through config - // Exe: NetCoreApp 9999.1.0 + var dotnet = fixture.BuiltDotnet; + var appDll = fixture.TestProject.AppDll; + + string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json"); + + var additionalfxs = new JArray(); + additionalfxs.Add(GetAdditionalFramework("Microsoft.UberFramework", "7777.0.0", null, null)); + // Specify Uber as additional fx so we find NetCoreApp 9999.1.1 and then need to do a re-try for 9999.5.5 + SharedFramework.SetRuntimeConfigJson(runtimeConfig, "9999.1.1", null, null, frameworks: additionalfxs); + + // Add versions in the exe folders + SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.1.1", "9999.5.5"); + SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.5.5", "7777.0.0"); + + // Version: NetCoreApp 9999.1.1 (in framework section) + // UberFramework 7777.0.0 (in frameworks section) + // NetCoreApp 9999.5.5 (in uber's config) + // Exe: NetCoreApp 9999.1.1 + // NetCoreApp 9999.5.5 // UberFramework 7777.0.0 - // Expected: 9999.1.0 + // Expected: 9999.5.5 // 7777.0.0 dotnet.Exec(appDll) .WorkingDirectory(_currentWorkingDir) .EnvironmentVariable("COREHOST_TRACE", "1") - .EnvironmentVariable("DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX", "0") .CaptureStdOut() .CaptureStdErr() .Execute() .Should() .Pass() .And - .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.1.0")) + .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.5.5")) .And - .HaveStdOutContaining("Framework Version:9999.1.0") + .HaveStdOutContaining("Framework Version:9999.5.5") .And .HaveStdErrContaining(Path.Combine(_exeFoundUberFxMessage, "7777.0.0")) .And - .HaveStdErrContaining("Property TestProperty = AppValue"); + .HaveStdErrContaining("Restarting all framework resolution because the previously resolved framework 'Microsoft.NETCore.App', version '9999.1.1' must be re-resolved with the new version '9999.5.5'"); } [Fact] - public void Multiple_SharedFxLookup_Propagated_Additional_Framework_RuntimeConfig_Values() + public void Multiple_Fx_References_Cant_Roll_Forward_Because_Disabled_Through_CommandLine() { var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture .Copy(); @@ -814,48 +913,63 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSharedFxLooku string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json"); var additionalfxs = new JArray(); - additionalfxs.Add(GetAdditionalFramework("Microsoft.NETCore.App", "9999.1.0", applyPatches: false, rollForwardOnNoCandidateFx: 0)); - SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", null, useUberFramework: true, additionalFrameworks: additionalfxs); + additionalfxs.Add(GetAdditionalFramework("Microsoft.NETCore.App", "9999.1.1", applyPatches: false, rollForwardOnNoCandidateFx: 1)); + SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", null, useUberFramework: true, frameworks: additionalfxs); // Add versions in the exe folders - SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.1.0"); - SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.5.5", "UberValue", "7777.0.0"); + SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.1.0", "9999.5.5"); + SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.5.5", "7777.0.0"); // Version: NetCoreApp 9999.5.5 (in framework section) - // NetCoreApp 9999.1.0 (in app's additionalFrameworks section) + // NetCoreApp 9999.1.0 (in frameworks section) // UberFramework 7777.0.0 - // Exe: NetCoreApp 9999.1.0 + // Exe: NetCoreApp 9999.1.0 rollForwardOnNoCandidateFx:1 applyPatches:false + // NetCoreApp 9999.5.5 // UberFramework 7777.0.0 - // Expected: 9999.1.0 + // --roll-forward-on-no-candidate-fx=0 should override config settings + // Expected: 9999.5.5 // 7777.0.0 - dotnet.Exec(appDll) + + dotnet.Exec( + "exec", + "--roll-forward-on-no-candidate-fx", "0", + appDll) .WorkingDirectory(_currentWorkingDir) .EnvironmentVariable("COREHOST_TRACE", "1") .CaptureStdOut() .CaptureStdErr() - .Execute() + .Execute(fExpectedToFail: true) .Should() - .Pass() - .And - .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.1.0")) - .And - .HaveStdOutContaining("Framework Version:9999.1.0") + .Fail() .And - .HaveStdErrContaining(Path.Combine(_exeFoundUberFxMessage, "7777.0.0")); + .HaveStdErrContaining("cannot roll-forward to the previously referenced version '9999.5.5"); + } - // Change the additionalFrameworks to allow roll forward, overriding Uber's global section and ignoring Uber's framework section - additionalfxs.Clear(); - additionalfxs.Add(GetAdditionalFramework("Microsoft.NETCore.App", "9999.0.0", applyPatches: false, rollForwardOnNoCandidateFx: 1)); - additionalfxs.Add(GetAdditionalFramework("UberFx", "7777.0.0", applyPatches: false, rollForwardOnNoCandidateFx: 0)); - SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", rollFwdOnNoCandidateFx: 0, useUberFramework: true, additionalFrameworks: additionalfxs); + [Fact] + public void Multiple_SharedFxLookup_NetCoreApp_MinorRollForward_Wins_Over_UberFx() + { + var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture + .Copy(); - // Version: NetCoreApp 9999.5.5 (in framework section) - // NetCoreApp 9999.0.0 (in app's additionalFrameworks section) + var dotnet = fixture.BuiltDotnet; + var appDll = fixture.TestProject.AppDll; + + string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json"); + SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", null, useUberFramework: true); + + // Modify the Uber values + SharedFramework.CreateUberFrameworkArtifacts(_builtSharedFxDir, _builtSharedUberFxDir, "0.0.0.1", "0.0.0.2"); + + // Add versions in the exe folders + SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.1.0"); + SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.0.0", "7777.0.0"); + + string uberFile = Path.Combine(_exeSharedUberFxBaseDir, "7777.0.0", "System.Collections.Immutable.dll"); + string netCoreAppFile = Path.Combine(_exeSharedFxBaseDir, "9999.1.0", "System.Collections.Immutable.dll"); + // The System.Collections.Immutable.dll is located in the UberFramework and NetCoreApp + // Version: NetCoreApp 9999.0.0 // UberFramework 7777.0.0 - // UberFramework 7777.0.0 (in app's additionalFrameworks section) - // 'Roll forward on no candidate fx' disabled through env var - // 'Roll forward on no candidate fx' disabled through Uber's global runtimeconfig - // 'Roll forward on no candidate fx' enabled for NETCore.App enabled through additionalFrameworks section + // 'Roll forward on no candidate fx' enabled through config // Exe: NetCoreApp 9999.1.0 // UberFramework 7777.0.0 // Expected: 9999.1.0 @@ -863,35 +977,51 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSharedFxLooku dotnet.Exec(appDll) .WorkingDirectory(_currentWorkingDir) .EnvironmentVariable("COREHOST_TRACE", "1") - .EnvironmentVariable("DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX", "0") .CaptureStdOut() .CaptureStdErr() .Execute() .Should() .Pass() .And - .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.1.0")) - .And - .HaveStdOutContaining("Framework Version:9999.1.0") - .And - .HaveStdErrContaining(Path.Combine(_exeFoundUberFxMessage, "7777.0.0")); + .HaveStdErrContaining($"Replacing deps entry [{uberFile}, AssemblyVersion:0.0.0.1, FileVersion:0.0.0.2] with [{netCoreAppFile}"); + } - // Same as previous except use of '--roll-forward-on-no-candidate-fx' - // Expected: Fail since '--roll-forward-on-no-candidate-fx' should apply to all layers - dotnet.Exec( - "exec", - "--roll-forward-on-no-candidate-fx", "0", - appDll) + [Fact] + public void Multiple_SharedFxLookup_Uber_Wins_Over_NetCoreApp_On_PatchRollForward() + { + var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture + .Copy(); + + var dotnet = fixture.BuiltDotnet; + var appDll = fixture.TestProject.AppDll; + + string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json"); + SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", null, useUberFramework: true); + + // Add versions in the exe folders + SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.0.1"); + SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.0.0", "7777.0.0"); + + // The System.Collections.Immutable.dll is located in the UberFramework and NetCoreApp + // Version: NetCoreApp 9999.0.0 + // UberFramework 7777.0.0 + // 'Roll forward on no candidate fx' enabled through config + // Exe: NetCoreApp 9999.0.1 + // UberFramework 7777.0.0 + // Expected: 9999.0.1 + // 7777.0.0 + dotnet.Exec(appDll) .WorkingDirectory(_currentWorkingDir) .EnvironmentVariable("COREHOST_TRACE", "1") - .EnvironmentVariable("DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX", "1") .CaptureStdOut() .CaptureStdErr() - .Execute(fExpectedToFail: true) + .Execute() .Should() - .Fail() + .Pass() .And - .HaveStdErrContaining("It was not possible to find any compatible framework version"); + .HaveStdErrContaining(Path.Combine("7777.0.0", "System.Collections.Immutable.dll")) + .And + .NotHaveStdErrContaining(Path.Combine("9999.1.0", "System.Collections.Immutable.dll")); } static private JObject GetAdditionalFramework(string fxName, string fxVersion, bool? applyPatches, int? rollForwardOnNoCandidateFx) diff --git a/src/installer/test/HostActivationTests/SharedFramework.cs b/src/installer/test/HostActivationTests/SharedFramework.cs index 0f7d033..539efef 100644 --- a/src/installer/test/HostActivationTests/SharedFramework.cs +++ b/src/installer/test/HostActivationTests/SharedFramework.cs @@ -61,7 +61,7 @@ namespace Microsoft.DotNet.CoreSetup.Test // This method adds a list of new framework version folders in the specified // sharedFxUberBaseDir. A runtimeconfig file is created that references // Microsoft.NETCore.App version=sharedFxBaseVersion - public static void AddAvailableSharedUberFxVersions(string sharedFxDir, string sharedUberFxBaseDir, string sharedFxBaseVersion, string testConfigPropertyValue = null, params string[] availableUberVersions) + public static void AddAvailableSharedUberFxVersions(string sharedFxDir, string sharedUberFxBaseDir, string sharedFxBaseVersion, params string[] availableUberVersions) { DirectoryInfo sharedFxUberBaseDirInfo = new DirectoryInfo(sharedUberFxBaseDir); @@ -76,7 +76,7 @@ namespace Microsoft.DotNet.CoreSetup.Test CopyDirectory(sharedFxDir, newSharedFxDir); string runtimeBaseConfig = Path.Combine(newSharedFxDir, "Microsoft.UberFramework.runtimeconfig.json"); - SharedFramework.SetRuntimeConfigJson(runtimeBaseConfig, sharedFxBaseVersion, null, testConfigPropertyValue); + SharedFramework.SetRuntimeConfigJson(runtimeBaseConfig, sharedFxBaseVersion, null); } } @@ -92,7 +92,7 @@ namespace Microsoft.DotNet.CoreSetup.Test * } * } */ - public static void SetRuntimeConfigJson(string destFile, string version, int? rollFwdOnNoCandidateFx = null, string testConfigPropertyValue = null, bool? useUberFramework = false, JArray additionalFrameworks = null) + public static void SetRuntimeConfigJson(string destFile, string version, int? rollFwdOnNoCandidateFx = null, bool? useUberFramework = false, JArray frameworks = null) { string name = useUberFramework.HasValue && useUberFramework.Value ? "Microsoft.UberFramework" : "Microsoft.NETCore.App"; @@ -110,20 +110,9 @@ namespace Microsoft.DotNet.CoreSetup.Test runtimeOptions.Add("rollForwardOnNoCandidateFx", rollFwdOnNoCandidateFx); } - if (testConfigPropertyValue != null) + if (frameworks != null) { - runtimeOptions.Add( - new JProperty("configProperties", - new JObject( - new JProperty("TestProperty", testConfigPropertyValue) - ) - ) - ); - } - - if (additionalFrameworks != null) - { - runtimeOptions.Add("additionalFrameworks", additionalFrameworks); + runtimeOptions.Add("frameworks", frameworks); } FileInfo file = new FileInfo(destFile); -- 2.7.4