From 6302e8e73987ce0329ceb211164cb2cf9dc397af Mon Sep 17 00:00:00 2001 From: WrinklyNinja Date: Thu, 6 Jun 2013 08:26:23 +0100 Subject: [PATCH] Started work on issue #10. If a circular dependency is detected, sorting should not take place, since it would get stuck in an infinite loop. Consistency checks are unoptimised. --- src/backend/metadata.cpp | 93 +++++++++++++++++++++++++++++++++++++--- src/backend/metadata.h | 5 +++ src/gui/main.cpp | 13 +++++- 3 files changed, 103 insertions(+), 8 deletions(-) diff --git a/src/backend/metadata.cpp b/src/backend/metadata.cpp index 6317838b..fa19cb25 100644 --- a/src/backend/metadata.cpp +++ b/src/backend/metadata.cpp @@ -500,13 +500,9 @@ namespace boss { } bool Plugin::MustLoadAfter(const Plugin& plugin) const { - for (vector::const_iterator it=masters.begin(), endIt=masters.end(); it != endIt; ++it) { - if (boost::iequals(*it, plugin.Name())) - return true; - } - if (find(requirements.begin(), requirements.end(), plugin) != requirements.end()) - return true; - if (find(loadAfter.begin(), loadAfter.end(), plugin) != loadAfter.end()) + if (find(masters.begin(), masters.end(), plugin) != masters.end() + || find(requirements.begin(), requirements.end(), plugin) != requirements.end() + || find(loadAfter.begin(), loadAfter.end(), plugin) != loadAfter.end()) return true; return false; } @@ -530,9 +526,92 @@ namespace boss { return issues; } + std::map Plugin::CheckSelfConsistency(const list& plugins, std::set branch, set incs, set reqs) const { + map issues; + //Add this plugin and its incompatibilities to the set. + if (!branch.insert(boost::to_lower_copy(name)).second) { + issues.insert(pair(name, false)); + return issues; //Prevent infinite recursion. + } + for (set::const_iterator it=incompatibilities.begin(), endit=incompatibilities.end(); it != endit; ++it) { + incs.insert(boost::to_lower_copy(it->Name())); + } + for (vector::const_iterator it=masters.begin(), endit=masters.end(); it != endit; ++it) { + reqs.insert(boost::to_lower_copy(*it)); + } + for (set::const_iterator it=requirements.begin(), endit=requirements.end(); it != endit; ++it) { + reqs.insert(boost::to_lower_copy(it->Name())); + } + for (set::const_iterator it=loadAfter.begin(), endIt=loadAfter.end(); it != endIt; ++it) { + reqs.insert(boost::to_lower_copy(it->Name())); + } + + //Check 1: None of this plugin's masters or requirements or 'load after' plugins may be in branch. + //Check 2: None of this plugin's masters or requirements or 'load after' plugins may be in incs. + //Check 3: None of this plugin's incompatibilities may be present in branch. + //Check 4: This plugin must not be present in incs. + //Check 5: This plugin must not be present in branch (performed above). + //Also check the consistency of the plugins this plugin is dependent on. + for (vector::const_iterator it=masters.begin(), endit=masters.end(); it != endit; ++it) { + if (branch.find(boost::to_lower_copy(*it)) != branch.end()) + issues.insert(pair(*it, true)); + if (incs.find(boost::to_lower_copy(*it)) != incs.end()) + issues.insert(pair(*it, false)); + list::const_iterator pluginIt = find(plugins.begin(), plugins.end(), *it); + if (pluginIt != plugins.end()) { + map childIssues = pluginIt->CheckSelfConsistency(plugins, branch, incs, reqs); + issues.insert(childIssues.begin(), childIssues.end()); + } + } + for (set::const_iterator it=requirements.begin(), endIt=requirements.end(); it != endIt; ++it) { + if (branch.find(boost::to_lower_copy(it->Name())) != branch.end()) + issues.insert(pair(it->Name(), true)); + if (incs.find(boost::to_lower_copy(it->Name())) != incs.end()) + issues.insert(pair(it->Name(), false)); + list::const_iterator pluginIt = find(plugins.begin(), plugins.end(), *it); + if (pluginIt != plugins.end()) { + map childIssues = pluginIt->CheckSelfConsistency(plugins, branch, incs, reqs); + issues.insert(childIssues.begin(), childIssues.end()); + } + } + for (set::const_iterator it=loadAfter.begin(), endIt=loadAfter.end(); it != endIt; ++it) { + if (branch.find(boost::to_lower_copy(it->Name())) != branch.end()) + issues.insert(pair(it->Name(), true)); + if (incs.find(boost::to_lower_copy(it->Name())) != incs.end()) + issues.insert(pair(it->Name(), false)); + list::const_iterator pluginIt = find(plugins.begin(), plugins.end(), *it); + if (pluginIt != plugins.end()) { + map childIssues = pluginIt->CheckSelfConsistency(plugins, branch, incs, reqs); + issues.insert(childIssues.begin(), childIssues.end()); + } + } + for (set::const_iterator it=incompatibilities.begin(), endIt=incompatibilities.end(); it != endIt; ++it) { + if (branch.find(boost::to_lower_copy(it->Name())) != branch.end()) + issues.insert(pair(it->Name(), false)); + if (reqs.find(boost::to_lower_copy(it->Name())) != reqs.end()) + issues.insert(pair(it->Name(), false)); + } + if (incs.find(boost::to_lower_copy(name)) != incs.end()) + issues.insert(pair(name, false)); + + return issues; + } + bool operator == (const File& lhs, const Plugin& rhs) { return boost::iequals(lhs.Name(), rhs.Name()); } + + bool operator == (const Plugin& lhs, const File& rhs) { + return rhs == lhs; + } + + bool operator == (const std::string& lhs, const Plugin& rhs) { + return boost::iequals(lhs, rhs.Name()); + } + + bool operator == (const Plugin& lhs, const std::string& rhs) { + return rhs == lhs; + } bool alpha_sort(const Plugin& lhs, const Plugin& rhs) { return boost::ilexicographical_compare(lhs.Name(), rhs.Name()); diff --git a/src/backend/metadata.h b/src/backend/metadata.h index 4ad654f8..ec736027 100644 --- a/src/backend/metadata.h +++ b/src/backend/metadata.h @@ -164,6 +164,7 @@ namespace boss { //Validity checks. std::map CheckInstallValidity(const Game& game) const; //Checks that reqs and masters are all present, and that no incs are present. Returns a map of filenames and whether they are missing (if bool is true, then filename is a req or master, otherwise it's an inc). + std::map CheckSelfConsistency(const std::list& plugins, std::set branch, std::set incs, std::set reqs) const; private: std::string name; bool enabled; //Default to true. @@ -181,6 +182,10 @@ namespace boss { }; bool operator == (const File& lhs, const Plugin& rhs); + + bool operator == (const Plugin& lhs, const File& rhs); + + bool operator == (const std::string& lhs, const Plugin& rhs); bool alpha_sort(const Plugin& lhs, const Plugin& rhs); diff --git a/src/gui/main.cpp b/src/gui/main.cpp index f587eee5..8b0d492e 100644 --- a/src/gui/main.cpp +++ b/src/gui/main.cpp @@ -437,8 +437,19 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) { progDia->Pulse(); + //Check that the metadata is self-consistent. + set dependencies, incompatibilities, reqs; + map issues; + issues = it->CheckSelfConsistency(plugins, dependencies, incompatibilities, reqs); + for (map::const_iterator jt=issues.begin(), endJt=issues.end(); jt != endJt; ++jt) { + if (jt->second) + messages.push_back(boss::Message(boss::MESSAGE_ERROR, "For \"" + it->Name() + "\", \"" + jt->first + "\" is a circular dependency.")); + else + messages.push_back(boss::Message(boss::MESSAGE_ERROR, "For \"" + it->Name() + "\", \"" + jt->first + "\" is given as a dependency and an incompatibility.")); + } + //Also check install validity. - map issues = it->CheckInstallValidity(_game); + issues = it->CheckInstallValidity(_game); list messages = it->Messages(); for (map::const_iterator jt=issues.begin(), endJt=issues.end(); jt != endJt; ++jt) { if (jt->second)