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