]> git.tdb.fi Git - builder.git/blob - source/lib/builder.cpp
Avoid double deletion of the module if a plugin fails to load
[builder.git] / source / lib / 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 "binarypackage.h"
16 #include "builder.h"
17 #include "installedfile.h"
18 #include "package.h"
19 #include "plugin.h"
20 #include "sharedlibrary.h"
21 #include "task.h"
22 #include "tool.h"
23 #include "virtualtarget.h"
24
25 using namespace std;
26 using namespace Msp;
27
28 Builder::Builder():
29         package_manager(*this),
30         native_arch(*this, string()),
31         vfs(*this),
32         build_graph(*this),
33         logger(&default_logger)
34 {
35         set_architecture(string());
36 }
37
38 Builder::~Builder()
39 {
40         if(current_arch!=&native_arch)
41                 delete current_arch;
42 }
43
44 void Builder::load_plugins()
45 {
46         using CreateFunc = Plugin *(Builder &);
47
48         FS::Path plugins_dir = FS::get_sys_lib_dir();
49         logger->log("files", "Traversing %s", plugins_dir);
50         for(const string &f: list_filtered(plugins_dir, "\\.dlm$"))
51         {
52                 LoadedPlugin plugin;
53                 plugin.path = plugins_dir/f;
54
55                 try
56                 {
57                         plugin.module = new Module(plugin.path.str());
58                 }
59                 catch(const exception &exc)
60                 {
61                         logger->log("plugins", "Failed to load plugin %s: %s", f, exc.what());
62                         continue;
63                 }
64
65                 try
66                 {
67                         CreateFunc *create_func = reinterpret_cast<CreateFunc *>(plugin.module->get_symbol("create_plugin"));
68                         plugin.plugin = create_func(*this);
69                         if(plugin.plugin)
70                         {
71                                 logger->log("plugins", "Loaded plugin %s", f);
72                                 plugins.emplace_back(move(plugin));
73                                 continue;
74                         }
75                         else
76                                 logger->log("plugins", "Plugin %s refused to initialize", f);
77                 }
78                 catch(const exception &exc)
79                 {
80                         logger->log("plugins", "Failed to initialize plugin %s: %s", f, exc.what());
81                 }
82         }
83 }
84
85 void Builder::set_architecture(const string &name)
86 {
87         if(current_arch!=&native_arch)
88                 delete current_arch;
89
90         if(name.empty())
91                 current_arch = &native_arch;
92         else
93                 current_arch = new Architecture(*this, name);
94
95         if(auto_prefix)
96                 update_auto_prefix();
97 }
98
99 vector<string> Builder::get_build_types() const
100 {
101         vector<string> keys;
102         keys.reserve(build_types.size());
103         for(const auto &kvp: build_types)
104                 keys.push_back(kvp.first);
105         return keys;
106 }
107
108 const BuildType &Builder::get_build_type() const
109 {
110         if(!build_type)
111                 throw invalid_state("no build type");
112         return *build_type;
113 }
114
115 void Builder::set_build_type(const string &name)
116 {
117         build_type = &get_item(build_types, name);
118 }
119
120 void Builder::set_prefix(const FS::Path &p)
121 {
122         auto_prefix = false;
123         prefix = p;
124 }
125
126 void Builder::set_temp_directory(const FS::Path &p)
127 {
128         tempdir = p;
129 }
130
131 void Builder::update_auto_prefix()
132 {
133         if(current_arch->is_native())
134                 prefix = FS::get_home_dir()/"local";
135         else
136                 prefix = FS::get_home_dir()/"local"/current_arch->get_name();
137 }
138
139 void Builder::add_default_tools()
140 {
141         for(const LoadedPlugin &p: plugins)
142                 p.plugin->add_tools(toolchain, *current_arch);
143
144         auto i = find_if(toolchain.get_toolchains(), [](const Toolchain *tc){ return (tc->has_tool("CC") || tc->has_tool("CXX")); });
145         if(i!=toolchain.get_toolchains().end())
146         {
147                 current_arch->refine((*i)->get_name());
148                 if(auto_prefix)
149                         update_auto_prefix();
150         }
151 }
152
153 void Builder::set_logger(const Logger *l)
154 {
155         logger = (l ? l : &default_logger);
156 }
157
158 vector<string> Builder::collect_problems() const
159 {
160         vector<string> target_problems;
161         vector<string> problems;
162         vector<const Package *> broken_packages;
163         vector<const Component *> broken_components;
164         vector<const Tool *> broken_tools;
165
166         for(const auto &kvp: build_graph.get_targets())
167                 if(kvp.second->is_broken())
168                 {
169                         const Package *package = kvp.second->get_package();
170                         if(package && package->is_broken())
171                                 collect_broken_packages(*package, broken_packages);
172
173                         const Component *component = kvp.second->get_component();
174                         if(component && component->is_broken() && !any_equals(broken_components, component))
175                         {
176                                 broken_components.push_back(component);
177                                 collect_broken_packages(component->get_package(), broken_packages);
178                                 for(const Package *r: component->get_required_packages())
179                                         if(r->is_broken())
180                                                 collect_broken_packages(*r, broken_packages);
181                         }
182
183                         const Tool *tool = kvp.second->get_tool();
184                         if(tool && tool->is_broken() && !any_equals(broken_tools, tool))
185                         {
186                                 broken_tools.push_back(tool);
187                                 for(const string &p: tool->get_problems())
188                                         problems.push_back(format("%s: %s", tool->get_tag(), p));
189                         }
190
191                         for(const string &p: kvp.second->get_problems())
192                                 target_problems.push_back(format("%s: %s", kvp.second->get_name(), p));
193                 }
194
195         for(const Package *p: broken_packages)
196         {
197                 for(const string &b: p->get_problems())
198                         problems.push_back(format("%s: %s", p->get_name(), b));
199
200                 for(const Component *c: broken_components)
201                         if(&c->get_package()==p)
202                         {
203                                 for(const string &b: c->get_problems())
204                                         problems.push_back(format("%s/%s: %s", p->get_name(), c->get_name(), b));
205                         }
206         }
207
208         problems.insert(problems.end(), make_move_iterator(target_problems.begin()), make_move_iterator(target_problems.end()));
209
210         return problems;
211 }
212
213 void Builder::collect_broken_packages(const Package &pkg, vector<const Package *> &broken) const
214 {
215         if(any_equals(broken, &pkg))
216                 return;
217
218         broken.push_back(&pkg);
219
220         for(const Package *r: pkg.get_required_packages())
221                 if(r->is_broken())
222                         collect_broken_packages(*r, broken);
223 }
224
225 void Builder::load_build_file(const FS::Path &fn, const Config::InputOptions *opts, bool all)
226 {
227         IO::BufferedFile in(fn.str());
228
229         get_logger().log("files", "Reading %s", fn);
230
231         DataFile::Parser parser(in, fn.str());
232         Loader loader(*this, opts, all);
233         loader.load(parser);
234 }
235
236 void Builder::save_caches()
237 {
238         for(const auto &kvp: package_manager.get_packages())
239                 kvp.second->save_caches();
240 }
241
242 int Builder::build(unsigned jobs, bool dry_run, bool show_progress)
243 {
244         unsigned total = build_graph.count_rebuild_targets();
245
246         if(!total)
247         {
248                 get_logger().log("summary", "Already up to date");
249                 return 0;
250         }
251         get_logger().log("summary", "Will build %d target%s", total, (total!=1 ? "s" : ""));
252
253         vector<Task *> tasks;
254
255         unsigned count = 0;
256
257         bool fail = false;
258         bool finish = false;
259         bool starved = false;
260
261         while(!finish)
262         {
263                 if(tasks.size()<jobs && !fail && !starved)
264                 {
265                         Target *tgt = build_graph.get_buildable_target();
266                         if(tgt)
267                         {
268                                 if(tgt->get_tool())
269                                 {
270                                         if(show_progress)
271                                                 IO::print("\033[K");
272                                         get_logger().log("tasks", "%-4s  %s", tgt->get_tool()->get_tag(), tgt->get_name());
273                                 }
274                                 Task *task = tgt->build();
275                                 if(task)
276                                 {
277                                         get_logger().log("commands", "%s", task->get_command());
278                                         if(dry_run)
279                                         {
280                                                 task->signal_finished.emit(true);
281                                                 delete task;
282                                         }
283                                         else
284                                         {
285                                                 task->start();
286                                                 tasks.push_back(task);
287                                         }
288                                 }
289
290                                 if(show_progress)
291                                         IO::print("%d of %d target%s built\033[1G", count, total, (total!=1 ? "s" : ""));
292                         }
293                         else if(tasks.empty())
294                                 finish = true;
295                         else
296                                 starved = true;
297                 }
298                 else
299                         Time::sleep(10*Time::msec);
300
301                 for(unsigned i=0; i<tasks.size();)
302                 {
303                         Task::Status status;
304                         if(jobs==1 || (tasks.size()==1 && starved))
305                                 status = tasks[i]->wait();
306                         else
307                                 status = tasks[i]->check();
308
309                         if(status!=Task::RUNNING)
310                         {
311                                 ++count;
312
313                                 delete tasks[i];
314                                 tasks.erase(tasks.begin()+i);
315                                 if(status==Task::ERROR)
316                                         fail = true;
317                                 if(tasks.empty() && fail)
318                                         finish = true;
319                                 starved = false;
320                         }
321                         else
322                                 ++i;
323                 }
324         }
325
326         if(show_progress)
327                 IO::print("\033[K");
328         if(fail)
329                 get_logger().log("summary", "Build failed");
330         else if(show_progress)
331                 get_logger().log("summary", "Build complete");
332
333         return fail;
334 }
335
336 int Builder::clean(bool all, bool dry_run)
337 {
338         // Cleaning doesn't care about ordering, so a simpler method can be used
339
340         set<Target *> clean_tgts;
341         deque<Target *> queue;
342         queue.push_back(&build_graph.get_goals());
343
344         while(!queue.empty())
345         {
346                 Target *tgt = queue.front();
347                 queue.pop_front();
348
349                 if(tgt->is_buildable() && (tgt->get_package()==&package_manager.get_main_package() || all))
350                         clean_tgts.insert(tgt);
351
352                 for(Target *t: tgt->get_dependencies())
353                         if(!clean_tgts.count(t))
354                                 queue.push_back(t);
355         }
356
357         for(Target *t: clean_tgts)
358         {
359                 get_logger().log("tasks", "RM    %s", t->get_name());
360                 if(!dry_run)
361                         t->clean();
362         }
363
364         return 0;
365 }
366
367
368 Builder::LoadedPlugin::LoadedPlugin(LoadedPlugin &&other):
369         path(move(other.path)),
370         module(other.module),
371         plugin(other.plugin)
372 {
373         other.module = 0;
374         other.plugin = 0;
375 }
376
377 Builder::LoadedPlugin::~LoadedPlugin()
378 {
379         delete plugin;
380         delete module;
381 }
382
383
384 Builder::Loader::Loader(Builder &b, const Config::InputOptions *o, bool a):
385         DataFile::ObjectLoader<Builder>(b),
386         options(o),
387         conf_all(a)
388 {
389         add("architecture", &Loader::architecture);
390         add("binary_package", &Loader::binpkg);
391         add("build_type", &Loader::build_type);
392         add("package", &Loader::package);
393
394         if(!obj.top_loader)
395                 obj.top_loader = this;
396         else if(!options && obj.top_loader!=this && obj.top_loader->conf_all)
397                 options = obj.top_loader->options;
398 }
399
400 Builder::Loader::~Loader()
401 {
402         if(obj.top_loader==this)
403                 obj.top_loader = 0;
404 }
405
406 void Builder::Loader::architecture(const string &n)
407 {
408         if(obj.current_arch->match_name(n))
409                 load_sub(*obj.current_arch);
410 }
411
412 void Builder::Loader::binpkg(const string &n)
413 {
414         BinaryPackage *pkg = new BinaryPackage(obj, n);
415         load_sub(*pkg);
416 }
417
418 void Builder::Loader::build_type(const string &n)
419 {
420         BuildType btype(n);
421         load_sub(btype);
422         auto i = obj.build_types.insert({ n, btype }).first;
423         if(!obj.build_type)
424                 obj.build_type = &i->second;
425 }
426
427 void Builder::Loader::package(const string &n)
428 {
429         SourcePackage *pkg = new SourcePackage(obj, n, get_source());
430
431         load_sub(*pkg, options);
432
433         if(obj.build_type)
434                 pkg->set_build_type(*obj.build_type);
435 }