mirror of
https://github.com/loot/libloot.git
synced 2026-07-27 14:16:01 -07:00
Re-implemented sorting and graph output, and added a cycle check.
This commit is contained in:
+41
-7
@@ -93,7 +93,8 @@ namespace boss {
|
||||
boost::tie(vit, vit_end) = boost::vertices(graph);
|
||||
|
||||
for (vit, vit_end; vit != vit_end; ++vit) {
|
||||
if (boost::iequals(graph[*vit]->Name(), name)) {
|
||||
// if (boost::iequals(graph[*vit]->Name(), name)) {
|
||||
if (graph[*vit]->Name() == name) {
|
||||
vertex = *vit;
|
||||
return true;
|
||||
}
|
||||
@@ -104,22 +105,41 @@ namespace boss {
|
||||
|
||||
void SaveGraph(const PluginGraph& graph, const boost::filesystem::path outpath) {
|
||||
//First need to extract vertex names, since their stored as private members otherwise.
|
||||
/* vector<string> names;
|
||||
vector<string> names;
|
||||
vertex_it vit, vit_end;
|
||||
boost::tie(vit, vit_end) = boost::vertices(graph);
|
||||
|
||||
for (vit, vit_end; vit != vit_end; ++vit) {
|
||||
names.push_back(graph[*vit]->Name());
|
||||
}
|
||||
|
||||
//Also, writing the graph requires an index map, which std::list-based VertexList graphs don't have, so one needs to be built separately.
|
||||
|
||||
map<vertex_t, string> index_map;
|
||||
boost::associative_property_map< map<vertex_t, string> > v_index_map(index_map);
|
||||
BGL_FORALL_VERTICES(v, graph, PluginGraph)
|
||||
put(v_index_map, v, graph[v]->Name());
|
||||
|
||||
//Now write graph to file.
|
||||
boss::ofstream out(outpath);
|
||||
boost::write_graphviz(out, graph, boost::make_label_writer(&names[0]));
|
||||
boost::write_graphviz(out, graph, boost::default_writer(), boost::default_writer(), boost::default_writer(), v_index_map);
|
||||
out.close();
|
||||
*/ }
|
||||
}
|
||||
|
||||
void Sort(const PluginGraph& graph, std::list<Plugin>& plugins) {
|
||||
/* std::list<vertex_t> sortedVertices;
|
||||
boost::topological_sort(graph, std::front_inserter(sortedVertices));
|
||||
|
||||
//Topological sort requires an index map, which std::list-based VertexList graphs don't have, so one needs to be built separately.
|
||||
|
||||
map<vertex_t, size_t> index_map;
|
||||
boost::associative_property_map< map<vertex_t, size_t> > v_index_map(index_map);
|
||||
size_t i=0;
|
||||
BGL_FORALL_VERTICES(v, graph, PluginGraph)
|
||||
put(v_index_map, v, i++);
|
||||
|
||||
//Now we can sort.
|
||||
|
||||
std::list<vertex_t> sortedVertices;
|
||||
boost::topological_sort(graph, std::front_inserter(sortedVertices), boost::vertex_index_map(v_index_map));
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Calculated order: ";
|
||||
list<boss::Plugin> tempPlugins;
|
||||
@@ -128,5 +148,19 @@ namespace boss {
|
||||
tempPlugins.push_back(*graph[*it]);
|
||||
}
|
||||
plugins.swap(tempPlugins);
|
||||
*/ }
|
||||
}
|
||||
|
||||
void CheckForCycles(const PluginGraph& graph) {
|
||||
|
||||
//Depth-first search requires an index map, which std::list-based VertexList graphs don't have, so one needs to be built separately.
|
||||
|
||||
map<vertex_t, size_t> index_map;
|
||||
boost::associative_property_map< map<vertex_t, size_t> > v_index_map(index_map);
|
||||
size_t i=0;
|
||||
BGL_FORALL_VERTICES(v, graph, PluginGraph)
|
||||
put(v_index_map, v, i++);
|
||||
|
||||
boss::cycle_detector vis;
|
||||
boost::depth_first_search(graph, visitor(vis).vertex_index_map(v_index_map));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#define __BOSS_GRAPH__
|
||||
|
||||
#include "metadata.h"
|
||||
#include "error.h"
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/graph/graph_traits.hpp>
|
||||
@@ -37,6 +38,18 @@ namespace boss {
|
||||
typedef boost::adjacency_list<boost::listS, boost::listS, boost::bidirectionalS, std::list<boss::Plugin>::iterator> PluginGraph;
|
||||
typedef boost::graph_traits<PluginGraph>::vertex_descriptor vertex_t;
|
||||
typedef boost::graph_traits<PluginGraph>::vertex_iterator vertex_it;
|
||||
typedef boost::graph_traits<PluginGraph>::edge_descriptor edge_t;
|
||||
|
||||
struct cycle_detector : public boost::dfs_visitor<> {
|
||||
inline cycle_detector() { }
|
||||
|
||||
inline void back_edge(edge_t e, const PluginGraph& g) {
|
||||
vertex_t vSource = boost::source(e, g);
|
||||
vertex_t vTarget = boost::target(e, g);
|
||||
|
||||
throw boss::error(boss::error::invalid_args, "Back edge detected between plugins \"" + g[vSource]->Name() + "\" and \"" + g[vTarget]->Name() + "\".");
|
||||
}
|
||||
};
|
||||
|
||||
//Gets the vertex for the plugin if it exists, or creates one if it doesn't.
|
||||
vertex_t GetPluginVertex(PluginGraph& graph, const Plugin& plugin, boost::unordered_map<std::string, vertex_t>& pluginVertexMap);
|
||||
@@ -49,6 +62,8 @@ namespace boss {
|
||||
void SaveGraph(const PluginGraph& graph, const boost::filesystem::path outpath);
|
||||
|
||||
void Sort(const PluginGraph& graph, std::list<Plugin>& plugins);
|
||||
|
||||
void CheckForCycles(const PluginGraph& graph);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+24
-7
@@ -604,21 +604,23 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Adding in-edges for masters.";
|
||||
vector<string> strVec(graph[*vit]->Masters());
|
||||
for (vector<string>::const_iterator it=strVec.begin(), itend=strVec.end(); it != itend; ++it) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Adding in-edge from \"" << *it << "\".";
|
||||
if (boss::GetVertexByName(graph, *it, parentVertex) &&
|
||||
!boost::edge(parentVertex, *vit, graph).second) {
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Adding edge from \"" << graph[parentVertex]->Name() << "\" to \"" << graph[*vit]->Name() << "\".";
|
||||
|
||||
boost::add_edge(parentVertex, *vit, graph);
|
||||
}
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(trace) << "Adding in-edges for requirements.";
|
||||
set<File> fileset(graph[*vit]->Reqs());
|
||||
for (set<File>::const_iterator it=fileset.begin(), itend=fileset.end(); it != itend; ++it) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Adding in-edge from \"" << it->Name() << "\".";
|
||||
if (boss::IsPlugin(it->Name()) &&
|
||||
boss::GetVertexByName(graph, it->Name(), parentVertex) &&
|
||||
!boost::edge(parentVertex, *vit, graph).second) {
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Adding edge from \"" << graph[parentVertex]->Name() << "\" to \"" << graph[*vit]->Name() << "\".";
|
||||
|
||||
boost::add_edge(parentVertex, *vit, graph);
|
||||
}
|
||||
}
|
||||
@@ -626,11 +628,12 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Adding in-edges for 'load after's.";
|
||||
fileset = graph[*vit]->LoadAfter();
|
||||
for (set<File>::const_iterator it=fileset.begin(), itend=fileset.end(); it != itend; ++it) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Adding in-edge from \"" << it->Name() << "\".";
|
||||
if (boss::IsPlugin(it->Name()) &&
|
||||
boss::GetVertexByName(graph, it->Name(), parentVertex) &&
|
||||
!boost::edge(parentVertex, *vit, graph).second) {
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Adding edge from \"" << graph[parentVertex]->Name() << "\" to \"" << graph[*vit]->Name() << "\".";
|
||||
|
||||
boost::add_edge(parentVertex, *vit, graph);
|
||||
}
|
||||
}
|
||||
@@ -638,12 +641,14 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) {
|
||||
|
||||
//Now add any overlaps, except where an edge already exists between the two plugins, going the other way, since overlap-based edges have the lowest priority and are not a candidate for causing cyclic loop errors.
|
||||
BOOST_LOG_TRIVIAL(trace) << "Adding in-edges for overlaps.";
|
||||
boost::unordered_map< std::string, std::vector<string> >::const_iterator overlapIt = overlapMap.find(graph[*vit]->Name());
|
||||
boost::unordered_map< string, vector<string> >::const_iterator overlapIt = overlapMap.find(graph[*vit]->Name());
|
||||
if (overlapIt != overlapMap.end()) {
|
||||
for (vector<string>::const_iterator it=overlapIt->second.begin(), itend=overlapIt->second.end(); it != itend; ++it) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Adding in-edge from \"" << *it << "\".";
|
||||
if (boss::GetVertexByName(graph, *it, parentVertex) &&
|
||||
!boost::edge(*vit, parentVertex, graph).second) { //No edge going the other way, OK to add this edge.
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Adding edge from \"" << graph[parentVertex]->Name() << "\" to \"" << graph[*vit]->Name() << "\".";
|
||||
|
||||
boost::add_edge(parentVertex, *vit, graph);
|
||||
}
|
||||
}
|
||||
@@ -653,11 +658,23 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) {
|
||||
|
||||
//Just for fun - output the graph as a ".dot" file for external rendering. If I can get this pretty, I might add it to a tab in the BOSS Report - here's a few Javascript libraries for displaying .dot files, but in my test case the graph is too complex and both libraries I found ran out of memory.
|
||||
//The .dot file can be converted to an SVG using Graphviz: the command is `dot -Tsvg output.dot -o output.svg`.
|
||||
BOOST_LOG_TRIVIAL(trace) << "Outputting the graph.";
|
||||
boss::SaveGraph(graph, "output.dot");
|
||||
|
||||
//Now perform a topological sort.
|
||||
boss::Sort(graph, plugins);
|
||||
//Check for back-edges.
|
||||
try {
|
||||
boss::CheckForCycles(graph);
|
||||
} catch (boss::error& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << e.what();
|
||||
}
|
||||
|
||||
//Now perform a topological sort.
|
||||
BOOST_LOG_TRIVIAL(trace) << "Performing a topological sort on the graph.";
|
||||
try {
|
||||
boss::Sort(graph, plugins);
|
||||
} catch (boost::not_a_dag& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << e.what();
|
||||
}
|
||||
|
||||
|
||||
if (!cyclicDependenciesExist) {
|
||||
|
||||
Reference in New Issue
Block a user