blob: 9bce73342ebe5251c2948738343e2656ef4ed3d9 [file] [log] [blame]
//=======================================================================
// Copyright 1997, 1998, 1999, 2000 University of Notre Dame.
// Authors: Andrew Lumsdaine, Lie-Quan Lee, Jeremy G. Siek
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================
// Some small modifications are done by Alexander Holler
/*
Paul Moore's request:
As an example of a practical problem which is not restricted to graph
"experts", consider file dependencies. It's basically graph construction,
plus topological sort, but it might make a nice "tutorial" example. Build a
dependency graph of files, then use the algorithms to do things like
1. Produce a full recompilation order (topological sort, by modified date)
2. Produce a "parallel" recompilation order (same as above, but group files
which can be built in parallel)
3. Change analysis (if I change file x, which others need recompiling)
4. Dependency changes (if I add a dependency between file x and file y, what
are the effects)
*/
#include <boost/config.hpp> // put this first to suppress some VC++ warnings
#include <iostream>
#include <iterator>
#include <algorithm>
#include <time.h>
#include <boost/utility.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/topological_sort.hpp>
#include <boost/graph/depth_first_search.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/visitors.hpp>
using namespace std;
using namespace boost;
enum files_e { dax_h, yow_h, boz_h, zow_h, foo_cpp,
foo_o, bar_cpp, bar_o, libfoobar_a,
zig_cpp, zig_o, zag_cpp, zag_o,
libzigzag_a, killerapp, N };
const char* name[] = { "dax.h", "yow.h", "boz.h", "zow.h", "foo.cpp",
"foo.o", "bar.cpp", "bar.o", "libfoobar.a",
"zig.cpp", "zig.o", "zag.cpp", "zag.o",
"libzigzag.a", "killerapp" };
struct print_visitor : public bfs_visitor<> {
template <class Vertex, class Graph>
void discover_vertex(Vertex v, Graph&) {
cout << name[v] << " ";
}
};
struct cycle_detector : public dfs_visitor<>
{
cycle_detector(bool& has_cycle)
: m_has_cycle(has_cycle) { }
template <class Edge, class Graph>
void back_edge(Edge, Graph&) { m_has_cycle = true; }
protected:
bool& m_has_cycle;
};
int main(int,char*[])
{
typedef pair<int,int> Edge;
Edge used_by[] = {
Edge(dax_h, foo_cpp), Edge(dax_h, bar_cpp), Edge(dax_h, yow_h),
Edge(yow_h, bar_cpp), Edge(yow_h, zag_cpp),
Edge(boz_h, bar_cpp), Edge(boz_h, zig_cpp), Edge(boz_h, zag_cpp),
Edge(zow_h, foo_cpp),
Edge(foo_cpp, foo_o),
Edge(foo_o, libfoobar_a),
Edge(bar_cpp, bar_o),
Edge(bar_o, libfoobar_a),
Edge(libfoobar_a, libzigzag_a),
Edge(zig_cpp, zig_o),
Edge(zig_o, libzigzag_a),
Edge(zag_cpp, zag_o),
Edge(zag_o, libzigzag_a),
Edge(libzigzag_a, killerapp)
};
const std::size_t nedges = sizeof(used_by)/sizeof(Edge);
typedef adjacency_list<vecS, vecS, bidirectionalS> Graph;
#if defined(BOOST_MSVC) && BOOST_MSVC <= 1300
// VC++ can't handle the iterator constructor
Graph g(N);
for (std::size_t j = 0; j < nedges; ++j) {
graph_traits<Graph>::edge_descriptor e; bool inserted;
boost::tie(e, inserted) = add_edge(used_by[j].first, used_by[j].second, g);
}
#else
Graph g(used_by, used_by + nedges, N);
#endif
typedef graph_traits<Graph>::vertex_descriptor Vertex;
// Determine ordering for a full recompilation
// and the order with files that can be compiled in parallel
{
typedef list<Vertex> MakeOrder;
MakeOrder::iterator i;
MakeOrder make_order;
topological_sort(g, std::front_inserter(make_order));
cout << "make ordering: ";
for (i = make_order.begin();
i != make_order.end(); ++i)
cout << name[*i] << " ";
cout << endl << endl;
// Parallel compilation ordering
std::vector<int> time(N, 0);
for (i = make_order.begin(); i != make_order.end(); ++i) {
// Walk through the in_edges an calculate the maximum time.
if (in_degree (*i, g) > 0) {
Graph::in_edge_iterator j, j_end;
int maxdist=0;
// Through the order from topological sort, we are sure that every
// time we are using here is already initialized.
for (boost::tie(j, j_end) = in_edges(*i, g); j != j_end; ++j)
maxdist=(std::max)(time[source(*j, g)], maxdist);
time[*i]=maxdist+1;
}
}
cout << "parallel make ordering, " << endl
<< "vertices with same group number can be made in parallel" << endl;
{
graph_traits<Graph>::vertex_iterator i, iend;
for (boost::tie(i,iend) = vertices(g); i != iend; ++i)
cout << "time_slot[" << name[*i] << "] = " << time[*i] << endl;
}
}
cout << endl;
// if I change yow.h what files need to be re-made?
{
cout << "A change to yow.h will cause what to be re-made?" << endl;
print_visitor vis;
breadth_first_search(g, vertex(yow_h, g), visitor(vis));
cout << endl;
}
cout << endl;
// are there any cycles in the graph?
{
bool has_cycle = false;
cycle_detector vis(has_cycle);
depth_first_search(g, visitor(vis));
cout << "The graph has a cycle? " << has_cycle << endl;
}
cout << endl;
// add a dependency going from bar.cpp to dax.h
{
cout << "adding edge bar_cpp -> dax_h" << endl;
add_edge(bar_cpp, dax_h, g);
}
cout << endl;
// are there any cycles in the graph?
{
bool has_cycle = false;
cycle_detector vis(has_cycle);
depth_first_search(g, visitor(vis));
cout << "The graph has a cycle now? " << has_cycle << endl;
}
return 0;
}