]> git.tdb.fi Git - builder.git/blob - source/target.cpp
Improvements for handling side effects
[builder.git] / source / target.cpp
1 #include <msp/fs/stat.h>
2 #include <msp/fs/utils.h>
3 #include <msp/strings/format.h>
4 #include "builder.h"
5 #include "filetarget.h"
6 #include "sourcepackage.h"
7 #include "target.h"
8 #include "task.h"
9 #include "tool.h"
10
11 using namespace std;
12 using namespace Msp;
13
14 Target::Target(Builder &b, const string &n):
15         builder(b),
16         package(0),
17         component(0),
18         name(n),
19         tool(0),
20         state(INIT),
21         primary_target(0)
22 {
23         builder.get_build_graph().add_target(this);
24 }
25
26 void Target::add_dependency(Target &dep)
27 {
28         if(&dep==this)
29                 throw invalid_argument("Target::add_depend");
30         depends.push_back(&dep);
31         if(state>PREPARING)
32                 dep.signal_bubble_rebuild.connect(sigc::mem_fun(this, &Target::check_rebuild));
33 }
34
35 void Target::add_side_effect(Target &se)
36 {
37         side_effects.push_back(&se);
38         se.add_dependency(*this);
39         if(tool)
40                 se.set_tool(*tool);
41         se.primary_target = this;
42         /* Side effects are checked for rebuild after the primary target.  Recheck
43         the primary if a side effect is marked for rebuild. */
44         se.signal_bubble_rebuild.connect(sigc::mem_fun(this, &Target::check_rebuild));
45 }
46
47 Target *Target::get_buildable_target()
48 {
49         if(primary_target)
50                 return primary_target->get_buildable_target();
51         if(!needs_rebuild())
52                 return 0;
53
54         bool self_ok = state!=BUILDING;
55         for(Dependencies::iterator i=depends.begin(); i!=depends.end(); ++i)
56         {
57                 Target *tgt = (*i)->get_buildable_target();
58                 if(tgt)
59                         return tgt;
60                 else if((*i)->needs_rebuild())
61                         self_ok = false;
62         }
63
64         if(self_ok)
65                 return this;
66
67         return 0;
68 }
69
70 void Target::set_tool(Tool &t)
71 {
72         tool = &t;
73         for(Dependencies::const_iterator i=side_effects.begin(); i!=side_effects.end(); ++i)
74                 (*i)->set_tool(t);
75 }
76
77 void Target::force_rebuild()
78 {
79         if(!is_buildable())
80                 throw logic_error("Target::force_rebuild");
81         mark_rebuild("Forced rebuild");
82 }
83
84 void Target::mark_rebuild(const string &reason)
85 {
86         if(reason.empty())
87                 throw invalid_argument("No reason given for rebuilding "+name);
88
89         state = REBUILD;
90         rebuild_reason = reason;
91
92         builder.get_logger().log("rebuild", format("Rebuilding %s: %s", name, reason));
93
94         signal_bubble_rebuild.emit();
95 }
96
97 void Target::prepare()
98 {
99         if(state>PREPARING)
100                 return;
101         if(state==PREPARING)
102         {
103                 problems.push_back("Dependency cycle detected");
104                 state = BROKEN;
105                 return;
106         }
107
108         state = PREPARING;
109         if(tool)
110                 tool->prepare();
111
112         find_dependencies();
113         bool broken = !problems.empty();
114
115         if(tool)
116         {
117                 if(FileTarget *tool_exe = tool->get_executable())
118                         add_dependency(*tool_exe);
119                 broken |= !tool->get_problems().empty();
120
121                 // Only check package and component problems for buildable targets
122                 // XXX How to propagate nested package problems?
123                 broken |= (package && !package->get_problems().empty());
124                 broken |= (component && !component->get_problems().empty());
125         }
126
127         for(Dependencies::iterator i=depends.begin(); i!=depends.end(); ++i)
128         {
129                 (*i)->prepare();
130                 broken |= (*i)->is_broken();
131         }
132
133         check_rebuild();
134         if(broken)
135                 state = BROKEN;
136         else if(state==PREPARING)
137                 state = UPTODATE;
138
139         for(Dependencies::iterator i=depends.begin(); i!=depends.end(); ++i)
140                 (*i)->signal_bubble_rebuild.connect(sigc::mem_fun(this, &Target::check_rebuild));
141 }
142
143 Task *Target::build()
144 {
145         if(primary_target)
146                 return primary_target->build();
147
148         Task *task = tool->run(*this);
149         task->signal_finished.connect(sigc::mem_fun(this, &Target::build_finished));
150         state = BUILDING;
151
152         return task;
153 }
154
155 void Target::build_finished(bool success)
156 {
157         state = UPTODATE;
158         if(success)
159         {
160                 modified();
161                 for(Dependencies::const_iterator i=side_effects.begin(); i!=side_effects.end(); ++i)
162                         (*i)->build_finished(success);
163         }
164 }