improved build-order and mingw-w64-bundle

This commit is contained in:
Martchus 2016-01-03 00:22:48 +01:00
parent 1f1d46c5dd
commit 39e3ccc77c
14 changed files with 722 additions and 239 deletions

View File

@ -91,9 +91,15 @@ void MingwBundle::addDependencies(const Package *pkg)
enum class RelevantFileType
{
Binary,
Data,
Translation,
QtTranslation,
QtPlugin,
Plugin,
SharedData,
GLib2Data,
GLib2Schemas,
Theme,
IconTheme,
ConfigFile
};
@ -122,6 +128,7 @@ struct RelevantFile
}
}
}
QString name;
QByteArray data;
RelevantFileType fileType;
@ -136,6 +143,7 @@ struct PkgFileInfo
path(path),
failure(false)
{}
QString name;
QString path;
unique_ptr<KTar> archive;
@ -143,15 +151,17 @@ struct PkgFileInfo
bool failure;
};
void addEntries(PkgFileInfo &pkgFileInfo, RelevantFileType fileType, const KArchiveDirectory *dir, const QString &relPath = QString())
void addEntries(PkgFileInfo &pkgFileInfo, RelevantFileType fileType, const KArchiveDirectory *dir, const QString &relPath = QString(), const QString &ext = QString(), RelevantFileArch relevantArch = RelevantFileArch::Any)
{
QString newPath = relPath.isEmpty() ? dir->name() : relPath % QChar('/') % dir->name();
const 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);
addEntries(pkgFileInfo, fileType, static_cast<const KArchiveDirectory *>(entry), newPath, ext, relevantArch);
} else if(entry->isFile()) {
pkgFileInfo.relevantFiles.emplace_back(static_cast<const KArchiveFile *>(entry), dir, fileType, RelevantFileArch::Any, newPath);
if(ext.isEmpty() || entry->name().endsWith(ext)) {
pkgFileInfo.relevantFiles.emplace_back(static_cast<const KArchiveFile *>(entry), dir, fileType, relevantArch, newPath);
}
}
}
}
@ -165,10 +175,14 @@ void getFiles(PkgFileInfo &pkgFileInfo)
make_pair(RelevantFileArch::x86_64, QStringLiteral("/usr/x86_64-w64-mingw32")),
make_pair(RelevantFileArch::i686, QStringLiteral("/usr/i686-w64-mingw32"))
};
// add files for each architecture separately
for(const auto &root : roots) {
// get root entry
const auto *rootEntry = pkgFileInfo.archive->directory()->entry(root.second);
if(rootEntry && rootEntry->isDirectory()) {
const auto *rootDir = static_cast<const KArchiveDirectory *>(rootEntry);
// binary directory: *.exe and *.dll files required
const auto *binEntry = rootDir->entry(QStringLiteral("bin"));
if(binEntry && binEntry->isDirectory()) {
const auto *binDir = static_cast<const KArchiveDirectory *>(binEntry);
@ -182,8 +196,11 @@ void getFiles(PkgFileInfo &pkgFileInfo)
}
}
}
// library directory: Qt, GTK and application specific libs/plugins required
const auto *libEntry = rootDir->entry(QStringLiteral("lib"));
if(libEntry && libEntry->isDirectory()) {
// Qt 5 plugins
const auto *libDir = static_cast<const KArchiveDirectory *>(libEntry);
const auto *qtEntry = libDir->entry("qt");
if(qtEntry && qtEntry->isDirectory()) {
@ -206,7 +223,38 @@ void getFiles(PkgFileInfo &pkgFileInfo)
}
}
}
// Qt 4 plugins
qtEntry = libDir->entry(QStringLiteral("qt4"));
if(qtEntry && qtEntry->isDirectory()) {
addEntries(pkgFileInfo, RelevantFileType::Plugin, static_cast<const KArchiveDirectory *>(qtEntry), QString(), QString(), root.first);
}
// GTK plugins
const auto *gtkEntry = libDir->entry(QStringLiteral("gtk-2.0"));
if(gtkEntry && gtkEntry->isDirectory()) {
addEntries(pkgFileInfo, RelevantFileType::Plugin, static_cast<const KArchiveDirectory *>(gtkEntry), QString(), QStringLiteral(".dll"), root.first);
}
gtkEntry = libDir->entry(QStringLiteral("gtk-3.0"));
if(gtkEntry && gtkEntry->isDirectory()) {
addEntries(pkgFileInfo, RelevantFileType::Plugin, static_cast<const KArchiveDirectory *>(gtkEntry), QString(), QStringLiteral(".dll"), root.first);
}
// app specific libs/plugins
const auto *evinceEntry = libDir->entry(pkgFileInfo.name);
if(evinceEntry && evinceEntry->isDirectory()) {
addEntries(pkgFileInfo, RelevantFileType::Plugin, static_cast<const KArchiveDirectory *>(evinceEntry), QString(), QStringLiteral(".dll"), root.first);
// required by evince
addEntries(pkgFileInfo, RelevantFileType::Plugin, static_cast<const KArchiveDirectory *>(evinceEntry), QString(), QStringLiteral(".evince-backend"), root.first);
}
}
// data directory: required by geany
const auto *dataEntry = rootDir->entry(QStringLiteral("data"));
if(dataEntry && dataEntry->isDirectory()) {
addEntries(pkgFileInfo, RelevantFileType::Data, static_cast<const KArchiveDirectory *>(dataEntry), QString(), QString(), root.first);
}
// shared directory: translations, themes, icons, schemas
const auto *shareEntry = rootDir->entry(QStringLiteral("share"));
if(shareEntry && shareEntry->isDirectory()) {
const auto *shareDir = static_cast<const KArchiveDirectory *>(shareEntry);
@ -227,16 +275,46 @@ void getFiles(PkgFileInfo &pkgFileInfo)
}
}
}
const auto *themesEntry = shareDir->entry(QStringLiteral("themes"));
if(themesEntry && themesEntry->isDirectory()) {
const auto *themesDir = static_cast<const KArchiveDirectory *>(themesEntry);
for(const auto &themeName : themesDir->entries()) {
const auto *themeEntry = themesDir->entry(themeName);
if(themeEntry && themeEntry->isDirectory()) {
addEntries(pkgFileInfo, RelevantFileType::Theme, static_cast<const KArchiveDirectory *>(themeEntry), QString(), QString(), root.first);
}
}
}
const auto *iconsEntry = shareDir->entry(QStringLiteral("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::IconTheme, static_cast<const KArchiveDirectory *>(themeEntry));
addEntries(pkgFileInfo, RelevantFileType::IconTheme, static_cast<const KArchiveDirectory *>(themeEntry), QString(), QString(), root.first);
}
}
}
const auto *glib2Entry = shareDir->entry(QStringLiteral("glib-2.0"));
if(glib2Entry && glib2Entry->isDirectory()) {
const auto *glib2Dir = static_cast<const KArchiveDirectory *>(glib2Entry);
for(const auto &glib2SubEntryName : glib2Dir->entries()) {
const auto *glib2SubEntry = glib2Dir->entry(glib2SubEntryName);
if(glib2SubEntry->isDirectory()) {
if(glib2SubEntry->name() == QLatin1String("schemas")) {
addEntries(pkgFileInfo, RelevantFileType::GLib2Schemas, static_cast<const KArchiveDirectory *>(glib2SubEntry), QStringLiteral("glib-2.0"), QString(), root.first);
} else {
addEntries(pkgFileInfo, RelevantFileType::GLib2Data, static_cast<const KArchiveDirectory *>(glib2SubEntry), QStringLiteral("glib-2.0"), QString(), root.first);
}
} else if(glib2SubEntry->isFile()) {
pkgFileInfo.relevantFiles.emplace_back(static_cast<const KArchiveFile *>(glib2SubEntry), glib2Dir, RelevantFileType::GLib2Data, root.first, QStringLiteral("glib-2.0"));
}
}
}
if(pkgFileInfo.name.compare(QLatin1String("qt"))) {
const auto *appEntry = shareDir->entry(pkgFileInfo.name);
if(appEntry && appEntry->isDirectory()) {
@ -285,9 +363,16 @@ void getFiles(PkgFileInfo &pkgFileInfo)
}
}
const auto *localeEntry = shareDir->entry(QStringLiteral("locale"));
if(localeEntry && localeEntry->isDirectory()) {
addEntries(pkgFileInfo, RelevantFileType::SharedData, static_cast<const KArchiveDirectory *>(localeEntry), QString(), QString(), root.first);
}
}
}
}
// icons from regular package
const auto *iconsEntry = pkgFileInfo.archive->directory()->entry(QStringLiteral("usr/share/icons"));
if(iconsEntry && iconsEntry->isDirectory()) {
const auto *iconsDir = static_cast<const KArchiveDirectory *>(iconsEntry);
@ -305,6 +390,7 @@ void getFiles(PkgFileInfo &pkgFileInfo)
void makeArchive(const list<PkgFileInfo> &pkgFiles, const QByteArray &pkgList, const QByteArray &indexFile, RelevantFileArch arch, const QString &root, const string &targetDir, const string &targetName, const string &targetFormat)
{
// prepare archive
QString targetPath = qstr(targetDir) % QChar('/') % root % QChar('-') % qstr(targetName) % QChar('.') % qstr(targetFormat);
cerr << shchar << "Making archive \"" << targetPath.toLocal8Bit().data() << "\" ..." << endl;
unique_ptr<KArchive> targetArchive;
@ -317,20 +403,63 @@ void makeArchive(const list<PkgFileInfo> &pkgFiles, const QByteArray &pkgList, c
} else {
throw runtime_error("Specified archive format \"" + targetFormat + "\" is unknown.");
}
// add files to archive
if(targetArchive->open(QIODevice::WriteOnly)) {
// add note
// -> compile glib2 schemas and add
vector<const RelevantFile *> glib2SchemaFiles;
glib2SchemaFiles.reserve(16);
for(const auto &pkgFile : pkgFiles) {
for(const RelevantFile &relevantFile : pkgFile.relevantFiles) {
if(relevantFile.arch == RelevantFileArch::Any || relevantFile.arch == arch) {
switch(relevantFile.fileType) {
case RelevantFileType::GLib2Schemas:
glib2SchemaFiles.push_back(&relevantFile);
break;
default:
;
}
}
}
}
if(!glib2SchemaFiles.empty()) {
QTemporaryDir tempDir;
if(tempDir.isValid()) {
for(const RelevantFile *schemaFile : glib2SchemaFiles) {
QFile file(tempDir.path() % QChar('/') % schemaFile->name);
if(!file.open(QFile::WriteOnly) || !file.write(schemaFile->data)) {
cerr << shchar << "Unable to create glib2 schema file \"" << schemaFile->name.toLocal8Bit().data() << "\"." << endl;
}
}
QProcess compiler;
compiler.setProcessChannelMode(QProcess::ForwardedChannels);
compiler.start(QStringLiteral("glib-compile-schemas"), QStringList() << tempDir.path());
compiler.waitForFinished();
QFile compiledSchemas(tempDir.path() + QStringLiteral("/gschemas.compiled"));
if(compiledSchemas.open(QFile::ReadOnly)) {
targetArchive->writeFile(root + QStringLiteral("/share/glib-2.0/schemas/gschemas.compiled"), compiledSchemas.readAll());
} else {
cerr << shchar << "Unable to compile glib2 schemas: Compiled schemas (gschemas.compiled) not found." << endl;
}
} else {
cerr << shchar << "Unable to compile glib2 schemas: Can't create temp dir." << endl;
}
}
// -> add note
targetArchive->writeFile(root + QStringLiteral("/note.txt"), QByteArray("This archive has been created with Martchus' repository indexing tool.\n"
"List of included packages: var/lib/repoindex/packages.list\n"
"More info: http://martchus.netai.net/page.php?name=programming"));
// add package list
// -> add package list
if(!pkgList.isEmpty()) {
targetArchive->writeFile(root + QStringLiteral("/var/lib/repoindex/packages.list"), pkgList);
}
// set default icon theme
// -> set default icon theme
if(!indexFile.isEmpty()) {
targetArchive->writeFile(root + QStringLiteral("/share/icons/default/index.theme"), indexFile);
}
// add relevant files from packages
// -> add relevant files from packages
for(const auto &pkgFile : pkgFiles) {
for(const RelevantFile &relevantFile : pkgFile.relevantFiles) {
if(relevantFile.arch == RelevantFileArch::Any || relevantFile.arch == arch) {
@ -341,6 +470,10 @@ void makeArchive(const list<PkgFileInfo> &pkgFiles, const QByteArray &pkgList, c
path = root % QStringLiteral("/bin/") % relevantFile.name;
mode = 0100755;
break;
case RelevantFileType::Data:
path = root % QStringLiteral("/") % relevantFile.subDir % QChar('/') % relevantFile.name;
mode = 0100644;
break;
case RelevantFileType::Translation:
path = root % QStringLiteral("/share/") % pkgFile.name % QStringLiteral("/translations/") % relevantFile.name;
mode = 0100644;
@ -353,6 +486,20 @@ void makeArchive(const list<PkgFileInfo> &pkgFiles, const QByteArray &pkgList, c
path = root % QStringLiteral("/bin/") % relevantFile.subDir % QChar('/') % relevantFile.name;
mode = 0100755;
break;
case RelevantFileType::Plugin:
path = root % QStringLiteral("/lib/") % relevantFile.subDir % QChar('/') % relevantFile.name;
mode = 0100755;
break;
case RelevantFileType::SharedData:
case RelevantFileType::GLib2Data:
case RelevantFileType::GLib2Schemas:
path = root % QStringLiteral("/share/") % relevantFile.subDir % QChar('/') % relevantFile.name;
mode = 0100644;
break;
case RelevantFileType::Theme:
path = root % QStringLiteral("/share/themes/") % relevantFile.subDir % QChar('/') % relevantFile.name;
mode = 0100644;
break;
case RelevantFileType::IconTheme:
path = root % QStringLiteral("/share/icons/") % relevantFile.subDir % QChar('/') % relevantFile.name;
mode = 0100644;

View File

@ -123,6 +123,9 @@ void Package::computeRequiredBy(Manager &manager)
m_requiredByComputed = true;
}
/*!
* \brief Checks whether the specified \a name and the specified \a version match the specified \a dependency.
*/
bool Package::matches(const QString &name, const QString &version, const Dependency &dependency)
{
if(name == dependency.name) {
@ -147,6 +150,25 @@ bool Package::matches(const QString &name, const QString &version, const Depende
return false;
}
/*!
* \brief Checks whether the package matches the specified \a dependency.
*/
bool Package::matches(const Dependency &dependency)
{
// check whether package matches "directly"
if(matches(name(), version(), dependency)) {
return true;
} else {
// check whether at least one of the provides matches
for(const auto &provide : provides()) {
if(matches(provide.name, provide.version, dependency)) {
return true;
}
}
return false;
}
}
/*!
* \cond
*/
@ -663,6 +685,24 @@ Dependency::Dependency(const QString &dependency)
}
}
QString Dependency::toString() const
{
switch(mode) {
case ALPM_DEP_MOD_EQ:
return name % QChar('=') % version;
case ALPM_DEP_MOD_GE:
return name % QChar('>') % QChar('=') % version;
case ALPM_DEP_MOD_LE:
return name % QChar('<') % QChar('=') % version;
case ALPM_DEP_MOD_GT:
return name % QChar('>') % version;
case ALPM_DEP_MOD_LT:
return name % QChar('<') % version;
default:
return name;
}
}
/*!
* \brief Returns a JSON object for the current instance.
*/
@ -690,6 +730,8 @@ QJsonObject Dependency::toJson() const
case ALPM_DEP_MOD_LT:
obj.insert(QStringLiteral("mod"), QStringLiteral("lt"));
break;
default:
;
}
if(!description.isEmpty()) {
obj.insert(QStringLiteral("desc"), description);

View File

@ -81,6 +81,9 @@ public:
explicit Dependency(const QString &name, const QString &version, _alpm_depmod_t mode = ALPM_DEP_MOD_ANY, const QString &description = QString());
explicit Dependency(const QString &dependency);
bool operator ==(const Dependency &other) const;
QString toString() const;
QJsonObject toJson() const;
QString name;
@ -96,6 +99,16 @@ inline Dependency::Dependency(const QString &name, const QString &version, _alpm
description(description)
{}
inline bool Dependency::operator ==(const Dependency &other) const
{
return other.name == name && other.description == description && other.mode == mode;
}
inline uint qHash(const Dependency &dependency, uint seed)
{
return qHash(dependency.name, seed) ^ qHash(dependency.version, seed) ^ qHash(dependency.mode, seed) ^ qHash(dependency.description, seed);
}
class Manager;
class Package
@ -160,6 +173,7 @@ public:
ChronoUtilities::DateTime lastModified() const;
const QString &tarUrl() const;
const std::map<QString, QByteArray> &sourceFiles() const;
QList<const QList<Dependency> *> allDependencies() const;
// version comparsion
PackageVersionComparsion compareVersion(const Package *syncPackage) const;
@ -651,6 +665,14 @@ inline const std::map<QString, QByteArray> &Package::sourceFiles() const
return m_sourceFiles;
}
/*!
* \brief Returns all dependencies.
*/
inline QList<const QList<Dependency> *> Package::allDependencies() const
{
return {&dependencies(), &makeDependencies(), &checkDependencies()};
}
/*!
* \brief Compares the version of the package with the specified sync package.
*/
@ -667,14 +689,6 @@ inline PackageVersionComparsion Package::compareVersion(const Dependency &depend
return PackageVersion(version()).compare(PackageVersion(dependency.version));
}
/*!
* \brief Checks whether the package matches the specified \a dependency.
*/
inline bool Package::matches(const Dependency &dependency)
{
return matches(name(), version(), dependency);
}
}
#endif // ALPM_PACKAGE_H

View File

@ -160,13 +160,6 @@ const Package *Repository::packageProviding(const Dependency &dependency) const
if(entry.second->matches(dependency)) {
// check whether package matches "directly"
return entry.second.get();
} else {
// check whether at least one of the provides matches
for(const auto &provide : entry.second->provides()) {
if(Package::matches(provide.name, provide.version, dependency)) {
return entry.second.get();
}
}
}
}
return nullptr;
@ -182,13 +175,6 @@ Package *Repository::packageProviding(const Dependency &dependency)
if(entry.second->matches(dependency)) {
// check whether package matches "directly"
return entry.second.get();
} else {
// check whether at least one of the provides matches
for(auto &provide : entry.second->provides()) {
if(Package::matches(provide.name, provide.version, dependency)) {
return entry.second.get();
}
}
}
}
return nullptr;
@ -202,16 +188,7 @@ QList<const Package *> Repository::packagesProviding(const Dependency &dependenc
QList<const Package *> res;
for(const auto &entry : m_packages) {
if(entry.second->matches(dependency)) {
// check whether package matches "directly"
res << entry.second.get();
} else {
// check whether at least one of the provides matches
for(const auto &provide : entry.second->provides()) {
if(Package::matches(provide.name, provide.version, dependency)) {
res << entry.second.get();
break;
}
}
}
}
return res;

View File

@ -9,6 +9,7 @@
#include <c++utilities/misc/memory.h>
#include <QStringBuilder>
#include <QEventLoop>
#include <iostream>
#include <sstream>
@ -18,14 +19,17 @@ using namespace ApplicationUtilities;
namespace RepoIndex {
/*!
* \brief The TaskInfo class defines a build task and provides the topo-sort required to resolve the build order.
*/
class TaskInfo
{
public:
TaskInfo(QString name, bool onlyDependency = false, const QList<TaskInfo *> &deps = QList<TaskInfo *>());
TaskInfo(const QString &name, bool onlyDependency = false, const QSet<TaskInfo *> &deps = QSet<TaskInfo *>());
const QString &name() const;
void setName(const QString &name);
const QList<TaskInfo *> deps() const;
const QSet<TaskInfo *> deps() const;
void addDep(TaskInfo *dep);
bool isDone() const;
bool isVisited() const;
@ -33,23 +37,29 @@ public:
void setIsOnlyDependency(bool isOnlyDependency);
bool isPackageRequested() const;
void setPackageRequested();
QSet<Dependency> requiredFor() const;
void addRequiredFor(const Dependency &dependency);
void add(QList<TaskInfo *> &results);
Package *associatedPackage() const;
void associatePackage(Package *package);
bool associatePackage(Package *package);
static void addAll(const QList<TaskInfo *> &tasks, QList<TaskInfo *> &results);
static TaskInfo *find(const QList<TaskInfo *> &tasks, const QString &name);
private:
QString m_name;
QList<TaskInfo *> m_deps;
QSet<TaskInfo *> m_deps;
bool m_done;
bool m_visited;
bool m_onlyDep;
bool m_pkgRequested;
QSet<Dependency> m_requiredFor;
Package *m_associatedPackage;
};
inline TaskInfo::TaskInfo(QString name, bool onlyDependency, const QList<TaskInfo *> &deps) :
/*!
* \brief Constructs a new task.
*/
inline TaskInfo::TaskInfo(const QString &name, bool onlyDependency, const QSet<TaskInfo *> &deps) :
m_name(name),
m_deps(deps),
m_done(false),
@ -59,6 +69,13 @@ inline TaskInfo::TaskInfo(QString name, bool onlyDependency, const QList<TaskInf
m_associatedPackage(nullptr)
{}
/*!
* \brief Returns the name of the task.
*
* This is usually the name which has been specified when constructing
* the task. However, the name might be adjusted to the package name
* when a package is associated.
*/
inline const QString &TaskInfo::name() const
{
return m_name;
@ -69,46 +86,105 @@ inline void TaskInfo::setName(const QString &name)
m_name = name;
}
inline const QList<TaskInfo *> TaskInfo::deps() const
/*!
* \brief Returns the dependencies added via addDep().
*/
inline const QSet<TaskInfo *> TaskInfo::deps() const
{
return m_deps;
}
/*!
* \brief Adds another task as dependency.
*/
inline void TaskInfo::addDep(TaskInfo *dep)
{
m_deps << dep;
}
/*!
* \brief Returns whether the task already has been added to the resulting list of tasks.
* \sa add()
*/
inline bool TaskInfo::isDone() const
{
return m_done;
}
/*!
* \brief Returns whether the task has been visited.
* \remarks Used to track circular dependencies (within add()).
*/
inline bool TaskInfo::isVisited() const
{
return m_visited;
}
/*!
* \brief Returns whether the task is only a dependency and none of the packages to be build.
* \remarks Circular dependencies between packages which are only dependencies can be ignored.
*/
inline bool TaskInfo::isOnlyDependency() const
{
return m_onlyDep;
}
/*!
* \brief Sets whether the package is only a dependency.
* \sa isOnlyDependency()
*/
inline void TaskInfo::setIsOnlyDependency(bool isOnlyDependency)
{
m_onlyDep = isOnlyDependency;
}
/*!
* \brief Returns whether the package for this task has been requested yet.
*
* Used to avoid requesting the same package twice after the package
* couldn't be found when requesting it the first time.
*/
inline bool TaskInfo::isPackageRequested() const
{
return m_pkgRequested;
}
/*!
* \brief Sets whether the package has been requested.
*
* This is set just before a package for this task is requrested.
*/
inline void TaskInfo::setPackageRequested()
{
m_pkgRequested = true;
}
/*!
* \brief Returns the dependencies the task has been added for.
* \remarks
* - The same task can be added for multiple dependencies, eg.
* mingw-w64-extra-cmake-modules and mingw-w64-extra-cmake-modules=5.15.0.
* - The dependencies must be set explicitely using addRequiredFor().
*/
inline QSet<Dependency> TaskInfo::requiredFor() const
{
return m_requiredFor;
}
/*!
* \brief Adds the specified \a dependency to the list of dependencies the task has been added for.
* \sa requiredFor()
*/
inline void TaskInfo::addRequiredFor(const Dependency &dependency)
{
m_requiredFor << dependency;
}
/*!
* \brief Adds the task to the specified list of results. Ensures that all dependencies are added before.
* \throws If a circular dependency between the build tasks has been detected, the causing task is thrown.
* \remarks Dependencies must be added via addDep() before calling this method.
*/
void TaskInfo::add(QList<TaskInfo *> &results)
{
if(!isDone()) {
@ -133,18 +209,57 @@ void TaskInfo::add(QList<TaskInfo *> &results)
}
}
/*!
* \brief Returns the package associated via associatePackage().
*/
inline Package *TaskInfo::associatedPackage() const
{
return m_associatedPackage;
}
void TaskInfo::associatePackage(Package *package)
/*!
* \brief Associates the specified \a package with this build task.
*
* Does nothing if there is already a package assigned which matches all
* dependencies the task has been added for. Returns true in this case.
*
* Fails if the specified \a package does not match all dependencies the task
* has been added for.
*
* \return Returns whether either the currenlty assigned package or the specified
* \a package matches all dependencies the task has been added for.
*/
bool TaskInfo::associatePackage(Package *package)
{
// check whether an appropriate package is already assigned
// and whether the package matches all dependencies this task is required for
bool assignmentRequired = !m_associatedPackage, assignmentPossible = true;
for(const Dependency &dep : m_requiredFor) {
if(!assignmentRequired && !m_associatedPackage->matches(dep)) {
assignmentRequired = true;
}
if(!package->matches(dep)) {
assignmentPossible = false;
}
}
if(!assignmentRequired) {
return true;
}
if(!assignmentPossible) {
return false;
}
m_associatedPackage = package;
// update the name to ensure we have the acutal package name and not just a "provides" name
m_name = package->name();
return true;
}
/*!
* \brief Adds the specified \a tasks and their dependencies to the specified list of results. Ensures that all
* tasks are added in the right order.
* \throws If a circular dependency between the build tasks has been detected, the causing task is thrown.
* \remarks Dependencies must be added to the \a tasks via addDep() before calling this method.
*/
void TaskInfo::addAll(const QList<TaskInfo *> &tasks, QList<TaskInfo *> &results)
{
for(auto *task : tasks) {
@ -152,6 +267,10 @@ void TaskInfo::addAll(const QList<TaskInfo *> &tasks, QList<TaskInfo *> &results
}
}
/*!
* \brief Finds the task with the specified \a name in the specified list of \a tasks.
* \remarks Does not search dependencies the list does not contain.
*/
TaskInfo *TaskInfo::find(const QList<TaskInfo *> &tasks, const QString &name)
{
for(auto *task : tasks) {
@ -162,104 +281,53 @@ TaskInfo *TaskInfo::find(const QList<TaskInfo *> &tasks, const QString &name)
return nullptr;
}
template<class ListType>
class DestroyList
{
public:
DestroyList(ListType &list) :
m_list(list)
{}
/*!
* \class BuildOrderResolver
* \brief The BuildOrderResolver class resolves the build order for a specified array of packages.
*
* Resolving the build order is done in the following steps:
* 1. instantiation: initial tasks are added
* 2. findDependencies(): dependencies for each task are added, some packages might have to be requested first
* 3. if some packages must be requested: wait for requested packages, if requested packages are available, go to 2.
* 4. all dependencies are added: signal ready() is emitted
* 5. resolve(): build order is resolved, either resolvingFinished() or resolvingFailed() is emitted
*/
~DestroyList()
{
qDeleteAll(m_list);
}
private:
ListType &m_list;
};
BuildOrderResolver::BuildOrderResolver(Manager &manager, const StringVector &packages, bool addSourceOnlyDeps) :
/*!
* \brief Creates a new BuildOrderResolver using the specified \a manager.
*/
BuildOrderResolver::BuildOrderResolver(Manager &manager, bool addSourceOnlyDeps) :
m_manager(manager),
m_finder(nullptr),
m_addSourceOnlyDeps(addSourceOnlyDeps)
m_addSourceOnlyDeps(addSourceOnlyDeps),
m_hasFinished(false)
{}
/*!
* \brief Adds the specified \a errorMessage.
*/
void BuildOrderResolver::addError(const QString &errorMessage)
{
cerr << shchar << "Getting package information ..." << endl;
m_tasks.clear();
m_tasks.reserve(packages.size() * 2);
// add a task for each specified package
for(const auto &pkgName : packages) {
m_tasks << new TaskInfo(QString::fromLocal8Bit(pkgName.data()));
if(!m_errorMessage.isEmpty()) {
m_errorMessage.append(QStringLiteral(" \u2192 "));
}
m_errorMessage.append(errorMessage);
}
/*!
* \brief Destroys the object.
*/
BuildOrderResolver::~BuildOrderResolver()
{
qDeleteAll(m_tasks);
}
void BuildOrderResolver::cli()
{
if(m_manager.config().isVerbose()) {
connect(this, &BuildOrderResolver::ready, this, &BuildOrderResolver::printRelevantPackages);
}
connect(this, &BuildOrderResolver::ready, this, &BuildOrderResolver::resolve);
connect(this, &BuildOrderResolver::resolvingFinished, this, &BuildOrderResolver::printResults);
connect(this, &BuildOrderResolver::resolvingFailed, static_cast<void(*)(const QString &)>(Utilities::printError));
findDependencies();
}
/*!
* \brief Finds the dependencies of all packages.
*
* Also internally called after requested packages are available.
*/
void BuildOrderResolver::findDependencies()
{
// find specified packages and their dependencies
for(int i = 0, size = m_tasks.size(); i != size; ++i) {
addDependenciesToTask(m_tasks.at(i));
}
// request dependencies to be requested
requestDependenciesToBeRequested();
}
QStringList BuildOrderResolver::resultNames()
{
QStringList names;
names.reserve(m_results.size());
for(const auto *res : m_results) {
names << res->name();
}
return names;
}
void BuildOrderResolver::printRelevantPackages()
{
cerr << shchar << "Relevant packages:";
for(const auto *task : m_tasks) {
cerr << ' ' << task->name().toLocal8Bit().data();
}
cerr << endl;
}
void BuildOrderResolver::printResults()
{
if(useShSyntax) {
Utilities::printBashArray(cout, "REPOINDEX_RESULTS", resultNames());
} else {
Utilities::printValues(cout, "Results", resultNames());
}
}
void BuildOrderResolver::resolve()
{
try {
m_results.clear();
m_results.reserve(m_tasks.size());
TaskInfo::addAll(m_tasks, m_results);
emit resolvingFinished();
} catch(const TaskInfo &cyclic) {
emit resolvingFailed(QStringLiteral("Can't resolve build order; the package ") % cyclic.name() % QStringLiteral(" is a cyclic dependency."));
}
}
void BuildOrderResolver::addRequestedPackages()
{
// find specified packages and their dependencies
for(int i = 0, size = m_tasks.size(); i != size; ++i) {
@ -271,6 +339,71 @@ void BuildOrderResolver::addRequestedPackages()
requestDependenciesToBeRequested();
}
/*!
* \brief Returns the resulting build order.
* \remarks Build order must have been resolved yet.
*/
QStringList BuildOrderResolver::resultNames()
{
QStringList names;
names.reserve(m_results.size());
for(const auto *res : m_results) {
names << res->name();
}
return names;
}
/*!
* \brief Prints relevant packages.
* \remarks All dependencies must have been found yet.
*/
void BuildOrderResolver::printRelevantPackages()
{
cerr << shchar << "Relevant packages:";
for(const auto *task : m_tasks) {
cerr << ' ' << task->name().toLocal8Bit().data();
}
cerr << endl;
}
/*!
* \brief Prints the resulting build order.
* \remarks Build order must have been resolved yet.
*/
void BuildOrderResolver::printResults()
{
if(useShSyntax) {
Utilities::printBashArray(cout, "REPOINDEX_RESULTS", resultNames());
} else {
Utilities::printValues(cout, "Results", resultNames());
}
}
/*!
* \brief Resolves the build order.
* \remarks All dependencies must have been found yet.
*
* Emits either resolvingFinished() or resolvingFailed() depending on whether
* the operation succeeded.
*/
void BuildOrderResolver::resolve()
{
try {
m_results.clear();
m_results.reserve(m_tasks.size());
TaskInfo::addAll(m_tasks, m_results);
m_hasFinished = true;
emit resolvingFinished();
} catch(const TaskInfo &cyclic) {
addError(QStringLiteral("Can't resolve build order; the package ") % cyclic.name() % QStringLiteral(" is a cyclic dependency."));
m_hasFinished = true;
emit resolvingFailed(m_errorMessage);
}
}
/*!
* \brief Returns the package for the specified \a dependency or nullptr if no package could be found.
*/
Package *BuildOrderResolver::findPackageForDependency(const Dependency &dependency)
{
Package *pkg;
@ -283,7 +416,7 @@ Package *BuildOrderResolver::findPackageForDependency(const Dependency &dependen
}
/*!
* \brief Finds the package for the specified \a task and then adds dependencies.
* \brief Finds the package for the specified \a task and then adds dependencies recursively.
*/
bool BuildOrderResolver::addDependenciesToTask(TaskInfo *task)
{
@ -294,17 +427,31 @@ bool BuildOrderResolver::addDependenciesToTask(TaskInfo *task)
}
Dependency dep(task->name(), QString());
if(const auto pkg = findPackageForDependency(dep)) {
task->associatePackage(pkg);
if(m_addSourceOnlyDeps && pkg->repository()->isSourceOnly()) {
task->setIsOnlyDependency(false);
task->addRequiredFor(dep);
if(task->associatePackage(pkg)) {
if(m_addSourceOnlyDeps && pkg->repository()->isSourceOnly()) {
task->setIsOnlyDependency(false);
}
// add dependencies to task
if(!addDependenciesToTask(task, pkg->allDependencies())) {
addError(QStringLiteral("Can not add dependencies of the dependency \"") % dep.toString() % QStringLiteral("\"."));
m_hasFinished = true;
emit resolvingFailed(errorMessage());
return false;
}
} else {
addError(QStringLiteral("Can not associate the task \"") % task->name() % QStringLiteral("\" with the package required by dependency \"") % dep.toString() % QStringLiteral("\"."));
m_hasFinished = true;
emit resolvingFailed(errorMessage());
return false;
}
// add dependencies to task
addDependenciesToTask(task, pkg->dependencies());
} else if(!task->isPackageRequested()) {
task->setPackageRequested();
m_dependenciesToBeRequested << dep;
} else {
emit resolvingFailed(QStringLiteral("The specified package \"") % task->name() % QStringLiteral("\" could not be found."));
addError(QStringLiteral("The specified package \"") % task->name() % QStringLiteral("\" could not be found."));
m_hasFinished = true;
emit resolvingFailed(errorMessage());
return false;
}
return true;
@ -312,38 +459,53 @@ bool BuildOrderResolver::addDependenciesToTask(TaskInfo *task)
/*!
* \brief Finds packages for the the specified \a dependencies and adds them to the specified \a task.
* \remarks Adds dependencies of the dependencies recursively.
*/
bool BuildOrderResolver::addDependenciesToTask(TaskInfo *task, const QList<Dependency> &dependencies)
bool BuildOrderResolver::addDependenciesToTask(TaskInfo *task, const QList<const QList<Dependency> *> &dependencies)
{
for(auto &dep : dependencies) {
const auto depPkg = findPackageForDependency(dep);
const QString taskName = depPkg ? depPkg->name() : dep.name;
auto *depTask = TaskInfo::find(m_tasks, taskName);
if(depTask) {
// we've already added a task for this dependency
// -> add dependency task to the dependencies of "parent" task
task->addDep(depTask);
} else {
// create new task
m_tasks << (depTask = new TaskInfo(taskName, true));
// adds dependency task to the dependencies of "parent" task
task->addDep(depTask);
if(depPkg) {
depTask->associatePackage(depPkg);
if(m_addSourceOnlyDeps && depPkg->repository()->isSourceOnly()) {
depTask->setIsOnlyDependency(false);
}
// add dependencies of the dependency
addDependenciesToTask(depTask, depPkg->dependencies());
}
}
if(!depPkg) {
if(!depTask->isPackageRequested()) {
depTask->setPackageRequested();
m_dependenciesToBeRequested << dep;
for(auto *deps : dependencies) {
for(auto &dep : *deps) {
auto *depPkg = findPackageForDependency(dep);
const QString taskName = depPkg ? depPkg->name() : dep.name;
auto *depTask = TaskInfo::find(m_tasks, dep.name);
bool newTask;
if(depTask) {
// we've already added a task for this dependency
newTask = false;
} else {
emit resolvingFailed(QStringLiteral("The specified package \"") % task->name() % QStringLiteral("\" could not be found."));
return false;
// create new task
m_tasks << (depTask = new TaskInfo(taskName, true));
newTask = true;
}
// add dependency task to the dependencies of "parent" task
task->addDep(depTask);
depTask->addRequiredFor(dep);
if(depPkg) {
if(depTask->associatePackage(depPkg)) {
if(m_addSourceOnlyDeps && depPkg->repository() && depPkg->repository()->isSourceOnly()) {
depTask->setIsOnlyDependency(false);
}
if(newTask) {
// add dependencies of the dependency
if(!addDependenciesToTask(depTask, depPkg->allDependencies())) {
addError(QStringLiteral("Can not add dependencies of the dependency \"") % dep.toString() % QStringLiteral("\"."));
return false;
}
}
} else {
addError(QStringLiteral("Can not associate the task \"") % depTask->name() % QStringLiteral("\" with the package required by dependency \"") % dep.toString() % QStringLiteral("\"."));
return false;
}
} else {
if(!m_dependenciesToBeRequested.contains(dep)) {
if(!depTask->isPackageRequested()) {
depTask->setPackageRequested();
m_dependenciesToBeRequested << dep;
} else {
addError(QStringLiteral("The specified package \"") % dep.toString() % QStringLiteral("\" could not be found."));
return false;
}
}
}
}
}
@ -375,10 +537,10 @@ void BuildOrderResolver::requestDependenciesToBeRequested()
// add results
if(m_finder->areAllResultsAvailable()) {
// results are immediately available (already cached)
addRequestedPackages();
findDependencies();
} else {
// need to request actually
connect(m_finder.get(), &PackageFinder::resultsAvailable, this, &BuildOrderResolver::addRequestedPackages);
connect(m_finder.get(), &PackageFinder::resultsAvailable, this, &BuildOrderResolver::findDependencies);
}
} else {
@ -387,5 +549,50 @@ void BuildOrderResolver::requestDependenciesToBeRequested()
}
}
/*!
* \class BuildOrderResolverCli
* \brief The BuildOrderResolverCli class resolves the build order of the specified packages and prints the results stdout.
*/
/*!
* \brief Creates a new BuildOrderResolverCli for the specified \a packages using the specified \a manager.
*/
BuildOrderResolverCli::BuildOrderResolverCli(Manager &manager, const StringVector &packages, bool addSourceOnlyDeps) :
BuildOrderResolver(manager, addSourceOnlyDeps)
{
cerr << shchar << "Getting package information ..." << endl;
tasks().clear();
tasks().reserve(packages.size() * 2);
// add a task for each specified package
for(const auto &pkgName : packages) {
tasks() << new TaskInfo(QString::fromLocal8Bit(pkgName.data()));
}
}
/*!
* \brief Determines dependencies, prints relevant packages, resolves build order, prints results/errors.
* \remarks Does not return until everything is done.
*/
int BuildOrderResolverCli::exec()
{
if(manager().config().isVerbose()) {
connect(this, &BuildOrderResolver::ready, this, &BuildOrderResolver::printRelevantPackages);
}
connect(this, &BuildOrderResolver::ready, this, &BuildOrderResolver::resolve);
connect(this, &BuildOrderResolver::resolvingFinished, this, &BuildOrderResolver::printResults);
connect(this, &BuildOrderResolver::resolvingFailed, static_cast<void(*)(const QString &)>(Utilities::printError));
QEventLoop loop;
connect(this, &BuildOrderResolver::resolvingFinished, &loop, &QEventLoop::quit);
connect(this, &BuildOrderResolver::resolvingFailed, &loop, &QEventLoop::quit);
findDependencies();
if(hasFinished()) {
// resolving might have been finished immidiately
return errorMessage().isEmpty();
} else {
// resolving not finished yet (deps must be requested)
return loop.exec();
}
}
} // namespace PackageManagement

View File

@ -21,15 +21,14 @@ class BuildOrderResolver : public QObject
{
Q_OBJECT
public:
BuildOrderResolver(Manager &manager, const ApplicationUtilities::StringVector &packages, bool addSourceOnlyDeps = false);
~BuildOrderResolver();
void cli();
void findDependencies();
QStringList resultNames();
bool hasFinished() const;
const QString &errorMessage() const;
public slots:
void findDependencies();
void printRelevantPackages();
void printResults();
void resolve();
@ -39,13 +38,16 @@ signals:
void resolvingFinished();
void resolvingFailed(const QString &message);
private slots:
void addRequestedPackages();
protected:
BuildOrderResolver(Manager &manager, bool addSourceOnlyDeps = false);
Manager &manager();
QList<TaskInfo *> &tasks();
private:
void addError(const QString &errorMessage);
Package *findPackageForDependency(const Dependency &dependency);
bool addDependenciesToTask(TaskInfo *task);
bool addDependenciesToTask(TaskInfo *task, const QList<Dependency> &dependencies);
bool addDependenciesToTask(TaskInfo *task, const QList<const QList<Dependency> *> &dependencies);
void requestDependenciesToBeRequested();
Manager &m_manager;
@ -54,6 +56,49 @@ private:
std::unique_ptr<PackageFinder> m_finder;
QList<TaskInfo *> m_results;
bool m_addSourceOnlyDeps;
bool m_hasFinished;
QString m_errorMessage;
};
/*!
* \brief Returns whether adding dependencies/resolving the build order has been finished.
* \remarks Also true when an error occured. In this case errorMessage() is not empty.
*/
inline bool BuildOrderResolver::hasFinished() const
{
return m_hasFinished;
}
/*!
* \brief Returns the error message if an error occured.
*/
inline const QString &BuildOrderResolver::errorMessage() const
{
return m_errorMessage;
}
/*!
* \brief Returns the manager used to find packages.
*/
inline Manager &BuildOrderResolver::manager()
{
return m_manager;
}
/*!
* \brief Returns the tasks.
*/
inline QList<TaskInfo *> &BuildOrderResolver::tasks()
{
return m_tasks;
}
class BuildOrderResolverCli : public BuildOrderResolver
{
Q_OBJECT
public:
BuildOrderResolverCli(Manager &manager, const ApplicationUtilities::StringVector &packages, bool addSourceOnlyDeps = false);
int exec();
};
} // namespace PackageManagement

View File

@ -77,12 +77,8 @@ int main(int argc, char *argv[])
// run Qt loop
return application.exec();
} else if(configArgs.buildOrderArg.isPresent()) {
BuildOrderResolver resolver(manager, configArgs.buildOrderArg.values(), configArgs.addSourceOnlyDeps.isPresent());
resolver.cli();
QObject::connect(&resolver, &BuildOrderResolver::resolvingFinished, &application, &QCoreApplication::quit);
QObject::connect(&resolver, &BuildOrderResolver::resolvingFailed, &application, &QCoreApplication::quit);
// run Qt loop
return application.exec();
BuildOrderResolverCli resolver(manager, configArgs.buildOrderArg.values(), configArgs.addSourceOnlyDeps.isPresent());
return resolver.exec();
//BuildOrderResolver::printResults(resolver.resolve(configArgs.buildOrderArg.values()));
} else if(configArgs.mingwBundleArg.isPresent()) {
MingwBundle bundle(manager, configArgs.mingwBundleArg.values(), configArgs.iconThemesArg.values(), configArgs.extraPackagesArg.values());

View File

@ -4,7 +4,7 @@ appname = "Repository Index"
appauthor = Martchus
appurl = "https://github.com/$${appauthor}/$${projectname}"
QMAKE_TARGET_DESCRIPTION = "Provides a web interface to browse Arch Linux package repositories."
VERSION = 1.0.0
VERSION = 0.0.1
# include ../../common.pri when building as part of a subdirs project; otherwise include general.pri
!include(../../common.pri) {

View File

@ -50,7 +50,7 @@ span.glyphicon {
background-color: #f9f9db!important;
cursor: pointer;
}
#info_container th, #info_container td {
.info-container th, .info-container td {
border: none!important;
font-size: 90%;
padding: 5px;
@ -95,10 +95,10 @@ span.glyphicon {
/*
* Package info
*/
#info_container {
.info-container {
margin-left: 5px;
}
#info_container {
.info-container {
float: none;
width: 100%;
}
@ -110,6 +110,13 @@ span.glyphicon {
.file-tree ul ul {
margin-left: 5px;
}
#multi_package_info_container > div {
float: left;
width: 350px;
}
#packages_info {
clear: both;
}
/*
* Tree

View File

@ -44,7 +44,7 @@
<li id="nav_packages" class="active"><a href="#packages">Packages <span class="sr-only">(current)</span></a></li>
<li id="nav_groups"><a href="#groups">Groups</a></li>
<li id="nav_repositories"><a href="#repositories">Repositories</a></li>
<li id="nav_settings"><a id="link_settings" href="#" aria-label="Settings" data-toggle="popover" data-placement="bottom" title="Settings" data-content="TODO"><span class="glyphicon glyphicon-wrench" aria-hidden="true"></span></a></li>
<li id="nav_settings"><a id="link_settings" href="#" onclick="return false;" aria-label="Settings" data-toggle="popover" data-placement="bottom" title="Settings" data-content="TODO"><span class="glyphicon glyphicon-wrench" aria-hidden="true"></span></a></li>
<li id="nav_about"><a id="link_settings" href="#" onclick="$('#dlg_about').modal('show'); return false;"><span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span></a></li>
</ul>
<form class="navbar-form navbar-left" target="#" onsubmit="repoindex.pageManager.applySearchTerm(this.searchtermInput.value, this.searchtermExact.checked, true); return false;">
@ -112,6 +112,7 @@
</div>
<!-- begin of packages page -->
<div id="page_packages" class="row">
<div id="multi_package_info_container"><!-- package info panel generated by JavaScript --></div>
<!-- left column -->
<div id="left_column_container_packages">
<!-- package list -->
@ -138,25 +139,8 @@
</div>
<!-- right column -->
<div id="right_column_container_packages">
<!-- package info -->
<div id="info_container" class="panel panel-default">
<div class="panel-heading">
Package info
<button type="button" class="close" onclick="repoindex.pageManager.hidePackageInfo();" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="panel-body">
<!-- package info table (stub) -->
<table class="table">
<colgroup>
<col style="width:30%">
<col style="width:70%">
</colgroup>
<tbody id="pkg_tbody"></tbody>
</table>
</div>
</div>
<!-- package info container (right column) -->
<div id="single_package_info_container"><!-- package info panel generated by JavaScript --></div>
</div>
</div>
<!-- begin of groups page -->
@ -195,24 +179,8 @@
<nav class="pagination-nav"><ul class="pagination pagination-sm" id="repos_pagination"><!-- generated by JavaScript --></ul></nav>
</div>
<div id="right_column_container_repos">
<div id="info_container" class="panel panel-default">
<div class="panel-heading">
Repository info
<button type="button" class="close" onclick="repoindex.pageManager.hideRepoInfo();" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="panel-body">
<!-- repo info table (stub) -->
<table class="table">
<colgroup>
<col style="width:30%">
<col style="width:70%">
</colgroup>
<tbody id="repo_tbody"></tbody>
</table>
</div>
</div>
<!-- repo info container (right column) -->
<div id="single_repo_info_container"><!-- repo info panel generated by JavaScript --></div>
</div>
</div>
</div>
@ -261,7 +229,7 @@
</div>
<div class="modal-body" style="text-align: center;">
<h2>Repository Index</h2>
<p>1.0.0</p>
<p>@META_APP_VERSION_STR@</p>
<p style="margin: 25px 0px;">
<em>Tool to browse Arch Linux repositories.</em><br>
<a href="https://github.com/Martchus/" onclick="window.open(this.href); return false;">Project website</a>

View File

@ -120,12 +120,16 @@
// check whether specified entry index is valid
var entry = this.entryByIndex(entryIndex);
if(entry) {
// show properties in "Package info" box
// show properties
var setProperties = function() {
// -> find info container and make info table
var infoContainer = document.getElementById("single_package_info_container");
infoContainer.wipeChildren();
var infoTable = repoindex.makeInfoTable();
var tb = infoTable.tbodyElement;
// -> basic package info
var basics = entry.info.basics ? entry.info.basics : {};
var tb = document.getElementById("pkg_tbody");
tb.wipeChildren();
repoindex.addPackageNames(tb, "Name", [entry.name]);
switch(entry.info.repo) {
case "AUR":
@ -197,6 +201,11 @@
downloadElement.appendChild(spanElement);
}
}
// -> make info panel
var infoPanel = repoindex.makeInfoPanel("Package info", repoindex.bind(repoindex.pageManager, repoindex.pageManager.hidePackageInfo));
infoPanel.bodyElement.appendChild(infoTable.tableElement);
infoContainer.appendChild(infoPanel.element);
};
setProperties();
if(!entry.info.basics || !entry.info.details) {

View File

@ -33,6 +33,7 @@
this.rightColumnContainerPackages = document.getElementById("right_column_container_packages");
this.leftColumnContainerRepos = document.getElementById("left_column_container_repos");
this.rightColumnContainerRepos = document.getElementById("right_column_container_repos");
this.packageInfoContainer = document.getElementById("container_package_info");
// provide a function to add an error message (shown in red box)
this.addError = function(msg) {
@ -281,14 +282,14 @@
this.setPage(repoindex.Pages.Packages);
// ensure right column is visible
this.showRightColumn(this.leftColumnContainerPackages, this.rightColumnContainerPackages);
var tb = document.getElementById("pkg_tbody");
if(tb.lastChild) {
if(scroll && !checkVisibility(tb.lastChild)) {
$('html, body').animate({
scrollTop: $("#" + this.rightColumnContainerPackages.id).offset().top
}, 500);
}
}
//var tb = document.getElementById("pkg_tbody");
//if(tb.lastChild) {
// if(scroll && !checkVisibility(tb.lastChild)) {
// $('html, body').animate({
// scrollTop: $("#" + this.rightColumnContainerPackages.id).offset().top
// }, 500);
// }
//}
};
this.hideRepoInfo = function() {

View File

@ -180,9 +180,13 @@
this.showRepoInfoForIndex = function(entryIndex) {
var entry = this.entryByIndex(entryIndex);
if(entry) {
// -> find info container and make info table
var infoContainer = document.getElementById("single_repo_info_container");
infoContainer.wipeChildren();
var infoTable = repoindex.makeInfoTable();
var tb = infoTable.tbodyElement;
var info = entry.info;
var tb = document.getElementById("repo_tbody");
tb.wipeChildren();
repoindex.addField(tb, "Name", repoindex.makeStr(entry.name));
repoindex.addField(tb, "Description", repoindex.makeStr(info.desc));
repoindex.addField(tb, "Package count", repoindex.makeStr(info.packageCount));
@ -199,6 +203,11 @@
this.currentInfo = info;
// ensures, that the "Package Info" box (with the properties just set) is shown
repoindex.pageManager.showRepoInfo();
// -> make info panel
var infoPanel = repoindex.makeInfoPanel("Repository info", repoindex.bind(repoindex.pageManager, repoindex.pageManager.hideRepoInfo));
infoPanel.bodyElement.appendChild(infoTable.tableElement);
infoContainer.appendChild(infoPanel.element);
}
};

View File

@ -301,6 +301,67 @@
repoindex.setPackageNames(repoindex.addField(element, fieldName), packageNames, pageName);
};
repoindex.makeInfoPanel = function(headingText, closeFunc) {
var panelElement = document.createElement("div");
panelElement.className = "panel panel-default info-container";
var panelHeadingElement = document.createElement("div");
panelHeadingElement.className = "panel-heading";
panelHeadingElement.appendChild(document.createTextNode(headingText));
var closeButton = document.createElement("button");
closeButton.setAttribute("type", "button");
closeButton.setAttribute("class", "close");
closeButton.setAttribute("aria-label", "Close");
closeButton.onclick = function() {
if(panelElement.parentNode) {
panelElement.parentNode.removeChild(panelElement);
if(closeFunc) {
closeFunc();
}
}
};
var closeButtonSpan = document.createElement("span");
closeButtonSpan.setAttribute("aria-hidden", "true");
closeButtonSpan.appendChild(document.createTextNode("\u00D7"));
closeButton.appendChild(closeButtonSpan);
panelHeadingElement.appendChild(closeButton);
panelElement.appendChild(panelHeadingElement);
var panelBodyElement = document.createElement("div");
panelBodyElement.className = "panel-body";
panelElement.appendChild(panelBodyElement);
return {
element: panelElement,
headingElement: panelHeadingElement,
bodyElement: panelBodyElement
};
};
repoindex.makeColumnGroup = function(columnWidths) {
var colgroupElement = document.createElement("colgroup");
for(var i = 0; i < columnWidths.length; ++i) {
var colElement = document.createElement("col");
colElement.setAttribute("style", "width:" + columnWidths[i]);
colgroupElement.appendChild(colElement);
}
return colgroupElement;
};
repoindex.makeInfoTable = function() {
var tableElement = document.createElement("table");
tableElement.appendChild(repoindex.makeColumnGroup(["30%", "70%"]));
var tbodyElement = document.createElement("tbody");
tableElement.appendChild(tbodyElement);
return {
tableElement: tableElement,
tbodyElement: tbodyElement
};
};
repoindex.bind = function(obj, func, args) {
return function() {
func.call(obj, args);
}
}
return repoindex;
})(repoindex || {});