imgtools/romtools/rombuild/romnibus.pl
author Mike Kinghan <mikek@symbian.org>
Thu, 25 Nov 2010 13:59:07 +0000
changeset 40 68f68128601f
parent 39 fa9d7d89d3d6
child 41 1600211976c3
permissions -rwxr-xr-x
1) Add the sbsv1 components from sftools/dev/build to make the linux_build package independent of the obsolete buildtools package. 2) Enhance romnibus.pl so that it generate the symbol file for the built rom when invoked by Raptor 3) Make the maksym.pl tool portable for Linux as well as Windows. 4) Remove the of armasm2as.pl from the e32tools component in favour of the copy now exported from sbsv1/e32util.

#!/usr/bin/perl
#
# Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
# All rights reserved.
# This component and the accompanying materials are made available
# under the terms of the License "Eclipse Public License v1.0"
# which accompanies this distribution, and is available
# at the URL "http://www.eclipse.org/legal/epl-v10.html".
#
# Initial Contributors:
# Nokia Corporation - initial contribution.
#
# Contributors:
# Mike Kinghan, mikek@symbian.org, for Symbian Foundation
#
# Description:
#
# romnibus.pl - Yet another rombuild wrapper. This one's claim to fame is that
# it works in Linux and in Windows, works when invoked from sbsv2 and when
# not invoked from sbsv2.
#
# Pre-processes the .oby/iby files then invokes rombuild.exe
# (or other specified builder)

# First, read our config file

use strict;
use Getopt::Long;
use Cwd;
use Cwd 'abs_path';
use File::Spec;

my $on_windows;
my $epocroot_vol;
my $epocroot_dir;
my $epocroot_file;
my %dir_listings = ();	
my %opts=();
my $param_count = scalar(@ARGV);
my (@assps, @builds, %variants, @templates, %flags, %insts, %zip, %builder);
my $main;
my $kmain;
my $toroot;
my $e32path;
my $rombuildpath;
my $euserdir;
my $elocldir;
my $kbdir;
my $romname;
my $single;
my $smain;
my $pagedCode;
my $debug;
my $quiet;
my $toolpath;
my $epoc32path;
my $epocroot;
my $lc_epocroot;
my $drive = "";
my $base_path;
my $line;
sub Variant_GetMacroHRHFile;
sub is_RVCT_build($);
sub parse_cfg;
sub ASSPS;
sub BUILDS;
sub TEMPLATES;
sub usage;
sub rectify($$$);
sub match_abbrev($$);
sub check_opts;
sub lookup_file_info($$);
sub lookupSymbolInfo($$);
sub parse_patch_data($$);
sub gen_file;
sub nix_fixes {
#	Fix case-sensitivity offenders for unix/linux environment.
	my $e32plat_pm = File::Spec->catfile($toolpath,"e32plat.pm");
	my $armutl_pm = File::Spec->catfile($toolpath,"armutl.pm");
	my $bpabiutl_pm = File::Spec->catfile($toolpath,"bpabiutl.pm");
	my $e32variant_pm = File::Spec->catfile($toolpath,"e32variant.pm");
	my $E32Plat_pm = File::Spec->catfile($toolpath,"E32Plat.pm");
	my $Armutl_pm = File::Spec->catfile($toolpath,"Armutl.pm");
	my $BPABIutl_pm = File::Spec->catfile($toolpath,"BPABIutl.pm");
	my $E32Variant_pm = File::Spec->catfile($toolpath,"E32Variant.pm");
	# Create symlinks for case-sensitively misnamed modules we need.
	unless ( -f $E32Plat_pm or -l $E32Plat_pm) {
		symlink($e32plat_pm,$E32Plat_pm);
	}
	unless ( -f $Armutl_pm or -l $Armutl_pm) {
		symlink($armutl_pm,$Armutl_pm);
	}
	unless ( -f $BPABIutl_pm or -l $BPABIutl_pm) {
		symlink($bpabiutl_pm,$BPABIutl_pm);
	}
	unless ( -f $E32Variant_pm or -l $E32Variant_pm) {
		symlink($e32variant_pm,$E32Variant_pm);
	}
	# Make uppercase symlinks to all .bsf files in /epoc32/tools
	my @bsf_files = glob(File::Spec->catfile($toolpath,"*.bsf"));
	foreach my $bsf_file (@bsf_files) {
		my ($vol,$dirs,$file) = File::Spec->splitpath($bsf_file);
		$file =~ /^(\S+)\.bsf/;
		my $uc_stem = uc($1);
		my $uc_bsf_file = File::Spec->catpath($vol,$dirs,($uc_stem .".bsf"));
		unless ( -f $uc_bsf_file or -l $uc_bsf_file ) {
			symlink($bsf_file,$uc_bsf_file) or die "Can't symlink $bsf_file -> $uc_bsf_file. $!";
		}
	}
}


