]> git.tdb.fi Git - builder.git/blob - source/builder.cpp
Avoid a memory leak if Builder::set_architecture is called again
[builder.git] / source / builder.cpp
1 #include <deque>
2 #include <set>
3 #include <msp/core/algorithm.h>
4 #include <msp/core/except.h>
5 #include <msp/core/maputils.h>
6 #include <msp/datafile/parser.h>
7 #include <msp/fs/dir.h>
8 #include <msp/fs/utils.h>
9 #include <msp/io/buffered.h>
10 #include <msp/io/file.h>
11 #include <msp/io/print.h>
12 #include <msp/strings/format.h>
13 #include <msp/time/timedelta.h>
14 #include <msp/time/utils.h>
15 #include "androidtools.h"
16 #include "binarypackage.h"
17 #include "builder.h"
18 #include "builtintools.h"
19 #include "clangtools.h"
20 #include "datatool.h"
21 #include "gnutools.h"
22 #include "installedfile.h"
23 #include "microsofttools.h"
24 #include "package.h"
25 #include "sharedlibrary.h"
26 #include "sourcepackage.h"
27 #include "task.h"
28 #include "virtualtarget.h"
29
30 using namespace std;
31 using namespace Msp;
32
33 Builder::Builder():
34         package_manager(*this),
35         native_arch(*this, string()),
36         vfs(*this),
37         build_graph(*this),
38         logger(&default_logger)
39 {
40         set_architecture(string());
41 }
42
43 Builder::~Builder()
44 {
45         if(current_arch!=&native_arch)
46                 delete current_arch;
47 }
48
49 void Builder::set_architecture(const string &name)
50 {
51         if(current_arch!=&native_arch)
52                 delete current_arch;
53
54         if(name.empty())
55         {
56                 current_arch = &native_arch;
57                 prefix = FS::get_home_dir()/"local";
58         }
59         else
60         {
61                 current_arch = new Architecture(*this, name);
62                 prefix = FS::get_home_dir()/"local"/current_arch->get_name();
63         }
64 }
65
66 vector<string> Builder::get_build_types() const
67 {
68         vector<string> keys;
69         keys.reserve(build_types.size());
70         for(const auto &kvp: build_types)
71                 keys.push_back(kvp.first);
72         return keys;
73 }
74
75 const BuildType &Builder::get_build_type() const
76 {
77         if(!build_type)
78                 throw invalid_state("no build type");
79         return *build_type;
80 }
81
82 void Builder::set_build_type(const string &name)
83 {
84         build_type = &get_item(build_types, name);
85 }
86
87 void Builder::set_prefix(const FS::Path &p)
88 {
89         prefix = p;
90 }
91
92 void Builder::set_temp_directory(const FS::Path &p)
93 {
94         tempdir = p;
95 }
96
97 void Builder::add_default_tools()
98 {
99         toolchain.add_toolchain(new GnuTools(*this, *current_arch));
100         toolchain.add_toolchain(new ClangTools(*this, *current_arch));
101         if(current_arch->get_system()=="android")
102                 toolchain.add_toolchain(new AndroidTools(*this, *current_arch));
103         if(current_arch->get_system()=="windows")
104                 toolchain.add_toolchain(new MicrosoftTools(*this, *current_arch));
105         toolchain.add_toolchain(new BuiltinTools(*this));
106         toolchain.add_tool(new DataTool(*this));
107
108         auto i = find_if(toolchain.get_toolchains(), [](const Toolchain *tc){ return (tc->has_tool("CC") || tc->has_tool("CXX")); });
109         if(i!=toolchain.get_toolchains().end())
110                 current_arch->refine((*i)->get_name());
111 }
112
113 void Builder::set_logger(const Logger *l)
114 {
115         logger = (l ? l : &default_logger);
116 }
117
118 vector<string> Builder::collect_problems() const
119 {
120         vector<string> problems;
121         set<const Package *> broken_packages;
122         set<const Component *> broken_components;
123         set<const Tool *> broken_tools;
124
125         for(const auto &kvp: build_graph.get_targets())
126                 if(kvp.second->is_broken())
127                 {
128                         for(const string &p: kvp.second->get_problems())
129                                 problems.push_back(format("%s: %s", kvp.second->get_name(), p));
130
131                         const Package *package = kvp.second->get_package();
132                         if(package && !package->get_problems().empty())
133                                 broken_packages.insert(package);
134
135                         const Component *component = kvp.second->get_component();
136                         if(component && !component->get_problems().empty())
137                                 broken_components.insert(component);
138
139                         const Tool *tool = kvp.second->get_tool();
140                         if(tool && !tool->get_problems().empty())
141                                 broken_tools.insert(tool);
142                 }
143
144         // TODO Sort components after their packages, and targets last
145         for(const Package *p: broken_packages)
146                 for(const string &b: p->get_problems())
147                         problems.push_back(format("%s: %s", p->get_name(), b));
148
149         for(const Component *c: broken_components)
150                 for(const string &b: c->get_problems())
151                         problems.push_back(format("%s/%s: %s", c->get_package().get_name(), c->get_name(), b));
152
153         for(const Tool *t: broken_tools)
154                 for(const string &b: t->get_problems())
155                         problems.push_back(format("%s: %s", t->get_tag(), b));
156
157         return problems;
158 }
159
160 void Builder::load_build_file(const FS::Path &fn, const Config::InputOptions *opts, bool all)
161 {
162         IO::BufferedFile in(fn.str());
163
164         get_logger().log("files", "Reading %s", fn);
165
166         DataFile::Parser parser(in, fn.str());
167         Loader loader(*this, opts, all);
168         loader.load(parser);
169 }
170
171 void Builder::save_caches()
172 {
173         for(const auto &kvp: package_manager.get_packages())
174                 kvp.second->save_caches();
175 }
176
177 int Builder::build(unsigned jobs, bool dry_run, bool show_progress)
178 {
179         unsigned total = build_graph.count_rebuild_targets();
180
181         if(!total)
182         {
183                 get_logger().log("summary", "Already up to date");
184                 return 0;
185         }
186         get_logger().log("summary", "Will build %d target%s", total, (total!=1 ? "s" : ""));
187
188         vector<Task *> tasks;
189
190         unsigned count = 0;
191
192         bool fail = false;
193         bool finish = false;
194         bool starved = false;
195
196         while(!finish)
197         {
198                 if(tasks.size()<jobs && !fail && !starved)
199                 {
200                         Target *tgt = build_graph.get_buildable_target();
201                         if(tgt)
202                         {
203                                 if(tgt->get_tool())
204                                 {
205                                         if(show_progress)
206                                                 IO::print("\033[K");
207                                         get_logger().log("tasks", "%-4s  %s", tgt->get_tool()->get_tag(), tgt->get_name());
208                                 }
209                                 Task *task = tgt->build();
210                                 if(task)
211                                 {
212                                         get_logger().log("commands", "%s", task->get_command());
213                                         if(dry_run)
214                                         {
215                                                 task->signal_finished.emit(true);
216                                                 delete task;
217                                         }
218                                         else
219                                         {
220                                                 task->start();
221                                                 tasks.push_back(task);
222                                         }
223                                 }
224
225                                 if(show_progress)
226                                         IO::print("%d of %d target%s built\033[1G", count, total, (total!=1 ? "s" : ""));
227                         }
228                         else if(tasks.empty())
229                                 finish = true;
230                         else
231                                 starved = true;
232                 }
233                 else
234                         Time::sleep(10*Time::msec);
235
236                 for(unsigned i=0; i<tasks.size();)
237                 {
238                         Task::Status status;
239                         if(jobs==1 || (tasks.size()==1 && starved))
240                                 status = tasks[i]->wait();
241                         else
242                                 status = tasks[i]->check();
243
244                         if(status!=Task::RUNNING)
245                         {
246                                 ++count;
247
248                                 delete tasks[i];
249                                 tasks.erase(tasks.begin()+i);
250                                 if(status==Task::ERROR)
251                                         fail = true;
252                                 if(tasks.empty() && fail)
253                                         finish = true;
254                                 starved = false;
255                         }
256                         else
257                                 ++i;
258                 }
259         }
260
261         if(show_progress)
262                 IO::print("\033[K");
263         if(fail)
264                 get_logger().log("summary", "Build failed");
265         else if(show_progress)
266                 get_logger().log("summary", "Build complete");
267
268         return fail;
269 }
270
271 int Builder::clean(bool all, bool dry_run)
272 {
273         // Cleaning doesn't care about ordering, so a simpler method can be used
274
275         set<Target *> clean_tgts;
276         deque<Target *> queue;
277         queue.push_back(&build_graph.get_goals());
278
279         while(!queue.empty())
280         {
281                 Target *tgt = queue.front();
282                 queue.pop_front();
283
284                 if(tgt->is_buildable() && (tgt->get_package()==&package_manager.get_main_package() || all))
285                         clean_tgts.insert(tgt);
286
287                 for(Target *t: tgt->get_dependencies())
288                         if(!clean_tgts.count(t))
289                                 queue.push_back(t);
290         }
291
292         for(Target *t: clean_tgts)
293         {
294                 get_logger().log("tasks", "RM    %s", t->get_name());
295                 if(!dry_run)
296                         t->clean();
297         }
298
299         return 0;
300 }
301
302
303 Builder::Loader::Loader(Builder &b, const Config::InputOptions *o, bool a):
304         DataFile::ObjectLoader<Builder>(b),
305         options(o),
306         conf_all(a)
307 {
308         add("architecture", &Loader::architecture);
309         add("binary_package", &Loader::binpkg);
310         add("build_type", &Loader::build_type);
311         add("package", &Loader::package);
312
313         if(!obj.top_loader)
314                 obj.top_loader = this;
315         else if(!options && obj.top_loader!=this && obj.top_loader->conf_all)
316                 options = obj.top_loader->options;
317 }
318
319 Builder::Loader::~Loader()
320 {
321         if(obj.top_loader==this)
322                 obj.top_loader = 0;
323 }
324
325 void Builder::Loader::architecture(const string &n)
326 {
327         if(obj.current_arch->match_name(n))
328                 load_sub(*obj.current_arch);
329 }
330
331 void Builder::Loader::binpkg(const string &n)
332 {
333         BinaryPackage *pkg = new BinaryPackage(obj, n);
334         load_sub(*pkg);
335 }
336
337 void Builder::Loader::build_type(const string &n)
338 {
339         BuildType btype(n);
340         load_sub(btype);
341         auto i = obj.build_types.insert({ n, btype }).first;
342         if(!obj.build_type)
343                 obj.build_type = &i->second;
344 }
345
346 void Builder::Loader::package(const string &n)
347 {
348         SourcePackage *pkg = new SourcePackage(obj, n, get_source());
349
350         load_sub(*pkg, options);
351
352         if(obj.build_type)
353                 pkg->set_build_type(*obj.build_type);
354 }