]> git.tdb.fi Git - builder.git/blob - source/builder.cpp
Add missing includes
[builder.git] / source / builder.cpp
1 #include <fstream>
2 #include <iostream>
3 #include <msp/progress.h>
4 #include <msp/strconv.h>
5 #include <msp/strutils.h>
6 #include <msp/core/error.h>
7 #include <msp/getopt++/getopt++.h>
8 #include <msp/parser/parser.h>
9 #include <msp/path/utils.h>
10 #include <msp/time/units.h>
11 #include <msp/time/utils.h>
12 #include "action.h"
13 #include "analyzer.h"
14 #include "builder.h"
15 #include "executable.h"
16 #include "header.h"
17 #include "install.h"
18 #include "misc.h"
19 #include "objectfile.h"
20 #include "package.h"
21 #include "systemlibrary.h"
22 #include "virtualtarget.h"
23
24 using namespace std;
25 using namespace Msp;
26
27 Builder::Builder(int argc, char **argv):
28         verbose(1),
29         cwd(Path::getcwd()),
30         build_file("Build"),
31         do_build(true),
32         analyzer(0),
33         jobs(1),
34         chrome(false)
35 {
36         GetOpt getopt;
37         getopt.add_option(GetOpt::Option('v', "verbose", GetOpt::NONE));
38         getopt.add_option(GetOpt::Option('a', "analyze", GetOpt::REQUIRED));
39         getopt.add_option(GetOpt::Option('b', "build", GetOpt::NONE));
40         getopt.add_option(GetOpt::Option("max-depth", GetOpt::REQUIRED));
41         getopt.add_option(GetOpt::Option('n', "dry-run", GetOpt::NONE));
42         getopt.add_option(GetOpt::Option('W', "what-if", GetOpt::REQUIRED));
43         getopt.add_option(GetOpt::Option('B', "build-all", GetOpt::NONE));
44         getopt.add_option(GetOpt::Option('C', "chdir", GetOpt::REQUIRED));
45         getopt.add_option(GetOpt::Option('j', "jobs", GetOpt::REQUIRED, "1"));
46         getopt.add_option(GetOpt::Option('h', "help", GetOpt::NONE));
47         getopt.add_option(GetOpt::Option('c', "clean", GetOpt::NONE));
48         getopt.add_option(GetOpt::Option('f', "file", GetOpt::REQUIRED, "Build"));
49         getopt.add_option(GetOpt::Option("chrome", GetOpt::NONE));
50         getopt.add_option(GetOpt::Option("full-paths", GetOpt::NONE));
51         getopt.add_option(GetOpt::Option('A', "conf-all", GetOpt::NONE));
52         int index=getopt(argc, argv);
53
54         verbose+=getopt['v'].count();
55
56         if(getopt['a'])
57         {
58                 analyzer=new Analyzer(*this);
59
60                 string mode=getopt['a'].arg();
61                 if(mode=="deps")
62                         analyzer->set_mode(Analyzer::DEPS);
63                 else if(mode=="alldeps")
64                         analyzer->set_mode(Analyzer::ALLDEPS);
65                 else if(mode=="rebuild")
66                         analyzer->set_mode(Analyzer::REBUILD);
67                 else if(mode=="rdeps")
68                         analyzer->set_mode(Analyzer::RDEPS);
69                 else
70                         throw UsageError("Invalid analysis mode");
71
72                 if(getopt["max-depth"])
73                         analyzer->set_max_depth(strtol(getopt["max-depth"].arg()));
74                 analyzer->set_full_paths(getopt["full-paths"]);
75
76                 if(!getopt['b'])
77                         do_build=false;
78         }
79
80         dry_run=getopt['n'];
81
82         jobs=max(strtol(getopt['j'].arg()), 1L);
83
84         chrome=getopt["chrome"];
85
86         conf_all=getopt['A'];
87
88         if(getopt['C'])
89                 chdir(getopt['C'].arg().c_str());
90
91         build_file=getopt['f'].arg();
92
93         for(int i=index; i<argc; ++i)
94         {
95                 string v(argv[i]);
96                 unsigned equal=v.find('=');
97                 if(equal!=string::npos)
98                         cmdline_options.insert(RawOptionMap::value_type(v.substr(0, equal), v.substr(equal+1)));
99                 else
100                         cmdline_targets.push_back(argv[i]);
101         }
102
103         if(cmdline_targets.empty())
104                 cmdline_targets.push_back("default");
105
106         if(getopt['W'])
107                 what_if.push_back(getopt['W'].arg());
108 }
109
110 /**
111 Gets a package with the specified name, possibly creating it.
112
113 @param   n  Package name
114
115 @return  Pointer to the package, or 0 if the package could not be located
116 */
117 Package *Builder::get_package(const string &n)
118 {
119         PackageMap::iterator i=packages.find(n);
120         if(i!=packages.end())
121                 return i->second;
122
123         // Try to get source directory with pkgconfig
124         list<string> argv;
125         argv.push_back("pkg-config");
126         argv.push_back("--variable=source");
127         argv.push_back(n);
128         string srcdir=strip(run_command(argv));
129         
130         PathList dirs;
131         if(!srcdir.empty())
132                 dirs.push_back(srcdir);
133
134         // Make some other guesses about the source directory
135         string dirname=n;
136         if(!dirname.compare(0, 3, "msp"))
137                 dirname.erase(0, 3);
138         dirs.push_back(cwd/dirname);
139         dirs.push_back(cwd/".."/dirname);
140
141         // Go through the candidate directories and look for a Build file
142         for(PathList::iterator j=dirs.begin(); j!=dirs.end(); ++j)
143                 if(!load_build_file(*j/"Build"))
144                 {
145                         i=packages.find(n);
146                         if(i!=packages.end())
147                                 return i->second;
148                         break;
149                 }
150
151         // Package source not found - create a binary package
152         Package *pkg=Package::create(*this, n);
153         packages.insert(PackageMap::value_type(n, pkg));
154         if(pkg)
155                 new_pkgs.push_back(pkg);
156
157         return pkg;
158 }
159
160 /**
161 Returns the target with the given name, or 0 if no such target exists.
162 */
163 Target *Builder::get_target(const string &n)
164 {
165         TargetMap::iterator i=targets.find(n);
166         if(i!=targets.end())
167                 return i->second;
168         return 0;
169 }
170
171 /**
172 Tries to locate a header included from a given location and with a given include
173 path.  Considers known targets as well as existing files.
174 */
175 Target *Builder::get_header(const string &include, const string &from, const list<string> &path)
176 {
177         string hash(8, 0);
178         update_hash(hash, from);
179         for(list<string>::const_iterator i=path.begin(); i!=path.end(); ++i)
180                 update_hash(hash, *i);
181
182         string id=hash+include;
183         TargetMap::iterator i=includes.find(id);
184         if(i!=includes.end())
185                 return i->second;
186
187         string fn=include.substr(1);
188         Target *tgt=0;
189         if(include[0]=='"' && (tgt=check_header(Path::Path(from)/fn)))
190                 return tgt;
191         if((tgt=check_header(Path::Path("/usr/include")/fn)))
192                 return tgt;
193         //XXX Determine the C++ header location dynamically
194         if((tgt=check_header(Path::Path("/usr/include/c++/4.1.2")/fn)))
195                 return tgt;
196         for(list<string>::const_iterator j=path.begin(); j!=path.end(); ++j)
197                 if((tgt=check_header(Path::Path(*j)/fn)))
198                         return tgt;
199         
200         return 0;
201 }
202
203 Target *Builder::get_library(const string &lib, const list<string> &path)
204 {
205         string hash(8, 0);
206         for(list<string>::const_iterator i=path.begin(); i!=path.end(); ++i)
207                 update_hash(hash, *i);
208
209         string id=hash+lib;
210         TargetMap::iterator i=libraries.find(id);
211         if(i!=libraries.end())
212                 return i->second;
213
214         string basename="lib"+lib+".so";
215         for(list<string>::const_iterator j=path.begin(); j!=path.end(); ++j)
216         {
217                 string full=(Path::Path(*j)/basename).str();
218                 Target *tgt=get_target(full);
219                 if(tgt) return tgt;
220                 
221                 if(Path::exists(full))
222                 {
223                         add_target(tgt=new SystemLibrary(*this, full));
224                         return tgt;
225                 }
226         }
227
228         return 0;
229 }
230
231 int Builder::main()
232 {
233         if(load_build_file(cwd/build_file))
234         {
235                 cerr<<"No build info here.\n";
236                 return 1;
237         }
238
239         default_pkg=packages.begin()->second;
240
241         while(!new_pkgs.empty())
242         {
243                 Package *pkg=new_pkgs.front();
244                 if(pkg==default_pkg || conf_all)
245                         pkg->process_options(cmdline_options);
246                 new_pkgs.erase(new_pkgs.begin());
247                 pkg->resolve_refs();
248         }
249
250         std::list<std::string> missing;
251         for(PackageMap::iterator i=packages.begin(); i!=packages.end(); ++i)
252                 if(!i->second)
253                         missing.push_back(i->first);
254
255         if(!missing.empty())
256         {
257                 missing.sort();
258                 cerr<<"The following packages were not found on the system:\n";
259                 for(list<string>::iterator i=missing.begin(); i!=missing.end(); ++i)
260                         cerr<<"  "<<*i<<'\n';
261                 cerr<<"Please install them and try again.\n";
262                 return 1;
263         }
264
265         default_pkg->create_build_info();
266
267         if(create_targets())
268                 return 1;
269
270         cout<<packages.size()<<" packages, "<<targets.size()<<" targets\n";
271         if(verbose>=2)
272         {
273                 for(PackageMap::iterator i=packages.begin(); i!=packages.end(); ++i)
274                 {
275                         cout<<' '<<i->second->get_name();
276                         if(i->second->get_buildable())
277                                 cout<<'*';
278                         unsigned count=0;
279                         for(TargetMap::iterator j=targets.begin(); j!=targets.end(); ++j)
280                                 if(j->second->get_package()==i->second)
281                                         ++count;
282                         cout<<" ("<<count<<" targets)\n";
283                 }
284         }
285
286         if(analyzer)
287                 analyzer->analyze();
288
289         if(do_build)
290                 build();
291
292         return exit_code;
293 }
294
295 Builder::~Builder()
296 {
297         for(PackageMap::iterator i=packages.begin(); i!=packages.end(); ++i)
298                 delete i->second;
299         for(TargetMap::iterator i=targets.begin(); i!=targets.end(); ++i)
300                 delete i->second;
301         delete analyzer;
302 }
303
304 int Builder::load_build_file(const Path::Path &fn)
305 {
306         ifstream in(fn.str().c_str());
307         if(!in)
308                 return -1;
309
310         Parser::Parser parser(in, fn.str());
311         Loader loader(*this, fn.subpath(0, fn.size()-1));
312         loader.load(parser);
313
314         return 0;
315 }
316
317 int Builder::create_targets()
318 {
319         Target *world=new VirtualTarget(*this, "world");
320         add_target(world);
321
322         Target *def_tgt=new VirtualTarget(*this, "default");
323         add_target(def_tgt);
324         world->add_depend(def_tgt);
325
326         Target *install=new VirtualTarget(*this, "install");
327         add_target(install);
328         world->add_depend(install);
329
330         for(PackageMap::iterator i=packages.begin(); i!=packages.end(); ++i)
331         {
332                 if(!i->second)
333                         continue;
334                 if(!i->second->get_buildable())
335                         continue;
336
337                 Path::Path inst_base;
338                 if(i->second->get_config().is_option("prefix"))
339                         inst_base=i->second->get_config().get_option("prefix").value;
340
341                 const ComponentList &components=i->second->get_components();
342                 for(ComponentList::const_iterator j=components.begin(); j!=components.end(); ++j)
343                 {
344                         PathList files;
345                         const PathList &sources=j->get_sources();
346                         for(PathList::const_iterator k=sources.begin(); k!=sources.end(); ++k)
347                         {
348                                 struct stat st;
349                                 stat(*k, st);
350                                 if(S_ISDIR(st.st_mode))
351                                 {
352                                         list<string> sfiles=list_files(*k);
353                                         for(list<string>::iterator l=sfiles.begin(); l!=sfiles.end(); ++l)
354                                                 files.push_back(*k / *l);
355                                 }
356                                 else
357                                         files.push_back(*k);
358                         }
359
360                         bool build_exe=j->get_type()!=Component::HEADERS;
361                         
362                         list<ObjectFile *> objs;
363                         for(PathList::iterator k=files.begin(); k!=files.end(); ++k)
364                         {
365                                 string basename=(*k)[-1];
366                                 string ext=Path::splitext(basename).ext;
367                                 if(ext==".cpp" || ext==".c")
368                                 {
369                                         if(build_exe)
370                                         {
371                                                 SourceFile *src=new SourceFile(*this, &*j, k->str());
372                                                 add_target(src);
373                                                 
374                                                 ObjectFile *obj=new ObjectFile(*this, *j, *src);
375                                                 add_target(obj);
376                                                 objs.push_back(obj);
377                                         }
378                                 }
379                                 else if(ext==".h")
380                                 {
381                                         Target *hdr=get_target(k->str());
382                                         if(!hdr)
383                                         {
384                                                 hdr=new Header(*this, &*j, k->str());
385                                                 add_target(hdr);
386                                         }
387                                         if(!j->get_install_headers().empty())
388                                         {
389                                                 Path::Path inst_path=inst_base/"include"/j->get_install_headers()/basename;
390                                                 Install *inst=new Install(*this, *i->second, *hdr, inst_path.str());
391                                                 add_target(inst);
392                                                 install->add_depend(inst);
393                                         }
394                                 }
395                         }
396
397                         if(build_exe)
398                         {
399                                 Executable *exe=new Executable(*this, *j, objs);
400                                 add_target(exe);
401                                 if(i->second==default_pkg)
402                                         def_tgt->add_depend(exe);
403                                 else
404                                         world->add_depend(exe);
405
406                                 if(j->get_install())
407                                 {
408                                         string inst_dir;
409                                         if(j->get_type()==Component::PROGRAM)
410                                                 inst_dir="bin";
411                                         else if(j->get_type()==Component::LIBRARY)
412                                                 inst_dir="lib";
413                                         if(!inst_dir.empty())
414                                         {
415                                                 Install *inst=new Install(*this, *i->second, *exe, (inst_base/inst_dir/Path::basename(exe->get_name())).str());
416                                                 add_target(inst);
417                                                 install->add_depend(inst);
418                                         }
419                                 }
420                         }
421                 }
422         }
423
424         while(!new_tgts.empty())
425         {
426                 Target *tgt=new_tgts.front();
427                 new_tgts.erase(new_tgts.begin());
428                 tgt->find_depends();
429                 if(!tgt->get_depends_ready())
430                         new_tgts.push_back(tgt);
431         }
432
433         for(list<string>::iterator i=what_if.begin(); i!=what_if.end(); ++i)
434         {
435                 Target *tgt=get_target((cwd/ *i).str());
436                 if(!tgt)
437                 {
438                         cerr<<"Unknown what-if target "<<*i<<'\n';
439                         return -1;
440                 }
441                 tgt->touch();
442         }
443
444         Target *cmdline=new VirtualTarget(*this, "cmdline");
445         add_target(cmdline);
446         world->add_depend(cmdline);
447         for(list<string>::iterator i=cmdline_targets.begin(); i!=cmdline_targets.end(); ++i)
448         {
449                 Target *tgt=get_target(*i);
450                 if(!tgt)
451                         tgt=get_target((cwd/ *i).str());
452                 if(!tgt)
453                 {
454                         cerr<<"I don't know anything about "<<*i<<'\n';
455                         return -1;
456                 }
457                 cmdline->add_depend(tgt);
458         }
459
460         world->prepare();
461
462         return 0;
463 }
464
465 Target *Builder::check_header(const Msp::Path::Path &fn)
466 {
467         Target *tgt=get_target(fn.str());
468         if(tgt) return tgt;
469
470         if(Path::exists(fn))
471         {
472                 add_target(tgt=new SystemHeader(*this, fn.str()));
473                 return tgt;
474         }
475         return 0;
476 }
477
478 void Builder::add_target(Target *t)
479 {
480         targets.insert(TargetMap::value_type(t->get_name(), t));
481         new_tgts.push_back(t);
482 }
483
484 void Builder::update_hash(string &hash, const string &value)
485 {
486         for(unsigned i=0; i<value.size(); ++i)
487                 hash[i%hash.size()]^=value[i];
488 }
489
490 /**
491 This function supervises the build process, starting new actions when slots
492 become available.
493 */
494 int Builder::build()
495 {
496         Target *cmdline=get_target("cmdline");
497
498         unsigned total=cmdline->count_rebuild();
499         if(!total)
500         {
501                 cout<<"Already up to date\n";
502                 return 0;
503         }
504         cout<<"Will build "<<total<<" target(s)\n";
505
506         vector<Action *> actions;
507
508         if(chrome)
509                 cout<<"0 targets built\n";
510         unsigned count=0;
511
512         bool fail=false;
513         bool finish=false;
514
515         while(!finish)
516         {
517                 if(actions.size()<jobs && !fail)
518                 {
519                         Target *tgt=cmdline->get_buildable_target();
520                         if(tgt)
521                         {
522                                 Action *action=tgt->build();
523                                 if(action)
524                                         actions.push_back(action);
525                         }
526                         else if(actions.empty())
527                                 finish=true;
528                 }
529                 else
530                         Time::sleep(10*Time::msec);
531
532                 for(unsigned i=0; i<actions.size();)
533                 {
534                         int status=actions[i]->check();
535                         if(status>=0)
536                         {
537                                 ++count;
538                                 if(chrome)
539                                 {
540                                         cout<<"\e["<<actions.size()+1<<'A';
541                                         cout<<count<<" targets built\n";
542                                         if(i)
543                                                 cout<<"\e["<<i<<"B";
544                                         cout<<"\e[M";
545                                         if(i<actions.size()-1)
546                                                 cout<<"\e["<<actions.size()-i-1<<"B";
547                                         cout.flush();
548                                 }
549                                 delete actions[i];
550                                 actions.erase(actions.begin()+i);
551                                 if(status>0)
552                                         fail=true;
553                                 if(actions.empty() && fail)
554                                         finish=true;
555                         }
556                         else
557                                 ++i;
558                 }
559         }
560
561         return fail?-1:0;
562 }
563
564 Application::RegApp<Builder> Builder::reg;
565
566 Builder::Loader::Loader(Builder &b, const Path::Path &s):
567         bld(b),
568         src(s)
569 {
570         add("package", &Loader::package);
571 }
572
573 void Builder::Loader::package(const string &n)
574 {
575         Package *pkg=new Package(bld, n, src);
576         load_sub(*pkg);
577         bld.packages.insert(PackageMap::value_type(n, pkg));
578         bld.new_pkgs.push_back(pkg);
579 }
580