BEGIN {
	$on_windows = $^O =~ /^MSWin/ ? 1 : 0;
	$epocroot = $ENV{EPOCROOT};
	die "ERROR: Must set the EPOCROOT environment variable.\n" if (!defined($epocroot));
	print "Environmental epocroot - >$epocroot<\n";
	$epocroot =~ s/:$/:\//, if $on_windows;
	$epocroot = abs_path($epocroot);
	die "ERROR: EPOCROOT must specify an existing directory.\n" if (!-d $epocroot);
	($epocroot_vol,$epocroot_dir,$epocroot_file) = File::Spec->splitpath($epocroot);
	$epocroot = File::Spec->catfile(($epocroot_vol,$epocroot_dir,$epocroot_file),undef);
	print "EPOCROOT=$ENV{EPOCROOT} resolved as \"$epocroot\"\n";
	$lc_epocroot = lc($epocroot);
	$epoc32path = File::Spec->catfile($epocroot,"epoc32");
	$toolpath = File::Spec->catfile($epoc32path,"tools");
	push @INC, $toolpath;
	nix_fixes(), unless ($on_windows);
}


my $result = GetOptions (\%opts, "assp=s",
						 "inst=s",
						 "type=s",
						 "variant=s",
						 "build=s", 
						 "conf=s",
						 "name=s",
						 "modules=s",
						 "xabi=s",
						 "clean",
						 "quiet",
						 "help",
						 "debug",
						 "zip",
						 "symbol",
						 "noheader",
						 "kerneltrace=s",
						 "rombuilder=s",
						 "define=s@",
						 "rofsbuilder=s",
						 "compress",
						 );

$debug = $opts{debug};
$quiet = $opts{quiet} unless $debug;

my $cwd = Cwd::cwd();
my ($vol,$dirs,$file) = File::Spec->splitpath($cwd);
$drive = $vol, if ($on_windows);
my @path_parts = File::Spec->splitdir($dirs);
while(@path_parts[-1] ne "sf") {
	pop(@path_parts);
}
$base_path = File::Spec->catdir((@path_parts,"os"));
$base_path = "$drive$base_path", if ($on_windows);
$rombuildpath = File::Spec->catfile($base_path,"kernelhwsrv","kernel","eka","rombuild");
$base_path .= ($on_windows ? '\\' : '/');  
$e32path = ($on_windows ? "\\sf\\os" : "/sf/os"); 

use E32Plat;
{
        Plat_Init($toolpath . ($on_windows ? '\\' : '/'));
}

if ($debug) {
	print "epocroot = $epocroot\n";
	print "epoc32path = $epoc32path\n";
	print "drive = $drive\n";
	print "toolpath = $toolpath\n";
	print "toroot = $toroot\n";
	print "e32path = $e32path\n";
	print "rombuildpath = $rombuildpath\n";
	print "base_path = $base_path\n";
}

#my $cppflags="-P -undef -Wno-endif-labels -traditional -lang-c++ -nostdinc -iwithprefixbefore $rombuildpath -I $rombuildpath -I $drive$epoc32path ";
my $cppflags="-P -undef -Wno-endif-labels -traditional -lang-c++ -nostdinc -I $rombuildpath -I $epoc32path ";

# Include variant hrh file defines when processing oby and ibys with cpp
# (Code copied from \\EPOC\master\cedar\generic\tools\romkit\tools\buildrom.pm -
# it used relative path to the working dir but we are using absolute path seems neater)

my $variantMacroHRHFile = Variant_GetMacroHRHFile();
if($variantMacroHRHFile){
	my ($ignore1,$dir,$file) = File::Spec->splitpath($variantMacroHRHFile);
	$cppflags .= " -I " . File::Spec->catpath($epocroot_vol,$dir,undef) . " -include " . File::Spec->catpath($epocroot_vol,$dir,$file); 
}

if($param_count == 0 || $opts{'help'} || !$result) {
	usage();
	exit 0;
}

# Now check that the options we have make sense

checkopts();

if (!$quiet) {
	print "Starting directory: ", Cwd::cwd(), "\n";
	print <<EOF;
OPTIONS:
\tTYPE: $opts{'type'}
\tVARIANT: $opts{'variant'}
\tINSTRUCTION SET: $opts{'inst'}
\tBUILD: $opts{'build'}
\tMODULES: $opts{'modules'}
EOF
}

#Pick out the type file
my $skel = "$opts{'type'}.oby";
unless (-e $skel) {
	$skel= File::Spec->catfile($rombuildpath,"$opts{'type'}.oby");
}
unless (-e $skel) {
	die "Can't find type file for type $opts{'type'}, $!";
}

print "Using type file $skel\n" if !$quiet;

# If clean is specified, zap all the image and .oby files

