]> git.tdb.fi Git - builder.git/blob - source/analyzer.cpp
Replace basic for loops with range-based loops or algorithms
[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                 for(const auto &kvp: builder.get_build_graph().get_targets())
28                 {
29                         for(Target *d: kvp.second->get_dependencies())
30                                 rdepends[d].insert(kvp.second);
31                         for(Target *d: kvp.second->get_transitive_dependencies())
32                                 rdepends[d].insert(kvp.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 &goals = builder.get_build_graph().get_goals();
47         if(mode==RDEPS)
48         {
49                 for(Target *d: goals.get_dependencies())
50                         build_depend_table(*d, 0);
51         }
52         else
53                 build_depend_table(goals, 0);
54
55         print_table();
56 }
57
58 void Analyzer::build_depend_table(Target &tgt, unsigned depth)
59 {
60         Target *real = tgt.get_real_target();
61         if(mode==DEPS)
62         {
63                 // Skip trivial targets
64                 if(real!=&tgt)
65                         return build_depend_table(*real, depth);
66                 if(const ObjectFile *obj = dynamic_cast<const ObjectFile *>(&tgt))
67                         return build_depend_table(obj->get_source(), depth);
68         }
69         else if(mode==REBUILD && !tgt.needs_rebuild())
70                 /* All targets that depend on to-be-built targets will be rebuilt
71                 themselves, so we can stop here. */
72                 return;
73         
74         TableRow row;
75
76         string name;
77         const FileTarget *ft = dynamic_cast<const FileTarget *>(&tgt);
78         if(full_paths && ft)
79                 name = ft->get_path().str();
80         else
81                 name = tgt.get_name();
82         row.push_back(string(depth*2, ' ')+name);
83
84         const Package *pkg = tgt.get_package();
85         if(pkg)
86                 row.push_back(pkg->get_name());
87         else
88                 row.push_back("");
89         
90         row.push_back(tgt.get_type());
91         const Tool *tool = tgt.get_tool();
92         if(tool)
93                 row.push_back(tool->get_tag());
94         else
95                 row.push_back("");
96
97         if(tgt.needs_rebuild())
98                 row.push_back(tgt.get_rebuild_reason());
99
100         table.push_back(row);
101
102         if(!max_depth || depth<max_depth-1)
103         {
104                 Target::Dependencies depends;
105                 if(mode==RDEPS)
106                 {
107                         const set<Target *> &rdeps = rdepends[&tgt];
108                         depends.assign(rdeps.begin(), rdeps.end());
109                 }
110                 else
111                 {
112                         depends = tgt.get_dependencies();
113                         const Target::Dependencies &tdeps = tgt.get_transitive_dependencies();
114                         depends.insert(depends.end(), tdeps.begin(), tdeps.end());
115                 }
116
117                 depends.sort(full_paths ? target_order_full : target_order);
118
119                 for(Target *d: depends)
120                         build_depend_table(*d, depth+1);
121         }
122 }
123
124 void Analyzer::print_table() const
125 {
126         vector<string::size_type> col_width;
127
128         // Determine column widths
129         for(const vector<string> &r: table)
130         {
131                 if(col_width.size()<r.size())
132                         col_width.resize(r.size(), 0);
133                 for(unsigned j=0; j<r.size(); ++j)
134                         col_width[j] = max(col_width[j], r[j].size());
135         }
136
137         for(const vector<string> &r: table)
138         {
139                 string line;
140                 for(unsigned j=0; j<r.size(); ++j)
141                 {
142                         if(j>0)
143                                 line += "  ";
144                         line += lexical_cast<string>(r[j], Fmt("%-s").width(col_width[j]));
145                 }
146                 IO::print("%s\n", line);
147         }
148 }
149
150 bool Analyzer::target_order(const Target *t1, const Target *t2)
151 {
152         return t1->get_name()<t2->get_name();
153 }
154
155 bool Analyzer::target_order_full(const Target *t1, const Target *t2)
156 {
157         const FileTarget *ft1 = dynamic_cast<const FileTarget *>(t1);
158         const FileTarget *ft2 = dynamic_cast<const FileTarget *>(t2);
159         if(!ft1)
160         {
161                 if(ft2)
162                         return true;
163                 return target_order(t1, t2);
164         }
165         else if(!ft2)
166                 return false;
167         return ft1->get_path().str()<ft2->get_path().str();
168 }