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