diff --git a/src/backend/graph.cpp b/src/backend/graph.cpp index 04a55881..c1f12b93 100644 --- a/src/backend/graph.cpp +++ b/src/backend/graph.cpp @@ -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 names; + 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()); } + + //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 index_map; + boost::associative_property_map< map > 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& plugins) { - /* std::list 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 index_map; + boost::associative_property_map< map > 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 sortedVertices; + boost::topological_sort(graph, std::front_inserter(sortedVertices), boost::vertex_index_map(v_index_map)); BOOST_LOG_TRIVIAL(info) << "Calculated order: "; list 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 index_map; + boost::associative_property_map< map > 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)); + } } diff --git a/src/backend/graph.h b/src/backend/graph.h index 5f10b8e4..dac82c93 100644 --- a/src/backend/graph.h +++ b/src/backend/graph.h @@ -25,6 +25,7 @@ #define __BOSS_GRAPH__ #include "metadata.h" +#include "error.h" #include #include @@ -37,6 +38,18 @@ namespace boss { typedef boost::adjacency_list::iterator> PluginGraph; typedef boost::graph_traits::vertex_descriptor vertex_t; typedef boost::graph_traits::vertex_iterator vertex_it; + typedef boost::graph_traits::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& pluginVertexMap); @@ -49,6 +62,8 @@ namespace boss { void SaveGraph(const PluginGraph& graph, const boost::filesystem::path outpath); void Sort(const PluginGraph& graph, std::list& plugins); + + void CheckForCycles(const PluginGraph& graph); } #endif diff --git a/src/gui/main.cpp b/src/gui/main.cpp index bf465746..a0c65f81 100644 --- a/src/gui/main.cpp +++ b/src/gui/main.cpp @@ -604,21 +604,23 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) { BOOST_LOG_TRIVIAL(trace) << "Adding in-edges for masters."; 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) { + 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 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_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::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 >::const_iterator overlapIt = overlapMap.find(graph[*vit]->Name()); + boost::unordered_map< string, vector >::const_iterator overlapIt = overlapMap.find(graph[*vit]->Name()); if (overlapIt != overlapMap.end()) { 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_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) {