/** * 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; }
/** * Resovle the TPA assembly locations */ bool deps_resolver_t::resolve_tpa_list( pal::string_t* output, std::unordered_set<pal::string_t>* breadcrumb) { const std::vector<deps_entry_t> empty(0); std::unordered_set<pal::string_t> items; auto process_entry = [&](const pal::string_t& deps_dir, deps_json_t* deps, const deps_entry_t& entry) -> 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; } pal::string_t candidate; 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()); if (probe_deps_entry(entry, deps_dir, &candidate)) { add_tpa_asset(entry.asset_name, candidate, &items, output); return true; } else { 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; } }; // First add managed assembly to the TPA. // TODO: Remove: the deps should contain the managed DLL. // Workaround for: csc.deps.json doesn't have the csc.dll pal::string_t managed_app_asset = get_filename_without_ext(m_managed_app); add_tpa_asset(managed_app_asset, m_managed_app, &items, output); const auto& deps_entries = m_deps->get_entries(deps_entry_t::asset_types::runtime); for (const auto& entry : deps_entries) { if (!process_entry(m_app_dir, m_deps.get(), entry)) { return false; } } // Finally, if the deps file wasn't present or has missing entries, then // add the app local assemblies to the TPA. if (!m_deps->exists()) { dir_assemblies_t local_assemblies; // Obtain the local assemblies in the app dir. get_dir_assemblies(m_app_dir, _X("local"), &local_assemblies); for (const auto& kv : local_assemblies) { add_tpa_asset(kv.first, kv.second, &items, output); } } // Probe FX deps entries after app assemblies are added. const auto& fx_entries = m_portable ? m_fx_deps->get_entries(deps_entry_t::asset_types::runtime) : empty; for (const auto& entry : fx_entries) { if (!process_entry(m_fx_dir, m_fx_deps.get(), entry)) { return false; } } return true; }
/** * Resolve the TPA assembly locations */ bool deps_resolver_t::resolve_tpa_list( pal::string_t* output, std::unordered_set<pal::string_t>* breadcrumb, bool ignore_missing_assemblies) { const std::vector<deps_entry_t> empty(0); name_to_resolved_asset_map_t items; auto process_entry = [&](const pal::string_t& deps_dir, const deps_entry_t& entry, int fx_level) -> bool { if (breadcrumb != nullptr && entry.is_serviceable) { breadcrumb->insert(entry.library_name + _X(",") + entry.library_version); breadcrumb->insert(entry.library_name); } // Ignore placeholders if (ends_with(entry.asset.relative_path, _X("/_._"), false)) { return true; } trace::info(_X("Processing TPA for deps entry [%s, %s, %s]"), entry.library_name.c_str(), entry.library_version.c_str(), entry.asset.relative_path.c_str()); pal::string_t resolved_path; name_to_resolved_asset_map_t::iterator existing = items.find(entry.asset.name); if (existing == items.end()) { if (probe_deps_entry(entry, deps_dir, fx_level, &resolved_path)) { deps_resolved_asset_t resolved_asset(entry.asset, resolved_path); add_tpa_asset(resolved_asset, &items); return true; } return report_missing_assembly_in_manifest(entry, ignore_missing_assemblies); } else { // Verify the extension is the same as the previous verified entry if (get_deps_filename(entry.asset.relative_path) != get_filename(existing->second.resolved_path)) { trace::error( DuplicateAssemblyWithDifferentExtensionMessage.c_str(), entry.deps_file.c_str(), entry.library_name.c_str(), entry.library_version.c_str(), entry.asset.relative_path.c_str(), existing->second.resolved_path.c_str()); return false; } deps_resolved_asset_t* existing_entry = &existing->second; // If deps entry is same or newer than existing, then see if it should be replaced if (entry.asset.assembly_version > existing_entry->asset.assembly_version || (entry.asset.assembly_version == existing_entry->asset.assembly_version && entry.asset.file_version >= existing_entry->asset.file_version)) { if (probe_deps_entry(entry, deps_dir, fx_level, &resolved_path)) { // If the path is the same, then no need to replace if (resolved_path != existing_entry->resolved_path) { trace::verbose(_X("Replacing deps entry [%s, AssemblyVersion:%s, FileVersion:%s] with [%s, AssemblyVersion:%s, FileVersion:%s]"), existing_entry->resolved_path.c_str(), existing_entry->asset.assembly_version.as_str().c_str(), existing_entry->asset.file_version.as_str().c_str(), resolved_path.c_str(), entry.asset.assembly_version.as_str().c_str(), entry.asset.file_version.as_str().c_str()); existing_entry = nullptr; items.erase(existing); deps_asset_t asset(entry.asset.name, entry.asset.relative_path, entry.asset.assembly_version, entry.asset.file_version); deps_resolved_asset_t resolved_asset(asset, resolved_path); add_tpa_asset(resolved_asset, &items); } } else if (fx_level != 0) { // The framework is missing a newer package, so this is an error. // For compat, it is not an error for the app; this can occur for the main application assembly when using --depsfile // and the app assembly does not exist with the deps file. return report_missing_assembly_in_manifest(entry); } } return true; } }; // We do not support self-contained in a libhost scenario since in the self-contained scenario, // we cannot determine what assemblies are framework assemblies, and what assemblies are app-local assemblies. if (m_host_mode != host_mode_t::libhost) { // First add managed assembly to the TPA. // TODO: Remove: the deps should contain the managed DLL. // Workaround for: csc.deps.json doesn't have the csc.dll deps_asset_t asset(get_filename_without_ext(m_managed_app), get_filename(m_managed_app), version_t(), version_t()); deps_resolved_asset_t resolved_asset(asset, m_managed_app); add_tpa_asset(resolved_asset, &items); // Add the app's entries const auto& deps_entries = get_deps().get_entries(deps_entry_t::asset_types::runtime); for (const auto& entry : deps_entries) { if (!process_entry(m_app_dir, entry, 0)) { return false; } } // If the deps file wasn't present or has missing entries, then // add the app local assemblies to the TPA. This is only valid // in non-libhost scenarios (e.g. comhost). if (!get_deps().exists()) { // Obtain the local assemblies in the app dir. get_dir_assemblies(m_app_dir, _X("local"), &items); } } // There should be no additional deps files in a libhost scenario. // See comments during additional deps.json resolution. assert(m_additional_deps.empty() || m_host_mode != host_mode_t::libhost); // If additional deps files were specified that need to be treated as part of the // application, then add them to the mix as well. for (const auto& additional_deps : m_additional_deps) { auto additional_deps_entries = additional_deps->get_entries(deps_entry_t::asset_types::runtime); for (auto entry : additional_deps_entries) { if (!process_entry(m_app_dir, entry, 0)) { return false; } } } // Probe FX deps entries after app assemblies are added. for (int i = 1; i < m_fx_definitions.size(); ++i) { const auto& deps_entries = m_is_framework_dependent ? m_fx_definitions[i]->get_deps().get_entries(deps_entry_t::asset_types::runtime) : empty; for (const auto& entry : deps_entries) { if (!process_entry(m_fx_definitions[i]->get_dir(), entry, i)) { return false; } } } // Convert the paths into a string and return it for (const auto& item : items) { // Workaround for CoreFX not being able to resolve sym links. pal::string_t real_asset_path = item.second.resolved_path; pal::realpath(&real_asset_path); output->append(real_asset_path); output->push_back(PATH_SEPARATOR); } return true; }