First public contribution.
3 # Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
5 # This component and the accompanying materials are made available
6 # under the terms of the License "Eclipse Public License v1.0"
7 # which accompanies this distribution, and is available
8 # at the URL "http://www.eclipse.org/legal/epl-v10.html".
10 # Initial Contributors:
11 # Nokia Corporation - initial contribution.
18 # rom.pl - Build a rom
20 # Pre-processes the .oby/iby files then invokes rombuild.exe
21 # (or other specified builder)
23 # First, read our config file
29 #Getopt::Long::Configure("ignore_case");
31 my $param_count = scalar(@ARGV);
32 my $result = GetOptions (\%opts, "assp=s",
55 my (@assps, @builds, %variants, @templates, %flags, %insts, %zip, %builder);
68 my $debug = $opts{_debug};
70 $quiet = $opts{quiet} unless $debug;
74 # discover where this script is running from to set the $toroot and $e32path variables
84 $EpocRoot = $ENV{EPOCROOT};
85 die "ERROR: Must set the EPOCROOT environment variable.\n" if (!defined($EpocRoot));
86 $EpocRoot =~ s-/-\\-go; # for those working with UNIX shells
87 die "ERROR: EPOCROOT must be an absolute path, " .
88 "not containing a drive letter.\n" if ($EpocRoot !~ /^\\/);
89 die "ERROR: EPOCROOT must not be a UNC path.\n" if ($EpocRoot =~ /^\\\\/);
90 die "ERROR: EPOCROOT must end with a backslash.\n" if ($EpocRoot !~ /\\$/);
91 die "ERROR: EPOCROOT must specify an existing directory.\n" if (!-d $EpocRoot);
93 # The cpp needed a drive apparently
101 unless ($fp0 =~ /^([A-Za-z]:)?\\/) {
109 my @path = split(/\\/, $cwd);
111 $toroot = ('..\\') x @path;
113 $e32path =~ s/\\kernelhwsrv\\kernel\\eka\\rombuild\\rom\.pl$//i;
114 $e32path =~ s/^[A-Za-z]://;
115 $rombuildpath = $toroot."os\\kernelhwsrv\\kernel\\eka\\rombuild";
116 $Epoc32Path = $toroot;
117 $Epoc32Path =~ s/\\$//;
118 $Epoc32Path .= $EpocRoot . "epoc32";
119 $toolpath = "$Epoc32Path\\tools\\";
120 push @INC, $toolpath;
122 $BasePath =~ s/\\$/$e32path\\/;
127 Plat_Init($toolpath);
131 print "EpocRoot = $EpocRoot\n";
132 print "Epoc32Path = $Epoc32Path\n";
133 print "drive = $drive\n";
134 print "toolpath = $toolpath\n";
135 print "toroot = $toroot\n";
136 print "e32path = $e32path\n";
137 print "rombuildpath = $rombuildpath\n";
138 print "BasePath = $BasePath\n";
141 my $cppflags="-P -undef -traditional -lang-c++ -nostdinc -iwithprefixbefore $rombuildpath -I $rombuildpath -I $Epoc32Path ";
143 # Include variant hrh file defines when processing oby and ibys with cpp
144 # (Code copied from \\EPOC\master\cedar\generic\tools\romkit\tools\buildrom.pm -
145 # it used relative path to the working dir but we are using absolute path seems neater)
148 my $variantMacroHRHFile = Variant_GetMacroHRHFile();
149 if($variantMacroHRHFile){
150 # Using absolute paths so must include drive letter otherwise cpp will fail
151 # Also adding the directory containing the HRH file to main includes paths in
152 # case it includes files
153 my $variantFilePath = Path_Split('Path',$variantMacroHRHFile);
154 $cppflags .= " -I $drive$variantFilePath -include $drive$variantMacroHRHFile";
157 if($param_count == 0 || $opts{'help'} || !$result) {
162 # Now check that the options we have make sense
167 print "Starting directory: ", Cwd::cwd(), "\n";
170 \tTYPE: $opts{'type'}
171 \tVARIANT: $opts{'variant'}
172 \tINSTRUCTION SET: $opts{'inst'}
173 \tBUILD: $opts{'build'}
174 \tMODULES: $opts{'modules'}
178 #Pick out the type file
181 if (-e "$opts{'type'}.oby") {
182 $skel="$opts{'type'}.oby";
183 } elsif (-e "$rombuildpath\\$opts{'type'}.oby") {
184 $skel="$rombuildpath\\$opts{'type'}.oby";
186 die "Can't find type file for type $opts{'type'}, $!";
189 print "Using type file $skel\n" if !$quiet;
191 # If clean is specified, zap all the image and .oby files
194 unlink glob("*.img");
196 unlink "rombuild.log";
197 unlink glob("*.rofs");
198 unlink "rofsbuild.log";
201 # Now pre-pre-process this file to point to the right places for .ibys
202 # Unfortunately cpp won't do macro replacement in #include strings, so
203 # we have to do it by hand
205 my $k = $opts{kerneltrace};
207 if ($opts{assp}=~/^m(\S+)/i) {
209 $kbdir="kbarm" if (lc $1 eq 'eig');
211 $kbdir="kb$opts{assp}";
213 $single=1 if ($opts{assp}=~/^s(\S+)/i);
216 # Hackery to cope with old compiler
217 if ($main eq 'MARM') {
226 open(X, "$skel") || die "Can't open type file $skel, $!";
227 open(OUT, "> rom1.tmp") || die "Can't open output file, $!";
229 # First output the ROM name
230 print OUT "\nromname=$romname\n";
232 s/\#\#ASSP\#\#/$opts{'assp'}/;
233 s/\#\#VARIANT\#\#/$opts{'variant'}/;
234 s/\#\#BUILD\#\#/$opts{'build'}/;
235 s/\#\#MAIN\#\#/$main/;
236 s/\#\#KMAIN\#\#/$kmain/;
237 s/\#\#E32PATH\#\#/$e32path/;
238 s/\#\#BASEPATH\#\#/$BasePath/;
239 s/\#\#EUSERDIR\#\#/$euserdir/;
240 s/\#\#ELOCLDIR\#\#/$elocldir/;
241 s/\#\#KBDIR\#\#/$kbdir/;
248 # Use cpp to pull in include chains and replace defines
251 $defines .= "-D MAIN=$main ";
252 $defines .= "-D KMAIN=$kmain ";
253 $defines .= "-D EUSERDIR=$euserdir ";
254 $defines .= "-D ELOCLDIR=$elocldir ";
255 $defines .= "-D E32PATH=$e32path ";
256 $defines .= "-D BASEPATH=$BasePath ";
257 $defines .= "-D EPOCROOT=$EpocRoot ";
258 $defines .= "-D SMAIN=$smain " if $smain;
260 foreach (@{$opts{'define'}}) {
261 my @array=split(/,/,$_);
263 $defines.="-D ".uc $_." ";
264 $pagedCode = 1 if $_ eq 'PAGED_CODE';
268 if ($opts{'modules'}) {
269 my @array=split(/,/,$opts{'modules'});
271 $defines.="-D ".uc $_." ";
275 foreach (keys %opts) {
276 next if ($_ eq 'name');
277 next if ($_ eq 'modules');
278 next if ($_ eq 'zip');
279 next if ($_ eq 'symbol');
280 next if ($_ eq 'kerneltrace');
281 next if ($_ eq 'define');
282 $defines.="-D ".uc $_."=".$opts{$_}." ";
283 $defines.="-D ".uc $_."_".$opts{$_}." ";
286 $defines.="-D SINGLE " if ($single);
290 return 1 if ($build =~ /^ARMV/i);
291 my @customizations = Plat_Customizations('ARMV5');
292 return 1 if (grep /$build/, @customizations);
296 $defines.="-D RVCT " if (IsRVCTBuild($main));
298 print "Using defines $defines\n" if !$quiet;
302 if($opts{'build'}=~/^u/i) {
304 $cppcmd = "cpp $cppflags -D UNICODE $defines rom1.tmp rom2.tmp";
306 $cppcmd = "cpp $cppflags $defines rom1.tmp rom2.tmp";
308 print "Executing CPP:\n\t$cppcmd\n" if $debug;
309 $ret = system($cppcmd);
310 die "ERROR EXECUTING CPP\n" if $ret;
312 # Zap any ## marks REMS or blank lines
314 cleanup("rom2.tmp", "rom3.tmp", $k);
316 # scan tmp file and generate auxiliary files, if required
317 open TMP, "rom3.tmp" or die("Can't open rom3.tmp\n");
321 if ($line=~/\s*gentestpaged/i) {
323 if ($line=~/\s*gentestnonpaged/i) {
324 genfile("nonpaged"); }
327 parsePatchData("rom3.tmp", "rom4.tmp");
329 # break down the oby file into rom, rofs, extensions and smr oby files
332 my $dumpfile="rom.oby";
338 open DUMPFILE, ">$dumpfile" or die("Can't create $dumpfile\n");
340 open TMP, "rom4.tmp" or die("Can't open rom4.tmp\n");
343 if ($line=~/^\s*rofsname/i)
345 close DUMPFILE; # close rom.oby or previous rofs#/extension#.oby
348 $corerofsname =~ s/rofsname\s*=\s*//i; # save core rofs name
349 $corerofsname =~ s/\s*$//g; # remove trailing \n
350 unlink $corerofsname || print "unable to delete $corerofsname";
351 my $dumpfile="rofs".$rofs.".oby";
353 open DUMPFILE, ">$dumpfile" or (close TMP and die("Can't create $dumpfile\n"));
356 if ($line=~/^\s*imagename/i)
358 close DUMPFILE; # close rom.oby or previous rofs#/extension#.oby
360 $smrname =~ s/imagename\s*=\s*//i; # save smr name
361 $smrname =~ s/\s*$//g; # remove trailing \n
362 unlink $smrname || print "unable to delete $smrname";
363 my $dumpfile="smr".$smr.".oby";
365 open DUMPFILE, ">$dumpfile" or (close TMP and die("Can't create $dumpfile\n"));
368 if ($line=~/^\s*coreimage/i)
370 close DUMPFILE; # close rofs.oby
371 if ($oby_index ne 1) {
373 die "Must specify ROFS image before ROFS extension\n";
376 $name =~ s/coreimage\s*=\s*//i; # read core rofs name
377 $name =~ s/\s*$//g; # remove trailing \n
378 if ($name ne $corerofsname) {
380 die "This extension does not relate to previous ROFS\n";
382 $oby_index=33; # open window
383 my $dumpfile="extension".$extension.".oby";
385 open DUMPFILE, ">$dumpfile" or (close TMP and die("Can't create $dumpfile\n"));
388 if ($line=~/^\s*extensionrofs/i)
390 $oby_index=3 if ($oby_index eq 2);
393 if (($oby_index eq 2) && !($line=~/^\s*$/)) {
395 die "Bad ROFS extension specification\n";
397 print DUMPFILE $line;
398 $oby_index=2 if ($oby_index eq 33); # close window
403 # For paged roms that use rofs, move all data= lines in rom which are not 'paging_unmovable' to rofs, so that paged ram-loaded code
404 # is automatically put into rofs
405 rename('rom.oby', 'rom4.tmp') || die;
407 open(IN, 'rom4.tmp') || die "Can't read rom4.tmp";
408 open(ROM, '>rom.oby') || die "Can't write to rom.oby";
410 if ($oby_index >= 1 && $pagedCode) {
411 open(ROFS, '>>rofs0.oby') || die "Can't append to rofs0.oby";
416 if(($oby_index >= 1) && ($pagedCode) && ($line=~/^\s*data\s*=/) && !($line=~/\.*paging_unmovable\s*/)) {
420 $line=~s/paging_unmovable//;
428 if ($oby_index >= 1 && $pagedCode) {
435 foreach (@{$flags{$opts{'assp'}}}) {
439 if($opts{'noheader'}) {
440 $flags.=" -no-header";
443 if($opts{'compress'}) {
444 $flags.=" -compress";
447 my $builder = $opts{'rombuilder'};
448 $builder = "rombuild" unless ($builder);
452 print "$builder $flags -type-safe-link -S rom.oby 2>&1\n\n";
454 open(Y, "$builder $flags -type-safe-link -S rom.oby 2>&1 |") ||
455 die "Can't start $builder command, $!";
461 my $error=(/^error:/i);
462 my $warning=(/^warning:/i);
463 print if ($error or $warning or !$quiet);
464 $nerrors++ if ($error);
465 $nwarnings++ if ($warning);
468 print "\nGenerated .oby file is rom.oby\n" if !$quiet;
469 print "\nGenerated image file is $romname\n" if (!$nerrors);
474 $rofsbuilder = $opts{'rofsbuilder'};
475 $rofsbuilder = "rofsbuild" unless ($rofsbuilder);
476 for(my $i=0;$i<$rofs;++$i) {
477 print "Executing $rofsbuilder on main rofs\n" if !$quiet;
478 my $image="rofs".$i.".oby";
479 system("$rofsbuilder $image");
482 print "$rofsbuilder $image returned $?\n";
485 rename "rofsbuild.log", "rofs$i.log"
489 if ($rofs and $extension) {
490 for(my $i=0;$i<$extension;++$i) {
491 print "Executing $rofsbuilder on extension rofs\n" if !$quiet;
492 my $image="extension".$i.".oby";
493 system("$rofsbuilder $image");
496 print "$rofsbuilder $image returned $?\n";
499 rename "rofsbuild.log", "extension$i.log"
504 $rofsbuilder = $opts{'rofsbuilder'};
505 $rofsbuilder = "rofsbuild" unless ($rofsbuilder);
506 for(my $i=0;$i<$smr;++$i) {
507 print "Executing $rofsbuilder on smr partition\n" if !$quiet;
508 my $image="smr".$i.".oby";
509 system("$rofsbuilder -smr=$image");
512 print "$rofsbuilder -smr=$image returned $?\n";
515 rename "rofsbuild.log", "smr$i.log"
520 print "\n\n Errors found during $builder!!\n\nLeaving tmp files\n";
521 } elsif ($nwarnings) {
522 print "\n\n Warnings during $builder!!\n\nLeaving tmp files\n";
524 print "\n\n Errors during $rofsbuilder!!\n\nLeaving tmp files\n";
526 unlink glob("*.tmp") if !$debug;
528 if ($opts{zip} or $zip{$opts{assp}}) {
529 my $zipname=$romname;
530 $zipname =~ s/\.(\w+)$/\.zip/i;
532 system("zip $zipname $romname");
535 my $logname=$romname;
536 $logname =~ s/\.(\w+)$/\.log/i;
537 my $obyname=$romname;
538 $obyname =~ s/\.(\w+)$/\.oby/i;
541 system("rename rombuild.log $logname");
542 system("rename rom.oby $obyname");
543 system("maksym $logname");
546 if ($nerrors || $nwarnings || $rerrors) {
553 ################################ Subroutines ##################################
560 Generate a rom image for the specified target, along with a rom.oby file
561 that can be fed to (a) rombuild to regenerate the image.
563 The following options are required:
564 --variant=<variant> e.g. --variant=assabet
565 --inst=<instruction set> e.g. --inst=arm4
566 --build=<build> e.g. --build=udeb
568 tshell for a text shell rom
569 e32tests for a rom with e32tests
570 f32tests for rom with f32tests
571 alltests for all the tests
573 The following are optional:
574 --name=<image name> Give image file specified name
575 --noheader Pass -no-header option on to rombuild
576 --help This help message.
577 --clean Remove existing generated files first
578 --quiet Be less verbose
579 --modules=<comma separated list> List of additional modules for this ROM
580 --define=<comma separated list> List of CPP macros to define
582 Options may be specified as a short abbreviation
583 e.g. -b udeb instead of --build udeb
589 my ($in, $out, $k) = @_;
590 my ($line, $lastblank);
592 open(OUTPUT_FILE, "> $out") or die "Cannot open $out for output";
593 open(INPUT_FILE, "< $in") or die "Cannot open for $in input";
595 while ($line=<INPUT_FILE>) {
598 # file=\epoc32\... ==> file=%EPOCROOT%\epoc32\...
599 $line =~ s/(=\s*)\\epoc32/\1${EpocRoot}Epoc32/i;
601 # Now compress blank lines down to one
607 # This is the first blank line
609 print OUTPUT_FILE $line;
614 if ($k and $line=~/^\s*kerneltrace/i) {
615 $line = "kerneltrace $k\n";
617 print OUTPUT_FILE $line if !($line=~/^\s*REM\s+/i);
634 return $SmpKernelDirs{lc $kdir};
638 unless($opts{variant}) { die "No Variant specified"; }
639 $opts{'build'}="UDEB" unless($opts{'build'});
640 $opts{'type'}="TSHELL" unless($opts{'type'});
641 $opts{'inst'}="ARM4" unless($opts{'inst'});
644 if ($opts{'modules'}) {
645 $additional="_".$opts{modules};
646 $additional=~ s/,/_/ig;
648 my $build=lc $opts{build};
649 my $inst=uc $opts{'inst'};
650 if ($inst eq "MARM") {
651 # Hackery to cope with old compiler
658 if ($main eq "THUMB") {
663 if ($main eq "ARMI" or $main eq "THUMB") {
669 $kmain = $opts{'xabi'};
670 $kmain = $main unless ($kmain);
675 $romname=$opts{name};
677 $romname=uc($opts{variant}.$additional.$main);
678 if ($build=~/^\w*DEB$/i) {
685 sub lookupFileInfo($$)
687 my ($infile, $fullname) = @_;
689 my ($name, $ext) = $fullname =~ /^(.+)\.(\w+)$/ ? ($1, $2) : ($fullname, undef);
691 open TMP, $infile or die("Can't open $infile\n");
695 if(/^\s*(\S+)\s*=\s*(\S+)\s+(\S+)/i)
697 my ($src, $dest) = ($2, $3);
699 my $destFullname = $dest =~ /^.*\\(.+)$/ ? $1 : $dest;
700 my ($destName, $destExt) = $destFullname =~ /^(.+?)\.(\w+)$/ ? ($1, $2) : ($destFullname, undef);
702 if ($destName eq $name && (!$ext || $ext eq $destExt))
705 return ($src, $dest);
710 die "patchdata: Can't find file $fullname\n";
713 sub lookupSymbolInfo($$)
715 my ($file, $name) = @_;
717 open TMP, $file or die "Can't read $file\n";
719 # ignore local symbols.
720 while (<TMP> !~ /Global Symbols/) { }
724 if (/^\s*(\S+)\s+(\S+)\s+data\s+(\S+)/i)
726 my ($symbol, $addr, $size) = ($1, $2, $3);
727 if ($symbol eq $name)
730 return ($addr, $size);
734 # This is a quick fix for RVCT 3.1, which uses the text "(EXPORTED)"
735 # in the map file. Here is an example:
737 # KHeapMinCellSize (EXPORTED) 0x0003d81c Data 4 mem.o(.constdata)
739 elsif (/^\s*(\S+)\s+\(exported\)\s+(\S+)\s+data\s+(\S+)/i)
741 my ($symbol, $addr, $size) = ($1, $2, $3);
742 if ($symbol eq $name)
745 return ($addr, $size);
750 die "patchdata: Can't find symbol $name\n";
753 sub parsePatchData($$)
755 my ($infile, $outfile) = @_;
757 open IN, $infile or die("Can't read $infile\n");
758 open OUT, ">$outfile" or die("Can't write $outfile\n");
763 if ($line =~ /^\s*patchdata\s+(.+?)\s*$/i)
765 if ($1 !~ /(\S+)\s*@\s*(\S+)\s+(\S+)\s*$/)
767 die "Bad patchdata command: $line\n";
770 my ($file, $symbol, $value) = (lc $1, $2, $3);
771 my ($srcFile, $destFile) = lookupFileInfo($infile, $file);
772 my ($index, $elementSize) = (undef, undef);
773 if ($symbol =~ s/:(\d+)\[(\d+)\]$//)
775 ($index, $elementSize) = ($2, $1);
776 $index = hex($index) if $index =~ /^0x/i;
779 if ($srcFile =~ /\\armv5(smp)?\\/i)
781 my ($symbolAddr, $symbolSize) = lookupSymbolInfo("$srcFile.map", $symbol);
787 $bytes = 1, $max = 0xff if $elementSize == 8;
788 $bytes = 2, $max = 0xffff if $elementSize == 16;
789 $bytes = 4, $max = 0xffffffff if $elementSize == 32;
790 die("patchdata: invalid element size $elementSize: $line\n") unless defined($bytes);
792 if ($bytes > 1 && (($symbolSize & ($bytes-1)) != 0))
794 die("patchdata: unexpected symbol size $symbolSize for array $symbol ($elementSize-bit elements)\n");
797 if ($index >= int($symbolSize / $bytes))
799 die("patchdata: index $index out of bounds for $symbol of $symbolSize bytes ($elementSize-bit elements)\n");
802 $symbolAddr = hex($symbolAddr) if $symbolAddr =~ /^0x/i;
803 $symbolAddr += $index * $bytes;
804 $symbolAddr = sprintf("0x%x", $symbolAddr);
806 $symbolSize = $bytes;
808 elsif ($symbolSize == 1) { $max = 0xff; }
809 elsif ($symbolSize == 2) { $max = 0xffff; }
810 elsif ($symbolSize == 4) { $max = 0xffffffff; }
811 else { die "patchdata: Unexpected symbol size $symbolSize for $symbol\n"; }
813 $value = hex($value) if $value =~ /^0x/i;
816 print("Warning: Value overflow of $symbol\n");
819 $value = sprintf("0x%08x", $value);
821 $line = "patchdata $destFile addr $symbolAddr $symbolSize $value\n";
839 if($_[0] eq 'paged') {
840 my $file='gentestpaged.txt';
842 open(OUTFILE, ">$file") or die "Can't open output file, $!";
843 for(my $i=0;$i<50000;++$i) {
844 if(($i >5) && ($i % 40 ==0)) {
848 if(($i+$count) % 5 ==0) {
849 print OUTFILE "SATOR ";
851 if(($i+$count) % 5 ==1) {
852 print OUTFILE "AREPO ";
854 if(($i+$count) % 5 ==2) {
855 print OUTFILE "TENET ";
857 if(($i+$count) % 5 ==3) {
858 print OUTFILE "OPERA ";
860 if(($i+$count) % 5 ==4) {
861 print OUTFILE "ROTAS ";
865 my $file='gentestnonpaged.txt';
867 open(OUTFILE, ">$file") or die "Can't open output file, $!";
868 for(my $i=0;$i<20000;++$i) {
869 if(($i >5) && ($i % 40 ==0)) {
873 if(($i+$count) % 4 ==0) {
874 print OUTFILE "STEP ";
876 if(($i+$count) % 4 ==1) {
877 print OUTFILE "TIME ";
879 if(($i+$count) % 4 ==2) {
880 print OUTFILE "EMIT ";
882 if(($i+$count) % 4 ==3) {
883 print OUTFILE "PETS ";
891 # Tell emacs that this is a perl script even 'though it has a .bat extension