if($opts{'clean'}) {
	unlink glob("*.img");
	unlink "rom.oby";
	unlink "rombuild.log";
	unlink glob("*.rofs");
	unlink "rofsbuild.log";
}

# Now pre-pre-process this file to point to the right places for .ibys
# Unfortunately cpp won't do macro replacement in #include strings, so
# we have to do it by hand

my $k = $opts{kerneltrace};

if ($opts{assp}=~/^m(\S+)/i) {
	$kbdir="kb$1";
	$kbdir="kbarm" if (lc $1 eq 'eig');
} else {
	$kbdir="kb$opts{assp}";
}
$single=1 if ($opts{assp}=~/^s(\S+)/i);

if ($single) {
	# Hackery to cope with old compiler
	if ($main eq 'MARM') {
		$smain='SARM';
	} else {
		$smain="S$main";
	}
} else {
	$smain=$main;
}

unless ($on_windows) {
	$main = lc($main);
	$kmain = lc($kmain);
}


open(X, "$skel") || die "Can't open type file $skel, $!";
open(OUT, "> rom1.tmp") || die "Can't open output file, $!";

# First output the ROM name
print OUT "\nromname=$romname\n";
while(<X>) {
	s/\#\#ASSP\#\#/$opts{'assp'}/;
	s/\#\#VARIANT\#\#/$opts{'variant'}/;
	s/\#\#BUILD\#\#/$opts{'build'}/;
	s/\#\#MAIN\#\#/$main/;
	s/\#\#KMAIN\#\#/$kmain/;
	s/\#\#E32PATH\#\#/$e32path/;
	s/\#\#BASEPATH\#\#/$base_path/;
	s/\#\#EUSERDIR\#\#/$euserdir/;
	s/\#\#ELOCLDIR\#\#/$elocldir/;
	s/\#\#KBDIR\#\#/$kbdir/;
	unless ($on_windows) {
		if (m/#include/) {
			s|\\|\/|g;
			lc;
		}
	}
	print OUT;
}

close X;
close OUT;

# Use cpp to pull in include chains and replace defines

my $defines = "";
$defines .= "-D MAIN=$main ";
$defines .= "-D KMAIN=$kmain ";
$defines .= "-D EUSERDIR=$euserdir ";
$defines .= "-D ELOCLDIR=$elocldir ";
$defines .= "-D E32PATH=$e32path ";
$defines .= "-D BASEPATH=$base_path ";
$defines .= "-D EPOCROOT=$epocroot ";
$defines .= "-D SMAIN=$smain " if $smain;

foreach (@{$opts{'define'}}) {
	my @array=split(/,/,$_);
	foreach (@array) {
		$defines.="-D ".uc $_." ";
		$pagedCode = 1 if $_ eq 'PAGED_CODE';
		}
	}

if ($opts{'modules'}) {
	my @array=split(/,/,$opts{'modules'});
	foreach (@array) {
		$defines.="-D ".uc $_." ";
		}
	}

foreach (keys %opts) {
	next if ($_ eq 'name');
	next if ($_ eq 'modules');
	next if ($_ eq 'zip');
	next if ($_ eq 'symbol');
	next if ($_ eq 'kerneltrace');
	next if ($_ eq 'define');
	$defines.="-D ".uc $_."=".$opts{$_}." ";
	$defines.="-D ".uc $_."_".$opts{$_}." ";
}

$defines.="-D SINGLE " if ($single);
$defines.="-D RVCT " if (IsRVCTBuild($main));

print "Using defines $defines\n" if !$quiet;

my $ret=1;
my $cppcmd;
if($opts{'build'}=~/^u/i and $on_windows) {
	# Unicode build
	$cppcmd = File::Spec->catfile($epoc32path,"gcc","bin","cpp") . " $cppflags -D UNICODE $defines rom1.tmp rom2.tmp";
} else {
	$cppcmd = "cpp $cppflags $defines rom1.tmp rom2.tmp";
}
print "Executing CPP:\n\t$cppcmd\n" if $debug;
$ret = system($cppcmd);
die "ERROR EXECUTING CPP\n" if $ret;

# Purge remarks and blank lines. Complete source filenames and adapt them to host filesystem.
rectify("rom2.tmp", "rom3.tmp", $k);

# scan tmp file and generate auxiliary files, if required
open TMP, "rom3.tmp" or die("Can't open rom3.tmp\n");
while ($line=<TMP>)
	{
	if ($line=~/\s*gentestpaged/i) {
		genfile("paged");	}
	if ($line=~/\s*gentestnonpaged/i) {
		genfile("nonpaged");	}
	}

parsePatchData("rom3.tmp", "rom4.tmp");

# break down the oby file into rom, rofs and extensions oby files

