Merge pull request #2156 from schellap/shared-paths2

Probe shared locations
This commit is contained in:
Senthil 2016-04-01 00:03:09 -07:00
commit 29b567db3f
24 changed files with 694 additions and 524 deletions

View file

@ -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<pal::string_t>& 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;
}

View file

@ -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<pal::string_t> 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<pal::string_t>& probe_paths, host_mode_t mode, const int argc, const pal::char_t* argv[], arguments_t* args);
#endif // ARGS_H

View file

@ -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.

View file

@ -5,6 +5,7 @@
#define __DEPS_ENTRY_H_
#include <iostream>
#include <array>
#include <vector>
#include "pal.h"
@ -18,14 +19,24 @@ struct deps_entry_t
count
};
static const std::array<const pal::char_t*, deps_entry_t::asset_types::count> 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;

View file

@ -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 <unordered_set>
#include <tuple>
#include <array>
#include <iterator>
#include <cassert>
#include <functional>
const std::array<const pal::char_t*, deps_entry_t::asset_types::count> deps_json_t::s_known_asset_types = {
_X("runtime"), _X("resources"), _X("native") };
const std::array<const pal::char_t*, deps_entry_t::asset_types::count> 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<bool(const pal::string_t&)>& library_exists_fn,
const std::function<const std::vector<pal::string_t>&(const pal::string_t&, int)>& get_rel_paths_by_asset_type_fn)
const std::function<const std::vector<pal::string_t>&(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<pal::string_t> empty;
auto get_relpaths = [&rid_assets, &non_rid_assets, &empty](const pal::string_t& package, int type_index) -> const std::vector<pal::string_t>& {
const std::vector<pal::string_t> empty;
auto get_relpaths = [&](const pal::string_t& package, int type_index, bool* rid_specific) -> const std::vector<pal::string_t>& {
*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<pal::string_t>& {
return assets[package][type_index];
auto get_relpaths = [&](const pal::string_t& package, int type_index, bool* rid_specific) -> const std::vector<pal::string_t>& {
*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.

View file

@ -6,6 +6,7 @@
#include <iostream>
#include <vector>
#include <unordered_set>
#include <functional>
#include "pal.h"
#include "deps_entry.h"
@ -14,13 +15,15 @@
class deps_json_t
{
typedef web::json::value json_value;
typedef std::array<std::vector<pal::string_t>, deps_entry_t::asset_types::count> vectors_t;
typedef std::unordered_map<pal::string_t, vectors_t> str_to_vectors_map_t;
typedef std::unordered_map<pal::string_t, std::vector<pal::string_t>> str_to_vector_map_t;
struct vec_t { std::vector<pal::string_t> vec; };
struct assets_t { std::array<vec_t, deps_entry_t::asset_types::count> by_type; };
struct deps_assets_t { std::unordered_map<pal::string_t, assets_t> libs; };
struct rid_assets_t { std::unordered_map<pal::string_t, assets_t> rid_assets; };
struct rid_specific_assets_t { std::unordered_map<pal::string_t, rid_assets_t> libs; };
typedef std::unordered_map<pal::string_t, std::vector<pal::string_t>> 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<pal::string_t, str_to_vectors_map_t> 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<bool(const pal::string_t&)>& library_exists_fn,
const std::function<const std::vector<pal::string_t>&(const pal::string_t&, int)>& get_rel_paths_by_asset_type_fn);
const std::function<const std::vector<pal::string_t>&(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<const pal::char_t*, deps_entry_t::asset_types::count> s_known_asset_types;
std::vector<deps_entry_t> m_deps_entries[deps_entry_t::asset_types::count];
deps_assets_t m_assets;
rid_specific_assets_t m_rid_assets;
std::unordered_map<pal::string_t, int> 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<deps_entry_t>& get_entries() { return m_deps_entries; }
private:
std::vector<deps_entry_t> m_deps_entries;
bool m_valid;
};
#endif // __DEPS_FORMAT_H_

View file

@ -6,9 +6,12 @@
#include <cassert>
#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<pal::string_t>* 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<pal::string_t>& 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<deps_entry_t> 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<pal::string_t> 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<pal::string_t(const pal::string_t&)> native = [] (const pal::string_t& str) {
return get_directory(str);
};
std::function<pal::string_t(const pal::string_t&)>& 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<pal::string_t(const pal::string_t&)>& action = is_resources ? resources : native;
std::set<pal::string_t> items;
std::vector<deps_entry_t> 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;
}

View file

@ -7,10 +7,10 @@
#include <vector>
#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<deps_json_t>(new deps_json_t(false, m_fx_deps_file));
@ -45,22 +47,25 @@ public:
{
m_deps = std::unique_ptr<deps_json_t>(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<pal::string_t>& 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<pal::string_t, pal::string_t>* 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<pal::string_t, pal::string_t> dir_assemblies_t;
dir_assemblies_t m_local_assemblies;
dir_assemblies_t m_fx_assemblies;
std::unordered_map<pal::string_t, pal::string_t> 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<deps_json_t> m_deps;
// Various probe configurations.
std::vector<probe_config_t> m_probes;
// Is the deps file valid
bool m_deps_valid;
// Fallback probe dir
std::vector<pal::string_t> m_additional_probes;
// Is the deps file portable app?
bool m_portable;
};

View file

@ -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)

View file

@ -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<pal::string_t> 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<pal::string_t>(), 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<pal::string_t>(), _X(""), host_mode_t::muxer, &config);
return execute_app(get_directory(sdk_dotnet), &init, new_argv.size(), new_argv.data());
}
}

