1 |
#
|
2 |
#
|
3 |
# Interface
|
4 |
# ---------
|
5 |
# new(ConfigArea) : A new BuildSetup
|
6 |
# BuildDir(directory,targets) : prepare the ground for a build and build
|
7 |
# getclass(directory) : return (Class, ClassDir, BuildFileobject)
|
8 |
# associated with directory
|
9 |
# setup(dir)
|
10 |
|
11 |
package BuildSystem::BuildSetup;
|
12 |
require 5.004;
|
13 |
use Utilities::Verbose;
|
14 |
use Utilities::SCRAMUtils;
|
15 |
use BuildSystem::BuildFile;
|
16 |
use BuildSystem::DateStampRecord;
|
17 |
use Utilities::AddDir;
|
18 |
@ISA=qw(Utilities::Verbose);
|
19 |
|
20 |
sub new {
|
21 |
my $class=shift;
|
22 |
my $self={};
|
23 |
bless $self,$class;
|
24 |
$self->{area}=shift;
|
25 |
$self->{toolbox}=$self->{area}->toolbox();
|
26 |
$self->{projconfigdir}=$self->{area}->configurationdir();
|
27 |
$self->{localtop}=$self->{area}->location();
|
28 |
$self->{releasearea}=$self->{area}->linkarea();
|
29 |
if ( ! defined $self->{releasearea} ) {
|
30 |
$self->{releasearea}=$self->{area};
|
31 |
}
|
32 |
$self->{releasetop}=$self->{releasearea}->location();
|
33 |
$self->{buildfilename}="BuildFile";
|
34 |
$self->_configurationsetup();
|
35 |
return $self;
|
36 |
}
|
37 |
|
38 |
sub _generateexternals {
|
39 |
my $self=shift;
|
40 |
my $outfile=shift;
|
41 |
|
42 |
# -- specify these files for dependency information
|
43 |
my $depfile=$self->{projconfigdir}."/External_Dependencies";
|
44 |
|
45 |
# -- get list of dependent files
|
46 |
my $datadir=$self->{localtop}."/.SCRAM/".$ENV{SCRAM_ARCH};
|
47 |
$fdir=FileHandle->new();
|
48 |
opendir $fdir, $datadir;
|
49 |
my @depfiles=grep !/^\.\.?$/, readdir $fdir;
|
50 |
undef $fdir;
|
51 |
for (my $i=0; $i<=$#depfiles; $i++ ) {
|
52 |
$depfiles[$i]=$datadir."/".$depfiles[$i];
|
53 |
}
|
54 |
|
55 |
# -- do we need to rebuild?
|
56 |
if ( SCRAMUtils::dated($outfile,@depfiles) ) {
|
57 |
print "Configuring Local Area\n";
|
58 |
# -- open output file
|
59 |
my $fout=FileHandle->new();
|
60 |
$fout->open(">".$outfile) or die "Unable to open $outfile for output".
|
61 |
$!."\n";
|
62 |
|
63 |
# -- print out tool/ version info
|
64 |
my ($tool,$toolobj,$f,$val,$version);
|
65 |
foreach $tool ( $self->{toolbox}->tools() ) {
|
66 |
$version=$self->{toolbox}->defaultversion($tool);
|
67 |
# default versions
|
68 |
print $fout "ifdef $tool\n".$tool."_V_".$version."=true\nendif\n";
|
69 |
# -- set up the different version -- externals
|
70 |
foreach $version ( $self->{toolbox}->versions($tool) ) {
|
71 |
$toolobj=$self->{toolbox}->gettool($tool,$version);
|
72 |
@deps=$toolobj->getfeature("_externals");
|
73 |
foreach $d ( @deps ) {
|
74 |
$d=~tr[A-Z][a-z];
|
75 |
print $fout "ifdef ".$tool."_V_".$version."\n $d=true\nendif\n";
|
76 |
}
|
77 |
# -- tool info
|
78 |
print $fout "ifdef ".$tool."_V_".$version."\n";
|
79 |
foreach $f ( $toolobj->features() ) {
|
80 |
foreach $val ( $toolobj->getfeature($f) ) {
|
81 |
print $fout "\t".$f." += ".$val."\n";
|
82 |
}
|
83 |
}
|
84 |
print $fout "endif\n";
|
85 |
}
|
86 |
}
|
87 |
# some addittional processing of specific vars
|
88 |
print $fout 'INCLUDEPATH+=$(addprefix -I,$(INCLUDE))'."\n";
|
89 |
print $fout 'LDFLAGS+=$(addprefix -L,$(LIBDIR))'."\n";
|
90 |
print $fout 'CPPFLAGS+=$(addprefix -D,$(CPPDEFINES))'."\n";
|
91 |
print $fout 'lib+=$(extralib)'."\n";
|
92 |
print $fout 'LDLIBS+=$(addprefix -l,$(lib))'."\n";
|
93 |
print $fout 'LDLIBS+=$(addprefix -l,$(REQUIRES))'."\n";
|
94 |
|
95 |
undef $fout;
|
96 |
$self->verbose("End Configuration Setup");
|
97 |
}
|
98 |
}
|
99 |
|
100 |
sub classsetup {
|
101 |
my $self=shift;
|
102 |
my $THISDIR=shift;
|
103 |
|
104 |
my $classmakefile;
|
105 |
|
106 |
my ($Class, $ClassDir, $bf)=$self->getclass($THISDIR);
|
107 |
$self->verbose("Class = $Class : ClassDir = $ClassDir for directory ".
|
108 |
$THISDIR);
|
109 |
|
110 |
# -- should we ignore?
|
111 |
if ( $bf->ignore() ) {
|
112 |
print "Nothing to be done - empty group\n";
|
113 |
exit;
|
114 |
}
|
115 |
|
116 |
|
117 |
# -- Create a makefile from the class BuildFile
|
118 |
my $classbuildfile=$self->{localtop}."/".
|
119 |
$self->{projconfigdir}."/".$Class."_BuildFile";
|
120 |
if ( -f $classbuildfile ) {
|
121 |
$classmakefile=$self->{localtop}."/".$ENV{INTwork}.
|
122 |
"/".$Class."_makefile.mk";
|
123 |
if ( SCRAMUtils::dated($classmakefile, $classbuildfile) ) {
|
124 |
# -- generate the new makefile if out of date
|
125 |
$self->verbose("Generating $classmakefile from".
|
126 |
" $classbuildfile");
|
127 |
my $classbf=BuildSystem::BuildFile->new($self->{area});
|
128 |
undef $ENV{LatestBuildFile}; # gets set by BuildFile
|
129 |
$classbf->GenerateMakefile($classbuildfile, $classmakefile);
|
130 |
}
|
131 |
}
|
132 |
else {
|
133 |
# -- No BuildFile - maybe its old style makefile
|
134 |
$classmakefile=$self->{localtop}."/".
|
135 |
$self->{projconfigdir}."/".$Class."_makefile.mk";
|
136 |
if ( ! -f $classmakefile ) {
|
137 |
$self->error("Unable to find matching ".$Class.
|
138 |
"_BuildFile or ".$Class."_makefile.mk");
|
139 |
}
|
140 |
}
|
141 |
# -- set LatestBuildFile
|
142 |
if ( $bf->buildfile() ne "" ) {
|
143 |
$ENV{LatestBuildFile}=$bf->makefile();
|
144 |
}
|
145 |
else {
|
146 |
$ENV{LatestBuildFile}=$self->{topbf}->makefile();
|
147 |
}
|
148 |
|
149 |
return ($Class,$ClassDir,$classmakefile);
|
150 |
}
|
151 |
|
152 |
sub _configurationsetup {
|
153 |
my $self=shift;
|
154 |
|
155 |
# -- set working directory
|
156 |
$self->{workdir}=$ENV{INTwork};
|
157 |
$self->{fullworkdir}=$self->{localtop}."/".$self->{workdir};
|
158 |
|
159 |
# -- make working directory
|
160 |
chdir $self->{localtop};
|
161 |
AddDir::adddir($self->{workdir});
|
162 |
|
163 |
# -- generate tool info
|
164 |
$self->_generateexternals($self->{fullworkdir}."/clientmakefile");
|
165 |
|
166 |
# -- process project BuildFile
|
167 |
$self->_topbuildfile();
|
168 |
}
|
169 |
|
170 |
sub BuildDir {
|
171 |
my $self=shift;
|
172 |
my $THISDIR=shift;
|
173 |
my @Targets=@_;
|
174 |
my $DefaultBuildFile="";
|
175 |
|
176 |
|
177 |
# -- Setup Class specifics
|
178 |
($Class,$ClassDir,$classmakefile)=$self->classsetup($THISDIR);
|
179 |
$ENV{classmakefile}=$classmakefile;
|
180 |
$ENV{Class}=$Class;
|
181 |
$ENV{ClassDir}=$ClassDir;
|
182 |
$DefaultBuildFile=$ENV{classmakefile};
|
183 |
$ENV{DefaultBuildFile}=$DefaultBuildFile;
|
184 |
|
185 |
# -- Create working directory
|
186 |
my $workdir=$self->{workdir}."/".$ClassDir;
|
187 |
chdir $self->{localtop};
|
188 |
AddDir::adddir($workdir);
|
189 |
$ENV{workdir}=$workdir;
|
190 |
my $fullworkdir=$self->{localtop}."/".$ENV{workdir};
|
191 |
chdir $fullworkdir || die "Unable to enter working directory $!";
|
192 |
|
193 |
# -- Set up some other useful variables for the Build
|
194 |
# set variables listing directories/files available
|
195 |
my $fh=FileHandle->new();
|
196 |
opendir $fh, "$self->{localtop}/$ClassDir";
|
197 |
my @allfiles= grep !/^\.\.?$/, readdir $fh;
|
198 |
undef $fh;
|
199 |
foreach $file ( @allfiles ) {
|
200 |
if ( -d "$self->{localtop}/$ClassDir/$file" ) {
|
201 |
$ENV{SCRAM_AVAILDIRS}=$ENV{SCRAM_AVAILDIRS}." ".$file;
|
202 |
}
|
203 |
else {
|
204 |
$ENV{SCRAM_AVAILFILES}=$ENV{SCRAM_AVAILFILES}." ".$file;
|
205 |
}
|
206 |
}
|
207 |
my $targetnumber=$#Targets;
|
208 |
$ENV{"MAKETARGETS"}="";
|
209 |
foreach $word ( @Targets ) {
|
210 |
if ( $word=~/.*=.*/ ) { # if we have an assignment it cant be a target
|
211 |
$targetnumber--;
|
212 |
}
|
213 |
else {
|
214 |
# set some variables for use in makefiles
|
215 |
$ENV{"MAKETARGET_".$word}=$word;
|
216 |
if ( $ENV{"MAKETARGETS"} ne "" ) {
|
217 |
$ENV{"MAKETARGETS"}=$ENV{"MAKETARGETS"}." ".$word;
|
218 |
}
|
219 |
else {
|
220 |
$ENV{"MAKETARGETS"}=$word;
|
221 |
}
|
222 |
}
|
223 |
}
|
224 |
|
225 |
# -- If target not specified default to the class name target
|
226 |
if ( $targetnumber == -1 ) {
|
227 |
push @Targets,$Class;
|
228 |
}
|
229 |
|
230 |
$ENV{DefaultMakefile}="$ENV{TOOL_HOME}/basics.mk";
|
231 |
|
232 |
|
233 |
$SCRAM_GROUPSDIR=$self->{localtop}."/".$self->{projconfigdir}."/groups.mk";
|
234 |
if ( -f $SCRAM_GROUPSDIR ) {
|
235 |
$ENV{SCRAM_GROUPSDIR}=$SCRAM_GROUPSDIR;
|
236 |
}
|
237 |
|
238 |
# Do a datestamp check so that make will build files that have changed
|
239 |
# rather than just those which are older than their dependencies
|
240 |
$self->_checkdatestampindir($ClassDir);
|
241 |
|
242 |
# -- call the block building method
|
243 |
#$report=$bs->build(@Targets);
|
244 |
|
245 |
|
246 |
# The main build here
|
247 |
$rv=system("gmake","--no-print-directory","-r","-k","-f","$ENV{DefaultMakefile}","-I$ENV{TOOL_HOME}", @Targets);
|
248 |
return $rv/256; # return the exit status of gmake
|
249 |
|
250 |
}
|
251 |
|
252 |
sub BuildIt {
|
253 |
my $self=shift;
|
254 |
my $dir=shift;
|
255 |
|
256 |
# -- get the building block for the directory
|
257 |
my $block=$self->_getdirblock($dir);
|
258 |
|
259 |
# -- is there a class block associated with the directory?
|
260 |
my ($class)=getclass($dir);
|
261 |
my $classblock=$self->_getclassblock($class);
|
262 |
|
263 |
# -- Search for Blocks up to tree root
|
264 |
my @dirblocks=();
|
265 |
my @dirs=split /\//, $dir;
|
266 |
my $fulldir="";
|
267 |
foreach $dire ( "/",@dirs ) {
|
268 |
$fulldir=$fulldir."/".$dire; # root is //
|
269 |
my $block=$self->_getdirblock($fulldir);
|
270 |
last if $block->ignore(); # we dont need to go futher
|
271 |
push @dirblocks, $block;
|
272 |
}
|
273 |
foreach $block ( $classblock, @dirblocks ) {
|
274 |
# -- merge class blocks together
|
275 |
foreach $name ( $block->classes() ) {
|
276 |
$object->merge();
|
277 |
}
|
278 |
foreach $classinst ( $block->classinst() ) {
|
279 |
$types{$classinst->id()}=$class->newinst();
|
280 |
foreach $element ( $classinst ) {
|
281 |
$types{$classinst->id()}->setelement($element,
|
282 |
$classinst->value($element));
|
283 |
}
|
284 |
}
|
285 |
}
|
286 |
|
287 |
# -- call the builder
|
288 |
|
289 |
}
|
290 |
|
291 |
sub _getdirblock {
|
292 |
my $self=shift;
|
293 |
my $dir=shift;
|
294 |
|
295 |
if ( ! defined $self->{blocks}{$dir} ) {
|
296 |
|
297 |
# -- get a buildfile and do a block parse
|
298 |
my $bf=$self->_startbuildfile($dir);
|
299 |
$bf->blockparse($self->{blocks}{$dir});
|
300 |
|
301 |
}
|
302 |
return $self->{blocks}{$dir};
|
303 |
}
|
304 |
|
305 |
sub getclass {
|
306 |
my $self=shift;
|
307 |
my $dirname = shift;
|
308 |
my $Class="DEFAULT";
|
309 |
my $ClassDir="";
|
310 |
|
311 |
#return if $dirname eq "";
|
312 |
@DIRA=split /\//, $dirname;
|
313 |
|
314 |
my $thispath=".";
|
315 |
# -- construct all class buildfiles in the path
|
316 |
for ( $i=0; $i<=$#DIRA; $i++ ) {
|
317 |
#$thispath=(($thispath eq "")?$DIRA[$i]:$thispath."/".$DIRA[$i]);
|
318 |
$thispath=$thispath."/".$DIRA[$i];
|
319 |
if ( ! exists $self->{pathbf}{$thispath} ) {
|
320 |
$self->verbose("Initialising BuildFile in $thispath");
|
321 |
$self->{pathbf}{$thispath}=$self->_startbuildfile($thispath);
|
322 |
}
|
323 |
# -- check class overriden by BuildFile
|
324 |
if ( defined $self->{pathbf}{$thispath}->classname() ) {
|
325 |
$Class=$self->{pathbf}{$thispath}->classname();
|
326 |
$ClassDir=$thispath;
|
327 |
}
|
328 |
else {
|
329 |
# -- sort it out from classpath directives
|
330 |
foreach $BlockClassA ( @{$self->{LoBCA}} ) {
|
331 |
next if ( $#{$BlockClassA} < $i );
|
332 |
$elem=${$BlockClassA}[$i];
|
333 |
if ( $elem=~/^$DIRA[$i]\+/ ) {
|
334 |
$elem=~s/^$DIRA[$i]//;
|
335 |
}
|
336 |
#print $elem." ".$DIRA[$i]."\n";
|
337 |
if ( $elem=~/^\+/ ) {
|
338 |
($Class=$elem)=~s/^\+//;
|
339 |
$ClassDir=$thispath;
|
340 |
}
|
341 |
#}
|
342 |
}
|
343 |
}
|
344 |
}
|
345 |
# -- default case
|
346 |
if ( $ClassDir eq "" ) {
|
347 |
if ( ! defined $self->{pathbf}{'.'}) {
|
348 |
$self->{pathbf}{'.'}=$self->_startbuildfile(".");
|
349 |
$self->verbose("DEFAULT class initialised : ".$self->{pathbf}{'.'});
|
350 |
}
|
351 |
$ClassDir=".";
|
352 |
}
|
353 |
|
354 |
# -- returns
|
355 |
($ClassDir_c=$ClassDir)=~s/^\.\///;
|
356 |
return ( $Class, $ClassDir_c, $self->{pathbf}{$ClassDir});
|
357 |
}
|
358 |
|
359 |
#
|
360 |
# Check to see if the buildfile is local or in the release area and
|
361 |
# parse appropriately
|
362 |
#
|
363 |
sub _startbuildfile {
|
364 |
my $self=shift;
|
365 |
my $classdir=shift;
|
366 |
|
367 |
my $bf=BuildSystem::BuildFile->new($self->{area});
|
368 |
my $thisfile="$classdir/$self->{buildfilename}";
|
369 |
|
370 |
if ( -e $self->{localtop}."/".$thisfile ) {
|
371 |
$bf->buildfile($self->{localtop}."/".$thisfile);
|
372 |
$bf->ParseBuildFile($self->{localtop}, $classdir,
|
373 |
$self->{buildfilename});
|
374 |
}
|
375 |
elsif ( -e $self->{releasetop}."/".$thisfile ) {
|
376 |
$bf->buildfile($self->{releasetop}."/".$thisfile);
|
377 |
$bf->ParseBuildFile($self->{releasetop}, $classdir,
|
378 |
$self->{buildfilename});
|
379 |
}
|
380 |
return $bf;
|
381 |
}
|
382 |
|
383 |
sub _topbuildfile {
|
384 |
my $self=shift;
|
385 |
|
386 |
# -- Analyse project buildfile if it exists
|
387 |
$self->{topbf}=BuildSystem::BuildFile->new($self->{area});
|
388 |
|
389 |
$self->{topbf}->buildfile($self->{localtop}."/".$self->{projconfigdir}
|
390 |
."/".$self->{buildfilename});
|
391 |
# -- generate top level makefile
|
392 |
$self->verbose("Generating Top Level BuildFile");
|
393 |
$self->{topbf}->ParseBuildFile($self->{localtop},
|
394 |
$self->{projconfigdir},$self->{buildfilename});
|
395 |
|
396 |
my @ClassPaths=split /:/, $self->{topbf}->BlockClassPath();
|
397 |
foreach $BClassPath ( @ClassPaths ) {
|
398 |
next if ( $BClassPath eq "");
|
399 |
push @{$self->{LoBCA}}, [ split /\//, $BClassPath ];
|
400 |
}
|
401 |
|
402 |
}
|
403 |
|
404 |
sub _checkdatestampindir {
|
405 |
my $self=shift;
|
406 |
my $dir=shift;
|
407 |
|
408 |
# -- get all local .ds files
|
409 |
my $fh=FileHandle->new();
|
410 |
my $ldir=$self->{localtop}."/".$self->{workdir}."/".$dir;
|
411 |
opendir $fh, $ldir;
|
412 |
my @dsfiles= grep /^.*\.ds$/, readdir $fh;
|
413 |
$fh->close();
|
414 |
|
415 |
# -- copy across ds files from releasetop if not existing locally
|
416 |
if ( $#dsfiles < 0 ) {
|
417 |
# -- get all releasetop .ds files
|
418 |
my $rdir=$self->{releasetop}."/".$self->{workdir}."/".$dir;
|
419 |
opendir $fh, $rdir;
|
420 |
my @releasedsfiles= grep /^.*\.ds$/, readdir $fh;
|
421 |
foreach $file ( @releasedsfiles ) {
|
422 |
use File::Copy;
|
423 |
copy($rdir."/".$file,$ldir."/".$file);
|
424 |
}
|
425 |
$fh->close();
|
426 |
@dsfiles=@releasedsfiles;
|
427 |
}
|
428 |
|
429 |
# -- process ds files
|
430 |
my $file;
|
431 |
foreach $datafile ( @dsfiles ) {
|
432 |
my $ds=BuildSystem::DateStampRecord->new($datafile);
|
433 |
my $needsupdate;
|
434 |
my $productfile=$ds->product();
|
435 |
my (%files,%moddate);
|
436 |
|
437 |
# now get dates in our dependency list
|
438 |
my @datedfiles=$ds->dated();
|
439 |
if ( $#datedfiles >= 0 ) {
|
440 |
$needsupdate=1;
|
441 |
$date=$datedfiles[0][1]-1;
|
442 |
}
|
443 |
else {
|
444 |
# -- extra checks for local replacement of files
|
445 |
foreach $file ( $ds->contents() ) {
|
446 |
# -- only check files
|
447 |
if ( -f $file ) {
|
448 |
$files{$file}=$ds->filedate($file);
|
449 |
# -- check to see if we have a new local copy
|
450 |
if ( ($file=~/\Q$self->{releasetop}\E/) &&
|
451 |
($self->{releasetop} ne $self->{localtop}) ) {
|
452 |
($tempfile=$file)=~s/\Q$self->{releasetop}\E/$self->{localtop}/;
|
453 |
if ( -f $tempfile ) {
|
454 |
$files{$tempfile}=$files{$file};
|
455 |
$file=$tempfile;
|
456 |
}
|
457 |
}
|
458 |
$moddate{$file}=(stat($file))[9];
|
459 |
if ( $moddate{$file} != $files{$file} ) {
|
460 |
$self->verbose($file." changed");
|
461 |
$date=$moddate{$file}-1;
|
462 |
$needsupdate=1;
|
463 |
}
|
464 |
}
|
465 |
}
|
466 |
}
|
467 |
# time stamp the product file to be older than the dependencies
|
468 |
if ( $needsupdate == 1 ) { # touch file into the past
|
469 |
my $newproductfile;
|
470 |
if ( $productfile!~/\Q$self->{localtop}\E/ ) {
|
471 |
if ( $productfile=~/\Q$self->{releasetop}\E/ ) {
|
472 |
($newproductfile=$productfile)=~
|
473 |
s/\Q$self->{releasetop}\E/$self->{localtop}/;
|
474 |
$self->verbose("Copying $productfile to $newproductfile");
|
475 |
copy($productfile,$newproductfile);
|
476 |
}
|
477 |
else { # assume no path to worry about
|
478 |
$newproductfile=$self->{localtop}."/".$ENV{workdir}.
|
479 |
"/".$productfile;
|
480 |
# -- make a local copy of the product file if not already here
|
481 |
my $oldproductfile=$self->{releasetop}."/".$ENV{workdir}.
|
482 |
"/".$productfile;
|
483 |
if ( ! -f $newproductfile ) {
|
484 |
if ( -f $oldproductfile ) {
|
485 |
$self->verbose("Copying $oldproductfile to $newproductfile");
|
486 |
copy($oldproductfile,$newproductfile);
|
487 |
}
|
488 |
}
|
489 |
}
|
490 |
}
|
491 |
else {
|
492 |
$newproductfile=$productfile;
|
493 |
}
|
494 |
if ( -f $newproductfile ) {
|
495 |
$self->verbose("Blasting $newproductfile to the past ($date)");
|
496 |
# If the (local) productfile exists - make it older
|
497 |
utime($date,$date,$newproductfile);
|
498 |
}
|
499 |
else {
|
500 |
$self->verbose("SomeThing Wrong(?) with $newproductfile\n".
|
501 |
"RELEASETOP=".$self->{releasetop}."\n".
|
502 |
"LOCALTOP=".$self->{localtop}."\n".
|
503 |
"workdir=".$ENV{workdir});
|
504 |
}
|
505 |
}
|
506 |
else {
|
507 |
$self->verbose("No need to touch $productfile");
|
508 |
}
|
509 |
}
|
510 |
undef $fh;
|
511 |
}
|