my $oby_index =0;
my $dumpfile="rom.oby";
my $rofs=0;
my $extension=0;
my $corerofsname="";
open DUMPFILE, ">$dumpfile" or die("Can't create $dumpfile\n");
open TMP, "rom4.tmp" or die("Can't open rom4.tmp\n");
while ($line=<TMP>)
	{
	if ($line=~/^\s*rofsname/i)
		{
		close DUMPFILE;							# close rom.oby or previous rofs#/extension#.oby
		$oby_index=1;
		$corerofsname=$line;
		$corerofsname =~ s/rofsname\s*=\s*//i;		# save core rofs name
		chomp($corerofsname);
		unlink $corerofsname || print "unable to delete $corerofsname";
		my $dumpfile="rofs".$rofs.".oby";
		$rofs++;
		open DUMPFILE, ">$dumpfile" or (close TMP and die("Can't create $dumpfile\n"));
		}

	if ($line=~/^\s*coreimage/i)
		{
		close DUMPFILE;							# close rofs.oby
		if ($oby_index ne 1) {
			close TMP;
			die "Must specify ROFS image before ROFS extension\n";
		}
		my $name=$line;
		$name =~ s/coreimage\s*=\s*//i;		# read core rofs name
		chomp($name); 			# remove trailing \n
		if ($name ne $corerofsname) {
			close TMP;
			die "This extension does not relate to previous ROFS\n";
		}
		$oby_index=33;						# open window
		my $dumpfile="extension".$extension.".oby";
		$extension++;
		open DUMPFILE, ">$dumpfile" or (close TMP and die("Can't create $dumpfile\n"));
		}

	if ($line=~/^\s*extensionrofs/i)
		{
		$oby_index=3 if ($oby_index eq 2);
		}

	if (($oby_index eq 2) && !($line=~/^\s*$/)) {
		close TMP;
		die "Bad ROFS extension specification\n";
	}
	print DUMPFILE $line;
	$oby_index=2 if ($oby_index eq 33);		# close window
	}
close DUMPFILE;
close TMP;

# 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
# is automatically put into rofs
rename('rom.oby', 'rom4.tmp') || die;

open(IN, 'rom4.tmp') || die "Can't read rom4.tmp";
open(ROM, '>rom.oby') || die "Can't write to rom.oby";

if ($oby_index >= 1 && $pagedCode)	{
	open(ROFS, '>>rofs0.oby') || die "Can't append to rofs0.oby";
}

while ($line=<IN>)
{
	if(($oby_index >= 1) && ($pagedCode) && ($line=~/^\s*data\s*=/) && !($line=~/\.*paging_unmovable\s*/)) {
		print ROFS $line;
	}
	else {
		$line=~s/paging_unmovable//;
		print ROM $line;
	}
}

close IN;
close ROM;

if ($oby_index >= 1 && $pagedCode)	{
	close ROFS;
}
	unlink 'rom4.tmp';

my $flags;

foreach (@{$flags{$opts{'assp'}}}) {
	$flags.=" -$_";
}

if($opts{'noheader'}) {
	$flags.=" -no-header";
}

if($opts{'compress'}) {
	$flags.=" -compress";
}

my $builder = $opts{'rombuilder'};
if ($on_windows) {
	$builder = File::Spec->catfile($toolpath,"rombuild.exe") unless ($builder);
}
else {
	$builder = File::Spec->catfile($toolpath,"rombuild") unless ($builder);
	unless ( -x $builder ) {
		chmod 0755,$builder;
	}
}

print "$builder $flags -type-safe-link -S rom.oby 2>&1\n\n";

open(Y, "$builder $flags -type-safe-link -S rom.oby 2>&1 | ") || 
	die "Can't start $builder command, $!";

my $nerrors=0;
my $nwarnings=0;

while(<Y>) {
	my $error=(/^error:/i);
	my $warning=(/^warning:/i);
	print if ($error or $warning or !$quiet);
	$nerrors++ if ($error);
	$nwarnings++ if ($warning);
}

print "\nGenerated .oby file is rom.oby\n" if !$quiet;
print "\nGenerated image file is $romname\n" if (!$nerrors);

my$rerrors;
my $rofsbuilder;
if ($rofs) {
	$rofsbuilder = $opts{'rofsbuilder'};
	$rofsbuilder = "rofsbuild" unless ($rofsbuilder);
	for(my $i=0;$i<$rofs;++$i) {
		print "Executing $rofsbuilder on main rofs\n" if !$quiet;
		my $image="rofs".$i.".oby";
		system("$rofsbuilder $image");
		if ($? != 0)
			{
			print "$rofsbuilder $image returned $?\n";
			$rerrors++;
			}
		rename "rofsbuild.log", "rofs$i.log"
		}
}

if ($rofs and $extension) {
	for(my $i=0;$i<$extension;++$i) {
		print "Executing $rofsbuilder on extension rofs\n" if !$quiet;
		my $image="extension".$i.".oby";
		system("$rofsbuilder $image");
		if ($? != 0)
			{
			print "$rofsbuilder $image returned $?\n";
			$rerrors++;
			}
		rename "rofsbuild.log", "extension$i.log"
		}
}

