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