]> git.tdb.fi Git - builder.git/blob - source/lib/target.cpp
558b3b2c107485594cb8831c40d5cdffb2c238fb
[builder.git] / source / lib / target.cpp
1 #include <msp/core/algorithm.h>
2 #include <msp/fs/stat.h>
3 #include <msp/fs/utils.h>
4 #include <msp/strings/utils.h>
5 #include "builder.h"
6 #include "filetarget.h"
7 #include "sourcepackage.h"
8 #include "target.h"
9 #include "task.h"
10 #include "tool.h"
11
12 using namespace std;
13 using namespace Msp;
14
15 vector<Target *> Target::prepare_stack;
16
17 Target::Target(Builder &b, const string &n):
18         builder(b),
19         name(n)
20 {
21         builder.get_build_graph().add_target(this);
22 }
23
24 void Target::add_dependency(Target &dep)
25 {
26         if(&dep==this)
27                 throw invalid_argument("Target::add_depend");
28         depends.push_back(&dep);
29         if(state>PREPARING)
30                 dep.signal_bubble_rebuild.connect(sigc::mem_fun(this, &Target::check_rebuild));
31 }
32
33 void Target::add_transitive_dependency(Target &dep)
34 {
35         if(&dep==this)
36                 throw invalid_argument("Target::add_transitive_dependency");
37         trans_depends.push_back(&dep);
38 }
39
40 void Target::add_side_effect(Target &se)
41 {
42         side_effects.push_back(&se);
43         if(tool)
44                 se.set_tool(*tool);
45         se.primary_target = this;
46         /* Side effects are checked for rebuild after the primary target.  Recheck
47         the primary if a side effect is marked for rebuild. */
48         se.signal_bubble_rebuild.connect(sigc::mem_fun(this, &Target::check_rebuild));
49 }
50
51 Target *Target::get_buildable_target()
52 {
53         if(primary_target)
54                 return primary_target->get_buildable_target();
55         if(!needs_rebuild())
56                 return 0;
57
58         bool self_ok = state!=BUILDING;
59         for(Target *d: depends)
60         {
61                 // Avoid infinite recursion if a target depends on its own side effect
62                 if(any_equals(side_effects, d))
63                         continue;
64
65                 Target *tgt = d->get_buildable_target();
66                 if(tgt)
67                         return tgt;
68                 else if(d->needs_rebuild())
69                         self_ok = false;
70         }
71
72         if(self_ok)
73                 return this;
74
75         return 0;
76 }
77
78 void Target::set_tool(Tool &t)
79 {
80         tool = &t;
81         for(Target *s: side_effects)
82                 s->set_tool(t);
83 }
84
85 void Target::collect_build_info(BuildInfo &binfo) const
86 {
87         if(tool)
88                 binfo.update_from(tool->get_build_info());
89         if(component)
90                 binfo.update_from(component->get_build_info());
91         else if(package)
92                 binfo.update_from(package->get_build_info());
93 }
94
95 void Target::force_rebuild()
96 {
97         if(!is_buildable())
98                 throw logic_error("Target::force_rebuild");
99         mark_rebuild("Forced rebuild");
100 }
101
102 void Target::mark_rebuild(const string &reason)
103 {
104         if(reason.empty())
105                 throw invalid_argument("No reason given for rebuilding "+name);
106
107         state = REBUILD;
108         rebuild_reason = reason;
109
110         builder.get_logger().log("rebuild", "Rebuilding %s: %s", name, reason);
111
112         signal_bubble_rebuild.emit();
113 }
114
115 void Target::prepare()
116 {
117         if(primary_target && primary_target->state<PREPARING)
118                 primary_target->prepare();
119
120         if(state>PREPARING)
121                 return;
122         if(state==PREPARING)
123         {
124                 auto i = find(prepare_stack, this);
125                 if(i!=prepare_stack.end())
126                 {
127                         string cycle;
128                         for(; i!=prepare_stack.end(); ++i)
129                                 append(cycle, " -> ", (*i)->name);
130                         append(cycle, " -> ", name);
131                         builder.get_logger().log("problems", "Dependency cycle detected: %s", cycle);
132                         problems.push_back(format("Dependency cycle detected: %s", cycle));
133                 }
134                 else
135                 {
136                         builder.get_logger().log("problems", "Dependency cycle detected at %s", name);
137                         problems.push_back("Dependency cycle detected");
138                 }
139                 state = BROKEN;
140                 return;
141         }
142
143         PushPrepare _push(this);
144         state = PREPARING;
145         /* Prepare existing dependencies early, because their information may be
146         needed to find other dependencies. */
147         for(Target *d: depends)
148                 d->prepare();
149         if(tool)
150                 tool->prepare();
151
152         find_dependencies();
153         bool broken = !problems.empty();
154
155         if(tool)
156         {
157                 if(FileTarget *tool_exe = tool->get_executable())
158                         add_dependency(*tool_exe);
159                 broken |= tool->is_broken();
160
161                 // Only check package and component brokenness for buildable targets
162                 broken |= (package && package->is_broken());
163                 broken |= (component && component->is_broken());
164         }
165
166         /* Now that all dependencies are known, prepare them again.  This will do
167         nothing to already prepared targets. */
168         for(Target *d: depends)
169         {
170                 d->prepare();
171                 broken |= d->is_broken();
172         }
173         for(Target *d: trans_depends)
174                 d->prepare();
175
176         check_rebuild();
177         if(broken)
178                 state = BROKEN;
179         else if(state==PREPARING)
180                 state = UPTODATE;
181
182         for(Target *d: depends)
183                 d->signal_bubble_rebuild.connect(sigc::mem_fun(this, &Target::check_rebuild));
184 }
185
186 Task *Target::build()
187 {
188         if(primary_target)
189                 return primary_target->build();
190
191         Task *task = tool->run(*this);
192         task->signal_finished.connect(sigc::mem_fun(this, &Target::build_finished));
193         state = BUILDING;
194
195         build(*task);
196         for(Target *s: side_effects)
197                 s->build(*task);
198
199         return task;
200 }
201
202 void Target::build_finished(bool success)
203 {
204         state = UPTODATE;
205         if(success)
206         {
207                 modified();
208                 for(Target *s: side_effects)
209                         s->build_finished(success);
210                 signal_modified.emit();
211         }
212 }