Skip to content

Commit

Permalink
Add hostfxr_resolve_frameworks_for_runtime_config API
Browse files Browse the repository at this point in the history
  • Loading branch information
elinor-fung committed Apr 19, 2024
1 parent 9907366 commit 13c2a98
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 31 deletions.
63 changes: 35 additions & 28 deletions src/native/corehost/fxr/fx_resolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -493,19 +493,16 @@ StatusCode fx_resolver_t::read_framework(
return StatusCode::Success;
}

StatusCode fx_resolver_t::resolve_frameworks_for_app(
StatusCode fx_resolver_t::resolve_frameworks(
const pal::string_t& dotnet_root,
const runtime_config_t::settings_t& override_settings,
const runtime_config_t& app_config,
fx_definition_vector_t& fx_definitions,
const pal::char_t* app_display_name,
bool print_errors)
resolution_failure_info& resolution_failure)
{
bool disable_multilevel_lookup = app_config.get_is_multilevel_lookup_disabled();
fx_resolver_t resolver{ disable_multilevel_lookup, override_settings };
fx_resolver_t resolver{ app_config.get_is_multilevel_lookup_disabled(), override_settings };

// Read the shared frameworks; retry is necessary when a framework is already resolved, but then a newer compatible version is processed.
resolution_failure_info resolution_failure;
StatusCode rc = StatusCode::Success;
int retry_count = 0;
do
Expand All @@ -520,29 +517,39 @@ StatusCode fx_resolver_t::resolve_frameworks_for_app(
{
display_summary_of_frameworks(fx_definitions, resolver.m_effective_fx_references);
}
else if (print_errors)

return rc;
}

StatusCode fx_resolver_t::resolve_frameworks_for_app(
const pal::string_t& dotnet_root,
const runtime_config_t::settings_t& override_settings,
const runtime_config_t& app_config,
fx_definition_vector_t& fx_definitions,
const pal::char_t* app_display_name)
{
resolution_failure_info resolution_failure;
StatusCode rc = resolve_frameworks(dotnet_root, override_settings, app_config, fx_definitions, resolution_failure);
switch (rc)
{
switch (rc)
{
case StatusCode::FrameworkMissingFailure:
trace::error(
INSTALL_OR_UPDATE_NET_ERROR_MESSAGE
_X("\n\n")
_X("App: %s\n")
_X("Architecture: %s"),
app_display_name,
get_current_arch_name());
display_missing_framework_error(resolution_failure.missing.get_fx_name(), resolution_failure.missing.get_fx_version(), pal::string_t(), dotnet_root, disable_multilevel_lookup);
break;
case StatusCode::FrameworkCompatFailure:
display_incompatible_framework_error(resolution_failure.incompatible_higher.get_fx_version(), resolution_failure.incompatible_lower);
break;
case StatusCode::InvalidConfigFile:
trace::error(_X("Invalid framework config.json [%s]"), resolution_failure.invalid_config->get_runtime_config().get_path().c_str());
break;
default:
break;
}
case StatusCode::FrameworkMissingFailure:
trace::error(
INSTALL_OR_UPDATE_NET_ERROR_MESSAGE
_X("\n\n")
_X("App: %s\n")
_X("Architecture: %s"),
app_display_name,
get_current_arch_name());
display_missing_framework_error(resolution_failure.missing.get_fx_name(), resolution_failure.missing.get_fx_version(), pal::string_t(), dotnet_root, app_config.get_is_multilevel_lookup_disabled());
break;
case StatusCode::FrameworkCompatFailure:
display_incompatible_framework_error(resolution_failure.incompatible_higher.get_fx_version(), resolution_failure.incompatible_lower);
break;
case StatusCode::InvalidConfigFile:
trace::error(_X("Invalid framework config.json [%s]"), resolution_failure.invalid_config->get_runtime_config().get_path().c_str());
break;
default:
break;
}

return rc;
Expand Down
11 changes: 8 additions & 3 deletions src/native/corehost/fxr/fx_resolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include "fx_definition.h"

class runtime_config_t;
struct host_startup_info_t;

class fx_resolver_t
{
Expand All @@ -23,13 +22,19 @@ class fx_resolver_t
};

public:
static StatusCode resolve_frameworks(
const pal::string_t& dotnet_root,
const runtime_config_t::settings_t& override_settings,
const runtime_config_t& app_config,
/*in_out*/ fx_definition_vector_t& fx_definitions,
resolution_failure_info& resolution_failure);

static StatusCode resolve_frameworks_for_app(
const pal::string_t& dotnet_root,
const runtime_config_t::settings_t& override_settings,
const runtime_config_t& app_config,
/*in_out*/ fx_definition_vector_t& fx_definitions,
const pal::char_t* app_display_name,
bool print_errors = true);
const pal::char_t* app_display_name);

static bool is_config_compatible_with_frameworks(
const runtime_config_t& config,
Expand Down
120 changes: 120 additions & 0 deletions src/native/corehost/fxr/hostfxr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "utils.h"
#include "fx_ver.h"
#include "fx_muxer.h"
#include "fx_resolver.h"
#include "error_codes.h"
#include "runtime_config.h"
#include "sdk_info.h"
Expand Down Expand Up @@ -571,6 +572,125 @@ namespace
}
}

