ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/cvsroot/COMP/SCRAM/src/BuildSystem/DataCollector.pm
Revision: 1.2
Committed: Fri Dec 10 13:41:37 2004 UTC (20 years, 5 months ago) by sashby
Content type: text/plain
Branch: MAIN
CVS Tags: V1_0_0
Changes since 1.1: +1189 -0 lines
Log Message:
Merged V1_0 branch to HEAD

File Contents

# User Rev Content
1 sashby 1.2 #____________________________________________________________________
2     # File: DataCollector.pm
3     #____________________________________________________________________
4     #
5     # Author: Shaun Ashby <Shaun.Ashby@cern.ch>
6     # Update: 2004-06-30 11:13:06+0200
7     # Revision: $Id: DataCollector.pm,v 1.1.2.15 2004/11/25 14:49:12 sashby Exp $
8     #
9     # Copyright: 2004 (C) Shaun Ashby
10     #
11     #--------------------------------------------------------------------
12     package BuildSystem::DataCollector;
13     require 5.004;
14    
15     # A transient cache for tracking frequency of packages:
16     my $TRANSIENTCACHE = {};
17    
18     use Exporter;
19     @ISA=qw(Exporter);
20     @EXPORT_OK=qw( );
21    
22     sub new()
23     ###############################################################
24     # new #
25     ###############################################################
26     # modified : Wed Jun 30 11:13:20 2004 / SFA #
27     # params : #
28     # : #
29     # function : #
30     # : #
31     ###############################################################
32     {
33     my $proto=shift;
34     my $class=ref($proto) || $proto;
35     my $self={};
36     my ($buildcache,$toolmgr,$path,$scramprojects,$sprojectbases,$scramgrapher)=@_;
37    
38     bless $self,$class;
39    
40     $self->{BUILDCACHE} = $buildcache;
41     $self->{TOOLMGR} = $toolmgr;
42     $self->{BRANCH} = $path;
43    
44     $self->{SPROJECTS} = $scramprojects;
45     $self->{SPROJECTBASES} = $sprojectbases;
46    
47     $self->{SEEN_LOCAL_PACKAGES}={};
48     $self->{SEEN_RELEASE_PACKAGES}={};
49     $self->{SEEN_REMOTE_PACKAGES}={};
50     $self->{SEEN_TOOLS}={};
51    
52     $self->{G} = $scramgrapher;
53    
54     # Somewhere to store the real data:
55     $self->{content} = {};
56    
57     return $self;
58     }
59    
60     sub prepare_meta()
61     {
62     my $self=shift;
63     my ($packdir)=@_;
64     my @itemnames=qw( INCLUDE LIBDIR LIB LIBTYPE MAKEFILE FLAGS ); # the list of tags to be collected
65     my $showgraphs = 1; # We assume we have packages as deps so can show graphs if enabled
66    
67     # See if we can do a topological sort:
68     $self->{BUILD_ORDER} = $self->{G}->sort();
69     $self->{METADATA_ORDER} = [ reverse(@{$self->{BUILD_ORDER}}) ];
70    
71     # Also need to collect other data e.g. INCLUDE LIBDIR LIB LIBTYPE MAKEFILE FLAGS, from
72     # inside product tags:
73     if (exists($self->{content}->{$packdir}))
74     {
75     foreach my $item (@itemnames)
76     {
77     $self->datacollector($item, $self->{content}->{$packdir}->{$item});
78     }
79     }
80    
81     # Check to see if there were any packages:
82     if ($#{$self->{METADATA_ORDER}} >= 0)
83     {
84     # Here is where we actually prepare the INCLUDE, LIB, LIBDIR, FLAGS data for
85     # all tools and packages needed by this package:
86     foreach my $mdobject (@{$self->{METADATA_ORDER}})
87     {
88     if (exists($self->{content}->{$mdobject}))
89     {
90     # We have a local, release or remote package:
91     foreach my $item (@itemnames)
92     {
93     $self->datacollector($item, $self->{content}->{$mdobject}->{$item});
94     }
95     }
96     elsif (exists($self->{SEEN_TOOLS}->{$mdobject}) && $self->{SEEN_TOOLS}->{$mdobject} ne 'SCRAM')
97     {
98     # Check tools
99     # Make a copy of the tool object:
100     my $t = $self->{SEEN_TOOLS}->{$mdobject};
101     $self->tooldatacollector($t);
102     }
103     elsif ($self->{TOOLMGR}->definedtool(lc($mdobject)))
104     {
105     # Maybe this is a tool picked up from another package. We check to see if it's in
106     # our toolbox:
107     my $t=$self->{TOOLMGR}->checkifsetup(lc($mdobject));
108     $self->tooldatacollector($t);
109     }
110     }
111     }
112     else
113     {
114     # There were no entries in METADATA_ORDER but we might have some other data,
115     # especially INCLUDE: handle that here
116     print "SCRAM debug: No packages in our data dir (\"",$packdir,"\")\n",if ($ENV{SCRAM_DEBUG});
117     # Check to see if there's data and collect it:
118     if (exists($self->{content}->{$packdir}))
119     {
120     # We have a local, release or remote package:
121     foreach my $item (@itemnames)
122     {
123     $self->datacollector($item, $self->{content}->{$packdir}->{$item});
124     }
125     }
126     # We don't show graphs:
127     $showgraphs = 0;
128     }
129     # return:
130     return $showgraphs;
131     }
132    
133     sub tooldatacollector()
134     {
135     my $self=shift;
136     my ($tool)=@_;
137     my $TS=[ qw( LIB LIBDIR INCLUDE ) ];
138    
139     # Deal with any variables first .Store directly into the hash
140     # that will be exposed to the template engine:
141     foreach my $toolvar ($tool->list_variables())
142     {
143     $self->{DATA}->{VARIABLES}->{$toolvar} = $tool->variable_data($toolvar);
144     }
145    
146     # Collect Makefile tags and store directly in DATA:
147     $self->storedata(MAKEFILE,[ $tool->makefile() ],''); # No referring name needed:
148    
149     # Store the flags into the DATA hash:
150     if (defined (my $fhash=$tool->allflags()))
151     {
152     while (my ($flag, $flagvalue) = each %{$fhash})
153     {
154     $self->flags($flag,$flagvalue);
155     }
156     }
157    
158     # Finally we get the LIB/LIBDIR/INCLUDE:
159     foreach my $T (@{$TS})
160     {
161     my $sub=lc($T);
162     $self->datacollector($T, [ $tool->$sub() ]);
163     }
164     }
165    
166     sub datacollector()
167     {
168     my $self=shift;
169     my ($tag,$data)=@_;
170    
171     if ($tag eq 'FLAGS') # This data is a hash
172     {
173     $self->storedata($tag,$data);
174     return;
175     }
176    
177     # We need somewhere to store the data:
178     if (! exists($self->{DATA}))
179     {
180     $self->{DATA} = {};
181     }
182    
183     # For libs, we need to check to see if we have a library that should
184     # appear first in list of libs:
185     if ($tag eq 'LIB')
186     {
187     # Now we take the data passed to us and squirrel it away:
188     if (exists($self->{DATA}->{$tag}))
189     {
190     # Only add the item if it doesn't already exist:
191     foreach my $d (@$data)
192     {
193     # If there is a match to F:<lib>, extract the lib name
194     # and store it in a FIRSTLIBS array:
195     if ($d =~ /^F:(.*)?/)
196     {
197     my $libname=$1;
198     if (exists($self->{DATA}->{FIRSTLIB}))
199     {
200     # Check to see if the library already appears in the LIB
201     if (! grep($libname eq $_, @{$self->{DATA}->{FIRSTLIB}}))
202     {
203     push(@{$self->{DATA}->{FIRSTLIB}}, $libname);
204     }
205     }
206     else
207     {
208     # Create the firstlib array:
209     $self->{DATA}->{FIRSTLIB} = [ $libname ];
210     }
211     }
212     else
213     {
214     if (! grep($d eq $_, @{$self->{DATA}->{$tag}}))
215     {
216     push(@{$self->{DATA}->{$tag}},$d);
217     }
218     }
219     }
220     }
221     else
222     {
223     # The storage for lib doesn't exist yet so create it here:
224     $self->{DATA}->{$tag} = [];
225    
226     foreach my $d (@$data)
227     {
228     # If there is a match to F:<lib>, extract the lib name
229     # and store it in a FIRSTLIBS array:
230     if ($d =~ /^F:(.*)?/)
231     {
232     my $libname=$1;
233     if (exists($self->{DATA}->{FIRSTLIB}))
234     {
235     # Check to see if the library already appears in the LIB
236     if (! grep($libname eq $_, @{$self->{DATA}->{FIRSTLIB}}))
237     {
238     push(@{$self->{DATA}->{FIRSTLIB}}, $libname);
239     }
240     }
241     else
242     {
243     # Create the firstlib array:
244     $self->{DATA}->{FIRSTLIB} = [ $libname ];
245     }
246     }
247     else
248     {
249     push(@{$self->{DATA}->{$tag}},$d);
250     }
251     }
252     }
253     }
254     else
255     {
256     # Now we take the data passed to us and squirrel it away:
257     if (exists($self->{DATA}->{$tag}))
258     {
259     # Only add the item if it doesn't already exist:
260     foreach my $d (@$data)
261     {
262     if (! grep($d eq $_, @{$self->{DATA}->{$tag}}))
263     {
264     push(@{$self->{DATA}->{$tag}},$d);
265     }
266     }
267     }
268     else
269     {
270     $self->{DATA}->{$tag} = [ @$data ];
271     }
272     }
273     }
274    
275     sub storedata()
276     {
277     my $self=shift;
278     my ($tag,$value,$referrer)=@_;
279    
280     if ($tag eq 'PRODUCTSTORE')
281     {
282     # Handle productstore variables. Store in a hash with "SCRAMSTORE_x" as the key
283     # pointing to correct path as it should appear in the Makefiles:
284     foreach my $H (@{$value})
285     {
286     my $storename="";
287     # Probably want the store value to be set to <name/<arch> or <arch>/<name> with
288     # <path> only prepending to this value rather than replacing <name>: FIXME...
289     if ($$H{'type'} eq 'arch')
290     {
291     if ($$H{'swap'} eq 'true')
292     {
293     (exists $$H{'path'}) ? ($storename .= $$H{'path'}."/".$ENV{SCRAM_ARCH})
294     : ($storename .= $$H{'name'}."/".$ENV{SCRAM_ARCH});
295     }
296     else
297     {
298     (exists $$H{'path'}) ? ($storename .= $ENV{SCRAM_ARCH}."/".$$H{'path'})
299     : ($storename .= $ENV{SCRAM_ARCH}."/".$$H{'name'});
300     }
301     }
302     else
303     {
304     (exists $$H{'path'}) ? ($storename .= $$H{'path'})
305     : ($storename .= $$H{'name'});
306     }
307    
308     $self->addstore("SCRAMSTORENAME_".uc($$H{'name'}),$storename);
309     }
310     }
311     elsif ($tag eq 'FLAGS')
312     {
313     while (my ($flag,$flagvalue) = each %{$value})
314     {
315     $self->flags($flag,$flagvalue);
316     }
317     }
318     elsif ($tag eq 'MAKEFILE')
319     {
320     if (! exists($self->{DATA}->{MAKEFILE}))
321     {
322     $self->{DATA}->{MAKEFILE} = [ @$value ];
323     }
324     else
325     {
326     push(@{$self->{DATA}->{MAKEFILE}},@$value);
327     }
328     }
329     else
330     {
331     if (exists($self->{content}->{$referrer}))
332     {
333     if (! exists($self->{content}->{$referrer}->{$tag}))
334     {
335     $self->{content}->{$referrer}->{$tag} = [ @$value ];
336     }
337     else
338     {
339     push(@{$self->{content}->{$referrer}->{$tag}},@$value);
340     }
341     }
342     else
343     {
344     $self->{content}->{$referrer} = {};
345     $self->{content}->{$referrer}->{$tag} = [ @$value ];
346     }
347     }
348     }
349    
350     sub check_export()
351     {
352     my $self=shift;
353     my ($pkdata,$package)=@_;
354    
355     if (! $pkdata->hasexport())
356     {
357     # No export so we return:
358     return(0);
359     }
360     else
361     {
362     my $exported = $pkdata->exported();
363    
364     # We've seen this package: make a note
365     $TRANSIENTCACHE->{$package} = 1;
366    
367     # Collect the exported data:
368     $self->{G}->vertex($package);
369     $self->process_export($exported,$package);
370     return(1);
371     }
372     }
373    
374     sub process_export()
375     {
376     my $self=shift;
377     my ($export,$package)=@_;
378    
379     while (my ($tag,$tagvalue) = each %{$export})
380     {
381     # We check for <use> and pull in this data too:
382     if ($tag eq 'USE')
383     {
384     foreach my $TV (@$tagvalue)
385     {
386     $self->{G}->edge($package, $TV);
387     }
388     # Resolve the list of uses:
389     $self->resolve_use($tagvalue);
390     }
391     elsif ($tag eq 'GROUP')
392     {
393     $self->resolve_groups($tagvalue,$package);
394     }
395     else
396     {
397     $self->storedata($tag,$tagvalue,$package);
398     }
399     }
400     }
401    
402     sub check_remote_export()
403     {
404     my $self=shift;
405     my ($projectname, $pkdata, $package)=@_;
406    
407     if (! $pkdata->hasexport())
408     {
409     # No export so we return:
410     return(0);
411     }
412     else
413     {
414     my $exported = $pkdata->exported();
415    
416     # We've seen this release/remote package: make a note
417     $TRANSIENTCACHE->{$package} = 1;
418    
419     # Collect the exported data:
420     $self->{G}->vertex($package);
421     $self->process_remote_export($projectname, $exported, $package);
422     return(1);
423     }
424     }
425    
426     sub process_remote_export()
427     {
428     my $self=shift;
429     my ($projectname,$export,$package)=@_;
430    
431     while (my ($tag,$tagvalue) = each %{$export})
432     {
433     # We check for s <use> and pull in this data too:
434     if ($tag eq 'USE')
435     {
436     foreach my $TV (@$tagvalue)
437     {
438     $self->{G}->edge($package, $TV);
439     }
440     # Resolve the list of uses:
441     $self->resolve_use($tagvalue);
442     }
443     elsif ($tag eq 'GROUP')
444     {
445     $self->resolve_groups($tagvalue,$package);
446     }
447     elsif ($tag eq 'MAKEFILE' || $tag eq 'FLAGS')
448     {
449     $self->storedata($tag, $tagvalue, $package);
450     }
451     else
452     {
453     my $newltop;
454     my $pjname=uc($projectname);
455     # Replace any occurrence of LOCALTOP in variables with <tool>_LOCALTOP unless
456     # the "project" is the release area, in which case we want RELEASETOP:
457     if ($pjname eq 'RELEASE')
458     {
459     $newltop = 'RELEASETOP';
460     }
461     else
462     {
463     $newltop=$pjname."_BASE";
464     }
465    
466     foreach my $val (@{$tagvalue})
467     {
468     $val =~ s/LOCALTOP/$newltop/g;
469     }
470    
471     # Now we store the modified data for variables:
472     $self->storedata($tag,$tagvalue,$package);
473     }
474     }
475     }
476    
477     sub resolve_arch()
478     {
479     my $self=shift;
480     my ($archdata,$referrer)=@_;
481    
482     while (my ($tagname, $tagvalue) = each %{$archdata})
483     {
484     # Look for group tags:
485     if ($tagname eq 'GROUP')
486     {
487     $self->resolve_groups($tagvalue,$referrer);
488     }
489     # Look for <use> tags:
490     elsif ($tagname eq 'USE')
491     {
492     # Add edges to our dep graph for packages needed
493     # by the referring package:
494     foreach my $TV (@{$tagvalue})
495     {
496     $self->{G}->edge($referrer, $TV);
497     }
498     # resolve the USE:
499     $self->resolve_use($tagvalue);
500     }
501     else
502     {
503     # We have another type of data:
504     $self->storedata($tagname,$tagvalue,$referrer);
505     }
506     }
507     }
508    
509     sub resolve_use()
510     {
511     my $self=shift;
512     my ($data) = @_;
513    
514     foreach my $use (@{$data})
515     {
516     # Look for the data object for the path (locally first):
517     if ($self->check_local_use($use))
518     {
519     print "- Found ",$use," locally:","\n", if ($ENV{SCRAM_DEBUG});
520     # Also store full package path for our build rules:
521     $self->local_package($use);
522     }
523     elsif ($self->check_release_use($use))
524     {
525     print "- Found ",$use," in release area:","\n", if ($ENV{SCRAM_DEBUG});
526     $self->release_package($use);
527     }
528     elsif ($self->check_remote_use($use))
529     {
530     print "- Found ",$use," in a scram-managed project:","\n", if ($ENV{SCRAM_DEBUG});
531     $self->remote_package($use);
532     }
533     # Check to see if it's an external tool. Convert the $use to lower-case first:
534     elsif ($self->{TOOLMGR}->definedtool(lc($use))
535     && (my $td=$self->{TOOLMGR}->checkifsetup(lc($use))))
536     {
537     my $toolname = $td->toolname();
538     my @tooldeps = $td->use();
539    
540     print "- Found ",$use," (an external tool):","\n", if ($ENV{SCRAM_DEBUG});
541     # We have a setup tool ($td is a ToolData object). Store the data:
542     $self->tool($td->toolname(), $td); # Store the tool data too to save retrieving again later;
543     $self->{G}->vertex(lc($toolname));
544    
545     foreach my $TD (@tooldeps)
546     {
547     # Make sure all tool refs are lowercase:
548     $self->{G}->edge(lc($toolname), lc($TD));
549     }
550    
551     # We also resolve the dependencies that this tool has on other tools:
552     $self->resolve_use(\@tooldeps);
553     }
554     else
555     {
556     # Check in the toolbox for this tool. If it doesn't
557     # exist, complain:
558     print "\n";
559     print "WARNING: Unable to find package/tool called ",$use,"\n";
560     print " in current project area (declared at ",$self->{BRANCH},")","\n";
561     return(2);
562     }
563     }
564     }
565    
566     sub resolve_groups()
567     {
568     my $self=shift;
569     my ($inputgroups,$referrer)=@_;
570     my $data={};
571     $data->{USE} = [];
572    
573     # First of all, resolve group requirements in this BuildFile:
574     foreach my $n_group (@{$inputgroups})
575     {
576     # Recursively check for groups and resolve them to lowest common denom (used packages):
577     $self->recursive_group_check($n_group,$data,$referrer);
578     }
579    
580     # Resolve the $data contents:
581     while (my ($tagname, $tagvalue) = each %{$data})
582     {
583     if ($tagname eq 'USE')
584     {
585     # Add edges to our dep graph for packages needed
586     # by the referring package:
587     foreach my $TV (@{$tagvalue})
588     {
589     $self->{G}->edge($referrer, $TV);
590     }
591     # resolve the USE:
592     $self->resolve_use($tagvalue);
593     }
594     else
595     {
596     # We have another type of data in the resolved group:
597     $self->storedata($tagname,$tagvalue,$referrer);
598     }
599     }
600     }
601    
602     sub recursive_group_check()
603     {
604     my $self=shift;
605     my ($groupname,$data,$referrer)=@_;
606     my ($location);
607    
608     # See if we find the group locally:
609     if ($location = $self->{BUILDCACHE}->findgroup($groupname))
610     {
611     print "- Found group ",$groupname," locally:","\n", if ($ENV{SCRAM_DEBUG});
612     # Get the BuildFile object for the BuildFile where the group is defined;
613     my $groupbuildobject = $self->{BUILDCACHE}->buildobject($location);
614     # Get the data contained in the defined group:
615     my %dataingroup = %{$groupbuildobject->dataforgroup($groupname)};
616    
617     # For this group, check to see if there are groups required (i.e. check for any
618     # groups in data of defined group):
619     while (my ($groupdatakey, $groupdatavalue) = each %dataingroup)
620     {
621     # If we have another group, call ourselves again:
622     if ($groupdatakey eq 'GROUP')
623     {
624     # NB: probably should become recursive by invoking resolve_groups() again
625     # since we might have more than one group to resolve:
626     $self->resolve_groups($groupdatavalue,$referrer);
627     }
628     else
629     {
630     if (ref($groupdatavalue) eq 'ARRAY')
631     {
632     push(@{$data->{$groupdatakey}},@{$groupdatavalue});
633     }
634     else
635     {
636     $data->{$groupdatakey} = $groupdatavalue;
637     }
638     }
639     }
640     }
641     # Check in the release area:
642     elsif ($self->groupsearchinrelease($groupname))
643     {
644     my ($releasegroupdataobject) = $self->groupsearchinrelease($groupname);
645     print "- Found group ",$groupname," in release area of current project:","\n", if ($ENV{SCRAM_DEBUG});
646    
647     # Get the data contained in the defined group:
648     my %datainrelgroup = %{$releasegroupdataobject->dataforgroup($groupname)};
649    
650     # For this group, check to see if there are groups required (i.e. check for any
651     # groups in data of defined group):
652     while (my ($relgroupdatakey, $relgroupdatavalue) = each %datainrelgroup)
653     {
654     # If we have another group, call ourselves again:
655     if ($relgroupdatakey eq 'GROUP')
656     {
657     $self->resolve_groups($relgroupdatavalue,$referrer);
658     }
659     else
660     {
661     if (ref($relgroupdatavalue) eq 'ARRAY')
662     {
663     push(@{$data->{$relgroupdatakey}},@{$relgroupdatavalue});
664     }
665     else
666     {
667     $data->{$relgroupdatakey} = $relgroupdatavalue;
668     }
669     }
670     }
671     }
672     # Look in SCRAM-managed projects:
673     elsif ($self->groupsearchinscramprojects($groupname))
674     {
675     my ($remotegroupdataobject) = $self->groupsearchinscramprojects($groupname);
676     print "- Found group ",$groupname," in remote project:","\n", if ($ENV{SCRAM_DEBUG});
677    
678     # Get the data contained in the defined group:
679     my %datainremgroup = %{$remotegroupdataobject->dataforgroup($groupname)};
680    
681     # For this group, check to see if there are groups required (i.e. check for any
682     # groups in data of defined group):
683     while (my ($rgroupdatakey, $rgroupdatavalue) = each %datainremgroup)
684     {
685     # If we have another group, call ourselves again:
686     if ($rgroupdatakey eq 'GROUP')
687     {
688     # NB: probably should become recursive by invoking resolve_groups() again
689     # since we might have more than one group to resolve:
690     $self->resolve_groups($rgroupdatavalue,$referrer);
691     }
692     else
693     {
694     if (ref($rgroupdatavalue) eq 'ARRAY')
695     {
696     push(@{$data->{$rgroupdatakey}},@{$rgroupdatavalue});
697     }
698     else
699     {
700     $data->{$rgroupdatakey} = $rgroupdatavalue;
701     }
702     }
703     }
704     }
705     else
706     {
707     print "WARNING: Group ",$groupname," not defined. Edit BuildFile at ",$self->{BRANCH},"\n";
708     return(0);
709     }
710    
711     return $data;
712     }
713    
714     sub check_local_use()
715     {
716     my $self=shift;
717     my ($dataposition)=@_;
718    
719     # See if this is a local package that has already been seen. We must check that the package is really
720     # local before we return true if it exists in TRANSIENTCACHE:
721     if (exists ($TRANSIENTCACHE->{$dataposition}) && exists($self->{SEEN_LOCAL_PACKAGES}->{$dataposition}))
722     {
723     # Found and data has already been handled so return OK:
724     return(1);
725     }
726    
727     # Look for the data object for the path locally:
728     if (my $pkdata=$self->{BUILDCACHE}->buildobject($dataposition))
729     {
730     # We check to see if this package exported something and parse/store the data
731     # if true:
732     if (! $self->check_export($pkdata,$dataposition))
733     {
734     print "\n";
735     print "WARNING: $dataposition/BuildFile does not export anything:\n";
736     print " **** $dataposition dependency dropped.","\n";
737     print "You must edit the BuildFile at ",$self->{BRANCH}," to add an <export>.\n";
738     print "\n";
739     }
740     # Found so return OK:
741     return(1);
742     }
743     # Otherwise, not found locally:
744     return(0);
745     }
746    
747     sub check_release_use()
748     {
749     my $self=shift;
750     my ($dataposition)=@_;
751    
752     # See if this is a release package that has already been seen. We must check that the package is really
753     # in the release before we return true if it exists in TRANSIENTCACHE:
754     if (exists ($TRANSIENTCACHE->{$dataposition}) && exists($self->{SEEN_RELEASE_PACKAGES}->{$dataposition}))
755     {
756     # Found and data has already been handled so return OK:
757     return(1);
758     }
759    
760     if (my ($sproject,$scramppkgdata)=@{$self->searchinrelease($dataposition)})
761     {
762     if (! $self->check_remote_export($sproject,$scramppkgdata,$dataposition))
763     {
764     print "\n";
765     print "WARNING: $dataposition/BuildFile in release area of current project does not export anything:\n";
766     print "**** $dataposition dependency dropped.","\n";
767     }
768     # Found so return OK:
769     return(1);
770     }
771     # Otherwise, not found in release area:
772     return(0);
773     }
774    
775     sub check_remote_use()
776     {
777     my $self=shift;
778     my ($dataposition)=@_;
779    
780     # See if this is a package from a SCRAM project that has already been seen. We must check that
781     # the package is really in a remote project before we return true if it exists in TRANSIENTCACHE:
782     if (exists ($TRANSIENTCACHE->{$dataposition}) && exists($self->{SEEN_REMOTE_PACKAGES}->{$dataposition}))
783     {
784     # Found and data has already been handled so return OK:
785     return(1);
786     }
787    
788     if (my ($sproject,$scramppkgdata)=@{$self->searchinscramprojects($dataposition)})
789     {
790     if (! $self->check_remote_export($sproject,$scramppkgdata,$dataposition))
791     {
792     print "\n";
793     print "WARNING: $dataposition/BuildFile in scram project \"",$sproject,"\" does not export anything:\n";
794     print "**** $dataposition dependency dropped.","\n";
795     }
796     # Found so return OK:
797     return(1);
798     }
799     return(0);
800     }
801    
802     sub searchinscramprojects()
803     {
804     my $self=shift;
805     my ($dataposition)=@_;
806    
807     foreach my $pobj (keys %{$self->{SPROJECTS}})
808     {
809     if ($self->{SPROJECTS}->{$pobj}->buildobject($dataposition))
810     {
811     # Add the dependency on this tool (even though it's a scram project, we need the
812     # other settings that the tool provides):
813     $self->{G}->vertex($pobj);
814     $self->tool($pobj,'SCRAM');
815    
816     # Return the data object (tool name, data object):
817     return [$pobj,$self->{SPROJECTS}->{$pobj}->buildobject($dataposition)];
818     }
819     }
820    
821     # If we got here, there were no matches:
822     return (0);
823     }
824    
825     sub groupsearchinscramprojects()
826     {
827     my $self=shift;
828     my ($groupname)=@_;
829    
830     foreach my $pobj (keys %{$self->{SPROJECTS}})
831     {
832     if (my $grouplocation = $self->{SPROJECTS}->{$pobj}->findgroup($groupname))
833     {
834     return $self->{SPROJECTS}->{$pobj}->buildobject($grouplocation);
835     }
836     }
837    
838     # If we got here, there were no matches:
839     return (0);
840     }
841    
842     sub searchinrelease()
843     {
844     my $self=shift;
845     my ($dataposition)=@_;
846    
847     if (exists ($self->{SPROJECTS}->{RELEASE}) &&
848     $self->{SPROJECTS}->{RELEASE}->buildobject($dataposition))
849     {
850     # Return the data object (tool name, data object):
851     return ['RELEASE',$self->{SPROJECTS}->{RELEASE}->buildobject($dataposition)];
852     }
853    
854     # If we got here, there were no matches:
855     return (0);
856     }
857    
858     sub groupsearchinrelease()
859     {
860     my $self=shift;
861     my ($groupname)=@_;
862    
863     if (exists($self->{SPROJECTS}->{RELEASE}))
864     {
865     if (my $grouplocation = $self->{SPROJECTS}->{RELEASE}->findgroup($groupname))
866     {
867     return $self->{SPROJECTS}->{RELEASE}->buildobject($grouplocation);
868     }
869     }
870    
871     # If we got here, there were no matches:
872     return (0);
873     }
874    
875     sub local_package()
876     {
877     my $self=shift;
878     my ($package)=@_;
879    
880     if (exists ($self->{SEEN_LOCAL_PACKAGES}->{$package}))
881     {
882     $self->{SEEN_LOCAL_PACKAGES}->{$package}++;
883     }
884     else
885     {
886     $self->{SEEN_LOCAL_PACKAGES}->{$package} = 1;
887     }
888     }
889    
890     sub release_package()
891     {
892     my $self=shift;
893     my ($package)=@_;
894    
895     if (exists ($self->{SEEN_RELEASE_PACKAGES}->{$package}))
896     {
897     $self->{SEEN_RELEASE_PACKAGES}->{$package}++;
898     }
899     else
900     {
901     $self->{SEEN_RELEASE_PACKAGES}->{$package} = 1;
902     }
903     }
904    
905     sub remote_package()
906     {
907     my $self=shift;
908     my ($package)=@_;
909    
910     if (exists ($self->{SEEN_REMOTE_PACKAGES}->{$package}))
911     {
912     $self->{SEEN_REMOTE_PACKAGES}->{$package}++;
913     }
914     else
915     {
916     $self->{SEEN_REMOTE_PACKAGES}->{$package} = 1;
917     }
918     }
919    
920     sub tool()
921     {
922     my $self=shift;
923     my ($tool,$td) = @_;
924    
925     if (! exists($self->{SEEN_TOOLS}->{$tool}))
926     {
927     $self->{SEEN_TOOLS}->{$tool} = $td;
928     }
929     }
930    
931     sub local_package_deps()
932     {
933     my $self=shift;
934     my $orderedpackages=[];
935     # Check the BUILD_ORDER array and store all local
936     # packages found:
937     foreach my $LP (@{$self->{BUILD_ORDER}})
938     {
939     if (exists($self->{SEEN_LOCAL_PACKAGES}->{$LP}))
940     {
941     push(@$orderedpackages, $LP);
942     }
943     }
944    
945     return $orderedpackages;
946     }
947    
948     ####### Now some interface routines ########
949     sub addstore()
950     {
951     my $self=shift;
952     my ($name,$value)=@_;
953     # Add a new SCRAMSTORE. First we check to see if there
954     # is already a store with the same name. If there is, we
955     # do nothing since the buildfiles are parsed in the order
956     # LOCAL->PARENT->PROJECT so the first one to be set will be
957     # obtained locally. We want this behaviour so we can override
958     # the main product storage locations locally:
959     if (!exists($self->{SCRAMSTORE}->{$name}))
960     {
961     $self->{SCRAMSTORE}->{$name} = $value;
962     }
963     else
964     {
965     print "INFO: Product storage area \"",$self->{SCRAMSTORE}->{$name},"\" has been redefined locally.","\n"
966     if ($ENV{SCRAM_DEBUG});
967     }
968     }
969    
970     sub scramstore()
971     {
972     my $self=shift;
973     my ($name) = @_;
974     (exists $self->{SCRAMSTORE}->{"SCRAMSTORENAME_".$name}) ?
975     return $self->{SCRAMSTORE}->{"SCRAMSTORENAME_".$name}
976     : return "";
977     }
978    
979     sub allscramstores()
980     {
981     my $self=shift;
982     # Return a hash of scram stores:
983     return $self->{SCRAMSTORE};
984     }
985    
986     sub flags()
987     {
988     my $self=shift;
989     my ($flag,$flagvalue) = @_;
990    
991     if ($flag && $flagvalue)
992     {
993     # If FLAGS already exist, append:
994     if (exists ($self->{DATA}->{FLAGS}->{$flag}))
995     {
996     # Add each flag ONLY if it doesn't already exist:
997     foreach my $F (@$flagvalue)
998     {
999     push(@{$self->{DATA}->{FLAGS}->{$flag}},$F),
1000     if (! grep($F eq $_, @{$self->{DATA}->{FLAGS}->{$flag}}));
1001     }
1002     }
1003     else
1004     {
1005     # Create a new array of flags:
1006     $self->{DATA}->{FLAGS}->{$flag} = [ @$flagvalue ];
1007     }
1008     }
1009     elsif ($flag && $self->{DATA}->{FLAGS}->{$flag}->[0] ne '')
1010     {
1011     return @{$self->{DATA}->{FLAGS}->{$flag}};
1012     }
1013     else
1014     {
1015     return "";
1016     }
1017     }
1018    
1019     sub allflags()
1020     {
1021     my $self=shift;
1022     my $flags={};
1023    
1024     # Return a hash containing FLAGNAME, FLAGSTRING pairs:
1025     while (my ($flagname,$flagvalue) = each %{$self->{DATA}->{FLAGS}})
1026     {
1027     $flags->{$flagname} = join(" ",@{$flagvalue});
1028     }
1029    
1030     return $flags;
1031     }
1032    
1033     sub variables()
1034     {
1035     my $self=shift;
1036     # Return a hash of variables:
1037     return $self->{DATA}->{VARIABLES};
1038     }
1039    
1040     sub data()
1041     {
1042     my $self=shift;
1043     my ($tag)=@_;
1044     my $sep;
1045     my $datastring="";
1046    
1047     if (exists($self->{DATA}->{$tag}))
1048     {
1049     ($tag eq 'MAKEFILE') ? $sep="\n" : $sep=" ";
1050     # Special treatment for LIB to handle libs that must
1051     # appear first in link list:
1052     if ($tag eq 'LIB')
1053     {
1054     if (exists($self->{DATA}->{FIRSTLIB}))
1055     {
1056     $datastring .= join($sep, @{$self->{DATA}->{FIRSTLIB}})." ";
1057     $datastring .= join($sep, @{$self->{DATA}->{LIB}});
1058     }
1059     else
1060     {
1061     $datastring .= join($sep,@{$self->{DATA}->{LIB}});
1062     }
1063     }
1064     else
1065     {
1066     # All other tags just join:
1067     $datastring .= join($sep, @{$self->{DATA}->{$tag}});
1068     }
1069    
1070     # return the data string:
1071     return $datastring;
1072     }
1073    
1074     return "";
1075     }
1076    
1077     sub copy()
1078     {
1079     my $self=shift;
1080     my ($localg)=@_;
1081     my $copy;
1082     # We copy the DataCollector. We only clone the grapher if
1083     # local graphing is being used, otherwise we leave the
1084     # original graph present:
1085     if ($localg) # Working at package-level
1086     {
1087     # Create a copy of our graph:
1088     my $gcopy = $self->{G}->copy();
1089     # Create a new DataCollector object, initialised with same settings as we have
1090     # in our current object:
1091     $copy = ref($self)->new($self->{BUILDCACHE},
1092     $self->{TOOLMGR},
1093     $self->{BRANCH},
1094     $self->{SPROJECTS},
1095     $self->{SPROJECTBASES},
1096     $gcopy); # The copy of the grapher
1097     }
1098     elsif ($localg == 0)
1099     {
1100     # Create a new DataCollector object, initialised with same settings as we have
1101     # in our current object:
1102     $copy = ref($self)->new($self->{BUILDCACHE},
1103     $self->{TOOLMGR},
1104     $self->{BRANCH},
1105     $self->{SPROJECTS},
1106     $self->{SPROJECTBASES},
1107     $self->{G}); # The GLOBAL grapher
1108     }
1109     else
1110     {
1111     # Unknown value:
1112     return undef;
1113     }
1114    
1115     # Copy other counters/tracking vars:
1116     $copy->{SEEN_LOCAL_PACKAGES} = { %{$self->{SEEN_LOCAL_PACKAGES}} };
1117     $copy->{SEEN_RELEASE_PACKAGES} = { %{$self->{SEEN_RELEASE_PACKAGES}} };
1118     $copy->{SEEN_REMOTE_PACKAGES} = { %{$self->{SEEN_REMOTE_PACKAGES}} };
1119     $copy->{SEEN_TOOLS} = { %{$self->{SEEN_TOOLS}} };
1120    
1121     # Copy the "content":
1122     use Data::Dumper;
1123     $Data::Dumper::Purity = 1;
1124     $Data::Dumper::Terse = 1;
1125     my $newcontent=eval(Dumper($self->{content}));
1126    
1127     if (@!)
1128     {
1129     print "SCRAM error [DataCollector]: problems copying content...","\n";
1130     }
1131    
1132     # Now copy the data:
1133     my $newdata=eval(Dumper($self->{DATA}));
1134    
1135     if (@!)
1136     {
1137     print "SCRAM error [DataCollector]: problems copying DATA content...","\n";
1138     }
1139    
1140     # Store the new content:
1141     $copy->{content} = $newcontent;
1142     # Store the new data content:
1143     $copy->{DATA} = $newdata;
1144     # Bless the object:
1145     bless $copy, ref($self);
1146     # Return the copy object:
1147     return $copy;
1148     }
1149    
1150     sub localgraph()
1151     {
1152     my $self=shift;
1153     return $self->{G};
1154     }
1155    
1156     sub attribute_data()
1157     {
1158     my $self=shift;
1159     # Prepare some data which says which packages are local,
1160     # release, remote or tools. This is needed for setting
1161     # graph attributes in SCRAMGrapher:
1162     my $attrdata =
1163     {
1164     LOCAL => [ keys %{ $self->{SEEN_LOCAL_PACKAGES}} ],
1165     RELEASE => [ keys %{ $self->{SEEN_RELEASE_PACKAGES}} ],
1166     REMOTE => [ keys %{ $self->{SEEN_REMOTE_PACKAGES}} ],
1167     TOOLS => [ keys %{ $self->{SEEN_TOOLS}} ]
1168     };
1169    
1170     return $attrdata;
1171     }
1172    
1173     sub clean4storage()
1174     {
1175     my $self=shift;
1176     # Delete all keys except those in KEEP:
1177     my @KEEP = qw( DATA BUILD_ORDER SEEN_LOCAL_PACKAGES SCRAMSTORE );
1178    
1179     foreach my $key (keys %$self)
1180     {
1181     # If this key isn't listed in KEEP, delete it:
1182     if (! grep($key eq $_, @KEEP))
1183     {
1184     delete $self->{$key};
1185     }
1186     }
1187     }
1188    
1189     1;