#include "mingwbundle.h" #include "utilities.h" #include "manager.h" #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; namespace RepoIndex { using namespace Utilities; const string prefix("mingw-w64-"); MingwBundle::MingwBundle(const Manager &manager, const ApplicationUtilities::StringVector &packages, const ApplicationUtilities::StringVector &iconPackages) : m_manager(manager) { cerr << shchar << "Resolving dependencies ..." << endl; string missing; // add mingw-w64 packages for(const auto &pkgName : packages) { bool found = false; for(const auto &syncDb : manager.syncDatabases()) { if(auto *pkg = syncDb.second->packageByName(QString::fromLocal8Bit(ConversionUtilities::startsWith(pkgName, prefix) ? pkgName.data() : (prefix + pkgName).data()))) { if(missing.empty()) { decltype(m_packages)::value_type entry(syncDb.second.get(), pkg); if(find(m_packages.cbegin(), m_packages.cend(), entry) == m_packages.cend()) { m_packages.emplace_back(entry); } } addDependencies(pkg); found = true; break; } } if(!found) { missing.push_back(' '); missing.append(pkgName); } } // add additional icon packages for(const auto &pkgName : iconPackages) { bool found = false; for(const auto &syncDb : manager.syncDatabases()) { if(auto *pkg = syncDb.second->packageByName(QString::fromLocal8Bit(pkgName.data()))) { if(missing.empty()) { decltype(m_packages)::value_type entry(syncDb.second.get(), pkg); if(find(m_packages.cbegin(), m_packages.cend(), entry) == m_packages.cend()) { m_packages.emplace_back(entry); } } found = true; break; } } if(!found) { missing.push_back(' '); missing.append(pkgName); } } if(!missing.empty()) { throw runtime_error("The following packages can not be found:" + missing); } else { cerr << shchar << "Adding the following packages:"; for(const auto &pkg : m_packages) { cerr << shchar << ' ' << pkg.second->name().toLocal8Bit().data(); } cerr << shchar << endl; } } void MingwBundle::addDependencies(const Package *pkg) { string missing; for(const auto &dep : pkg->dependencies()) { if(dep.name.startsWith(QLatin1String("mingw-w64-"), Qt::CaseInsensitive)) { bool found = false; for(const auto &syncDbEntry : m_manager.syncDatabases()) { if(const auto *pkg = syncDbEntry.second->packageProviding(dep)) { if(missing.empty()) { decltype(m_packages)::value_type entry(syncDbEntry.second.get(), pkg); if(find(m_packages.cbegin(), m_packages.cend(), entry) == m_packages.cend()) { m_packages.emplace_back(entry); } } addDependencies(pkg); found = true; break; } } if(!found) { missing.push_back(' '); missing.append(dep.name.toLocal8Bit().data()); } } } if(!missing.empty()) { throw runtime_error("The following dependencies of the " + string(pkg->name().toLocal8Bit().data()) + " package can not be resolved:" + missing); } } enum class RelevantFileType { Binary, Translation, QtTranslation, QtPlugin, Icon }; enum class RelevantFileArch { x86_64, i686, Any }; struct RelevantFile { RelevantFile(const KArchiveFile *file, const RelevantFileType type, const RelevantFileArch arch, const QString &subDir = QString()) : file(file), fileType(type), arch(arch), subDir(subDir) {} const KArchiveFile *file; RelevantFileType fileType; RelevantFileArch arch; QString subDir; }; struct PkgFileInfo { PkgFileInfo(const QString &name, const QString &path) : name(name), path(path), failure(false) {} QString name; QString path; unique_ptr archive; list relevantFiles; bool failure; }; void addEntries(PkgFileInfo &pkgFileInfo, RelevantFileType fileType, const KArchiveDirectory *dir, const QString &relPath = QString()) { QString newPath = relPath.isEmpty() ? dir->name() : relPath % QChar('/') % dir->name(); for(const auto &entryName : dir->entries()) { if(auto *entry = dir->entry(entryName)) { if(entry->isDirectory()) { addEntries(pkgFileInfo, fileType, static_cast(entry), newPath); } else if(entry->isFile()) { pkgFileInfo.relevantFiles.emplace_back(static_cast(entry), fileType, RelevantFileArch::Any, newPath); } } } } void getFiles(PkgFileInfo &pkgFileInfo) { pkgFileInfo.archive = make_unique(pkgFileInfo.path); if(pkgFileInfo.archive->open(QIODevice::ReadOnly)) { static const pair roots[] = { make_pair(RelevantFileArch::x86_64, QStringLiteral("/usr/x86_64-w64-mingw32")), make_pair(RelevantFileArch::i686, QStringLiteral("/usr/i686-w64-mingw32")) }; for(const auto &root : roots) { const auto *rootEntry = pkgFileInfo.archive->directory()->entry(root.second); if(rootEntry && rootEntry->isDirectory()) { const auto *rootDir = static_cast(rootEntry); const auto *binEntry = rootDir->entry(QStringLiteral("bin")); if(binEntry && binEntry->isDirectory()) { const auto *binDir = static_cast(binEntry); for(const auto &entryName : binDir->entries()) { if(entryName.endsWith(QLatin1String(".exe")) || entryName.endsWith(QLatin1String(".dll"))) { if(const auto *entry = binDir->entry(entryName)) { if(entry->isFile()) { const auto *binFile = static_cast(entry); pkgFileInfo.relevantFiles.emplace_back(binFile, RelevantFileType::Binary, root.first); } } } } } const auto *libEntry = rootDir->entry(QStringLiteral("lib")); if(libEntry && libEntry->isDirectory()) { const auto *libDir = static_cast(libEntry); const auto *qtEntry = libDir->entry("qt"); if(qtEntry && qtEntry->isDirectory()) { const auto *qtDir = static_cast(qtEntry); const auto *pluginsEntry = qtDir->entry(QStringLiteral("plugins")); if(pluginsEntry && pluginsEntry->isDirectory()) { const auto *pluginsDir = static_cast(pluginsEntry); for(const auto &pluginCategory : pluginsDir->entries()) { const auto *categoryEntry = pluginsDir->entry(pluginCategory); if(categoryEntry && categoryEntry->isDirectory()) { const auto *categoryDir = static_cast(categoryEntry); for(const auto &entryName : categoryDir->entries()) { if(const auto *pluginEntry = categoryDir->entry(entryName)) { if(pluginEntry->isFile()) { const auto *pluginFile = static_cast(pluginEntry); pkgFileInfo.relevantFiles.emplace_back(pluginFile, RelevantFileType::QtPlugin, root.first, categoryDir->name()); } } } } } } } } const auto *shareEntry = rootDir->entry(QStringLiteral("share")); if(shareEntry && shareEntry->isDirectory()) { const auto *shareDir = static_cast(shareEntry); const auto *qtEntry = shareDir->entry(QStringLiteral("qt")); if(qtEntry && qtEntry->isDirectory()) { const auto *qtDir = static_cast(qtEntry); const auto *trEntry = qtDir->entry(QStringLiteral("translations")); if(trEntry && trEntry->isDirectory()) { const auto trDir = static_cast(trEntry); for(const auto &entryName : trDir->entries()) { if(entryName.endsWith(QLatin1String(".qm"))) { if(const auto *qmEntry = trDir->entry(entryName)) { if(qmEntry->isFile()) { const auto *qmFile = static_cast(qmEntry); pkgFileInfo.relevantFiles.emplace_back(qmFile, RelevantFileType::QtTranslation, root.first); } } } } } } if(pkgFileInfo.name.compare(QLatin1String("qt"))) { const auto *appEntry = shareDir->entry(pkgFileInfo.name); if(appEntry && appEntry->isDirectory()) { const auto *appDir = static_cast(appEntry); const auto *trEntry = appDir->entry(QStringLiteral("translations")); if(trEntry && trEntry->isDirectory()) { const auto trDir = static_cast(trEntry); for(const auto &entryName : trDir->entries()) { if(entryName.endsWith(QLatin1String(".qm"))) { if(const auto *qmEntry = trDir->entry(entryName)) { if(qmEntry->isFile()) { const auto *qmFile = static_cast(qmEntry); pkgFileInfo.relevantFiles.emplace_back(qmFile, RelevantFileType::Translation, root.first); } } } } } } } } } } const auto *iconsEntry = pkgFileInfo.archive->directory()->entry(QStringLiteral("usr/share/icons")); if(iconsEntry && iconsEntry->isDirectory()) { const auto *iconsDir = static_cast(iconsEntry); for(const auto &themeName : iconsDir->entries()) { const auto *themeEntry = iconsDir->entry(themeName); if(themeEntry && themeEntry->isDirectory()) { addEntries(pkgFileInfo, RelevantFileType::Icon, static_cast(themeEntry)); } } } } else { pkgFileInfo.failure = true; } } void MingwBundle::createBundle(const string &targetDir, const string &targetName, const string &targetFormat) const { cerr << shchar << "Gathering relevant files ..." << endl; // get package files list pkgFiles; for(const auto &entry : m_packages) { QString pkgFile; if(!entry.first->packagesDirectory().isEmpty()) { pkgFile = entry.first->packagesDirectory() % QChar('/') % entry.second->fileName(); } if(pkgFile.isEmpty() || !QFile::exists(pkgFile)) { if(!m_manager.pacmanCacheDir().isEmpty()) { pkgFile = m_manager.pacmanCacheDir() % QChar('/') % entry.second->fileName(); } if(pkgFile.isEmpty() || !QFile::exists(pkgFile)) { throw runtime_error("The package file " + string(entry.second->fileName().toLocal8Bit().data()) + " can't be found."); // TODO: download package from mirror } } pkgFiles.emplace_back(entry.second->name().startsWith(QLatin1String("mingw-w64-")) ? entry.second->name().mid(10) : entry.second->name(), pkgFile); } // get relevant files from packages QtConcurrent::blockingMap(pkgFiles, getFiles); // check whether all packages could be opened string failed; for(const auto &pkgFile : pkgFiles) { if(pkgFile.failure) { failed.push_back(' '); failed.append(pkgFile.path.toLocal8Bit().data()); } } if(!failed.empty()) { throw runtime_error("Unable to open the following package files:" + failed); } // make a list with package info to be included in the target archive QJsonArray pkgArray; for(const auto &entry : m_packages) { pkgArray << entry.second->basicInfo(true); } QJsonDocument pkgList; pkgList.setArray(pkgArray); QByteArray pkgListBytes = pkgList.toJson(); // make target archive static const QString user(QStringLiteral("root")); static const QString &group = user; static const pair roots[] = { make_pair(RelevantFileArch::x86_64, QStringLiteral("x86_64-w64-mingw32")), make_pair(RelevantFileArch::i686, QStringLiteral("i686-w64-mingw32")) }; for(const auto &root : roots) { QString targetPath = qstr(targetDir) % QChar('/') % root.second % QChar('-') % qstr(targetName) % QChar('.') % qstr(targetFormat); cerr << shchar << "Making archive \"" << targetPath.toLocal8Bit().data() << "\" ..." << endl; unique_ptr targetArchive; if(targetFormat == "7z") { targetArchive = make_unique(targetPath); } else if(targetFormat == "zip") { targetArchive = make_unique(targetPath); } else if(ConversionUtilities::startsWith(targetFormat, "tar")) { targetArchive = make_unique(targetPath); } else { throw runtime_error("Specified archive format \"" + targetFormat + "\" is unknown."); } if(targetArchive->open(QIODevice::WriteOnly)) { // add package list targetArchive->writeFile(root.second % QStringLiteral("/var/lib/repoindex/packages.list"), pkgListBytes, 0100644, user, group); // add relevant files from packages for(const auto &pkgFile : pkgFiles) { for(const RelevantFile &relevantFile : pkgFile.relevantFiles) { if(relevantFile.arch == RelevantFileArch::Any || relevantFile.arch == root.first) { switch(relevantFile.fileType) { case RelevantFileType::Binary: targetArchive->writeFile(root.second % QStringLiteral("/bin/") % relevantFile.file->name(), relevantFile.file->data(), 0100755, user, group); break; case RelevantFileType::Translation: targetArchive->writeFile(root.second % QStringLiteral("/share/") % pkgFile.name % QStringLiteral("/translations/") % relevantFile.file->name(), relevantFile.file->data(), 0100644, user, group); break; case RelevantFileType::QtTranslation: targetArchive->writeFile(root.second % QStringLiteral("/share/qt/translations/") % relevantFile.file->name(), relevantFile.file->data(), 0100644, user, group); break; case RelevantFileType::QtPlugin: targetArchive->writeFile(root.second % QStringLiteral("/bin/") % relevantFile.subDir % QChar('/') % relevantFile.file->name(), relevantFile.file->data(), 0100755, user, group); break; case RelevantFileType::Icon: targetArchive->writeFile(root.second % QStringLiteral("/share/icons/") % relevantFile.subDir % QChar('/') % relevantFile.file->name(), relevantFile.file->data(), 0100644, user, group); break; } } } } } else if(targetArchive->device()) { throw runtime_error("Unable to open target archive: " + string(targetArchive->device()->errorString().toLocal8Bit().data())); } else { throw runtime_error("Unable to open target archive."); } } } } // namespace PackageManagement