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