diff --git a/src/backend/graph.cpp b/src/backend/graph.cpp index ce620f79..04a55881 100644 --- a/src/backend/graph.cpp +++ b/src/backend/graph.cpp @@ -22,6 +22,7 @@ */ #include "graph.h" +#include "streams.h" #include #include @@ -47,8 +48,8 @@ namespace boss { return vertex; } - //The map maps each plugin name to a vector of names of plugins that overlap with it and should load before it. - void CalcPluginOverlaps(const std::list& plugins, boost::unordered_map< std::string, std::vector::const_iterator> >& overlapMap) { + //The map maps each plugin name to a vector of names of plugins that overlap with it and should load before it. + void CalcPluginOverlaps(const std::list& plugins, boost::unordered_map< std::string, std::vector >& overlapMap) { for (list::const_iterator it=plugins.begin(), endit=plugins.end(); it != endit; @@ -59,26 +60,26 @@ namespace boss { BOOST_LOG_TRIVIAL(trace) << "Checking for FormID overlap between \"" << it->Name() << "\" and \"" << jt->Name() << "\"."; if (it->DoFormIDsOverlap(*jt)) { std::string key; - list::const_iterator value; + std::string value; //Priority values should override the number of override records as the deciding factor if they differ. if (it->MustLoadAfter(*jt) || jt->MustLoadAfter(*it)) break; if (it->Priority() < jt->Priority()) { key = jt->Name(); - value = it; + value = it->Name(); } else if (jt->Priority() < it->Priority()) { key = it->Name(); - value = jt; + value = jt->Name(); } else if (it->NumOverrideFormIDs() >= jt->NumOverrideFormIDs()) { key = jt->Name(); - value = it; + value = it->Name(); } else { key = it->Name(); - value = jt; + value = jt->Name(); } - boost::unordered_map< string, vector::const_iterator> >::iterator mapIt = overlapMap.find(key); + boost::unordered_map< string, vector >::iterator mapIt = overlapMap.find(key); if (mapIt == overlapMap.end()) { - overlapMap.insert(pair::const_iterator> >(key, vector::const_iterator>(1, value))); + overlapMap.insert(pair >(key, vector(1, value))); } else { mapIt->second.push_back(value); } @@ -86,4 +87,46 @@ namespace boss { } } } + + bool GetVertexByName(const PluginGraph& graph, const std::string& name, vertex_t& vertex) { + vertex_it vit, vit_end; + boost::tie(vit, vit_end) = boost::vertices(graph); + + for (vit, vit_end; vit != vit_end; ++vit) { + if (boost::iequals(graph[*vit]->Name(), name)) { + vertex = *vit; + return true; + } + } + + return false; + } + + void SaveGraph(const PluginGraph& graph, const boost::filesystem::path outpath) { + //First need to extract vertex names, since their stored as private members otherwise. + /* vector 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()); + } + //Now write graph to file. + boss::ofstream out(outpath); + boost::write_graphviz(out, graph, boost::make_label_writer(&names[0])); + out.close(); + */ } + + void Sort(const PluginGraph& graph, std::list& plugins) { + /* std::list sortedVertices; + boost::topological_sort(graph, std::front_inserter(sortedVertices)); + + BOOST_LOG_TRIVIAL(info) << "Calculated order: "; + list tempPlugins; + for (std::list::iterator it = sortedVertices.begin(), endit = sortedVertices.end(); it != endit; ++it) { + BOOST_LOG_TRIVIAL(info) << '\t' << graph[*it]->Name(); + tempPlugins.push_back(*graph[*it]); + } + plugins.swap(tempPlugins); + */ } } diff --git a/src/backend/graph.h b/src/backend/graph.h index 12000c5c..5f10b8e4 100644 --- a/src/backend/graph.h +++ b/src/backend/graph.h @@ -30,18 +30,25 @@ #include #include #include +#include namespace boss { - typedef boost::adjacency_list::const_iterator> PluginGraph; + typedef boost::adjacency_list::iterator> PluginGraph; typedef boost::graph_traits::vertex_descriptor vertex_t; + typedef boost::graph_traits::vertex_iterator vertex_it; //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& pluginVertexMap); //The map maps each plugin name to a vector of names of plugins that overlap with it and should load before it. - void CalcPluginOverlaps(const std::list& plugins, boost::unordered_map< std::string, std::vector::const_iterator> >& overlapMap); + void CalcPluginOverlaps(const std::list& plugins, boost::unordered_map< std::string, std::vector >& overlapMap); + bool GetVertexByName(const PluginGraph& graph, const std::string& name, vertex_t& vertex); + + void SaveGraph(const PluginGraph& graph, const boost::filesystem::path outpath); + + void Sort(const PluginGraph& graph, std::list& plugins); } #endif diff --git a/src/gui/main.cpp b/src/gui/main.cpp index e5f88c6a..bf465746 100644 --- a/src/gui/main.cpp +++ b/src/gui/main.cpp @@ -556,17 +556,9 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) { BOOST_LOG_TRIVIAL(trace) << "Moving masters before non-masters."; plugins.sort(boss::master_sort); - list::const_iterator firstNonMaster; - for (list::const_iterator it=plugins.begin(), endIt=plugins.end(); it != endIt; ++it) { - if (!it->IsMaster()) { - firstNonMaster = it; - break; - } - } - //Now build overlap map. BOOST_LOG_TRIVIAL(trace) << "Building plugin overlap map."; - boost::unordered_map< string, vector::const_iterator> > overlapMap; + boost::unordered_map< string, vector > overlapMap; CalcPluginOverlaps(plugins, overlapMap); BOOST_LOG_TRIVIAL(trace) << "Building the plugin dependency graph..."; @@ -577,110 +569,94 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) { boss::PluginGraph graph; //Now add the plugins in order to the graph as vertices. - for (list::const_iterator it=plugins.begin(), endIt=plugins.end(); it != endIt; ++it) { + for (list::iterator it=plugins.begin(), endIt=plugins.end(); it != endIt; ++it) { BOOST_LOG_TRIVIAL(trace) << "Creating vertex for \"" << it->Name() << "\"."; boost::add_vertex(it, graph); } //Vertices are numbered from 0 to (N - 1), where N is the number of vertices in the graph. A plugin's vertex number is therefore the same as its position in the list, and this can be used to create edges between plugins. - list::const_iterator beginIt = plugins.begin(); - for (list::const_iterator it=plugins.begin(), endIt=plugins.end(); it != endIt; ++it) { - BOOST_LOG_TRIVIAL(trace) << "Editing vertex for \"" << it->Name() << "\"."; - //Check if this plugin already has a vertex, and if not, create one. - boss::vertex_t vertex = boost::vertex( std::distance(beginIt, it), graph); - //Now add edges for everything. - list::const_iterator result; - set fileset; - vector strVec = it->Masters(); - if (it->IsMaster()) { + boss::vertex_it vitFirstNonMaster, vit, vitend; + for (boost::tie(vit, vitend) = boost::vertices(graph); vit != vitend; ++vit) { + if (!graph[*vit]->IsMaster()) { + vitFirstNonMaster = vit; + break; + } + } + + for (boost::tie(vit, vitend) = boost::vertices(graph); vit != vitend; ++vit) { + vertex_t parentVertex; + + BOOST_LOG_TRIVIAL(trace) << "Adding edges to vertex for \"" << graph[*vit]->Name() << "\"."; + + if (graph[*vit]->IsMaster()) { BOOST_LOG_TRIVIAL(trace) << "Adding out-edges for non-master plugins."; - //Need to add out-edges to all non-master plugins. - for (list::const_iterator jt=firstNonMaster, endIt; jt != endIt; ++jt) { - size_t pos = std::distance(beginIt, jt); - BOOST_LOG_TRIVIAL(trace) << "Current position: " << pos << ", list size: " << plugins.size() << ", number of vertices: " << boost::num_vertices(graph); - if (pos == plugins.size() - 1) - break; - BOOST_LOG_TRIVIAL(trace) << "Getting vertex for \"" << jt->Name() << "\"."; - boss::vertex_t childVertex = boost::vertex(pos , graph ); - //Now that we have the non-master plugin's vertex, create an edge between the two. - BOOST_LOG_TRIVIAL(trace) << "Adding edge from \"" << it->Name() << "\" to \"" << jt->Name() << "\"."; - if (!boost::edge(vertex, childVertex, graph).second) //To avoid duplicates (helps visualisation). - boost::add_edge(vertex, childVertex, graph); + for (boss::vertex_it vit2 = vitFirstNonMaster; vit2 != vitend; ++vit2) { + BOOST_LOG_TRIVIAL(trace) << "Adding edge from \"" << graph[*vit]->Name() << "\" to \"" << graph[*vit2]->Name() << "\"."; + + if (!boost::edge(*vit, *vit2, graph).second) //To avoid duplicates (helps visualisation). + boost::add_edge(*vit, *vit2, graph); } } - //Now add masters. BOOST_LOG_TRIVIAL(trace) << "Adding in-edges for masters."; - for (vector::const_iterator jt=strVec.begin(), endjt=strVec.end(); jt != endjt; ++jt) { - //Find the other plugin. - result = std::find(plugins.begin(), plugins.end(), boss::Plugin(*jt)); - if (result == plugins.end()) - continue; - //Add the vertex (again assuming that duplicates won't be created). - boss::vertex_t parentVertex = boost::vertex( std::distance(beginIt, result), graph); - if (!boost::edge(parentVertex, vertex, graph).second) //To avoid duplicates (helps visualisation). - boost::add_edge(parentVertex, vertex, graph); - } + vector strVec(graph[*vit]->Masters()); + for (vector::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) { - //Now add requirements. + boost::add_edge(parentVertex, *vit, graph); + } + } BOOST_LOG_TRIVIAL(trace) << "Adding in-edges for requirements."; - fileset = it->Reqs(); - for (set::const_iterator jt=fileset.begin(), endjt=fileset.end(); jt != endjt; ++jt) { - if (boss::IsPlugin(jt->Name())) { - //Find the other plugin. - result = std::find(plugins.begin(), plugins.end(), *jt); - if (result == plugins.end()) - continue; - //Add the vertex (again assuming that duplicates won't be created). - boss::vertex_t parentVertex = boost::vertex( std::distance(beginIt, result), graph); - if (!boost::edge(parentVertex, vertex, graph).second) //To avoid duplicates (helps visualisation). - boost::add_edge(parentVertex, vertex, graph); + set fileset(graph[*vit]->Reqs()); + for (set::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::add_edge(parentVertex, *vit, graph); } } - //Now add "load after" plugins. BOOST_LOG_TRIVIAL(trace) << "Adding in-edges for 'load after's."; - fileset = it->LoadAfter(); - for (set::const_iterator jt=fileset.begin(), endjt=fileset.end(); jt != endjt; ++jt) { - if (boss::IsPlugin(jt->Name())) { - //Find the other plugin. - result = std::find(plugins.begin(), plugins.end(), *jt); - if (result == plugins.end()) - continue; - //Add the vertex (again assuming that duplicates won't be created). - boss::vertex_t parentVertex = boost::vertex( std::distance(beginIt, result), graph); - if (!boost::edge(parentVertex, vertex, graph).second) //To avoid duplicates (helps visualisation). - boost::add_edge(parentVertex, vertex, graph); + fileset = graph[*vit]->LoadAfter(); + for (set::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::add_edge(parentVertex, *vit, graph); } } + //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::const_iterator> >::const_iterator overlapIt = overlapMap.find(it->Name()); + boost::unordered_map< std::string, std::vector >::const_iterator overlapIt = overlapMap.find(graph[*vit]->Name()); if (overlapIt != overlapMap.end()) { - for (vector::const_iterator>::const_iterator jt=overlapIt->second.begin(), endjt=overlapIt->second.end(); jt != endjt; ++jt) { - BOOST_LOG_TRIVIAL(trace) << "Getting vertex for \"" << (*jt)->Name() << "\"."; - boss::vertex_t parentVertex = boost::vertex( std::distance(beginIt, *jt), graph); - - BOOST_LOG_TRIVIAL(trace) << "Checking if there is already a vertex between the plugins in the opposite direction."; - if (!boost::edge(vertex, parentVertex, graph).second) { - //No edge going the other way, OK to add this edge. - boost::add_edge(parentVertex, vertex, graph); + for (vector::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::add_edge(parentVertex, *vit, graph); } } } + } - //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 - I think there's a Javascript library for displaying .dot files (though stuff of this complexity might make it explode...). - vector names; - for (list::const_iterator it=plugins.begin(), endIt=plugins.end(); it != endIt; ++it) { - names.push_back(it->Name()); - } - boss::ofstream outfun("fun.dot"); - boost::write_graphviz(outfun, graph, boost::make_label_writer(&names[0])); + //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`. + boss::SaveGraph(graph, "output.dot"); + + //Now perform a topological sort. + boss::Sort(graph, plugins);