]> git.tdb.fi Git - builder.git/blob - source/lib/builder.cpp
Convert the remaining tools into actual plugins
[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                 delete plugin.module;
84         }
85 }
86
87 void Builder::set_architecture(const string &name)
88 {
89         if(current_arch!=&native_arch)
90                 delete current_arch;
91
92         if(name.empty())
93                 current_arch = &native_arch;
94         else
95                 current_arch = new Architecture(*this, name);
96
97         if(auto_prefix)
98                 update_auto_prefix();
99 }
100
101 vector<string> Builder::get_build_types() const
102 {
103         vector<string> keys;
104         keys.reserve(build_types.size());
105         for(const auto &kvp: build_types)
106                 keys.push_back(kvp.first);
107         return keys;
108 }
109
110 const BuildType &Builder::get_build_type() const
111 {
112         if(!build_type)
113                 throw invalid_state("no build type");
114         return *build_type;
115 }
116
117 void Builder::set_build_type(const string &name)
118 {
119         build_type = &get_item(build_types, name);
120 }
121
122 void Builder::set_prefix(const FS::Path &p)
123 {
124         auto_prefix = false;
125         prefix = p;
126 }
127
128 void Builder::set_temp_directory(const FS::Path &p)
129 {
130         tempdir = p;
131 }
132
133 void Builder::update_auto_prefix()
134 {
135         if(current_arch->is_native())
136                 prefix = FS::get_home_dir()/"local";
137         else
138                 prefix = FS::get_home_dir()/"local"/current_arch->get_name();
139 }
140
141 void Builder::add_default_tools()
142 {
143         for(const LoadedPlugin &p: plugins)
144                 p.plugin->add_tools(toolchain, *current_arch);
145
146         auto i = find_if(toolchain.get_toolchains(), [](const Toolchain *tc){ return (tc->has_tool("CC") || tc->has_tool("CXX")); });
147         if(i!=toolchain.get_toolchains().end())
148         {
149                 current_arch->refine((*i)->get_name());
150                 if(auto_prefix)
151                         update_auto_prefix();
152         }
153 }
154
155 void Builder::set_logger(const Logger *l)
156 {
157         logger = (l ? l : &default_logger);
158 }
159
160 vector<string> Builder::collect_problems() const
161 {
162         vector<string> problems;
163         set<const Package *> broken_packages;
164         set<const Component *> broken_components;
165         set<const Tool *> broken_tools;
166
167         for(const auto &kvp: build_graph.get_targets())
168                 if(kvp.second->is_broken())
169                 {
170                         for(const string &p: kvp.second->get_problems())
171                                 problems.push_back(format("%s: %s", kvp.second->get_name(), p));
172
173                         const Package *package = kvp.second->get_package();
174                         if(package && !package->get_problems().empty())
175                                 broken_packages.insert(package);
176
177                         const Component *component = kvp.second->get_component();
178                         if(component && !component->get_problems().empty())
179                                 broken_components.insert(component);
180
181                         const Tool *tool = kvp.second->get_tool();
182                         if(tool && !tool->get_problems().empty())
183                                 broken_tools.insert(tool);
184                 }
185
186         // TODO Sort components after their packages, and targets last
187         for(const Package *p: broken_packages)
188                 for(const string &b: p->get_problems())
189                         problems.push_back(format("%s: %s", p->get_name(), b));
190
191         for(const Component *c: broken_components)
192                 for(const string &b: c->get_problems())
193                         problems.push_back(format("%s/%s: %s", c->get_package().get_name(), c->get_name(), b));
194
195         for(const Tool *t: broken_tools)
196                 for(const string &b: t->get_problems())
197                         problems.push_back(format("%s: %s", t->get_tag(), b));
198
199         return problems;
200 }
201
202 void Builder::load_build_file(const FS::Path &fn, const Config::InputOptions *opts, bool all)
203 {
204         IO::BufferedFile in(fn.str());
205
206         get_logger().log("files", "Reading %s", fn);
207
208         DataFile::Parser parser(in, fn.str());
209         Loader loader(*this, opts, all);
210         loader.load(parser);
211 }
212
213 void Builder::save_caches()
214 {
215         for(const auto &kvp: package_manager.get_packages())
216                 kvp.second->save_caches();
217 }
218
219 int Builder::build(unsigned jobs, bool dry_run, bool show_progress)
220 {
221         unsigned total = build_graph.count_rebuild_targets();
222
223         if(!total)
224         {
225                 get_logger().log("summary", "Already up to date");
226                 return 0;
227         }
228         get_logger().log("summary", "Will build %d target%s", total, (total!=1 ? "s" : ""));
229
230         vector<Task *> tasks;
231
232         unsigned count = 0;
233
234         bool fail = false;
235         bool finish = false;
236         bool starved = false;
237
238         while(!finish)
239         {
240                 if(tasks.size()<jobs && !fail && !starved)
241                 {
242                         Target *tgt = build_graph.get_buildable_target();
243                         if(tgt)
244                         {
245                                 if(tgt->get_tool())
246                                 {
247                                         if(show_progress)
248                                                 IO::print("\033[K");
249                                         get_logger().log("tasks", "%-4s  %s", tgt->get_tool()->get_tag(), tgt->get_name());
250                                 }
251                                 Task *task = tgt->build();
252                                 if(task)
253                                 {
254                                         get_logger().log("commands", "%s", task->get_command());
255                                         if(dry_run)
256                                         {
257                                                 task->signal_finished.emit(true);
258                                                 delete task;
259                                         }
260                                         else
261                                         {
262                                                 task->start();
263                                                 tasks.push_back(task);
264                                         }
265                                 }
266
267                                 if(show_progress)
268                                         IO::print("%d of %d target%s built\033[1G", count, total, (total!=1 ? "s" : ""));
269                         }
270                         else if(tasks.empty())
271                                 finish = true;
272                         else
273                                 starved = true;
274                 }
275                 else
276                         Time::sleep(10*Time::msec);
277
278                 for(unsigned i=0; i<tasks.size();)
279                 {
280                         Task::Status status;
281                         if(jobs==1 || (tasks.size()==1 && starved))
282                                 status = tasks[i]->wait();
283                         else
284                                 status = tasks[i]->check();
285
286                         if(status!=Task::RUNNING)
287                         {
288                                 ++count;
289
290                                 delete tasks[i];
291                                 tasks.erase(tasks.begin()+i);
292                                 if(status==Task::ERROR)
293                                         fail = true;
294                                 if(tasks.empty() && fail)
295                                         finish = true;
296                                 starved = false;
297                         }
298                         else
299                                 ++i;
300                 }
301         }
302
303         if(show_progress)
304                 IO::print("\033[K");
305         if(fail)
306                 get_logger().log("summary", "Build failed");
307         else if(show_progress)
308                 get_logger().log("summary", "Build complete");
309
310         return fail;
311 }
312
313 int Builder::clean(bool all, bool dry_run)
314 {
315         // Cleaning doesn't care about ordering, so a simpler method can be used
316
317         set<Target *> clean_tgts;
318         deque<Target *> queue;
319         queue.push_back(&build_graph.get_goals());
320
321         while(!queue.empty())
322         {
323                 Target *tgt = queue.front();
324                 queue.pop_front();
325
326                 if(tgt->is_buildable() && (tgt->get_package()==&package_manager.get_main_package() || all))
327                         clean_tgts.insert(tgt);
328
329                 for(Target *t: tgt->get_dependencies())
330                         if(!clean_tgts.count(t))
331                                 queue.push_back(t);
332         }
333
334         for(Target *t: clean_tgts)
335         {
336                 get_logger().log("tasks", "RM    %s", t->get_name());
337                 if(!dry_run)
338                         t->clean();
339         }
340
341         return 0;
342 }
343
344
345 Builder::LoadedPlugin::LoadedPlugin(LoadedPlugin &&other):
346         path(move(other.path)),
347         module(other.module),
348         plugin(other.plugin)
349 {
350         other.module = 0;
351         other.plugin = 0;
352 }
353
354 Builder::LoadedPlugin::~LoadedPlugin()
355 {
356         delete plugin;
357         delete module;
358 }
359
360
361 Builder::Loader::Loader(Builder &b, const Config::InputOptions *o, bool a):
362         DataFile::ObjectLoader<Builder>(b),
363         options(o),
364         conf_all(a)
365 {
366         add("architecture", &Loader::architecture);
367         add("binary_package", &Loader::binpkg);
368         add("build_type", &Loader::build_type);
369         add("package", &Loader::package);
370
371         if(!obj.top_loader)
372                 obj.top_loader = this;
373         else if(!options && obj.top_loader!=this && obj.top_loader->conf_all)
374                 options = obj.top_loader->options;
375 }
376
377 Builder::Loader::~Loader()
378 {
379         if(obj.top_loader==this)
380                 obj.top_loader = 0;
381 }
382
383 void Builder::Loader::architecture(const string &n)
384 {
385         if(obj.current_arch->match_name(n))
386                 load_sub(*obj.current_arch);
387 }
388
389 void Builder::Loader::binpkg(const string &n)
390 {
391         BinaryPackage *pkg = new BinaryPackage(obj, n);
392         load_sub(*pkg);
393 }
394
395 void Builder::Loader::build_type(const string &n)
396 {
397         BuildType btype(n);
398         load_sub(btype);
399         auto i = obj.build_types.insert({ n, btype }).first;
400         if(!obj.build_type)
401                 obj.build_type = &i->second;
402 }
403
404 void Builder::Loader::package(const string &n)
405 {
406         SourcePackage *pkg = new SourcePackage(obj, n, get_source());
407
408         load_sub(*pkg, options);
409
410         if(obj.build_type)
411                 pkg->set_build_type(*obj.build_type);
412 }