ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/cvsroot/COMP/SCRAM/src/BuildSystem/ToolManager.pm
Revision: 1.16
Committed: Tue Feb 27 11:59:45 2007 UTC (18 years, 2 months ago) by sashby
Content type: text/plain
Branch: MAIN
CVS Tags: V110p6, V110p5, V110p4, V110p3
Branch point for: v200branch
Changes since 1.15: +107 -6 lines
Log Message:
Merged from XML branch to HEAD. Start release prep.

File Contents

# Content
1 #____________________________________________________________________
2 # File: ToolManager.pm
3 #____________________________________________________________________
4 #
5 # Author: Shaun Ashby <Shaun.Ashby@cern.ch>
6 # Update: 2003-11-12 15:04:16+0100
7 # Revision: $Id: ToolManager.pm,v 1.15.2.3 2007/02/27 11:38:39 sashby Exp $
8 #
9 # Copyright: 2003 (C) Shaun Ashby
10 #
11 #--------------------------------------------------------------------
12 package BuildSystem::ToolManager;
13 require 5.004;
14
15 use Exporter;
16 use BuildSystem::ToolCache;
17 use BuildSystem::ToolParser;
18 use Utilities::AddDir;
19 use URL::URLhandler;
20 use Utilities::Verbose;
21
22 @ISA=qw(BuildSystem::ToolCache Utilities::Verbose);
23 @EXPORT_OK=qw( );
24 #
25
26 sub new
27 ###############################################################
28 # new #
29 ###############################################################
30 # modified : Wed Nov 12 10:34:10 2003 / SFA #
31 # params : #
32 # : #
33 # function : #
34 # : #
35 ###############################################################
36 {
37 my $proto=shift;
38 my $class=ref($proto) || $proto;
39 my $self=$class->SUPER::new(); # Inherit from ToolCache
40 my $projectarea=shift;
41
42 bless $self,$class;
43
44 $self->{arch}=shift;
45 $self->{topdir}=$projectarea->location();
46 $self->{configdir}=$self->{topdir}."/".$projectarea->configurationdir();
47 $self->{cache}=$projectarea->cache(); # Download tool cache
48 $self->{toolfiledir}=$self->{topdir}."/.SCRAM/InstalledTools";
49 $self->{datastore}=$self->{topdir}."/.SCRAM";
50 $self->{archstore}=$self->{topdir}."/.SCRAM/".$ENV{SCRAM_ARCH};
51
52 if (exists $ENV{SCRAM_TOOL_TIMESTAMP_DIR})
53 {
54 $self->{tooltimestamp}=$ENV{SCRAM_TOOL_TIMESTAMP_DIR};
55 }
56 else
57 {
58 $self->{tooltimestamp}=$self->{archstore}."/timestamps";
59 }
60
61 # Make sure our tool download dir exists:
62 AddDir::adddir($self->{toolfiledir});
63 AddDir::adddir($self->{archstore});
64 AddDir::adddir($self->{tooltimestamp});
65
66 # Set the tool cache file to read/write:
67 $self->name($projectarea->toolcachename());
68
69 # Check for the downloaded tools cache:
70 if (exists($self->{cache}))
71 {
72 $self->{urlhandler}=URL::URLhandler->new($self->{cache});
73 }
74
75 return $self;
76 }
77
78 sub clone()
79 {
80 my $self=shift;
81 my $projectarea=shift;
82
83 # Change cache settings to reflect the new location:
84 $self->{topdir}=$projectarea->location();
85
86 $self->{configdir}=$self->{topdir}."/".$projectarea->configurationdir();
87 $self->{toolfiledir}=$self->{topdir}."/.SCRAM/InstalledTools";
88 $self->{datastore}=$self->{topdir}."/.SCRAM";
89 $self->{archstore}=$self->{topdir}."/.SCRAM/".$ENV{SCRAM_ARCH};
90
91 # Change the cache name:
92 $self->name($projectarea->toolcachename());
93 $self->cloned_tm(1);
94
95 return $self;
96 }
97
98 sub arch_change_after_copy()
99 {
100 my $self=shift;
101 my ($newarch, $cachename)=@_;
102 # Make changes to arch-specific settings when copying tool manager
103 # object to another arch during setup:
104 $self->{arch} = $newarch;
105 $self->{archstore} = $self->{topdir}."/.SCRAM/".$newarch;
106 # Change the name of the cache to reflect new (arch-specific) location:
107 $self->name($cachename);
108 }
109
110 sub interactive()
111 {
112 my $self=shift;
113 # Interactive mode on/off:
114 @_ ? $self->{interactive} = shift
115 : ((defined $self->{interactive}) ? $self->{interactive} : 0);
116 }
117
118 sub setupalltools()
119 {
120 my $self = shift;
121 my ($arealocation,$setupopt) = @_;
122 my (@localtools);
123 my $selected;
124
125 # Get the selected tool list. Handle the case where there might not be
126 # any selected tools: //FIXME: need to handle case where there are no
127 # selected tools (not very often but a possibility):
128 my $sel = $self->selected();
129
130 if (defined ($sel))
131 {
132 $selected = [ keys %{$sel} ];
133 }
134
135 # Setup option "setupopt" directs the setup: 1 is for booting from
136 # scratch, 0 is when just doing "scram setup" (in this case we don't
137 # want to pick up everything from any scram-managed projects):
138 if ($setupopt == 1) # We're booting from scratch
139 {
140 # Check to see if there are any SCRAM-managed projects in our local requirements:
141 my $scramprojects = $::scram->_loadscramdb();
142
143 # Look for a match in the scram db:
144 foreach my $S (@$selected)
145 {
146 if (exists ($scramprojects->{$S}))
147 {
148 # Now check the version required exists in
149 # list of scram projects with this name:
150 while (my ($pdata,$plocation) = each %{$scramprojects->{$S}})
151 {
152 # Split the $pdata string to get the real name and the version:
153 my ($pname,$pversion) = split(":",$pdata);
154 if ($pversion eq $self->defaultversion($S))
155 {
156 # Get the tool manager for the scram project:
157 my $sa=$::scram->scramfunctions()->scramprojectdb()->getarea($pname,$pversion);
158 # Load the tool cache:
159 if ( -r $sa->toolcachename())
160 {
161 use Cache::CacheUtilities;
162 my $satoolmanager=&Cache::CacheUtilities::read($sa->toolcachename());
163 # Copy needed content from toolmanager for scram-managed project only
164 # if the projects have compatible configurations (compare first set of
165 # digits):
166 if ($self->check_compatibility($satoolmanager))
167 {
168 $self->inheritcontent($satoolmanager);
169 }
170 }
171 }
172 }
173 # Also add this scram-managed project to list of tools to set up:
174 push(@localtools,$S);
175 }
176 else
177 {
178 # Store other tools in ReqDoc in separate array. We will set up these tools later:
179 push(@localtools,$S);
180 }
181 }
182
183 # Set up extra tools required in this project, in addition to
184 # any scram-managed projects
185 foreach my $localtool (@localtools)
186 {
187 # First check to see if it's already set up (i.e., was contained
188 # in list of requirements for scram project):
189 if (! $self->definedtool($localtool))
190 {
191 $self->toolsetup($arealocation,$localtool,$self->defaultversion($localtool));
192 $self->addtoselected($localtool);
193 }
194 else
195 {
196 print $localtool," already set up.","\n",if ($ENV{SCRAM_DEBUG});
197 }
198 }
199 }
200 else
201 {
202 # Just loop over all tools and setup again:
203 foreach my $localtool (@{$selected})
204 {
205 $self->toolsetup($arealocation,$localtool,$self->defaultversion($localtool));
206 }
207 }
208
209 print "\n";
210 }
211
212 sub coresetup()
213 {
214 my $self=shift;
215 my ($toolname, $toolversion, $toolfile, $force) = @_;
216 my ($toolcheck, $toolparser);
217
218 print "\n";
219 print $::bold."Setting up ",$toolname," version ",$toolversion,": ".$::normal,"\n";
220
221 # New ToolParser object for this tool if there isn't one already.
222 # Look in array of raw tools to see if this tool has a ToolParser object:
223 $toolcheck=0;
224
225 map
226 {
227 if ($_->toolname() eq $toolname) {$toolcheck = 1; $toolparser = $_;}
228 } $self->rawtools();
229
230 # Tool not known so we create a new ToolParser object and parse it:
231 if ($toolcheck != 1 || $force == 1)
232 {
233 $toolparser = BuildSystem::ToolParser->new();
234 # We only want to store the stuff relevant for one particular version:
235 $toolparser->parse($toolname, $toolversion, $toolfile);
236 # Store the ToolParser object in the cache:
237 $self->store($toolparser);
238 print "\nFile $toolfile reparsed (modified)","\n",if ($ENV{SCRAM_DEBUG});
239 }
240
241 # Next, set up the tool:
242 my $store = $toolparser->processrawtool($self->interactive());
243 # Make sure that we have this tool in the list of selected tools (just in case this tool was
244 # set up by hand afterwards):
245 $self->addtoselected($toolname);
246
247 # Check to see if this tool is a compiler. If so, store it.
248 # Also store the language that this compiler supprots, and a
249 # compiler name (e.g. gcc323) which, in conjunction with a stem
250 # architecture name like slc3_ia32_, can be used to build a complete arch string:
251 if ($store->scram_compiler() == 1)
252 {
253 my @supported_language = $store->flags("SCRAM_LANGUAGE_TYPE");
254 my @compilername = $store->flags("SCRAM_COMPILER_NAME");
255 $self->scram_compiler($supported_language[0],$toolname,$compilername[0]);
256 }
257
258 # Store the ToolData object in the cache:
259 $self->storeincache($toolparser->toolname(),$store);
260 return $self;
261 }
262
263 sub toolsetup()
264 {
265 my $self=shift;
266 my ($arealocation, $toolname, $toolversion, $toolurl) = @_;
267 my ($urlcache, $url, $filename, $tfname);
268 my $toolfile;
269 my $force = 0; # we may have to force a reparse of a tool file
270
271 $toolname =~ tr[A-Z][a-z];
272 $toolversion ||= $self->defaultversion($toolname);
273 $urlcache=URL::URLcache->new($arealocation."/.SCRAM/cache"); # Download tool cache
274
275 # Check for the downloaded tools cache:
276 if (defined($urlcache))
277 {
278 $self->{urlhandler}=URL::URLhandler->new($urlcache);
279 }
280
281 $url = $self->toolurls()->{$toolname};
282 $filename = $self->{toolfiledir}."/".$toolname;
283
284 # If .SCRAM/InstalledTools doesn't exist, create it:
285 if (! -d $self->{toolfiledir})
286 {
287 AddDir::adddir($self->{toolfiledir});
288 }
289
290 # First, check to see if there was a tool URL given. If so, we might need to read
291 # from http or from a file: type URL:
292 if (my ($proto, $urlv) = ($toolurl =~ /(.*):(.*)/))
293 {
294 # See what kind of URL (file:, http:, cvs:, svn:, .. ):
295 if ($proto eq 'file')
296 {
297 # Check to see if there is a ~ and substitute the user
298 # home directory if there is (file:~/xyz):
299 if (my ($urlpath) = ($urlv =~ m|^\~/(.*)$|))
300 {
301 $urlv = $ENV{HOME}."/".$urlpath;
302 }
303 elsif (my ($urlpath) = ($urlv =~ m|^\./(.*)$|))
304 {
305 # Relative to current directory (file:./xyz):
306 use Cwd qw(&cwd);
307 $urlv = cwd()."/".$urlpath;
308 }
309
310 # If the tool url is a file and the file exists,
311 # copy it to .SCRAM/InstalledTools and set the
312 # filename accordingly:
313 if ( -f $urlv)
314 {
315 use File::Copy;
316 copy($urlv, $filename);
317 my $mode = 0644; chmod $mode, $filename;
318 $toolfile=$filename;
319 # Here we must account for the fact that the file tool doc may be
320 # a modified version of an existing tool in the current config. we
321 # make sure that this file is reparsed, even if there is already a
322 # ToolParser object for the tool:
323 $force = 1;
324 }
325 else
326 {
327 $::scram->scramerror("Unable to set up $toolname from URL \"$toolurl\" - $urlv does not exist!");
328 }
329 }
330 elsif ($proto eq 'http')
331 {
332 print "SCRAM: downloading $toolname from $toolurl","\n";
333 # Download from WWW first:
334 use LWP::Simple qw(&getstore);
335 my $http_response_val = &getstore($toolurl, $filename);
336
337 # Check the HTTP status. If doc not found, exit:
338 if ($http_response_val != 200)
339 {
340 my ($server,$doc) = ($urlv =~ m|//(.*?)/(.*)|);
341 $::scram->scramerror("Unable to set up $toolname: $doc not found on $server!");
342 }
343 else
344 {
345 $toolfile=$filename;
346 }
347 }
348 elsif ($proto eq 'cvs')
349 {
350 print "SCRAM: downloading $toolname from $urlv using protocol $proto.","\n";
351 print "[ not yet supported ]","\n";
352 exit(0);
353 }
354 elsif ($proto eq 'svn')
355 {
356 print "SCRAM: downloading $toolname from $urlv using protocol $proto.","\n";
357 print "[ not yet supported ]","\n";
358 exit(0);
359 }
360 else
361 {
362 $::scram->scramerror("Unable to download $urlv! Unknown protocol \"$proto\". Bye.");
363 }
364 }
365 else
366 {
367 # Copy the downloaded tool file to InstalledTools directory:
368 if ( ! -f $filename )
369 {
370 # If the URL is empty, the chances are that this tool was not downloaded to .SCRAM/InstalledTools.
371 # We signal an error and exit:
372 if ($url eq '')
373 {
374 $::scram->scramerror("$toolname was selected in project requirements but is not in the configuration!");
375 }
376 else
377 {
378 # Otherwise, we try to download it:
379 $self->verbose("Attempting Download of $url");
380 # Get file from download cache:
381 ($url,$filename)=$self->{urlhandler}->get($url);
382 use File::Copy;
383 $tfname=$self->{toolfiledir}."/".$toolname;
384 copy($filename, $tfname);
385 my $mode = 0644; chmod $mode, $tfname;
386 $toolfile=$tfname;
387 }
388 }
389 else
390 {
391 # File already exists in the .SCRAM/InstallTools directory:
392 $toolfile=$filename;
393 }
394 }
395
396 # Run the core setup routine:
397 $self->coresetup($toolname, $toolversion, $toolfile,$force);
398 return $self;
399 }
400
401 sub setupself()
402 {
403 my $self=shift;
404 my ($location)=@_;
405 # Process the file "Self" in local config directory. This is used to
406 # set all the paths/runtime settings for this project:
407 my $filename=$location."/config/Self.xml";
408
409 if ( -f $filename )
410 {
411 print "\n";
412 print $::bold."Setting up SELF:".$::normal,"\n";
413 # Self file exists so process it:
414 $selfparser = BuildSystem::ToolParser->new();
415 $selfparser->parse('self','SELF',$filename);
416
417 # Next, set up the tool:
418 $store = $selfparser->processrawtool($self->interactive());
419
420 # If we are in a developer area, also add RELEASETOP paths:
421 if (exists($ENV{RELEASETOP}))
422 {
423 print "\nAdding RELEASE area settings to self....OK","\n", if ($ENV{SCRAM_DEBUG});
424 $store->addreleasetoself();
425 }
426
427 # Store the ToolData object in the cache:
428 $self->storeincache($selfparser->toolname(),$store);
429 print "\n";
430 }
431 else
432 {
433 print "\n";
434 print "SCRAM: No file config/Self.xml...nothing to do.";
435 print "\n";
436 return;
437 }
438 }
439
440 sub defaultversion()
441 {
442 my $self = shift;
443 my ($tool) = @_;
444 # Return default versions as taken from configuration:
445 return (%{$self->defaultversions()}->{$tool});
446 }
447
448 sub storeincache()
449 {
450 my $self=shift;
451 my ($toolname,$dataobject)=@_;
452
453 # Store ToolData object (for a set-up tool) in cache:
454 if (ref($dataobject) eq 'BuildSystem::ToolData')
455 {
456 $self->updatetooltimestamp($dataobject, $toolname);
457 $self->{SETUP}->{$toolname} = $dataobject;
458 }
459 else
460 {
461 $::scram->scramerror("ToolManager: BuildSystem::ToolData object reference expected.")
462 }
463 }
464
465 sub tools()
466 {
467 my $self = shift;
468 my @tools;
469
470 map
471 {
472 if ($_ ne "self")
473 {
474 push(@tools, $_);
475 }
476 } keys %{$self->{SETUP}};
477
478 # Return list of set-up tools:
479 return @tools;
480 }
481
482 sub toolsdata()
483 {
484 my $self = shift;
485 my $tooldata = [];
486 my $rawsel = $self->selected();
487
488 foreach my $tool ( sort { %{$rawsel}->{$a}
489 <=> %{$rawsel}->{$b}}
490 keys %{$rawsel} )
491 {
492 # Return tool data objects of all set-up tools, skipping the tool "self":
493 if ($_ ne "self")
494 {
495 # Keep only tools that have really been set up:
496 if (exists $self->{SETUP}->{$tool})
497 {
498 push(@tooldata,$self->{SETUP}->{$tool});
499 }
500 }
501 }
502
503 # Return the array of tools, in order that they appear in RequirementsDoc:
504 return @tooldata;
505 }
506
507 sub definedtool()
508 {
509 my $self=shift;
510 my ($tool)=@_;
511
512 # Check to see if tool X is an external tool:
513 grep ($_ eq $tool, keys %{$self->{SETUP}}) ? return 1
514 : return 0;
515 }
516
517 sub checkifsetup()
518 {
519 my $self=shift;
520 my ($tool)=@_;
521 # Return the ToolData object if the tool has been set up:
522 (exists $self->{SETUP}->{$tool}) ? return ($self->{SETUP}->{$tool})
523 : return undef;
524 }
525
526 sub cloned_tm()
527 {
528 my $self=shift;
529 # Has this area already been cloned and brought in-line with current location:
530 @_ ? $self->{CLONED} = $_[0]
531 : $self->{CLONED};
532 }
533
534 sub remove_tool()
535 {
536 my $self=shift;
537 my ($toolname)=@_;
538 my $tools = $self->{SETUP};
539 my $newtlist = {};
540
541 while (my ($tool, $tooldata) = each %$tools)
542 {
543 if ($tool ne $toolname)
544 {
545 $newtlist->{$tool} = $tooldata;
546 }
547 else
548 {
549 # Is this tool a compiler?
550 if ($tooldata->scram_compiler() == 1)
551 {
552 # Also remove this from the compiler info if there happens to be an entry:
553 while (my ($langtype, $ctool) = each %{$self->{SCRAM_COMPILER}})
554 {
555 if ($toolname eq $ctool->[0])
556 {
557 delete $self->{SCRAM_COMPILER}->{$langtype};
558 print "Deleting compiler $toolname from cache.","\n";
559 }
560 }
561 }
562 else
563 {
564 print "Deleting $toolname from cache.","\n";
565 }
566 }
567 }
568
569 $self->{SETUP} = $newtlist;
570 $self->updatetooltimestamp ("", $toolname);
571 # Now remove from the RAW tool list:
572 $self->cleanup_raw($toolname);
573 print "ToolManager: Updating tool cache.","\n";
574 $self->writecache();
575 }
576
577 sub scram_projects()
578 {
579 my $self=shift;
580 my $scram_projects={};
581
582 foreach my $t ($self->tools())
583 {
584 # Get the ToolData object:
585 my $td=$self->{SETUP}->{$t};
586 $scram_projects->{$t}=$td->variable_data(uc($t)."_BASE"), if ($td->scram_project());
587 }
588
589 return $scram_projects;
590 }
591
592 sub scram_compiler()
593 {
594 my $self=shift;
595 my ($langtype, $toolname, $compilername)=@_;
596
597 if ($langtype)
598 {
599 # Store the compiler info according to supported
600 # language types.
601 #
602 # ---------------------- e.g C++ cxxcompiler gcc323
603 $self->{SCRAM_COMPILER}->{$langtype}=[ $toolname, $compilername ];
604 }
605 else
606 {
607 return $self->{SCRAM_COMPILER};
608 }
609 }
610
611 sub updatetool()
612 {
613 my $self=shift;
614 my ($name, $obj) = @_;
615
616 # Replace the existing copy of the tool with the new one:
617 if (exists $self->{SETUP}->{$name})
618 {
619 # Check to make sure that we were really passed a compiler with
620 # the desired name:
621 if ($obj->toolname() eq $name)
622 {
623 $self->updatetooltimestamp ($obj, $name);
624 print "ToolManager: Updating the cached copy of ".$name."\n";
625 delete $self->{SETUP}->{$name};
626 $self->{SETUP}->{$name} = $obj;
627 $self->writecache();
628 }
629 else
630 {
631 print "WARNING: Tool name (".$name.") and tool obj name (".$obj->toolname().") don't match!","\n";
632 print " Not making any changes.","\n";
633 }
634 }
635 else
636 {
637 print "WARNING: No entry in cache for ".$name.". Not making any updates.\n";
638 }
639 }
640
641 sub check_compatibility()
642 {
643 my $self=shift;
644 my ($itoolmgr)=@_;
645 # Get the version of the toolmanager. If the project fails to return a version
646 # string we return 0 for no compatibility (in which case, all tools will be set
647 # up in the traditional way):
648 my $itm_configversion = $itoolmgr->configversion();
649 if ($itm_configversion)
650 {
651 # The configurations won't be identical. We must compare the digits:
652 my ($numeric_version) = ($itm_configversion =~ /[a-zA-Z]*\_([0-9a-z]*).*?/);
653 my $current_configversion = $self->configversion();
654 my ($current_numeric_version) = ($current_configversion =~ /[a-zA-Z]*\_([0-9a-z]*).*?/);
655 ($current_numeric_version == $numeric_version) && return 1; # OK, compatible;
656 }
657 # Project does not define configuration version so just return:
658 return 0;
659 }
660
661 sub configversion()
662 {
663 my $self=shift;
664 @_ ? $self->{CONFIGVERSION} = shift
665 : $self->{CONFIGVERSION};
666 }
667
668 sub updatetooltimestamp ()
669 {
670 my $self=shift;
671 my $obj=shift;
672 my $toolname=shift;
673 my $samevalues=0;
674 if (exists $self->{SETUP}->{$toolname})
675 {
676 $samevalues=$self->comparetoolsdata($self->{SETUP}->{$toolname},$obj);
677 }
678 if (!$samevalues)
679 {
680 if (!-d $self->{tooltimestamp})
681 {
682 AddDir::adddir($self->{tooltimestamp});
683 }
684 open(TIMESTAMPFILE,">".$self->{tooltimestamp}."/$toolname");
685 close(TIMESTAMPFILE);
686 }
687 }
688
689 sub comparetoolsdata ()
690 {
691 my $self=shift;
692 my $data1=shift || ();
693 my $data2=shift || ();
694
695 my $ref1=ref($data1);
696 my $ref2=ref($data2);
697
698 if ($ref1 ne $ref2)
699 {
700 return 0;
701 }
702 elsif ($ref1 eq "CODE")
703 {
704 return 1;
705 }
706 elsif(($ref1 eq "SCALAR") || ($ref1 eq ""))
707 {
708 if ($data1 eq $data2)
709 {
710 return 1;
711 }
712 return 0;
713 }
714 elsif ($ref1 eq "ARRAY")
715 {
716 my $count = scalar(@$data1);
717 if ($count != scalar(@$data2))
718 {
719 return 0;
720 }
721 for (my $i=0; $i<$count; $i++)
722 {
723 if (! $self->comparetoolsdata($data1->[$i],$data2->[$i]))
724 {
725 return 0;
726 }
727 }
728 return 1;
729 }
730 else
731 {
732 foreach my $k (keys %{$data1})
733 {
734 if (! exists $data2->{$k})
735 {
736 return 0;
737 }
738 }
739 foreach my $k (keys %{$data2})
740 {
741 if (! exists $data1->{$k})
742 {
743 return 0;
744 }
745 }
746 foreach my $k (keys %{$data2})
747 {
748 if (! $self->comparetoolsdata($data1->{$k},$data2->{$k}))
749 {
750 return 0;
751 }
752 }
753 return 1;
754 }
755 }
756
757 1;