PluginDependencyGraph.hpp
1 #pragma once
2 #include <score/plugins/Addon.hpp>
3 #include <score/plugins/qt_interfaces/GUIApplicationPlugin_QtInterface.hpp>
4 #include <score/plugins/qt_interfaces/PluginRequirements_QtInterface.hpp>
5 #include <score/tools/std/HashMap.hpp>
6 
7 #include <ossia/detail/pod_vector.hpp>
8 
9 #include <boost/graph/adjacency_list.hpp>
10 #include <boost/graph/topological_sort.hpp>
11 
12 #include <QDebug>
13 
14 #include <chrono>
15 #include <exception>
16 #include <memory>
17 #include <set>
18 namespace score
19 {
20 namespace PluginLoader
21 {
29 {
30  struct GraphVertex
31  {
32  GraphVertex()
33  : addon{}
34  {
35  }
36  explicit GraphVertex(const score::Addon* add)
37  : addon{add}
38  {
39  }
40  const score::Addon* addon{};
41  };
42 
43  using Graph
44  = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, GraphVertex>;
45 
46 public:
47  explicit PluginDependencyGraph(const std::vector<score::Addon>& addons)
48  {
49  if(addons.empty())
50  return;
51 
52  score::hash_map<PluginKey, int64_t> keys;
53  std::vector<const score::Addon*> not_loaded;
54 
55  // First add all the vertices to the graph
56  for(const score::Addon& addon : addons)
57  {
58  auto vx = boost::add_vertex(GraphVertex{&addon}, m_graph);
59  keys[addon.key] = vx;
60  }
61 
62  // If A depends on B, then there is an edge from B to A.
63  for(const score::Addon& addon : addons)
64  {
65  auto addon_k = keys[addon.key];
66  for(const auto& k : addon.plugin->required())
67  {
68  auto it = keys.find(k);
69  if(it != keys.end())
70  {
71  boost::add_edge(addon_k, it->second, m_graph);
72  }
73  else
74  {
75  boost::clear_vertex(addon_k, m_graph);
76  boost::remove_vertex(addon_k, m_graph);
77  not_loaded.push_back(&addon);
78  break;
79  }
80  }
81  }
82 
83  if(!not_loaded.empty())
84  qDebug() << not_loaded.size()
85  << "plugins were not loaded due to a dependency problem.";
86 
87  // Then do a topological sort, to detect cycles and to be able to iterate
88  // easily afterwards.
89  ossia::int_vector topo_order;
90  topo_order.reserve(addons.size() - not_loaded.size());
91 
92  try
93  {
94  boost::topological_sort(m_graph, std::back_inserter(topo_order));
95  for(auto e : topo_order)
96  {
97  if(m_graph[e].addon)
98  m_sorted.push_back(*m_graph[e].addon);
99  }
100  }
101  catch(const std::exception& e)
102  {
103  qDebug() << "Invalid plug-in graph: " << e.what();
104  m_graph.clear();
105  }
106  }
107 
108  auto& sortedAddons() const { return m_sorted; }
109 
110 private:
111  Graph m_graph;
112  std::vector<score::Addon> m_sorted;
113 };
114 }
115 }
Classes and functions used at the plug-in loading step.
Definition: PluginManager.hpp:26
Base toolkit upon which the software is built.
Definition: Application.cpp:90
The Addon struct.
Definition: Addon.hpp:16
Definition: PluginDependencyGraph.hpp:31
Allows to load plug-ins in the order they all require each other.
Definition: PluginDependencyGraph.hpp:29