]> git.tdb.fi Git - builder.git/blob - source/builder.cpp
Revamp problem reporting system to be more useful
[builder.git] / source / builder.cpp
1 /* $Id$
2
3 This file is part of builder
4 Copyright © 2006-2007 Mikko Rasa, Mikkosoft Productions
5 Distributed under the LGPL
6 */
7
8 #include <fstream>
9 #include <iostream>
10 #include <set>
11 #include <msp/core/error.h>
12 #include <msp/core/getopt.h>
13 #include <msp/datafile/parser.h>
14 #include <msp/path/utils.h>
15 #include <msp/strings/formatter.h>
16 #include <msp/strings/utils.h>
17 #include <msp/time/units.h>
18 #include <msp/time/utils.h>
19 #include "action.h"
20 #include "analyzer.h"
21 #include "binarypackage.h"
22 #include "builder.h"
23 #include "header.h"
24 #include "install.h"
25 #include "misc.h"
26 #include "package.h"
27 #include "pkgconfig.h"
28 #include "sharedlibrary.h"
29 #include "sourcepackage.h"
30 #include "systemlibrary.h"
31 #include "tarball.h"
32 #include "unlink.h"
33 #include "virtualtarget.h"
34
35 using namespace std;
36 using namespace Msp;
37
38 Builder::Builder(int argc, char **argv):
39         main_pkg(0),
40         analyzer(0),
41         build(false),
42         clean(0),
43         dry_run(false),
44         help(false),
45         verbose(1),
46         chrome(false),
47         build_file("Build"),
48         jobs(1),
49         conf_all(false),
50         conf_only(false),
51         build_all(false),
52         create_makefile(false)
53 {
54         string   analyze_mode;
55         string   work_dir;
56         bool     full_paths=false;
57         unsigned max_depth=5;
58
59         GetOpt getopt;
60         getopt.add_option('a', "analyze",    analyze_mode, GetOpt::REQUIRED_ARG);
61         getopt.add_option('b', "build",      build,        GetOpt::NO_ARG);
62         getopt.add_option('c', "clean",      clean,        GetOpt::NO_ARG);
63         getopt.add_option('f', "file",       build_file,   GetOpt::REQUIRED_ARG);
64         getopt.add_option('h', "help",       help,         GetOpt::NO_ARG);
65         getopt.add_option('j', "jobs",       jobs,         GetOpt::REQUIRED_ARG);
66         getopt.add_option('n', "dry-run",    dry_run,      GetOpt::NO_ARG);
67         getopt.add_option('v', "verbose",    verbose,      GetOpt::NO_ARG);
68         getopt.add_option('A', "conf-all",   conf_all,     GetOpt::NO_ARG);
69         getopt.add_option('B', "build-all",  build_all,    GetOpt::NO_ARG);
70         getopt.add_option('C', "chdir",      work_dir,     GetOpt::REQUIRED_ARG);
71         getopt.add_option('W', "what-if",    what_if,      GetOpt::REQUIRED_ARG);
72         getopt.add_option(     "chrome",     chrome,       GetOpt::NO_ARG);
73         getopt.add_option(     "conf-only",  conf_only,    GetOpt::NO_ARG);
74         getopt.add_option(     "full-paths", full_paths,   GetOpt::NO_ARG);
75         //getopt.add_option(     "makefile",   create_makefile, GetOpt::NO_ARG);
76         getopt.add_option(     "max-depth",  max_depth,    GetOpt::REQUIRED_ARG);
77         getopt(argc, argv);
78
79         if(!analyze_mode.empty())
80         {
81                 analyzer=new Analyzer(*this);
82
83                 if(analyze_mode=="deps")
84                         analyzer->set_mode(Analyzer::DEPS);
85                 else if(analyze_mode=="alldeps")
86                         analyzer->set_mode(Analyzer::ALLDEPS);
87                 else if(analyze_mode=="rebuild")
88                         analyzer->set_mode(Analyzer::REBUILD);
89                 else if(analyze_mode=="rdeps")
90                         analyzer->set_mode(Analyzer::RDEPS);
91                 else
92                         throw UsageError("Invalid analyze mode");
93
94                 analyzer->set_max_depth(max_depth);
95                 analyzer->set_full_paths(full_paths);
96         }
97         else if(!clean && !create_makefile)
98                 build=true;
99
100         const list<string> &args=getopt.get_args();
101         for(list<string>::const_iterator i=args.begin(); i!=args.end(); ++i)
102         {
103                 unsigned equal=i->find('=');
104                 if(equal!=string::npos)
105                         cmdline_options.insert(StringMap::value_type(i->substr(0, equal), i->substr(equal+1)));
106                 else
107                         cmdline_targets.push_back(*i);
108         }
109
110         if(cmdline_targets.empty())
111                 cmdline_targets.push_back("default");
112
113         if(!work_dir.empty())
114                 chdir(work_dir.c_str());
115
116         cwd=Path::getcwd();
117
118         archs.insert(StringMap::value_type("native", ""));
119
120         StringMap &native_tools=tools.insert(ToolMap::value_type("native", StringMap())).first->second;
121         native_tools.insert(StringMap::value_type("CC",   "gcc"));
122         native_tools.insert(StringMap::value_type("CXX",  "g++"));
123         native_tools.insert(StringMap::value_type("LD",   "gcc"));
124         native_tools.insert(StringMap::value_type("LDXX", "g++"));
125         native_tools.insert(StringMap::value_type("AR",   "ar"));
126
127         const char *home=getenv("HOME");
128         if(home)
129                 load_build_file((Path::Path(home)/".builderrc").str());
130 }
131
132 /**
133 Gets a package with the specified name, possibly creating it.
134
135 @param   n  Package name
136
137 @return  Pointer to the package, or 0 if the package could not be located
138 */
139 Package *Builder::get_package(const string &n)
140 {
141         PackageMap::iterator i=packages.find(n);
142         if(i!=packages.end())
143                 return i->second;
144
145         // Try to get source directory with pkgconfig
146         list<string> argv;
147         argv.push_back("pkg-config");
148         argv.push_back("--variable=source");
149         argv.push_back(n);
150         string srcdir=strip(run_command(argv));
151
152         PathList dirs;
153         if(!srcdir.empty())
154                 dirs.push_back(srcdir);
155
156         // Make some other guesses about the source directory
157         string dirname=n;
158         if(!dirname.compare(0, 3, "msp"))
159                 dirname.erase(0, 3);
160         dirs.push_back(cwd/dirname);
161         dirs.push_back(cwd/".."/dirname);
162
163         // Go through the candidate directories and look for a Build file
164         for(PathList::iterator j=dirs.begin(); j!=dirs.end(); ++j)
165                 if(!load_build_file(*j/"Build"))
166                 {
167                         i=packages.find(n);
168                         if(i!=packages.end())
169                                 return i->second;
170                         break;
171                 }
172
173         // Package source not found - create a binary package
174         Package *pkg=BinaryPackage::from_pkgconfig(*this, n);
175
176         packages.insert(PackageMap::value_type(n, pkg));
177
178         if(!pkg)
179                 problem(n, "not found");
180
181         return pkg;
182 }
183
184 /**
185 Returns the target with the given name, or 0 if no such target exists.
186 */
187 Target *Builder::get_target(const string &n) const
188 {
189         TargetMap::const_iterator i=targets.find(n);
190         if(i!=targets.end())
191                 return i->second;
192         return 0;
193 }
194
195 /**
196 Tries to locate a header included from a given location and with a given include
197 path.  Considers known targets as well as existing files.  If a matching target
198 is not found but a file exists, a new SystemHeader target will be created and
199 returned.
200 */
201 Target *Builder::get_header(const string &include, const string &, const string &from, const list<string> &path)
202 {
203         string hash(8, 0);
204         update_hash(hash, from);
205         for(list<string>::const_iterator i=path.begin(); i!=path.end(); ++i)
206                 update_hash(hash, *i);
207
208         string id=hash+include;
209         TargetMap::iterator i=includes.find(id);
210         if(i!=includes.end())
211                 return i->second;
212
213         string fn=include.substr(1);
214         Target *tgt=0;
215         if(include[0]=='"' && (tgt=get_header(Path::Path(from)/fn)))
216                 ;
217         else if((tgt=get_header(Path::Path("/usr/include")/fn)))
218                 ;
219         //XXX Determine the C++ header location dynamically
220         else if((tgt=get_header(Path::Path("/usr/include/c++/4.1.2")/fn)))
221                 ;
222         else
223         {
224                 for(list<string>::const_iterator j=path.begin(); (j!=path.end() && !tgt); ++j)
225                         tgt=get_header(cwd/ *j/fn);
226         }
227
228         includes.insert(TargetMap::value_type(id, tgt));
229
230         return tgt;
231 }
232
233 /**
234 Tries to locate a library with the given library path.  Considers known targets
235 as well as existing files.  If a matching target is not found but a file exists,
236 a new SystemLibrary target will be created and returned.
237
238 @param   lib   Name of the library to get (without "lib" prefix or extension)
239 @param   path  List of paths to search for the library
240 @param   mode  Shared / static mode
241
242 @return  Some kind of library target, if a match was found
243 */
244 Target *Builder::get_library(const string &lib, const string &arch, const list<string> &path, LibMode mode)
245 {
246         string hash(8, 0);
247         for(list<string>::const_iterator i=path.begin(); i!=path.end(); ++i)
248                 update_hash(hash, *i);
249
250         //XXX Incorporate mode into id
251         string id=hash+lib;
252         TargetMap::iterator i=libraries.find(id);
253         if(i!=libraries.end())
254                 return i->second;
255
256         StringList syspath;
257         if(arch=="native")
258         {
259                 syspath.push_back("/lib");
260                 syspath.push_back("/usr/lib");
261         }
262         else
263                 syspath.push_back("/usr/"+get_arch_prefix(arch)+"/lib");
264
265         Target *tgt=0;
266         for(StringList::iterator j=syspath.begin(); (!tgt && j!=syspath.end()); ++j)
267                 tgt=get_library(lib, arch, *j, mode);
268         for(StringList::const_iterator j=path.begin(); (!tgt && j!=path.end()); ++j)
269                 tgt=get_library(lib, arch, cwd/ *j, mode);
270
271         libraries.insert(TargetMap::value_type(id, tgt));
272
273         return tgt;
274 }
275
276 const string &Builder::get_arch_prefix(const string &arch) const
277 {
278         StringMap::const_iterator i=archs.find(arch);
279         if(i==archs.end())
280                 throw InvalidParameterValue("Unknown architecture");
281
282         return i->second;
283 }
284
285 string Builder::get_tool(const std::string &tool, const std::string &arch)
286 {
287         ToolMap::iterator i=tools.find(arch);
288         if(i!=tools.end())
289         {
290                 StringMap::iterator j=i->second.find(tool);
291                 if(j!=i->second.end())
292                         return j->second;
293         }
294
295         // Either the arch, or the tool within the arch was not found
296         i=tools.find("native");
297         StringMap::iterator j=i->second.find(tool);
298         if(j==i->second.end())
299                 throw InvalidParameterValue("Unknown tool");
300
301         return get_arch_prefix(arch)+"-"+j->second;
302 }
303
304 void Builder::apply_profile_template(Config &config, const string &pt) const
305 {
306         vector<string> parts=split(pt, '-');
307
308         for(vector<string>::iterator i=parts.begin(); i!=parts.end(); ++i)
309         {
310                 ProfileTemplateMap::const_iterator j=profile_tmpl.find(*i);
311                 if(j==profile_tmpl.end())
312                         continue;
313
314                 config.update(j->second);
315         }
316 }
317
318 void Builder::problem(const string &p, const string &d)
319 {
320         problems.push_back(Problem(p, d));
321 }
322
323 /**
324 Adds a target to both the target map and the new target queue.  Called from
325 Target constructor.
326 */
327 void Builder::add_target(Target *t)
328 {
329         targets.insert(TargetMap::value_type(t->get_name(), t));
330         new_tgts.push_back(t);
331 }
332
333 int Builder::main()
334 {
335         if(load_build_file(cwd/build_file))
336         {
337                 cerr<<"No build info here.\n";
338                 return 1;
339         }
340
341         main_pkg->configure(cmdline_options, conf_all?2:1);
342
343         if(help)
344         {
345                 usage(0, "builder", false);
346                 cout<<'\n';
347                 package_help();
348                 return 0;
349         }
350
351         if(!conf_only && create_targets())
352                 return 1;
353
354         PackageList all_reqs=main_pkg->collect_requires();
355
356         for(PackageList::const_iterator i=all_reqs.begin(); i!=all_reqs.end(); ++i)
357         {
358                 SourcePackage *spkg=dynamic_cast<SourcePackage *>(*i);
359                 string prob;
360                 if(spkg && spkg->get_arch()!=main_pkg->get_arch())
361                         problem(spkg->get_name(), format("wrong architecture (%s)", spkg->get_arch()));
362         }
363
364         if(!problems.empty())
365         {
366                 cerr<<"The following problems were detected:\n";
367                 for(ProblemList::iterator i=problems.begin(); i!=problems.end(); ++i)
368                         cerr<<"  "<<i->package<<": "<<i->descr<<'\n';
369                 cerr<<"Please fix them and try again.\n";
370                 return 1;
371         }
372
373         if(conf_only)
374                 return 0;
375
376         cout<<all_reqs.size()<<" active packages, "<<targets.size()<<" targets\n";
377         if(verbose>=2)
378         {
379                 for(PackageList::const_iterator i=all_reqs.begin(); i!=all_reqs.end(); ++i)
380                 {
381                         cout<<' '<<(*i)->get_name();
382                         if(dynamic_cast<SourcePackage *>(*i))
383                                 cout<<'*';
384                         unsigned count=0;
385                         unsigned ood_count=0;
386                         for(TargetMap::iterator j=targets.begin(); j!=targets.end(); ++j)
387                                 if(j->second->get_package()==*i)
388                                 {
389                                         ++count;
390                                         if(j->second->get_rebuild())
391                                                 ++ood_count;
392                                 }
393                         if(count)
394                         {
395                                 cout<<" ("<<count<<" targets";
396                                 if(ood_count)
397                                         cout<<", "<<ood_count<<" out-of-date";
398                                 cout<<')';
399                         }
400                         cout<<'\n';
401                 }
402         }
403
404         if(analyzer)
405                 analyzer->analyze();
406
407         //if(create_makefile
408
409         if(clean)
410                 exit_code=do_clean();
411         else if(build)
412                 exit_code=do_build();
413
414         return exit_code;
415 }
416
417 Builder::~Builder()
418 {
419         for(PackageMap::iterator i=packages.begin(); i!=packages.end(); ++i)
420                 delete i->second;
421         for(TargetMap::iterator i=targets.begin(); i!=targets.end(); ++i)
422                 delete i->second;
423         delete analyzer;
424 }
425
426 void Builder::usage(const char *reason, const char *argv0, bool brief)
427 {
428         if(reason)
429                 cerr<<reason<<'\n';
430
431         if(brief)
432                 cerr<<"Usage: "<<argv0<<" [-a|--analyze MODE] [-b|--build] [-c|--clean] [-f|--file FILE] [-h|--help] [-j|--jobs NUM] [-n||--dry-run] [-v|--verbose] [-A|--conf-all] [-B|--build-all] [-C|--chdir DIRECTORY] [-W|--what-if FILE] [--chrome] [--conf-only] [--full-paths] [--max-depth NUM] [<target> ...]";
433         else
434         {
435                 cerr<<
436                         "Usage: "<<argv0<<" [options] [<target> ...]\n"
437                         "\n"
438                         "Options:\n"
439                         "  -a, --analyze MODE  Perform analysis.  MODE can be deps, alldeps or rebuild.\n"
440                         "  -b, --build         Perform build even if doing analysis.\n"
441                         "  -c, --clean         Clean buildable targets.\n"
442                         "  -f, --file FILE     Read info from FILE instead of Build.\n"
443                         "  -h, --help          Print this message.\n"
444                         "  -j, --jobs NUM      Run NUM commands at once, whenever possible.\n"
445                         "  -n, --dry-run       Don't actually do anything, only show what would be done.\n"
446                         "  -v, --verbose       Print more information about what's going on.\n"
447                         "  -A, --conf-all      Apply configuration to all packages.\n"
448                         "  -B, --build-all     Build all targets unconditionally.\n"
449                         "  -C, --chdir DIR     Change to DIR before doing anything else.\n"
450                         "  -W, --what-if FILE  Pretend that FILE has changed.\n"
451                         "  --chrome            Use extra chrome to print status.\n"
452                         "  --conf-only         Stop after configuring packages.\n"
453                         "  --full-paths        Output full paths in analysis.\n"
454                         //"  --makefile          Create a makefile for this package.\n"
455                         "  --max-depth NUM     Maximum depth to show in analysis.\n";
456         }
457 }
458
459 /**
460 Loads the given build file.
461
462 @param   fn  Path to the file
463
464 @return  0 on success, -1 if the file could not be opened
465 */
466 int Builder::load_build_file(const Path::Path &fn)
467 {
468         ifstream in(fn.str().c_str());
469         if(!in)
470                 return -1;
471
472         if(verbose>=3)
473                 cout<<"Reading "<<fn<<'\n';
474
475         DataFile::Parser parser(in, fn.str());
476         Loader loader(*this, fn.subpath(0, fn.size()-1));
477         loader.load(parser);
478
479         return 0;
480 }
481
482 /**
483 Creates targets for all packages and prepares them for building.
484
485 @return  0 if everything went ok, -1 if something bad happened and a build
486          shouldn't be attempted
487 */
488 int Builder::create_targets()
489 {
490         Target *world=new VirtualTarget(*this, "world");
491
492         Target *def_tgt=new VirtualTarget(*this, "default");
493         world->add_depend(def_tgt);
494
495         Target *install=new VirtualTarget(*this, "install");
496         world->add_depend(install);
497
498         Target *tarballs=new VirtualTarget(*this, "tarballs");
499         world->add_depend(tarballs);
500
501         PackageList all_reqs=main_pkg->collect_requires();
502         for(PackageList::iterator i=all_reqs.begin(); i!=all_reqs.end(); ++i)
503         {
504                 SourcePackage *spkg=dynamic_cast<SourcePackage *>(*i);
505                 if(!spkg)
506                         continue;
507
508                 const ComponentList &components=spkg->get_components();
509                 for(ComponentList::const_iterator j=components.begin(); j!=components.end(); ++j)
510                         j->create_targets();
511
512                 if(spkg->get_install_flags()&(SourcePackage::LIB|SourcePackage::INCLUDE))
513                 {
514                         PkgConfig *pc=new PkgConfig(*this, *spkg);
515                         install->add_depend(new Install(*this, *spkg, *pc));
516                 }
517
518                 tarballs->add_depend(new TarBall(*this, *spkg));
519         }
520
521         // Find dependencies until no new targets are created
522         while(!new_tgts.empty())
523         {
524                 Target *tgt=new_tgts.front();
525                 new_tgts.erase(new_tgts.begin());
526                 tgt->find_depends();
527                 if(!tgt->get_depends_ready())
528                         new_tgts.push_back(tgt);
529         }
530
531         // Apply what-ifs
532         for(StringList::iterator i=what_if.begin(); i!=what_if.end(); ++i)
533         {
534                 Target *tgt=get_target((cwd/ *i).str());
535                 if(!tgt)
536                 {
537                         cerr<<"Unknown what-if target "<<*i<<'\n';
538                         return -1;
539                 }
540                 tgt->touch();
541         }
542
543         // Make the cmdline target depend on all targets mentioned on the command line
544         Target *cmdline=new VirtualTarget(*this, "cmdline");
545         bool build_world=false;
546         for(list<string>::iterator i=cmdline_targets.begin(); i!=cmdline_targets.end(); ++i)
547         {
548                 Target *tgt=get_target(*i);
549                 if(!tgt)
550                         tgt=get_target((cwd/ *i).str());
551                 if(!tgt)
552                 {
553                         cerr<<"I don't know anything about "<<*i<<'\n';
554                         return -1;
555                 }
556                 if(tgt==world)
557                         build_world=true;
558                 cmdline->add_depend(tgt);
559         }
560
561         /* If world is to be built, prepare cmdline.  If not, add cmdline to world
562            and prepare world.  I don't really like this, but it keeps the graph
563            acyclic. */
564         if(build_world)
565                 cmdline->prepare();
566         else
567         {
568                 world->add_depend(cmdline);
569                 world->prepare();
570         }
571
572         for(PackageMap::iterator i=packages.begin(); i!=packages.end(); ++i)
573                 if(SourcePackage *spkg=dynamic_cast<SourcePackage *>(i->second))
574                         spkg->get_deps_cache().save();
575
576         return 0;
577 }
578
579 /**
580 Check if a header exists, either as a target or a file.  Either an existing
581 target or a new SystemHeader target will be returned.
582 */
583 Target *Builder::get_header(const Msp::Path::Path &fn)
584 {
585         Target *tgt=get_target(fn.str());
586         if(tgt) return tgt;
587
588         if(Path::exists(fn))
589         {
590                 tgt=new SystemHeader(*this, fn.str());
591                 return tgt;
592         }
593         return 0;
594 }
595
596 Target *Builder::get_library(const string &lib, const string &arch, const Path::Path &path, LibMode mode)
597 {
598         // Populate a list of candidate filenames
599         StringList candidates;
600
601         if(mode!=ALL_STATIC)
602         {
603                 if(arch=="win32")
604                         candidates.push_back("lib"+lib+".dll");
605                 else
606                         candidates.push_back("lib"+lib+".so");
607         }
608
609         /* Static libraries are always considered, since sometimes shared versions
610         may not be available */
611         candidates.push_back("lib"+lib+".a");
612         if(arch=="win32")
613                 candidates.push_back("lib"+lib+".dll.a");
614
615         for(StringList::iterator i=candidates.begin(); i!=candidates.end(); ++i)
616         {
617                 string full=(path/ *i).str();
618                 Target *tgt=get_target(full);
619
620                 if(tgt)
621                 {
622                         Target *real_tgt=tgt;
623                         if(dynamic_cast<Install *>(tgt))
624                                 real_tgt=real_tgt->get_depends().front();
625
626                         /* Ignore dynamic libraries from local packages unless library mode is
627                         DYNAMIC */
628                         if(dynamic_cast<SharedLibrary *>(real_tgt) && mode!=DYNAMIC)
629                                 continue;
630                         else if(tgt)
631                                 return tgt;
632                 }
633                 else if(Path::exists(full))
634                 {
635                         tgt=new SystemLibrary(*this, full);
636                         return tgt;
637                 }
638         }
639
640         return 0;
641 }
642
643 /**
644 Updates a hash with a string.  This is used from get_header and get_library.
645 */
646 void Builder::update_hash(string &hash, const string &value)
647 {
648         for(unsigned i=0; i<value.size(); ++i)
649                 hash[i%hash.size()]^=value[i];
650 }
651
652 /**
653 This function supervises the build process, starting new actions when slots
654 become available.
655 */
656 int Builder::do_build()
657 {
658         Target *cmdline=get_target("cmdline");
659
660         unsigned total=cmdline->count_rebuild();
661         if(!total)
662         {
663                 cout<<"Already up to date\n";
664                 return 0;
665         }
666         cout<<"Will build "<<total<<" target(s)\n";
667
668         vector<Action *> actions;
669
670         if(chrome)
671                 cout<<"0 targets built\n";
672         unsigned count=0;
673
674         bool fail=false;
675         bool finish=false;
676
677         while(!finish)
678         {
679                 if(actions.size()<jobs && !fail)
680                 {
681                         Target *tgt=cmdline->get_buildable_target();
682                         if(tgt)
683                         {
684                                 Action *action=tgt->build();
685                                 if(action)
686                                         actions.push_back(action);
687                         }
688                         else if(actions.empty())
689                                 finish=true;
690                 }
691                 else
692                         Time::sleep(10*Time::msec);
693
694                 for(unsigned i=0; i<actions.size();)
695                 {
696                         int status=actions[i]->check();
697                         if(status>=0)
698                         {
699                                 ++count;
700                                 if(chrome)
701                                 {
702                                         cout<<"\e["<<actions.size()+1<<'A';
703                                         cout<<count<<" targets built\n";
704                                         if(i)
705                                                 cout<<"\e["<<i<<"B";
706                                         cout<<"\e[M";
707                                         if(i<actions.size()-1)
708                                                 cout<<"\e["<<actions.size()-i-1<<"B";
709                                         cout.flush();
710                                 }
711                                 delete actions[i];
712                                 actions.erase(actions.begin()+i);
713                                 if(status>0)
714                                         fail=true;
715                                 if(actions.empty() && fail)
716                                         finish=true;
717                         }
718                         else
719                                 ++i;
720                 }
721         }
722
723         if(fail)
724                 cout<<"Build failed\n";
725
726         return fail?1:0;
727 }
728
729 /**
730 Cleans buildable targets.  If clean is 1, cleans only this package.  If
731 clean is 2 or greater, cleans all buildable packages.
732 */
733 int Builder::do_clean()
734 {
735         // Cleaning doesn't care about ordering, so a simpler method can be used
736
737         set<Target *> clean_tgts;
738         TargetList queue;
739         queue.push_back(get_target("cmdline"));
740
741         while(!queue.empty())
742         {
743                 Target *tgt=queue.front();
744                 queue.erase(queue.begin());
745
746                 if(tgt->get_buildable() && (tgt->get_package()==main_pkg || clean>=2))
747                         clean_tgts.insert(tgt);
748
749                 const TargetList &deps=tgt->get_depends();
750                 for(TargetList::const_iterator i=deps.begin(); i!=deps.end(); ++i)
751                         if(!clean_tgts.count(*i))
752                                 queue.push_back(*i);
753         }
754
755         for(set<Target *>::iterator i=clean_tgts.begin(); i!=clean_tgts.end(); ++i)
756         {
757                 Action *action=new Unlink(*this, **i);
758                 while(action->check()<0);
759                 delete action;
760         }
761
762         return 0;
763 }
764
765 /**
766 Prints out information about the default package.
767 */
768 void Builder::package_help()
769 {
770         const Config &config=main_pkg->get_config();
771         const Config::OptionMap &options=config.get_options();
772
773         cout<<"Required packages:\n  ";
774         const PackageList &requires=main_pkg->get_requires();
775         for(PackageList::const_iterator i=requires.begin(); i!=requires.end(); ++i)
776         {
777                 if(i!=requires.begin())
778                         cout<<", ";
779                 cout<<(*i)->get_name();
780         }
781         cout<<"\n\n";
782         cout<<"Package configuration:\n";
783         for(Config::OptionMap::const_iterator i=options.begin(); i!=options.end(); ++i)
784         {
785                 const Config::Option &opt=i->second;
786                 cout<<"  "<<opt.name<<": "<<opt.descr<<" ("<<opt.value<<") ["<<opt.defv<<"]\n";
787         }
788 }
789
790 Application::RegApp<Builder> Builder::reg;
791
792
793 Builder::Loader::Loader(Builder &b, const Path::Path &s):
794         bld(b),
795         src(s)
796 {
797         add("architecture", &Loader::architecture);
798         add("binary_package", &Loader::binpkg);
799         add("profile", &Loader::profile);
800         add("package", &Loader::package);
801 }
802
803 void Builder::Loader::architecture(const string &a, const string &p)
804 {
805         bld.archs.insert(StringMap::value_type(a, p));
806 }
807
808 void Builder::Loader::binpkg(const string &n)
809 {
810         BinaryPackage *pkg=new BinaryPackage(bld, n);
811         load_sub(*pkg);
812         bld.packages.insert(PackageMap::value_type(n, pkg));
813 }
814
815 void Builder::Loader::profile(const string &n)
816 {
817         StringMap prf;
818         load_sub<ProfileLoader>(prf);
819         bld.profile_tmpl.insert(ProfileTemplateMap::value_type(n, prf));
820 }
821
822 void Builder::Loader::package(const string &n)
823 {
824         SourcePackage *pkg=new SourcePackage(bld, n, src);
825         if(!bld.main_pkg)
826                 bld.main_pkg=pkg;
827
828         load_sub(*pkg);
829         bld.packages.insert(PackageMap::value_type(n, pkg));
830 }
831
832
833 Builder::ProfileLoader::ProfileLoader(StringMap &p):
834         profile(p)
835 {
836         add("option", &ProfileLoader::option);
837 }
838
839 void Builder::ProfileLoader::option(const string &o, const string &v)
840 {
841         profile.insert(StringMap::value_type(o, v));
842 }