repoindex/alpm/mingwbundle.cpp

384 lines
18 KiB
C++

#include "mingwbundle.h"
#include "utilities.h"
#include "manager.h"
#include <c++utilities/conversion/stringconversion.h>
#include <c++utilities/misc/memory.h>
#include <KTar>
#include <K7Zip>
#include <KZip>
#include <QtConcurrent/QtConcurrent>
#include <QStringBuilder>
#include <QJsonDocument>
#include <QJsonArray>
#include <QFile>
#include <string>
#include <iostream>
using namespace std;
namespace PackageManagement {
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 << "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 << "Adding the following packages:";
for(const auto &pkg : m_packages) {
cerr << ' ' << pkg.second->name().toLocal8Bit().data();
}
cerr << 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<KTar> archive;
list<RelevantFile> 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<const KArchiveDirectory *>(entry), newPath);
} else if(entry->isFile()) {
pkgFileInfo.relevantFiles.emplace_back(static_cast<const KArchiveFile *>(entry), fileType, RelevantFileArch::Any, newPath);
}
}
}
}
void getFiles(PkgFileInfo &pkgFileInfo)
{
pkgFileInfo.archive = make_unique<KTar>(pkgFileInfo.path);
if(pkgFileInfo.archive->open(QIODevice::ReadOnly)) {
static const pair<RelevantFileArch, QString> 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<const KArchiveDirectory *>(rootEntry);
const auto *binEntry = rootDir->entry(QStringLiteral("bin"));
if(binEntry && binEntry->isDirectory()) {
const auto *binDir = static_cast<const KArchiveDirectory *>(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<const KArchiveFile *>(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<const KArchiveDirectory *>(libEntry);
const auto *qtEntry = libDir->entry("qt");
if(qtEntry && qtEntry->isDirectory()) {
const auto *qtDir = static_cast<const KArchiveDirectory *>(qtEntry);
const auto *pluginsEntry = qtDir->entry(QStringLiteral("plugins"));
if(pluginsEntry && pluginsEntry->isDirectory()) {
const auto *pluginsDir = static_cast<const KArchiveDirectory *>(pluginsEntry);
for(const auto &pluginCategory : pluginsDir->entries()) {
const auto *categoryEntry = pluginsDir->entry(pluginCategory);
if(categoryEntry && categoryEntry->isDirectory()) {
const auto *categoryDir = static_cast<const KArchiveDirectory *>(categoryEntry);
for(const auto &entryName : categoryDir->entries()) {
if(const auto *pluginEntry = categoryDir->entry(entryName)) {
if(pluginEntry->isFile()) {
const auto *pluginFile = static_cast<const KArchiveFile *>(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<const KArchiveDirectory *>(shareEntry);
const auto *qtEntry = shareDir->entry(QStringLiteral("qt"));
if(qtEntry && qtEntry->isDirectory()) {
const auto *qtDir = static_cast<const KArchiveDirectory *>(qtEntry);
const auto *trEntry = qtDir->entry(QStringLiteral("translations"));
if(trEntry && trEntry->isDirectory()) {
const auto trDir = static_cast<const KArchiveDirectory *>(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<const KArchiveFile *>(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<const KArchiveDirectory *>(appEntry);
const auto *trEntry = appDir->entry(QStringLiteral("translations"));
if(trEntry && trEntry->isDirectory()) {
const auto trDir = static_cast<const KArchiveDirectory *>(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<const KArchiveFile *>(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<const KArchiveDirectory *>(iconsEntry);
for(const auto &themeName : iconsDir->entries()) {
const auto *themeEntry = iconsDir->entry(themeName);
if(themeEntry && themeEntry->isDirectory()) {
addEntries(pkgFileInfo, RelevantFileType::Icon, static_cast<const KArchiveDirectory *>(themeEntry));
}
}
}
} else {
pkgFileInfo.failure = true;
}
}
void MingwBundle::createBundle(const string &targetDir, const string &targetName, const string &targetFormat) const
{
cerr << "Gathering relevant files ..." << endl;
// get package files
list<PkgFileInfo> 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<RelevantFileArch, QString> 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 << "Making archive \"" << targetPath.toLocal8Bit().data() << "\" ..." << endl;
unique_ptr<KArchive> targetArchive;
if(targetFormat == "7z") {
targetArchive = make_unique<K7Zip>(targetPath);
} else if(targetFormat == "zip") {
targetArchive = make_unique<KZip>(targetPath);
} else if(ConversionUtilities::startsWith<string>(targetFormat, "tar")) {
targetArchive = make_unique<KTar>(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