]> git.tdb.fi Git - builder.git/blob - source/builder.cpp
1de726bcb2b3393f267e363741525da20d6c72e6
[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         if(create_targets())
237                 return 1;
238
239         cout<<packages.size()<<" packages, "<<targets.size()<<" targets\n";
240         if(verbose>=2)
241         {
242                 for(PackageMap::iterator i=packages.begin(); i!=packages.end(); ++i)
243                 {
244                         cout<<' '<<i->second->get_name();
245                         if(i->second->get_buildable())
246                                 cout<<'*';
247                         unsigned count=0;
248                         for(TargetMap::iterator j=targets.begin(); j!=targets.end(); ++j)
249                                 if(j->second->get_package()==i->second)
250                                         ++count;
251                         cout<<" ("<<count<<" targets)\n";
252                 }
253         }
254
255         if(analyzer)
256                 analyzer->analyze();
257
258         build();
259
260         return exit_code;
261 }
262
263 Builder::~Builder()
264 {
265         for(PackageMap::iterator i=packages.begin(); i!=packages.end(); ++i)
266                 delete i->second;
267         for(TargetMap::iterator i=targets.begin(); i!=targets.end(); ++i)
268                 delete i->second;
269         delete analyzer;
270 }
271
272 int Builder::load_build_file(const Path::Path &fn)
273 {
274         ifstream in(fn.str().c_str());
275         if(!in)
276                 return -1;
277
278         Parser::Parser parser(in, fn.str());
279         Loader loader(*this, fn.subpath(0, fn.size()-1));
280         loader.load(parser);
281
282         return 0;
283 }
284
285 int Builder::create_targets()
286 {
287         Target *world=new VirtualTarget(*this, "world");
288         add_target(world);
289
290         Target *def_tgt=new VirtualTarget(*this, "default");
291         add_target(def_tgt);
292         world->add_depend(def_tgt);
293
294         Target *install=new VirtualTarget(*this, "install");
295         add_target(install);
296         world->add_depend(install);
297
298         for(PackageMap::iterator i=packages.begin(); i!=packages.end(); ++i)
299         {
300                 if(!i->second)
301                         continue;
302                 if(!i->second->get_buildable())
303                         continue;
304
305                 const ComponentList &components=i->second->get_components();
306                 for(ComponentList::const_iterator j=components.begin(); j!=components.end(); ++j)
307                 {
308                         PathList files;
309                         const PathList &sources=j->get_sources();
310                         for(PathList::const_iterator k=sources.begin(); k!=sources.end(); ++k)
311                         {
312                                 list<string> sfiles=list_files(*k);
313                                 for(list<string>::iterator l=sfiles.begin(); l!=sfiles.end(); ++l)
314                                         files.push_back(*k / *l);
315                         }
316
317                         Path::Path inst_base=i->second->get_config().get_option("prefix").value;
318
319                         bool build_exe=j->get_type()!=Component::HEADERS;
320                         
321                         list<ObjectFile *> objs;
322                         for(PathList::iterator k=files.begin(); k!=files.end(); ++k)
323                         {
324                                 string basename=(*k)[-1];
325                                 string ext=Path::splitext(basename).ext;
326                                 if(ext==".cpp" || ext==".c")
327                                 {
328                                         if(build_exe)
329                                         {
330                                                 SourceFile *src=new SourceFile(*this, &*j, k->str());
331                                                 add_target(src);
332                                                 
333                                                 ObjectFile *obj=new ObjectFile(*this, *j, *src);
334                                                 add_target(obj);
335                                                 objs.push_back(obj);
336                                         }
337                                 }
338                                 else if(ext==".h")
339                                 {
340                                         Target *hdr=get_target(k->str());
341                                         if(!hdr)
342                                         {
343                                                 hdr=new Header(*this, &*j, k->str());
344                                                 add_target(hdr);
345                                         }
346                                         if(!j->get_install_headers().empty())
347                                         {
348                                                 Path::Path inst_path=inst_base/"include"/j->get_install_headers()/basename;
349                                                 Install *inst=new Install(*this, *i->second, *hdr, inst_path.str());
350                                                 add_target(inst);
351                                                 install->add_depend(inst);
352                                         }
353                                 }
354                         }
355
356                         if(build_exe)
357                         {
358                                 Executable *exe=new Executable(*this, *j, objs);
359                                 add_target(exe);
360                                 if(i->second==default_pkg)
361                                         def_tgt->add_depend(exe);
362                                 else
363                                         world->add_depend(exe);
364
365                                 if(j->get_install())
366                                 {
367                                         string inst_dir;
368                                         if(j->get_type()==Component::PROGRAM)
369                                                 inst_dir="bin";
370                                         else if(j->get_type()==Component::LIBRARY)
371                                                 inst_dir="lib";
372                                         if(!inst_dir.empty())
373                                         {
374                                                 Install *inst=new Install(*this, *i->second, *exe, (inst_base/inst_dir/Path::basename(exe->get_name())).str());
375                                                 add_target(inst);
376                                                 install->add_depend(inst);
377                                         }
378                                 }
379                         }
380                 }
381         }
382
383         while(!new_tgts.empty())
384         {
385                 Target *tgt=new_tgts.front();
386                 new_tgts.erase(new_tgts.begin());
387                 tgt->find_depends();
388         }
389
390         Target *cmdline=new VirtualTarget(*this, "cmdline");
391         add_target(cmdline);
392         world->add_depend(cmdline);
393         for(list<string>::iterator i=cmdline_targets.begin(); i!=cmdline_targets.end(); ++i)
394         {
395                 Target *tgt=get_target(*i);
396                 if(!tgt)
397                         tgt=get_target((cwd/ *i).str());
398                 if(!tgt)
399                 {
400                         cerr<<"I don't know anything about "<<*i<<'\n';
401                         return -1;
402                 }
403                 cmdline->add_depend(tgt);
404         }
405
406         world->prepare();
407
408         return 0;
409 }
410
411 Target *Builder::check_header(const Msp::Path::Path &fn)
412 {
413         Target *tgt=get_target(fn.str());
414         if(tgt) return tgt;
415
416         if(Path::exists(fn))
417         {
418                 add_target(tgt=new SystemHeader(*this, fn.str()));
419                 return tgt;
420         }
421         return 0;
422 }
423
424 void Builder::add_target(Target *t)
425 {
426         targets.insert(TargetMap::value_type(t->get_name(), t));
427         new_tgts.push_back(t);
428 }
429
430 int Builder::build()
431 {
432         Target *cmdline=get_target("cmdline");
433
434         unsigned total=cmdline->count_rebuild();
435         if(!total)
436         {
437                 cout<<"Already up to date\n";
438                 return 0;
439         }
440         cout<<"Will build "<<total<<" target(s)\n";
441
442         vector<Action *> actions;
443
444         //ProgressBar  *progress=0;
445         if(chrome)
446         {
447                 //progress=new ProgressBar(cout, total);
448                 cout<<"0 targets built\n";
449         }
450         unsigned count=0;
451
452         bool fail=false;
453         bool finish=false;
454
455         while(!finish)
456         {
457                 if(actions.size()<jobs && !fail)
458                 {
459                         Target *tgt=cmdline->get_buildable_target();
460                         if(tgt)
461                         {
462                                 /*if(chrome)
463                                 {
464                                         cout<<"\e["<<actions.size()+1<<'A';
465                                         //progress->set(count);
466                                         cout<<"\e["<<actions.size()+1<<'B';
467                                 }*/
468                                 Action *action=tgt->build();
469                                 if(action)
470                                         actions.push_back(action);
471                         }
472                         else if(actions.empty())
473                                 finish=true;
474                 }
475                 else
476                         Time::sleep(10*Time::msec);
477
478                 for(unsigned i=0; i<actions.size();)
479                 {
480                         int status=actions[i]->check();
481                         if(status>=0)
482                         {
483                                 ++count;
484                                 if(chrome)
485                                 {
486                                         cout<<"\e["<<actions.size()+1<<'A';
487                                         cout<<count<<" targets built\n";
488                                         if(i)
489                                                 cout<<"\e["<<i<<"B";
490                                         cout<<"\e[M";
491                                         if(i<actions.size()-1)
492                                                 cout<<"\e["<<actions.size()-i-1<<"B";
493                                         cout.flush();
494                                 }
495                                 delete actions[i];
496                                 actions.erase(actions.begin()+i);
497                                 if(status>0)
498                                         fail=true;
499                                 if(actions.empty() && fail)
500                                         finish=true;
501                         }
502                         else
503                                 ++i;
504                 }
505         }
506
507         //delete progress;
508
509         return fail?-1:0;
510 }
511
512 Application::RegApp<Builder> Builder::reg;
513
514 Builder::Loader::Loader(Builder &b, const Path::Path &s):
515         bld(b),
516         src(s)
517 {
518         add("package", &Loader::package);
519 }
520
521 void Builder::Loader::package(const string &n)
522 {
523         Package *pkg=new Package(bld, n, src);
524         load_sub(*pkg);
525         bld.packages.insert(PackageMap::value_type(n, pkg));
526         bld.new_pkgs.push_back(pkg);
527 }
528