/** * 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; }
void deps_resolver_t::resolve_tpa_list( const pal::string_t& clr_dir, pal::string_t* output) { const std::vector<deps_entry_t> empty(0); // Obtain the local assemblies in the app dir. get_dir_assemblies(m_app_dir, _X("local"), &m_local_assemblies); if (m_portable) { // For portable also obtain FX dir assemblies. get_dir_assemblies(m_fx_dir, _X("fx"), &m_fx_assemblies); } std::unordered_set<pal::string_t> items; auto process_entry = [&](const pal::string_t& deps_dir, deps_json_t* deps, const dir_assemblies_t& dir_assemblies, const deps_entry_t& entry) { if (items.count(entry.asset_name)) { return; } 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()); // Try to probe from the shared locations. if (probe_entry_in_configs(entry, &candidate)) { add_tpa_asset(entry.asset_name, candidate, &items, output); } // The rid asset should be picked up from app relative subpath. else if (entry.is_rid_specific && entry.to_rel_path(deps_dir, &candidate)) { add_tpa_asset(entry.asset_name, candidate, &items, output); } // The rid-less asset should be picked up from the app base. else if (dir_assemblies.count(entry.asset_name)) { add_tpa_asset(entry.asset_name, dir_assemblies.find(entry.asset_name)->second, &items, output); } else { // FIXME: Consider this error as a fail fast? trace::verbose(_X("Error: Could not resolve path to assembly: [%s, %s, %s]"), entry.library_name.c_str(), entry.library_version.c_str(), entry.relative_path.c_str()); } }; const auto& deps_entries = m_deps->get_entries(deps_entry_t::asset_types::runtime); std::for_each(deps_entries.begin(), deps_entries.end(), [&](const deps_entry_t& entry) { process_entry(m_app_dir, m_deps.get(), m_local_assemblies, entry); }); // Finally, if the deps file wasn't present or has missing entries, then // add the app local assemblies to the TPA. for (const auto& kv : m_local_assemblies) { add_tpa_asset(kv.first, kv.second, &items, output); } const auto& fx_entries = m_portable ? m_fx_deps->get_entries(deps_entry_t::asset_types::runtime) : empty; std::for_each(fx_entries.begin(), fx_entries.end(), [&](const deps_entry_t& entry) { process_entry(m_fx_dir, m_fx_deps.get(), m_fx_assemblies, entry); }); for (const auto& kv : m_fx_assemblies) { add_tpa_asset(kv.first, kv.second, &items, output); } }
// ----------------------------------------------------------------------------- // Load local assemblies by priority order of their file extensions and // unique-fied by their simple name. // void deps_resolver_t::get_dir_assemblies( const pal::string_t& dir, const pal::string_t& dir_name, name_to_resolved_asset_map_t* items) { version_t empty; trace::verbose(_X("Adding files from %s dir %s"), dir_name.c_str(), dir.c_str()); // Managed extensions in priority order, pick DLL over EXE and NI over IL. const pal::string_t managed_ext[] = { _X(".ni.dll"), _X(".dll"), _X(".ni.exe"), _X(".exe") }; // List of files in the dir std::vector<pal::string_t> files; pal::readdir(dir, &files); for (const auto& ext : managed_ext) { for (const auto& file : files) { // Nothing to do if file length is smaller than expected ext. if (file.length() <= ext.length()) { continue; } auto file_name = file.substr(0, file.length() - ext.length()); auto file_ext = file.substr(file_name.length()); // Ext did not match expected ext, skip this file. if (pal::strcasecmp(file_ext.c_str(), ext.c_str())) { continue; } // Already added entry for this asset, by priority order skip this ext if (items->count(file_name)) { trace::verbose(_X("Skipping %s because the %s already exists in %s assemblies"), file.c_str(), items->find(file_name)->second.asset.relative_path.c_str(), dir_name.c_str()); continue; } // Add entry for this asset pal::string_t file_path = dir; if (!file_path.empty() && file_path.back() != DIR_SEPARATOR) { file_path.push_back(DIR_SEPARATOR); } file_path.append(file); trace::verbose(_X("Adding %s to %s assembly set from %s"), file_name.c_str(), dir_name.c_str(), file_path.c_str()); deps_asset_t asset(file_name, file, empty, empty); deps_resolved_asset_t resolved_asset(asset, file_path); add_tpa_asset(resolved_asset, items); } } }