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