View file

@ -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;

View file

@ -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__

View file

@ -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<pal::string_t>(), 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<pal::string_t>(), _X(""), host_mode_t::standalone, nullptr);
return execute_app(
hostpolicy_exists_in_svc(&svc_dir) ? svc_dir : own_dir, &init, argc, argv);
}

View file

@ -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;
}

View file

@ -56,3 +56,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<pal::string_t> 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());
}
}

View file

@ -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<pal::string_t> 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<pal::string_t>& 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<pal::string_t>& 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__

View file

@ -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<pal::string_t>& runtime_config_t::get_probe_paths() const
{
return m_probe_paths;
}
void runtime_config_t::config_kv(std::vector<std::string>* keys, std::vector<std::string>* values) const
{
for (const auto& kv : m_properties)

View file

@ -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<pal::string_t>& 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<pal::string_t, pal::string_t> m_properties;
std::vector<std::string> m_prop_keys;
std::vector<std::string> m_prop_values;
std::vector<pal::string_t> m_probe_paths;
pal::string_t m_fx_name;
pal::string_t m_fx_ver;
bool m_fx_roll_fwd;

View file

@ -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;
}

View file

@ -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<pal::string_t, pal::string_t> m_redirections;
pal::string_t m_patch_root;
pal::string_t m_index_file;
bool m_parsed;
};

View file

@ -8,6 +8,8 @@ if(WIN32)
add_definitions(-D_WIN64=1)
endif()
add_compile_options($<$<CONFIG:Debug>:-DDEBUG>)
add_compile_options($<$<CONFIG:Release>:-DNDEBUG>)
add_compile_options($<$<CONFIG:RelWithDebInfo>:-DNDEBUG>)
add_compile_options($<$<CONFIG:Debug>:/Od>)
add_compile_options(/DEBUG)
add_compile_options(/GS)

View file

@ -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<pal::string_t>* list);
void readdir(const string_t& path, std::vector<pal::string_t>* 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);

View file

@ -13,7 +13,7 @@
#include <pwd.h>
#include <unistd.h>
#include <unistd.h>
#include <fnmatch.h>
#if defined(__APPLE__)
#include <mach-o/dyld.h>
@ -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<pal::string_t>* list)
void pal::readdir(const string_t& path, const string_t& pattern, std::vector<pal::string_t>* list)
{
assert(list != nullptr);
@ -210,8 +218,13 @@ void pal::readdir(const pal::string_t& path, std::vector<pal::string_t>* 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<pal::string_t>* list)
}
}
}
void pal::readdir(const pal::string_t& path, std::vector<pal::string_t>* list)
{
readdir(path, _X("*"), list);
}

View file

@ -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<pal::string_t>* list)
void pal::readdir(const string_t& path, const string_t& pattern, std::vector<pal::string_t>* list)
{
assert(list != nullptr);
std::vector<string_t>& 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<pal::string_t>* list)
} while (::FindNextFileW(handle, &data));
::FindClose(handle);
}
void pal::readdir(const string_t& path, std::vector<pal::string_t>* list)
{
pal::readdir(path, _X("*"), list);
}