1 |
sashby |
1.1.2.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;
|