repoindex/alpm/resolvebuildorder.cpp

225 lines
6.0 KiB
C++
Raw Normal View History

#include "resolvebuildorder.h"
#include "manager.h"
#include "config.h"
#include <c++utilities/misc/memory.h>
#include <iostream>
#include <sstream>
using namespace std;
using namespace ApplicationUtilities;
namespace RepoIndex {
class TaskInfo
{
public:
TaskInfo(QString name, bool onlyDependency = false, const QList<TaskInfo *> &deps = QList<TaskInfo *>());
const QString &name() const;
void setName(const QString &name);
const QList<TaskInfo *> deps() const;
void addDep(TaskInfo *dep);
bool isDone() const;
bool isVisited() const;
bool isOnlyDependency() const;
void add(QList<TaskInfo *> &results);
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;
bool m_done;
bool m_visited;
bool m_onlyDep;
};
inline TaskInfo::TaskInfo(QString name, bool onlyDependency, const QList<TaskInfo *> &deps) :
m_name(name),
m_deps(deps),
m_done(false),
m_visited(false),
m_onlyDep(onlyDependency)
{}
inline const QString &TaskInfo::name() const
{
return m_name;
}
inline void TaskInfo::setName(const QString &name)
{
m_name = name;
}
inline const QList<TaskInfo *> TaskInfo::deps() const
{
return m_deps;
}
inline void TaskInfo::addDep(TaskInfo *dep)
{
m_deps << dep;
}
inline bool TaskInfo::isDone() const
{
return m_done;
}
inline bool TaskInfo::isVisited() const
{
return m_visited;
}
inline bool TaskInfo::isOnlyDependency() const
{
return m_onlyDep;
}
void TaskInfo::add(QList<TaskInfo *> &results)
{
if(!isDone()) {
if(isVisited()) {
// cyclic dependency
if(isOnlyDependency()) {
// if this is only a dependency (which we don't want to build) don't care about it
return;
} else {
throw *this;
}
} else {
m_visited = true;
}
for(auto *dep : deps()) {
dep->add(results);
}
m_done = true;
if(!isOnlyDependency()) {
results << this;
}
}
}
void TaskInfo::addAll(const QList<TaskInfo *> &tasks, QList<TaskInfo *> &results)
{
for(auto *task : tasks) {
task->add(results);
}
}
TaskInfo *TaskInfo::find(const QList<TaskInfo *> &tasks, const QString &name)
{
for(auto *task : tasks) {
if(task->name() == name) {
return task;
}
}
return nullptr;
}
template<class ListType>
class DestroyList
{
public:
DestroyList(ListType &list) :
m_list(list)
{}
~DestroyList()
{
qDeleteAll(m_list);
}
private:
ListType &m_list;
};
BuildOrderResolver::BuildOrderResolver(const Manager &manager) :
m_manager(manager)
{}
QStringList BuildOrderResolver::resolve(const StringVector &packages) const
{
cerr << shchar << "Getting package information ..." << endl;
QList<TaskInfo *> tasks;
tasks.reserve(packages.size());
try {
// add a task for each specified package
for(const auto &pkgName : packages) {
tasks << new TaskInfo(QString::fromLocal8Bit(pkgName.data()));
}
// find specified packages and their dependencies
for(int i = 0, size = tasks.size(); i != size; ++i) {
addDeps(tasks, tasks.at(i));
}
if(m_manager.config().isVerbose()) {
cerr << shchar << "Relevant packages: ";
for(const auto *task : tasks) {
cerr << task->name().toLocal8Bit().data() << ' ';
}
cerr << endl;
}
// topo sort
QList<TaskInfo *> results;
try {
results.reserve(packages.size());
TaskInfo::addAll(tasks, results);
QStringList names;
names.reserve(results.size());
for(const auto *res : results) {
names << res->name();
}
return names;
} catch (const TaskInfo &cyclic) {
throw runtime_error("Can't resolve build order; the package " + cyclic.name().toStdString() + " is a cyclic dependency.");
}
} catch(...) {
qDeleteAll(tasks);
throw;
}
}
void BuildOrderResolver::addDeps(QList<TaskInfo *> &tasks, TaskInfo *task) const
{
if(const auto pkg = m_manager.packageProviding(Dependency(task->name()))) {
task->setName(pkg->name()); // update the name to ensure we have the acutal package name and not just a "provides" name
addDeps(tasks, task, pkg->dependencies());
} else {
stringstream ss;
ss << "The specified package \"" << task->name().toLocal8Bit().data() << "\" could not be found; TODO: search AUR for package, add AUR deps to the packages we want to build";
throw runtime_error(ss.str());
}
}
void BuildOrderResolver::addDeps(QList<TaskInfo *> &tasks, TaskInfo *task, const QList<Dependency> &dependencies) const
{
for(auto &dep : dependencies) {
if(const auto depPkg = m_manager.packageProviding(dep)) {
auto *depTask = TaskInfo::find(tasks, depPkg->name());
if(depTask) {
// we've already added a task for this dependency
// adds dependency task to the dependencies of "parent" task
task->addDep(depTask);
} else {
// create new task
tasks << (depTask = new TaskInfo(depPkg->name(), true));
// adds dependency task to the dependencies of "parent" task
task->addDep(depTask);
// add dependencies of the dependency
addDeps(tasks, depTask, depPkg->dependencies());
}
} else {
stringstream ss;
ss << "The dependency \"" << dep.name.toLocal8Bit().data() << "\" could not be found; TODO: search AUR for package, add AUR deps to the packages we want to build";
throw runtime_error(ss.str());
}
}
}
} // namespace PackageManagement