if ($nerrors) {
	print "\n\n Errors found during $builder!!\n\nLeaving tmp files\n";
} elsif ($nwarnings) {
	print "\n\n Warnings during $builder!!\n\nLeaving tmp files\n";
} elsif ($rerrors) {
	print "\n\n Errors during $rofsbuilder!!\n\nLeaving tmp files\n";
} else {
	unlink glob("*.tmp") if !$debug;
}
if ($opts{zip} or $zip{$opts{assp}}) {
	my $zipname=$romname;
	$zipname =~ s/\.(\w+)$/\.zip/i;
	unlink $zipname;
	system("zip $zipname $romname");
}
if ($opts{symbol}) {
	my $maksym = File::Spec->catfile($toolpath,"maksym.pl");
	my $logname=$romname;
	$logname =~ s/\.(\w+)$/\.log/i;
	my $obyname=$romname;
	$obyname =~ s/\.(\w+)$/\.oby/i;
	unlink $logname;
	unlink $obyname;
	unless (rename("ROMBUILD.LOG",$logname)) {
		print "\"rename ROMBUILD.LOG -> $logname\" failed: $!\n";
		exit 1;
	}
	unless (rename("rom.oby",$obyname)) {
		print "\"rename rom.oby -> $obyname\" failed: $!\n";
		exit 1;
	}
	if (system("$maksym $logname")) {
		print "\"maksym.pl $logname\" failed: $!\n";
		exit 1;
	}
	unless (rename($logname,"ROMBUILD.LOG")) {
		print "\"rename $logname -> ROMBUILD.LOG\" failed: $!\n";
		exit 1;
	}
	unless (rename($obyname,"rom.oby")) {
		print "rename $obyname -\. rom.oby\" failed: $!\n";
		exit 1;
	}
}

#IMK if ($nerrors || $nwarnings || $rerrors) {
#IMK	exit 4;
#IMK}	
if ($nerrors || $rerrors) {
	exit 4;
}	

	
exit 0;


################################ Subroutines  ##################################

sub usage {
	print <<EOT;

rom <options>

Generate a rom image for the specified target, along with a rom.oby file
that can be fed to (a) rombuild to regenerate the image.

The following options are required:
  --variant=<variant>         e.g. --variant=assabet
  --inst=<instruction set>    e.g. --inst=arm4
  --build=<build>             e.g. --build=udeb
  --type=<type of rom>  
         tshell for a text shell rom
         e32tests for a rom with e32tests
         f32tests for rom with f32tests
         alltests for all the tests

The following are optional:
  --name=<image name>               Give image file specified name
  --noheader                        Pass -no-header option on to rombuild
  --help                            This help message.
  --clean                           Remove existing generated files first
  --quiet                           Be less verbose
  --modules=<comma separated list>  List of additional modules for this ROM
  --define=<comma separated list>   List of CPP macros to define

Options may be specified as a short abbreviation 
e.g. -b udeb instead of --build udeb

EOT
}

