// ----------------------------------------------------------------------------- // Resolve the directories order for resources/native lookup // // Description: // This general purpose function specifies priority order of directory lookup // for both native images and resources specific resource images. Lookup for // resources assemblies is done by looking up two levels above from the file // path. Lookup for native images is done by looking up one level from the // file path. // // Parameters: // asset_type - The type of the asset that needs lookup, currently // supports "resources" and "native" // 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 lookup dirs // void deps_resolver_t::resolve_probe_dirs( deps_entry_t::asset_types asset_type, const pal::string_t& clr_dir, pal::string_t* output) { 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 std::function<pal::string_t(const pal::string_t&)> resources = [] (const pal::string_t& str) { return get_directory(get_directory(str)); }; // For native assemblies, obtain the directory path from the file path 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 = is_resources ? resources : native; std::unordered_set<pal::string_t> items; std::vector<deps_entry_t> empty(0); 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; bool track_api_sets = true; auto add_package_cache_entry = [&](const deps_entry_t& entry) { if (probe_entry_in_configs(entry, &candidate)) { // For standalone apps, on win7, coreclr needs ApiSets which has to be in the DLL search path. const pal::string_t result_dir = action(candidate); if (track_api_sets && pal::need_api_sets() && ends_with(entry.library_name, _X("Microsoft.NETCore.Windows.ApiSets"), false)) { // For standalone and portable apps, get the ApiSets DLL directory, // as they could come from servicing or other probe paths. // Note: in portable apps, the API set would come from FX deps // which is actually a standalone deps (rid specific API set). // If the portable app relied on its version of API sets, then // the rid selection fallback would have already been performed // by the host (deps_format.cpp) m_api_set_paths.insert(result_dir); } add_unique_path(asset_type, result_dir, &items, output); } }; std::for_each(entries.begin(), entries.end(), add_package_cache_entry); track_api_sets = m_api_set_paths.empty(); std::for_each(fx_entries.begin(), fx_entries.end(), add_package_cache_entry); track_api_sets = m_api_set_paths.empty(); // 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.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); } // App called out an explicit API set dependency. if (track_api_sets && entry.is_rid_specific && pal::need_api_sets() && ends_with(entry.library_name, _X("Microsoft.NETCore.Windows.ApiSets"), false)) { m_api_set_paths.insert(action(candidate)); } }); } track_api_sets = m_api_set_paths.empty(); // App local path add_unique_path(asset_type, m_app_dir, &items, output); // If API sets is not found (i.e., empty) in the probe paths above: // 1. For standalone app, do nothing as all are sxs. // 2. For portable app, add FX dir. // FX path if present if (!m_fx_dir.empty()) { // For portable apps, if we didn't find api sets in probe paths // add the FX directory. if (track_api_sets && pal::need_api_sets()) { m_api_set_paths.insert(m_fx_dir); } add_unique_path(asset_type, m_fx_dir, &items, output); } // CLR path add_unique_path(asset_type, clr_dir, &items, output); }
/** * Resolve native and culture assembly directories based on "asset_type" parameter. */ bool deps_resolver_t::resolve_probe_dirs( deps_entry_t::asset_types asset_type, pal::string_t* output, std::unordered_set<pal::string_t>* breadcrumb) { 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 std::function<pal::string_t(const pal::string_t&)> resources = [] (const pal::string_t& str) { return get_directory(get_directory(str)); }; // For native assemblies, obtain the directory path from the file path std::function<pal::string_t(const pal::string_t&)> native = [] (const pal::string_t& str) { return get_directory(str); }; // Action for post processing the resolved path std::function<pal::string_t(const pal::string_t&)>& action = is_resources ? resources : native; // Set for de-duplication std::unordered_set<pal::string_t> items; pal::string_t core_servicing = m_core_servicing; pal::realpath(&core_servicing); // Filter out non-serviced assets so the paths can be added after servicing paths. pal::string_t non_serviced; std::vector<deps_entry_t> empty(0); 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; auto add_package_cache_entry = [&](const deps_entry_t& entry, const pal::string_t& deps_dir) -> bool { if (entry.is_serviceable) { breadcrumb->insert(entry.library_name + _X(",") + entry.library_version); breadcrumb->insert(entry.library_name); } if (items.count(entry.asset_name)) { return true; } // Ignore placeholders if (ends_with(entry.relative_path, _X("/_._"), false)) { return true; } trace::verbose(_X("Processing native/culture for deps entry [%s, %s, %s]"), entry.library_name.c_str(), entry.library_version.c_str(), entry.relative_path.c_str()); if (probe_deps_entry(entry, deps_dir, &candidate)) { init_known_entry_path(entry, candidate); add_unique_path(asset_type, action(candidate), &items, output, &non_serviced, core_servicing); } else { // For standalone apps, apphost.exe will be renamed. Do not use the full package name // because of rid-fallback could happen (ex: CentOS falling back to RHEL) if ((ends_with(entry.library_name, _X(".Microsoft.NETCore.DotNetHost"), false) && entry.asset_name == _X("dotnet")) || (ends_with(entry.library_name, _X(".Microsoft.NETCore.DotNetAppHost"), false) && entry.asset_name == _X("apphost"))) { trace::warning(_X("Warning: assembly specified in the dependencies manifest was not found -- package: '%s', version: '%s', path: '%s'"), entry.library_name.c_str(), entry.library_version.c_str(), entry.relative_path.c_str()); return true; } trace::error(_X("Error: assembly specified in the dependencies manifest was not found -- package: '%s', version: '%s', path: '%s'"), entry.library_name.c_str(), entry.library_version.c_str(), entry.relative_path.c_str()); return false; } if (m_api_set_paths.empty() && pal::need_api_sets() && ends_with(entry.library_name, _X("Microsoft.NETCore.Windows.ApiSets"), false)) { m_api_set_paths.insert(action(candidate)); } return true; }; for (const auto& entry : entries) { if (!add_package_cache_entry(entry, m_app_dir)) { return false; } } // If the deps file is missing add known locations. if (!m_deps->exists()) { // App local path add_unique_path(asset_type, m_app_dir, &items, output, &non_serviced, core_servicing); (void) library_exists_in_dir(m_app_dir, LIBCORECLR_NAME, &m_coreclr_path); (void) library_exists_in_dir(m_app_dir, LIBCLRJIT_NAME, &m_clrjit_path); } for (const auto& entry : fx_entries) { if (!add_package_cache_entry(entry, m_fx_dir)) { return false; } } output->append(non_serviced); return true; }
/** * Resolve native and culture assembly directories based on "asset_type" parameter. */ bool deps_resolver_t::resolve_probe_dirs( deps_entry_t::asset_types asset_type, pal::string_t* output, std::unordered_set<pal::string_t>* breadcrumb) { 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 std::function<pal::string_t(const pal::string_t&)> resources = [] (const pal::string_t& str) { return get_directory(get_directory(str)); }; // For native assemblies, obtain the directory path from the file path std::function<pal::string_t(const pal::string_t&)> native = [] (const pal::string_t& str) { return get_directory(str); }; // Action for post processing the resolved path std::function<pal::string_t(const pal::string_t&)>& action = is_resources ? resources : native; // Set for de-duplication std::unordered_set<pal::string_t> items; pal::string_t core_servicing = m_core_servicing; pal::realpath(&core_servicing, true); // Filter out non-serviced assets so the paths can be added after servicing paths. pal::string_t non_serviced; std::vector<deps_entry_t> empty(0); pal::string_t candidate; auto add_package_cache_entry = [&](const deps_entry_t& entry, const pal::string_t& deps_dir, int fx_level) -> bool { if (breadcrumb != nullptr && entry.is_serviceable) { breadcrumb->insert(entry.library_name + _X(",") + entry.library_version); breadcrumb->insert(entry.library_name); } if (items.count(entry.asset.name)) { return true; } // Ignore placeholders if (ends_with(entry.asset.relative_path, _X("/_._"), false)) { return true; } trace::verbose(_X("Processing native/culture for deps entry [%s, %s, %s]"), entry.library_name.c_str(), entry.library_version.c_str(), entry.asset.relative_path.c_str()); if (probe_deps_entry(entry, deps_dir, fx_level, &candidate)) { init_known_entry_path(entry, candidate); add_unique_path(asset_type, action(candidate), &items, output, &non_serviced, core_servicing); } else { // For self-contained apps do not use the full package name // because of rid-fallback could happen (ex: CentOS falling back to RHEL) if ((entry.asset.name == _X("apphost")) && ends_with(entry.library_name, _X(".Microsoft.NETCore.DotNetAppHost"), false)) { return report_missing_assembly_in_manifest(entry, true); } return report_missing_assembly_in_manifest(entry); } return true; }; // Add app entries const auto& entries = get_deps().get_entries(asset_type); for (const auto& entry : entries) { if (!add_package_cache_entry(entry, m_app_dir, 0)) { return false; } } // If the deps file is missing add known locations. if (!get_deps().exists()) { // App local path add_unique_path(asset_type, m_app_dir, &items, output, &non_serviced, core_servicing); (void) library_exists_in_dir(m_app_dir, LIBCORECLR_NAME, &m_coreclr_path); (void) library_exists_in_dir(m_app_dir, LIBCLRJIT_NAME, &m_clrjit_path); } // Handle any additional deps.json that were specified. for (const auto& additional_deps : m_additional_deps) { const auto additional_deps_entries = additional_deps->get_entries(asset_type); for (const auto entry : additional_deps_entries) { if (!add_package_cache_entry(entry, m_app_dir, 0)) { return false; } } } // Add fx package locations to fx_dir for (int i = 1; i < m_fx_definitions.size(); ++i) { const auto& fx_entries = m_fx_definitions[i]->get_deps().get_entries(asset_type); for (const auto& entry : fx_entries) { if (!add_package_cache_entry(entry, m_fx_definitions[i]->get_dir(), i)) { return false; } } } output->append(non_serviced); return true; }