ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/cvsroot/COMP/SCRAM/src/BuildSystem/BuildDataStorage.pm
Revision: 1.15
Committed: Tue Nov 14 17:43:14 2006 UTC (18 years, 5 months ago) by sashby
Content type: text/plain
Branch: MAIN
CVS Tags: before110xmlBRmerge
Changes since 1.14: +2 -1 lines
Log Message:
Fix to make template dir path configurable

File Contents

# Content
1 #____________________________________________________________________
2 # File: BuildDataStorage.pm
3 #____________________________________________________________________
4 #
5 # Author: Shaun Ashby <Shaun.Ashby@cern.ch>
6 # Update: 2004-06-22 15:16:01+0200
7 # Revision: $Id: BuildDataStorage.pm,v 1.14 2006/09/11 14:53:39 sashby Exp $
8 #
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 $self->{SKIPPEDDIRS} = {}; # Global skipped dirs
47
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 my ($files, $datapath)=@_;
185 my $bfbranch;
186 my $buildfiles;
187 # 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 if (exists($ENV{SCRAM_XMLBUILDFILES}) && ($ENV{SCRAM_XMLBUILDFILES}))
193 {
194 print "Reading ".$files->[0].".xml","\n";
195 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
206 # Scan all buildfiles in a branch:
207 $bfbranch->parsebranchfiles($buildfiles);
208
209 # Store:
210 $self->storebranchmetadata($datapath,$bfbranch);
211
212 return $self;
213 }
214
215 sub procrecursive()
216 {
217 my $self=shift;
218 my ($dir)=@_;
219 my $datacollector;
220
221 # 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 # 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 my $baseclasses = [ qw( DOMAIN SUBSYSTEM PACKAGE ) ];
236
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
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 # 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 my $baseclasses = [ qw( DOMAIN SUBSYSTEM PACKAGE ) ];
313
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
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 # Data for current dir:
376 my $treedata = $self->buildtreeitem($startdir);
377 # Run the engine:
378 $self->run_engine($treedata);
379
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
474 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 # as BuildFile so all BuildFile methods can be used on the Product object:
572 $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 sub populate()
607 {
608 my $self=shift;
609 my ($paths,$filecache,$toolmanager)=@_;
610 my $datapath;
611 my $buildfile;
612 $|=1; # Flush
613
614 # The tool manager:
615 $self->{TOOLMANAGER} = $toolmanager;
616
617 # If there are some paths to iterate over, get scram projects from
618 # toolbox. Each project cache is loaded at this point too.
619 # Note that this could be done later, when running processtree() which
620 # is when access to the project caches is really needed (actually when
621 # running datacollector::processbuildfile):
622 $self->scramprojects();
623
624 # Check that there's a global config. Exit if not:
625 $self->check_global_config();
626
627 # Loop over all paths. Apply a sort so that src (shortest path) is first (FIXME!):
628 foreach my $path (sort(@$paths))
629 {
630 # Ignore config content here:
631 next if ($path !~ m|^\Q$ENV{SCRAM_SOURCEDIR}\L|);
632
633 # Set the data path:
634 $datapath = $self->datapath($path);
635
636 # Create a TreeItem object:
637 use BuildSystem::TreeItem;
638 my $treeitem = BuildSystem::TreeItem->new();
639 $self->{BUILDTREE}->{$datapath} = $treeitem;
640
641 # If we have the project root (i.e. src), we want to process the
642 # top-level (project config) BuildFile:
643 if ($path eq $ENV{SCRAM_SOURCEDIR})
644 {
645 $buildfile = $ENV{SCRAM_CONFIGDIR}."/BuildFile";
646 # Parse the top-level BuildFile. We must do this here
647 # because we need the ClassPaths. Store as RAWDATA:
648 $self->scan($buildfile, $datapath);
649 # We need scram project base vars at project-level:
650 $treeitem->scramprojectbases($self->{SCRAM_PROJECT_BASES});
651 }
652 else
653 {
654 $buildfile = $path."/BuildFile";
655 }
656
657 # If this BuildFile exists, store in METABF:
658 if ( -f $buildfile )
659 {
660 # This level has a buildfile so store this path:
661 $treeitem->metabf($buildfile);
662 # Scan to resolve groups. Store as RAWDATA:
663 $self->scan($buildfile, $datapath);
664 ($ENV{SCRAM_DEBUG}) ? print "Scanning ",$buildfile,"\n" : print "." ;
665 }
666
667 if ($self->skipdir($datapath))
668 {
669 $treeitem->skip(1);
670 print $datapath," building skipped.\n", if ($ENV{SCRAM_DEBUG});
671 }
672
673 # Now add the class and path info to the TreeItem:
674 my ($class, $classdir, $suffix) = @{$self->buildclass($path)};
675
676 $treeitem->class($class);
677 $treeitem->classdir($classdir);
678 $treeitem->suffix($suffix);
679 $treeitem->path($path);
680 $treeitem->safepath($path);
681 $treeitem->parent($datapath);
682 $treeitem->children($filecache);
683 $treeitem->name();
684 }
685
686 print "\n";
687
688 # Check dependencies- look for cycles in the global dependency data:
689 $self->check_dependencies();
690 $self->skipdir() if ($ENV{SCRAM_DEBUG});
691 }
692
693 sub check_dependencies()
694 {
695 my $self=shift;
696 # Use the SCRAMGrapher to process the deps and return a
697 # Graph object:
698 use BuildSystem::SCRAMGrapher;
699
700 my $SG = BuildSystem::SCRAMGrapher->new($self->{DEPENDENCIES}); # GLOBAL dependencies
701 my $G = $SG->_graph_init();
702 my @classification = $G->edge_classify();
703 my @cycles;
704 my $status=0;
705
706 # Dump the vertex classification if required:
707 if ($ENV{SCRAM_DEBUG})
708 {
709 print "\n";
710 print "Dumping vertex/path classifications:","\n";
711 print "\n";
712 printf("%-40s %-40s %-15s\n",'Vertex_i','Vertex_j','CLASS');
713 printf("%-95s\n",'-'x95);
714 }
715
716 foreach my $element (@classification)
717 {
718 printf("%-40s %-40s %-15s\n",$element->[0],$element->[1],$element->[2]), if ($ENV{SCRAM_DEBUG});
719 # Save our cycles to list separately:
720 if ($element->[2] eq 'back')
721 {
722 push(@cycles,$element);
723 $status++;
724 }
725 }
726
727 print "\n";
728 if ($status)
729 {
730 map
731 {
732 print $::fail."SCRAM buildsystem ERROR: Cyclic dependency ",$_->[0]," <--------> ",$_->[1].$::normal."\n";
733 } @cycles;
734 print "\n";
735
736 # Exit:
737 exit(1);
738 }
739
740 # Otherwise return:
741 return;
742 }
743
744 sub update_toplevel()
745 {
746 my $self=shift;
747 my (@buildfiles) = @_;
748 my $treeitem;
749
750 print "Re-scanning at top-level..\n";
751
752 my $datapath = $self->datapath($ENV{LOCALTOP}."/".$ENV{SCRAM_CONFIGDIR}."/BuildFile");
753
754 # This updates the raw data:
755 $self->scan($ENV{LOCALTOP}."/".$ENV{SCRAM_CONFIGDIR}."/BuildFile", $datapath);
756
757 # Update everything else:
758 foreach my $B (@buildfiles)
759 {
760 next if ($B eq $ENV{LOCALTOP}."/config/BuildFile");
761 $datapath = $self->datapath($B);
762 # Check to see if we already have the raw data for this buildfile.
763 # Note that we won't if this scan was run from update mode. In this
764 # case, we set up the TreeItem object:
765 if (! exists($self->{BUILDTREE}->{$datapath}))
766 {
767 use BuildSystem::TreeItem;
768 $treeitem = BuildSystem::TreeItem->new();
769 my $path=$ENV{SCRAM_SOURCEDIR}."/".$datapath;
770 my ($class, $classdir, $suffix) = @{$self->buildclass($path)};
771
772 $treeitem->class($class);
773 $treeitem->classdir($classdir);
774 $treeitem->suffix($suffix);
775 $treeitem->path($path);
776 $treeitem->safepath($path);
777 $treeitem->parent($datapath);
778 $treeitem->children($filecache);
779 $treeitem->name();
780
781 $self->{BUILDTREE}->{$datapath} = $treeitem;
782
783 print "Scanning ",$B,"\n";
784 $self->scan($B,$datapath); # This updates the raw data
785 }
786 else
787 {
788 print "Scanning ",$B,"\n";
789 $self->scan($B,$datapath); # This updates the raw data
790 }
791
792 # Recursively update the tree from this data path:
793 $self->updatetree($datapath);
794 }
795 }
796
797 sub update()
798 {
799 my $self=shift;
800 my ($changeddirs, $addeddirs, $bf, $removedpaths, $toolmanager, $filecache) = @_;
801 my $buildfiles = {};
802 # Copy the contents of the array of BuildFiles to a hash so that
803 # we can track which ones have been parsed:
804 map
805 {
806 $buildfiles->{$_} = 0;
807 } @$bf;
808
809 # Tool manager:
810 $self->{TOOLMANAGER} = $toolmanager;
811 # Get scram projects from toolbox. Each project cache is
812 # loaded at this point too:
813 $self->scramprojects();
814
815 # Remove build data for removed directories:
816 $self->removedata($removedpaths);
817
818 # Now check to see if something changed at the top-level. If so we reparse everything:
819 my $toplevel = $ENV{LOCALTOP}."/".$ENV{SCRAM_CONFIGDIR}."/BuildFile";
820
821 if (exists($buildfiles->{$toplevel}))
822 {
823 $buildfiles->{$toplevel} = 1; # Parsed
824 $self->update_toplevel(@$bf);
825 }
826 else
827 {
828 # Process all new directories first then changed ones. This means that everything will be in
829 # place once we start parsing any modified BuildFiles and once we run updatetree():
830
831 $self->update_newdirs($addeddirs);
832
833 $self->update_existingdirs($changeddirs);
834
835 # Now check for any modified BuildFiles that have not yet been rescanned:
836 foreach my $bftoscan (keys %$buildfiles)
837 {
838 if ($buildfiles->{$bftoscan} == 0)
839 {
840 my $datapath = $self->datapath($bftoscan);
841 $self->scan($bftoscan,$datapath); # This updates the raw data
842 }
843 }
844 }
845
846 # Also rebuild the project Makefile from scratch:
847 $self->updatemkfrommeta();
848 print "\n";
849 }
850
851 sub update_newdirs()
852 {
853 my $self=shift;
854 my ($newdirs) = @_;
855 foreach my $path (@$newdirs)
856 {
857 print "Processing new directory \"",$path,"\"\n",if ($ENV{SCRAM_DEBUG});
858 $self->updateadir($path);
859 # Now check to see if the current (newly-added) package is needed by some
860 # packages that have already built their metadata. If so, force an update
861 # of those packages:
862 my $locations = $self->unresolved_locations($self->datapath($path));
863 if ($#$locations >= 0)
864 {
865 # Also need to check to see if a location is updated more than once.
866 foreach my $notified_dir (@$locations)
867 {
868 print "Going to notify $notified_dir of update","\n", if ($ENV{SCRAM_DEBUG});
869 $self->updateadir($notified_dir);
870 $self->remove_unresolved($self->datapath($path),$notified_dir);
871 }
872 }
873 }
874 }
875
876 sub update_existingdirs()
877 {
878 my $self=shift;
879 my ($changeddirs) = @_;
880 foreach my $path (@$changeddirs)
881 {
882 print "Processing modified directory \"",$path,"\"\n",if ($ENV{SCRAM_DEBUG});
883 $self->updateadir($path);
884 }
885 }
886
887 sub updateadir()
888 {
889 my $self=shift;
890 my ($path) = @_;
891 my $datapath = $self->datapath($path);
892 my $possiblebf = $path."/BuildFile";
893 my $treeitem;
894
895 if (! exists($self->{BUILDTREE}->{$datapath}))
896 {
897 use BuildSystem::TreeItem;
898 $treeitem = BuildSystem::TreeItem->new();
899
900 # Get the class info:
901 my ($class, $classdir, $suffix) = @{$self->buildclass($path)};
902
903 $treeitem->class($class);
904 $treeitem->classdir($classdir);
905 $treeitem->suffix($suffix);
906 $treeitem->path($path);
907 $treeitem->safepath($path);
908 $treeitem->parent($datapath);
909 $treeitem->children($filecache);
910 $treeitem->name();
911 # Store the TreeItem object:
912 $self->{BUILDTREE}->{$datapath} = $treeitem;
913 }
914
915 # Update the status of the parent. Add the child and update
916 # the safe subdirs:
917 my $parent = $self->{BUILDTREE}->{$datapath}->parent();
918
919 if (defined($self->{BUILDTREE}->{$parent}))
920 {
921 $self->{BUILDTREE}->{$parent}->updateparentstatus($datapath);
922 }
923
924 # Now check to see if there is a BuildFile here. If there is, parse it:
925 if ( -f $possiblebf)
926 {
927 # This level has a buildfile so store this path:
928 $self->{BUILDTREE}->{$datapath}->metabf($possiblebf);
929 # Scan to resolve groups. Store as RAWDATA:
930 print "Scanning ",$possiblebf,"\n";
931 $self->scan($possiblebf, $datapath);
932 # Check to see if this BuildFile is known to have needed scanning. If so,
933 # mark it as read:
934 if (exists($buildfiles->{$possiblebf}))
935 {
936 $buildfiles->{$possiblebf} = 1;
937 }
938 }
939
940 # Recursively update the tree from this data path:
941 $self->updatetree($datapath);
942 }
943
944 sub scan()
945 {
946 my $self=shift;
947 my ($inputbuildfile, $datapath) = @_;
948 my $bfparse;
949 my $buildfile;
950
951 if (exists($ENV{SCRAM_XMLBUILDFILES}) && ($ENV{SCRAM_XMLBUILDFILES}))
952 {
953 use BuildSystem::XMLBuildFile;
954 $bfparse=BuildSystem::XMLBuildFile->new();
955 $buildfile=$inputbuildfile.".xml";
956 print "Reading ",$buildfile,"\n";
957 }
958 else
959 {
960 use BuildSystem::BuildFile;
961 $bfparse=BuildSystem::BuildFile->new();
962 $buildfile=$inputbuildfile;
963 }
964
965 # Execute the parse:
966 $bfparse->parse($buildfile);
967
968 # Store group data:
969 $self->addgroup($bfparse->defined_group(), $datapath)
970 if ($bfparse->defined_group());
971
972 # See if there were skipped dirs:
973 my $skipped = $bfparse->skippeddirs($datapath);
974 # Check to see if there was an info array for this location.
975 # If so, we extract the first element of the array (i.e. ->[1])
976 # and store it under the datapath entry. This is just so that useful
977 # messages explaining why the dir was skipped can be preserved.
978 if (ref($skipped) eq 'ARRAY')
979 {
980 $self->skipdir($datapath,$skipped->[1]);
981 }
982
983 $self->storedata($datapath, $bfparse);
984
985 # Add the dependency list to our store:
986 $self->{DEPENDENCIES}->{$datapath} = $bfparse->dependencies();
987 return $self;
988 }
989
990 sub init_engine()
991 {
992 my $self=shift;
993
994 # Create the interface to the template engine:
995 use BuildSystem::TemplateInterface;
996 # Pass in the config dir as the location where templates live:
997 $self->{TEMPLATE_ENGINE} = BuildSystem::TemplateInterface->new();
998 }
999
1000 sub run_engine()
1001 {
1002 my $self=shift;
1003 my ($templatedata)=@_;
1004
1005 $self->{TEMPLATE_ENGINE}->template_data($templatedata);
1006 $self->{TEMPLATE_ENGINE}->run();
1007 return $self;
1008 }
1009
1010 sub buildclass
1011 {
1012 my $self=shift;
1013 my ($path)=@_;
1014 my $cache=[];
1015 # From Lassi TUURA (with mods by me):
1016 #
1017 # Associate a path with ClassPath setting.
1018 # For now, just assumes global data has been scanned and class settings
1019 # are already known (in $self->{CONFIGDATA}->classpath()).
1020 # Generate more optimal classpath data structure, only once.
1021 # Split every cache definition into an array of pairs, directory
1022 # name and class. So ClassPath of type "+foo/+bar/src+library"
1023 # becomes [ [ "" "foo" ] [ "" "bar" ] [ "src" "library" ] ]
1024 my @CLASSPATHS=@{$self->{BUILDTREE}->{$ENV{SCRAM_SOURCEDIR}}->rawdata()->classpath()};
1025
1026 if (! scalar @$cache)
1027 {
1028 foreach my $classpath (@CLASSPATHS)
1029 {
1030 push (@$cache, [map { [ split(/\+/, $_) ] } split(/\//, $classpath)]);
1031 }
1032 }
1033
1034 print "WARNING: No ClassPath definitions, nothing will be done!","\n",
1035 if (! scalar @$cache);
1036
1037 # Now scan the class paths. All the classpaths are given a rank
1038 # to mark how relevant they are, and then the best match is chosen.
1039 #
1040 # The ranking logic is as follows. We scan each class path and
1041 # drop if it doesn't match at all. For paths that match, we
1042 # record how many components of the class was *not* used to match
1043 # on the class: for a short $path, many classes will match.
1044 # For each path component we record whether the match was exact
1045 # (if the class part is empty, i.e. "", it's a wildcard that
1046 # matches everything). Given these rankings, we pick
1047 # - the *first* class that
1048 # - has least *unmatched* components
1049 # - with *first* or *longest* exact match sequence in
1050 # left-to-right order.
1051 my @ranks = ();
1052 my @dirs = split(/\/+/, $path);
1053 CLASS: foreach my $class (@$cache)
1054 {
1055 # The first two members of $rank are fixed: how much of path
1056 # was and was not used in the match.
1057 my $rank = [[], [@dirs]];
1058 foreach my $component (@$class)
1059 {
1060 my $dir = $rank->[1][0];
1061 if (! defined $dir)
1062 {
1063 # Path exhausted. Leave used/unused as is.
1064 last;
1065 }
1066 elsif ($component->[0] eq "")
1067 {
1068 # Wildcard match, push class and use up path
1069 push(@$rank, [1, $component->[1]]);
1070 push(@{$rank->[0]}, shift(@{$rank->[1]}));
1071 }
1072 elsif ($component->[0] eq $dir)
1073 {
1074 # Exact match, push class and use up path
1075 push(@$rank, [0, $component->[1]]);
1076 push(@{$rank->[0]}, shift(@{$rank->[1]}));
1077 }
1078 else
1079 {
1080 # Unmatched, leave used/unused as is.
1081 last;
1082 }
1083 }
1084
1085 push(@ranks, $rank);
1086 }
1087
1088 # If no classes match, bail out:
1089 if (! scalar @ranks)
1090 {
1091 return "";
1092 }
1093
1094 # Sort in ascending order by how much was of class was not used;
1095 # the first entry has least "extra" trailing match data. Then
1096 # truncate to only those equal to the best rank.
1097 my @sorted = sort { scalar(@{$a->[1]}) <=> scalar(@{$b->[1]}) } @ranks;
1098 my @best = grep(scalar(@{$_->[1]}) == scalar(@{$sorted[0][1]}), @sorted);
1099
1100 # Now figure which of the best-ranking classes have the longest
1101 # exact match in left-to-right order (= which one is first, and
1102 # those with equal first exact match, longest exact match).
1103 my $n = 0;
1104 my $class = $best[$n][scalar @{$best[$n]}-1];
1105
1106 # Return the class data:
1107 return [ $class->[1], join('/', @{$best[$n][0]}), join('/', @{$best[$n][1]}) ];
1108 }
1109
1110 sub storedata
1111 {
1112 my $self=shift;
1113 my ($datapath, $data)=@_;
1114
1115 # Store the content of this BuildFile in cache:
1116 $self->{BUILDTREE}->{$datapath}->rawdata($data);
1117 return $self;
1118 }
1119
1120 sub removedata
1121 {
1122 my $self=shift;
1123 my ($removedpaths) = @_;
1124
1125 foreach my $rd (@$removedpaths)
1126 {
1127 my $datapath = $self->datapath($rd);
1128 # Remove all data, recursively, from $datapath:
1129 $self->recursive_remove_data($datapath);
1130 }
1131
1132 return $self;
1133 }
1134
1135 sub recursive_remove_data()
1136 {
1137 my $self=shift;
1138 my ($datapath)=@_;
1139
1140 # Delete main entry in build data via TreeItem:
1141 if (exists($self->{BUILDTREE}->{$datapath}))
1142 {
1143 # We also must modify the parent TreeItem to remove the child
1144 # from SAFE_SUBDIRS as well as from CHILDREN array:
1145 my $parent = $self->{BUILDTREE}->{$datapath}->parent();
1146 $self->{BUILDTREE}->{$parent}->updatechildlist($datapath);
1147
1148 # Get the children:
1149 my @children = $self->{BUILDTREE}->{$datapath}->children();
1150
1151 foreach my $childpath (@children)
1152 {
1153 # The child path value is the datapath so can be used
1154 # directly when deleting data entries
1155 $self->recursive_remove_data($childpath);
1156 }
1157
1158 # Finally, delete the parent data (a TreeItem):
1159 delete $self->{BUILDTREE}->{$datapath};
1160 }
1161
1162 # return:
1163 return $self;
1164 }
1165
1166 sub storebranchmetadata()
1167 {
1168 my $self=shift;
1169 my ($datapath,$data)=@_;
1170
1171 # Store the content of this BuildFile in cache:
1172 $self->{BUILDTREE}->{$datapath}->branchmetadata($data);
1173 return $self;
1174 }
1175
1176 sub buildobject
1177 {
1178 my $self=shift;
1179 my ($datapath)=@_;
1180
1181 if (exists($self->{BUILDTREE}->{$datapath}) && defined($self->{BUILDTREE}->{$datapath}->rawdata()))
1182 {
1183 return $self->{BUILDTREE}->{$datapath}->rawdata();
1184 }
1185 else
1186 {
1187 return undef;
1188 }
1189 }
1190
1191 sub metaobject
1192 {
1193 my $self=shift;
1194 my ($datapath)=@_;
1195
1196 if (exists($self->{BUILDTREE}->{$datapath}) && defined($self->{BUILDTREE}->{$datapath}->branchmetadata()))
1197 {
1198 return $self->{BUILDTREE}->{$datapath}->branchmetadata();
1199 }
1200 else
1201 {
1202 return undef;
1203 }
1204 }
1205
1206 sub addgroup
1207 {
1208 my $self=shift;
1209 my ($grouparray,$datapath)=@_;
1210 my $project;
1211
1212 foreach my $group (@{$grouparray})
1213 {
1214 # Report an error if the group is defined already in a BuildFile
1215 # other than the one at $path (avoids errors because KNOWNGROUPS
1216 # is not reset before re-parsing a BuildFile in which a group is defined):
1217 if (exists $self->{KNOWNGROUPS}->{$group}
1218 && $self->{KNOWNGROUPS}->{$group} ne $datapath)
1219 {
1220 # Group already exists locally so exit:
1221 print "\n\n";
1222 $::scram->scramerror("Group \"".$group."\", defined in ".$datapath."/BuildFile, is already defined in ".
1223 $self->{KNOWNGROUPS}->{$group}."/BuildFile.\n");
1224 print "\n";
1225 }
1226 elsif ($self->searchprojects($group,\$project))
1227 {
1228 # Group already exists in a scram project so exit:
1229 print "\n\n";
1230 $::scram->scramerror("Group \"".$group."\", defined locally in ".$datapath."/BuildFile, is already defined in ".
1231 $project."\n");
1232 print "\n";
1233 }
1234 else
1235 {
1236 $self->{KNOWNGROUPS}->{$group} = $datapath;
1237 }
1238 }
1239 }
1240
1241 sub searchprojects()
1242 {
1243 my $self=shift;
1244 my ($group,$projectref)=@_;
1245
1246 foreach my $pjt (keys %{$self->{SCRAM_PROJECTS}})
1247 {
1248 print "Checking for group $group in SCRAM project $pjt","\n", if ($ENV{SCRAM_DEBUG});
1249 # As soon as a project is found to have defined $group, we return
1250 # the project name:
1251 if (exists $self->{SCRAM_PROJECTS}->{$pjt}->{KNOWNGROUPS}->{$group})
1252 {
1253 # Store the project name and data path:
1254 $$projectref="project ".uc($pjt)." (".$self->{SCRAM_PROJECTS}->{$pjt}->{KNOWNGROUPS}->{$group}."/BuildFile)";
1255 return(1);
1256 }
1257 }
1258
1259 # No group found to have been defined already so return false:
1260 return (0);
1261 }
1262
1263 sub findgroup
1264 {
1265 my $self=shift;
1266 my ($groupname) = @_;
1267
1268 if (exists $self->{KNOWNGROUPS}->{$groupname})
1269 {
1270 # If group exists, return data:
1271 return $self->{KNOWNGROUPS}->{$groupname};
1272 }
1273 else
1274 {
1275 # Not found so return:
1276 return(0);
1277 }
1278 }
1279
1280 sub knowngroups
1281 {
1282 my $self=shift;
1283 @_ ? $self->{KNOWNGROUPS}=shift
1284 : $self->{KNOWNGROUPS}
1285 }
1286
1287 sub scramprojects()
1288 {
1289 my $self=shift;
1290 # Need this to be able to read our project cache:
1291 use Cache::CacheUtilities;
1292
1293 $self->{SCRAM_PROJECTS} = $self->{TOOLMANAGER}->scram_projects();
1294
1295 # Also store the BASE of each project:
1296 $self->{SCRAM_PROJECT_BASES}={};
1297
1298 # Load the project cache for every scram-managed project in our toolbox:
1299 while (my ($project, $info) = each %{$self->{SCRAM_PROJECTS}})
1300 {
1301 if ( -f $info."/.SCRAM/".$ENV{SCRAM_ARCH}."/ProjectCache.db")
1302 {
1303 print "Reading cache for ",uc($project),"\n", if ($ENV{SCRAM_DEBUG});
1304 $self->{SCRAM_PROJECTS}->{$project} =
1305 &Cache::CacheUtilities::read($info."/.SCRAM/".$ENV{SCRAM_ARCH}."/ProjectCache.db");
1306 $self->{SCRAM_PROJECT_BASES}->{uc($project)."_BASE"} = $info;
1307 }
1308 else
1309 {
1310 print "WARNING: Unable to read project cache for ",uc($project)," tool.\n", if ($ENV{SCRAM_DEBUG});
1311 print " It could be that the project has not been built for your current architecture.","\n",
1312 if ($ENV{SCRAM_DEBUG});
1313 delete $self->{SCRAM_PROJECTS}->{$project};
1314 }
1315 }
1316
1317 # Also check to see if we're based on a release area. If so, store the cache as above. Don't store
1318 # the project name but instead just use 'RELEASE':
1319 if (my $releasearea=$::scram->releasearea() && exists $ENV{RELEASETOP})
1320 {
1321 if ( -f $ENV{RELEASETOP}."/.SCRAM/".$ENV{SCRAM_ARCH}."/ProjectCache.db")
1322 {
1323 # OK, so we found the cache. Now read it and store in the projects list:
1324 $self->{SCRAM_PROJECTS}->{RELEASE} =
1325 &Cache::CacheUtilities::read($ENV{RELEASETOP}."/.SCRAM/".$ENV{SCRAM_ARCH}."/ProjectCache.db");
1326 print "OK found release cache ",$self->{SCRAM_PROJECTS}->{RELEASE},"\n", if ($ENV{SCRAM_DEBUG});
1327 }
1328 else
1329 {
1330 print "WARNING: Current area is based on a release area but the project cache does not exist!","\n";
1331 }
1332 }
1333 }
1334
1335 sub scramprojectbases()
1336 {
1337 my $self=shift;
1338 return $self->{SCRAM_PROJECT_BASES};
1339 }
1340
1341 sub alldirs
1342 {
1343 my $self=shift;
1344 return @{$self->{ALLDIRS}};
1345 }
1346
1347 sub skipdir
1348 {
1349 my $self=shift;
1350 my ($dir, $message) = @_;
1351
1352 # Set the info if we have both args:
1353 if ($dir && $message)
1354 {
1355 $self->{SKIPPEDDIRS}->{$dir} = $message;
1356 }
1357 # If we have the dir name only, return true if
1358 # this dir is to be skipped:
1359 elsif ($dir)
1360 {
1361 (exists($self->{SKIPPEDDIRS}->{$dir})) ? return 1 : return 0;
1362 }
1363 else
1364 {
1365 # Dump the list of directories and the message for each:
1366 foreach my $directory (keys %{$self->{SKIPPEDDIRS}})
1367 {
1368 print "Directory \"",$directory,"\" skipped by the build system";
1369 if (length($self->{SKIPPEDDIRS}->{$directory}->[0]) > 10)
1370 {
1371 chomp($self->{SKIPPEDDIRS}->{$directory}->[0]);
1372 my @lines = split("\n",$self->{SKIPPEDDIRS}->{$directory}->[0]); print ":\n";
1373 foreach my $line (@lines)
1374 {
1375 next if ($line =~ /^\s*$/);
1376 print "\t-- ",$line,"\n";
1377 }
1378 print "\n";
1379 }
1380 else
1381 {
1382 print ".","\n";
1383 }
1384 }
1385 }
1386 }
1387
1388 # Keep a record of which packages are missed by each location
1389 # so that, on subsequent updates, these can be inserted auto-
1390 # matically in the metadata for the location:
1391 sub unresolved()
1392 {
1393 my $self=shift;
1394 my ($location, $pneeded) = @_;
1395 # Need to record a mapping "LOCATION -> [ missing packages ]" and a
1396 # reverse-lookup "<missing package> -> [ LOCATIONS (where update required) ]"
1397 $self->{UNRESOLVED_DEPS_BY_LOC}->{$location}->{$pneeded} = 1;
1398 $self->{UNRESOLVED_DEPS_BY_PKG}->{$pneeded}->{$location} = 1;
1399 }
1400
1401 sub remove_unresolved()
1402 {
1403 my $self=shift;
1404 my ($package, $dir) = @_;
1405 if (exists($self->{UNRESOLVED_DEPS_BY_PKG}->{$package}->{$dir}))
1406 {
1407 delete $self->{UNRESOLVED_DEPS_BY_PKG}->{$package}->{$dir};
1408 # Check to see if there are any keys left. If not, remove the
1409 # package entry:
1410 my $nkeys = scalar(keys %{$self->{UNRESOLVED_DEPS_BY_PKG}->{$package}});
1411 if ($nkeys == 0)
1412 {
1413 delete $self->{UNRESOLVED_DEPS_BY_PKG}->{$package};
1414 }
1415 }
1416 }
1417
1418 sub unresolved_locations()
1419 {
1420 my $self=shift;
1421 my ($package)=@_;
1422
1423 if (exists ($self->{UNRESOLVED_DEPS_BY_PKG}->{$package}))
1424 {
1425 # Return locations which miss the metadata of $package:
1426 return [ keys %{$self->{UNRESOLVED_DEPS_BY_PKG}->{$package}} ];
1427 }
1428 }
1429
1430 sub unresolved_packages()
1431 {
1432 my $self=shift;
1433 my ($location)=@_;
1434
1435 if (exists ($self->{UNRESOLVED_DEPS_BY_LOC}->{$location}))
1436 {
1437 # Return packages which are needed by $location:
1438 return [ keys %{$self->{UNRESOLVED_DEPS_BY_LOC}->{$location}} ];
1439 }
1440 }
1441
1442 sub verbose
1443 {
1444 my $self=shift;
1445 # Turn on verbose mode:
1446 @_ ? $self->{VERBOSE} = shift
1447 : $self->{VERBOSE}
1448 }
1449
1450 sub cachestatus()
1451 {
1452 my $self=shift;
1453 # Set/return the status of the cache:
1454 @_ ? $self->{STATUS} = shift
1455 : $self->{STATUS}
1456 }
1457
1458 sub logmsg
1459 {
1460 my $self=shift;
1461 # Print a message to STDOUT if VERBOSE is true:
1462 print STDERR @_ if $self->verbose();
1463 }
1464
1465 sub name()
1466 {
1467 my $self=shift;
1468 # Set/return the name of the cache to use:
1469 @_ ? $self->{CACHENAME} = shift
1470 : $self->{CACHENAME}
1471 }
1472
1473 sub save()
1474 {
1475 my $self=shift;
1476 # Delete unwanted stuff:
1477 delete $self->{DEPENDENCIES};
1478 delete $self->{TOOLMANAGER};
1479 delete $self->{TEMPLATE_ENGINE};
1480 delete $self->{SCRAM_PROJECTS};
1481 delete $self->{SCRAM_PROJECT_BASES};
1482 return $self;
1483 }
1484
1485
1486
1487
1488
1489
1490
1491 #### Routines for migrating BuildFile syntax to XML ####
1492 sub scan2xml()
1493 {
1494 my $self=shift;
1495 my ($buildfile) = @_;
1496 print "Migrating $buildfile to XML","\n";
1497 use BuildSystem::BuildFileXMLWriter;
1498 my $bfparse=BuildSystem::BuildFileXMLWriter->new();
1499 $bfparse->parse($buildfile);
1500 return $self;
1501 }
1502
1503 sub migrate2XML()
1504 {
1505 my $self=shift;
1506 my ($paths)=@_;
1507 my $datapath;
1508 my $buildfile;
1509 $|=1; # Flush
1510
1511 # Loop over all paths. Apply a sort so that src (shortest path) is first (FIXME!):
1512 foreach my $path (sort(@$paths))
1513 {
1514 # Ignore config content here:
1515 next if ($path !~ m|^\Q$ENV{SCRAM_SOURCEDIR}\L|);
1516
1517 # If we have the project root (i.e. src), we want to process the
1518 # top-level (project config) BuildFile:
1519 if ($path eq $ENV{SCRAM_SOURCEDIR})
1520 {
1521 $buildfile = $ENV{SCRAM_CONFIGDIR}."/BuildFile";
1522 # Parse the top-level BuildFile. We must do this here
1523 # because we need the ClassPaths. Store as RAWDATA:
1524 $self->scan2xml($buildfile);
1525 next;
1526 }
1527 else
1528 {
1529 $buildfile = $path."/BuildFile";
1530 }
1531
1532 # If this BuildFile exists, store in METABF:
1533 if ( -f $buildfile )
1534 {
1535 # Scan to resolve groups. Store as RAWDATA:
1536 $self->scan2xml($buildfile);
1537 }
1538 }
1539
1540 print "\n";
1541 }
1542
1543 1;