ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/cvsroot/COMP/SCRAM/src/BuildSystem/BuildDataStorage.pm
Revision: 1.8
Committed: Tue May 24 09:09:42 2005 UTC (19 years, 11 months ago) by sashby
Content type: text/plain
Branch: MAIN
Changes since 1.7: +3 -3 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 sashby 1.2 #____________________________________________________________________
2     # File: BuildDataStorage.pm
3     #____________________________________________________________________
4     #
5     # Author: Shaun Ashby <Shaun.Ashby@cern.ch>
6     # Update: 2004-06-22 15:16:01+0200
7 sashby 1.8 # Revision: $Id: BuildDataStorage.pm,v 1.7 2005/04/29 16:18:56 sashby Exp $
8 sashby 1.2 #
9     # Copyright: 2004 (C) Shaun Ashby
10     #
11     #--------------------------------------------------------------------
12     package BuildSystem::BuildDataStorage;
13     require 5.004;
14     use Exporter;
15     @ISA=qw(Exporter);
16     @EXPORT_OK=qw( );
17    
18     sub new()
19     ###############################################################
20     # new #
21     ###############################################################
22     # modified : Tue Jun 22 15:16:08 2004 / SFA #
23     # params : #
24     # : #
25     # function : #
26     # : #
27     ###############################################################
28     {
29     my $proto=shift;
30     my $class=ref($proto) || $proto;
31     my ($configdir) = @_;
32     my $self=
33     {
34     BUILDTREE => {}, # Path/data pairs;
35     STATUS => 0, # Status of cache
36 sashby 1.6 UNRESOLVED_DEPS => {}, # Hash containing packages and deps they're missing
37 sashby 1.2 VERBOSE => 0 # Verbose mode (0/1);
38     };
39    
40     bless $self,$class;
41    
42     # The location of the top-level BuildFile:
43     $self->{CONFIGDIR} = $configdir;
44    
45     # Somewhere to store the dependencies:
46     $self->{DEPENDENCIES} = {}; # GLOBAL dependencies
47 sashby 1.3 $self->{SKIPPEDDIRS} = {}; # Global skipped dirs
48 sashby 1.2
49     # Initialize the Template Engine:
50     $self->init_engine();
51    
52     return $self;
53     }
54    
55     sub grapher()
56     {
57     my $self=shift;
58     my ($mode,$writeopt)=@_;
59    
60     if ($mode)
61     {
62     $mode =~ tr[A-Z][a-z];
63     # Check to see what the mode is:
64     if ($mode =~ /^g.*?/)
65     {
66     $self->{GRAPH_MODE} = 'GLOBAL';
67     # GLOBAL package graphing:
68     use BuildSystem::SCRAMGrapher;
69     $self->{SCRAMGRAPHER} = BuildSystem::SCRAMGrapher->new();
70     }
71     elsif ($mode =~ /^p.*?/)
72     {
73     # All other cases assume per package. This means that each package
74     # is responsible for creating/destroying grapher objects and writing
75     # out graphs, if required:
76     $self->{GRAPH_MODE} = 'PACKAGE';
77     }
78     else
79     {
80     print "SCRAM error: no mode (w=p,w=g) given for graphing utility!","\n";
81     exit(1);
82     }
83    
84     # Set write option:
85     $self->{GRAPH_WRITE} = $writeopt;
86     }
87     else
88     {
89     print "SCRAM error: no mode (w=p,w=g) given for graphing utility!","\n";
90     exit(1);
91     }
92     }
93    
94     sub global_graph_writer()
95     {
96     my $self=shift;
97     my $name='Project';
98     # Only produce graphs with DOT if enabled. This routine is
99     # only used at Project level:
100     if (defined($self->{SCRAMGRAPHER}) && $self->{GRAPH_WRITE})
101     {
102     my $data; # Fake data - there isn't a DataCollector object
103     $self->{SCRAMGRAPHER}->graph_write($data, $name);
104     delete $self->{SCRAMGRAPHER};
105     }
106     else
107     {
108     print "SCRAM error: can't write graph!","\n";
109     exit(1);
110     }
111    
112     return;
113     }
114    
115     #### The methods ####
116     sub datapath()
117     {
118     my $self=shift;
119     my ($path)=@_;
120     my $datapath;
121     # At project-level, the path is src so just return src. Also,
122     # if we received a BuildFile path that we need to determine the data path for,
123     # check first to see if the path matches config/BuildFile. If it does, we have the top-level
124     # datapath which should be src:
125     if ($path eq "$ENV{LOCALTOP}/$ENV{SCRAM_CONFIGDIR}/BuildFile" || $path eq $ENV{SCRAM_SOURCEDIR})
126     {
127     return $ENV{SCRAM_SOURCEDIR};
128     }
129    
130     # For other paths, strip off the src dir (part of LOCALTOP) and the final BuildFile to
131     # get a data position to be used as a key:
132     ($datapath = $path) =~ s|^\Q$ENV{SCRAM_SOURCEDIR}\L/||;
133    
134     if ($datapath =~ m|(.*)/BuildFile$|)
135     {
136     return $1;
137     }
138    
139     return $datapath;
140     }
141    
142     sub check_global_config()
143     {
144     my $self=shift;
145     my $topbuildfile = $self->{CONFIGDIR}."/BuildFile";
146    
147     if ( ! -f $topbuildfile )
148     {
149     print "SCRAM error: no BuildFile at top-level (config)! Invalid area!","\n";
150     exit(1);
151     }
152    
153     return $self;
154     }
155    
156     sub processtree()
157     {
158     my $self=shift;
159     my $parent = $ENV{SCRAM_SOURCEDIR};
160     $self->procrecursive($parent);
161     return $self;
162     }
163    
164     sub updatetree()
165     {
166     my $self=shift;
167     my ($startdir) = @_;
168     print "Updating metadata from $startdir","\n",if ($ENV{SCRAM_DEBUG});
169     $self->updaterecursive($startdir);
170     return $self;
171     }
172    
173     sub updatemkfrommeta()
174     {
175     my $self=shift;
176     my ($startdir)=$ENV{SCRAM_SOURCEDIR};
177     print "Updating Makefile from $startdir","\n",if ($ENV{SCRAM_DEBUG});
178     $self->updatefrommeta($startdir);
179     return $self;
180     }
181    
182     sub scanbranch()
183     {
184     my $self=shift;
185 sashby 1.7 my ($files, $datapath)=@_;
186     my $bfbranch;
187     my $buildfiles;
188 sashby 1.4 # Fix (or rather hack) so that only the current buildfile is parsed, not the parent.
189     # This is required becuase it's not desired to pick up dependencies from the level lower:
190     # one should always do it via a <use name=x> to get the package deps. We don't care about
191     # deps in subsystems (they're only used to define groups) and project-wide deps are added at
192     # template level:
193 sashby 1.7 if (exists($ENV{SCRAM_XMLBUILDFILES}) && ($ENV{SCRAM_XMLBUILDFILES}))
194     {
195     use BuildSystem::XMLBuildFile;
196     $bfbranch=BuildSystem::XMLBuildFile->new();
197     $buildfiles = [ $files->[0].".xml" ];
198     }
199     else
200     {
201     use BuildSystem::BuildFile;
202     $bfbranch=BuildSystem::BuildFile->new();
203     $buildfiles=[ $files->[0] ];
204     }
205 sashby 1.4
206 sashby 1.2 # Scan all buildfiles in a branch:
207 sashby 1.7 $bfbranch->parsebranchfiles($buildfiles);
208    
209 sashby 1.2 # Store:
210     $self->storebranchmetadata($datapath,$bfbranch);
211 sashby 1.7
212 sashby 1.2 return $self;
213     }
214    
215     sub procrecursive()
216     {
217     my $self=shift;
218     my ($dir)=@_;
219     my $datacollector;
220    
221     # Data for current dir:
222     my $treedata = $self->buildtreeitem($dir);
223     # Data for the parent:
224     my $parent = $treedata->parent();
225     my $parenttree = $self->buildtreeitem($parent);
226     # Base classes. These are structural template classes which are fixed in SCRAM:
227 sashby 1.8 my $baseclasses = [ qw( DOMAIN SUBSYSTEM PACKAGE ) ];
228 sashby 1.2
229     # If we have a parent dir, collect METABF. Skip inheriting from config/BuildFile:
230     if (defined ($parenttree) && $parenttree->metabf() && $parent ne 'src')
231     {
232     # Add the meta (BuildFile) location to the current locations meta:
233     $treedata->metabf(@{$parenttree->metabf()});
234     }
235    
236     # Perfect match to class:
237     if ($treedata->suffix() eq '')
238     {
239     # For directories where there's a full match to the classpath, check the class.
240     # Only process Buildfiles if the match occurs for a build product class. In either case,
241     # run the template engine.
242     # Don't process BuildFiles unless we happen to be in a product branch (i.e.,
243     # not a baseclass as defined above) except for Project which we do want:
244     if (! grep($treedata->class() eq $_, @$baseclasses))
245     {
246     # Scan all BuildFiles in this branch:
247     $self->scanbranch($treedata->metabf(),$self->datapath($dir));
248     # Process the build data:
249     $datacollector = $self->processbuildfile($dir, $treedata->path());
250     $treedata->clean(); # Get rid of BRANCHMETA
251     $treedata->branchdata($datacollector);
252     }
253    
254     # And run the engine:
255     $self->run_engine($treedata);
256    
257     foreach my $c ($treedata->children())
258     {
259     if ($c ne '')
260     {
261     $self->procrecursive($c);
262     }
263     }
264     }
265     else
266     {
267     # For directories where there isn't a full match, just run the template engine:
268     $self->run_engine($treedata);
269    
270     foreach my $c ($treedata->children())
271     {
272     if ($c ne '')
273     {
274     $self->procrecursive($c);
275     }
276     }
277     }
278    
279     return $self;
280     }
281    
282     sub updaterecursive()
283     {
284     my $self=shift;
285     my ($dir)=@_;
286     my $datacollector;
287     # updaterecursive() only SCANS and UPDATES METADATA. The Makefile is rebuilt in
288     # its entirety using updatefrommeta(), called after metadata is updated and stored:
289    
290     # Data for current dir:
291     my $treedata = $self->buildtreeitem($dir);
292     # Data for the parent:
293     my $parent = $treedata->parent();
294     my $parenttree = $self->buildtreeitem($parent);
295     # Base classes. These are structural template classes which are fixed in SCRAM:
296 sashby 1.8 my $baseclasses = [ qw( DOMAIN SUBSYSTEM PACKAGE ) ];
297 sashby 1.2
298     # If we have a parent dir, collect METABF. Skip inheriting from config/BuildFile:
299     if (defined ($parenttree) && $parenttree->metabf() && $parent ne 'src')
300     {
301     # Add the meta (BuildFile) location to the current locations meta:
302     $treedata->metabf(@{$parenttree->metabf()});
303     }
304    
305     # Perfect match to class:
306     if ($treedata->suffix() eq '')
307     {
308     # For directories where there's a full match to the classpath, check the class.
309     # Only process Buildfiles if the match occurs for a build product class. In either case,
310     # run the template engine.
311     # Don't process BuildFiles unless we happen to be in a product branch (i.e.,
312     # not a baseclass as defined above):
313     if (! grep($treedata->class() eq $_, @$baseclasses))
314     {
315     # Scan all BuildFiles in this branch:
316     $self->scanbranch($treedata->metabf(),$self->datapath($dir));
317     # Process the build data:
318     $datacollector = $self->processbuildfile($dir, $treedata->path());
319     $treedata->clean();
320     $treedata->branchdata($datacollector);
321     }
322    
323     foreach my $c ($treedata->children())
324     {
325     if ($c ne '')
326     {
327     $self->updaterecursive($c);
328     }
329     }
330     }
331     else
332     {
333     foreach my $c ($treedata->children())
334     {
335     if ($c ne '')
336     {
337     $self->updaterecursive($c);
338     }
339     }
340     }
341    
342     return $self;
343     }
344    
345     sub updatefrommeta()
346     {
347     my $self=shift;
348     my $datacollector;
349     my ($startdir)=@_;
350     # Data for current dir:
351     my $treedata = $self->buildtreeitem($startdir);
352     # Run the engine:
353 sashby 1.3 $self->run_engine($treedata);
354 sashby 1.2
355     foreach my $c ($treedata->children())
356     {
357     if ($c ne '')
358     {
359     $self->updatefrommeta($c);
360     }
361     }
362    
363     return $self;
364     }
365    
366     sub buildtreeitem()
367     {
368     my $self=shift;
369     my ($datapath)=@_;
370     # This will return the TreeItem object for
371     # the corresponding data path:
372     return $self->{BUILDTREE}->{$datapath};
373     }
374    
375     sub bproductparse()
376     {
377     my $self=shift;
378     my ($dataposition, $path, $bcollector, $product, $localg)=@_;
379     my $packdir;
380    
381     if ($dataposition =~ m|(.*)/src|)
382     {
383     $packdir=$1;
384     }
385     elsif ($dataposition =~ m|(.*)/|)
386     {
387     $packdir=$dataposition;
388     }
389    
390     # Probably better to use the bin name/safename:
391     $packdir = $product->safename();
392     my $label = $product->name();
393    
394     # Look for architecture-specific tags:
395     if (my $archdata=$product->archspecific())
396     {
397     $bcollector->resolve_arch($archdata,$packdir);
398     }
399    
400     # Groups:
401     if (my @groups=$product->group())
402     {
403     $bcollector->resolve_groups(\@groups,$packdir);
404     }
405    
406     # Check for packages and external tools:
407     if (my @otheruses=$product->use())
408     {
409     $bcollector->localgraph()->vertex($packdir);
410    
411     # Add vertex and edges for current package and its dependencies:
412     foreach my $OU (@otheruses)
413     {
414     $bcollector->localgraph()->edge($packdir, $OU);
415     }
416    
417     $bcollector->resolve_use(\@otheruses);
418     }
419    
420     # For each tag type that has associated data in this buildfile
421     # data object, get the data and store it:
422     map { my $subname = lc($_); $bcollector->storedata($_, $product->$subname(),$packdir); }
423     $product->basic_tags();
424    
425     # Prepare the metadata for this location:
426     my $graphexists = $bcollector->prepare_meta($packdir);
427    
428     # Write out the graph if required:
429     if ($localg && $self->{GRAPH_WRITE} && $graphexists)
430     {
431     $bcollector->localgraph()->graph_write($bcollector->attribute_data(), $packdir);
432     }
433    
434     # Clean up:
435     $bcollector->clean4storage();
436     return $bcollector;
437     }
438    
439     sub processbuildfile()
440     {
441     my $self=shift;
442     my ($dataposition, $path)=@_;
443     my $collector;
444     my $packdir;
445     my $CURRENTBF = $self->metaobject($dataposition);
446     my $localgrapher=0;
447     my $scramgrapher;
448 sashby 1.4
449 sashby 1.2 if (defined($CURRENTBF))
450     {
451     use BuildSystem::DataCollector;
452    
453     # Graphing:
454     if (! defined($self->{SCRAMGRAPHER}))
455     {
456     # We don't have a grapher object so we must we working at package level.
457     $localgrapher=1;
458     # Create the object here:
459     use BuildSystem::SCRAMGrapher;
460     $scramgrapher = BuildSystem::SCRAMGrapher->new();
461     }
462     else
463     {
464     $scramgrapher = $self->{SCRAMGRAPHER};
465     }
466    
467     my %projects = %{$self->{SCRAM_PROJECTS}};
468     my %projectbases = %{$self->{SCRAM_PROJECT_BASES}};
469    
470     # Set up the collector object:
471     $collector = BuildSystem::DataCollector->new($self, $self->{TOOLMANAGER},
472     $path, \%projects, \%projectbases,
473     $scramgrapher);
474    
475     # Need the package name for our dep tracking:
476     if ($dataposition =~ m|(.*)/src|)
477     {
478     $packdir=$1;
479     }
480     elsif ($dataposition =~ m|(.*)/|)
481     {
482     $packdir=$dataposition;
483     }
484     elsif ($dataposition eq $ENV{SCRAM_SOURCEDIR})
485     {
486     $packdir = $ENV{SCRAM_SOURCEDIR};
487     }
488    
489     # Look for architecture-specific tags:
490     if (my $archdata=$CURRENTBF->archspecific())
491     {
492     $collector->resolve_arch($archdata,$packdir);
493     }
494    
495     # Groups:
496     if (my @groups=$CURRENTBF->group())
497     {
498     $collector->resolve_groups(\@groups,$packdir);
499     }
500    
501     # Check for packages and external tools:
502     if (my @otheruses=$CURRENTBF->use())
503     {
504     $scramgrapher->vertex($packdir);
505    
506     # Add vertex and edges for current package and its dependencies:
507     foreach my $OU (@otheruses)
508     {
509     $scramgrapher->edge($packdir, $OU);
510     }
511    
512     $collector->resolve_use(\@otheruses);
513     }
514    
515     # If we are at project-level, also resolve the 'self' tool. We ONLY do this
516     # at project-level:
517     if ($dataposition eq $ENV{SCRAM_SOURCEDIR})
518     {
519     $collector->resolve_use(['self']);
520     }
521    
522     # For each tag type that has associated data in this buildfile
523     # data object, get the data and store it:
524     map { my $subname = lc($_); $collector->storedata($_, $CURRENTBF->$subname(),$packdir); }
525     $CURRENTBF->basic_tags();
526    
527     # Check for build products and process them here:
528     my $buildproducts=$CURRENTBF->buildproducts();
529    
530     my $BUILDP = {};
531    
532     # If we have build products:
533     if ($buildproducts)
534     {
535     # Build a list of target types that should built at this location in
536     # addition to normal libraries:
537     foreach my $type (keys %$buildproducts)
538     {
539     my $typedata=$CURRENTBF->values($type);
540     while (my ($name,$product) = each %$typedata)
541     {
542     # We make a copy from existing collector object. This is basically a "new()"
543     # followed by some copying of relevant data elements:
544     $bcollector = $collector->copy($localgrapher);
545     # The Product object inherits from same core utility packages
546 sashby 1.4 # as BuildFile so all BuildFile methods can be used on the Product object:
547 sashby 1.2 $self->bproductparse($dataposition,$path,$bcollector,$product,$localgrapher);
548     $product->data($bcollector);
549     $BUILDP->{$product->safename()} = $product;
550     }
551     }
552    
553     # Return the hash of products (safe_name/Product object pairs):
554     return $BUILDP;
555     }
556     else
557     {
558     # Prepare the metadata for this location. Also needed for each build product:
559     my $graphexists = $collector->prepare_meta($packdir);
560    
561     # Write out the graph if required (also to be done for each product):
562     if ($localgrapher && $self->{GRAPH_WRITE} && $graphexists)
563     {
564     $scramgrapher->graph_write($collector->attribute_data(), $packdir);
565     }
566    
567     # At this point I think we can clean away the graph object:
568     $collector->clean4storage();
569    
570     # No products: return main collector:
571     return $collector;
572     }
573     }
574     else
575     {
576     # No build data, just return:
577     return $collector;
578     }
579     }
580    
581     sub create_productstores()
582     {
583     my $self=shift;
584     # This routine will only ever be run for top-level so
585     # datapath can be coded here:
586     my $datapath='src';
587     my $tldata=$self->buildtreeitem($datapath);
588     my $stores=$tldata->rawdata()->productstore();
589    
590     # Iterate over the stores:
591     foreach my $H (@$stores)
592     {
593     my $storename="";
594     # Probably want the store value to be set to <name/<arch> or <arch>/<name> with
595     # <path> only prepending to this value rather than replacing <name>: FIXME...
596     if ($$H{'type'} eq 'arch')
597     {
598     if ($$H{'swap'} eq 'true')
599     {
600     (exists $$H{'path'}) ? ($storename .= $$H{'path'}."/".$ENV{SCRAM_ARCH})
601     : ($storename .= $$H{'name'}."/".$ENV{SCRAM_ARCH});
602     }
603     else
604     {
605     (exists $$H{'path'}) ? ($storename .= $ENV{SCRAM_ARCH}."/".$$H{'path'})
606     : ($storename .= $ENV{SCRAM_ARCH}."/".$$H{'name'});
607     }
608     }
609     else
610     {
611     (exists $$H{'path'}) ? ($storename .= $$H{'path'})
612     : ($storename .= $$H{'name'});
613     }
614    
615     # Create the dir: FIXME: may need a more portable mkdir?
616     system("mkdir","-p",$ENV{LOCALTOP}."/".$storename);
617     }
618     }
619    
620     sub populate()
621     {
622     my $self=shift;
623     my ($paths,$filecache,$toolmanager)=@_;
624     my $datapath;
625     my $buildfile;
626     $|=1; # Flush
627    
628     # The tool manager:
629     $self->{TOOLMANAGER} = $toolmanager;
630    
631 sashby 1.5 # If there are some paths to iterate over, get scram projects from
632     # toolbox. Each project cache is loaded at this point too.
633     # Note that this could be done later, when running processtree() which
634     # is when access to the project caches is really needed (actually when
635     # running datacollector::processbuildfile):
636 sashby 1.2 $self->scramprojects();
637    
638     # Check that there's a global config. Exit if not:
639     $self->check_global_config();
640    
641     # Loop over all paths. Apply a sort so that src (shortest path) is first (FIXME!):
642     foreach my $path (sort(@$paths))
643     {
644     # Ignore config content here:
645     next if ($path !~ m|^\Q$ENV{SCRAM_SOURCEDIR}\L|);
646 sashby 1.3
647 sashby 1.2 # Set the data path:
648 sashby 1.3 $datapath = $self->datapath($path);
649    
650 sashby 1.2 # Create a TreeItem object:
651     use BuildSystem::TreeItem;
652     my $treeitem = BuildSystem::TreeItem->new();
653     $self->{BUILDTREE}->{$datapath} = $treeitem;
654    
655     # If we have the project root (i.e. src), we want to process the
656     # top-level (project config) BuildFile:
657     if ($path eq $ENV{SCRAM_SOURCEDIR})
658     {
659     $buildfile = $ENV{SCRAM_CONFIGDIR}."/BuildFile";
660     # Parse the top-level BuildFile. We must do this here
661     # because we need the ClassPaths. Store as RAWDATA:
662     $self->scan($buildfile, $datapath);
663     # At this point, we've scanned the top-level BuildFile so we can
664     # create the store dirs and setup "self":
665     $self->create_productstores();
666     # We need scram project base vars at project-level:
667     $treeitem->scramprojectbases($self->{SCRAM_PROJECT_BASES});
668     }
669     else
670     {
671     $buildfile = $path."/BuildFile";
672     }
673    
674     # If this BuildFile exists, store in METABF:
675     if ( -f $buildfile )
676     {
677     # This level has a buildfile so store this path:
678     $treeitem->metabf($buildfile);
679     # Scan to resolve groups. Store as RAWDATA:
680     $self->scan($buildfile, $datapath);
681     ($ENV{SCRAM_DEBUG}) ? print "Scanning ",$buildfile,"\n" : print "." ;
682     }
683    
684 sashby 1.3 if ($self->skipdir($datapath))
685     {
686     $treeitem->skip(1);
687     print $datapath," building skipped.\n", if ($ENV{SCRAM_DEBUG});
688     }
689    
690 sashby 1.2 # Now add the class and path info to the TreeItem:
691     my ($class, $classdir, $suffix) = @{$self->buildclass($path)};
692    
693     $treeitem->class($class);
694     $treeitem->classdir($classdir);
695     $treeitem->suffix($suffix);
696     $treeitem->path($path);
697     $treeitem->safepath($path);
698     $treeitem->parent($datapath);
699     $treeitem->children($filecache);
700     $treeitem->name();
701     }
702    
703     print "\n";
704    
705     # Check dependencies- look for cycles in the global dependency data:
706     $self->check_dependencies();
707 sashby 1.3 $self->skipdir() if ($ENV{SCRAM_DEBUG});
708 sashby 1.2 }
709    
710     sub check_dependencies()
711     {
712     my $self=shift;
713     # Use the SCRAMGrapher to process the deps and return a
714     # Graph object:
715     use BuildSystem::SCRAMGrapher;
716    
717     my $SG = BuildSystem::SCRAMGrapher->new($self->{DEPENDENCIES}); # GLOBAL dependencies
718     my $G = $SG->_graph_init();
719     my @classification = $G->edge_classify();
720     my @cycles;
721     my $status=0;
722    
723     # Dump the vertex classification if required:
724     if ($ENV{SCRAM_DEBUG})
725     {
726     print "\n";
727     print "Dumping vertex/path classifications:","\n";
728     print "\n";
729     printf("%-40s %-40s %-15s\n",'Vertex_i','Vertex_j','CLASS');
730     printf("%-95s\n",'-'x95);
731     }
732    
733     foreach my $element (@classification)
734     {
735     printf("%-40s %-40s %-15s\n",$element->[0],$element->[1],$element->[2]), if ($ENV{SCRAM_DEBUG});
736     # Save our cycles to list separately:
737     if ($element->[2] eq 'back')
738     {
739     push(@cycles,$element);
740     $status++;
741     }
742     }
743    
744     print "\n";
745     if ($status)
746     {
747     map
748     {
749     print $::fail."SCRAM buildsystem ERROR: Cyclic dependency ",$_->[0]," <--------> ",$_->[1].$::normal."\n";
750     } @cycles;
751     print "\n";
752    
753     # Exit:
754     exit(1);
755     }
756    
757     # Otherwise return:
758     return;
759     }
760    
761     sub update_toplevel()
762     {
763     my $self=shift;
764     my (@buildfiles) = @_;
765     my $treeitem;
766    
767     print "Re-scanning at top-level..\n";
768    
769     my $datapath = $self->datapath($ENV{LOCALTOP}."/".$ENV{SCRAM_CONFIGDIR}."/BuildFile");
770    
771     # This updates the raw data:
772     $self->scan($ENV{LOCALTOP}."/".$ENV{SCRAM_CONFIGDIR}."/BuildFile", $datapath);
773    
774     # Update everything else:
775     foreach my $B (@buildfiles)
776     {
777     next if ($B eq $ENV{LOCALTOP}."/config/BuildFile");
778     $datapath = $self->datapath($B);
779     # Check to see if we already have the raw data for this buildfile.
780     # Note that we won't if this scan was run from update mode. In this
781     # case, we set up the TreeItem object:
782     if (! exists($self->{BUILDTREE}->{$datapath}))
783     {
784     use BuildSystem::TreeItem;
785     $treeitem = BuildSystem::TreeItem->new();
786     my $path=$ENV{SCRAM_SOURCEDIR}."/".$datapath;
787     my ($class, $classdir, $suffix) = @{$self->buildclass($path)};
788    
789     $treeitem->class($class);
790     $treeitem->classdir($classdir);
791     $treeitem->suffix($suffix);
792     $treeitem->path($path);
793     $treeitem->safepath($path);
794     $treeitem->parent($datapath);
795     $treeitem->children($filecache);
796     $treeitem->name();
797    
798     $self->{BUILDTREE}->{$datapath} = $treeitem;
799    
800     print "Scanning ",$B,"\n";
801     $self->scan($B,$datapath); # This updates the raw data
802     }
803     else
804     {
805     print "Scanning ",$B,"\n";
806     $self->scan($B,$datapath); # This updates the raw data
807     }
808    
809     # Recursively update the tree from this data path:
810     $self->updatetree($datapath);
811     }
812     }
813    
814     sub update()
815     {
816     my $self=shift;
817     my ($changeddirs, $addeddirs, $bf, $removedpaths, $toolmanager, $filecache) = @_;
818     my $buildfiles = {};
819     # Copy the contents of the array of BuildFiles to a hash so that
820     # we can track which ones have been parsed:
821     map
822     {
823     $buildfiles->{$_} = 0;
824     } @$bf;
825    
826     # Tool manager:
827     $self->{TOOLMANAGER} = $toolmanager;
828     # Get scram projects from toolbox. Each project cache is
829     # loaded at this point too:
830     $self->scramprojects();
831    
832     # Remove build data for removed directories:
833     $self->removedata($removedpaths);
834    
835     # Now check to see if something changed at the top-level. If so we reparse everything:
836     my $toplevel = $ENV{LOCALTOP}."/".$ENV{SCRAM_CONFIGDIR}."/BuildFile";
837    
838     if (exists($buildfiles->{$toplevel}))
839     {
840     $buildfiles->{$toplevel} = 1; # Parsed
841     $self->update_toplevel(@$bf);
842     }
843     else
844     {
845     # Process all new directories first then changed ones. This means that everything will be in
846     # place once we start parsing any modified BuildFiles and once we run updatetree():
847    
848     $self->update_newdirs($addeddirs);
849    
850     $self->update_existingdirs($changeddirs);
851    
852     # Now check for any modified BuildFiles that have not yet been rescanned:
853     foreach my $bftoscan (keys %$buildfiles)
854     {
855     if ($buildfiles->{$bftoscan} == 0)
856     {
857     my $datapath = $self->datapath($bftoscan);
858     $self->scan($bftoscan,$datapath); # This updates the raw data
859     }
860     }
861     }
862    
863     # Also rebuild the project Makefile from scratch:
864     $self->updatemkfrommeta();
865     print "\n";
866     }
867    
868     sub update_newdirs()
869     {
870     my $self=shift;
871     my ($newdirs) = @_;
872     foreach my $path (@$newdirs)
873     {
874     print "Processing new directory \"",$path,"\"\n",if ($ENV{SCRAM_DEBUG});
875     $self->updateadir($path);
876     }
877     }
878    
879     sub update_existingdirs()
880     {
881     my $self=shift;
882     my ($changeddirs) = @_;
883     foreach my $path (@$changeddirs)
884     {
885     print "Processing modified directory \"",$path,"\"\n",if ($ENV{SCRAM_DEBUG});
886     $self->updateadir($path);
887     }
888     }
889    
890     sub updateadir()
891     {
892     my $self=shift;
893     my ($path) = @_;
894     my $datapath = $self->datapath($path);
895     my $possiblebf = $path."/BuildFile";
896     my $treeitem;
897    
898     if (! exists($self->{BUILDTREE}->{$datapath}))
899     {
900     use BuildSystem::TreeItem;
901     $treeitem = BuildSystem::TreeItem->new();
902    
903     # Get the class info:
904     my ($class, $classdir, $suffix) = @{$self->buildclass($path)};
905    
906     $treeitem->class($class);
907     $treeitem->classdir($classdir);
908     $treeitem->suffix($suffix);
909     $treeitem->path($path);
910     $treeitem->safepath($path);
911     $treeitem->parent($datapath);
912     $treeitem->children($filecache);
913     $treeitem->name();
914     # Store the TreeItem object:
915     $self->{BUILDTREE}->{$datapath} = $treeitem;
916     }
917    
918     # Update the status of the parent. Add the child and update
919     # the safe subdirs:
920     my $parent = $self->{BUILDTREE}->{$datapath}->parent();
921     $self->{BUILDTREE}->{$parent}->updateparentstatus($datapath);
922    
923     # Now check to see if there is a BuildFile here. If there is, parse it:
924     if ( -f $possiblebf)
925     {
926     # This level has a buildfile so store this path:
927     $self->{BUILDTREE}->{$datapath}->metabf($possiblebf);
928     # Scan to resolve groups. Store as RAWDATA:
929     print "Scanning ",$possiblebf,"\n";
930     $self->scan($possiblebf, $datapath);
931     # Check to see if this BuildFile is known to have needed scanning. If so,
932     # mark it as read:
933     if (exists($buildfiles->{$possiblebf}))
934     {
935     $buildfiles->{$possiblebf} = 1;
936     }
937     }
938    
939     # Recursively update the tree from this data path:
940     $self->updatetree($datapath);
941     }
942    
943     sub scan()
944     {
945     my $self=shift;
946 sashby 1.7 my ($inputbuildfile, $datapath) = @_;
947     my $bfparse;
948     my $buildfile;
949 sashby 1.2
950 sashby 1.7 if (exists($ENV{SCRAM_XMLBUILDFILES}) && ($ENV{SCRAM_XMLBUILDFILES}))
951     {
952     use BuildSystem::XMLBuildFile;
953     $bfparse=BuildSystem::XMLBuildFile->new();
954     $buildfile=$inputbuildfile.".xml";
955     }
956     else
957     {
958     use BuildSystem::BuildFile;
959     $bfparse=BuildSystem::BuildFile->new();
960     $buildfile=$inputbuildfile;
961     }
962    
963     # Execute the parse:
964 sashby 1.2 $bfparse->parse($buildfile);
965 sashby 1.7
966 sashby 1.2 # Store group data:
967 sashby 1.3 $self->addgroup($bfparse->defined_group(), $datapath)
968     if ($bfparse->defined_group());
969    
970     # See if there were skipped dirs:
971     my $skipped = $bfparse->skippeddirs($datapath);
972     # Check to see if there was an info array for this location.
973     # If so, we extract the first element of the array (i.e. ->[1])
974     # and store it under the datapath entry. This is just so that useful
975     # messages explaining why the dir was skipped can be preserved.
976     if (ref($skipped) eq 'ARRAY')
977     {
978     $self->skipdir($datapath,$skipped->[1]);
979     }
980 sashby 1.2
981     $self->storedata($datapath, $bfparse);
982    
983 sashby 1.3 # Add the dependency list to our store:
984     $self->{DEPENDENCIES}->{$datapath} = $bfparse->dependencies();
985 sashby 1.2 return $self;
986     }
987    
988     sub init_engine()
989     {
990     my $self=shift;
991    
992     # Create the interface to the template engine:
993     use BuildSystem::TemplateInterface;
994     # Pass in the config dir as the location where templates live:
995     $self->{TEMPLATE_ENGINE} = BuildSystem::TemplateInterface->new();
996     }
997    
998     sub run_engine()
999     {
1000     my $self=shift;
1001     my ($templatedata)=@_;
1002    
1003     $self->{TEMPLATE_ENGINE}->template_data($templatedata);
1004     $self->{TEMPLATE_ENGINE}->run();
1005     return $self;
1006     }
1007    
1008     sub buildclass
1009     {
1010     my $self=shift;
1011     my ($path)=@_;
1012     my $cache=[];
1013     # Associate a path with ClassPath setting.
1014     # For now, just assumes global data has been scanned and class settings
1015     # are already known (in $self->{CONFIGDATA}->classpath()).
1016     # Generate more optimal classpath data structure, only once.
1017     # Split every cache definition into an array of pairs, directory
1018     # name and class. So ClassPath of type "+foo/+bar/src+library"
1019     # becomes [ [ "" "foo" ] [ "" "bar" ] [ "src" "library" ] ]
1020     my @CLASSPATHS=@{$self->{BUILDTREE}->{$ENV{SCRAM_SOURCEDIR}}->rawdata()->classpath()};
1021    
1022     if (! scalar @$cache)
1023     {
1024     foreach my $classpath (@CLASSPATHS)
1025     {
1026     push (@$cache, [map { [ split(/\+/, $_) ] } split(/\//, $classpath)]);
1027     }
1028     }
1029    
1030     print "WARNING: No ClassPath definitions, nothing will be done!","\n",
1031     if (! scalar @$cache);
1032     # Now scan the class paths. All the classpaths are given a rank
1033     # to mark how relevant they are, and then the best match is chosen.
1034     #
1035     # The ranking logic is as follows. We scan each class path and
1036     # drop if it doesn't match at all. For paths that match, we
1037     # record how many components of the class was *not* used to match
1038     # on the class: for a short $path, many classes will match.
1039     # For each path component we record whether the match was exact
1040     # (if the class part is empty, i.e. "", it's a wildcard that
1041     # matches everything). Given these rankings, we pick
1042     # - the *first* class that
1043     # - has least *unmatched* components
1044     # - with *first* or *longest* exact match sequence in
1045     # left-to-right order.
1046     my @ranks = ();
1047     my @dirs = split(/\/+/, $path);
1048     CLASS: foreach my $class (@$cache)
1049     {
1050     # The first two members of $rank are fixed: how much of path
1051     # was and was not used in the match.
1052     my $rank = [[], [@dirs]];
1053     foreach my $component (@$class)
1054     {
1055     my $dir = $rank->[1][0];
1056     if (! defined $dir)
1057     {
1058     # Path exhausted. Leave used/unused as is.
1059     last;
1060     }
1061     elsif ($component->[0] eq "")
1062     {
1063     # Wildcard match, push class and use up path
1064     push(@$rank, [1, $component->[1]]);
1065     push(@{$rank->[0]}, shift(@{$rank->[1]}));
1066     }
1067     elsif ($component->[0] eq $dir)
1068     {
1069     # Exact match, push class and use up path
1070     push(@$rank, [0, $component->[1]]);
1071     push(@{$rank->[0]}, shift(@{$rank->[1]}));
1072     }
1073     else
1074     {
1075     # Unmatched, leave used/unused as is.
1076     last;
1077     }
1078     }
1079    
1080     push(@ranks, $rank);
1081     }
1082    
1083     # If no classes match, bail out:
1084     if (! scalar @ranks)
1085     {
1086     return "";
1087     }
1088    
1089     # Sort in ascending order by how much was of class was not used;
1090     # the first entry has least "extra" trailing match data. Then
1091     # truncate to only those equal to the best rank.
1092     my @sorted = sort { scalar(@{$a->[1]}) <=> scalar(@{$b->[1]}) } @ranks;
1093     my @best = grep(scalar(@{$_->[1]}) == scalar(@{$sorted[0][1]}), @sorted);
1094    
1095     # Now figure which of the best-ranking classes have the longest
1096     # exact match in left-to-right order (= which one is first, and
1097     # those with equal first exact match, longest exact match).
1098     my $n = 0;
1099     my $class = $best[$n][scalar @{$best[$n]}-1];
1100    
1101     # Return the class data:
1102     return [ $class->[1], join('/', @{$best[$n][0]}), join('/', @{$best[$n][1]}) ];
1103     }
1104    
1105     sub storedata
1106     {
1107     my $self=shift;
1108     my ($datapath, $data)=@_;
1109 sashby 1.3
1110 sashby 1.2 # Store the content of this BuildFile in cache:
1111     $self->{BUILDTREE}->{$datapath}->rawdata($data);
1112     return $self;
1113     }
1114    
1115     sub removedata
1116     {
1117     my $self=shift;
1118     my ($removedpaths) = @_;
1119    
1120     foreach my $rd (@$removedpaths)
1121     {
1122     my $datapath = $self->datapath($rd);
1123     # Remove all data, recursively, from $datapath:
1124     $self->recursive_remove_data($datapath);
1125     }
1126    
1127     return $self;
1128     }
1129    
1130     sub recursive_remove_data()
1131     {
1132     my $self=shift;
1133     my ($datapath)=@_;
1134    
1135     # Delete main entry in build data via TreeItem:
1136     if (exists($self->{BUILDTREE}->{$datapath}))
1137     {
1138     # We also must modify the parent TreeItem to remove the child
1139     # from SAFE_SUBDIRS as well as from CHILDREN array:
1140     my $parent = $self->{BUILDTREE}->{$datapath}->parent();
1141     $self->{BUILDTREE}->{$parent}->updatechildlist($datapath);
1142    
1143     # Get the children:
1144     my @children = $self->{BUILDTREE}->{$datapath}->children();
1145    
1146     foreach my $childpath (@children)
1147     {
1148     # The child path value is the datapath so can be used
1149     # directly when deleting data entries
1150     $self->recursive_remove_data($childpath);
1151     }
1152    
1153     # Finally, delete the parent data (a TreeItem):
1154     delete $self->{BUILDTREE}->{$datapath};
1155     }
1156    
1157     # return:
1158     return $self;
1159     }
1160    
1161     sub storebranchmetadata()
1162     {
1163     my $self=shift;
1164     my ($datapath,$data)=@_;
1165    
1166     # Store the content of this BuildFile in cache:
1167     $self->{BUILDTREE}->{$datapath}->branchmetadata($data);
1168     return $self;
1169     }
1170    
1171     sub buildobject
1172     {
1173     my $self=shift;
1174     my ($datapath)=@_;
1175    
1176     if (exists($self->{BUILDTREE}->{$datapath}) && defined($self->{BUILDTREE}->{$datapath}->rawdata()))
1177     {
1178     return $self->{BUILDTREE}->{$datapath}->rawdata();
1179     }
1180     else
1181     {
1182     return undef;
1183     }
1184     }
1185    
1186     sub metaobject
1187     {
1188     my $self=shift;
1189     my ($datapath)=@_;
1190    
1191     if (exists($self->{BUILDTREE}->{$datapath}) && defined($self->{BUILDTREE}->{$datapath}->branchmetadata()))
1192     {
1193     return $self->{BUILDTREE}->{$datapath}->branchmetadata();
1194     }
1195     else
1196     {
1197     return undef;
1198     }
1199     }
1200    
1201     sub addgroup
1202     {
1203     my $self=shift;
1204     my ($grouparray,$datapath)=@_;
1205 sashby 1.6 my $project;
1206 sashby 1.2
1207     foreach my $group (@{$grouparray})
1208     {
1209 sashby 1.6 # Report an error if the group is defined already in a BuildFile
1210     # other than the one at $path (avoids errors because KNOWNGROUPS
1211     # is not reset before re-parsing a BuildFile in which a group is defined):
1212 sashby 1.2 if (exists $self->{KNOWNGROUPS}->{$group}
1213     && $self->{KNOWNGROUPS}->{$group} ne $datapath)
1214     {
1215 sashby 1.6 # Group already exists locally so exit:
1216     print "\n\n";
1217     $::scram->scramerror("Group \"".$group."\", defined in ".$datapath."/BuildFile, is already defined in ".
1218     $self->{KNOWNGROUPS}->{$group}."/BuildFile.\n");
1219     print "\n";
1220     }
1221     elsif ($self->searchprojects($group,\$project))
1222     {
1223     # Group already exists in a scram project so exit:
1224     print "\n\n";
1225     $::scram->scramerror("Group \"".$group."\", defined locally in ".$datapath."/BuildFile, is already defined in ".
1226     $project."\n");
1227     print "\n";
1228 sashby 1.2 }
1229     else
1230     {
1231     $self->{KNOWNGROUPS}->{$group} = $datapath;
1232     }
1233     }
1234     }
1235    
1236 sashby 1.6 sub searchprojects()
1237     {
1238     my $self=shift;
1239     my ($group,$projectref)=@_;
1240    
1241     foreach my $pjt (keys %{$self->{SCRAM_PROJECTS}})
1242     {
1243     print "Checking for group $group in SCRAM project $pjt","\n", if ($ENV{SCRAM_DEBUG});
1244     # As soon as a project is found to have defined $group, we return
1245     # the project name:
1246     if (exists $self->{SCRAM_PROJECTS}->{$pjt}->{KNOWNGROUPS}->{$group})
1247     {
1248     # Store the project name and data path:
1249     $$projectref="project ".uc($pjt)." (".$self->{SCRAM_PROJECTS}->{$pjt}->{KNOWNGROUPS}->{$group}."/BuildFile)";
1250     return(1);
1251     }
1252     }
1253    
1254     # No group found to have been defined already so return false:
1255     return (0);
1256     }
1257    
1258 sashby 1.2 sub findgroup
1259     {
1260     my $self=shift;
1261     my ($groupname) = @_;
1262    
1263     if (exists $self->{KNOWNGROUPS}->{$groupname})
1264     {
1265     # If group exists, return data:
1266     return $self->{KNOWNGROUPS}->{$groupname};
1267     }
1268     else
1269     {
1270     # Not found so return:
1271     return(0);
1272     }
1273     }
1274    
1275     sub knowngroups
1276     {
1277     my $self=shift;
1278     @_ ? $self->{KNOWNGROUPS}=shift
1279     : $self->{KNOWNGROUPS}
1280     }
1281    
1282     sub scramprojects()
1283     {
1284     my $self=shift;
1285     # Need this to be able to read our project cache:
1286     use Cache::CacheUtilities;
1287    
1288     $self->{SCRAM_PROJECTS} = $self->{TOOLMANAGER}->scram_projects();
1289    
1290     # Also store the BASE of each project:
1291     $self->{SCRAM_PROJECT_BASES}={};
1292    
1293     # Load the project cache for every scram-managed project in our toolbox:
1294     while (my ($project, $info) = each %{$self->{SCRAM_PROJECTS}})
1295     {
1296     if ( -f $info."/.SCRAM/".$ENV{SCRAM_ARCH}."/ProjectCache.db")
1297     {
1298     print "Reading cache for ",uc($project),"\n", if ($ENV{SCRAM_DEBUG});
1299     $self->{SCRAM_PROJECTS}->{$project} =
1300     &Cache::CacheUtilities::read($info."/.SCRAM/".$ENV{SCRAM_ARCH}."/ProjectCache.db");
1301     $self->{SCRAM_PROJECT_BASES}->{uc($project)."_BASE"} = $info;
1302     }
1303     else
1304     {
1305     print "WARNING: Unable to read project cache for ",uc($project)," tool.\n", if ($ENV{SCRAM_DEBUG});
1306     print " It could be that the project has not been built for your current architecture.","\n",
1307     if ($ENV{SCRAM_DEBUG});
1308     delete $self->{SCRAM_PROJECTS}->{$project};
1309     }
1310     }
1311    
1312     # Also check to see if we're based on a release area. If so, store the cache as above. Don't store
1313     # the project name but instead just use 'RELEASE':
1314     if (my $releasearea=$::scram->releasearea() && exists $ENV{RELEASETOP})
1315     {
1316     if ( -f $ENV{RELEASETOP}."/.SCRAM/".$ENV{SCRAM_ARCH}."/ProjectCache.db")
1317     {
1318     # OK, so we found the cache. Now read it and store in the projects list:
1319     $self->{SCRAM_PROJECTS}->{RELEASE} =
1320     &Cache::CacheUtilities::read($ENV{RELEASETOP}."/.SCRAM/".$ENV{SCRAM_ARCH}."/ProjectCache.db");
1321     print "OK found release cache ",$self->{SCRAM_PROJECTS}->{RELEASE},"\n", if ($ENV{SCRAM_DEBUG});
1322     }
1323     else
1324     {
1325     print "WARNING: Current area is based on a release area but the project cache does not exist!","\n";
1326     }
1327     }
1328     }
1329    
1330     sub scramprojectbases()
1331     {
1332     my $self=shift;
1333     return $self->{SCRAM_PROJECT_BASES};
1334     }
1335    
1336     sub alldirs
1337     {
1338     my $self=shift;
1339     return @{$self->{ALLDIRS}};
1340     }
1341    
1342 sashby 1.3 sub skipdir
1343     {
1344     my $self=shift;
1345     my ($dir, $message) = @_;
1346    
1347     # Set the info if we have both args:
1348     if ($dir && $message)
1349     {
1350     $self->{SKIPPEDDIRS}->{$dir} = $message;
1351     }
1352     # If we have the dir name only, return true if
1353     # this dir is to be skipped:
1354     elsif ($dir)
1355     {
1356     (exists($self->{SKIPPEDDIRS}->{$dir})) ? return 1 : return 0;
1357     }
1358     else
1359     {
1360     # Dump the list of directories and the message for each:
1361     foreach my $directory (keys %{$self->{SKIPPEDDIRS}})
1362     {
1363     print "Directory \"",$directory,"\" skipped by the build system";
1364     if (length($self->{SKIPPEDDIRS}->{$directory}->[0]) > 10)
1365     {
1366     chomp($self->{SKIPPEDDIRS}->{$directory}->[0]);
1367     my @lines = split("\n",$self->{SKIPPEDDIRS}->{$directory}->[0]); print ":\n";
1368     foreach my $line (@lines)
1369     {
1370     next if ($line =~ /^\s*$/);
1371     print "\t-- ",$line,"\n";
1372     }
1373     print "\n";
1374     }
1375     else
1376     {
1377     print ".","\n";
1378     }
1379     }
1380     }
1381     }
1382    
1383 sashby 1.2 sub verbose
1384     {
1385     my $self=shift;
1386     # Turn on verbose mode:
1387     @_ ? $self->{VERBOSE} = shift
1388     : $self->{VERBOSE}
1389     }
1390    
1391     sub cachestatus()
1392     {
1393     my $self=shift;
1394     # Set/return the status of the cache:
1395     @_ ? $self->{STATUS} = shift
1396     : $self->{STATUS}
1397     }
1398    
1399     sub logmsg
1400     {
1401     my $self=shift;
1402     # Print a message to STDOUT if VERBOSE is true:
1403     print STDERR @_ if $self->verbose();
1404     }
1405    
1406     sub name()
1407     {
1408     my $self=shift;
1409     # Set/return the name of the cache to use:
1410     @_ ? $self->{CACHENAME} = shift
1411     : $self->{CACHENAME}
1412     }
1413    
1414     sub save()
1415     {
1416     my $self=shift;
1417     # Delete unwanted stuff:
1418     delete $self->{DEPENDENCIES};
1419     delete $self->{TOOLMANAGER};
1420     delete $self->{TEMPLATE_ENGINE};
1421     delete $self->{SCRAM_PROJECTS};
1422     delete $self->{SCRAM_PROJECT_BASES};
1423     return $self;
1424     }
1425    
1426 sashby 1.7
1427    
1428    
1429    
1430    
1431    
1432     #### Routines for migrating BuildFile syntax to XML ####
1433     sub scan2xml()
1434     {
1435     my $self=shift;
1436     my ($buildfile) = @_;
1437     print "Migrating $buildfile to XML","\n";
1438     use BuildSystem::BuildFileXMLWriter;
1439     my $bfparse=BuildSystem::BuildFileXMLWriter->new();
1440     $bfparse->parse($buildfile);
1441     return $self;
1442     }
1443    
1444     sub migrate2XML()
1445     {
1446     my $self=shift;
1447     my ($paths)=@_;
1448     my $datapath;
1449     my $buildfile;
1450     $|=1; # Flush
1451    
1452     # Loop over all paths. Apply a sort so that src (shortest path) is first (FIXME!):
1453     foreach my $path (sort(@$paths))
1454     {
1455     # Ignore config content here:
1456     next if ($path !~ m|^\Q$ENV{SCRAM_SOURCEDIR}\L|);
1457    
1458     # If we have the project root (i.e. src), we want to process the
1459     # top-level (project config) BuildFile:
1460     if ($path eq $ENV{SCRAM_SOURCEDIR})
1461     {
1462     $buildfile = $ENV{SCRAM_CONFIGDIR}."/BuildFile";
1463     # Parse the top-level BuildFile. We must do this here
1464     # because we need the ClassPaths. Store as RAWDATA:
1465     $self->scan2xml($buildfile);
1466     next;
1467     }
1468     else
1469     {
1470     $buildfile = $path."/BuildFile";
1471     }
1472    
1473     # If this BuildFile exists, store in METABF:
1474     if ( -f $buildfile )
1475     {
1476     # Scan to resolve groups. Store as RAWDATA:
1477     $self->scan2xml($buildfile);
1478     }
1479     }
1480    
1481     print "\n";
1482     }
1483    
1484 sashby 1.2 1;