sub rectify($$$) {
	my ($in, $out, $k) = @_;
	my $lastblank;
	my $lineno = 0;
	my $epocroot_pattern = $on_windows ? $epocroot . '\\\\' : $epocroot;

	open(OUTPUT_FILE, "> $out") or die "Cannot open $out for output";
	open(INPUT_FILE, "< $in") or die "Cannot open for $in input";
  
	while ($line=<INPUT_FILE>) {
		++$lineno;
		if ($line =~ /^\/\// ) {} # Ignore c++ commented lines.
		elsif ($line =~ /^\s*REM\s+/i) {} # Ignore REM commented lines.
		elsif ($line =~ /^\s*$/) { # Compress blank lines down to one
			if($lastblank) {
				# Do nothing
			}
			else {
				# This is the first blank line
				$lastblank=1;
				print OUTPUT_FILE $line;
			}
		}
		else {
			# Not blank
			my $epoc32_line = 0;
			$lastblank = 0;
			$line =~ s|\#\#||g; # Delete "token-pasting" ops
			$line =~ s|//.*$||g; # Delete trailing c++ comments
			# prefix the epocroot dir to occurrences of 'epoc32' in all "KEYWORD=..." lines.
			$line =~ s/(=\s*)[\\\/]epoc32/\1$epoc32path/i;
			$epoc32_line = defined($1);
			if (!$epoc32_line) {
				$line =~ /(=.*$epocroot_pattern)/i;
				$epoc32_line = defined($1);
			}
			if (!$epoc32_line) {
				if ($k and $line=~/^\s*kerneltrace/i) {
					$line = "kerneltrace $k\n";
				}
			}
			elsif ($on_windows) {
				$line =~ s|\/|\\|g;
			}
			elsif ($line =~ /(^bootbinary\s*=\s*${epocroot}epoc32)(\S+)$/) {
				# unixify the bootbinary line
				my $tail = $2;
				$line =~ s|\\|\/|g;
				$tail =~ s|\\|\/|g;
				my $lc_tail = lc($tail);
				$line =~ s|$tail|$lc_tail|;
			}
			elsif ($line =~ /^(\s*\S+\s*=\s*)(\S+)(\s*\S*)/) {
				#unixify the lefthand sides of rom-mapping lines.
				my $keyword_part = $1;
				my $src = $2;
				my $dest = $3;
				$dest =~ s/^\s+//;
				$dest =~ s/\s+$//;
				$src =~ s|\\|\/|g;
				if ($dest) {
					my ($vol,$dir,$leaf) = File::Spec->splitpath($src);
					my $lc_leaf = lc($leaf);
					my $lc_dir = lc($dir);
					$lc_dir =~ s/\/$//;
					$lc_dir =~ s|^$lc_epocroot|$epocroot|;
					my $fulldir = $lc_dir;	
					$fulldir =~ s|//|/|g;
					$dest =~ s|\/|\\|g;
					$dest = '\\' . $dest, unless ($dest =~ /^\\/);
					unless ( -d $fulldir ) {
						chomp $line;
						# Lower-cased source directory doesn't exist. Give up.
						die "Guessed source directory \"$fulldir\" does not exist for line $lineno: \"$line\"\n";
					}
					if (($leaf eq $lc_leaf) or (-f "$fulldir\/$leaf")) { 
						# Using source directory lowercase and source filename as input.
						$line = "${keyword_part}${lc_dir}\/${leaf}\t${dest}\n";
					}
					elsif ( -f "$fulldir\/$lc_leaf") {
						# Using source directory source filename both lowercase.
						$line = "${keyword_part}${lc_dir}\/${lc_leaf}\t${dest}\n";
					}
					else { # Harder.
						my @dirlist;
						my $found = 0;
						if (!defined($dir_listings{$fulldir})) {
							# Haven't got a cached dir listing for the source directory.
							# Make one now.
							@dirlist = glob("$fulldir.*");
							$dir_listings{$fulldir} = \@dirlist;
						}	
						@dirlist = @{dir_listings{$fulldir}}; # Get listing of source directory from cache.
						foreach my $file (@dirlist) {
							# See if any file in the source directory case-insensitively matches the input source file.
							if ( (-f "$fulldir\/$file") and (lc($file) eq $lc_leaf)) {
								$line = "${keyword_part}${lc_dir}\/${file}\t${dest}\n";
								$found = 1;
								last;
							}
						}
						unless ($found) {
							die "Cannot find any file case-insensitively matching \"$fulldir\/$leaf\" at line $lineno: \"$line\"\n";
						}
					}
				}
				else {
					$line =~ s|\\|\/|g;
				}								
			}
			print OUTPUT_FILE $line;
		}
	}
	close(INPUT_FILE);
	close(OUTPUT_FILE);
}

sub IsRVCTBuild($) {
    my ($build)=@_;
    return 1 if ($build =~ /^ARMV/i);
	my @customizations = Plat_Customizations('ARMV5');
	return 1 if (grep /$build/, @customizations);
	return 0;
}


sub IsSmp($) {
	my %SmpKernelDirs=(
		'x86smp' => 1,
		'x86gmp' => 1,
		'arm4smp' => 1,
		'armv4smp' => 1,
		'armv5smp' => 1
	);

	my ($kdir) = @_;
	return $SmpKernelDirs{lc $kdir};
}

sub checkopts {
	unless($opts{variant}) { die "No Variant specified"; }
	$opts{'build'}="UDEB" unless($opts{'build'});
	$opts{'type'}="TSHELL" unless($opts{'type'});
	$opts{'inst'}="ARM4" unless($opts{'inst'});

	my $additional;
	if ($opts{'modules'}) {
		$additional="_".$opts{modules};
		$additional=~ s/,/_/ig;
	}
	my $build=lc $opts{build};
	my $inst=uc $opts{'inst'};
	if ($inst eq "MARM") {
		# Hackery to cope with old compiler
		$main="MARM";
		$euserdir="MARM";
		$elocldir="MARM";
	}
	else {
		$main=$inst;
		if ($main eq "THUMB") {
			$euserdir="ARMI";
		} else {
			$euserdir=$main;
		}
		if ($main eq "ARMI" or $main eq "THUMB") {
			$elocldir="ARM4";
		} else {
			$elocldir=$main;
		}
	}
	$kmain = $opts{'xabi'};
	$kmain = $main unless ($kmain);
	if (IsSmp($kmain)) {
		$euserdir = $kmain;
	}
	if ($opts{name}) {
		$romname=$opts{name};
	} else {
		$romname=uc($opts{variant}.$additional.$main);
		if ($build=~/^\w*DEB$/i) {
			$romname.='D';
		}
		$romname.='.IMG';
	}
}

sub lookupFileInfo($$)
{
	my ($infile, $fullname) = @_;

	my ($name, $ext) = $fullname =~ /^(.+)\.(\w+)$/ ? ($1, $2) : ($fullname, undef);

	open TMP, $infile or die("Can't open $infile\n");
	while(<TMP>)
	{
		$_ = lc;
		if(/^\s*(\S+)\s*=\s*(\S+)\s+(\S+)/i)
		{
			my ($src, $dest) = ($2, $3);

			my $destFullname = $dest =~ /^.*\\(.+)$/ ? $1 : $dest;
			my ($destName, $destExt) = $destFullname =~ /^(.+?)\.(\w+)$/ ? ($1, $2) : ($destFullname, undef);

			if ($destName eq $name && (!$ext || $ext eq $destExt))
			{
				close TMP;
				return ($src, $dest);
			}
		}
	}

	die "patchdata: Can't find file $fullname\n";
}

sub lookupSymbolInfo($$)
{
	my ($file, $name) = @_;
	open TMP, $file or die "Can't read $file\n";

	# ignore local symbols.
	while (<TMP>)
	{
		last if /Global Symbols|Linker script and memory map/;
	}

  my @return_values = ();
  my $line;
	while ($line = <TMP>)
	{
		next if (index($line, $name) < 0);		
		
		# RVCT 2.2
		# 
		#     KHeapMinCellSize  0x0004e38c  Data 4  mem.o(.constdata)
		#
		if ($line =~ /^\s*(\S+)\s+(\S+)\s+data\s+(\S+)/i)
		{
			my ($symbol, $addr, $size) = ($1, $2, $3);
			if ($symbol eq $name)
			{
				@return_values = ($addr, $size);
				last;
			}
		}

		# This is a quick fix for RVCT 3.1, which uses the text "(EXPORTED)"
		# in the map file. Here is an example:
		#
		# KHeapMinCellSize (EXPORTED) 0x0003d81c Data 4 mem.o(.constdata)
		#
		elsif ($line =~ /^\s*(\S+)\s+\(exported\)\s+(\S+)\s+data\s+(\S+)/i)
		{
			my ($symbol, $addr, $size) = ($1, $2, $3);
			if ($symbol eq $name)
			{
				@return_values = ($addr, $size);
				last;
			}
		}
		
		# GCC 4.x map files
		#                 0x00114c68                KHeapMinCellSize
		#                 0x00114c6c                KHeapShrinkHysRatio
		#  .rodata        0x00115130      0x968 M:/epoc32/build/kernel/c_99481fddbd6c6f58/_omap3530_ekern_exe/armv5/udeb/heap_hybrid.o
		#
		elsif ($line =~ /^.+\s+(0x\S+)\s+(\S+)/i)
		{
			my ($addr, $symbol) = ($1, $2);
			if ($symbol eq $name)
			{
				my $next_line = <TMP>;
				if ($next_line =~ /^.+\s+(0x\S+)\s+(\S+)/i)
				{
					my $addr2 = $1;
					
					@return_values = ($addr, hex($addr2) - hex($addr));
					last;
				}
			}
		}

	}
	close TMP;

	die "patchdata: Can't find symbol $name\n" if (scalar @return_values == 0);
	return @return_values;
}

sub parsePatchData($$)
{
	my ($infile, $outfile) = @_;

	open IN, $infile or die("Can't read $infile\n");
	open OUT, ">$outfile" or die("Can't write $outfile\n");

	my $line;
	while($line = <IN>)
	{
		if ($line =~ /^\s*patchdata\s+(.+?)\s*$/i)
		{
			if ($1 !~ /(\S+)\s*@\s*(\S+)\s+(\S+)\s*$/)
			{
				die "Bad patchdata command: $line\n";
			}

			my ($file, $symbol, $value) = (lc $1, $2, $3);
			my ($srcFile, $destFile) = lookupFileInfo($infile, $file);
			my ($index, $elementSize) = (undef, undef);
			if ($symbol =~ s/:(\d+)\[(\d+)\]$//)
			{
				($index, $elementSize) = ($2, $1);
				$index = hex($index) if $index =~ /^0x/i;
			}

			if ($srcFile =~ /\\armv5(smp)?\\/i)
			{
				my ($symbolAddr, $symbolSize) = lookupSymbolInfo("$srcFile.map", $symbol);

				my $max;
				if (defined($index))
				{
					my $bytes;
					$bytes = 1, $max = 0xff       if $elementSize ==  8;
					$bytes = 2, $max = 0xffff     if $elementSize == 16;
					$bytes = 4, $max = 0xffffffff if $elementSize == 32;
					die("patchdata: invalid element size $elementSize: $line\n") unless defined($bytes);

					if ($bytes > 1 && (($symbolSize & ($bytes-1)) != 0))
					{
						die("patchdata: unexpected symbol size $symbolSize for array $symbol ($elementSize-bit elements)\n");
					}

					if ($index >= int($symbolSize / $bytes))
					{
						die("patchdata: index $index out of bounds for $symbol of $symbolSize bytes ($elementSize-bit elements)\n");
					}

					$symbolAddr = hex($symbolAddr) if $symbolAddr =~ /^0x/i;
					$symbolAddr += $index * $bytes;
					$symbolAddr = sprintf("0x%x", $symbolAddr);

					$symbolSize = $bytes;
				}
				elsif ($symbolSize == 1) { $max = 0xff; }
				elsif ($symbolSize == 2) { $max = 0xffff; }
				elsif ($symbolSize == 4) { $max = 0xffffffff; }
				else { die "patchdata: Unexpected symbol size $symbolSize for $symbol\n"; }

				$value = hex($value) if $value =~ /^0x/i;
				if ($value > $max)
				{
					print("Warning:  Value overflow of $symbol\n");
					$value &= $max;
				}					
				$value = sprintf("0x%08x", $value);

				$line = "patchdata $destFile addr $symbolAddr $symbolSize $value\n";
			}
			else
			{
				$line = "";
			}

		}

		print OUT $line;
	}

	close IN;
	close OUT;
}

sub genfile {
	my $count=0;
	if($_[0] eq 'paged') {
		my $file='gentestpaged.txt';
		unlink $file;
		open(OUTFILE, ">$file") or die "Can't open output file, $!";
		for(my $i=0;$i<50000;++$i) {
			if(($i >5) && ($i % 40 ==0)) {
			print OUTFILE "\n";
			$count++;
			} 
			if(($i+$count) % 5 ==0) {
			print OUTFILE "SATOR ";
			}
			if(($i+$count) % 5 ==1) {
			print OUTFILE "AREPO ";
			}
			if(($i+$count) % 5 ==2) {
			print OUTFILE "TENET ";
			}
			if(($i+$count) % 5 ==3) {
			print OUTFILE "OPERA ";
			}
			if(($i+$count) % 5 ==4) {
			print OUTFILE "ROTAS ";
			}
		}
	} else {
		my $file='gentestnonpaged.txt';
		unlink $file;
		open(OUTFILE, ">$file") or die "Can't open output file, $!";
		for(my $i=0;$i<20000;++$i) {
			if(($i >5) && ($i % 40 ==0)) {
			print OUTFILE "\n";
			$count++;
			} 
			if(($i+$count) % 4 ==0) {
			print OUTFILE "STEP ";
			}
			if(($i+$count) % 4 ==1) {
			print OUTFILE "TIME ";
			}
			if(($i+$count) % 4 ==2) {
			print OUTFILE "EMIT ";
			}
			if(($i+$count) % 4 ==3) {
			print OUTFILE "PETS ";
			}
		}
	}
}

sub Variant_GetMacroHRHFile {
	my $cfgFile =  File::Spec->catfile($toolpath,"variant","variant.cfg"); # default location
	# save the volume, if any.
	my ($cfg_vol,$ignore1,$ignore2) = File::Spec->splitpath($cfgFile);    
    my $file;
    if(-e $cfgFile){
		open(FILE, $cfgFile) || die "\nCould not open for reading: " . $cfgFile ."\n";
		while (<FILE>) {
			# strip comments
			s/^([^#]*)#.*$/$1/o;
			# skip blank lines
			if (/^\s*$/o) {
				next;
			}
			# get the hrh file
			if($_ =~ /\.hrh/xi){
				$file = $_; 
				last;
			}
		}
		close FILE;
		die "\nERROR: No variant file specified in $cfgFile!\n" unless $file;
		$file =~ s/\s+//g;
		$file =~ s|\\|\/|g, unless($on_windows);

		if ($on_windows) {
			if (File::Spec->file_name_is_absolute($file)) {
				my ($vol,$dir,$leaf) = File::Spec->splitpath($file);
				unless ( $vol) {
					$vol = substr $epocroot,0,2;
					$file = substr $file,1;                 
					$file = File::Spec->catfile($epocroot,$dir,$leaf);
				}
				die "\nERROR: Variant file specified in $cfgFile is not on the same volume as EPOCROOT\n", if (lc($vol) ne lc($cfg_vol));
			}
			else {
				$file = File::Spec->catfile($epoc32path,$file);
			}
		}
		elsif (File::Spec->file_name_is_absolute($file) && ! -e $file) {
			$file = File::Spec->catfile($epocroot,$file);
		} 

		unless(-e $file) {
			die "\nERROR: $cfgFile specifies $file which doesn't exist!\n";
		}

		$file =~ s/\//\\/g, if ($on_windows);
    }
    return $file;
}