ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/cvsroot/COMP/SCRAM/src/BuildSystem/BuildManager.pm
Revision: 1.1.2.1
Committed: Fri Feb 27 15:34:54 2004 UTC (21 years, 2 months ago) by sashby
Content type: text/plain
Branch: SCRAM_V1_BRANCH
CVS Tags: SCRAM_V1, SCRAMV1_IMPORT
Changes since 1.1: +510 -0 lines
Log Message:
First import of new SCRAM packages into CMS cvs repos.

File Contents

# Content
1 #____________________________________________________________________
2 # File: BuildManager.pm
3 #____________________________________________________________________
4 #
5 # Author: Shaun Ashby <Shaun.Ashby@cern.ch>
6 # Update: 2004-02-23 14:53:38+0100
7 # Revision: $Id: BuildManager.pm,v 1.7 2004/02/27 13:33:03 sashby Exp $
8 #
9 # Copyright: 2004 (C) Shaun Ashby
10 #
11 #--------------------------------------------------------------------
12 package BuildSystem::BuildManager;
13 require 5.004;
14 use Exporter;
15 use BuildSystem::TransientDataObject;
16 @ISA=qw(Exporter);
17 @EXPORT_OK=qw( );
18
19 sub new()
20 ###############################################################
21 # new #
22 ###############################################################
23 # modified : Mon Feb 23 14:53:51 2004 / SFA #
24 # params : #
25 # : #
26 # function : #
27 # : #
28 ###############################################################
29 {
30 my $proto=shift;
31 my $class=ref($proto) || $proto;
32 my $self={};
33
34 # Pass in the top-level data key (dir), working dir, global
35 # cache and tool manager object:
36 my ($configbfdir, $workingdir, $dataobject, $toolmanager)=@_;
37 bless $self,$class;
38
39 $self->{CONFIGBFDIR} = $configbfdir;
40 $self->{CACHEDATA} = $dataobject; # Global buildfile data cache
41 $self->{TOOLMANAGER} = $toolmanager;
42
43 # Process the top-level data (path and datapos are the same):
44 $self->location($self->{CONFIGBFDIR}, $self->{CONFIGBFDIR});
45 $self->process_buildfile();
46
47 return $self;
48 }
49
50 sub location()
51 {
52 my $self=shift;
53 my ($path,$dataposition)=@_;
54 # Store current path:
55 $self->path($path);
56 # Store the safe path:
57 $self->safepath($self->make_safepath($path));
58 # Set pointer to current buildfile object:
59 chomp($dataposition);
60 $self->level($dataposition);
61 return $self;
62 }
63
64 sub safepath()
65 {
66 my $self=shift;
67 # Return the safe version of the current path:
68 @_ ? $self->{SAFEPATH} = shift
69 : $self->{SAFEPATH};
70 }
71
72 sub make_safepath()
73 {
74 my $self=shift;
75 my ($path)=@_;
76 my $safepath;
77 # Make a safe path from our path:
78 ($safepath = $path) =~ s|/|_|g;
79 return $safepath;
80 }
81
82 sub path()
83 {
84 my $self=shift;
85 @_ ? $self->{CURRENTPATH} = shift
86 : $self->{CURRENTPATH};
87 }
88
89 sub class()
90 {
91 my $self=shift;
92 # Set/return the part of the ClassPath that matched a template name:
93 @_ ? $self->{CLASS} = shift
94 : $self->{CLASS};
95 }
96
97 sub classdir()
98 {
99 my $self=shift;
100 # Set/return the part of the ClassPath that matched:
101 @_ ? $self->{CLASSDIR} = shift
102 : $self->{CLASSDIR};
103 }
104
105 sub suffix()
106 {
107 my $self=shift;
108 # Set/return the part of the ClassPath that didn't match:
109 @_ ? $self->{SUFFIX} = shift
110 : $self->{SUFFIX};
111 }
112
113 sub generate_makefile()
114 {
115 my $self=shift;
116 my ($templatedir)=@_;
117 $templatedir ||= $ENV{LOCALTOP}."/".$ENV{SCRAM_CONFIGDIR};
118
119 use Template;
120
121 # Set up Template opts:
122 my $template_config =
123 {
124 INCLUDE_PATH => $templatedir,
125 PLUGIN_BASE => 'BuildSystem::Template::Plugins',
126 EVAL_PERL => 1
127 };
128
129 # Data to be used inside the template engine:
130 my $data =
131 { # Maybe buildmanager is not needed....
132 projectdata => $self->{BUILD}->{$self->{CONFIGBFDIR}},
133 dirdata => $self->{THISTDO},
134 cachedata => $self->{CACHEDATA},
135 toolmanager => $self->{TOOLMANAGER},
136 buildmanager => $self,
137 safepath => $self->safepath(),
138 path => $self->path(),
139 class => $self->class(),
140 classdir => $self->classdir(),
141 suffix => $self->suffix()
142 };
143
144 # Check to see if there was a "class" given via ClassPath. If there
145 # wasn't, don't do anything:
146 if ($self->class() ne '')
147 {
148 my $template = $self->class()."_template.tmpl";
149 my $ptemplate = Template->new($template_config);
150
151 # Run the engine:
152 $ptemplate->process($template, $data, $self->makefile())#, STDOUT for tests !!! $self->makefile())
153 || die "Template error: ",$ptemplate->error;
154 }
155 else
156 {
157 print "BuildManager: No CLASS, therefore no template to apply to generate a Makefile.","\n";
158 }
159 }
160
161 sub makefile()
162 {
163 my $self=shift;
164
165 if (@_)
166 {
167 if ($self->safepath() =~ m|^$ENV{SCRAM_SOURCEDIR}$|)
168 {
169 $self->{OUTFILE} = $_[0]."/Makefile";
170 }
171 else
172 {
173 $self->{OUTFILE} = $_[0]."/".$self->safepath().".mk";
174 }
175 }
176 else
177 {
178 ($self->{OUTFILE} ne '') ? return $self->{OUTFILE} : return 'Makefile';
179 }
180 }
181
182 sub level()
183 {
184 my $self=shift;
185 # Arg is the data position. CURRENTBF then points to build data
186 # for current location:
187 @_ ? $self->{CURRENTBF} = $self->{CACHEDATA}->buildobject($_[0])
188 : $self->{CURRENTBF};
189 }
190
191 sub tool_dependency()
192 {
193 my $self=shift;
194 my ($tool) = @_;
195
196 if (exists $self->{THISTDO}->{SEENTOOLS}->{$tool})
197 {
198 $self->{THISTDO}->{SEENTOOLS}->{$tool}++;
199 }
200 else
201 {
202 $self->{THISTDO}->{SEENTOOLS}->{$tool} = 1;
203 # Store this tool data object:
204 push(@{$self->{THISTDO}->{TOOLS}},$tool);
205 }
206 }
207
208 sub package_dependency()
209 {
210 my $self=shift;
211 my ($pkg)=@_;
212
213 if ($pkg)
214 {
215 # Store the required package path (hash removes dups):
216 $self->{THISTDO}->{PACKAGES}->{$pkg} = 1;
217 }
218 else
219 {
220 # Otherwise return array of required packages (jsut the keys):
221 return [keys %{$self->{THISTDO}->{PACKAGES}}];
222 }
223 }
224
225 sub check_deps()
226 {
227 my $self=shift;
228 my ($dep)=@_;
229 # Return 0/1 according to whether package has
230 # already been seen in PACKAGES:
231 (exists $self->{THISTDO}->{PACKAGES}->{$dep}) ? return 0 : return 1;
232 }
233
234 sub process_buildfile()
235 {
236 my $self=shift;
237 # Somewhere to store the data:
238 $self->{THISTDO} = BuildSystem::TransientDataObject->new();
239 # Groups first:
240 $self->resolve_groups();
241
242 # Check to see which are external tools:
243 if (my @otheruses=$self->{CURRENTBF}->use())
244 {
245 $self->resolve_use(\@otheruses);
246 }
247
248 # Now look for architecture-specific tags:
249 while (my ($archtag,$archval) = each %{$self->{CURRENTBF}->archspecific()})
250 {
251 if ($archtag eq 'USE')
252 {
253 $self->resolve_use($archval);
254 }
255 else
256 {
257 # We have another type of data in the resolved group:
258 $self->{THISTDO}->storedata($archtag, $archval);
259 }
260 }
261
262 # For each tag type that has associated data in this buildfile
263 # data object, get the data and store it in the package builder:
264 map { my $subname = lc($_); $self->{THISTDO}->storedata($_, $self->{CURRENTBF}->$subname()); }
265 $self->{CURRENTBF}->basic_tags();
266
267 # Handle productstore variables. Store in a hash with "SCRAMSTORE_x" as the key
268 # pointing to correct path as it should appear in the Makefiles:
269 map
270 {
271 my $storename="";
272 # Probably want the store value to be set to <name/<arch> or <arch>/<name> with
273 # <path> only prepending to this value rather than replacing <name>: FIXME...
274 if ($$_{'type'} eq 'arch')
275 {
276 if ($$_{'swap'} eq 'true')
277 {
278 (exists $$_{'path'}) ? ($storename .= $$_{'path'}."/".$ENV{SCRAM_ARCH})
279 : ($storename .= $$_{'name'}."/".$ENV{SCRAM_ARCH});
280 }
281 else
282 {
283 (exists $$_{'path'}) ? ($storename .= $ENV{SCRAM_ARCH}."/".$$_{'path'})
284 : ($storename .= $ENV{SCRAM_ARCH}."/".$$_{'name'});
285 }
286 }
287 else
288 {
289 (exists $$_{'path'}) ? ($storename .= $$_{'path'})
290 : ($storename .= $$_{'name'});
291 }
292
293 $self->{THISTDO}->addstore("SCRAMSTORENAME_".uc($$_{'name'}),$storename);
294 } @{$self->{THISTDO}->{PRODUCTSTORE}};
295
296 # Clean up:
297 delete $self->{THISTDO}->{PRODUCTSTORE};
298
299 # Next, tools:
300 # my @keyvars = qw(VARIABLES LIB LIBDIR INCLUDE MAKEFILE FLAGS);
301 # Sort according to how often a tool was seen: the more often, the further
302 # down the list it should appear:
303 foreach $k ( sort { %{$self->{THISTDO}->{SEENTOOLS}}->{$a}
304 <=> %{$self->{THISTDO}->{SEENTOOLS}}->{$b}}
305 keys %{$self->{THISTDO}->{SEENTOOLS}} )
306 {
307 my $t = $self->{TOOLMANAGER}->checkifsetup($k);
308
309 # Deal with any variables first:
310 foreach my $tvar ($t->list_variables())
311 {
312 $self->{THISTDO}->{VARIABLES}->{$tvar} = $t->variable_data($tvar);
313 }
314 # Makefile:
315 push(@{$self->{THISTDO}->{MAKEFILE}}, $t->makefile());
316
317 ### These must be filterable:
318 # Lib:
319 push(@{$self->{THISTDO}->{LIB}}, $t->lib());
320 # Libdir:
321 push(@{$self->{THISTDO}->{LIBDIR}}, $t->libdir());
322 # Include:
323 push(@{$self->{THISTDO}->{INCLUDE}}, $t->include());
324 ###############
325
326 # Flags:
327 if (defined (my $fhash=$t->allflags()))
328 {
329 while (my ($flag, $flagvalue) = each %{$fhash})
330 {
331 $self->{THISTDO}->flags($flag,$flagvalue);
332 }
333 }
334 }
335
336 # Now save the TDO:
337 $self->{BUILD}->{$self->path()} = $self->{THISTDO};
338 delete $self->{THISTDO};
339 return $self;
340 }
341
342 sub check_use()
343 {
344 my $self=shift;
345 my ($dataposition)=@_;
346
347 # Look for the data object for the path:
348 if (my $pkdata=$self->{CACHEDATA}->buildobject($dataposition))
349 {
350 if (! $self->check_export($pkdata))
351 {
352 print "\n";
353 print " WARNING: $dataposition/BuildFile does not export anything:\n";
354 print " **** $dataposition dependency dropped.","\n";
355 }
356 }
357 # elsif (my $relpkdata=$self->searchinscramprojects($dataposition))
358 # {
359 # }
360 else
361 {
362 # Check in the toolbox for this tool. If it doesn't
363 # exist, complain:
364 print "\n";
365 print "WARNING: Unable to find package/tool called ",$dataposition,"\n";
366 print " in current project area.","\n";
367 return(2);
368 }
369 }
370
371 sub check_export()
372 {
373 my $self=shift;
374 my ($pkdata)=@_;
375
376 if (! $pkdata->hasexport())
377 {
378 # No export so we return:
379 return(0);
380 }
381 else
382 {
383 # Collect the exported data and store in PackageBuilder object:
384 $self->process_export($pkdata->exported());
385 return(1);
386 }
387 }
388
389 sub process_export()
390 {
391 my $self=shift;
392 my ($export)=@_;
393
394 while (my ($tag,$tagvalue) = each %{$export})
395 {
396 $self->{THISTDO}->storedata($tag,$tagvalue);
397 }
398 }
399
400 sub resolve_use()
401 {
402 my $self=shift;
403 my ($data) = @_;
404
405 foreach my $use (@{$data})
406 {
407 # Check to see which are just external tools:
408 if ($self->{TOOLMANAGER}->definedtool($use) && (my $td=$self->{TOOLMANAGER}->checkifsetup($use)))
409 {
410 # We have a setup tool ($td is a ToolData object). Store the data in
411 # the PackageBuilder:
412 $self->tool_dependency($td->toolname());
413 # We also resolve the dependencies that this tool has on other tools:
414 $self->resolve_use([ $td->use() ]);
415 }
416 else
417 {
418 # We have a local or release-area package dependency. Check
419 # that the appropriate BuildFile exports something and grab the data:
420 $self->check_use($use),
421 if ($self->check_deps($use));
422 # Also store full package path for our build rules:
423 $self->package_dependency($use);
424 }
425 }
426 }
427
428 sub resolve_groups()
429 {
430 my $self=shift;
431
432 # First of all, resolve group requirements in this BuildFile:
433 my $groupdata=$self->check_groups();
434
435 while (my ($tagname, $tagvalue) = each %{$groupdata})
436 {
437 # Look for <use> tags:
438 if ($tagname eq 'USE')
439 {
440 $self->resolve_use($tagvalue);
441 }
442 else
443 {
444 # We have another type of data in the resolved group:
445 $self->{THISTDO}->storedata($tagname,$tagvalue);
446 }
447 }
448 }
449
450 sub check_groups()
451 {
452 my $self=shift;
453 my $data={};
454 $data->{USE} = [];
455
456 # - For current BuildFile object, check to see if there are any group tags;
457 my @needed_groups=$self->{CURRENTBF}->group();
458
459 # - For each group required, get the corresponding path to the BuildFile where they are defined;
460 foreach my $n_group (@needed_groups)
461 {
462 my $location = $self->{CACHEDATA}->findgroup($n_group);
463
464 if (defined $location)
465 {
466 $self->recursive_group_check($n_group,$location,$data);
467 }
468 else
469 {
470 print "Warning: Group ",$n_group," not defined.\n";
471 }
472 }
473 return $data;
474 }
475
476 sub recursive_group_check
477 {
478 my $self=shift;
479 my ($groupname,$location,$data)=@_;
480
481 # - Get the BuildFile object for the BuildFile where the group is defined;
482 my $groupbuildobject = $self->{CACHEDATA}->buildobject($location);
483 # - Look for defined_groups in the BuildFile object and find match to group tag required;
484 # - For this group, check to see if there are groups required (i.e. check for any
485 # groups in data of defined group)
486 while (my ($gkey,$gvalue) = each %{$groupbuildobject->dataforgroup($groupname)})
487 {
488 if ($gkey eq 'GROUP')
489 {
490 # - For those required groups, check the path to the BuildFile object;
491 my $nextlocation = $self->{CACHEDATA}->findgroup(@{$gvalue});
492 if (defined $nextlocation)
493 {
494 $self->recursive_group_check(@{$gvalue},$nextlocation,$data);
495 }
496 else
497 {
498 print "Warning: Group ",@{$gvalue}," not defined.\n";
499 }
500 ##
501 }
502 else
503 {
504 push(@{$data->{$gkey}},@{$gvalue});
505 }
506 }
507 return $data;
508 }
509
510 1;