{
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
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 &&
}
}
- 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());
}
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()
{
public:
fx_definition_t();
-
fx_definition_t(
const pal::string_t& name,
const pal::string_t& dir,
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; }
--- /dev/null
+// 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);
+ }
+ }
+}
--- /dev/null
+// 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 <list>
+#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_t> fx_reference_vector_t;
+typedef std::unordered_map<pal::string_t, fx_reference_t> fx_name_to_fx_reference_map_t;
+
+#endif // __FX_REFERENCE_H__
../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
}
/*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_info>* 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<pal::string_t> global_dirs;
bool multilevel_lookup = multilevel_lookup_enabled();
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());
/*static*/ bool framework_info::print_all_frameworks(const pal::string_t& own_dir, const pal::string_t& leading_whitespace)
{
std::vector<framework_info> 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());
, 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_info>* framework_infos);
#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"
* 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,
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())
// 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)
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<pal::string_t>& probe_realpaths,
pal::string_t* impl_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());
{
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]"),
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]"),
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());
}
}
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() &&
}
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());
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)
{
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
{
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))
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<fx_ver_t> 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)
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)
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))
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());
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_definition_t>& 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_definition_t>(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,
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;
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<roll_fwd_on_no_candidate_fx_option>(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<fx_definition_t>(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;
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<roll_fwd_on_no_candidate_fx_option>(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)
{
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_definition_t>(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);
}
}
}
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;
}
#include "libhost.h"
+const int Max_Framework_Resolve_Retries = 100;
+
int execute_app(
const pal::string_t& impl_dll_dir,
corehost_init_t* init,
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<pal::string_t>& probe_realpaths,
pal::string_t* impl_dir);
static fx_ver_t resolve_framework_version(
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);
};
--- /dev/null
+// 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 <cassert>
+#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());
+ }
+ }
+ }
+}
{
}
+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;
// 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);
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;
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());
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())
std::vector<pal::string_t> 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() ||
{
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))
{
../deps_format.cpp
../deps_entry.cpp
../fx_definition.cpp
+ ../fx_reference.cpp
../version.cpp
)
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());
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());
--- /dev/null
+// 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_
// 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<roll_fwd_on_no_candidate_fx_option>(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<roll_fwd_on_no_candidate_fx_option>(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);
}
}
}
+ // 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_option>(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_option>(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"));
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_option>(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_option>(roll_fwd_on_no_candidate_fx->second.as_integer()));
}
+ fx_out.apply_settings_from(m_fx_overrides);
+
return true;
}
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())
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)
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;
}
}
}
+
+void runtime_config_t::set_fx_version(pal::string_t version)
+{
+ assert(m_frameworks.size() > 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);
+}
#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<T>" 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<pal::string_t>& 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<pal::string_t, pal::string_t>& 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<pal::string_t, pal::string_t> m_properties;
- std::unordered_map<pal::string_t, runtime_config_framework_t> 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<std::string> m_prop_keys;
std::vector<std::string> m_prop_values;
std::list<pal::string_t> 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;
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__
std::vector<pal::string_t> 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;
LibHostUnknownCommand = 0x80008099,
LibHostAppRootFindFailure = 0x8000809a,
SdkResolverResolveFailure = 0x8000809b,
+ FrameworkCompatFailure = 0x8000809c,
+ FrameworkCompatRetry = 0x8000809d,
};
#endif // __ERROR_CODES_H__
// 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");
// 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");
// 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");
{
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;
_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);
// 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
// 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
}
[Fact]
- public void Multiple_SharedFxLookup_Propagated_Global_RuntimeConfig_Values()
+ public void Multiple_SharedFxLookup_Do_Not_Propagate()
{
var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
.Copy();
// 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
.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();
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
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)
// 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);
CopyDirectory(sharedFxDir, newSharedFxDir);
string runtimeBaseConfig = Path.Combine(newSharedFxDir, "Microsoft.UberFramework.runtimeconfig.json");
- SharedFramework.SetRuntimeConfigJson(runtimeBaseConfig, sharedFxBaseVersion, null, testConfigPropertyValue);
+ SharedFramework.SetRuntimeConfigJson(runtimeBaseConfig, sharedFxBaseVersion, null);
}
}
* }
* }
*/
- 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";
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);