diff --git a/src/corehost/cli/args.cpp b/src/corehost/cli/args.cpp index 06cbdadf2..07ae3e04a 100644 --- a/src/corehost/cli/args.cpp +++ b/src/corehost/cli/args.cpp @@ -13,12 +13,12 @@ arguments_t::arguments_t() : app_argc(0), app_argv(nullptr), dotnet_packages_cache(_X("")), - dotnet_servicing(_X("")), + dotnet_extensions(_X("")), deps_path(_X("")) { } -bool parse_arguments(const pal::string_t& deps_path, const pal::string_t& probe_dir, host_mode_t mode, +bool parse_arguments(const pal::string_t& deps_path, const std::vector& probe_paths, host_mode_t mode, const int argc, const pal::char_t* argv[], arguments_t* arg_out) { arguments_t& args = *arg_out; @@ -80,7 +80,10 @@ bool parse_arguments(const pal::string_t& deps_path, const pal::string_t& probe_ pal::string_t opts_deps_file = _X("--depsfile"); pal::string_t opts_probe_path = _X("--additionalprobingpath"); pal::string_t deps_file = opts.count(opts_deps_file) ? opts[opts_deps_file] : deps_path; - pal::string_t probe_path = opts.count(opts_probe_path) ? opts[opts_probe_path] : probe_dir; + if (opts.count(opts_probe_path)) + { + args.probe_paths.push_back(opts[opts_probe_path]); + } if (!deps_file.empty()) { @@ -88,7 +91,10 @@ bool parse_arguments(const pal::string_t& deps_path, const pal::string_t& probe_ args.app_dir = get_directory(args.deps_path); } - args.probe_dir = probe_path; + for (const auto& probe : probe_paths) + { + args.probe_paths.push_back(probe); + } if (args.deps_path.empty()) { @@ -102,7 +108,8 @@ bool parse_arguments(const pal::string_t& deps_path, const pal::string_t& probe_ args.deps_path.append(_X(".deps.json")); } - pal::getenv(_X("DOTNET_PACKAGES_CACHE"), &args.dotnet_packages_cache); - pal::getenv(_X("DOTNET_SERVICING"), &args.dotnet_servicing); + pal::getenv(_X("DOTNET_HOSTING_OPTIMIZATION_CACHE"), &args.dotnet_packages_cache); + pal::get_default_extensions_directory(&args.dotnet_extensions); + return true; } diff --git a/src/corehost/cli/args.h b/src/corehost/cli/args.h index be1b99f0d..491e0f229 100644 --- a/src/corehost/cli/args.h +++ b/src/corehost/cli/args.h @@ -7,15 +7,85 @@ #include "utils.h" #include "pal.h" #include "trace.h" +#include "deps_format.h" #include "libhost.h" +struct probe_config_t +{ + pal::string_t probe_dir; + bool match_hash; + bool roll_forward; + const deps_json_t* probe_deps_json; + + bool only_runtime_assets; + bool only_serviceable_assets; + + void print() const + { + trace::verbose(_X("probe_config_t: probe=[%s] match-hash=[%d] roll-forward=[%d] deps-json=[%p]"), + probe_dir.c_str(), match_hash, roll_forward, probe_deps_json); + } + + probe_config_t( + const pal::string_t& probe_dir, + bool match_hash, + bool roll_forward, + const deps_json_t* probe_deps_json, + bool only_serviceable_assets, + bool only_runtime_assets) + : probe_dir(probe_dir) + , match_hash(match_hash) + , roll_forward(roll_forward) + , probe_deps_json(probe_deps_json) + , only_serviceable_assets(only_serviceable_assets) + , only_runtime_assets(only_runtime_assets) + { + // Cannot roll forward and also match hash. + assert(!roll_forward || !match_hash); + // Will not roll forward within a deps json. + assert(!roll_forward || probe_deps_json == nullptr); + // Will not do hash match when probing a deps json. + assert(!match_hash || probe_deps_json == nullptr); + } + + static probe_config_t svc_ni(const pal::string_t dir, bool roll_fwd) + { + return probe_config_t(dir, false, roll_fwd, nullptr, true, true); + } + + static probe_config_t svc(const pal::string_t dir, bool roll_fwd) + { + return probe_config_t(dir, false, roll_fwd, nullptr, true, false); + } + + static probe_config_t cache_ni(const pal::string_t dir) + { + return probe_config_t(dir, true, false, nullptr, false, true); + } + + static probe_config_t cache(const pal::string_t dir) + { + return probe_config_t(dir, true, false, nullptr, false, false); + } + + static probe_config_t fx(const pal::string_t dir, const deps_json_t* deps) + { + return probe_config_t(dir, false, false, deps, false, false); + } + + static probe_config_t additional(const pal::string_t dir, bool roll_fwd) + { + return probe_config_t(dir, false, roll_fwd, nullptr, false, false); + } +}; + struct arguments_t { pal::string_t own_path; pal::string_t app_dir; pal::string_t deps_path; - pal::string_t dotnet_servicing; - pal::string_t probe_dir; + pal::string_t dotnet_extensions; + std::vector probe_paths; pal::string_t dotnet_packages_cache; pal::string_t managed_application; @@ -28,12 +98,16 @@ struct arguments_t { if (trace::is_enabled()) { - trace::verbose(_X("args: own_path=%s app_dir=%s deps=%s servicing=%s probe_dir=%s packages_cache=%s mgd_app=%s"), - own_path.c_str(), app_dir.c_str(), deps_path.c_str(), dotnet_servicing.c_str(), probe_dir.c_str(), dotnet_packages_cache.c_str(), managed_application.c_str()); + trace::verbose(_X("-- arguments_t: own_path=%s app_dir=%s deps=%s extensions=%s packages_cache=%s mgd_app=%s"), + own_path.c_str(), app_dir.c_str(), deps_path.c_str(), dotnet_extensions.c_str(), dotnet_packages_cache.c_str(), managed_application.c_str()); + for (const auto& probe : probe_paths) + { + trace::verbose(_X("-- arguments_t: probe dir: [%s]"), probe.c_str()); + } } } }; -bool parse_arguments(const pal::string_t& deps_path, const pal::string_t& probe_dir, host_mode_t mode, const int argc, const pal::char_t* argv[], arguments_t* args); +bool parse_arguments(const pal::string_t& deps_path, const std::vector& probe_paths, host_mode_t mode, const int argc, const pal::char_t* argv[], arguments_t* args); #endif // ARGS_H diff --git a/src/corehost/cli/deps_entry.cpp b/src/corehost/cli/deps_entry.cpp index 0eceb9898..c14a4d59f 100644 --- a/src/corehost/cli/deps_entry.cpp +++ b/src/corehost/cli/deps_entry.cpp @@ -7,19 +7,7 @@ #include "trace.h" -// ----------------------------------------------------------------------------- -// Given a "base" directory, yield the relative path of this file in the package -// layout. -// -// Parameters: -// base - The base directory to look for the relative path of this entry -// str - If the method returns true, contains the file path for this deps -// entry relative to the "base" directory -// -// Returns: -// If the file exists in the path relative to the "base" directory. -// -bool deps_entry_t::to_rel_path(const pal::string_t& base, pal::string_t* str) const +bool deps_entry_t::to_path(const pal::string_t& base, bool look_in_base, pal::string_t* str) const { pal::string_t& candidate = *str; @@ -44,21 +32,55 @@ bool deps_entry_t::to_rel_path(const pal::string_t& base, pal::string_t* str) co pal_relative_path.length() + 3); candidate.assign(base); - append_path(&candidate, pal_relative_path.c_str()); + pal::string_t sub_path = look_in_base ? get_filename(pal_relative_path) : pal_relative_path; + append_path(&candidate, sub_path.c_str()); bool exists = pal::file_exists(candidate); + const pal::char_t* query_type = look_in_base ? _X("Local") : _X("Relative"); if (!exists) { - trace::verbose(_X("Relative path query did not exist %s"), candidate.c_str()); + trace::verbose(_X(" %s path query did not exist %s"), query_type, candidate.c_str()); candidate.clear(); } else { - trace::verbose(_X("Relative path query exists %s"), candidate.c_str()); + trace::verbose(_X(" %s path query exists %s"), query_type, candidate.c_str()); } return exists; } +// ----------------------------------------------------------------------------- +// Given a "base" directory, yield the local path of this file +// +// Parameters: +// base - The base directory to look for the relative path of this entry +// str - If the method returns true, contains the file path for this deps +// entry relative to the "base" directory +// +// Returns: +// If the file exists in the path relative to the "base" directory. +// +bool deps_entry_t::to_dir_path(const pal::string_t& base, pal::string_t* str) const +{ + return to_path(base, true, str); +} +// ----------------------------------------------------------------------------- +// Given a "base" directory, yield the relative path of this file in the package +// layout. +// +// Parameters: +// base - The base directory to look for the relative path of this entry +// str - If the method returns true, contains the file path for this deps +// entry relative to the "base" directory +// +// Returns: +// If the file exists in the path relative to the "base" directory. +// +bool deps_entry_t::to_rel_path(const pal::string_t& base, pal::string_t* str) const +{ + return to_path(base, false, str); +} + // ----------------------------------------------------------------------------- // Given a "base" directory, yield the relative path of this file in the package // layout. diff --git a/src/corehost/cli/deps_entry.h b/src/corehost/cli/deps_entry.h index 3b5c616cc..d016c2a21 100644 --- a/src/corehost/cli/deps_entry.h +++ b/src/corehost/cli/deps_entry.h @@ -5,6 +5,7 @@ #define __DEPS_ENTRY_H_ #include +#include #include #include "pal.h" @@ -18,14 +19,24 @@ struct deps_entry_t count }; + static const std::array s_known_asset_types; + pal::string_t library_type; pal::string_t library_name; pal::string_t library_version; pal::string_t library_hash; - pal::string_t asset_type; + asset_types asset_type; pal::string_t asset_name; pal::string_t relative_path; bool is_serviceable; + bool is_rid_specific; + + + // Given a "base" dir, yield the filepath within this directory or relative to this directory based on "look_in_base" + bool to_path(const pal::string_t& base, bool look_in_base, pal::string_t* str) const; + + // Given a "base" dir, yield the file path within this directory. + bool to_dir_path(const pal::string_t& base, pal::string_t* str) const; // Given a "base" dir, yield the relative path in the package layout. bool to_rel_path(const pal::string_t& base, pal::string_t* str) const; diff --git a/src/corehost/cli/deps_format.cpp b/src/corehost/cli/deps_format.cpp index 58c477c97..9c0885700 100644 --- a/src/corehost/cli/deps_format.cpp +++ b/src/corehost/cli/deps_format.cpp @@ -1,18 +1,19 @@ // 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 "deps_entry.h" #include "deps_format.h" #include "utils.h" #include "trace.h" -#include #include #include #include #include #include -const std::array deps_json_t::s_known_asset_types = { - _X("runtime"), _X("resources"), _X("native") }; +const std::array deps_entry_t::s_known_asset_types = { + _X("runtime"), _X("resources"), _X("native") +}; const deps_entry_t& deps_json_t::try_ni(const deps_entry_t& entry) const { @@ -27,7 +28,7 @@ const deps_entry_t& deps_json_t::try_ni(const deps_entry_t& entry) const void deps_json_t::reconcile_libraries_with_targets( const json_value& json, const std::function& library_exists_fn, - const std::function&(const pal::string_t&, int)>& get_rel_paths_by_asset_type_fn) + const std::function&(const pal::string_t&, int, bool*)>& get_rel_paths_by_asset_type_fn) { const auto& libraries = json.at(_X("libraries")).as_object(); for (const auto& library : libraries) @@ -50,9 +51,10 @@ void deps_json_t::reconcile_libraries_with_targets( const pal::string_t& hash = properties.at(_X("sha512")).as_string(); bool serviceable = properties.at(_X("serviceable")).as_bool(); - for (int i = 0; i < s_known_asset_types.size(); ++i) + for (int i = 0; i < deps_entry_t::s_known_asset_types.size(); ++i) { - for (const auto& rel_path : get_rel_paths_by_asset_type_fn(library.first, i)) + bool rid_specific = false; + for (const auto& rel_path : get_rel_paths_by_asset_type_fn(library.first, i, &rid_specific)) { bool ni_dll = false; auto asset_name = get_filename_without_ext(rel_path); @@ -69,9 +71,10 @@ void deps_json_t::reconcile_libraries_with_targets( entry.library_type = _X("package"); entry.library_hash = hash; entry.asset_name = asset_name; - entry.asset_type = s_known_asset_types[i]; + entry.asset_type = (deps_entry_t::asset_types) i; entry.relative_path = rel_path; entry.is_serviceable = serviceable; + entry.is_rid_specific = rid_specific; // TODO: Deps file does not follow spec. It uses '\\', should use '/' replace_char(&entry.relative_path, _X('\\'), _X('/')); @@ -84,7 +87,7 @@ void deps_json_t::reconcile_libraries_with_targets( [deps_entry_t::asset_types::runtime].size() - 1; } - trace::info(_X("Added %s %s deps entry [%d] [%s, %s, %s]"), s_known_asset_types[i], entry.asset_name.c_str(), m_deps_entries[i].size() - 1, entry.library_name.c_str(), entry.library_version.c_str(), entry.relative_path.c_str()); + trace::info(_X("Added %s %s deps entry [%d] [%s, %s, %s]"), deps_entry_t::s_known_asset_types[i], entry.asset_name.c_str(), m_deps_entries[i].size() - 1, entry.library_name.c_str(), entry.library_version.c_str(), entry.relative_path.c_str()); if (i == deps_entry_t::asset_types::native && entry.asset_name == LIBCORECLR_FILENAME) @@ -124,9 +127,9 @@ pal::string_t get_own_rid() bool deps_json_t::perform_rid_fallback(rid_specific_assets_t* portable_assets, const rid_fallback_graph_t& rid_fallback_graph) { pal::string_t host_rid = get_own_rid(); - for (auto& package : *portable_assets) + for (auto& package : portable_assets->libs) { - pal::string_t matched_rid = package.second.count(host_rid) ? host_rid : _X(""); + pal::string_t matched_rid = package.second.rid_assets.count(host_rid) ? host_rid : _X(""); if (matched_rid.empty()) { if (rid_fallback_graph.count(host_rid) == 0) @@ -136,7 +139,7 @@ bool deps_json_t::perform_rid_fallback(rid_specific_assets_t* portable_assets, c } const auto& fallback_rids = rid_fallback_graph.find(host_rid)->second; auto iter = std::find_if(fallback_rids.begin(), fallback_rids.end(), [&package](const pal::string_t& rid) { - return package.second.count(rid); + return package.second.rid_assets.count(rid); }); if (iter != fallback_rids.end()) { @@ -146,15 +149,15 @@ bool deps_json_t::perform_rid_fallback(rid_specific_assets_t* portable_assets, c if (matched_rid.empty()) { - package.second.clear(); + package.second.rid_assets.clear(); } - for (auto iter = package.second.begin(); iter != package.second.end(); /* */) + for (auto iter = package.second.rid_assets.begin(); iter != package.second.rid_assets.end(); /* */) { if (iter->first != matched_rid) { trace::verbose(_X("Chose %s, so removing rid (%s) specific assets for package %s"), matched_rid.c_str(), iter->first.c_str(), package.first.c_str()); - iter = package.second.erase(iter); + iter = package.second.rid_assets.erase(iter); } else { @@ -182,12 +185,12 @@ bool deps_json_t::process_runtime_targets(const json_value& json, const pal::str for (const auto& file : files) { const auto& type = file.second.at(_X("assetType")).as_string(); - for (int i = 0; i < s_known_asset_types.size(); ++i) + for (int i = 0; i < deps_entry_t::s_known_asset_types.size(); ++i) { - if (pal::strcasecmp(type.c_str(), s_known_asset_types[i]) == 0) + if (pal::strcasecmp(type.c_str(), deps_entry_t::s_known_asset_types[i]) == 0) { const auto& rid = file.second.at(_X("rid")).as_string(); - assets[package.first][rid][i].push_back(file.first); + assets.libs[package.first].rid_assets[rid].by_type[i].vec.push_back(file.first); } } } @@ -208,15 +211,15 @@ bool deps_json_t::process_targets(const json_value& json, const pal::string_t& t { // if (package.second.at(_X("type")).as_string() != _X("package")) continue; const auto& asset_types = package.second.as_object(); - for (int i = 0; i < s_known_asset_types.size(); ++i) + for (int i = 0; i < deps_entry_t::s_known_asset_types.size(); ++i) { - auto iter = asset_types.find(s_known_asset_types[i]); + auto iter = asset_types.find(deps_entry_t::s_known_asset_types[i]); if (iter != asset_types.end()) { for (const auto& file : iter->second.as_object()) { - trace::info(_X("Adding %s asset %s from %s"), s_known_asset_types[i], file.first.c_str(), package.first.c_str()); - assets[package.first][i].push_back(file.first); + trace::info(_X("Adding %s asset %s from %s"), deps_entry_t::s_known_asset_types[i], file.first.c_str(), package.first.c_str()); + assets.libs[package.first].by_type[i].vec.push_back(file.first); } } } @@ -226,40 +229,41 @@ bool deps_json_t::process_targets(const json_value& json, const pal::string_t& t bool deps_json_t::load_portable(const json_value& json, const pal::string_t& target_name, const rid_fallback_graph_t& rid_fallback_graph) { - rid_specific_assets_t rid_assets; - if (!process_runtime_targets(json, target_name, rid_fallback_graph, &rid_assets)) + if (!process_runtime_targets(json, target_name, rid_fallback_graph, &m_rid_assets)) { return false; } - deps_assets_t non_rid_assets; - if (!process_targets(json, target_name, &non_rid_assets)) + if (!process_targets(json, target_name, &m_assets)) { return false; } - auto package_exists = [&rid_assets, &non_rid_assets](const pal::string_t& package) -> bool { - return rid_assets.count(package) || non_rid_assets.count(package); + auto package_exists = [&](const pal::string_t& package) -> bool { + return m_rid_assets.libs.count(package) || m_assets.libs.count(package); }; - std::vector empty; - auto get_relpaths = [&rid_assets, &non_rid_assets, &empty](const pal::string_t& package, int type_index) -> const std::vector& { + const std::vector empty; + auto get_relpaths = [&](const pal::string_t& package, int type_index, bool* rid_specific) -> const std::vector& { + + *rid_specific = false; // Is there any rid specific assets for this type ("native" or "runtime" or "resources") - if (rid_assets.count(package) && !rid_assets[package].empty()) + if (m_rid_assets.libs.count(package) && !m_rid_assets.libs[package].rid_assets.empty()) { - const auto& assets_by_type = rid_assets[package].begin()->second[type_index]; + const auto& assets_by_type = m_rid_assets.libs[package].rid_assets.begin()->second.by_type[type_index].vec; if (!assets_by_type.empty()) { + *rid_specific = true; return assets_by_type; } - trace::verbose(_X("There were no rid specific %s asset for %s"), deps_json_t::s_known_asset_types[type_index], package.c_str()); + trace::verbose(_X("There were no rid specific %s asset for %s"), deps_entry_t::s_known_asset_types[type_index], package.c_str()); } - if (non_rid_assets.count(package)) + if (m_assets.libs.count(package)) { - return non_rid_assets[package][type_index]; + return m_assets.libs[package].by_type[type_index].vec; } return empty; @@ -272,19 +276,18 @@ bool deps_json_t::load_portable(const json_value& json, const pal::string_t& tar bool deps_json_t::load_standalone(const json_value& json, const pal::string_t& target_name) { - deps_assets_t assets; - - if (!process_targets(json, target_name, &assets)) + if (!process_targets(json, target_name, &m_assets)) { return false; } - auto package_exists = [&assets](const pal::string_t& package) -> bool { - return assets.count(package); + auto package_exists = [&](const pal::string_t& package) -> bool { + return m_assets.libs.count(package); }; - auto get_relpaths = [&assets](const pal::string_t& package, int type_index) -> const std::vector& { - return assets[package][type_index]; + auto get_relpaths = [&](const pal::string_t& package, int type_index, bool* rid_specific) -> const std::vector& { + *rid_specific = false; + return m_assets.libs[package].by_type[type_index].vec; }; reconcile_libraries_with_targets(json, package_exists, get_relpaths); @@ -320,6 +323,24 @@ bool deps_json_t::load_standalone(const json_value& json, const pal::string_t& t return true; } +bool deps_json_t::has_package(const pal::string_t& name, const pal::string_t& ver) const +{ + pal::string_t pv = name; + pv.push_back(_X('/')); + pv.append(ver); + + auto iter = m_rid_assets.libs.find(pv); + if (iter != m_rid_assets.libs.end()) + { + if (!iter->second.rid_assets.empty()) + { + return true; + } + } + + return m_assets.libs.count(pv); +} + // ----------------------------------------------------------------------------- // Load the deps file and parse its "entry" lines which contain the "fields" of // the entry. Populate an array of these entries. diff --git a/src/corehost/cli/deps_format.h b/src/corehost/cli/deps_format.h index 94cb11fa8..8ac273b46 100644 --- a/src/corehost/cli/deps_format.h +++ b/src/corehost/cli/deps_format.h @@ -6,6 +6,7 @@ #include #include +#include #include #include "pal.h" #include "deps_entry.h" @@ -14,13 +15,15 @@ class deps_json_t { typedef web::json::value json_value; - typedef std::array, deps_entry_t::asset_types::count> vectors_t; - typedef std::unordered_map str_to_vectors_map_t; - typedef std::unordered_map> str_to_vector_map_t; + struct vec_t { std::vector vec; }; + struct assets_t { std::array by_type; }; + struct deps_assets_t { std::unordered_map libs; }; + struct rid_assets_t { std::unordered_map rid_assets; }; + struct rid_specific_assets_t { std::unordered_map libs; }; + typedef std::unordered_map> str_to_vector_map_t; typedef str_to_vector_map_t rid_fallback_graph_t; - typedef str_to_vectors_map_t deps_assets_t; - typedef std::unordered_map rid_specific_assets_t; + public: deps_json_t() @@ -47,6 +50,8 @@ public: return m_deps_entries[type]; } + bool has_package(const pal::string_t& name, const pal::string_t& ver) const; + bool has_coreclr_entry() { return m_coreclr_index >= 0; @@ -91,14 +96,15 @@ private: void reconcile_libraries_with_targets( const json_value& json, const std::function& library_exists_fn, - const std::function&(const pal::string_t&, int)>& get_rel_paths_by_asset_type_fn); + const std::function&(const pal::string_t&, int, bool*)>& get_rel_paths_by_asset_type_fn); bool perform_rid_fallback(rid_specific_assets_t* portable_assets, const rid_fallback_graph_t& rid_fallback_graph); - static const std::array s_known_asset_types; - std::vector m_deps_entries[deps_entry_t::asset_types::count]; + deps_assets_t m_assets; + rid_specific_assets_t m_rid_assets; + std::unordered_map m_ni_entries; rid_fallback_graph_t m_rid_fallback_graph; int m_coreclr_index; @@ -106,21 +112,4 @@ private: bool m_valid; }; -class deps_text_t -{ -public: - deps_text_t(const pal::string_t& deps_path) - : m_valid(load(deps_path)) - { - } - - bool load(const pal::string_t& deps_path); - bool is_valid() { return m_valid; } - const std::vector& get_entries() { return m_deps_entries; } - -private: - std::vector m_deps_entries; - bool m_valid; -}; - #endif // __DEPS_FORMAT_H_ diff --git a/src/corehost/cli/deps_resolver.cpp b/src/corehost/cli/deps_resolver.cpp index 167fc5107..23a6ca8b6 100644 --- a/src/corehost/cli/deps_resolver.cpp +++ b/src/corehost/cli/deps_resolver.cpp @@ -6,9 +6,12 @@ #include #include "trace.h" +#include "deps_entry.h" #include "deps_format.h" #include "deps_resolver.h" #include "utils.h" +#include "fx_ver.h" +#include "libhost.h" namespace { @@ -43,7 +46,7 @@ void add_tpa_asset( // the "output" string. // void add_unique_path( - const pal::string_t& type, + deps_entry_t::asset_types asset_type, const pal::string_t& path, std::set* existing, pal::string_t* output) @@ -57,7 +60,7 @@ void add_unique_path( return; } - trace::verbose(_X("Adding to %s path: %s"), type.c_str(), real.c_str()); + trace::verbose(_X("Adding to %s path: %s"), deps_entry_t::s_known_asset_types[asset_type], real.c_str()); output->append(real); @@ -119,6 +122,200 @@ void deps_resolver_t::get_dir_assemblies( } } +bool deps_resolver_t::try_roll_forward(const deps_entry_t& entry, + const pal::string_t& probe_dir, + pal::string_t* candidate) +{ + trace::verbose(_X("Attempting a roll forward for [%s/%s/%s] in [%s]"), entry.library_name.c_str(), entry.library_version.c_str(), entry.relative_path.c_str(), probe_dir.c_str()); + + const pal::string_t& lib_ver = entry.library_version; + + fx_ver_t cur_ver(-1, -1, -1); + if (!fx_ver_t::parse(lib_ver, &cur_ver, false)) + { + trace::verbose(_X("No roll forward as specified version [%s] could not be parsed"), lib_ver.c_str()); + return false; + } + + // Extract glob string of the form: 1.0.* from the version 1.0.0-prerelease-00001. + size_t pat_start = lib_ver.find(_X('.'), lib_ver.find(_X('.')) + 1); + pal::string_t maj_min_star = lib_ver.substr(0, pat_start + 1) + _X('*'); + + pal::string_t path = probe_dir; + append_path(&path, entry.library_name.c_str()); + + pal::string_t cache_key = path; + append_path(&cache_key, maj_min_star.c_str()); + + pal::string_t max_str; + if (m_roll_forward_cache.count(cache_key)) + { + max_str = m_roll_forward_cache[cache_key]; + trace::verbose(_X("Found cached roll forward version [%s] -> [%s]"), lib_ver.c_str(), max_str.c_str()); + } + else + { + try_patch_roll_forward_in_dir(path, cur_ver, &max_str, true); + m_roll_forward_cache[cache_key] = max_str; + } + + append_path(&path, max_str.c_str()); + + return entry.to_rel_path(path, candidate); +} + +void deps_resolver_t::setup_probe_config( + const corehost_init_t* init, + const runtime_config_t& config, + const arguments_t& args) +{ + if (pal::directory_exists(args.dotnet_extensions)) + { + pal::string_t ext_ni = args.dotnet_extensions; + append_path(&ext_ni, get_arch()); + if (pal::directory_exists(ext_ni)) + { + // Servicing NI probe. + m_probes.push_back(probe_config_t::svc_ni(ext_ni, config.get_fx_roll_fwd())); + } + + // Servicing normal probe. + m_probes.push_back(probe_config_t::svc(args.dotnet_extensions, config.get_fx_roll_fwd())); + } + + if (pal::directory_exists(args.dotnet_packages_cache)) + { + pal::string_t ni_packages_cache = args.dotnet_packages_cache; + append_path(&ni_packages_cache, get_arch()); + if (pal::directory_exists(ni_packages_cache)) + { + // Packages cache NI probe + m_probes.push_back(probe_config_t::cache_ni(ni_packages_cache)); + } + + // Packages cache probe + m_probes.push_back(probe_config_t::cache(args.dotnet_packages_cache)); + } + + if (pal::directory_exists(m_fx_dir)) + { + // FX probe + m_probes.push_back(probe_config_t::fx(m_fx_dir, m_fx_deps.get())); + } + + for (const auto& probe : m_additional_probes) + { + // Additional paths + bool roll_fwd = config.get_fx_roll_fwd(); + if (m_package_cache == probe) + { + // FIXME: For now, no roll forward for nuget cache. This should come from config runtimeconfig.dev.json. + roll_fwd = false; + } + m_probes.push_back(probe_config_t::additional(probe, roll_fwd)); + } + + if (trace::is_enabled()) + { + trace::verbose(_X("-- Listing probe configurations...")); + for (const auto& pc : m_probes) + { + pc.print(); + } + } +} + +void deps_resolver_t::setup_additional_probes(const std::vector& probe_paths) +{ + m_additional_probes.assign(probe_paths.begin(), probe_paths.end()); + + for (auto iter = m_additional_probes.begin(); iter != m_additional_probes.end(); ) + { + if (pal::directory_exists(*iter)) + { + ++iter; + } + else + { + iter = m_additional_probes.erase(iter); + } + } + // FIXME: Remove nuget support with runtimeconfig.dev.json + // and setup roll forward to come from the config in setup_probe_config + if (m_additional_probes.empty()) + { + // FIXME: Remove this function call entirely including the functiond definition. + (void)pal::get_default_packages_directory(&m_package_cache); + if (pal::directory_exists(m_package_cache)) + { + m_additional_probes.push_back(m_package_cache); + } + } +} + +bool deps_resolver_t::probe_entry_in_configs(const deps_entry_t& entry, pal::string_t* candidate) +{ + candidate->clear(); + for (const auto& config : m_probes) + { + trace::verbose(_X(" Considering entry [%s/%s/%s] and probe dir [%s]"), entry.library_name.c_str(), entry.library_version.c_str(), entry.relative_path.c_str(), config.probe_dir.c_str()); + + if (config.only_serviceable_assets && !entry.is_serviceable) + { + trace::verbose(_X(" Skipping... not serviceable asset")); + continue; + } + if (config.only_runtime_assets && entry.asset_type != deps_entry_t::asset_types::runtime) + { + trace::verbose(_X(" Skipping... not runtime asset")); + continue; + } + pal::string_t probe_dir = config.probe_dir; + if (config.match_hash) + { + if (entry.to_hash_matched_path(probe_dir, candidate)) + { + assert(!config.roll_forward); + trace::verbose(_X(" Matched hash for [%s]"), candidate->c_str()); + return true; + } + trace::verbose(_X(" Skipping... match hash failed")); + } + else if (config.probe_deps_json) + { + // If the deps json has it then someone has already done rid selection and put the right stuff in the dir. + // So checking just package name and version would suffice. No need to check further for the exact asset relative path. + if (config.probe_deps_json->has_package(entry.library_name, entry.library_version) && entry.to_dir_path(probe_dir, candidate)) + { + trace::verbose(_X(" Probed deps json and matched [%s]"), candidate->c_str()); + return true; + } + trace::verbose(_X(" Skipping... probe in deps json failed")); + } + else if (!config.roll_forward) + { + if (entry.to_full_path(probe_dir, candidate)) + { + trace::verbose(_X(" Specified no roll forward; matched [%s]"), candidate->c_str()); + return true; + } + trace::verbose(_X(" Skipping... not found in probe dir")); + } + else if (config.roll_forward) + { + if (try_roll_forward(entry, probe_dir, candidate)) + { + trace::verbose(_X(" Specified roll forward; matched [%s]"), candidate->c_str()); + return true; + } + trace::verbose(_X(" Skipping... could not roll forward and match in probe dir")); + } + + // continue to try next probe config + } + return false; +} + // ----------------------------------------------------------------------------- // Resolve coreclr directory from the deps file. // @@ -126,22 +323,21 @@ void deps_resolver_t::get_dir_assemblies( // Look for CoreCLR from the dependency list in the package cache and then // the packages directory. // -pal::string_t deps_resolver_t::resolve_coreclr_dir( - const pal::string_t& app_dir, - const pal::string_t& package_dir, - const pal::string_t& package_cache_dir) +pal::string_t deps_resolver_t::resolve_coreclr_dir() { auto process_coreclr = [&] (bool is_portable, const pal::string_t& deps_dir, deps_json_t* deps) -> pal::string_t { pal::string_t candidate; - // Servicing override. if (deps->has_coreclr_entry()) { const deps_entry_t& entry = deps->get_coreclr_entry(); - trace::verbose(_X("Probing for CoreCLR package=[%s][%s] in servicing"), entry.library_name.c_str(), entry.library_version.c_str()); - if (entry.is_serviceable && m_svc.find_redirection(entry.library_name, entry.library_version, entry.relative_path, &candidate)) + if (probe_entry_in_configs(entry, &candidate)) + { + return get_directory(candidate); + } + else if (entry.is_rid_specific && entry.to_rel_path(deps_dir, &candidate)) { return get_directory(candidate); } @@ -151,68 +347,21 @@ pal::string_t deps_resolver_t::resolve_coreclr_dir( trace::verbose(_X("Deps has no coreclr entry.")); } - // Package cache. - pal::string_t coreclr_cache; - if (!package_cache_dir.empty()) + // App/FX main dir or standalone app dir. + trace::verbose(_X("Probing for CoreCLR in deps directory=[%s]"), deps_dir.c_str()); + if (coreclr_exists_in_dir(deps_dir)) { - if (deps->has_coreclr_entry()) - { - const deps_entry_t& entry = deps->get_coreclr_entry(); - trace::verbose(_X("Probing for CoreCLR package=[%s][%s] in package cache=[%s]"), entry.library_name.c_str(), entry.library_version.c_str(), package_cache_dir.c_str()); - if (entry.to_hash_matched_path(package_cache_dir, &coreclr_cache)) - { - return get_directory(coreclr_cache); - } - } - } - - // Deps directory: lookup relative path if portable, else look sxs. - if (is_portable) - { - pal::string_t coreclr_portable; - if (deps->has_coreclr_entry()) - { - const deps_entry_t& entry = deps->get_coreclr_entry(); - trace::verbose(_X("Probing for CoreCLR package=[%s][%s] in portable app dir=[%s]"), entry.library_name.c_str(), entry.library_version.c_str(), deps_dir.c_str()); - if (entry.to_full_path(deps_dir, &coreclr_portable)) - { - return get_directory(coreclr_portable); - } - } - } - else - { - // App main dir or standalone app dir. - trace::verbose(_X("Probing for CoreCLR in deps directory=[%s]"), deps_dir.c_str()); - if (coreclr_exists_in_dir(deps_dir)) - { - return deps_dir; - } - } - - // Packages dir. - pal::string_t coreclr_package; - if (!package_dir.empty()) - { - if (deps->has_coreclr_entry()) - { - const deps_entry_t& entry = deps->get_coreclr_entry(); - trace::verbose(_X("Probing for CoreCLR package=[%s][%s] in packages dir=[%s]"), entry.library_name.c_str(), entry.library_version.c_str(), package_dir.c_str()); - if (entry.to_full_path(package_dir, &coreclr_package)) - { - return get_directory(coreclr_package); - } - } + return deps_dir; } return pal::string_t(); }; - trace::info(_X("--- Starting CoreCLR Proble from app deps.json")); - pal::string_t clr_dir = process_coreclr(m_portable, app_dir, m_deps.get()); + trace::info(_X("-- Starting CoreCLR Probe from app deps.json")); + pal::string_t clr_dir = process_coreclr(m_portable, m_app_dir, m_deps.get()); if (clr_dir.empty() && m_portable) { - trace::info(_X("--- Starting CoreCLR Proble from FX deps.json")); + trace::info(_X("-- Starting CoreCLR Probe from FX deps.json")); clr_dir = process_coreclr(false, m_fx_dir, m_fx_deps.get()); } if (!clr_dir.empty()) @@ -230,44 +379,14 @@ pal::string_t deps_resolver_t::resolve_coreclr_dir( return pal::string_t(); } -// ----------------------------------------------------------------------------- -// Resolve the TPA list order. -// -// Description: -// First, add mscorlib to the TPA. Then for each deps entry, check if they -// are serviced. If they are not serviced, then look if they are present -// app local. Worst case, default to the primary and seconday package -// caches. Finally, for cases where deps file may not be present or if deps -// did not have an entry for an app local assembly, just use them from the -// app dir in the TPA path. -// -// Parameters: -// app_dir - The application local directory -// package_dir - The directory path to where packages are restored -// package_cache_dir - The directory path to secondary cache for packages -// clr_dir - The directory where the host loads the CLR -// -// Returns: -// output - Pointer to a string that will hold the resolved TPA paths -// void deps_resolver_t::resolve_tpa_list( - const pal::string_t& app_dir, - const pal::string_t& package_dir, - const pal::string_t& package_cache_dir, const pal::string_t& clr_dir, pal::string_t* output) { const std::vector empty(0); - pal::string_t ni_package_cache_dir; - if (!package_cache_dir.empty()) - { - ni_package_cache_dir = package_cache_dir; - append_path(&ni_package_cache_dir, get_arch()); - } - // Obtain the local assemblies in the app dir. - get_dir_assemblies(app_dir, _X("local"), &m_local_assemblies); + get_dir_assemblies(m_app_dir, _X("local"), &m_local_assemblies); if (m_portable) { // For portable also obtain FX dir assemblies. @@ -276,9 +395,8 @@ void deps_resolver_t::resolve_tpa_list( std::set items; - auto process_entry = [&](bool is_portable, deps_json_t* deps, const dir_assemblies_t& dir_assemblies, const deps_entry_t& entry) + auto process_entry = [&](const pal::string_t& deps_dir, deps_json_t* deps, const dir_assemblies_t& dir_assemblies, const deps_entry_t& entry) { - // Is this asset a "runtime" type? if (items.count(entry.asset_name)) { return; @@ -288,42 +406,31 @@ void deps_resolver_t::resolve_tpa_list( trace::info(_X("Processing TPA for deps entry [%s, %s, %s]"), entry.library_name.c_str(), entry.library_version.c_str(), entry.relative_path.c_str()); - // Is this a serviceable entry and is there an entry in the servicing index? - if (entry.is_serviceable && entry.library_type == _X("Package") && - m_svc.find_redirection(entry.library_name, entry.library_version, entry.relative_path, &candidate)) + // Try to probe from the shared locations. + if (probe_entry_in_configs(entry, &candidate)) { add_tpa_asset(entry.asset_name, candidate, &items, output); } - // Is an NI image for this entry present in the secondary package cache? - else if (entry.to_hash_matched_path(ni_package_cache_dir, &candidate)) + // The rid asset should be picked up from app relative subpath. + else if (entry.is_rid_specific && entry.to_rel_path(deps_dir, &candidate)) { add_tpa_asset(entry.asset_name, candidate, &items, output); } - // Is this entry present in the secondary package cache? (note: no .ni extension) - else if (entry.to_hash_matched_path(package_cache_dir, &candidate)) - { - add_tpa_asset(entry.asset_name, candidate, &items, output); - } - // The app is portable so the rid asset should be picked up from relative subpath. - else if (is_portable && deps->try_ni(entry).to_rel_path(app_dir, &candidate)) - { - add_tpa_asset(entry.asset_name, candidate, &items, output); - } - // The app is portable, but there could be a rid-less asset in the app base. + // The rid-less asset should be picked up from the app base. else if (dir_assemblies.count(entry.asset_name)) { add_tpa_asset(entry.asset_name, dir_assemblies.find(entry.asset_name)->second, &items, output); } - // Is this entry present in the package restore dir? - else if (!package_dir.empty() && deps->try_ni(entry).to_full_path(package_dir, &candidate)) + else { - add_tpa_asset(entry.asset_name, candidate, &items, output); + // FIXME: Consider this error as a fail fast? + trace::verbose(_X("Error: Could not resolve path to assembly: [%s, %s, %s]"), entry.library_name.c_str(), entry.library_version.c_str(), entry.relative_path.c_str()); } }; const auto& deps_entries = m_deps->get_entries(deps_entry_t::asset_types::runtime); std::for_each(deps_entries.begin(), deps_entries.end(), [&](const deps_entry_t& entry) { - process_entry(m_portable, m_deps.get(), m_local_assemblies, entry); + process_entry(m_app_dir, m_deps.get(), m_local_assemblies, entry); }); // Finally, if the deps file wasn't present or has missing entries, then @@ -335,7 +442,7 @@ void deps_resolver_t::resolve_tpa_list( const auto& fx_entries = m_portable ? m_fx_deps->get_entries(deps_entry_t::asset_types::runtime) : empty; std::for_each(fx_entries.begin(), fx_entries.end(), [&](const deps_entry_t& entry) { - process_entry(false, m_fx_deps.get(), m_fx_assemblies, entry); + process_entry(m_fx_dir, m_fx_deps.get(), m_fx_assemblies, entry); }); for (const auto& kv : m_fx_assemblies) @@ -366,14 +473,12 @@ void deps_resolver_t::resolve_tpa_list( // output - Pointer to a string that will hold the resolved lookup dirs // void deps_resolver_t::resolve_probe_dirs( - const pal::string_t& asset_type, - const pal::string_t& app_dir, - const pal::string_t& package_dir, - const pal::string_t& package_cache_dir, + deps_entry_t::asset_types asset_type, const pal::string_t& clr_dir, pal::string_t* output) { - assert(asset_type == _X("resources") || asset_type == _X("native")); + bool is_resources = asset_type == deps_entry_t::asset_types::resources; + assert(is_resources || asset_type == deps_entry_t::asset_types::native); // For resources assemblies, we need to provide the base directory of the resources path. // For example: .../Foo/en-US/Bar.dll, then, the resolved path is .../Foo @@ -384,51 +489,31 @@ void deps_resolver_t::resolve_probe_dirs( std::function native = [] (const pal::string_t& str) { return get_directory(str); }; - std::function& action = (asset_type == _X("resources")) ? resources : native; - deps_entry_t::asset_types entry_type = (asset_type == _X("resources")) ? deps_entry_t::asset_types::resources : deps_entry_t::asset_types::native; + std::function& action = is_resources ? resources : native; std::set items; std::vector empty(0); - const auto& entries = m_deps->get_entries(entry_type); - const auto& fx_entries = m_portable ? m_fx_deps->get_entries(entry_type) : empty; - - // Fill the "output" with serviced DLL directories if they are serviceable - // and have an entry present. - auto add_serviced_entry = [&](const deps_entry_t& entry) - { - pal::string_t redirection_path; - if (entry.is_serviceable && entry.library_type == _X("Package") && - m_svc.find_redirection(entry.library_name, entry.library_version, entry.relative_path, &redirection_path)) - { - add_unique_path(asset_type, action(redirection_path), &items, output); - } - }; - - std::for_each(entries.begin(), entries.end(), add_serviced_entry); - std::for_each(fx_entries.begin(), fx_entries.end(), add_serviced_entry); + const auto& entries = m_deps->get_entries(asset_type); + const auto& fx_entries = m_portable ? m_fx_deps->get_entries(asset_type) : empty; pal::string_t candidate; - // Take care of the secondary cache path - if (!package_cache_dir.empty()) + auto add_package_cache_entry = [&](const deps_entry_t& entry) { - auto add_package_cache_entry = [&](const deps_entry_t& entry) + if (probe_entry_in_configs(entry, &candidate)) { - if (entry.to_hash_matched_path(package_cache_dir, &candidate)) - { - add_unique_path(asset_type, action(candidate), &items, output); - } - }; - std::for_each(entries.begin(), entries.end(), add_package_cache_entry); - std::for_each(fx_entries.begin(), fx_entries.end(), add_package_cache_entry); - } + add_unique_path(asset_type, action(candidate), &items, output); + } + }; + std::for_each(entries.begin(), entries.end(), add_package_cache_entry); + std::for_each(fx_entries.begin(), fx_entries.end(), add_package_cache_entry); - // For portable path, the app relative directory must be used. + // For portable rid specific assets, the app relative directory must be used. if (m_portable) { std::for_each(entries.begin(), entries.end(), [&](const deps_entry_t& entry) { - if (entry.asset_type == asset_type && entry.to_rel_path(app_dir, &candidate)) + if (entry.is_rid_specific && entry.asset_type == asset_type && entry.to_rel_path(m_app_dir, &candidate)) { add_unique_path(asset_type, action(candidate), &items, output); } @@ -436,7 +521,7 @@ void deps_resolver_t::resolve_probe_dirs( } // App local path - add_unique_path(asset_type, app_dir, &items, output); + add_unique_path(asset_type, m_app_dir, &items, output); // FX path if present if (!m_fx_dir.empty()) @@ -444,20 +529,6 @@ void deps_resolver_t::resolve_probe_dirs( add_unique_path(asset_type, m_fx_dir, &items, output); } - // Take care of the package restore path - if (!package_dir.empty()) - { - auto add_packages_entry = [&](const deps_entry_t& entry) - { - if (entry.asset_type == asset_type && entry.to_full_path(package_dir, &candidate)) - { - add_unique_path(asset_type, action(candidate), &items, output); - } - }; - std::for_each(entries.begin(), entries.end(), add_packages_entry); - std::for_each(fx_entries.begin(), fx_entries.end(), add_packages_entry); - } - // CLR path add_unique_path(asset_type, clr_dir, &items, output); } @@ -475,15 +546,10 @@ void deps_resolver_t::resolve_probe_dirs( // resolved path ordering. // // -bool deps_resolver_t::resolve_probe_paths( - const pal::string_t& app_dir, - const pal::string_t& package_dir, - const pal::string_t& package_cache_dir, - const pal::string_t& clr_dir, - probe_paths_t* probe_paths) +bool deps_resolver_t::resolve_probe_paths(const pal::string_t& clr_dir, probe_paths_t* probe_paths) { - resolve_tpa_list(app_dir, package_dir, package_cache_dir, clr_dir, &probe_paths->tpa); - resolve_probe_dirs(_X("native"), app_dir, package_dir, package_cache_dir, clr_dir, &probe_paths->native); - resolve_probe_dirs(_X("resources"), app_dir, package_dir, package_cache_dir, clr_dir, &probe_paths->resources); + resolve_tpa_list(clr_dir, &probe_paths->tpa); + resolve_probe_dirs(deps_entry_t::asset_types::native, clr_dir, &probe_paths->native); + resolve_probe_dirs(deps_entry_t::asset_types::resources, clr_dir, &probe_paths->resources); return true; } diff --git a/src/corehost/cli/deps_resolver.h b/src/corehost/cli/deps_resolver.h index c261daf15..04eeedc79 100644 --- a/src/corehost/cli/deps_resolver.h +++ b/src/corehost/cli/deps_resolver.h @@ -7,10 +7,10 @@ #include #include "pal.h" +#include "args.h" #include "trace.h" #include "deps_format.h" #include "deps_entry.h" -#include "servicing_index.h" #include "runtime_config.h" // Probe paths to be resolved for ordering @@ -24,18 +24,20 @@ struct probe_paths_t class deps_resolver_t { public: - deps_resolver_t(const pal::string_t& fx_dir, const runtime_config_t* config, const arguments_t& args) - : m_svc(args.dotnet_servicing) - , m_fx_dir(fx_dir) + deps_resolver_t(const corehost_init_t* init, const runtime_config_t& config, const arguments_t& args) + // Important: FX dir should come from "init" than "config", + // since the host could be launching from FX dir. + : m_fx_dir(init->fx_dir()) + , m_app_dir(args.app_dir) , m_coreclr_index(-1) - , m_portable(config->get_portable()) + , m_portable(config.get_portable()) , m_deps(nullptr) , m_fx_deps(nullptr) { m_deps_file = args.deps_path; if (m_portable) { - m_fx_deps_file = get_fx_deps(fx_dir, config->get_fx_name()); + m_fx_deps_file = get_fx_deps(m_fx_dir, config.get_fx_name()); trace::verbose(_X("Using %s FX deps file"), m_fx_deps_file.c_str()); trace::verbose(_X("Using %s deps file"), m_deps_file.c_str()); m_fx_deps = std::unique_ptr(new deps_json_t(false, m_fx_deps_file)); @@ -45,22 +47,25 @@ public: { m_deps = std::unique_ptr(new deps_json_t(false, m_deps_file)); } - } + setup_additional_probes(args.probe_paths); + setup_probe_config(init, config, args); + } bool valid() { return m_deps->is_valid() && (!m_portable || m_fx_deps->is_valid()); } + void setup_probe_config( + const corehost_init_t* init, + const runtime_config_t& config, + const arguments_t& args); + + void setup_additional_probes(const std::vector& probe_paths); + bool resolve_probe_paths( - const pal::string_t& app_dir, - const pal::string_t& package_dir, - const pal::string_t& package_cache_dir, const pal::string_t& clr_dir, probe_paths_t* probe_paths); - pal::string_t resolve_coreclr_dir( - const pal::string_t& app_dir, - const pal::string_t& package_dir, - const pal::string_t& package_cache_dir); + pal::string_t resolve_coreclr_dir(); const pal::string_t& get_fx_deps_file() const { @@ -83,18 +88,12 @@ private: // Resolve order for TPA lookup. void resolve_tpa_list( - const pal::string_t& app_dir, - const pal::string_t& package_dir, - const pal::string_t& package_cache_dir, const pal::string_t& clr_dir, pal::string_t* output); // Resolve order for culture and native DLL lookup. void resolve_probe_dirs( - const pal::string_t& asset_type, - const pal::string_t& app_dir, - const pal::string_t& package_dir, - const pal::string_t& package_cache_dir, + deps_entry_t::asset_types asset_type, const pal::string_t& clr_dir, pal::string_t* output); @@ -104,18 +103,32 @@ private: const pal::string_t& dir_name, std::unordered_map* dir_assemblies); - // Servicing index to resolve serviced assembly paths. - servicing_index_t m_svc; + // Probe entry in probe configurations. + bool probe_entry_in_configs( + const deps_entry_t& entry, + pal::string_t* candidate); + + // Try auto roll forward, if not return entry in probe dir. + bool try_roll_forward( + const deps_entry_t& entry, + const pal::string_t& probe_dir, + pal::string_t* candidate); // Framework deps file. pal::string_t m_fx_dir; + pal::string_t m_app_dir; + // Map of simple name -> full path of local/fx assemblies populated // in priority order of their extensions. typedef std::unordered_map dir_assemblies_t; dir_assemblies_t m_local_assemblies; dir_assemblies_t m_fx_assemblies; + std::unordered_map m_roll_forward_cache; + + pal::string_t m_package_cache; + // Special entry for coreclr in the deps entries int m_coreclr_index; @@ -131,9 +144,15 @@ private: // Deps files for the app std::unique_ptr m_deps; + // Various probe configurations. + std::vector m_probes; + // Is the deps file valid bool m_deps_valid; + // Fallback probe dir + std::vector m_additional_probes; + // Is the deps file portable app? bool m_portable; }; diff --git a/src/corehost/cli/dll/CMakeLists.txt b/src/corehost/cli/dll/CMakeLists.txt index fd19ec602..88d09c23f 100644 --- a/src/corehost/cli/dll/CMakeLists.txt +++ b/src/corehost/cli/dll/CMakeLists.txt @@ -25,13 +25,13 @@ set(SOURCES ../json/casablanca/src/json/json_parsing.cpp ../json/casablanca/src/json/json_serialization.cpp ../json/casablanca/src/utilities/asyncrt_utils.cpp + ../fxr/fx_ver.cpp ../args.cpp ../hostpolicy.cpp ../coreclr.cpp ../deps_resolver.cpp ../deps_format.cpp - ../deps_entry.cpp - ../servicing_index.cpp) + ../deps_entry.cpp) if(WIN32) diff --git a/src/corehost/cli/fxr/fx_muxer.cpp b/src/corehost/cli/fxr/fx_muxer.cpp index 3a0ed9d51..cde8f7577 100644 --- a/src/corehost/cli/fxr/fx_muxer.cpp +++ b/src/corehost/cli/fxr/fx_muxer.cpp @@ -263,13 +263,13 @@ int fx_muxer_t::execute(const int argc, const pal::char_t* argv[]) { trace::verbose(_X("Executing as a portable app as per config file [%s]"), config_file.c_str()); pal::string_t fx_dir = resolve_fx_dir(own_dir, &config); - corehost_init_t init(_X(""), _X(""), fx_dir, host_mode_t::muxer, &config); + corehost_init_t init(_X(""), config.get_probe_paths(), fx_dir, host_mode_t::muxer, &config); return execute_app(fx_dir, &init, argc, argv); } else { trace::verbose(_X("Executing as a standlone app as per config file [%s]"), config_file.c_str()); - corehost_init_t init(_X(""), _X(""), _X(""), host_mode_t::muxer, &config); + corehost_init_t init(_X(""), config.get_probe_paths(), _X(""), host_mode_t::muxer, &config); return execute_app(get_directory(app_path), &init, argc, argv); } } @@ -321,11 +321,12 @@ int fx_muxer_t::execute(const int argc, const pal::char_t* argv[]) trace::error(_X("Deps file [%s] specified but doesn't exist"), deps_file.c_str()); return StatusCode::InvalidArgFailure; } + std::vector probe_paths = { probe_path }; if (config.get_portable()) { trace::verbose(_X("Executing as a portable app as per config file [%s]"), config_file.c_str()); pal::string_t fx_dir = resolve_fx_dir(own_dir, &config); - corehost_init_t init(deps_file, probe_path, fx_dir, host_mode_t::muxer, &config); + corehost_init_t init(deps_file, probe_paths, fx_dir, host_mode_t::muxer, &config); return execute_app(fx_dir, &init, new_argv.size(), new_argv.data()); } else @@ -344,7 +345,7 @@ int fx_muxer_t::execute(const int argc, const pal::char_t* argv[]) } impl_dir = get_directory(candidate); } - corehost_init_t init(deps_file, probe_path, _X(""), host_mode_t::muxer, &config); + corehost_init_t init(deps_file, probe_paths, _X(""), host_mode_t::muxer, &config); return execute_app(impl_dir, &init, new_argv.size(), new_argv.data()); } } @@ -380,13 +381,13 @@ int fx_muxer_t::execute(const int argc, const pal::char_t* argv[]) { trace::verbose(_X("Executing dotnet.dll as a portable app as per config file [%s]"), config_file.c_str()); pal::string_t fx_dir = resolve_fx_dir(own_dir, &config); - corehost_init_t init(_X(""), _X(""), fx_dir, host_mode_t::muxer, &config); + corehost_init_t init(_X(""), std::vector(), fx_dir, host_mode_t::muxer, &config); return execute_app(fx_dir, &init, new_argv.size(), new_argv.data()); } else { trace::verbose(_X("Executing dotnet.dll as a standalone app as per config file [%s]"), config_file.c_str()); - corehost_init_t init(_X(""), _X(""), _X(""), host_mode_t::muxer, &config); + corehost_init_t init(_X(""), std::vector(), _X(""), host_mode_t::muxer, &config); return execute_app(get_directory(sdk_dotnet), &init, new_argv.size(), new_argv.data()); } } diff --git a/src/corehost/cli/fxr/fx_ver.cpp b/src/corehost/cli/fxr/fx_ver.cpp index f8490e4cc..04d19f7bc 100644 --- a/src/corehost/cli/fxr/fx_ver.cpp +++ b/src/corehost/cli/fxr/fx_ver.cpp @@ -44,7 +44,7 @@ bool fx_ver_t::operator >(const fx_ver_t& b) const return compare(*this, b) > 0; } -pal::string_t fx_ver_t::as_str() +pal::string_t fx_ver_t::as_str() const { pal::stringstream_t stream; stream << m_major << _X(".") << m_minor << _X(".") << m_patch; diff --git a/src/corehost/cli/fxr/fx_ver.h b/src/corehost/cli/fxr/fx_ver.h index e914df4b6..af6aac590 100644 --- a/src/corehost/cli/fxr/fx_ver.h +++ b/src/corehost/cli/fxr/fx_ver.h @@ -1,6 +1,9 @@ // 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_VER_H__ +#define __FX_VER_H__ + #include "pal.h" // Note: This is not SemVer (esp., in comparing pre-release part, fx_ver_t does not @@ -11,17 +14,17 @@ struct fx_ver_t 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); - int get_major() { return m_major; } - int get_minor() { return m_minor; } - int get_patch() { return m_patch; } + int get_major() const { return m_major; } + int get_minor() const { return m_minor; } + int get_patch() const { return m_patch; } void set_major(int m) { m_major = m; } void set_minor(int m) { m_minor = m; } void set_patch(int p) { m_patch = p; } - bool is_prerelease() { return !m_pre.empty(); } + bool is_prerelease() const { return !m_pre.empty(); } - pal::string_t as_str(); + pal::string_t as_str() const; bool operator ==(const fx_ver_t& b) const; bool operator !=(const fx_ver_t& b) const; @@ -40,3 +43,4 @@ private: static int compare(const fx_ver_t&a, const fx_ver_t& b); }; +#endif // __FX_VER_H__ \ No newline at end of file diff --git a/src/corehost/cli/fxr/hostfxr.cpp b/src/corehost/cli/fxr/hostfxr.cpp index 02c8e3c81..0b83286ff 100644 --- a/src/corehost/cli/fxr/hostfxr.cpp +++ b/src/corehost/cli/fxr/hostfxr.cpp @@ -6,6 +6,7 @@ #include "pal.h" #include "utils.h" #include "libhost.h" +#include "fx_ver.h" #include "fx_muxer.h" #include "error_codes.h" @@ -76,9 +77,13 @@ int execute_app( bool hostpolicy_exists_in_svc(pal::string_t* resolved_dir) { pal::string_t svc_dir; - if (!pal::getenv(_X("DOTNET_SERVICING"), &svc_dir)) + pal::get_default_extensions_directory(&svc_dir); + + pal::string_t version = _STRINGIFY(HOST_POLICY_PKG_VER); + + fx_ver_t lib_ver(-1, -1, -1); + if (!fx_ver_t::parse(version, &lib_ver, false)) { - trace::verbose(_X("Servicing root doesn't exist")); return false; } @@ -87,17 +92,16 @@ bool hostpolicy_exists_in_svc(pal::string_t* resolved_dir) { replace_char(&rel_dir, '/', DIR_SEPARATOR); } - + pal::string_t path = svc_dir; append_path(&path, _STRINGIFY(HOST_POLICY_PKG_NAME)); - append_path(&path, _STRINGIFY(HOST_POLICY_PKG_VER)); + + pal::string_t max_ver; + try_patch_roll_forward_in_dir(path, lib_ver, &max_ver, false); + + append_path(&path, max_ver.c_str()); append_path(&path, rel_dir.c_str()); - append_path(&path, LIBHOSTPOLICY_NAME); - if (!pal::realpath(&path)) - { - trace::verbose(_X("Servicing root ::realpath(%s) doesn't exist"), path.c_str()); - return false; - } + if (library_exists_in_dir(path, LIBHOSTPOLICY_NAME, nullptr)) { resolved_dir->assign(path); @@ -127,7 +131,7 @@ SHARED_API int hostfxr_main(const int argc, const pal::char_t* argv[]) case split_fx: { trace::info(_X("Host operating in split mode; own dir=[%s]"), own_dir.c_str()); - corehost_init_t init(_X(""), _X(""), own_dir, host_mode_t::split_fx, nullptr); + corehost_init_t init(_X(""), std::vector(), own_dir, host_mode_t::split_fx, nullptr); return execute_app(own_dir, &init, argc, argv); } @@ -136,7 +140,7 @@ SHARED_API int hostfxr_main(const int argc, const pal::char_t* argv[]) trace::info(_X("Host operating from standalone app dir %s"), own_dir.c_str()); pal::string_t svc_dir; - corehost_init_t init(_X(""), _X(""), _X(""), host_mode_t::standalone, nullptr); + corehost_init_t init(_X(""), std::vector(), _X(""), host_mode_t::standalone, nullptr); return execute_app( hostpolicy_exists_in_svc(&svc_dir) ? svc_dir : own_dir, &init, argc, argv); } diff --git a/src/corehost/cli/hostpolicy.cpp b/src/corehost/cli/hostpolicy.cpp index 286a7a2fc..a6588a674 100644 --- a/src/corehost/cli/hostpolicy.cpp +++ b/src/corehost/cli/hostpolicy.cpp @@ -18,7 +18,7 @@ corehost_init_t* g_init = nullptr; int run(const corehost_init_t* init, const runtime_config_t& config, const arguments_t& args) { // Load the deps resolver - deps_resolver_t resolver(init->fx_dir(), &config, args); + deps_resolver_t resolver(init, config, args); if (!resolver.valid()) { @@ -26,15 +26,7 @@ int run(const corehost_init_t* init, const runtime_config_t& config, const argum return StatusCode::ResolverInitFailure; } - // Add packages directory - pal::string_t packages_dir = init->probe_dir(); - if (packages_dir.empty() || !pal::directory_exists(packages_dir)) - { - (void)pal::get_default_packages_directory(&packages_dir); - } - trace::info(_X("Package directory: %s"), packages_dir.empty() ? _X("not specified") : packages_dir.c_str()); - - pal::string_t clr_path = resolver.resolve_coreclr_dir(args.app_dir, packages_dir, args.dotnet_packages_cache); + pal::string_t clr_path = resolver.resolve_coreclr_dir(); if (clr_path.empty() || !pal::realpath(&clr_path)) { trace::error(_X("Could not resolve coreclr path")); @@ -46,7 +38,7 @@ int run(const corehost_init_t* init, const runtime_config_t& config, const argum } probe_paths_t probe_paths; - if (!resolver.resolve_probe_paths(args.app_dir, packages_dir, args.dotnet_packages_cache, clr_path, &probe_paths)) + if (!resolver.resolve_probe_paths(clr_path, &probe_paths)) { return StatusCode::ResolverResolveFailure; } @@ -196,6 +188,11 @@ int run(const corehost_init_t* init, const runtime_config_t& config, const argum SHARED_API int corehost_load(corehost_init_t* init) { g_init = init; + if (g_init->version() != corehost_init_t::s_version) + { + trace::error(_X("The structure of init data has changed, do not know how to interpret it")); + return StatusCode::LibHostInitFailure; + } return 0; } @@ -220,12 +217,15 @@ SHARED_API int corehost_main(const int argc, const pal::char_t* argv[]) trace::info(_X("Host mode: %d"), g_init->host_mode()); trace::info(_X("Deps file: %s"), g_init->deps_file().c_str()); - trace::info(_X("Probe dir: %s"), g_init->probe_dir().c_str()); + for (const auto& probe : g_init->probe_paths()) + { + trace::info(_X("Additional probe dir: %s"), probe.c_str()); + } } // Take care of arguments arguments_t args; - if (!parse_arguments(g_init->deps_file(), g_init->probe_dir(), g_init->host_mode(), argc, argv, &args)) + if (!parse_arguments(g_init->deps_file(), g_init->probe_paths(), g_init->host_mode(), argc, argv, &args)) { return StatusCode::LibHostInvalidArgs; } diff --git a/src/corehost/cli/libhost.cpp b/src/corehost/cli/libhost.cpp index c68253f23..50f885cb5 100644 --- a/src/corehost/cli/libhost.cpp +++ b/src/corehost/cli/libhost.cpp @@ -59,3 +59,36 @@ host_mode_t detect_operating_mode(const int argc, const pal::char_t* argv[], pal } } +void try_patch_roll_forward_in_dir(const pal::string_t& cur_dir, const fx_ver_t& start_ver, pal::string_t* max_str, bool only_production) +{ + pal::string_t path = cur_dir; + + if (trace::is_enabled()) + { + pal::string_t start_str = start_ver.as_str(); + trace::verbose(_X("Reading roll forward candidates in dir [%s] for version [%s]"), path.c_str(), start_str.c_str()); + } + + pal::string_t maj_min_star = pal::to_string(start_ver.get_major()) + _X(".") + pal::to_string(start_ver.get_minor()) + _X("*"); + + std::vector list; + pal::readdir(path, maj_min_star, &list); + + fx_ver_t max_ver = start_ver; + fx_ver_t ver(-1, -1, -1); + for (const auto& str : list) + { + trace::verbose(_X("Considering roll forward candidate version [%s]"), str.c_str()); + if (fx_ver_t::parse(str, &ver, only_production)) + { + max_ver = std::max(ver, max_ver); + } + } + max_str->assign(max_ver.as_str()); + + if (trace::is_enabled()) + { + pal::string_t start_str = start_ver.as_str(); + trace::verbose(_X("Roll forwarded [%s] -> [%s] in [%s]"), start_str.c_str(), max_str->c_str(), path.c_str()); + } +} diff --git a/src/corehost/cli/libhost.h b/src/corehost/cli/libhost.h index efb83b185..78bd31791 100644 --- a/src/corehost/cli/libhost.h +++ b/src/corehost/cli/libhost.h @@ -4,6 +4,8 @@ #ifndef __LIBHOST_H__ #define __LIBHOST_H__ +#include "fx_ver.h" + enum host_mode_t { invalid = 0, @@ -12,11 +14,21 @@ enum host_mode_t split_fx }; +class fx_ver_t; class runtime_config_t; class corehost_init_t { - const pal::string_t m_probe_path; + // // WARNING // WARNING // WARNING // WARNING // WARNING // WARNING // + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // !! If you change this class layout increment the s_version field; !! + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +public: + static const int s_version = 0x8002; +private: + int m_version; + std::vector m_probe_paths; const pal::string_t m_deps_file; const pal::string_t m_fx_dir; host_mode_t m_host_mode; @@ -24,15 +36,16 @@ class corehost_init_t public: corehost_init_t( const pal::string_t& deps_file, - const pal::string_t& probe_path, + const std::vector& probe_paths, const pal::string_t& fx_dir, const host_mode_t mode, const runtime_config_t* runtime_config) : m_fx_dir(fx_dir) , m_runtime_config(runtime_config) , m_deps_file(deps_file) - , m_probe_path(probe_path) + , m_probe_paths(probe_paths) , m_host_mode(mode) + , m_version(s_version) { } @@ -46,9 +59,9 @@ public: return m_deps_file; } - const pal::string_t& probe_dir() const + const std::vector& probe_paths() const { - return m_probe_path; + return m_probe_paths; } const pal::string_t& fx_dir() const @@ -60,9 +73,16 @@ public: { return m_runtime_config; } + + int version() const + { + return m_version; + } }; pal::string_t get_runtime_config_from_file(const pal::string_t& file); host_mode_t detect_operating_mode(const int argc, const pal::char_t* argv[], pal::string_t* own_dir = nullptr); -#endif // __LIBHOST_H__ +void try_patch_roll_forward_in_dir(const pal::string_t& cur_dir, const fx_ver_t& start_ver, pal::string_t* max_str, bool only_production); + +#endif // __LIBHOST_H__ \ No newline at end of file diff --git a/src/corehost/cli/runtime_config.cpp b/src/corehost/cli/runtime_config.cpp index 03dab4ba3..7652a80b1 100644 --- a/src/corehost/cli/runtime_config.cpp +++ b/src/corehost/cli/runtime_config.cpp @@ -38,6 +38,29 @@ bool runtime_config_t::parse_opts(const json_value& opts) } } + auto probe_paths = opts_obj.find(_X("additionalProbingPaths")); + if (probe_paths != opts_obj.end()) + { + if (probe_paths->second.is_string()) + { + m_probe_paths.push_back(probe_paths->second.as_string()); + } + else + { + const auto& arr = probe_paths->second.as_array(); + for (const auto& str : arr) + { + m_probe_paths.push_back(str.as_string()); + } + } + } + + auto roll_fwd = opts_obj.find(_X("applyPatches")); + if (roll_fwd != opts_obj.end()) + { + m_fx_roll_fwd = roll_fwd->second.as_bool(); + } + auto framework = opts_obj.find(_X("framework")); if (framework == opts_obj.end()) { @@ -49,14 +72,6 @@ bool runtime_config_t::parse_opts(const json_value& opts) const auto& fx_obj = framework->second.as_object(); m_fx_name = fx_obj.at(_X("name")).as_string(); m_fx_ver = fx_obj.at(_X("version")).as_string(); - - auto value = fx_obj.find(_X("rollForward")); - if (value == fx_obj.end()) - { - return true; - } - - m_fx_roll_fwd = value->second.as_bool(); return true; } @@ -118,6 +133,11 @@ bool runtime_config_t::get_portable() const return m_portable; } +const std::vector& runtime_config_t::get_probe_paths() const +{ + return m_probe_paths; +} + void runtime_config_t::config_kv(std::vector* keys, std::vector* values) const { for (const auto& kv : m_properties) diff --git a/src/corehost/cli/runtime_config.h b/src/corehost/cli/runtime_config.h index 04c582aba..0c5466e53 100644 --- a/src/corehost/cli/runtime_config.h +++ b/src/corehost/cli/runtime_config.h @@ -15,6 +15,7 @@ public: const pal::string_t& get_gc_server() const; const pal::string_t& get_fx_version() const; const pal::string_t& get_fx_name() const; + const std::vector& get_probe_paths() const; bool get_fx_roll_fwd() const; bool get_portable() const; bool parse_opts(const json_value& opts); @@ -26,6 +27,7 @@ private: std::unordered_map m_properties; std::vector m_prop_keys; std::vector m_prop_values; + std::vector m_probe_paths; pal::string_t m_fx_name; pal::string_t m_fx_ver; bool m_fx_roll_fwd; diff --git a/src/corehost/cli/servicing_index.cpp b/src/corehost/cli/servicing_index.cpp deleted file mode 100644 index 8dd9ebd76..000000000 --- a/src/corehost/cli/servicing_index.cpp +++ /dev/null @@ -1,144 +0,0 @@ -// 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 "trace.h" -#include "servicing_index.h" - -static const pal::char_t* DOTNET_SERVICING_INDEX_TXT = _X("dotnet_servicing_index.txt"); - -servicing_index_t::servicing_index_t(const pal::string_t& svc_dir) -{ - m_patch_root = svc_dir; - if (!m_patch_root.empty()) - { - m_index_file.assign(m_patch_root); - append_path(&m_index_file, DOTNET_SERVICING_INDEX_TXT); - } - m_parsed = m_index_file.empty() || !pal::file_exists(m_index_file); -} - -bool servicing_index_t::find_redirection( - const pal::string_t& package_name, - const pal::string_t& package_version, - const pal::string_t& package_relative, - pal::string_t* redirection) -{ - ensure_redirections(); - - redirection->clear(); - - if (m_redirections.empty()) - { - return false; - } - - pal::stringstream_t stream; - stream << package_name << _X("|") << package_version << _X("|") << package_relative; - - auto iter = m_redirections.find(stream.str()); - if (iter != m_redirections.end()) - { - pal::string_t ni_root = m_patch_root; - append_path(&ni_root, get_arch()); - - // First prefer the architecture specific NI image. - pal::string_t paths[2] = { ni_root, m_patch_root }; - for (pal::string_t& full_path : paths) - { - append_path(&full_path, iter->second.c_str()); - if (pal::file_exists(full_path)) - { - *redirection = full_path; - if (trace::is_enabled()) - { - pal::string_t stream_str = stream.str(); - trace::verbose(_X("Servicing %s with %s"), stream_str.c_str(), redirection->c_str()); - } - return true; - } - trace::verbose(_X("Serviced file %s doesn't exist"), full_path.c_str()); - } - } - - if (trace::is_enabled()) - { - auto stream_str = stream.str(); - trace::verbose(_X("Entry %s not serviced or file doesn't exist"), stream_str.c_str()); - } - return false; -} - -void servicing_index_t::ensure_redirections() -{ - if (m_parsed) - { - return; - } - - pal::ifstream_t fstream(m_index_file); - if (!fstream.good()) - { - return; - } - - pal::stringstream_t sstream; - std::string line; - while (std::getline(fstream, line)) - { - pal::string_t str; - pal::to_palstring(line.c_str(), &str); - - // Can interpret line as "package"? - pal::string_t prefix = _X("package|"); - if (str.find(prefix) != 0) - { - continue; - } - - pal::string_t name, version, relative; - pal::string_t* tokens[] = { &name, &version, &relative }; - pal::string_t delim[] = { pal::string_t(_X("|")), pal::string_t(_X("|")), pal::string_t(_X("=")) }; - - bool bad_line = false; - - size_t from = prefix.length(); - for (size_t cur = 0; cur < (sizeof(delim) / sizeof(delim[0])); ++cur) - { - size_t pos = str.find(delim[cur], from); - if (pos == pal::string_t::npos) - { - bad_line = true; - break; - } - - tokens[cur]->assign(str.substr(from, pos - from)); - from = pos + 1; - } - - if (bad_line) - { - trace::error(_X("Invalid line in servicing index. Skipping...")); - continue; - } - - // Save redirection for this package. - sstream.str(_X("")); - sstream << name << _X("|") << version << _X("|") << relative; - - if (trace::is_enabled()) - { - auto stream_str = sstream.str(); - trace::verbose(_X("Adding servicing entry %s => %s"), stream_str.c_str(), str.substr(from).c_str()); - } - - // Store just the filename. - pal::string_t redir = str.substr(from); - if (_X('/') != DIR_SEPARATOR) - { - replace_char(&redir, _X('/'), DIR_SEPARATOR); - } - m_redirections.emplace(sstream.str(), redir); - } - - m_parsed = true; -} diff --git a/src/corehost/cli/servicing_index.h b/src/corehost/cli/servicing_index.h deleted file mode 100644 index 6a188d458..000000000 --- a/src/corehost/cli/servicing_index.h +++ /dev/null @@ -1,24 +0,0 @@ -// 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 "utils.h" -#include "args.h" - -class servicing_index_t -{ -public: - servicing_index_t(const pal::string_t& svc_dir); - - bool find_redirection(const pal::string_t& package_name, - const pal::string_t& package_version, - const pal::string_t& package_relative, - pal::string_t* redirection); - -private: - void ensure_redirections(); - - std::unordered_map m_redirections; - pal::string_t m_patch_root; - pal::string_t m_index_file; - bool m_parsed; -}; diff --git a/src/corehost/cli/setup.cmake b/src/corehost/cli/setup.cmake index d4c1df369..b8113aca9 100644 --- a/src/corehost/cli/setup.cmake +++ b/src/corehost/cli/setup.cmake @@ -8,6 +8,8 @@ if(WIN32) add_definitions(-D_WIN64=1) endif() add_compile_options($<$:-DDEBUG>) + add_compile_options($<$:-DNDEBUG>) + add_compile_options($<$:-DNDEBUG>) add_compile_options($<$:/Od>) add_compile_options(/DEBUG) add_compile_options(/GS) diff --git a/src/corehost/common/pal.h b/src/corehost/common/pal.h index 8161427d3..2e4543297 100644 --- a/src/corehost/common/pal.h +++ b/src/corehost/common/pal.h @@ -163,11 +163,13 @@ namespace pal bool realpath(string_t* path); bool file_exists(const string_t& path); inline bool directory_exists(const string_t& path) { return file_exists(path); } + void readdir(const string_t& path, const string_t& pattern, std::vector* list); void readdir(const string_t& path, std::vector* list); bool get_own_executable_path(string_t* recv); bool getenv(const char_t* name, string_t* recv); bool get_default_packages_directory(string_t* recv); + bool get_default_extensions_directory(string_t* recv); bool is_path_rooted(const string_t& path); int xtoi(const char_t* input); diff --git a/src/corehost/common/pal.unix.cpp b/src/corehost/common/pal.unix.cpp index b7cf9caf0..a894ca843 100644 --- a/src/corehost/common/pal.unix.cpp +++ b/src/corehost/common/pal.unix.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include #if defined(__APPLE__) #include @@ -134,6 +134,14 @@ bool pal::get_default_packages_directory(pal::string_t* recv) return true; } +bool pal::get_default_extensions_directory(string_t* recv) +{ + recv->clear(); + append_path(recv, _X("opt")); + append_path(recv, _X("dotnetextensions")); + return true; +} + #if defined(__APPLE__) bool pal::get_own_executable_path(pal::string_t* recv) { @@ -200,7 +208,7 @@ bool pal::file_exists(const pal::string_t& path) return (::stat(path.c_str(), &buffer) == 0); } -void pal::readdir(const pal::string_t& path, std::vector* list) +void pal::readdir(const string_t& path, const string_t& pattern, std::vector* list) { assert(list != nullptr); @@ -210,8 +218,13 @@ void pal::readdir(const pal::string_t& path, std::vector* list) if (dir != nullptr) { struct dirent* entry = nullptr; - while((entry = readdir(dir)) != nullptr) + while ((entry = readdir(dir)) != nullptr) { + if (fnmatch(pattern.c_str(), entry->d_name, FNM_PATHNAME) != 0) + { + continue; + } + // We are interested in files only switch (entry->d_type) { @@ -250,3 +263,8 @@ void pal::readdir(const pal::string_t& path, std::vector* list) } } } + +void pal::readdir(const pal::string_t& path, std::vector* list) +{ + readdir(path, _X("*"), list); +} diff --git a/src/corehost/common/pal.windows.cpp b/src/corehost/common/pal.windows.cpp index c67c33c9a..217f20894 100644 --- a/src/corehost/common/pal.windows.cpp +++ b/src/corehost/common/pal.windows.cpp @@ -130,8 +130,27 @@ bool pal::get_default_packages_directory(string_t* recv) { return false; } - append_path(&*recv, _X(".nuget")); - append_path(&*recv, _X("packages")); + append_path(recv, _X(".nuget")); + append_path(recv, _X("packages")); + return true; +} + +bool pal::get_default_extensions_directory(string_t* recv) +{ + recv->clear(); + + // See https://github.com/dotnet/cli/issues/2179 +#ifdef _TARGET_X86_ + // In WOW64 mode, PF maps to PFx86. + if (!pal::getenv(_X("ProgramFiles"), recv)) +#elif defined(_TARGET_AMD64_) + if (!pal::getenv(_X("ProgramFiles(x86)"), recv)) +#endif + { + return false; + } + + append_path(recv, _X("dotnetextensions")); return true; } @@ -231,15 +250,14 @@ bool pal::file_exists(const string_t& path) return found; } -void pal::readdir(const string_t& path, std::vector* list) +void pal::readdir(const string_t& path, const string_t& pattern, std::vector* list) { assert(list != nullptr); std::vector& files = *list; string_t search_string(path); - search_string.push_back(DIR_SEPARATOR); - search_string.push_back(L'*'); + append_path(&search_string, pattern.c_str()); WIN32_FIND_DATAW data; auto handle = ::FindFirstFileW(search_string.c_str(), &data); @@ -250,3 +268,8 @@ void pal::readdir(const string_t& path, std::vector* list) } while (::FindNextFileW(handle, &data)); ::FindClose(handle); } + +void pal::readdir(const string_t& path, std::vector* list) +{ + pal::readdir(path, _X("*"), list); +}