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