From 5e9f99901dc1a48811945d5a38102f7b44a70b53 Mon Sep 17 00:00:00 2001 From: Rama Krishnan Raghupathy Date: Thu, 18 May 2017 17:30:42 -0700 Subject: [PATCH] roll forward across hive Commit migrated from https://github.com/dotnet/core-setup/commit/c3dd461c99bfdcb8d136bedf33e46156682fc348 --- .../design-docs/multilevel-sharedfx-lookup.md | 24 ++--- src/installer/corehost/cli/args.cpp | 6 +- src/installer/corehost/cli/args.h | 7 +- src/installer/corehost/cli/deps_resolver.cpp | 9 +- src/installer/corehost/cli/fxr/fx_muxer.cpp | 111 ++++++++++++++------- src/installer/corehost/cli/fxr/fx_muxer.h | 2 +- src/installer/corehost/common/pal.h | 4 +- src/installer/corehost/common/pal.unix.cpp | 30 ++++-- src/installer/corehost/common/pal.windows.cpp | 8 +- src/installer/corehost/common/utils.cpp | 14 ++- src/installer/corehost/common/utils.h | 2 +- 11 files changed, 134 insertions(+), 83 deletions(-) diff --git a/docs/installer/design-docs/multilevel-sharedfx-lookup.md b/docs/installer/design-docs/multilevel-sharedfx-lookup.md index 5eaac3ca..ce3483a 100644 --- a/docs/installer/design-docs/multilevel-sharedfx-lookup.md +++ b/docs/installer/design-docs/multilevel-sharedfx-lookup.md @@ -80,14 +80,7 @@ At last, the coreclr is loaded into memory and called to run the application. ## Proposed changes -Almost every file search is done in relation to the executable directory. It would be better to be able to search for some files in other directories as well. Suggested folders are the user location and the global .NET location. The user and global folders may vary depending on the running operational system. They are defined as follows: - -User location: - - Windows 32-bit: %SystemDrive%\Users\username\.dotnet\x86 - Windows 64-bit: %SystemDrive%\Users\username\.dotnet\x64 - Unix 32-bit: /home/username/.dotnet/x86 - Unix 64-bit: /home/username/.dotnet/x64 +Almost every file search is done in relation to the executable directory. It would be better to be able to search for some files in other directories as well, namely the global .NET location. The global folders may vary depending on the running operational system. They are defined as follows: Global .NET location: @@ -102,15 +95,13 @@ It’s being proposed that, if the specified version is defined through the conf - For productions: - 1. In relation to the user location: search for the most appropriate version by rolling forward. If it cannot be found, proceed to the next step. - 2. In relation to the executable directory: search for the most appropriate version by rolling forward. If it cannot be found, proceed to the next step. - 3. In relation to the global location: search for the most appropriate version by rolling forward. If it cannot be found, then we were not able to locate any compatible version. + 1. In relation to the executable directory: search for the most appropriate version by rolling forward. If it cannot be found, proceed to the next step. + 2. In relation to the global location: search for the most appropriate version by rolling forward. If it cannot be found, then we were not able to locate any compatible version. - For pre-releases: - 1. In relation to the user location: search for the specified version. If it cannot be found, search for the most appropriate version by rolling forward. If no compatible version can be found, proceed to the next step. - 2. In relation to the executable directory: search for the specified version. If it cannot be found, search for the most appropriate version by rolling forward. If no compatible version can be found, proceed to the next step. - 3. In relation to the global location: search for the specified version. If it cannot be found, search for the most appropriate version by rolling forward. If no compatible version can be found, then we were not able to locate any compatible version. + 1. In relation to the executable directory: search for the specified version. If it cannot be found, search for the most appropriate version by rolling forward. If no compatible version can be found, proceed to the next step. + 2. In relation to the global location: search for the specified version. If it cannot be found, search for the most appropriate version by rolling forward. If no compatible version can be found, then we were not able to locate any compatible version. In the case that the desired version is defined through an argument, the multi-level lookup will happen as well but it will only consider the exact specified version (it will not roll forward). @@ -132,6 +123,5 @@ By following similar logic, it will be possible to implement future changes in t The search would be conducted as follows: -1. In relation to the user location: search for the specified version. If it cannot be found, choose the most appropriate available version. If there’s no available version, proceed to the next step. -2. In relation to the executable directory: search for the specified version. If it cannot be found, choose the most appropriate available version. If there’s no available version, proceed to the next step. -3. In relation to the global location: search for the specified version. If it cannot be found, choose the most appropriate available version. If there’s no available version, then we were not able to find any version folder and an error message must be returned. +1. In relation to the executable directory: search for the specified version. If it cannot be found, choose the most appropriate available version. If there’s no available version, proceed to the next step. +2. In relation to the global location: search for the specified version. If it cannot be found, choose the most appropriate available version. If there’s no available version, then we were not able to find any version folder and an error message must be returned. diff --git a/src/installer/corehost/cli/args.cpp b/src/installer/corehost/cli/args.cpp index ecaa8d8..e42887b 100644 --- a/src/installer/corehost/cli/args.cpp +++ b/src/installer/corehost/cli/args.cpp @@ -58,11 +58,7 @@ void setup_shared_store_paths(const hostpolicy_init_t& init, const pal::string_t } // Global shared store dir - if (get_global_shared_store_dir(&args->global_shared_store)) - { - append_path(&args->global_shared_store, get_arch()); - append_path(&args->global_shared_store, init.tfm.c_str()); - } + get_global_shared_store_dirs(&args->global_shared_stores, get_arch(), init.tfm); } bool parse_arguments( diff --git a/src/installer/corehost/cli/args.h b/src/installer/corehost/cli/args.h index 6a99437..cf7827c 100644 --- a/src/installer/corehost/cli/args.h +++ b/src/installer/corehost/cli/args.h @@ -85,7 +85,7 @@ struct arguments_t std::vector probe_paths; pal::string_t managed_application; pal::string_t local_shared_store; - pal::string_t global_shared_store; + std::vector global_shared_stores; pal::string_t dotnet_shared_store; std::vector env_shared_store; int app_argc; @@ -109,7 +109,10 @@ struct arguments_t } trace::verbose(_X("-- arguments_t: local shared store: '%s'"), local_shared_store.c_str()); trace::verbose(_X("-- arguments_t: dotnet shared store: '%s'"), dotnet_shared_store.c_str()); - trace::verbose(_X("-- arguments_t: global shared store: '%s'"), global_shared_store.c_str()); + for (const auto& global_shared : global_shared_stores) + { + trace::verbose(_X("-- arguments_t: global shared store: '%s'"), global_shared.c_str()); + } } } }; diff --git a/src/installer/corehost/cli/deps_resolver.cpp b/src/installer/corehost/cli/deps_resolver.cpp index 9b8d43d..d7807aa 100644 --- a/src/installer/corehost/cli/deps_resolver.cpp +++ b/src/installer/corehost/cli/deps_resolver.cpp @@ -162,9 +162,14 @@ void deps_resolver_t::setup_shared_store_probes( m_probes.push_back(probe_config_t::lookup(args.dotnet_shared_store)); } - if (args.global_shared_store != args.dotnet_shared_store && pal::directory_exists(args.global_shared_store)) + + for (const auto& global_shared : args.global_shared_stores) { - m_probes.push_back(probe_config_t::lookup(args.global_shared_store)); + if (global_shared != args.dotnet_shared_store && pal::directory_exists(global_shared)) + { + // Shared Store probe: DOTNET_SHARED_STORE + m_probes.push_back(probe_config_t::lookup(global_shared)); + } } } diff --git a/src/installer/corehost/cli/fxr/fx_muxer.cpp b/src/installer/corehost/cli/fxr/fx_muxer.cpp index 806ed91..17472c8 100644 --- a/src/installer/corehost/cli/fxr/fx_muxer.cpp +++ b/src/installer/corehost/cli/fxr/fx_muxer.cpp @@ -333,7 +333,7 @@ bool fx_muxer_t::resolve_hostpolicy_dir(host_mode_t mode, return false; } -void fx_muxer_t::resolve_roll_forward(pal::string_t& fx_dir, +fx_ver_t fx_muxer_t::resolve_framework_version(const std::vector& version_list, const pal::string_t& fx_ver, const fx_ver_t& specified, const bool& patch_roll_fwd, @@ -341,8 +341,7 @@ void fx_muxer_t::resolve_roll_forward(pal::string_t& fx_dir, { trace::verbose(_X("Attempting FX roll forward starting from [%s]"), fx_ver.c_str()); - std::vector list; - pal::readdir(fx_dir, &list); + fx_ver_t most_compatible = specified; if (!specified.is_prerelease()) { @@ -350,10 +349,10 @@ void fx_muxer_t::resolve_roll_forward(pal::string_t& fx_dir, { trace::verbose(_X("'Roll forward on no candidate fx' enabled. Looking for the least production greater than or equal to [%s]"), fx_ver.c_str()); fx_ver_t next_lowest(-1, -1, -1); - for (const auto& version : list) + for (const auto& ver : version_list) { - fx_ver_t ver(-1, -1, -1); - if (fx_ver_t::parse(version, &ver, true) && (ver >= specified)) + + if (!ver.is_prerelease() && ver >= specified) { next_lowest = (next_lowest == fx_ver_t(-1, -1, -1)) ? ver : std::min(next_lowest, ver); } @@ -371,12 +370,11 @@ void fx_muxer_t::resolve_roll_forward(pal::string_t& fx_dir, if (patch_roll_fwd) { trace::verbose(_X("Applying patch roll forward from [%s]"), most_compatible.as_str().c_str()); - for (const auto& version : list) + for (const auto& ver : version_list) { - trace::verbose(_X("Inspecting version... [%s]"), version.c_str()); - fx_ver_t ver(-1, -1, -1); + trace::verbose(_X("Inspecting version... [%s]"), ver.as_str().c_str()); - if (fx_ver_t::parse(version, &ver, true) && // true -- only prod. prevents roll forward to prerelease. + if (!ver.is_prerelease() && // only prod. prevents roll forward to prerelease. ver.get_major() == most_compatible.get_major() && ver.get_minor() == most_compatible.get_minor()) { @@ -388,13 +386,12 @@ void fx_muxer_t::resolve_roll_forward(pal::string_t& fx_dir, } else { - for (const auto& version : list) + for (const auto& ver : version_list) { - trace::verbose(_X("Inspecting version... [%s]"), version.c_str()); - fx_ver_t ver(-1, -1, -1); + trace::verbose(_X("Inspecting version... [%s]"), ver.as_str().c_str()); - if (fx_ver_t::parse(version, &ver, false) && // false -- implies both production and prerelease. - ver.is_prerelease() && // prevent roll forward to production. + //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() && ver.get_patch() == specified.get_patch() && @@ -405,8 +402,8 @@ void fx_muxer_t::resolve_roll_forward(pal::string_t& fx_dir, } } } - pal::string_t most_compatible_str = most_compatible.as_str(); - append_path(&fx_dir, most_compatible_str.c_str()); + + return most_compatible; } pal::string_t fx_muxer_t::resolve_fx_dir(host_mode_t mode, @@ -438,30 +435,27 @@ pal::string_t fx_muxer_t::resolve_fx_dir(host_mode_t mode, // Multi-level SharedFX lookup will look for the most appropriate version in several locations // by following the priority rank below: - // User directory // .exe directory // Global .NET directory // If it is not activated, then only .exe directory will be considered std::vector hive_dir; pal::string_t local_dir; - pal::string_t global_dir; + std::vector global_dirs; bool multilevel_lookup = multilevel_lookup_enabled(); - if (multilevel_lookup) + hive_dir.push_back(own_dir); + if (multilevel_lookup && pal::get_global_dotnet_dirs(&global_dirs)) { - if (pal::get_local_dotnet_dir(&local_dir)) + for (pal::string_t dir : global_dirs) { - hive_dir.push_back(local_dir); + hive_dir.push_back(dir); } } - hive_dir.push_back(own_dir); - if (multilevel_lookup && pal::get_global_dotnet_dir(&global_dir)) - { - hive_dir.push_back(global_dir); - } bool patch_roll_fwd_enabled = config.get_patch_roll_fwd(); + pal::string_t selected_fx_dir; + fx_ver_t selected_ver(-1, -1, -1); for (pal::string_t dir : hive_dir) { @@ -492,22 +486,60 @@ pal::string_t fx_muxer_t::resolve_fx_dir(host_mode_t mode, { 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(), patch_roll_fwd_enabled, roll_fwd_on_no_candidate_fx_val, fx_ver.c_str()); + append_path(&fx_dir, fx_ver.c_str()); + if (pal::directory_exists(fx_dir)) + { + trace::verbose(_X("Chose FX version [%s]"), fx_dir.c_str()); + return fx_dir; + } } else { - resolve_roll_forward(fx_dir, fx_ver, specified, patch_roll_fwd_enabled, roll_fwd_on_no_candidate_fx_val); - } + std::vector list; + std::vector version_list; + pal::readdir(fx_dir, &list); - if (pal::directory_exists(fx_dir)) - { - trace::verbose(_X("Chose FX version [%s]"), fx_dir.c_str()); - return fx_dir; + for (const auto& version : list) + { + fx_ver_t ver(-1, -1, -1); + 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, patch_roll_fwd_enabled, roll_fwd_on_no_candidate_fx_val); + + 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)) + { + //Continue to search for a better match if available + 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, patch_roll_fwd_enabled, roll_fwd_on_no_candidate_fx_val); + + if (resolved_ver != selected_ver) + { + trace::verbose(_X("Changing Selected FX version from [%s] to [%s]"), selected_fx_dir.c_str(), fx_dir.c_str()); + selected_ver = resolved_ver; + selected_fx_dir = fx_dir; + } + } } } - trace::error(_X("It was not possible to find any compatible framework version")); - return pal::string_t(); + if (selected_fx_dir.empty()) + { + trace::error(_X("It was not possible to find any compatible framework version")); + return pal::string_t(); + } + + trace::verbose(_X("Chose FX version [%s]"), selected_fx_dir.c_str()); + return selected_fx_dir; } pal::string_t fx_muxer_t::resolve_cli_version(const pal::string_t& global_json) @@ -656,7 +688,7 @@ bool fx_muxer_t::resolve_sdk_dotnet_path(const pal::string_t& own_dir, const pal std::vector hive_dir; pal::string_t local_dir; - pal::string_t global_dir; + std::vector global_dirs; bool multilevel_lookup = multilevel_lookup_enabled(); if (multilevel_lookup) @@ -672,9 +704,12 @@ bool fx_muxer_t::resolve_sdk_dotnet_path(const pal::string_t& own_dir, const pal hive_dir.push_back(own_dir); } - if (multilevel_lookup && pal::get_global_dotnet_dir(&global_dir)) + if (multilevel_lookup && pal::get_global_dotnet_dirs(&global_dirs)) { - hive_dir.push_back(global_dir); + for (pal::string_t dir : global_dirs) + { + hive_dir.push_back(dir); + } } pal::string_t cli_version; diff --git a/src/installer/corehost/cli/fxr/fx_muxer.h b/src/installer/corehost/cli/fxr/fx_muxer.h index 5fbae1c..3b762ac 100644 --- a/src/installer/corehost/cli/fxr/fx_muxer.h +++ b/src/installer/corehost/cli/fxr/fx_muxer.h @@ -35,7 +35,7 @@ private: const std::vector& probe_realpaths, const runtime_config_t& config, pal::string_t* impl_dir); - static void resolve_roll_forward(pal::string_t& fx_dir, const pal::string_t& fx_ver, const fx_ver_t& specified, const bool& patch_roll_fwd, const int& roll_fwd_on_no_candidate_fx); + static fx_ver_t resolve_framework_version(const std::vector& version_list, const pal::string_t& fx_ver, const fx_ver_t& specified, const bool& patch_roll_fwd, const int& roll_fwd_on_no_candidate_fx); static pal::string_t resolve_fx_dir(host_mode_t mode, const pal::string_t& own_dir, const runtime_config_t& config, const pal::string_t& specified_fx_version, const int& roll_fwd_on_no_candidate_fx_val); static pal::string_t resolve_cli_version(const pal::string_t& global); static bool resolve_sdk_dotnet_path(const pal::string_t& own_dir, pal::string_t* cli_sdk); diff --git a/src/installer/corehost/common/pal.h b/src/installer/corehost/common/pal.h index 13f6f61..76fb18e 100644 --- a/src/installer/corehost/common/pal.h +++ b/src/installer/corehost/common/pal.h @@ -211,7 +211,9 @@ namespace pal bool getenv(const char_t* name, string_t* recv); bool get_default_servicing_directory(string_t* recv); bool get_local_dotnet_dir(string_t* recv); - bool get_global_dotnet_dir(string_t* recv); + //On Linux, we determine global location by enumerating the location where dotnet is present on path, hence there could be multiple such locations + //On Windows there will be only one global location + bool get_global_dotnet_dirs(std::vector* recv); bool get_default_breadcrumb_store(string_t* recv); bool is_path_rooted(const string_t& path); diff --git a/src/installer/corehost/common/pal.unix.cpp b/src/installer/corehost/common/pal.unix.cpp index 2ae8aa0..9f0fb4f 100644 --- a/src/installer/corehost/common/pal.unix.cpp +++ b/src/installer/corehost/common/pal.unix.cpp @@ -183,7 +183,7 @@ bool is_executable(const pal::string_t& file_path) } static -bool locate_dotnet_on_path(pal::string_t* dotnet_exe) +bool locate_dotnet_on_path(std::vector* dotnet_exes) { pal::string_t path; if (!pal::getenv(_X("PATH"), &path)) @@ -204,12 +204,17 @@ bool locate_dotnet_on_path(pal::string_t* dotnet_exe) append_path(&tok, _X("dotnet")); if (pal::realpath(&tok) && is_executable(tok)) { - *dotnet_exe = tok; - return true; + dotnet_exes->push_back(tok); } tok.clear(); } - return false; + + if (dotnet_exes->empty()) + { + return false; + } + + return true; } bool pal::get_local_dotnet_dir(pal::string_t* recv) @@ -234,16 +239,21 @@ bool pal::get_local_dotnet_dir(pal::string_t* recv) return true; } - bool pal::get_global_dotnet_dir(pal::string_t* recv) + bool pal::get_global_dotnet_dirs(std::vector* recv) { - recv->clear(); - pal::string_t dotnet_exe; - if (!locate_dotnet_on_path(&dotnet_exe)) + + std::vector dotnet_exes; + if (!locate_dotnet_on_path(&dotnet_exes)) { return false; } - pal::string_t dir = get_directory(dotnet_exe); - recv->assign(dir); + + for (pal::string_t path : dotnet_exes) + { + pal::string_t dir = get_directory(path); + recv->push_back(dir); + } + return true; } diff --git a/src/installer/corehost/common/pal.windows.cpp b/src/installer/corehost/common/pal.windows.cpp index b13e0ab..c7ee63e 100644 --- a/src/installer/corehost/common/pal.windows.cpp +++ b/src/installer/corehost/common/pal.windows.cpp @@ -195,14 +195,16 @@ bool pal::get_default_servicing_directory(string_t* recv) return true; } -bool pal::get_global_dotnet_dir(pal::string_t* dir) +bool pal::get_global_dotnet_dirs(std::vector* dirs) { - if (!get_file_path_from_env(_X("ProgramFiles"), dir)) + pal::string_t dir; + if (!get_file_path_from_env(_X("ProgramFiles"), &dir)) { return false; } - append_path(dir, _X("dotnet")); + append_path(&dir, _X("dotnet")); + dirs->push_back(dir); return true; } diff --git a/src/installer/corehost/common/utils.cpp b/src/installer/corehost/common/utils.cpp index e49039d..b104d32 100644 --- a/src/installer/corehost/common/utils.cpp +++ b/src/installer/corehost/common/utils.cpp @@ -278,13 +278,21 @@ bool get_env_shared_store_dirs(std::vector* dirs, const pal::stri return true; } -bool get_global_shared_store_dir(pal::string_t* dir) +bool get_global_shared_store_dirs(std::vector* dirs, const pal::string_t& arch, const pal::string_t& tfm) { - if (!pal::get_global_dotnet_dir(dir)) + std::vector global_dirs; + if (!pal::get_global_dotnet_dirs(&global_dirs)) { return false; } - append_path(dir, RUNTIME_STORE_DIRECTORY_NAME); + + for (pal::string_t dir : global_dirs) + { + append_path(&dir, RUNTIME_STORE_DIRECTORY_NAME); + append_path(&dir, arch.c_str()); + append_path(&dir, tfm.c_str()); + dirs->push_back(dir); + } return true; } diff --git a/src/installer/corehost/common/utils.h b/src/installer/corehost/common/utils.h index dbbc327..ec18142 100644 --- a/src/installer/corehost/common/utils.h +++ b/src/installer/corehost/common/utils.h @@ -41,5 +41,5 @@ bool parse_known_args( bool skip_utf8_bom(pal::ifstream_t* stream); bool get_env_shared_store_dirs(std::vector* dirs, const pal::string_t& arch, const pal::string_t& tfm); bool get_local_shared_store_dir(pal::string_t* recv); -bool get_global_shared_store_dir(pal::string_t* recv); +bool get_global_shared_store_dirs(std::vector* dirs, const pal::string_t& arch, const pal::string_t& tfm); #endif -- 2.7.4