From 13c2a98c01c33314f52fb98c69fb112a895fd47b Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Fri, 12 Apr 2024 15:43:33 -0700 Subject: [PATCH] Add hostfxr_resolve_frameworks_for_runtime_config API --- src/native/corehost/fxr/fx_resolver.cpp | 63 +++++++------ src/native/corehost/fxr/fx_resolver.h | 11 ++- src/native/corehost/fxr/hostfxr.cpp | 120 ++++++++++++++++++++++++ src/native/corehost/hostfxr.h | 52 ++++++++++ 4 files changed, 215 insertions(+), 31 deletions(-) diff --git a/src/native/corehost/fxr/fx_resolver.cpp b/src/native/corehost/fxr/fx_resolver.cpp index 4f0f70107b838a..e78bd63eca1096 100644 --- a/src/native/corehost/fxr/fx_resolver.cpp +++ b/src/native/corehost/fxr/fx_resolver.cpp @@ -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 @@ -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; diff --git a/src/native/corehost/fxr/fx_resolver.h b/src/native/corehost/fxr/fx_resolver.h index 4a6d4d9eb03572..466decd846fc90 100644 --- a/src/native/corehost/fxr/fx_resolver.h +++ b/src/native/corehost/fxr/fx_resolver.h @@ -9,7 +9,6 @@ #include "fx_definition.h" class runtime_config_t; -struct host_startup_info_t; class fx_resolver_t { @@ -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, diff --git a/src/native/corehost/fxr/hostfxr.cpp b/src/native/corehost/fxr/hostfxr.cpp index a386cbe3762163..69f223d4171335 100644 --- a/src/native/corehost/fxr/hostfxr.cpp +++ b/src/native/corehost/fxr/hostfxr.cpp @@ -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" @@ -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("") : runtime_config_path); + if (parameters == nullptr) + { + trace::info(_X(" parameters=")); + } + else + { + trace::info( + _X(" parameters={") + _X(" host_path=%s\n") + _X(" dotnet_root=%s\n") + _X(" }"), + parameters->host_path == nullptr ? _X("") : parameters->host_path, + parameters->dotnet_root == nullptr ? _X("") : 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(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 resolved; + std::vector 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[], diff --git a/src/native/corehost/hostfxr.h b/src/native/corehost/hostfxr.h index a19636b9e3fd08..4c794b17d4dfd4 100644 --- a/src/native/corehost/hostfxr.h +++ b/src/native/corehost/hostfxr.h @@ -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__