Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make host look for RID-specific assets via a known list by default #84100

Merged
merged 13 commits into from
May 18, 2023
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,15 @@ public static AndConstraint<CommandResultAssertions> HaveUsedAdditionalProbingPa
.And.HaveStdErrContaining($"probe type=lookup dir=[{path}]");
}

public static AndConstraint<CommandResultAssertions> HaveReadRidGraph(this CommandResultAssertions assertion, bool readRidGraph)
{
string ridGraphMsg = "RID fallback graph =";
string hostRidsMsg = "Host RID list =";
return readRidGraph
? assertion.HaveStdErrContaining(ridGraphMsg).And.NotHaveStdErrContaining(hostRidsMsg)
: assertion.HaveStdErrContaining(hostRidsMsg).And.NotHaveStdErrContaining(ridGraphMsg);
}

public static AndConstraint<CommandResultAssertions> HaveUsedFallbackRid(this CommandResultAssertions assertion, bool usedFallbackRid)
{
string msg = "Falling back to base HostRID";
Expand Down

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/installer/tests/TestUtils/RuntimeConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ public static RuntimeConfig FromFile(string path)
{
foreach (var includedFramework in includedFrameworks)
{
runtimeConfig.WithFramework(Framework.FromJson((JsonObject)includedFramework));
runtimeConfig.WithIncludedFramework(Framework.FromJson((JsonObject)includedFramework));
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/native/corehost/fxr/files.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ list(APPEND SOURCES
${CMAKE_CURRENT_LIST_DIR}/install_info.cpp
${CMAKE_CURRENT_LIST_DIR}/sdk_info.cpp
${CMAKE_CURRENT_LIST_DIR}/sdk_resolver.cpp
${CMAKE_CURRENT_LIST_DIR}/../tfm_compat.cpp
)

list(APPEND HEADERS
Expand All @@ -35,5 +36,6 @@ list(APPEND HEADERS
${CMAKE_CURRENT_LIST_DIR}/install_info.h
${CMAKE_CURRENT_LIST_DIR}/sdk_info.h
${CMAKE_CURRENT_LIST_DIR}/sdk_resolver.h
${CMAKE_CURRENT_LIST_DIR}/../tfm_compat.h
)

194 changes: 148 additions & 46 deletions src/native/corehost/hostpolicy/deps_format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ namespace

if (trace::is_enabled())
{
trace::verbose(_X("The rid fallback graph is: {"));
trace::verbose(_X("RID fallback graph = {"));
for (const auto& rid : rid_fallback_graph)
{
trace::verbose(_X("%s => ["), rid.first.c_str());
Expand Down Expand Up @@ -180,31 +180,148 @@ void deps_json_t::reconcile_libraries_with_targets(
}
}

// Returns the RID determined (computed or fallback) for the platform the host is running on.
pal::string_t deps_json_t::get_current_rid(const rid_fallback_graph_t& rid_fallback_graph)
namespace
{
pal::string_t currentRid = get_current_runtime_id(false /*use_fallback*/);
const pal::char_t* s_host_rids[] =
{
#if defined(TARGET_WINDOWS)
_X("win"),
#elif defined(TARGET_OSX)
_X("osx"),
_X("unix"),
#elif defined(TARGET_LINUX_MUSL)
Copy link
Member

@tmds tmds Apr 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a non-portable build on a musl-based Linux, we also need to add the target rid for probing.

_X("linux-musl"),
_X("linux"),
_X("unix"),
#elif defined(TARGET_ANDROID)
_X("linux-bionic"),
_X("linux"),
_X("unix"),
#else
_X("linux"),
_X("unix"),
#endif
};

// Returns the RID determined (computed or fallback) for the platform the host is running on.
pal::string_t get_current_rid(const deps_json_t::rid_fallback_graph_t* rid_fallback_graph)
{
pal::string_t currentRid = get_current_runtime_id(false /*use_fallback*/);

trace::info(_X("HostRID is %s"), currentRid.empty() ? _X("not available") : currentRid.c_str());

// If the current RID is not present in the RID fallback graph, then the platform
// is unknown to us. At this point, we will fallback to using the base RIDs and attempt
// asset lookup using them.
//
// We do the same even when the RID is empty.
if (currentRid.empty() || (rid_fallback_graph != nullptr && rid_fallback_graph->count(currentRid) == 0))
{
currentRid = pal::get_current_os_fallback_rid() + pal::string_t(_X("-")) + get_current_arch_name();

trace::info(_X("HostRID is %s"), currentRid.empty()? _X("not available"): currentRid.c_str());
trace::info(_X("Falling back to base HostRID: %s"), currentRid.c_str());
}

return currentRid;
}

// If the current RID is not present in the RID fallback graph, then the platform
// is unknown to us. At this point, we will fallback to using the base RIDs and attempt
// asset lookup using them.
//
// We do the same even when the RID is empty.
if (currentRid.empty() || (rid_fallback_graph.count(currentRid) == 0))
std::vector<pal::string_t> get_host_rid_list(const pal::string_t& host_rid)
{
currentRid = pal::get_current_os_fallback_rid() + pal::string_t(_X("-")) + get_current_arch_name();
std::vector<pal::string_t> rids;
rids.reserve((sizeof(s_host_rids) / sizeof(*s_host_rids)) * 2 + 3);

pal::string_t arch_suffix = _X("-");
arch_suffix.append(get_current_arch_name());

trace::info(_X("Falling back to base HostRID: %s"), currentRid.c_str());
rids.push_back(host_rid);
if (ends_with(host_rid, arch_suffix, true))
{
// Host RID without architecture
rids.push_back(host_rid.substr(0, host_rid.size() - arch_suffix.size()));
}

// Use our list of known portable RIDs
for (const pal::char_t* rid : s_host_rids)
{
// Architecture-specific RID
rids.push_back(rid + arch_suffix);
elinor-fung marked this conversation as resolved.
Show resolved Hide resolved

// RID without architecture
rids.push_back(rid);
}

rids.push_back(_X("any"));

if (trace::is_enabled())
{
trace::verbose(_X("Host RID list = ["));
for (const pal::string_t& rid : rids)
{
trace::verbose(_X(" %s,"), rid.c_str());
}
trace::verbose(_X("]"));
}

return rids;
}

return currentRid;
bool try_get_matching_rid(const std::unordered_map<pal::string_t, std::vector<deps_asset_t>>& rid_assets, const std::vector<pal::string_t>& rid_list, pal::string_t& out_rid)
{
for (const pal::string_t& rid : rid_list)
{
if (rid_assets.count(rid) != 0)
{
out_rid = rid;
return true;
}
}

return false;
}

bool try_get_matching_rid_with_fallback_graph(const std::unordered_map<pal::string_t, std::vector<deps_asset_t>>& rid_assets, const pal::string_t& host_rid, const deps_json_t::rid_fallback_graph_t& rid_fallback_graph, pal::string_t& out_rid)
{
// Check for exact match with the host RID
if (rid_assets.count(host_rid) != 0)
{
out_rid = host_rid;
return true;
}

// Check if the RID exists in the fallback graph
auto rid_fallback_iter = rid_fallback_graph.find(host_rid);
if (rid_fallback_iter == rid_fallback_graph.end())
{
trace::warning(_X("The targeted framework does not support the runtime '%s'. Some libraries may fail to load on this platform."), host_rid.c_str());
return false;
}

// Find the first RID fallback that has assets
const auto& fallback_rids = rid_fallback_iter->second;
auto iter = std::find_if(fallback_rids.begin(), fallback_rids.end(), [&rid_assets](const pal::string_t& rid) {
return rid_assets.count(rid);
});
if (iter != fallback_rids.end())
{
out_rid.assign(*iter);
return true;
}

return false;
}
}

void deps_json_t::perform_rid_fallback(rid_specific_assets_t* portable_assets, const rid_fallback_graph_t& rid_fallback_graph)
void deps_json_t::perform_rid_fallback(rid_specific_assets_t* portable_assets)
{
pal::string_t host_rid = get_current_rid(rid_fallback_graph);
const rid_fallback_graph_t* rid_fallback_graph = nullptr;
if (m_rid_resolution_options.use_fallback_graph)
rid_fallback_graph = m_rid_resolution_options.rid_fallback_graph == nullptr ? &m_rid_fallback_graph : m_rid_resolution_options.rid_fallback_graph;

const pal::string_t host_rid = get_current_rid(rid_fallback_graph);

std::vector<pal::string_t> host_rid_list;
if (!m_rid_resolution_options.use_fallback_graph)
host_rid_list = get_host_rid_list(host_rid);

for (auto& package : portable_assets->libs)
{
Expand All @@ -215,28 +332,11 @@ void deps_json_t::perform_rid_fallback(rid_specific_assets_t* portable_assets, c
if (rid_assets.empty())
continue;

pal::string_t matched_rid = rid_assets.count(host_rid) ? host_rid : _X("");
if (matched_rid.empty())
{
auto rid_fallback_iter = rid_fallback_graph.find(host_rid);
if (rid_fallback_iter == rid_fallback_graph.end())
{
trace::warning(_X("The targeted framework does not support the runtime '%s'. Some native libraries from [%s] may fail to load on this platform."), host_rid.c_str(), package.first.c_str());
}
else
{
const auto& fallback_rids = rid_fallback_iter->second;
auto iter = std::find_if(fallback_rids.begin(), fallback_rids.end(), [&rid_assets](const pal::string_t& rid) {
return rid_assets.count(rid);
});
if (iter != fallback_rids.end())
{
matched_rid = *iter;
}
}
}

if (matched_rid.empty())
pal::string_t matched_rid;
bool found_match = m_rid_resolution_options.use_fallback_graph
? try_get_matching_rid_with_fallback_graph(rid_assets, host_rid, *rid_fallback_graph, matched_rid)
: try_get_matching_rid(rid_assets, host_rid_list, matched_rid);
if (!found_match)
{
trace::verbose(_X(" No matching %s assets for package %s"), deps_entry_t::s_known_asset_types[asset_type_index], package.first.c_str());
rid_assets.clear();
Expand All @@ -260,7 +360,7 @@ void deps_json_t::perform_rid_fallback(rid_specific_assets_t* portable_assets, c
}
}

void deps_json_t::process_runtime_targets(const json_parser_t::value_t& json, const pal::string_t& target_name, const rid_fallback_graph_t& rid_fallback_graph, rid_specific_assets_t* p_assets)
void deps_json_t::process_runtime_targets(const json_parser_t::value_t& json, const pal::string_t& target_name, rid_specific_assets_t* p_assets)
{
rid_specific_assets_t& assets = *p_assets;
for (const auto& package : json[_X("targets")][target_name.c_str()].GetObject())
Expand Down Expand Up @@ -318,7 +418,7 @@ void deps_json_t::process_runtime_targets(const json_parser_t::value_t& json, co
}
}

perform_rid_fallback(&assets, rid_fallback_graph);
perform_rid_fallback(&assets);
}

void deps_json_t::process_targets(const json_parser_t::value_t& json, const pal::string_t& target_name, deps_assets_t* p_assets)
Expand Down Expand Up @@ -374,9 +474,9 @@ void deps_json_t::process_targets(const json_parser_t::value_t& json, const pal:
}
}

void deps_json_t::load_framework_dependent(const pal::string_t& deps_path, const json_parser_t::value_t& json, const pal::string_t& target_name, const rid_fallback_graph_t& rid_fallback_graph)
void deps_json_t::load_framework_dependent(const pal::string_t& deps_path, const json_parser_t::value_t& json, const pal::string_t& target_name)
{
process_runtime_targets(json, target_name, rid_fallback_graph, &m_rid_assets);
process_runtime_targets(json, target_name, &m_rid_assets);
process_targets(json, target_name, &m_assets);

auto package_exists = [&](const pal::string_t& package) -> bool {
Expand Down Expand Up @@ -426,7 +526,9 @@ void deps_json_t::load_self_contained(const pal::string_t& deps_path, const json
};

reconcile_libraries_with_targets(deps_path, json, package_exists, get_relpaths);
populate_rid_fallback_graph(json, m_rid_fallback_graph);

if (m_rid_resolution_options.use_fallback_graph)
populate_rid_fallback_graph(json, m_rid_fallback_graph);
}

bool deps_json_t::has_package(const pal::string_t& name, const pal::string_t& ver) const
Expand Down Expand Up @@ -456,7 +558,7 @@ bool deps_json_t::has_package(const pal::string_t& name, const pal::string_t& ve
// Load the deps file and parse its "entry" lines which contain the "fields" of
// the entry. Populate an array of these entries.
//
void deps_json_t::load(bool is_framework_dependent, const pal::string_t& deps_path, const rid_fallback_graph_t& rid_fallback_graph)
void deps_json_t::load(bool is_framework_dependent, const pal::string_t& deps_path)
{
m_deps_file = deps_path;
m_file_exists = deps_file_exists(m_deps_file);
Expand All @@ -478,11 +580,11 @@ void deps_json_t::load(bool is_framework_dependent, const pal::string_t& deps_pa
runtime_target.GetString() :
runtime_target[_X("name")].GetString();

trace::verbose(_X("Loading deps file... %s as framework dependent=[%d]"), deps_path.c_str(), is_framework_dependent);
trace::verbose(_X("Loading deps file... [%s] as framework dependent=%d, use_fallback_graph=%d"), deps_path.c_str(), is_framework_dependent, m_rid_resolution_options.use_fallback_graph);

if (is_framework_dependent)
{
load_framework_dependent(deps_path, json.document(), name, rid_fallback_graph);
load_framework_dependent(deps_path, json.document(), name);
}
else
{
Expand Down
24 changes: 16 additions & 8 deletions src/native/corehost/hostpolicy/deps_format.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,18 @@ class deps_json_t
public:
typedef str_to_vector_map_t rid_fallback_graph_t;

deps_json_t(bool is_framework_dependent, const pal::string_t& deps_path, const rid_fallback_graph_t* graph)
struct rid_resolution_options_t
{
bool use_fallback_graph;
const deps_json_t::rid_fallback_graph_t* rid_fallback_graph;
elinor-fung marked this conversation as resolved.
Show resolved Hide resolved
};

deps_json_t(bool is_framework_dependent, const pal::string_t& deps_path, const rid_resolution_options_t& rid_resolution_options)
: m_file_exists(false)
, m_valid(false)
, m_rid_resolution_options(rid_resolution_options)
{
load(is_framework_dependent, deps_path, graph == nullptr ? m_rid_fallback_graph : *graph);
load(is_framework_dependent, deps_path);
}

const std::vector<deps_entry_t>& get_entries(deps_entry_t::asset_types type) const
Expand Down Expand Up @@ -68,9 +75,9 @@ class deps_json_t

private:
void load_self_contained(const pal::string_t& deps_path, const json_parser_t::value_t& json, const pal::string_t& target_name);
void load_framework_dependent(const pal::string_t& deps_path, const json_parser_t::value_t& json, const pal::string_t& target_name, const rid_fallback_graph_t& rid_fallback_graph);
void load(bool is_framework_dependent, const pal::string_t& deps_path, const rid_fallback_graph_t& rid_fallback_graph);
void process_runtime_targets(const json_parser_t::value_t& json, const pal::string_t& target_name, const rid_fallback_graph_t& rid_fallback_graph, rid_specific_assets_t* p_assets);
void load_framework_dependent(const pal::string_t& deps_path, const json_parser_t::value_t& json, const pal::string_t& target_name);
void load(bool is_framework_dependent, const pal::string_t& deps_path);
void process_runtime_targets(const json_parser_t::value_t& json, const pal::string_t& target_name, rid_specific_assets_t* p_assets);
void process_targets(const json_parser_t::value_t& json, const pal::string_t& target_name, deps_assets_t* p_assets);

void reconcile_libraries_with_targets(
Expand All @@ -79,18 +86,19 @@ class deps_json_t
const std::function<bool(const pal::string_t&)>& library_exists_fn,
const std::function<const vec_asset_t&(const pal::string_t&, size_t, bool*)>& get_assets_fn);

pal::string_t get_current_rid(const rid_fallback_graph_t& rid_fallback_graph);
void perform_rid_fallback(rid_specific_assets_t* portable_assets, const rid_fallback_graph_t& rid_fallback_graph);
void perform_rid_fallback(rid_specific_assets_t* portable_assets);

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;

rid_fallback_graph_t m_rid_fallback_graph;
bool m_file_exists;
bool m_valid;

const rid_resolution_options_t& m_rid_resolution_options;
rid_fallback_graph_t m_rid_fallback_graph;

pal::string_t m_deps_file;
};

Expand Down
6 changes: 3 additions & 3 deletions src/native/corehost/hostpolicy/deps_resolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,7 @@ void deps_resolver_t::init_known_entry_path(const deps_entry_t& entry, const pal
}
}

void deps_resolver_t::resolve_additional_deps(const pal::char_t* additional_deps_serialized, const deps_json_t::rid_fallback_graph_t* rid_fallback_graph)
void deps_resolver_t::resolve_additional_deps(const pal::char_t* additional_deps_serialized, const deps_json_t::rid_resolution_options_t& rid_resolution_options)
{
if (!m_is_framework_dependent
|| m_host_mode == host_mode_t::libhost)
Expand Down Expand Up @@ -647,7 +647,7 @@ void deps_resolver_t::resolve_additional_deps(const pal::char_t* additional_deps
additional_deps_path.c_str());

m_additional_deps.push_back(std::unique_ptr<deps_json_t>(
new deps_json_t(true, additional_deps_path, rid_fallback_graph)));
new deps_json_t(true, additional_deps_path, rid_resolution_options)));
}
else
{
Expand Down Expand Up @@ -708,7 +708,7 @@ void deps_resolver_t::resolve_additional_deps(const pal::char_t* additional_deps
json_full_path.c_str());

m_additional_deps.push_back(std::unique_ptr<deps_json_t>(
new deps_json_t(true, json_full_path, rid_fallback_graph)));
new deps_json_t(true, json_full_path, rid_resolution_options)));
}
}
}
Expand Down
Loading