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