]> git.tdb.fi Git - builder.git/blob - source/analyzer.cpp
Refactor build graph into its own class
[builder.git] / source / analyzer.cpp
1 #include <msp/fs/utils.h>
2 #include <msp/io/print.h>
3 #include "analyzer.h"
4 #include "builder.h"
5 #include "buildgraph.h"
6 #include "objectfile.h"
7 #include "sourcefile.h"
8 #include "sourcepackage.h"
9 #include "target.h"
10 #include "tool.h"
11
12 using namespace std;
13 using namespace Msp;
14
15 Analyzer::Analyzer(Builder &b):
16         builder(b),
17         mode(DEPS),
18         max_depth(0),
19         full_paths(false)
20 { }
21
22 void Analyzer::analyze()
23 {
24         if(mode==RDEPS)
25         {
26                 rdepends.clear();
27                 const BuildGraph::TargetMap &targets = builder.get_build_graph().get_targets();
28                 for(BuildGraph::TargetMap::const_iterator i=targets.begin(); i!=targets.end(); ++i)
29                 {
30                         const Target::Dependencies &depends = i->second->get_dependencies();
31                         for(Target::Dependencies::const_iterator j=depends.begin(); j!=depends.end(); ++j)
32                                 rdepends[*j].insert(i->second);
33                 }
34         }
35
36         table.clear();
37
38         TableRow row;
39         row.push_back("Name");
40         row.push_back("Package");
41         row.push_back("Type");
42         row.push_back("Tool");
43         row.push_back("Rebuild");
44         table.push_back(row);
45         
46         Target &cmdline = *builder.get_build_graph().get_target("cmdline");
47         if(mode==RDEPS)
48         {
49                 const Target::Dependencies &deps = cmdline.get_dependencies();
50                 for(Target::Dependencies::const_iterator i=deps.begin(); i!=deps.end(); ++i)
51                         build_depend_table(**i, 0);
52         }
53         else
54                 build_depend_table(cmdline, 0);
55
56         print_table();
57 }
58
59 void Analyzer::build_depend_table(Target &tgt, unsigned depth)
60 {
61         Target *real = tgt.get_real_target();
62         if(mode==DEPS)
63         {
64                 // Skip trivial targets
65                 if(real!=&tgt)
66                         return build_depend_table(*real, depth);
67                 if(const ObjectFile *obj = dynamic_cast<const ObjectFile *>(&tgt))
68                         return build_depend_table(obj->get_source(), depth);
69         }
70         else if(mode==REBUILD && !tgt.needs_rebuild())
71                 /* All targets that depend on to-be-built targets will be rebuilt
72                 themselves, so we can stop here. */
73                 return;
74         
75         TableRow row;
76
77         string name;
78         const FileTarget *ft = dynamic_cast<const FileTarget *>(&tgt);
79         if(full_paths && ft)
80                 name = ft->get_path().str();
81         else
82                 name = tgt.get_name();
83         row.push_back(string(depth*2, ' ')+name);
84
85         const Package *pkg = tgt.get_package();
86         if(pkg)
87                 row.push_back(pkg->get_name());
88         else
89                 row.push_back("");
90         
91         row.push_back(tgt.get_type());
92         const Tool *tool = tgt.get_tool();
93         if(tool)
94                 row.push_back(tool->get_tag());
95         else
96                 row.push_back("");
97
98         if(tgt.needs_rebuild())
99                 row.push_back(tgt.get_rebuild_reason());
100
101         table.push_back(row);
102
103         if(!max_depth || depth<max_depth-1)
104         {
105                 Target::Dependencies depends;
106                 if(mode==RDEPS)
107                 {
108                         const set<Target *> &rdeps = rdepends[&tgt];
109                         depends.assign(rdeps.begin(), rdeps.end());
110                 }
111                 else
112                         depends = tgt.get_dependencies();
113
114                 depends.sort(full_paths ? target_order_full : target_order);
115
116                 for(Target::Dependencies::const_iterator i=depends.begin(); i!=depends.end(); ++i)
117                         build_depend_table(**i, depth+1);
118         }
119 }
120
121 void Analyzer::print_table() const
122 {
123         vector<string::size_type> col_width;
124
125         // Determine column widths
126         for(Table::const_iterator i=table.begin(); i!=table.end(); ++i)
127         {
128                 if(col_width.size()<i->size())
129                         col_width.resize(i->size(), 0);
130                 for(unsigned j=0; j<i->size(); ++j)
131                         col_width[j] = max(col_width[j], (*i)[j].size());
132         }
133
134         for(Table::const_iterator i=table.begin(); i!=table.end(); ++i)
135         {
136                 string line;
137                 for(unsigned j=0; j<i->size(); ++j)
138                 {
139                         if(j>0)
140                                 line += "  ";
141                         line += lexical_cast<string>((*i)[j], Fmt("%-s").width(col_width[j]));
142                 }
143                 IO::print("%s\n", line);
144         }
145 }
146
147 bool Analyzer::target_order(const Target *t1, const Target *t2)
148 {
149         return t1->get_name()<t2->get_name();
150 }
151
152 bool Analyzer::target_order_full(const Target *t1, const Target *t2)
153 {
154         const FileTarget *ft1 = dynamic_cast<const FileTarget *>(t1);
155         const FileTarget *ft2 = dynamic_cast<const FileTarget *>(t2);
156         if(!ft1)
157         {
158                 if(ft2)
159                         return true;
160                 return target_order(t1, t2);
161         }
162         else if(!ft2)
163                 return false;
164         return ft1->get_path().str()<ft2->get_path().str();
165 }