SHARED_API int32_t HOSTFXR_CALLTYPE hostfxr_resolve_frameworks_for_runtime_config(
const char_t* runtime_config_path,
/*opt*/ const hostfxr_initialize_parameters* parameters,
/*opt*/ hostfxr_resolve_frameworks_result_fn callback,
/*opt*/ void* result_context)
{
trace_hostfxr_entry_point(_X("hostfxr_resolve_frameworks_for_runtime_config"));
if (trace::is_enabled())
{
trace::info(_X(" runtime_config_path=%s"), runtime_config_path == nullptr ? _X("<nullptr>") : runtime_config_path);
if (parameters == nullptr)
{
trace::info(_X(" parameters=<nullptr>"));
}
else
{
trace::info(
_X(" parameters={")
_X(" host_path=%s\n")
_X(" dotnet_root=%s\n")
_X(" }"),
parameters->host_path == nullptr ? _X("<nullptr>") : parameters->host_path,
parameters->dotnet_root == nullptr ? _X("<nullptr>") : parameters->dotnet_root);
}
}

if (runtime_config_path == nullptr)
{
trace::error(_X("hostfxr_resolve_frameworks_for_runtime_config received an invalid argument: runtime_config_path should not be null."));
return StatusCode::InvalidArgFailure;
}

pal::string_t runtime_config = runtime_config_path;
if (runtime_config.empty() || !pal::realpath(&runtime_config))
{
trace::error(_X("The specified runtimeconfig.json [%s] does not exist"), runtime_config.c_str());
return StatusCode::InvalidConfigFile;
}

host_startup_info_t host_info{};
int rc = populate_startup_info(parameters, host_info);
if (rc != StatusCode::Success)
return rc;

fx_definition_vector_t fx_definitions;
auto app = new fx_definition_t();
fx_definitions.push_back(std::unique_ptr<fx_definition_t>(app));

const runtime_config_t::settings_t override_settings;
app->parse_runtime_config(runtime_config, _X(""), override_settings);

const runtime_config_t app_config = app->get_runtime_config();

// Resolve frameworks for framework-dependent apps.
// Self-contained apps assume the framework is next to the app, so we just treat it as success.
fx_resolver_t::resolution_failure_info failure_info;
rc = app_config.get_is_framework_dependent()
? fx_resolver_t::resolve_frameworks(host_info.dotnet_root, override_settings, app_config, fx_definitions, failure_info)
: StatusCode::Success;

if (callback)
{
std::vector<hostfxr_framework_result> resolved;
std::vector<hostfxr_framework_result> unresolved;

pal::string_t config_dir;
if (app_config.get_is_framework_dependent())
{
for (const auto& fx : fx_definitions)
{
// Skip the app itself
if (fx.get() == app)
continue;

resolved.push_back({ sizeof(hostfxr_framework_result), fx->get_name().c_str(), fx->get_requested_version().c_str(), fx->get_found_version().c_str(), fx->get_dir().c_str() });
}

switch (rc)
{
case StatusCode::FrameworkMissingFailure:
unresolved.push_back({ sizeof(hostfxr_framework_result), failure_info.missing.get_fx_name().c_str(), failure_info.missing.get_fx_version().c_str(), nullptr, nullptr });
break;
case StatusCode::FrameworkCompatFailure:
unresolved.push_back({ sizeof(hostfxr_framework_result), failure_info.incompatible_lower.get_fx_name().c_str(), failure_info.incompatible_lower.get_fx_version().c_str(), nullptr, nullptr });
unresolved.push_back({ sizeof(hostfxr_framework_result), failure_info.incompatible_higher.get_fx_name().c_str(), failure_info.incompatible_higher.get_fx_version().c_str(), nullptr, nullptr });
break;
case StatusCode::InvalidConfigFile:
assert(failure_info.invalid_config != nullptr);
unresolved.push_back({ sizeof(hostfxr_framework_result), failure_info.invalid_config->get_name().c_str(), failure_info.invalid_config->get_requested_version().c_str(), failure_info.invalid_config->get_found_version().c_str(), failure_info.invalid_config->get_dir().c_str() });
default:
break;
}
}
else
{
// For self-contained apps, add all the included frameworks as resolved frameworks assumed to be next to the config
config_dir = get_directory(runtime_config);
remove_trailing_dir_separator(&config_dir);
for (const fx_reference_t& fx : app_config.get_included_frameworks())
{
resolved.push_back({ sizeof(hostfxr_framework_result), fx.get_fx_name().c_str(), fx.get_fx_version().c_str(), fx.get_fx_version().c_str(), config_dir.c_str() });
}
}

const hostfxr_resolve_frameworks_result result
{
sizeof(hostfxr_resolve_frameworks_result),
resolved.size(),
resolved.empty() ? nullptr : resolved.data(),
unresolved.size(),
unresolved.empty() ? nullptr : unresolved.data()
};

callback(&result, result_context);
}

return rc;
}

