ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/cvsroot/COMP/SCRAM/src/BuildSystem/DataCollector.pm
Revision: 1.3
Committed: Wed Mar 9 19:28:19 2005 UTC (20 years, 2 months ago) by sashby
Content type: text/plain
Branch: MAIN
Changes since 1.2: +9 -1 lines
Log Message:
Started adding support for skipping builds in some dirs.

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 sashby 1.3 # Revision: $Id: DataCollector.pm,v 1.2 2004/12/10 13:41:37 sashby Exp $
8 sashby 1.2 #
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 sashby 1.3
562     if ($ENV{SCRAM_DEBUG}) # Print more details if debug mode on
563     {
564     print "It might be that ",$use," is a relic of SCRAM V0_x series BuildFile syntax.","\n";
565     print "If so, ",$use," refers to a SubSystem: the corresponding <use name=",$use,">\n";
566     print "must be removed to get rid of this message.","\n";
567     }
568    
569 sashby 1.2 return(2);
570     }
571     }
572     }
573    
574     sub resolve_groups()
575     {
576     my $self=shift;
577     my ($inputgroups,$referrer)=@_;
578     my $data={};
579     $data->{USE} = [];
580    
581     # First of all, resolve group requirements in this BuildFile:
582     foreach my $n_group (@{$inputgroups})
583     {
584     # Recursively check for groups and resolve them to lowest common denom (used packages):
585     $self->recursive_group_check($n_group,$data,$referrer);
586     }
587    
588     # Resolve the $data contents:
589     while (my ($tagname, $tagvalue) = each %{$data})
590     {
591     if ($tagname eq 'USE')
592     {
593     # Add edges to our dep graph for packages needed
594     # by the referring package:
595     foreach my $TV (@{$tagvalue})
596     {
597     $self->{G}->edge($referrer, $TV);
598     }
599     # resolve the USE:
600     $self->resolve_use($tagvalue);
601     }
602     else
603     {
604     # We have another type of data in the resolved group:
605     $self->storedata($tagname,$tagvalue,$referrer);
606     }
607     }
608     }
609    
610     sub recursive_group_check()
611     {
612     my $self=shift;
613     my ($groupname,$data,$referrer)=@_;
614     my ($location);
615    
616     # See if we find the group locally:
617     if ($location = $self->{BUILDCACHE}->findgroup($groupname))
618     {
619     print "- Found group ",$groupname," locally:","\n", if ($ENV{SCRAM_DEBUG});
620     # Get the BuildFile object for the BuildFile where the group is defined;
621     my $groupbuildobject = $self->{BUILDCACHE}->buildobject($location);
622     # Get the data contained in the defined group:
623     my %dataingroup = %{$groupbuildobject->dataforgroup($groupname)};
624    
625     # For this group, check to see if there are groups required (i.e. check for any
626     # groups in data of defined group):
627     while (my ($groupdatakey, $groupdatavalue) = each %dataingroup)
628     {
629     # If we have another group, call ourselves again:
630     if ($groupdatakey eq 'GROUP')
631     {
632     # NB: probably should become recursive by invoking resolve_groups() again
633     # since we might have more than one group to resolve:
634     $self->resolve_groups($groupdatavalue,$referrer);
635     }
636     else
637     {
638     if (ref($groupdatavalue) eq 'ARRAY')
639     {
640     push(@{$data->{$groupdatakey}},@{$groupdatavalue});
641     }
642     else
643     {
644     $data->{$groupdatakey} = $groupdatavalue;
645     }
646     }
647     }
648     }
649     # Check in the release area:
650     elsif ($self->groupsearchinrelease($groupname))
651     {
652     my ($releasegroupdataobject) = $self->groupsearchinrelease($groupname);
653     print "- Found group ",$groupname," in release area of current project:","\n", if ($ENV{SCRAM_DEBUG});
654    
655     # Get the data contained in the defined group:
656     my %datainrelgroup = %{$releasegroupdataobject->dataforgroup($groupname)};
657    
658     # For this group, check to see if there are groups required (i.e. check for any
659     # groups in data of defined group):
660     while (my ($relgroupdatakey, $relgroupdatavalue) = each %datainrelgroup)
661     {
662     # If we have another group, call ourselves again:
663     if ($relgroupdatakey eq 'GROUP')
664     {
665     $self->resolve_groups($relgroupdatavalue,$referrer);
666     }
667     else
668     {
669     if (ref($relgroupdatavalue) eq 'ARRAY')
670     {
671     push(@{$data->{$relgroupdatakey}},@{$relgroupdatavalue});
672     }
673     else
674     {
675     $data->{$relgroupdatakey} = $relgroupdatavalue;
676     }
677     }
678     }
679     }
680     # Look in SCRAM-managed projects:
681     elsif ($self->groupsearchinscramprojects($groupname))
682     {
683     my ($remotegroupdataobject) = $self->groupsearchinscramprojects($groupname);
684     print "- Found group ",$groupname," in remote project:","\n", if ($ENV{SCRAM_DEBUG});
685    
686     # Get the data contained in the defined group:
687     my %datainremgroup = %{$remotegroupdataobject->dataforgroup($groupname)};
688    
689     # For this group, check to see if there are groups required (i.e. check for any
690     # groups in data of defined group):
691     while (my ($rgroupdatakey, $rgroupdatavalue) = each %datainremgroup)
692     {
693     # If we have another group, call ourselves again:
694     if ($rgroupdatakey eq 'GROUP')
695     {
696     # NB: probably should become recursive by invoking resolve_groups() again
697     # since we might have more than one group to resolve:
698     $self->resolve_groups($rgroupdatavalue,$referrer);
699     }
700     else
701     {
702     if (ref($rgroupdatavalue) eq 'ARRAY')
703     {
704     push(@{$data->{$rgroupdatakey}},@{$rgroupdatavalue});
705     }
706     else
707     {
708     $data->{$rgroupdatakey} = $rgroupdatavalue;
709     }
710     }
711     }
712     }
713     else
714     {
715     print "WARNING: Group ",$groupname," not defined. Edit BuildFile at ",$self->{BRANCH},"\n";
716     return(0);
717     }
718    
719     return $data;
720     }
721    
722     sub check_local_use()
723     {
724     my $self=shift;
725     my ($dataposition)=@_;
726    
727     # See if this is a local package that has already been seen. We must check that the package is really
728     # local before we return true if it exists in TRANSIENTCACHE:
729     if (exists ($TRANSIENTCACHE->{$dataposition}) && exists($self->{SEEN_LOCAL_PACKAGES}->{$dataposition}))
730     {
731     # Found and data has already been handled so return OK:
732     return(1);
733     }
734    
735     # Look for the data object for the path locally:
736     if (my $pkdata=$self->{BUILDCACHE}->buildobject($dataposition))
737     {
738     # We check to see if this package exported something and parse/store the data
739     # if true:
740     if (! $self->check_export($pkdata,$dataposition))
741     {
742     print "\n";
743     print "WARNING: $dataposition/BuildFile does not export anything:\n";
744     print " **** $dataposition dependency dropped.","\n";
745     print "You must edit the BuildFile at ",$self->{BRANCH}," to add an <export>.\n";
746     print "\n";
747     }
748     # Found so return OK:
749     return(1);
750     }
751     # Otherwise, not found locally:
752     return(0);
753     }
754    
755     sub check_release_use()
756     {
757     my $self=shift;
758     my ($dataposition)=@_;
759    
760     # See if this is a release package that has already been seen. We must check that the package is really
761     # in the release before we return true if it exists in TRANSIENTCACHE:
762     if (exists ($TRANSIENTCACHE->{$dataposition}) && exists($self->{SEEN_RELEASE_PACKAGES}->{$dataposition}))
763     {
764     # Found and data has already been handled so return OK:
765     return(1);
766     }
767    
768     if (my ($sproject,$scramppkgdata)=@{$self->searchinrelease($dataposition)})
769     {
770     if (! $self->check_remote_export($sproject,$scramppkgdata,$dataposition))
771     {
772     print "\n";
773     print "WARNING: $dataposition/BuildFile in release area of current project does not export anything:\n";
774     print "**** $dataposition dependency dropped.","\n";
775     }
776     # Found so return OK:
777     return(1);
778     }
779     # Otherwise, not found in release area:
780     return(0);
781     }
782    
783     sub check_remote_use()
784     {
785     my $self=shift;
786     my ($dataposition)=@_;
787    
788     # See if this is a package from a SCRAM project that has already been seen. We must check that
789     # the package is really in a remote project before we return true if it exists in TRANSIENTCACHE:
790     if (exists ($TRANSIENTCACHE->{$dataposition}) && exists($self->{SEEN_REMOTE_PACKAGES}->{$dataposition}))
791     {
792     # Found and data has already been handled so return OK:
793     return(1);
794     }
795    
796     if (my ($sproject,$scramppkgdata)=@{$self->searchinscramprojects($dataposition)})
797     {
798     if (! $self->check_remote_export($sproject,$scramppkgdata,$dataposition))
799     {
800     print "\n";
801     print "WARNING: $dataposition/BuildFile in scram project \"",$sproject,"\" does not export anything:\n";
802     print "**** $dataposition dependency dropped.","\n";
803     }
804     # Found so return OK:
805     return(1);
806     }
807     return(0);
808     }
809    
810     sub searchinscramprojects()
811     {
812     my $self=shift;
813     my ($dataposition)=@_;
814    
815     foreach my $pobj (keys %{$self->{SPROJECTS}})
816     {
817     if ($self->{SPROJECTS}->{$pobj}->buildobject($dataposition))
818     {
819     # Add the dependency on this tool (even though it's a scram project, we need the
820     # other settings that the tool provides):
821     $self->{G}->vertex($pobj);
822     $self->tool($pobj,'SCRAM');
823    
824     # Return the data object (tool name, data object):
825     return [$pobj,$self->{SPROJECTS}->{$pobj}->buildobject($dataposition)];
826     }
827     }
828    
829     # If we got here, there were no matches:
830     return (0);
831     }
832    
833     sub groupsearchinscramprojects()
834     {
835     my $self=shift;
836     my ($groupname)=@_;
837    
838     foreach my $pobj (keys %{$self->{SPROJECTS}})
839     {
840     if (my $grouplocation = $self->{SPROJECTS}->{$pobj}->findgroup($groupname))
841     {
842     return $self->{SPROJECTS}->{$pobj}->buildobject($grouplocation);
843     }
844     }
845    
846     # If we got here, there were no matches:
847     return (0);
848     }
849    
850     sub searchinrelease()
851     {
852     my $self=shift;
853     my ($dataposition)=@_;
854    
855     if (exists ($self->{SPROJECTS}->{RELEASE}) &&
856     $self->{SPROJECTS}->{RELEASE}->buildobject($dataposition))
857     {
858     # Return the data object (tool name, data object):
859     return ['RELEASE',$self->{SPROJECTS}->{RELEASE}->buildobject($dataposition)];
860     }
861    
862     # If we got here, there were no matches:
863     return (0);
864     }
865    
866     sub groupsearchinrelease()
867     {
868     my $self=shift;
869     my ($groupname)=@_;
870    
871     if (exists($self->{SPROJECTS}->{RELEASE}))
872     {
873     if (my $grouplocation = $self->{SPROJECTS}->{RELEASE}->findgroup($groupname))
874     {
875     return $self->{SPROJECTS}->{RELEASE}->buildobject($grouplocation);
876     }
877     }
878    
879     # If we got here, there were no matches:
880     return (0);
881     }
882    
883     sub local_package()
884     {
885     my $self=shift;
886     my ($package)=@_;
887    
888     if (exists ($self->{SEEN_LOCAL_PACKAGES}->{$package}))
889     {
890     $self->{SEEN_LOCAL_PACKAGES}->{$package}++;
891     }
892     else
893     {
894     $self->{SEEN_LOCAL_PACKAGES}->{$package} = 1;
895     }
896     }
897    
898     sub release_package()
899     {
900     my $self=shift;
901     my ($package)=@_;
902    
903     if (exists ($self->{SEEN_RELEASE_PACKAGES}->{$package}))
904     {
905     $self->{SEEN_RELEASE_PACKAGES}->{$package}++;
906     }
907     else
908     {
909     $self->{SEEN_RELEASE_PACKAGES}->{$package} = 1;
910     }
911     }
912    
913     sub remote_package()
914     {
915     my $self=shift;
916     my ($package)=@_;
917    
918     if (exists ($self->{SEEN_REMOTE_PACKAGES}->{$package}))
919     {
920     $self->{SEEN_REMOTE_PACKAGES}->{$package}++;
921     }
922     else
923     {
924     $self->{SEEN_REMOTE_PACKAGES}->{$package} = 1;
925     }
926     }
927    
928     sub tool()
929     {
930     my $self=shift;
931     my ($tool,$td) = @_;
932    
933     if (! exists($self->{SEEN_TOOLS}->{$tool}))
934     {
935     $self->{SEEN_TOOLS}->{$tool} = $td;
936     }
937     }
938    
939     sub local_package_deps()
940     {
941     my $self=shift;
942     my $orderedpackages=[];
943     # Check the BUILD_ORDER array and store all local
944     # packages found:
945     foreach my $LP (@{$self->{BUILD_ORDER}})
946     {
947     if (exists($self->{SEEN_LOCAL_PACKAGES}->{$LP}))
948     {
949     push(@$orderedpackages, $LP);
950     }
951     }
952    
953     return $orderedpackages;
954     }
955    
956     ####### Now some interface routines ########
957     sub addstore()
958     {
959     my $self=shift;
960     my ($name,$value)=@_;
961     # Add a new SCRAMSTORE. First we check to see if there
962     # is already a store with the same name. If there is, we
963     # do nothing since the buildfiles are parsed in the order
964     # LOCAL->PARENT->PROJECT so the first one to be set will be
965     # obtained locally. We want this behaviour so we can override
966     # the main product storage locations locally:
967     if (!exists($self->{SCRAMSTORE}->{$name}))
968     {
969     $self->{SCRAMSTORE}->{$name} = $value;
970     }
971     else
972     {
973     print "INFO: Product storage area \"",$self->{SCRAMSTORE}->{$name},"\" has been redefined locally.","\n"
974     if ($ENV{SCRAM_DEBUG});
975     }
976     }
977    
978     sub scramstore()
979     {
980     my $self=shift;
981     my ($name) = @_;
982     (exists $self->{SCRAMSTORE}->{"SCRAMSTORENAME_".$name}) ?
983     return $self->{SCRAMSTORE}->{"SCRAMSTORENAME_".$name}
984     : return "";
985     }
986    
987     sub allscramstores()
988     {
989     my $self=shift;
990     # Return a hash of scram stores:
991     return $self->{SCRAMSTORE};
992     }
993    
994     sub flags()
995     {
996     my $self=shift;
997     my ($flag,$flagvalue) = @_;
998    
999     if ($flag && $flagvalue)
1000     {
1001     # If FLAGS already exist, append:
1002     if (exists ($self->{DATA}->{FLAGS}->{$flag}))
1003     {
1004     # Add each flag ONLY if it doesn't already exist:
1005     foreach my $F (@$flagvalue)
1006     {
1007     push(@{$self->{DATA}->{FLAGS}->{$flag}},$F),
1008     if (! grep($F eq $_, @{$self->{DATA}->{FLAGS}->{$flag}}));
1009     }
1010     }
1011     else
1012     {
1013     # Create a new array of flags:
1014     $self->{DATA}->{FLAGS}->{$flag} = [ @$flagvalue ];
1015     }
1016     }
1017     elsif ($flag && $self->{DATA}->{FLAGS}->{$flag}->[0] ne '')
1018     {
1019     return @{$self->{DATA}->{FLAGS}->{$flag}};
1020     }
1021     else
1022     {
1023     return "";
1024     }
1025     }
1026    
1027     sub allflags()
1028     {
1029     my $self=shift;
1030     my $flags={};
1031    
1032     # Return a hash containing FLAGNAME, FLAGSTRING pairs:
1033     while (my ($flagname,$flagvalue) = each %{$self->{DATA}->{FLAGS}})
1034     {
1035     $flags->{$flagname} = join(" ",@{$flagvalue});
1036     }
1037    
1038     return $flags;
1039     }
1040    
1041     sub variables()
1042     {
1043     my $self=shift;
1044     # Return a hash of variables:
1045     return $self->{DATA}->{VARIABLES};
1046     }
1047    
1048     sub data()
1049     {
1050     my $self=shift;
1051     my ($tag)=@_;
1052     my $sep;
1053     my $datastring="";
1054    
1055     if (exists($self->{DATA}->{$tag}))
1056     {
1057     ($tag eq 'MAKEFILE') ? $sep="\n" : $sep=" ";
1058     # Special treatment for LIB to handle libs that must
1059     # appear first in link list:
1060     if ($tag eq 'LIB')
1061     {
1062     if (exists($self->{DATA}->{FIRSTLIB}))
1063     {
1064     $datastring .= join($sep, @{$self->{DATA}->{FIRSTLIB}})." ";
1065     $datastring .= join($sep, @{$self->{DATA}->{LIB}});
1066     }
1067     else
1068     {
1069     $datastring .= join($sep,@{$self->{DATA}->{LIB}});
1070     }
1071     }
1072     else
1073     {
1074     # All other tags just join:
1075     $datastring .= join($sep, @{$self->{DATA}->{$tag}});
1076     }
1077    
1078     # return the data string:
1079     return $datastring;
1080     }
1081    
1082     return "";
1083     }
1084    
1085     sub copy()
1086     {
1087     my $self=shift;
1088     my ($localg)=@_;
1089     my $copy;
1090     # We copy the DataCollector. We only clone the grapher if
1091     # local graphing is being used, otherwise we leave the
1092     # original graph present:
1093     if ($localg) # Working at package-level
1094     {
1095     # Create a copy of our graph:
1096     my $gcopy = $self->{G}->copy();
1097     # Create a new DataCollector object, initialised with same settings as we have
1098     # in our current object:
1099     $copy = ref($self)->new($self->{BUILDCACHE},
1100     $self->{TOOLMGR},
1101     $self->{BRANCH},
1102     $self->{SPROJECTS},
1103     $self->{SPROJECTBASES},
1104     $gcopy); # The copy of the grapher
1105     }
1106     elsif ($localg == 0)
1107     {
1108     # Create a new DataCollector object, initialised with same settings as we have
1109     # in our current object:
1110     $copy = ref($self)->new($self->{BUILDCACHE},
1111     $self->{TOOLMGR},
1112     $self->{BRANCH},
1113     $self->{SPROJECTS},
1114     $self->{SPROJECTBASES},
1115     $self->{G}); # The GLOBAL grapher
1116     }
1117     else
1118     {
1119     # Unknown value:
1120     return undef;
1121     }
1122    
1123     # Copy other counters/tracking vars:
1124     $copy->{SEEN_LOCAL_PACKAGES} = { %{$self->{SEEN_LOCAL_PACKAGES}} };
1125     $copy->{SEEN_RELEASE_PACKAGES} = { %{$self->{SEEN_RELEASE_PACKAGES}} };
1126     $copy->{SEEN_REMOTE_PACKAGES} = { %{$self->{SEEN_REMOTE_PACKAGES}} };
1127     $copy->{SEEN_TOOLS} = { %{$self->{SEEN_TOOLS}} };
1128    
1129     # Copy the "content":
1130     use Data::Dumper;
1131     $Data::Dumper::Purity = 1;
1132     $Data::Dumper::Terse = 1;
1133     my $newcontent=eval(Dumper($self->{content}));
1134    
1135     if (@!)
1136     {
1137     print "SCRAM error [DataCollector]: problems copying content...","\n";
1138     }
1139    
1140     # Now copy the data:
1141     my $newdata=eval(Dumper($self->{DATA}));
1142    
1143     if (@!)
1144     {
1145     print "SCRAM error [DataCollector]: problems copying DATA content...","\n";
1146     }
1147    
1148     # Store the new content:
1149     $copy->{content} = $newcontent;
1150     # Store the new data content:
1151     $copy->{DATA} = $newdata;
1152     # Bless the object:
1153     bless $copy, ref($self);
1154     # Return the copy object:
1155     return $copy;
1156     }
1157    
1158     sub localgraph()
1159     {
1160     my $self=shift;
1161     return $self->{G};
1162     }
1163    
1164     sub attribute_data()
1165     {
1166     my $self=shift;
1167     # Prepare some data which says which packages are local,
1168     # release, remote or tools. This is needed for setting
1169     # graph attributes in SCRAMGrapher:
1170     my $attrdata =
1171     {
1172     LOCAL => [ keys %{ $self->{SEEN_LOCAL_PACKAGES}} ],
1173     RELEASE => [ keys %{ $self->{SEEN_RELEASE_PACKAGES}} ],
1174     REMOTE => [ keys %{ $self->{SEEN_REMOTE_PACKAGES}} ],
1175     TOOLS => [ keys %{ $self->{SEEN_TOOLS}} ]
1176     };
1177    
1178     return $attrdata;
1179     }
1180    
1181     sub clean4storage()
1182     {
1183     my $self=shift;
1184     # Delete all keys except those in KEEP:
1185     my @KEEP = qw( DATA BUILD_ORDER SEEN_LOCAL_PACKAGES SCRAMSTORE );
1186    
1187     foreach my $key (keys %$self)
1188     {
1189     # If this key isn't listed in KEEP, delete it:
1190     if (! grep($key eq $_, @KEEP))
1191     {
1192     delete $self->{$key};
1193     }
1194     }
1195     }
1196    
1197     1;