diff -r 22486c9c7b15 -r 378360dbbdba imgtools/buildrom/tools/romutl.pm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/imgtools/buildrom/tools/romutl.pm Wed Jun 30 11:35:58 2010 +0800 @@ -0,0 +1,1697 @@ +# Copyright (c) 2006-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: +# +# Description: +# Collection of utilitiy functions which is copied from Symbian OS perl modules. +# It provides platform related information to ROM Tools including buildrom, +# features.pl, etc. +# + +package romutl; + +require Exporter; +@ISA=qw(Exporter); +@EXPORT=qw( + init_plat + init_bsfs + init_platwithbpabi + + get_epocroot + get_drive + get_epocdrive + get_versionedname + get_bpabiplatlist + get_platlist + get_platcustomizes + get_platroot + get_makeeabspath + get_variantmacrolist + get_variantmacroHRHfile + get_abiv2mode + get_variantfullpath + get_BVbinname + get_variant + + is_existinpath + + set_verbose + + check_varfile + split_path + append_driveandquote + write_32bit +); + + +# EPOCROOT with proper format +my $epocroot; +my $epocdrive = ""; +my $drive = ""; + +BEGIN { + require 5.005_03; # check user has a version of perl that will cope + + $epocroot = $ENV{EPOCROOT}; + $epocroot = "\/" if (!$epocroot); # use "\" if EPOCROOT is not specified + $epocroot =~ s/\\/\//g; + $epocroot .= "\/" unless $epocroot =~ /\/$/; +} + +use strict; +use Cwd; +use File::Spec; +use romosvariant; + +my $verbose=0; + +######################## +#Init +# + +#die "ERROR: EPOCROOT must specify an existing directory." if (!-d $epocroot); +#die "ERROR: EPOCROOT must not be a UNC path\n" if ($epocroot =~ /^\\\\/); + +$drive=$1 if (cwd =~ /^(.:)/); + +if ($epocroot =~ /^(.:)/) +{ + $epocdrive=$1; +} +else +{ +# current working directory is different to SDK's + $epocdrive=$drive +} + +##################################### +# General functions +# + +sub get_epocroot +{ + return $epocroot; +} + +sub get_epocdrive +{ + return $epocdrive; +} + +sub get_drive +{ + return $drive; +} + +sub set_verbose +{ + $verbose = shift; +} + +use constant QUIET_NOT_FOUND => 0; # return 0 if file not found in the PATH +use constant DIE_NOT_FOUND => 1; # issue error and die if file not found in the PATH +use constant ERROR_NOT_FOUND => 2; # issue error and return 0 if file not found in the PATH + +# Simulate shell to locate executable file in the PATH +# +# WARNING: don't use this func in a deep loop because of the +# less efficient implementation +# +# usage: is_existinpath [] +# +# flag == DIE_NOT_FOUND die and display error when not found +# flag == ERROR_NOT_FOUND display error and return 0 when not found +# else return 0 when not found +# return 1 when found + +sub is_existinpath +{ + my ($filename, $flag)=@_; + return 0 unless defined $filename; + return 0 if ($filename =~ /\\/); + return 0 if ($filename =~ /\//); + + my @paths; + my $delimiter = &env_delimiter; + @paths = split(/$delimiter/, $ENV{PATH}); + unshift @paths, "\."; + + foreach my $path (@paths) + { + next if ($path =~ /^\s*$/); + chomp $path; + $path =~ s/\\/\//g; + $path .= "\/" unless ($path =~ /\/$/); + $path = $path.$filename; + foreach my $ext ("", ".bat", ".cmd", ".exe", ".com", ".pl", ".py") + { + return 1 if (-e $path.$ext); + } + } + die "Error: Cannot found \"$filename\" in the PATH.\n" if ($flag == DIE_NOT_FOUND); + print "Error: Cannot found \"$filename\" in the PATH.\n" if ($flag == ERROR_NOT_FOUND); + return 0; +} + +######################################### +# Symbian variant functions and variables +# +# copy from e32variant.pm +# + +my $toolspath = $epocroot . "epoc32\/tools\/"; +# SPPR begin +# enable includation of spp_variant.cfg if it exist +my $spp_cfgFile = $toolspath . "variant\/spp_variant.cfg"; +my $cfgFile = $toolspath . "variant\/variant.cfg"; # default location +$cfgFile = $spp_cfgFile if -e $spp_cfgFile; # use spp_variant.cfg +# SPPR End + +my $variantABIV2Keyword = &get_abiv2mode; # if variant ABIv2 mode enabled + +my $hrhdrive = $epocdrive; # variant hrh drive +my $hrhfile; # variant hrh file +my @macros; # variant macros + +if ($cfgFile =~ /^(.:)/i) +{ + $hrhdrive = lc($1); +} + +# returns the variant specific macro definitions as a list +sub get_variantmacrolist{ + + return @macros if (@macros); + + my $vfile = get_variantmacroHRHfile(); + + if($vfile) + { + my $VariantFilePath = split_path('Path',$vfile); + chop( $VariantFilePath ); + $VariantFilePath = &append_driveandquote($VariantFilePath); + $vfile = &append_driveandquote($vfile); + my $e32Path = &append_driveandquote($epocroot."epoc32\/include"); + + open CPPPIPE,"cpp -I $e32Path -I $VariantFilePath -undef -dM $vfile |" or die "ERROR: Can't invoke CPP.EXE\n"; + while(){ + if($_ =~ /(\#define)(\s+)(.+)/){ + push @macros, $3; + } + } + close CPPPIPE; + } + return @macros; +} + +# return hrh filename defined in variant cfg file +# notice: abort if hrh file located in different drive to cfg file +sub get_variantmacroHRHfile{ + + return $hrhfile if ($hrhfile); + if(-e $cfgFile){ + open(FILE, $cfgFile) || die "\nCould not open: " . $cfgFile ."\n"; + while () { + # strip comments + s/^([^#]*)#.*$/$1/o; + # skip blank lines + if (/^\s*$/o) { + next; + } + # get the hrh file + if($_ =~ /\.hrh/xi){ + $hrhfile = $_; + last; + } + } + close FILE; + die "\nERROR: No variant file specified in $cfgFile!\n" unless $hrhfile; + $hrhfile =~ s/\s+//g; + $hrhfile=~s/^(.:)//io; # remove drive letter + my $paths_drive = lc($1); + + chomp $hrhfile; + $hrhfile = get_makeeabspath($epocroot."epoc32\/", $epocroot, $hrhfile); # assume relative to EPOCROOT + + + if($paths_drive){ + die "\nERROR: Variant file specified in $cfgFile is not on the same drive as \/epoc32\/\n" + unless ($paths_drive eq $hrhdrive); + } + die "\nERROR: $cfgFile specifies $hrhfile which doesn't exist!\n" unless (-e $hrhfile); + + # make sure it is in unix syntax + $hrhfile=~ s/\\/\//g; + } + return $hrhfile; +} + +# get status of EANBLE_ABIV2_MODE +# 1=enabled 0=disabled +sub get_abiv2mode{ + + return $variantABIV2Keyword if (defined $variantABIV2Keyword); + + $variantABIV2Keyword=0; + if(-e $cfgFile){ + open(FILE, $cfgFile) || die "\nCould not open: " . $cfgFile ."\n"; + while () { + # strip comments + s/^([^#]*)#.*$/$1/o; + # skip blank lines + if (/^\s*$/o) { + next; + } + # get the hrh file + if($_ =~ /^ENABLE_ABIV2_MODE$/xi){ + $variantABIV2Keyword=1; + last; + } + } + close FILE; + } + + return $variantABIV2Keyword; +} + +############################# +# Path utilities +# +# copy from pathutl.pm +# + +#args: $_[0] Start EPOCPath Abs FilePath/Path $_[1]... list of (Abs/Rel FilePath/Path) +# Variant of MakAbs which also maps "+\\" to "${EPOCPath}" +sub get_makeeabspath ($@) { + return undef unless $_[0]=~m-^(.:)?[\\\/]-o; + my ($EPOCPath,$Path,@List)=@_; + my $BasePath=&split_path("Path",$Path); + undef $Path; + my $p; + foreach $p (@List) { + $p =~ s-\\-\/-g; + if ($p=~m-^\/?epoc32\/(.*)$-io) { # change - special case for existing \\epoc32 references + $p=$EPOCPath.$1; + next; + } + if ($p=~m-^\s*\+\/(.*)$-o) { + $p=$EPOCPath.$1; + next; + } + if ($p=~m-^\.{2}-o) { + $p=&strip_path($BasePath.$p); + next; + } + if ($p=~m-^[^\.\/]-o) { + $p=$BasePath.$p unless ($p =~ m-^.:-o); + next; + } + if ($p=~m-^(.:)?\/-o) { + next; + } + if ($p=~m-^\.\/(.*)$-o) { + $p=&strip_path($BasePath.$1); + next; + } + return undef; + } + return wantarray ? @List : $List[0]; +} + +#args: $_[0] Abs FilePath/Path +# Remove excess occurrences of '..' and '.' from a path +sub strip_path ($) { + return undef unless $_[0]=~m-^(.:)?[\/\\]-o; + my $P=$_[0]; + while ($P=~s-([\/\\])\.[\/\\]-$1-go) { } + while ($P=~s-[\\](?!\.{2}\\)[^\\]*\\\.{2}(?=\\)--go) { } + $P; +} + +#args: $_[0] 'Path' or 'Base' or 'Ext' $_[1] Abs/Rel FilePath/Path +# return the section of a file path required - Path, Base, Ext or File +sub split_path ($$) { + my ($Sect,$P)=@_; + + return '' if !$P; + $Sect= ucfirst lc $Sect; + if ($Sect eq 'Path') { + if ($P=~/^(.*[\\\/])/o) { + return $1; + } + return ''; + } + undef; +} + +sub append_driveandquote ($) { +# Take a path, or list of paths, and prefix with drive based on 1. epocroot, 2.CWD. +# Relative paths are just quoted. + my @List=@_; + my $Path; + + + foreach $Path (@List) { + next if ($Path !~ /^[\/\\]/); # skip prefix with drive letter or relative path + $Path=$epocdrive.$Path; + } + + foreach $Path (@List) { + chomp $Path; + $Path="\"".$Path."\""; + } + + return wantarray ? @List : $List[0]; +} + + +############################### +# General Utilities +# +# copy from genutl.pm +# + +# return name with well formated version id in hex +sub get_versionedname($) { + my ($name) = @_; + if ($name =~ /(.*)\{\s*(\d+)\s*\.\s*(\d+)\s*\}(.*?)$/i) { + my $a = $1; + my $b = $4; + my $major = $2; + my $minor = $3; + return $a.sprintf("{%04x%04x}",$major,$minor).$b if ($major<32768 and $minor<32768); + } + return $name; +} + + +############################### +# BPABI Platform Utilities +# +# copy from bpabiutl.pm +# + +my @BPABIPlats; + + +# Identify the BPABI platforms to be supported based on the compiler configuration files +# present in the location specified by the environment variable "SYMBIAN_COMPILATION_CONFIG_DIR" +# and in the directory $EPOCROOT\epoc32\tools\compilation_config +sub get_bpabiplatlist +{ + return @BPABIPlats if (scalar(@BPABIPlats)); + + my @CompilerConfigPath; + + if (exists($ENV{'SYMBIAN_COMPILATION_CONFIG_DIR'})) + { + my $Path = $ENV{SYMBIAN_COMPILATION_CONFIG_DIR}; + @CompilerConfigPath = split(/;/, $Path); + } + + push @CompilerConfigPath, "${epocroot}epoc32\/tools\/compilation_config"; + + my $ConfigDir; + + foreach $ConfigDir (@CompilerConfigPath) + { + opendir DIR, "$ConfigDir"; + my @Plats=grep /\.mk$/i, readdir DIR; + my $Plat; + foreach $Plat (@Plats) + { +# The platform name will be same as the name of the configuration file +# with the suffix '.mk' removed + $Plat =~ s/\.mk//; + if ($variantABIV2Keyword) { + if ($Plat =~ /^armv5_abiv2$/i) { + $Plat = "ARMV5"; + } + } + else { + if ($Plat =~ /^armv5$/i) { + $Plat = "ARMV5_ABIV2"; + } + } + unless (grep /$Plat$/i, @BPABIPlats) { + $Plat = uc $Plat; + push @BPABIPlats, $Plat; + } + } + } + closedir DIR; + return @BPABIPlats; +} + +############################# +# Platform Utilities +# +# copy from e32plat.pm +# +my %Plat=( + ARM4=>{ + ABI=>'ARM4', + ASSP=>'MARM', + ASSPABI=>'', + Generic=>1, + }, + ARM4SMP=>{ + ABI=>'ARM4', + ASSP=>'MARM', + ASSPABI=>'', + Generic=>1, + SMP=>1, + StatLink=>'ARM4SMP', + }, + ARM4T=>{ + ABI=>'ARM4T', + ASSP=>'MARM', + ASSPABI=>'', + Generic=>1, + }, + ARMI=>{ + ASSP=>'MARM', + Generic=>1, + ASSPABI=>'', + }, + SARM4=>{ + ABI=>'ARM4', + ASSP=>'MARM', + ASSPABI=>'', + Generic=>1, + Single=>1, + }, + SARMI=>{ + ASSP=>'MARM', + ASSPABI=>'', + Generic=>1, + Single=>1, + }, + STHUMB=>{ + ABI=>'THUMB', + ASSP=>'MARM', + ASSPABI=>'', + Generic=>1, + Single=>1, + }, + THUMB=>{ + ABI=>'THUMB', + ASSP=>'MARM', + ASSPABI=>'', + Generic=>1, + }, + TOOLS=>{ + ABI=>'TOOLS', + ASSPABI=>'', + Compiler=>'VC32', + CPU=>'TOOLS', + OS=>'TOOLS', + MakeMod=>'Cl_win', + MakeCmd=>'nmake', + }, + TOOLS2=>{ + ABI=>'TOOLS2', + ASSPABI=>'', + Compiler=>'GCC32', + CPU=>'TOOLS2', + OS=>'TOOLS2', + MakeMod=>'Cl_mingw', + MakeCmd=>'make', + }, + CWTOOLS=>{ + ABI=>'TOOLS', + ASSPABI=>'', + Compiler=>'CW32', + CPU=>'TOOLS', + OS=>'TOOLS', + MakeMod=>'Cl_tools', + MakeCmd=>'make', + }, + VC6TOOLS=>{ + ABI=>'TOOLS', + ASSPABI=>'', + Compiler=>'VC32', + CPU=>'TOOLS', + Ext=>'.DSP', + MakeMod=>'Ide_vc6', + MakeCmd=>'nmake', + OS=>'TOOLS', + Real=>'TOOLS', + UsrHdrsOnly=>1, + }, + WINS=>{ + ABI=>'WINS', + ASSPABI=>'', + Compiler=>'VC32', + CPU=>'WINS', + MakeMod=>'Cl_win', + MakeCmd=>'nmake', + OS=>'WINS', + }, + VC6=>{ + ABI=>'WINS', + ASSPABI=>'', + Compiler=>'VC32', + CPU=>'WINS', + Ext=>'.DSP', + MakeMod=>'Ide_vc6', + MakeCmd=>'nmake', + OS=>'WINS', + Real=>'WINS', + UsrHdrsOnly=>1, + }, + WINSCW=>{ + ABI=>'WINSCW', + ASSPABI=>'', + Compiler=>'CW32', + CPU=>'WINS', + MakeMod=>'Cl_codewarrior', + OS=>'WINS', + DefFile=>'WINS', # use the MSVC def files + }, + CW_IDE=>{ + ABI=>'WINSCW', + ASSPABI=>'', + Compiler=>'CW32', + CPU=>'WINS', + Ext=>'.xml', + MakeMod=>'Ide_cw', + MakeCmd=>'make', + OS=>'WINS', + Real=>'WINSCW', + DefFile=>'WINS', # use the MSVC def files + UsrHdrsOnly=>1, + SupportsMultiplePlatforms=>1, # supports more than one real platform + }, + X86=>{ + ABI=>'X86', + ASSPABI=>'', + Compiler=>'VC32', + CPU=>'X86', + MakeMod=>'Cl_x86', + MakeCmd=>'nmake', + OS=>'EPOC32', + DefFile=>'X86', + Generic=>1, + }, + X86SMP=>{ + ABI=>'X86', + ASSPABI=>'', + Compiler=>'VC32', + CPU=>'X86', + MakeMod=>'Cl_x86', + MakeCmd=>'nmake', + OS=>'EPOC32', + DefFile=>'X86', + Generic=>1, + SMP=>1, + StatLink=>'X86SMP', + }, + X86GCC=>{ + ABI=>'X86gcc', + ASSPABI=>'', + Compiler=>'X86GCC', + CPU=>'X86', + MakeMod=>'Cl_x86gcc', + OS=>'EPOC32', + DefFile=>'x86gcc', + Generic=>1, + }, + X86GMP=>{ + ABI=>'X86gcc', + ASSPABI=>'', + Compiler=>'X86GCC', + CPU=>'X86', + MakeMod=>'Cl_x86gcc', + OS=>'EPOC32', + DefFile=>'x86gcc', + Generic=>1, + SMP=>1, + StatLink=>'X86GMP', + }, + ARMV4=>{ + ABI=>'ARMV4', + ASSP=>'MARM', + ASSPABI=>'', + Generic=>1, + MakeMod=>'Cl_arm', + Compiler=>'ARMCC', + DefFile=>'EABI', + EABI=>1, + }, + ARMV4SMP=>{ + ABI=>'ARMV4', + ASSP=>'MARM', + ASSPABI=>'', + Generic=>1, + MakeMod=>'Cl_arm', + Compiler=>'ARMCC', + DefFile=>'EABI', + EABI=>1, + SMP=>1, + StatLink=>'ARMV4SMP', + }, + ARMV5_ABIV1=>{ + ABI=>'ARMV5', + ASSP=>'MARM', + ASSPABI=>'', + Generic=>1, + MakeMod=>'Cl_arm', + Compiler=>'ARMCC', + DefFile=>'EABI', + EABI=>1, + SupportsFeatureVariants=>1, + }, + ABIV2=>{ + ABI=>'ARMV5', + ASSP=>'MARM', + ASSPABI=>'', + Generic=>1, + MakeMod=>'Cl_bpabi', + DefFile=>'EABI', + EABI=>1, + SupportsFeatureVariants=>1, + }, + GCCXML=>{ + ABI=>'ARM4', + ASSP=>'MARM', + ASSPABI=>'', + Generic=>1, + MakeMod=>'cl_gccxml', + }, + VS6=>{ + ABI=>'WINSCW', + ASSPABI=>'', + Compiler=>'CW32', + CPU=>'WINS', + MakeMod=>'Cl_vscw', + OS=>'WINS', + Real=>'WINSCW', + DefFile=>'WINS', # use the MSVC def files + Ext=>'.mak' + }, + VS2003=>{ + ABI=>'WINSCW', + ASSPABI=>'', + Compiler=>'CW32', + CPU=>'WINS', + MakeMod=>'Cl_vscw', + OS=>'WINS', + Real=>'WINSCW', + DefFile=>'WINS', # use the MSVC def files + Ext=>'.mak' + }, + EDG=>{ + ABI=>'ARMV5', + ASSP=>'MARM', + ASSPABI=>'', + Generic=>1, + MakeMod=>'cl_edg', + }, + + # ASSP platforms should be described using .ASSP files + # Do not add additional ASSP platforms to this file. +); + +my $init_bsfs_done = 0; +my $init_plat_done = 0; +my @PlatList; # Platlist returned by list_plat() + +# initialize BSF platforms into %Plat +sub init_bsfs($) { + return $init_bsfs_done if ($init_bsfs_done); + + my ($Path)=@_; +# get a list of modules + opendir DIR, $Path; + my @BSFs=grep s/^([^\.].*)\.BSF$/$1/, map { uc $_ } sort readdir DIR; + closedir DIR; + + my $BSF; + foreach $BSF (@BSFs) { + my $File=$Path.lc($BSF).'.bsf'; +# check whether the assp is already defined + if (defined %{$Plat{$BSF}}) { + warn( + "$File : warning: Platform \"$BSF\" already defined\n", + " ... skipping this spec\n" + ); + delete $Plat{$BSF}; + next; + } +# open the module + unless (open FILE, $File) { + delete $Plat{$BSF}; + warn "warning: Can't open BSF specification \"$File\"\n"; + next; + } + my $line1 = ; + $line1 = uc($line1); + unless ($line1 =~ /^\#\\#/) { + warn "warning: \"$File\" Invalid BSF specification - missing ##\n"; + delete $Plat{$BSF}; + close FILE; + next; + } + my $custom; + while ($custom = ) { + #skip blank lines and comments + delete $Plat{$BSF}; + last unless ($custom =~ /^$|^\#/); + } + $custom = uc $custom; + unless ($custom =~ /^\s*CUSTOMIZES\s+(\S+)/) { + warn "warning: \"$File\" Invalid BSF specification - 'customizes' missing\n"; + delete $Plat{$BSF}; + close FILE; + next; + } + my $root = $1; + my $platname = ''; + my $CustomizedPlatName = ''; + + # In v1 mode, ARMV5 platform implies ARMV5_ABIV1 platform listed in the platlist + my $Armv5Flag = 0; + if (!$variantABIV2Keyword && $root =~ /^ARMV5$/i) { + $Armv5Flag = 1; + } + + # Support for Hierarchy of Customizations (BSF file customization of another BSF file) + # 1. Check whether the BSF file customizes another BSF file. + # 2. If so, check whether the root BSF file has already been read. + # 3. If not read, then defer the current BSF file reading until the root file is read. + my $rootPlatFound = 0; + if (defined %{$Plat{$root}} || $Armv5Flag) + { + # BSF platform customizes another valid BSF platform + if (defined $Plat{$root}{'CUSTOMIZES'}) + { + $rootPlatFound = 1; + $platname = $root; + $CustomizedPlatName = $root; + + # Set the root platform name which is same as of customizes platform + $Plat{$BSF}{'ROOTPLATNAME'} = $Plat{$root}{'ROOTPLATNAME'}; + } + # BSF platform customizes to one of the existing ABI platforms + else + { + # All BPABI platforms inherits from ABIV2 platform listed in the platlist + if (grep /^$root$/i, @BPABIPlats) { + $platname = "ABIV2"; + } + elsif ($Armv5Flag) { + # In v1 mode, ARMV5 platform implies ARMV5_ABIV1 platform listed in the platlist + $platname = "ARMV5_ABIV1"; + } + else { + $platname = $root; + } + + $CustomizedPlatName=$root; + + # BSF File check Begins + # The following check is included to handle the existing BSF files which has to behave in different manner + # in default v1 mode and v2 mode. The following code changes the BSF name and the custmoized platform name + # to the implied names. This is done to support switching between v1 and v2 modes by enabling the keyword in + # the variant configuration file. + # In v1 mode, the ARMV6_ABIV1 => ARMV6 platform and ARMV6 => ARMV6_ABIV2 platform. + if (!$variantABIV2Keyword) { + if ($BSF =~ /^ARMV6_ABIV1$/i) { + $BSF = "ARMV6"; + $CustomizedPlatName = "ARMV5"; + } + elsif ($BSF =~ /^ARMV6$/i) { + $BSF = "ARMV6_ABIV2"; + $CustomizedPlatName = "ARMV5_ABIV2"; + $platname = "ABIV2"; + } + } + # BSF File check Ends + + # Set the root platform name + $Plat{$BSF}{'ROOTPLATNAME'} = $CustomizedPlatName; + } + } + else + { + my $rootbsf = $Path.$root.".bsf"; + if ( -e $rootbsf ) { + # BSF file customizes another BSF file which has not been read yet. + # So defer current BSF file reading until the root BSF file is read. + delete $Plat{$BSF}; + push(@BSFs, $BSF); + next; + } + } + # If the customizes platform is not a valid BSF platform or BPABI platorm or ARMV5 or ARMV5_ABIV1, + # then throw warning. + unless ($rootPlatFound || $root =~ /^ARMV5(_ABIV1)?$/ || (grep /^$root$/i, @BPABIPlats)) { + warn "warning: \"$File\" Invalid BSF specification - customization restricted to ARMV5, ABIv2 and valid BSF platforms\n"; + close FILE; + delete $Plat{$BSF}; + next; + } + + my ( $key, $value); + while (($key, $value) = each %{$Plat{$platname}}) { + $Plat{$BSF}{$key}=$value; + } + + push @{$Plat{$CustomizedPlatName}{'CUSTOMIZATIONS'}}, $BSF; + $Plat{$BSF}{'CUSTOMIZES'} = $CustomizedPlatName; + while () { + next if (/^$|^\#/); + if (/^\s*SMP\s*$/i) { + $Plat{$BSF}{'SMP'} = 1; + $Plat{$BSF}{'StatLink'} = lc $BSF; + next; + } + $Plat{$BSF}{'CUSTOMIZATION_DATA'} .= $_; + } + # BSF file statements will have newline character("\n") at the end, except for the last statement. + # So append "\n" for the last BSF file statement. + # "\n" will be used to split BSF statements to support hierarchy of customizations. + $Plat{$BSF}{'CUSTOMIZATION_DATA'} .= "\n"; + close FILE; + } + $init_bsfs_done = 1; +} + +# setup Plat with bpabi platforms +sub init_platwithbpabi() +{ + foreach my $Candidate (&get_bpabiplatlist) + { +# All BPABI platforms inherit from ABIV2 properties as listed in the platlist +# and Platlist is updated to include the BPABI platforms. + my ( $key, $value); + while (($key, $value) = each %{$Plat{ABIV2}}) { + $Plat{$Candidate}{$key}=$value; + } + } +} + +# initialize %Plat with BSF/Bpabi/ASSP +sub init_plat ($) { # takes path to ASSP modules + + return $init_plat_done if ($init_plat_done); + + my ($Path)=@_; + + my %PlatHashKeys=( + ABI=>1, + ASSPABI=>1, + SINGLE=>1, + Compiler=>1, + CPU=>1, + MakeMod=>1, + MakeCmd=>1, + OS=>1, + DefFile=>1, + ASSP=>1, + ); + +# Include the list of BPABI platforms + &init_platwithbpabi; + + init_bsfs($Path); + +# get a list of modules + opendir DIR, $Path; + my @_ASSPs=grep s/^([^\.].*)\.ASSP$/$1/, map { uc $_ } readdir DIR; + closedir DIR; + + my @ASSPs; + foreach (@_ASSPs) { + next if (!$ENV{USEARMCC} and /EDG$/i); + push @ASSPs, $_; + } + +# open each module in turn, and add it to the array + my $ASSP; + foreach $ASSP (@ASSPs) { + my $File=$Path.$ASSP.'.assp'; +# check whether the assp is already defined + if (defined %{$Plat{$ASSP}}) { + warn( + "$File : warning: ASSP \"$ASSP\" already defined\n", + " ... skipping this module\n" + ); + + next; + } +# open the module + unless (open FILE, $File) { + warn "warning: Can't open assp module \"$File\"\n"; + next; + } + my %Data=(); + my %SingleData=(); + my $MatchingSingle=""; + my @Errors=(); + while () { +# strip comments + s/^([^#]*)#.*$/$1/o; +# skip blank lines + if (/^\s*$/o) { + next; + } +# get the key-value pair + unless (/^\s*(\w+)\s+(\w+)\s*$/o) { + push @Errors, "$File($.) : warning: syntax error - only key-value pairs allowed\n"; + next; + } + my ($Key, $Val)=($1, $2); + if ($PlatHashKeys{$Key}!=1) { + push @Errors, "$File($.) : warning: unrecognized keyword - $Key\n"; + next; + } + if ($Key eq "SINGLE") { + $SingleData{Single} = 1; + $SingleData{ASSP} = $ASSP; + $MatchingSingle = uc $2; + } else { + $Data{$Key}=$Val; + $SingleData{$Key}=$Val; + } + } + close FILE; + if (@Errors) { + warn( + @Errors, + " ... skipping this module\n" + ); + next; + } +# change - Allow ASSPs to pick up all the options of the ABI they specify, +# in particular the compiler they need. + $Data{'ASSP'} = $ASSP unless $Data{'ASSP'}; + if ($Plat{$Data{'ABI'}}) { + foreach (keys %{$Plat{$Data{'ABI'}}}) { + $Data{$_} = $Plat{$Data{'ABI'}}{$_} unless ($_ =~ /^GENERIC$/i) or $Data{$_}; + } + } + + %{$Plat{$ASSP}}=%Data; + if ($MatchingSingle ne "") { + foreach (keys %Data) { + $SingleData{$_} = $Data{$_} unless ($_ =~ /^GENERIC$/i) or $SingleData{$_}; + } + %{$Plat{$MatchingSingle}}=%SingleData; + } + } + $init_plat_done=1; +} + +# return list of supported platforms +# should be invoked atfer init_plat +sub get_platlist () { + + return @PlatList if (scalar(@PlatList)); + + &init_plat; + + my $Key; + foreach $Key (keys %Plat) { + if (!$variantABIV2Keyword && $Key =~ /^armv5_abiv1$/i) { + $Key = 'ARMV5'; + } + unless (grep /^$Key$/i, @PlatList) { + push @PlatList, $Key; + } + } + return @PlatList +} + +# return customizes BSF plat if any +sub get_platcustomizes($) { + my ($plat) = @_; + return $Plat{$plat}{'CUSTOMIZES'} ? $Plat{$plat}{'CUSTOMIZES'} : ""; +} + +# return root of a specific plat +sub get_platroot($) { + my ($plat) = @_; + + my $RootName = $Plat{$plat}{'ROOTPLATNAME'}; + + if ($RootName) { + return $RootName; + } + else { + # A non-BSF platform is its own root. + return $plat; + } +} + +################################# +# featurevariant map functions +# +# copy from featurevariantmap.pm + +my $featureListDir = "${epocroot}epoc32\/include\/variant\/featurelists"; + +# Usage: get_BVbinname("my.dll", "myvar") +# +# Look for a binary using its "final" name. We will use the feature +# variant map and the feature variant name to deduce the "variant" +# binary name and test for its existence. +# +# "my.dll" - the final target (full path) +# "myvar" - the feature variant name +# +# returns the file name if found, or "" otherwise. + +sub get_BVbinname +{ + my $binName = shift; + my $varName = shift; + + # look for the vmap file + my $vmapFile = "$binName.$varName.vmap"; + + if (! -e $vmapFile) + { + # compatible to old BV + $vmapFile = "$binName.vmap"; + } + + if (-e $vmapFile) + { + my $key = get_vmapkey($varName, $vmapFile); + + if ($key) + { + $binName =~ /^(.*)\.([^\.]*)$/; + $binName = "$1.$key.$2"; + } + else + { + print "ERROR: No \'$varName\' variant for $binName in $vmapFile\n"; + return ""; # file not found + } + } + + # check that the actual binary exists + if (-e $binName) + { + return $binName; + } + return ""; # file not found +} + +# internal functions +sub get_vmapkey +{ + my @res = get_vmapdata(@_); + return $res[0]; +} + +# Usage: featurevariantmap->GetDataFromVMAP("myvar", "mydll.vmap") +# +# Opens the vmap file indicated and returns the data for the requested variant +# +# "myvar" - the feature variant name +# "my.vmap" - the final target vmap file (full path) +# +# Returns a list ( hash, features ) for the variant in the vmap or undef if not found + +sub get_vmapdata +{ + my $varName = shift; + my $fileName = shift; + + if (!open(VMAP, $fileName)) + { + print "ERROR: Could not read VMAP from $fileName\n"; + return ""; + } + while () + { + chomp; + if (/(\w{32})\s+$varName\s+(.*)$/i or /(\w{32})\s+$varName$/i) + { + my ( $hash, $features ) = ( $1, $2 ? $2 : '' ); + close(VMAP); + return ( $hash, $features ); + } + } + close(VMAP); + return; +} + +###################################### +# Feature variant parser +# +# copy from featurevariantparser.pm +# + + +# Parses .VAR files and returns key variables. +# The following hashes can be used with this module: +# NAME -> Returns the name of the variant file (without the extension) +# FULLPATH -> Returns the full path of the variant file (including the extension) +# VALID -> Set to 1 if the variant file is valid, otherwise set to 0 +# VIRTUAL -> Set to 1 if the variant is a grouping node, otherwise set to 0 +# ROM_INCLUDES -> Returns a pointer to the list of ROM_INCLUDES (including Parent nodes). +# VARIANT_HRH -> Returns the full VARIANT_HRH file path used by the VAR file. + + +my $defaultDir = "${epocroot}epoc32\/tools\/variant"; +my $pathregex = '.+[^\s]' ; # Regex to match all characters (including \ or /), excluding whitespaces. + +my @rominclude; +my @parents; +my @childNodes; +my $virtual; +my $childNodeStatus; +my $varianthrh; + +my $dir; #var directory +my $fullpath; #full path of var file +my $fulldir; # + +# Wrapper function to return all the correct variables +# Arguments : (Variant Name, Variant Directory(optional)) +# Returns a Hash. +sub get_variant +{ + @rominclude = (); + @parents = (); + @childNodes = (); + $dir = ""; + $fullpath = ""; + $varianthrh = ""; + $virtual = 0; + $childNodeStatus = 0; + + + my %data; + my $romincs = ""; + + $data{'VALID'} = 0; + + my ( $varname, $dirname ) = @_; + + my $fullvarpath = get_variantfullpath( $varname, $dirname ); + + if ( $dirname ) + { + $fulldir = $dirname; + } + else + { + $fulldir = $defaultDir; + } + + $data{'FULLPATH'} = "$fullvarpath"; + $data{'NAME'} = "$varname"; + + # If the variant file exists, check the syntax and setup variables. + if ( -e $fullvarpath ) + { + if ( check_varfile( $fullvarpath, $varname ) ) + { + $data{'VALID'} = 1; + } + } + else + { + print "ERROR: $fullvarpath" . " does not exist\n"; + } + + my $count = 0; + + # If VAR file is valid, setup all other variables. + if ( $data{'VALID'} ) + { + + $romincs = find_varrominc($fullvarpath); + + # Remove empty elements from the ROM_INCLUDE list + @$romincs = grep /\S/, @$romincs; + + # Fix paths for all ROM_INCLUDES + for ( my $i = 0 ; $i < scalar(@$romincs) ; $i++ ) + { + @$romincs[$i] = get_fixpath( @$romincs[$i] ); + } + + $data{'ROM_INCLUDES'} = clone_list($romincs); + $data{'VARIANT_HRH'} = $varianthrh; + $data{'VIRTUAL'} = $virtual; + } + + # If variant file is not valid, return reference to a blank array + else + { + $data{'ROM_INCLUDES'} = []; + $data{'VARIANT_HRH'} = ""; + } + + return %data; +} + +# Method to construct a full variant path from the variant file and directory +sub get_variantfullpath +{ + + my $vardirectory = $_[1]; + my $varname = $_[0]; + + my $dir; + + # Check if a directory is supplied + if ($vardirectory) + { + $dir = "$vardirectory"; + } + + else + { + $dir = $defaultDir; + } + my $filename = "$varname" . "\.var"; + $fullpath = File::Spec->catfile( File::Spec->rel2abs($dir), $filename ); + + if ( !File::Spec->file_name_is_absolute($fullpath) ) + { + $fullpath = File::Spec->rel2abs($fullpath); + } + + return $fullpath; +} + +# Checks the variant file for the correct syntax and reports any errors +# Also sets up some variables(VIRTUAL ,VARIANT_HRH and VARIANT) whilst file is being parsed. + +# Usage: check_varfile(,) . Note: without .var +sub check_varfile +{ + + my $fullpath = $_[0]; + my $varname = $_[1]; + my $varianthrhpresent = 0; + + open( READVAR, "<$fullpath" ); + my $exp = "#"; + my $line = ""; + + while () + { + s/\r\n/\n/g; + + $line = $.; + + # Checks for a valid argument supplied to EXTENDS keyword. Checks for one and only one argument supplied. + if (/^EXTENDS/) + { + if ( !m/^EXTENDS\s+./ ) + { + print "\nERROR: Invalid format supplied to argument EXTENDS on line " + . "$." + . " in file " + . "$fullpath"; + return 0; + } + my $str = get_extends($_); + if ( $str =~ /\s+/ ) + { + print "\nERROR: Cannot extend from two nodes. Error in line " + . "$." + . " in file " + . "$fullpath"; + return 0; + } + + $childNodeStatus = 1; + } + + # Checks for the grammar of BUILD_INCLUDE, i.e. KEYWORD MODIFIER VALUE + elsif (/^BUILD_INCLUDE/) + { + # skip build inc checking + } + + # Checks for the grammar of ROM_INCLUDE, i.e. KEYWORD MODIFIER VALUE + elsif (/^ROM_INCLUDE/) + { + + if (!m/^ROM_INCLUDE\s+(append|prepend|set)\s+$pathregex/) + { + print "\nERROR: Invalid syntax supplied to keyword ROM_INCLUDE on line " + . "$." + . " in file " + . "$fullpath"; + return 0; + } + + if (m/^ROM_INCLUDE\s+(append|prepend|set)\s+$pathregex\s+$pathregex/) + { + print "\nERROR: Too many arguments supplied to keyword ROM_INCLUDE on line " + . "$." + . " in file " + . "$fullpath"; + return 0; + } + } + + # Checks for a valid VARIANT name + elsif (/^VARIANT[^_HRH]/) + { + if ( !m/^VARIANT\s+\w+/ ) + { + print "\nERROR: VARIANT name not specified on line " . "$." + . " in file " + . "$fullpath"; + return 0; + } + if ( uc("$varname") ne uc( get_variantname($_) ) ) + { + print "\nERROR: VARIANT filename does not match variant name specified on line " + . "$line" + . " in file " + . "$fullpath" + . "\nVariant value extracted from the VAR file is " . "$_"; + } + + } + + # Checks that keyword VIRTUAL is declared correctly + elsif (/^VIRTUAL/) + { + if (m/^VIRTUAL\s+\w+/) + { + print "\nERROR: Invalid declaration of VIRTUAL on line " . "$." + . " in file " + . "$fullpath"; + return 0; + } + + $virtual = 1; + } + + # Checks if VARIANT_HRH is declared correctly. + elsif (/^VARIANT_HRH/) + { + $varianthrhpresent = 1; + my $lineno = $.; + if ( !m/^VARIANT_HRH\s+./ ) + { + print "\nERROR: Invalid format supplied to argument VARIANT_HRH on line " + . "$lineno" + . " in file " + . "$fullpath"; + return 0; + } + + my $str = get_hrhname($_); + if ( $str =~ /\s+/ ) + { + print "\nERROR: Cannot have 2 or more hrh files. Error in line " + . "$lineno" + . " in file " + . "$fullpath"; + return 0; + } + + unless( -e get_fixpath($str) ) + { + print "\nERROR: VARIANT HRH file : " + . get_fixpath($str) + . " specified on line " + . "$lineno" + . " does not exist"; + return 0; + } + + $varianthrh = get_fixpath( get_hrhname($_) ); + + } + + # If none of the valid keywords are found + else + { + + # Do nothing if a comment or blank line is found + if ( (m/$exp\s+\S/) || (m/$exp\S/) || ( !m/./ ) || (m/^\n/) ) + { + } + + # Unsupported keyword + else + { + + print "\nERROR: Invalid keyword " . '"' . "$_" . '"' + . " found on line " . "$." + . " in file " + . "$fullpath"; + return 0; + } + } + } + + close(READVAR); + + # If no HRH file defined, check if the default one exists + if ( !$varianthrhpresent ) + { + print "\nINFO: No VARIANT_HRH defined in VAR file, using ${epocroot}epoc32\/include\/variant\/$varname\.hrh" if ($verbose); + my $str = + get_hrhname( + "VARIANT_HRH ${epocroot}epoc32\/include\/variant\/$varname\.hrh" + ); + + if ( ! -e $str ) + { + print "\nERROR: VARIANT HRH file : " . "$str " . "does not exist\n"; + return 0; + } + else + { + $varianthrh = $str; + } + } + return 1; +} + +# Extract the value of the VARIANT keyword +sub get_variantname +{ + + $_[0] =~ m/^VARIANT\s+(\w+)/i; + return $1; +} + +# Extracts the value of the HRH file from the VARIANT_HRH line supplied +sub get_hrhname +{ + + $_[0] =~ m/^VARIANT_HRH\s+($pathregex)/; + return $1; + +} + +# Method to find the immediate parent node of a child node +sub get_extends +{ + + $_[0] =~ m/^EXTENDS\s+(\w+)/; + return $1; +} + + +# Method to correct all the slashes, and also append EPOCROOT if the path begins with a \ or / +# If path doesn't start with \ or /, returns an abosulte canonical path +sub get_fixpath +{ + + my $arr = $_[0]; + + if ( $arr =~ m/^\// ) + { + $arr =~ s/^\/?//; + return File::Spec->canonpath( "$epocroot" . "$arr" ); + } + + elsif ( $arr =~ m/^\\/ ) + { + $arr =~ s/^\\?//; + return File::Spec->canonpath( "$epocroot" . "$arr" ); + } + + else + { + return File::Spec->rel2abs( File::Spec->canonpath("$arr") ); + } + +} + +# Method to find the ROMINCLUDE values of the VAR file. +sub find_varrominc +{ + + my $filename = $_[0]; + + my $parentNodes; + + # Construct a list of parent nodes if node is a child + if ($childNodeStatus) + { + $parentNodes = find_varparentnode("$filename"); + } + + if ($parentNodes) + { + + # Go through and build the list of all parent ROM_INCLUDES + for ( my $i = scalar(@$parentNodes) - 1 ; $i >= 0 ; $i-- ) + { + my $t = get_variantfullpath( @$parentNodes[$i], $fulldir ); + open( NEWHANDLE, "<$t" ); + + while () + { + if (/ROM_INCLUDE/) + { + get_varrominc($_); + } + } + close(NEWHANDLE); + } + } + + # Append the ROM_INCLUDES of the VAR file in the end + open( NEWHANDLE, "<$filename" ); + + while () + { + if (/ROM_INCLUDE/) + { + get_varrominc($_); + } + } + + undef(@parents); # Flush out parent array; + return \@rominclude; + +} + +# Constructs a list of Parent nodes for a given Child node. +sub find_varparentnode +{ + + my $filename = $_[0]; + my $hasparents = 0; + + open( READHANDLE, "<$filename" ); + while () + { + if (/EXTENDS/) + { + $hasparents = 1; + push( @parents, get_extends($_) ); + + } + } + + close(READHANDLE); + + if ( $hasparents == 1 ) + { + find_varparentnode( + get_variantfullpath( @parents[ scalar(@parents) - 1 ], $fulldir ) + ); + } + else + { + return \@parents; + } + +} + +# Method to extract the ROM_INCLUDE value of a node. +sub get_varrominc +{ + +# If modifier append is found, push the rominclude to the end of the array list. + if (/^ROM_INCLUDE\s+append\s+($pathregex)/) + { + push( @rominclude, ($1) ); + } + +# If modifier prepend is found, push the rominclude to the beginning of the array list. + if (/^ROM_INCLUDE\s+prepend\s+($pathregex)/) + { + unshift( @rominclude, ($1) ); + } + +# If keyword set is found, then empty the rominclude variable and push the new value + if (/^ROM_INCLUDE\s+set\s+($pathregex)/) + { + undef(@rominclude); + push( @rominclude, ($1) ); + } + +} + +# Helper method that clones a reference to a simple list +sub clone_list + { + my $ref = shift; + + # Check the reference is a list + die "Not a list ref" if ref($ref) ne 'ARRAY'; + + # Create a new list object + my @list; + foreach my $entry ( @$ref ) + { + # Only clone lists of scalars + die "Not a scalar" if ref($entry); + + # Add the entry to the new list + push @list, $entry; + } + + # return a reference to the copy + return \@list; + } + +############################## +# write helper +# +# copy from writer.pm +sub write_32bit # little-endian +{ + my $fileHandle=shift; + my $integer=shift; + &write_8bit($fileHandle, $integer&0x000000ff); + &write_8bit($fileHandle, ($integer>>8)&0x000000ff); + &write_8bit($fileHandle, ($integer>>16)&0x000000ff); + &write_8bit($fileHandle, ($integer>>24)&0x000000ff); +} + +sub write_8bit +{ + my $fileHandle=shift; + my $integer=shift; + if ($integer&0xffffff00) + { + die("Error: the integer ".sprintf("0x%08x", $integer)." is too large to write into 8 bits\n"); + } + printf $fileHandle "%c", $integer; +} + + +1; +