SHARED_API int32_t HOSTFXR_CALLTYPE hostfxr_initialize_for_dotnet_command_line(
int argc,
const pal::char_t *argv[],
Expand Down
52 changes: 52 additions & 0 deletions src/native/corehost/hostfxr.h
Original file line number Diff line number Diff line change
Expand Up @@ -363,4 +363,56 @@ typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_get_dotnet_environment_info_fn)(
hostfxr_get_dotnet_environment_info_result_fn result,
void* result_context);

struct hostfxr_framework_result
{
size_t size;
const char_t* name;
const char_t* requested_version;
const char_t* resolved_version;
const char_t* resolved_path;
};

struct hostfxr_resolve_frameworks_result
{
size_t size;

size_t resolved_count;
const struct hostfxr_framework_result* resolved_frameworks;

size_t unresolved_count;
const struct hostfxr_framework_result* unresolved_frameworks;
};

typedef void (*hostfxr_resolve_frameworks_result_fn)(
const hostfxr_resolve_frameworks_result* result,
void* result_context);

//
// Resolves frameworks for a runtime config
//
// Parameters:
// runtime_config_path
// Path to the .runtimeconfig.json file
// parameters
// Optional. Additional parameters for initialization.
// If null or dotnet_root is null, the root corresponding to the running hostfx is used.
// callback
// Optional. Result callback invoked with result of the resolution.
// Structs and their elements are valid for the duration of the call.
// result_context
// Optional. Additional context passed to the result callback.
//
// Return value:
// 0 on success, otherwise failure.
//
// String encoding:
// Windows - UTF-16 (pal::char_t is 2-byte wchar_t)
// Unix - UTF-8 (pal::char_t is 1-byte char)
//
typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_resolve_frameworks_for_runtime_config_fn)(
const char_t* runtime_config_path,
/*opt*/ const hostfxr_initialize_parameters* parameters,
/*opt*/ hostfxr_resolve_frameworks_result_fn callback,
/*opt*/ void* result_context);

#endif //__HOSTFXR_H__

0 comments on commit 13c2a98

Please sign in to comment.