1 //=======================================================================
2 // Copyright 1997, 1998, 1999, 2000 University of Notre Dame.
3 // Authors: Andrew Lumsdaine, Lie-Quan Lee, Jeremy G. Siek
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See
6 // accompanying file LICENSE_1_0.txt or copy at
7 // http://www.boost.org/LICENSE_1_0.txt)
8 //=======================================================================
9 
10 // Some small modifications are done by Alexander Holler
11 
12 /*
13 
14   Paul Moore's request:
15 
16   As an example of a practical problem which is not restricted to graph
17   "experts", consider file dependencies. It's basically graph construction,
18   plus topological sort, but it might make a nice "tutorial" example. Build a
19   dependency graph of files, then use the algorithms to do things like
20 
21   1. Produce a full recompilation order (topological sort, by modified date)
22   2. Produce a "parallel" recompilation order (same as above, but group files
23   which can be built in parallel)
24   3. Change analysis (if I change file x, which others need recompiling)
25   4. Dependency changes (if I add a dependency between file x and file y, what
26   are the effects)
27 
28 */
29 
30 #include <boost/config.hpp> // put this first to suppress some VC++ warnings
31 
32 #include <iostream>
33 #include <iterator>
34 #include <algorithm>
35 #include <time.h>
36 
37 #include <boost/utility.hpp>
38 #include <boost/graph/adjacency_list.hpp>
39 #include <boost/graph/topological_sort.hpp>
40 #include <boost/graph/depth_first_search.hpp>
41 #include <boost/graph/dijkstra_shortest_paths.hpp>
42 #include <boost/graph/visitors.hpp>
43 
44 using namespace std;
45 using namespace boost;
46 
47 enum files_e { dax_h, yow_h, boz_h, zow_h, foo_cpp,
48                foo_o, bar_cpp, bar_o, libfoobar_a,
49                zig_cpp, zig_o, zag_cpp, zag_o,
50                  libzigzag_a, killerapp, N };
51 const char* name[] = { "dax.h", "yow.h", "boz.h", "zow.h", "foo.cpp",
52                        "foo.o", "bar.cpp", "bar.o", "libfoobar.a",
53                        "zig.cpp", "zig.o", "zag.cpp", "zag.o",
54                        "libzigzag.a", "killerapp" };
55 
56 
57 struct print_visitor : public bfs_visitor<> {
58   template <class Vertex, class Graph>
discover_vertexprint_visitor59   void discover_vertex(Vertex v, Graph&) {
60     cout << name[v] << " ";
61   }
62 };
63 
64 
65 struct cycle_detector : public dfs_visitor<>
66 {
cycle_detectorcycle_detector67   cycle_detector(bool& has_cycle)
68     : m_has_cycle(has_cycle) { }
69 
70   template <class Edge, class Graph>
back_edgecycle_detector71   void back_edge(Edge, Graph&) { m_has_cycle = true; }
72 protected:
73   bool& m_has_cycle;
74 };
75 
76 
77 
78 
main(int,char * [])79 int main(int,char*[])
80 {
81 
82   typedef pair<int,int> Edge;
83   Edge used_by[] = {
84     Edge(dax_h, foo_cpp), Edge(dax_h, bar_cpp), Edge(dax_h, yow_h),
85     Edge(yow_h, bar_cpp), Edge(yow_h, zag_cpp),
86     Edge(boz_h, bar_cpp), Edge(boz_h, zig_cpp), Edge(boz_h, zag_cpp),
87     Edge(zow_h, foo_cpp),
88     Edge(foo_cpp, foo_o),
89     Edge(foo_o, libfoobar_a),
90     Edge(bar_cpp, bar_o),
91     Edge(bar_o, libfoobar_a),
92     Edge(libfoobar_a, libzigzag_a),
93     Edge(zig_cpp, zig_o),
94     Edge(zig_o, libzigzag_a),
95     Edge(zag_cpp, zag_o),
96     Edge(zag_o, libzigzag_a),
97     Edge(libzigzag_a, killerapp)
98   };
99   const std::size_t nedges = sizeof(used_by)/sizeof(Edge);
100 
101   typedef adjacency_list<vecS, vecS, bidirectionalS> Graph;
102 #if defined(BOOST_MSVC) && BOOST_MSVC <= 1300
103   // VC++ can't handle the iterator constructor
104   Graph g(N);
105   for (std::size_t j = 0; j < nedges; ++j) {
106     graph_traits<Graph>::edge_descriptor e; bool inserted;
107     boost::tie(e, inserted) = add_edge(used_by[j].first, used_by[j].second, g);
108   }
109 #else
110   Graph g(used_by, used_by + nedges, N);
111 #endif
112   typedef graph_traits<Graph>::vertex_descriptor Vertex;
113 
114   // Determine ordering for a full recompilation
115   // and the order with files that can be compiled in parallel
116   {
117     typedef list<Vertex> MakeOrder;
118     MakeOrder::iterator i;
119     MakeOrder make_order;
120 
121     topological_sort(g, std::front_inserter(make_order));
122     cout << "make ordering: ";
123     for (i = make_order.begin();
124          i != make_order.end(); ++i)
125       cout << name[*i] << " ";
126 
127     cout << endl << endl;
128 
129     // Parallel compilation ordering
130     std::vector<int> time(N, 0);
131     for (i = make_order.begin(); i != make_order.end(); ++i) {
132       // Walk through the in_edges an calculate the maximum time.
133       if (in_degree (*i, g) > 0) {
134         Graph::in_edge_iterator j, j_end;
135         int maxdist=0;
136         // Through the order from topological sort, we are sure that every
137         // time we are using here is already initialized.
138         for (boost::tie(j, j_end) = in_edges(*i, g); j != j_end; ++j)
139           maxdist=(std::max)(time[source(*j, g)], maxdist);
140         time[*i]=maxdist+1;
141       }
142     }
143 
144     cout << "parallel make ordering, " << endl
145          << "vertices with same group number can be made in parallel" << endl;
146     {
147       graph_traits<Graph>::vertex_iterator i, iend;
148       for (boost::tie(i,iend) = vertices(g); i != iend; ++i)
149         cout << "time_slot[" << name[*i] << "] = " << time[*i] << endl;
150     }
151 
152   }
153   cout << endl;
154 
155   // if I change yow.h what files need to be re-made?
156   {
157     cout << "A change to yow.h will cause what to be re-made?" << endl;
158     print_visitor vis;
159     breadth_first_search(g, vertex(yow_h, g), visitor(vis));
160     cout << endl;
161   }
162   cout << endl;
163 
164   // are there any cycles in the graph?
165   {
166     bool has_cycle = false;
167     cycle_detector vis(has_cycle);
168     depth_first_search(g, visitor(vis));
169     cout << "The graph has a cycle? " << has_cycle << endl;
170   }
171   cout << endl;
172 
173   // add a dependency going from bar.cpp to dax.h
174   {
175     cout << "adding edge bar_cpp -> dax_h" << endl;
176     add_edge(bar_cpp, dax_h, g);
177   }
178   cout << endl;
179 
180   // are there any cycles in the graph?
181   {
182     bool has_cycle = false;
183     cycle_detector vis(has_cycle);
184     depth_first_search(g, visitor(vis));
185     cout << "The graph has a cycle now? " << has_cycle << endl;
186   }
187 
188   return 0;
189 }
190