#include "./config.h" #include "./storageprivate.h" #include #include using namespace std; using namespace CppUtilities; namespace LibPkg { Status::Status(const Config &config) : architectures(config.architectures) , pacmanDatabasePath(config.pacmanDatabasePath) , packageCacheDirs(config.packageCacheDirs) { dbStats.reserve(config.databases.size() + 1); for (const auto &db : config.databases) { dbStats.emplace_back(db); } dbStats.emplace_back(config.aur); } static const std::string &firstNonLocalMirror(const std::vector &mirrors) { for (const auto &mirror : mirrors) { if (!mirror.empty() && !startsWith(mirror, "file:")) { return mirror; } } static const auto emptyMirror = std::string(); return emptyMirror; } DatabaseStatistics::DatabaseStatistics(const Database &db) : name(db.name) , packageCount(db.packageCount()) , arch(db.arch) , lastUpdate(db.lastUpdate) , localPkgDir(db.localPkgDir) , mainMirror(firstNonLocalMirror(db.mirrors)) , syncFromMirror(db.syncFromMirror) { } Config::Config() { } Config::~Config() { } void Config::initStorage(const char *path, std::uint32_t maxDbs) { assert(m_storage == nullptr); // only allow initializing storage once m_storage = std::make_unique(path, maxDbs ? maxDbs : std::max(databases.size() * 10u + 15u, 60u)); for (auto &db : databases) { db.initStorage(*m_storage); } aur.initStorage(*m_storage); } void LibPkg::Config::rebuildDb() { assert(m_storage != nullptr); for (auto &db : databases) { db.rebuildDb(); } aur.rebuildDb(); } void Config::dumpDb(const std::optional &filterRegex) { assert(m_storage != nullptr); for (auto &db : databases) { db.dumpDb(filterRegex); } aur.dumpDb(filterRegex); } std::size_t Config::cachedPackages() const { return m_storage ? m_storage->packageCache().size() : 0; } void Config::setPackageCacheLimit(std::size_t limit) { m_storage->packageCache().setLimit(limit); } static std::string addDatabaseDependencies( Config &config, Database &database, std::vector &result, std::unordered_map &visited, bool addSelf) { // abort if ... const auto insertion = visited.emplace(make_pair(&database, false)); if (insertion.first->second) { return string(); // ... the database is already dealt with } if (!insertion.second) { return argsToString("cycle at ", database.name); // ... a cycle has been detected } // add the dependencies first for (const auto &dependency : database.dependencies) { auto *const requiredDb = config.findDatabase(dependency, database.arch); if (!requiredDb) { return argsToString( "database \"", dependency, "\" required by \"", database.name, "\" does not exist (architecture ", database.arch, ')'); } if (auto error = addDatabaseDependencies(config, *requiredDb, result, visited, true); !error.empty()) { return error; } } // add database itself if (addSelf) { result.emplace_back(&database); } // consider this db done; if something else depends on it is o.k. and not a cycle visited[&database] = true; return string(); } std::variant, std::string> Config::computeDatabaseDependencyOrder(Database &database, bool addSelf) { std::vector result; std::unordered_map visited; result.reserve(database.dependencies.size()); auto error = addDatabaseDependencies(*this, database, result, visited, addSelf); if (!error.empty()) { return std::variant, std::string>(std::move(error)); } return std::variant, std::string>(std::move(result)); } static void addDatabasesRequiringDatabase( Config &config, Database ¤tDatabase, std::vector &result, std::unordered_set &visited) { // abort if the database is already dealt with or being processed (not caring about cycles here) if (!visited.emplace(¤tDatabase).second) { return; } // add the current database result.emplace_back(¤tDatabase); // add all configured databases depending on the current database for (auto &configuredDb : config.databases) { if (&configuredDb == ¤tDatabase || configuredDb.arch != currentDatabase.arch) { continue; } if (std::find(configuredDb.dependencies.begin(), configuredDb.dependencies.end(), currentDatabase.name) != configuredDb.dependencies.end()) { addDatabasesRequiringDatabase(config, configuredDb, result, visited); } } } std::vector Config::computeDatabasesRequiringDatabase(Database &database) { std::vector result; std::unordered_set visited; result.reserve(databases.size()); addDatabasesRequiringDatabase(*this, database, result, visited); return result; } void Config::pullDependentPackages(const std::shared_ptr &package, const std::unordered_set &relevantDbs, std::unordered_map> &runtimeDependencies, DependencySet &missingDependencies, std::unordered_set &visited) { auto remainingPackages = std::unordered_set>{ package }; for (auto packageIterator = remainingPackages.begin(); packageIterator != remainingPackages.end(); packageIterator = remainingPackages.begin()) { const auto ¤tPackage = *packageIterator; for (const auto &dependencies : { currentPackage->dependencies, currentPackage->optionalDependencies }) { auto found = false; for (const auto &dependency : dependencies) { for (auto &db : databases) { if (relevantDbs.find(&db) == relevantDbs.end()) { continue; } db.providingPackages(dependency, false, [&](StorageID packageID, const std::shared_ptr &providingPackage) { found = true; if (visited.emplace(packageID).second) { const auto &[i, inserted] = runtimeDependencies.try_emplace(packageID); if (inserted) { i->second = providingPackage; } remainingPackages.emplace(providingPackage); } return false; }); } if (!found) { missingDependencies.add(dependency, currentPackage); } } } remainingPackages.erase(packageIterator); } } void Config::markAllDatabasesToBeDiscarded() { for_each(databases.begin(), databases.end(), [](auto &db) { db.toBeDiscarded = true; }); } void Config::discardDatabases() { databases.erase(remove_if(databases.begin(), databases.end(), [](const auto &db) { return db.toBeDiscarded; }), databases.end()); } } // namespace LibPkg