sbsv1_os/e32toolp/bldmake/bldmake.pl
author Ross Qin <ross.qin@nokia.com>
Thu, 30 Sep 2010 14:38:30 +0800
branchRCL_3
changeset 16 22bdd8a90cc4
parent 2 99082257a271
permissions -rw-r--r--
synchronize from nokia internal Synergy depository buildtools/92, including fix to bugs 151,468,2049,1371,325,2539, etc.

# Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies).
# All rights reserved.
# This component and the accompanying materials are made available
# under the terms of "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:
# all variables called *Path* are set up to end with a backslash
# all variables called *Path or *File are stored as absolute (file)paths within makmake
# all variables called UpPath* are stored as relative paths within makmake
# 
#

use FindBin;		# for FindBin::Bin
use Getopt::Long;

my $PerlLibPath;    # fully qualified pathname of the directory containing our Perl modules

BEGIN {
# check user has a version of perl that will cope
	require 5.005_03;
# establish the path to the Perl libraries: currently the same directory as this script
	$PerlLibPath = $FindBin::Bin;	# X:/epoc32/tools
	$PerlLibPath =~ s/\//\\/g;	# X:\epoc32\tools
	$PerlLibPath .= "\\";
}
sub ExportDirs ($);

use lib $PerlLibPath;
use E32env;
use E32Plat;
use Modload;
use Output;
use Pathutl;
use E32Variant;
use RVCT_plat2set;
use BPABIutl;
use wrappermakefile;
use CheckSource;
use File::Path; # for rmtree
use featurevariantparser;

my $BldInfName = 'BLD.INF';
my %Options;
my %KeepGoing;
my @DefaultPlats=('WINSCW', 'GCCXML', 'EDG', 'X86GCC');
my @BaseUserDefaultPlats=('ARM4', 'ARM4T', 'WINSCW', 'GCCXML', 'EDG', 'X86GCC');
my @OptionalPlats=('VS6', 'VS2003');
my @PlatsReq;

my %CheckSourceEXPORTSMetaData;
my %CheckSourceEXPORTSIncludes;
my %CheckSourceMMPFILESMetaData;
my %CheckSourceEXTENSIONSMetaData;
my %CheckSourceBldInfIncludes;

for ('ARMV4', 'ARMV5')
{
	push @BaseUserDefaultPlats, $_ if RVCT_plat2set::compiler_exists($_);
}

# Add ARMV5_ABIV1 platform if ENABLE_ABIV2_MODE is set in variant.cfg
my $variantABIV2Keyword = &Variant_GetMacro();
# Add ARMV5_ABIV1 platform only after determining the presence of RVCT compiler.
if ($variantABIV2Keyword && RVCT_plat2set::compiler_exists('ARMV5_ABIV1') ) {
	push @OptionalPlats, 'ARMV5_ABIV1';
}

# bldmake -k shouldn't die if Extension Makefile is missing
our $IgnoreMissingExtensionMakefile = 0;

# Add the BPABI Platforms to be added 
my @BPABIPlats = &BPABIutl_Plat_List;
foreach my $BPABIPlat (@BPABIPlats) 
{
	# BPABI platform related with ARMV5(eg.ARMV5_ABIV2) is added to the platform list after 
	# determining the presence of RVCT compiler	
	if(($BPABIPlat =~/^ARMV5/i))
		{
			if(!($BPABIPlat =~/^ARMV5$/i) && RVCT_plat2set::compiler_exists('ARMV5'))
			{
			push @OptionalPlats, $BPABIPlat;
			}
		}
	# All other BPABI platforms(eg. gcce) are added to the platform list.
	else
		{
			push @OptionalPlats, $BPABIPlat;
		}
}

if ( RVCT_plat2set::compiler_exists('ARMV5') ) {
	#determine the presence of ARVCT compiler
	push @DefaultPlats, 'ARMV5';
}
	
# Need to add WINS and X86 if MSDEV compiler is present
# Use MSDevDir to determine the presence of the compiler
push @BaseUserDefaultPlats, 'WINS', 'X86' if (exists($ENV{'MSDevDir'}));

my @BaseDefaultPlats = @BaseUserDefaultPlats;
push @BaseDefaultPlats, 'X86SMP' if (grep /^X86$/, @BaseUserDefaultPlats);
push @BaseDefaultPlats, 'ARM4SMP' if (grep /^ARM4$/, @BaseUserDefaultPlats);
push @BaseDefaultPlats, 'ARMV4SMP' if (grep /^ARMV4$/, @BaseUserDefaultPlats);
push @BaseDefaultPlats, 'ARMV5SMP' if (grep /^ARMV5$/, @BaseUserDefaultPlats);
push @BaseDefaultPlats, 'X86GMP' if (grep /^X86GCC$/, @BaseUserDefaultPlats);

my $variantMacroHRHFile = Variant_GetMacroHRHFile();
sub ExportDirs ($);

# THE MAIN PROGRAM SECTION
##########################

# Load default feature variant info - for the hrh file
my %DefaultFeatureVariant = featurevariantparser->GetVariant('DEFAULT') if (featurevariantparser->DefaultExists());
my @FeatureVariants = featurevariantparser->GetBuildableFeatureVariants();
	
{
	Load_SetModulePath($PerlLibPath);
	Plat_Init($PerlLibPath);

	{
		my @PlatList = &Plat_List();
		
		if (RVCT_plat2set::compiler_exists('ARMV6')){
			foreach my $ARMV6Target ("ARMV6", "ARMV6_ABIV1", "ARMV6_ABIV2"){
				if (grep /^$ARMV6Target$/, @PlatList) {
					push @BaseUserDefaultPlats, "$ARMV6Target" if (!grep /^$ARMV6Target$/, @BaseUserDefaultPlats);
					push @BaseDefaultPlats, "$ARMV6Target" if (!grep /^$ARMV6Target$/, @BaseDefaultPlats);
				}
			}
		}
	
		if (RVCT_plat2set::compiler_exists('ARMV7')){
 			my $rvct_ver = RVCT_plat2set::get_version_string('ARMV7');
 			if ((defined $rvct_ver) and ($rvct_ver ge "3.1.674")) {
 				if (grep /^ARMV7$/, @PlatList ) {
					push @DefaultPlats, 'ARMV7' if (!grep /^ARMV7$/, @DefaultPlats);
					push @BaseUserDefaultPlats, "ARMV7" if (!grep /^ARMV7$/, @BaseUserDefaultPlats);
					push @BaseDefaultPlats, "ARMV7" if (!grep /^ARMV7$/, @BaseDefaultPlats);
 				}
 			}
 		}
	}
	
#	process the commmand-line
	unless (GetOptions(\%Options, 'v', "k|keepgoing", "notest", "file|f=s")) {
		exit 1;
	}
	unless (@ARGV>=1) {
		&Usage();
	}
	my $Command=uc shift @ARGV;
	unless ($Command=~/^(BLDFILES|CLEAN|INF|PLAT)$/o) {
		&Usage();
	}
	my $CLPlat=uc shift @ARGV;

	unless ($CLPlat) {
		$CLPlat='ALL';
	}

	if ($Command eq 'INF') {
		&ShowBldInfSyntax();
		exit;
	}

	if ($Command eq 'PLAT') {
		my @PlatList = ($CLPlat);
		my $PlatName;
		# Variable introduced to check if the bldmake plat command is called, To be
		# passed as an argument in Plat_GetL function call.
		my $platcommand=1;
		if ($CLPlat eq "ALL") {
			@PlatList = &Plat_List();
			print(
				"Supported Platforms:\n",
				"  @PlatList\n\n"
			);
		}
		print(
			"Macros defined for BLD.INF preprocessing of MMPFILE sections:\n"
		);
		foreach $PlatName (@PlatList) {
			my %Plat;
			eval { &Plat_GetL($PlatName, \%Plat,{},$platcommand); };
			die $@ if $@;
			print(
				"\nPlatform $PlatName:\n",
				"  @{$Plat{MmpMacros}}\n"
			);
		}
		exit;
	}
	if ($Options{file}) {
		$BldInfName = $Options{file};
	}

#	check that the BLD.INF file exists
#	maybe BLDMAKE should allow a path to be specified leading to the BLD.INF file
	my $BldInfPath=&Path_WorkPath;
	unless (-e "${BldInfPath}$BldInfName") {
		&FatalError("Can't find \"${BldInfPath}$BldInfName\"");
	}

	if (!-d $E32env::Data{EPOCPath}){
		&FatalError("Directory \"$E32env::Data{EPOCPath}\" does not exist");
	}

#	decide the output directory
	my $OutDir=&Path_Chop($E32env::Data{BldPath}).$BldInfPath;

#	Work out the path for the IBY files
	my $RomDir=&Path_Chop($E32env::Data{RomPath}).$BldInfPath;

#	Work out the name for the BLD.INF module
	my @Dirs=&Path_Dirs($BldInfPath);
	my $Module = pop @Dirs;
	if (lc($Module) eq 'group') {
		$Module = pop @Dirs;
	}

	if ($Command eq 'CLEAN') {
		unlink "${BldInfPath}ABLD.BAT";
		$OutDir=~m-(.*)\\-o;
		if (-d $1) { # remove backslash for test because some old versions of perl can't cope
			opendir DIR, $1;
			my @Files=grep s/^([^\.].*)$/$OutDir$1/, readdir DIR;
			closedir DIR;
			unlink @Files;
		}
		rmtree <$OutDir\\wrappermakefiles>;
# modified start: makefile improvement 
		rmtree <$OutDir\\FeatureVariantInfo>;
# modified end: makefile improvement 
		exit;
	}

#	parse BLD.INF - to get the platforms and the export files
	eval { &Load_ModuleL('PREPFILE'); };
	&FatalError($@) if $@;

	my @RealPlats=();
	my @Exports=();
	my @TestExports=();
	if ($Options{v}) {
		print "Reading \"${BldInfPath}$BldInfName\" for platforms and exports\n";
	}
	&ParseBldInf(\@RealPlats, \@Exports, \@TestExports, $BldInfPath, 
		$E32env::Data{EPOCIncPath}, $E32env::Data{EPOCPath}, $E32env::Data{EPOCDataPath});

#       Add Customizations
	my @additions;
	foreach my $plat (@RealPlats) {
	        my @customizations = Plat_Customizations($plat);
	        foreach my $custom (@customizations) {
	                push @additions, $custom 
			        unless grep /$custom/, @additions;
		}
        }
	unless ($CLPlat eq 'ALL') {
		push @RealPlats, @additions;
	}

 #	Force GCCXML support for anything that compiles as ARMV5
	if ( (grep /^ARMV5$/, @RealPlats) and not (grep /^GCCXML$/,@RealPlats) ) 
			{
			push @RealPlats, 'GCCXML';
			}

 #	Force EDG support for anything that compiles as ARMV5
	if ( (grep /^ARMV5$/, @RealPlats) and not (grep /^EDG$/,@RealPlats) ) 
			{
			push @RealPlats, 'EDG';
			}

if (0) {
#	Add ARMV5 to the platforms if ARM4 is defined
	if (grep /^ARM4$/, @RealPlats) {
		unless ( (grep /^ARMV4$/, @RealPlats) or (grep /^ARMV5$/, @RealPlats) ){
		push @RealPlats, 'ARMV5';
		push @RealPlats, 'ARMV4';
		}
	}
}
	
#	get any IDE platforms required into a new platforms list, and
#	Create a hash to contain the 'real' names of the platforms, i.e. WINS rather than VC6
	my @Plats=@RealPlats;
	my %Real;
	foreach (@RealPlats) { # change to get VC6 batch files
		$Real{$_}=$_;
		my $AssocIDE;
		my @AssocIDEs;

#		Get the IDEs associated with a real platform. A real plat like,
#		WINSCW may have multiple associated IDEs like VC6 .NET2003 or CW IDE.
		&Plat_AssocIDE($_, \@AssocIDEs);
		next unless @AssocIDEs;
		
		push @Plats, @AssocIDEs;
		foreach $AssocIDE (@AssocIDEs)
		{
			$Real{$AssocIDE}=$Real{$_};
		}
		
	}
	if ($Options{v}) {
		print "Platforms: \"@Plats\"\n";
	}

#	check that the platform specified on the command-line is acceptable
#	and sort out a list of platforms to process
	my @DoRealPlats=@RealPlats;
	my @DoPlats=@Plats;

	unless (@Plats) { 
#	include the optional platform list if no platform is specified
		my $OptionalPlat;
		foreach $OptionalPlat (@OptionalPlats) {
			unless (grep /^$OptionalPlat$/i, @DoPlats) {
				push @DoPlats, $OptionalPlat;
			}
		}
	}

	unless ($CLPlat eq 'ALL') {
		unless (grep /^$CLPlat$/, @Plats) {
			&FatalError("Platform $CLPlat not supported by \"${BldInfPath}$BldInfName\"\n");
		}
		@DoPlats=($CLPlat);
		@DoRealPlats=$Real{$CLPlat};
	}
			
#	sort out the export directories we might need to make
	my @ExportDirs=ExportDirs(\@Exports);
	my @TestExportDirs=ExportDirs(\@TestExports);

#	parse the BLD.INF file again for each platform supported by the project
#	storing the information in a big data structure
	my %AllPlatData;
	my %AllPlatTestData;
	my $Plat;
	
	if ($Options{v} and $CLPlat ne 'ALL'){
		print "Reading \"${BldInfPath}$BldInfName\" for $CLPlat \n";
	}

	foreach $Plat (@RealPlats) {
		if ($Options{v}) {
			if ($CLPlat eq 'ALL') {
				print "Reading \"${BldInfPath}$BldInfName\" for $Plat\n";
			}
		}
		my (@PlatData, @PlatTestData);
		if ($CLPlat eq 'ALL') {
			&ParseBldInfPlat(\@PlatData, \@PlatTestData, $Plat, $BldInfPath, ($DefaultFeatureVariant{VALID} && &Plat_SupportsFeatureVariants($Plat) ? \%DefaultFeatureVariant : undef));
		}
		else {
			&ParseBldInfPlat(\@PlatData, \@PlatTestData, $CLPlat, $BldInfPath, ($DefaultFeatureVariant{VALID} && &Plat_SupportsFeatureVariants($CLPlat) ? \%DefaultFeatureVariant : undef));
		}
		$AllPlatData{$Plat}=\@PlatData;
		$AllPlatTestData{$Plat}=\@PlatTestData;
	}
	undef $Plat;

	undef $CLPlat;
	if ($Command eq 'BLDFILES') {

#		create the perl file, PLATFORM.PM, listing the platforms
		if ($Options{v}) {
			print "Creating \"${OutDir}PLATFORM.PM\"\n";
		}
		&CreatePlatformPm($OutDir, \@Plats, \@RealPlats, \%Real, \%AllPlatData, \%AllPlatTestData);

#		create the .BAT files required to call ABLD.PL
		if ($Options{v}) {
			print "Creating \"${BldInfPath}ABLD.BAT\"\n";
		}
		&CreatePerlBat($BldInfPath);

#		create the makefile for exporting files
		if ($Options{v}) {
			print "Creating \"${OutDir}EXPORT.MAKE\"\n";
		}
		&CreateExportMak("${OutDir}EXPORT.MAKE", \@Exports, \@ExportDirs);

#		create the makefile for exporting test files
		if ($Options{v}) {
			print "Creating \"${OutDir}EXPORTTEST.MAKE\"\n";
		}
		&CreateExportMak("${OutDir}EXPORTTEST.MAKE", \@TestExports, \@TestExportDirs);

# modified start: makefile improvement 
		#create the feature variant infor file
		foreach my $copyofPlat (@DoPlats)
		{
			my $realplat = $Real{$copyofPlat};
			if(&Plat_SupportsFeatureVariants($copyofPlat))
			{
				my $variant_info = &Path_Chop($E32env::Data{BldPath}).$BldInfPath."\\FeatureVariantInfo\\".$realplat."\\";	
				eval { &Path_MakePathL($variant_info); };
				die $@ if $@;
				if ($Options{v}) {
					print "Creating: \"$variant_info\"\n";
				}
				foreach my $featureVariant (@FeatureVariants)
				{
					my $variant_file = $variant_info."$realplat.$featureVariant.info";
# modified by SV start: makefile improvement 
					my $refdata = $AllPlatData{$realplat};
					my $testrefdata = $AllPlatTestData{$realplat};
					if ( @$refdata ) {
						foreach my $RefPro (@$refdata)
						{
							$variant_file = $variant_info."$realplat.$featureVariant.$$RefPro{Base}.info";
							my $ref_basedir = $variant_file;
							$ref_basedir=~s/(.*[\\\/]).*/$1/;
							if ( ! -d $ref_basedir ){
								eval { &Path_MakePathL($ref_basedir); };
								die $@ if $@;
							}
							open VARIANTINFOR,">$variant_file" or die "ERROR: Can't open or create file \"$variant_file\"\n";
							print VARIANTINFOR "VARIANT_PLAT_NAME_$$RefPro{Base}:=default \n";
							close VARIANTINFOR or die "ERROR: Can't close file \"$variant_file\"\n";
						}
					}
					else {
						open VARIANTINFOR,">$variant_file" or die "ERROR: Can't open or create file \"$variant_file\"\n";
						print VARIANTINFOR "VARIANT_PLAT_NAME:=$featureVariant \n";
						close VARIANTINFOR or die "ERROR: Can't close file \"$variant_file\"\n";
						print "file \"$variant_file\"\n"
					}
					if ($testrefdata){
						foreach my $RefPro (@$testrefdata)
						{
							$variant_file = $variant_info."$realplat.$featureVariant.$$RefPro{Base}.info";
							my $ref_basedir = $variant_file;
							$ref_basedir=~s/(.*[\\\/]).*/$1/;
							if ( ! -d $ref_basedir ){
								eval { &Path_MakePathL($ref_basedir); };
								die $@ if $@;
							}
							open VARIANTINFOR,">$variant_file" or die "ERROR: Can't open or create file \"$variant_file\"\n";
							print VARIANTINFOR "VARIANT_PLAT_NAME_$$RefPro{Base}:=default \n";
							close VARIANTINFOR or die "ERROR: Can't close file \"$variant_file\"\n";
						}
						
					}
# modified by SV end: makefile improvement 
					# Close and cleanup
					if ($Options{v}) {
						print "Variant info file has been successfully created\n";
					}
				}				
			}
		}
# modified end: makefile improvement 
#		create the platform meta-makefiles
		foreach my $copyofPlat (@DoPlats) {  # Do not use $_ here !!
			if ($Options{v}) {
				print "Creating \"$OutDir$copyofPlat.MAKE\"\n";
			}
			my $realplat = $Real{$copyofPlat};
			&CreatePlatMak($OutDir, $E32env::Data{BldPath}, $AllPlatData{$realplat}, $copyofPlat, $realplat, $RomDir, $Module, $BldInfPath, \@Exports, '');

			if (&Plat_SupportsFeatureVariants($copyofPlat))
				{
				foreach my $featureVariant (@FeatureVariants)
					{
					print "Creating \"$OutDir$copyofPlat.$featureVariant.MAKE\"\n" if ($Options{v});
					&CreatePlatMak($OutDir, $E32env::Data{BldPath}, $AllPlatData{$realplat}, $copyofPlat, $realplat, $RomDir, $Module, $BldInfPath, \@Exports, '', ".$featureVariant");
					}
				}
		}
		foreach (@DoPlats) {
			if ($Options{v}) {
				print "Creating \"$OutDir${_}TEST.MAKE\"\n";
			}
			&CreatePlatMak($OutDir, $E32env::Data{BldPath}, $AllPlatTestData{$Real{$_}}, $_, $Real{$_}, $RomDir, $Module, $BldInfPath, \@TestExports, 'TEST');

			if (&Plat_SupportsFeatureVariants($_))
				{
				foreach my $featureVariant (@FeatureVariants)
					{
					print "Creating \"$OutDir${_}.".$featureVariant."TEST.MAKE\"\n" if ($Options{v});
					&CreatePlatMak($OutDir, $E32env::Data{BldPath}, $AllPlatTestData{$Real{$_}}, $_, $Real{$_}, $RomDir, $Module, $BldInfPath, \@TestExports, 'TEST', ".$featureVariant");
					}
				}
		}

#		create the platform test batch files
		foreach (@DoRealPlats) {
			if ($Options{v}) {
				print "Creating test batch files in \"$OutDir\" for $_\n";
			}
			&CreatePlatBatches($OutDir, $AllPlatTestData{$_}, $_);
		}

# modified by SV start: makefile improvement 
# create all sub directories
	foreach my $refplat (@DoRealPlats) {
		my $tmp = $AllPlatData{$refplat};
		foreach my $dref (@$tmp){
			my $builddir = $OutDir . $$dref{Base} ."\\" . $refplat . "\\";
				if (!-d $builddir){
					if ($Options{v}) {
						print "Creating directory \"$builddir\" \n";
					}
					eval { &Path_MakePathL($builddir); };
					&FatalError($@) if $@;
				}
			}
	}
# modified by SV end: makefile improvement 

#		report any near-fatal errors
		if (scalar keys %KeepGoing) {
		    print STDERR
			    "\n${BldInfPath}$BldInfName WARNING(S):\n",
			    sort keys %KeepGoing
			    ;
		}

		exit;
	}
}


################ END OF MAIN PROGRAM SECTION #################
#------------------------------------------------------------#
##############################################################


# SUBROUTINE SECTION
####################

sub Usage () {

	eval { &Load_ModuleL('E32TPVER'); };
	&FatalError($@) if $@;

	print
		"\n",
		"BLDMAKE - Project building Utility (Build ",&E32tpver,")\n",
		"\n",
		"BLDMAKE {options} [<command>] [<platform>]\n",
		"\n",
		"<command>: (case insensitive)\n",
		" BLDFILES - create build batch files\n",
		" CLEAN    - remove all files bldmake creates\n",
		" INF      - display basic BLD.INF syntax\n",
		" PLAT     - display platform macros\n",
		"\n",
		"<platform>: (case insensitive)\n",
		"  if not specified, defaults to \"ALL\"\n",
		"\n",
		"Options: (case insensitive)\n",
		" -v   ->  verbose mode\n",
		" -k   ->  keep going even if files are missing\n"
	;
	exit 1;
}

sub ShowBldInfSyntax () {

	print <<ENDHERE1;

BLD.INF - Syntax

/* Use C++ comments if required */
// (Curly braces denote optional arguments)

PRJ_PLATFORMS
{DEFAULT} {-<platform> ...} {<list of platforms>}
// list platforms your project supports here if not default
ENDHERE1

	print "// default = ".join(" ",@DefaultPlats)."\n";

	print <<ENDHERE;
	
PRJ_EXPORTS
[<source path>\<source file>]	{<destination>}
// list each file exported from source on a separate line
// {<destination>} defaults to \\EPOC32\\Include\\<source file>

PRJ_TESTEXPORTS
[<source path>\<source file>]	{<destination>}
// list each file exported from source on a separate line
// {<destination>} defaults to BLD.INF dir

PRJ_MMPFILES
[<mmp path>\<mmp file>] {<qualifiers>}
{MAKEFILE|NMAKEFILE} [<path>\<makefile>] {build_as_arm}
// <qualifiers> are tidy, ignore, build_as_arm

#if defined(<platform>)
// .MMP statements restricted to <platform>
#endif

PRJ_TESTMMPFILES
[<mmp path>\<mmp file>] {<qualifiers>}
{MAKEFILE|NMAKEFILE} [<path>\<makefile>] {<qualifiers>}
// <qualifiers> are {tidy} {ignore} {manual} {support} {build_as_arm}

#if defined(<platform>)
// .MMP statements restricted to <platform>
#endif

ENDHERE

}

sub WarnOrDie ($$) {
	my ($dieref, $message) = @_;
	if ($Options{k}) {
		$KeepGoing{$message} = 1;
	} else {
		push @{$dieref}, $message;
	}
}

sub ExtensionMakefileMissing($)
{
	$IgnoreMissingExtensionMakefile = @_;
}

sub ParseBldInf ($$$$$) {
	my ($PlatsRef, $ExportsRef, $TestExportsRef, $BldInfPath, $EPOCIncPath, $EPOCPath, $EPOCDataPath)=@_;

	my @Prj2D;
	eval { &Prepfile_ProcessL(\@Prj2D, "${BldInfPath}$BldInfName",$variantMacroHRHFile); };
	&FatalError($@) if $@;
	
	my @SupportedPlats=&Plat_List();

	my @Plats;
	my %RemovePlats;

	my $DefaultPlatsUsed=0;
	my %PlatformCheck;

	my %ExportCheck;
	my $Section=0;
	our @PrjFileDie;
	my $Line;
	my $CurFile="${BldInfPath}$BldInfName";
	LINE: foreach $Line (@Prj2D) {
		my $LineNum=shift @$Line;
		$_=shift @$Line;
		if ($LineNum eq '#') {
			$CurFile=$_;
			next LINE;
		}

		$CurFile = &Path_Norm ($CurFile); 
		
		if (/^PRJ_(\w*)$/io) {
			$Section=uc $1;
			if ($Section=~/^(PLATFORMS|EXPORTS|TESTEXPORTS|MMPFILES|TESTMMPFILES|EXTENSIONS|TESTEXTENSIONS)$/o) {
				if (@$Line) {
					push @PrjFileDie, "$CurFile($LineNum) : Can't specify anything on the same line as a section header\n";
				}
				next LINE;
			}
			push @PrjFileDie, "$CurFile($LineNum) : Unknown section header - $_\n";
			$Section=0;
			next LINE;
		}
		if ($Section eq 'PLATFORMS') {
#			platforms are gathered up into a big list that contains no duplicates.  "DEFAULT" is
#			expanded to the list of default platforms.  Platforms specified with a "-" prefix
#			are scheduled for removal from the list.  After processing platforms specified
#			with the "-" prefix are removed from the list.

			unshift @$Line, $_;
			my $Candidate;
			CANDLOOP: foreach $Candidate (@$Line) {
				$Candidate=uc $Candidate;
#				ignore old WINC target
				if ($Candidate eq 'WINC') {
					next CANDLOOP;
				}
#				expand DEFAULT
				if ($Candidate eq 'DEFAULT') {
					$DefaultPlatsUsed=1;
					my $Default;
					foreach $Default (@DefaultPlats) {
						unless ($PlatformCheck{$Default}) {
							push @Plats, $Default;
							$PlatformCheck{$Default}="$CurFile: $LineNum";
						}
					}
					next CANDLOOP;
				}
#				expand BASEDEFAULT
				if ($Candidate eq 'BASEDEFAULT') {
					$DefaultPlatsUsed=1;
					my $Default;
					foreach $Default (@BaseDefaultPlats) {
						unless ($PlatformCheck{$Default}) {
							push @Plats, $Default;
							$PlatformCheck{$Default}="$CurFile: $LineNum";
						}
					}
					next CANDLOOP;
				}
#				expand BASEUSERDEFAULT
				if ($Candidate eq 'BASEUSERDEFAULT') {
					$DefaultPlatsUsed=1;
					my $Default;
					foreach $Default (@BaseUserDefaultPlats) {
						unless ($PlatformCheck{$Default}) {
							push @Plats, $Default;
							$PlatformCheck{$Default}="$CurFile: $LineNum";
						}
					}
					next CANDLOOP;
				}
#				check for removals
				if ($Candidate=~/^-(.*)$/o) {
					$Candidate=$1;
#					check default is specified
					unless ($DefaultPlatsUsed) {
						push @PrjFileDie, "$CurFile($LineNum) : \"DEFAULT\" must be specified before platform to be removed\n";
						next CANDLOOP;
					}
					$RemovePlats{$Candidate}=1;
					next CANDLOOP;
				}
# 				If tools platform is specified in bld.inf file then component is built for cwtools as well 
				if ($Candidate =~ /^tools/i)
						{
						push @Plats, 'CWTOOLS';
						}
#				check platform is supported
				unless (grep /^$Candidate$/, @SupportedPlats) {
					WarnOrDie(\@PrjFileDie, "$CurFile($LineNum) : Unsupported platform $Candidate specified\n");
					next CANDLOOP;
				}
#				check platform is not an IDE
				if ($Candidate=~/^VC/o) {
					push @PrjFileDie, "$CurFile($LineNum) : No need to specify platform $Candidate here\n";
					next CANDLOOP;
				}
#				add the platform
				unless ($PlatformCheck{$Candidate}) {
					push @Plats, $Candidate;
					my $SubPlat = sprintf("%sEDG", $Candidate);
					push @Plats, $SubPlat 
					    if (grep /^$SubPlat$/, @SupportedPlats);
					$PlatformCheck{$Candidate}="$CurFile: $LineNum";
				}
			}
			next LINE;
		}

		# Skip PRJ_TESTEXPORT section if -notest flag		
		next LINE if ($Options{notest} && ($Section=~/^(TESTEXPORTS)$/o)); 
		
		if ($Section=~/^(EXPORTS|TESTEXPORTS)$/o) {

#			make path absolute - assume relative to group directory
			my $Type = 'file';
			if (/^\:(\w+)/) {
				# Export an archive
				$Type = lc $1;
				unless ($Type eq 'zip') {
					push @PrjFileDie, "$CurFile($LineNum) : Unknown archive type - $Type\n";
					next LINE;
				}
				$_ = shift @$Line;
			}

			my $loggedSourceExport = $_;
			$_ = &Path_Norm ($_);
			
			my $Source=&Path_MakeAbs($CurFile, $_);
			my $Releasable='';
			my $emReleasable='';
			my $unzip_option ='';
			if (@$Line) {
#				get the destination file if it's specified
				$Releasable=shift @$Line;
				CheckSource_MetaData(%CheckSourceEXPORTSMetaData, $CurFile, "PRJ_".$Section, $Releasable, $LineNum);
				$Releasable = &Path_Norm ($Releasable);
				$emReleasable=ucfirst $Releasable;
				if ($emReleasable=~/^([A-Z]):(\\.*)$/)  {
				  	$emReleasable=~s/://;
					$Releasable=$EPOCDataPath.$emReleasable;
				}
			}

			my $sourceExportTypeSuffix = "";
			$sourceExportTypeSuffix .= " (NO DESTINATION)" if (!$Releasable && $Section =~ /^EXPORTS$/);		
			CheckSource_MetaData(%CheckSourceEXPORTSMetaData, $CurFile, "PRJ_".$Section.$sourceExportTypeSuffix, $loggedSourceExport, $LineNum, $CheckSource_PhysicalCheck);
			
			if (@$Line) {
				$unzip_option = shift @$Line;
				unless ($unzip_option=~ /overwrite/i) {
					push @PrjFileDie, "$CurFile($LineNum) : Too many arguments in exports section line\n";
					next LINE;
				}
			}
			unless ($Type eq 'zip' or &Path_Split('File', $Releasable)) {
#				use the source filename if no filename is specified in the destination
#				no filename for archives
				$Releasable.=&Path_Split('File', $Source);
			}
			my $defpath;
			if ($Type eq 'zip') {
#				archives relative to EPOCROOT
				$defpath = $ENV{EPOCROOT};
			}
			elsif (($Section =~ /EXPORTS$/) && ($Releasable =~ s/^\|[\/|\\]?//)) {
#			'|' prefix forces "relative to bld.inf file" in PRJ_[TEST]EXPORTS destinations
				$defpath = $CurFile;
			}
			elsif ($Section eq 'EXPORTS') {
#				assume the destination is relative to $EPOCIncPath
				$defpath = $EPOCIncPath;
			}
			else {
				$defpath = $CurFile;
			}
			$Releasable=&Path_MakeEAbs($EPOCPath, $defpath, $Releasable);

#			sanity checks!
			if ($Type eq 'file' && $ExportCheck{uc $Releasable}) {
				push @PrjFileDie, "$CurFile($LineNum) : Duplicate export $Releasable (from line $ExportCheck{uc $Releasable})\n";
				next LINE;
			}
			$ExportCheck{uc $Releasable}="$CurFile: $LineNum";
			if (! -e $Source) {
				WarnOrDie(\@PrjFileDie, "$CurFile($LineNum) : Exported source file $Source not found\n");
			}
			elsif ($Type ne 'zip' && -d $Releasable) {
				push @PrjFileDie, "$CurFile($LineNum) : Export target $Releasable must be a file.\n";
			}
			else {
				if ($Section eq 'EXPORTS') {
					push @$ExportsRef, {
						'Source'=>$Source,
						'Releasable'=>$Releasable,
						'emReleasable'=>$emReleasable,
						'Type'=>$Type,
						'UnzipOption'=>$unzip_option
					};
				}
				else {
					push @$TestExportsRef, {
						'Source'=>$Source,
						'Releasable'=>$Releasable,
						'emReleasable'=>$emReleasable,
						'Type'=>$Type,
						'UnzipOption'=>$unzip_option
					};
				}
			}
			next LINE;
		}
	}
	if (@PrjFileDie) {
		print STDERR
			"\n${BldInfPath}$BldInfName FATAL ERROR(S):\n",
			@PrjFileDie
		;
		exit 1;
	}

#	set the list of platforms to the default if there aren't any platforms specified,
#	else add platforms to the global list unless they're scheduled for removal,
	unless (@Plats) {
		@$PlatsRef=@DefaultPlats;
		# Include the list of BPABI Platforms in a default build.
		my $OptionalPlat;
		foreach $OptionalPlat (@OptionalPlats) {
			#	VS6 and VS2003 are not real platforms and hence are not included in a default build
			unless ( $OptionalPlat eq 'VS6' || $OptionalPlat eq 'VS2003') {
				if (not grep /^$OptionalPlat$/i, @$PlatsRef) {
					push @$PlatsRef, $OptionalPlat;
				}
			}
		}		
	}
	else {
		my $Plat;
		foreach $Plat (@Plats) {
			unless ($RemovePlats{$Plat}) {
				push @$PlatsRef, $Plat;
			}
		}
		push @PlatsReq , @$PlatsRef;
	}
}

sub ExportDirs ($) {
	my ($ExportsRef)=@_;

	my %ExportDirHash;
	foreach (@$ExportsRef) {
		my $dir = ($$_{Type} eq 'zip') ? $$_{Releasable} : &Path_Split('Path',$$_{Releasable});
		if ($dir) {
			$dir=&Path_Chop($dir);
			$ExportDirHash{uc $dir}=$dir;
		}
	}
	my @ExportDirs;
	foreach (keys %ExportDirHash) {
		push @ExportDirs, $ExportDirHash{$_};
	}
	@ExportDirs;
}


sub ParseBldInfPlat ($$$$) {
	my ($DataRef, $TestDataRef, $Plat, $BldInfPath, $FeatureVar)=@_;
		
#	get the platform .MMP macros
	my %Plat;
	eval { &Plat_GetL($Plat,\%Plat); };
	&FatalError($@) if $@;

#	get the raw data from the BLD.INF file
	my @Prj2D;
	eval { &Prepfile_ProcessL(\@Prj2D, "${BldInfPath}$BldInfName", ($FeatureVar ? $FeatureVar->{VARIANT_HRH} : $variantMacroHRHFile), @{$Plat{MmpMacros}}); };
	&FatalError($@) if $@;

	my %dummy;
	my @userIncludes = ('.');
	my @systemIncludes = ();
	$CheckSourceBldInfIncludes{$Plat} = CheckSource_Includes("${BldInfPath}$BldInfName", %dummy, $variantMacroHRHFile, @{$Plat{MmpMacros}}, @userIncludes, @systemIncludes, $CheckSource_NoUserSystemDistinction);
	
#	process the raw data
	my $IsExtensionBlock =0;
	my (@ExtensionBlockData, $ErrorString);
	my %Check;
	my $Section=0;
	my @PrjFileDie;
	my $Line;
	my $CurFile="${BldInfPath}$BldInfName";
	LINE: foreach $Line (@Prj2D) {

		my %Data;
		my %Temp;

		my $LineNum=shift @$Line;
		if ($LineNum eq '#') {
			$CurFile=shift @$Line;
			next LINE;
		}

		$CurFile = &Path_Norm ($CurFile);
		
#		upper-case all the data here, but record original source case
#		in a hash so that it can be recalled for CheckSource purposes

		my %originalSourceCase;
   		foreach (@$Line) {
 			$originalSourceCase{uc $_} = $_;  # needed for extension template makefile MACROs 
   			$_=uc $_;
   		}

		$_=shift @$Line;

#		check for section headers - don't test for the right ones here
#		because we do that in the first parse function

		if (/^PRJ_(\w*)$/o) {
			$Section=$1;
			next LINE;
		}

#		Skip section if PRJ_TESTMMPFILES and -notest option
		next LINE if ($Options{notest} && ($Section=~/^(TESTMMPFILES)$/o)); 

#		check for EXTENSION sections
		if ($Section=~/^(EXTENSIONS|TESTEXTENSIONS)$/o) {

#			We have an extension block
			if (/^start(\w*)$/io) {
				if ($IsExtensionBlock) {
					&FatalError("$CurFile($LineNum) : Cannot embed Extension Template 'start' sections\n");
				}
				$IsExtensionBlock =1;
				$ErrorString = "$CurFile($LineNum)";
				foreach (@$Line)
				{
				if (/^EXTENSION$/)
					{
					my $extensionTemplate = @$Line[scalar(@$Line)-1];
					CheckSource_MetaData(%CheckSourceEXTENSIONSMetaData, $CurFile, "PRJ_".$Section, $originalSourceCase{$extensionTemplate}.".mk", $LineNum, $CheckSource_PhysicalCheck) if ($extensionTemplate);
					}
				}

				push @ExtensionBlockData, $Line; 			
				next LINE;
			}		
			
			if (($IsExtensionBlock) & (! (/^end(\w*)$/io))) {
				if (($_ ne "TOOL") & ($_ ne "OPTION") & ($_ ne "TARGET") & ($_ ne "SOURCES") & ($_ ne "DEPENDENCIES")) {
							&FatalError("$CurFile($LineNum) : Unrecognised keyword: $_.  Is there an 'end' corresponding to the 'start' for the Extension Template?\n"); 
				}
				if ($_ ne "OPTION") {
					unshift(@$Line, $_);					
				}
#				Need to revert MACROs back to their original case
				foreach (@$Line) {
					$_=$originalSourceCase{$_};
				}
				push @ExtensionBlockData, $Line; 
				next LINE;
			}
			
			if (/^end(\w*)$/io) {
				if (! $IsExtensionBlock) {
					&FatalError("$CurFile($LineNum) : No 'start' corresponding to this 'end' in Extension Template section\n"); 
				}
				$IsExtensionBlock =0;
				my $OutDir=Path_Chop($E32env::Data{BldPath}).$BldInfPath;
#				Generate wrapper makefile for this platform.
				eval { &Load_ModuleL('WrapperMakefile'); };
					&FatalError($@) if $@;
				$OutDir=~ s/\\/\//g;  # convert to unix slashes for wrappermakefile.pm
				%Data = GenerateWrapper($Plat, $OutDir, $ErrorString, \@PrjFileDie, @ExtensionBlockData);
				if (!$IgnoreMissingExtensionMakefile)
				{
					$Data{ExtensionRoot}=&Path_Split('Path', $CurFile);
					$Data{Path}=~ s/\//\\/g;  # convert unix slashes back to win32 
					$Data{Base}=~ s/\//\\/g;
				}
				@ExtensionBlockData = ();  # clear array
			}
		}

#		check for MMP sections and get the .MMP file details
		if ($Section=~/^(MMPFILES|TESTMMPFILES)$/o) {
			$Data{Ext}='.MMP';
#			check for MAKEFILE statements for custom building
			my $SubSection = "MMP";
			if (/^MAKEFILE$/o) {
				$SubSection = $_;
				$Data{Makefile}=2;  # treat MAKEFILE=>NMAKEFILE   =1;
				$_=shift @$Line;
				$Data{Ext}=&Path_Split('Ext', $_);
			}
			if (/^NMAKEFILE$/o) {
				$SubSection = $_;
				$Data{Makefile}=2;
				$_=shift @$Line;
				$Data{Ext}=&Path_Split('Ext', $_);
			}
			if (/^GNUMAKEFILE$/o) {
				$SubSection = $_;
				$Data{Makefile}=1;
				$_=shift @$Line;
				$Data{Ext}=&Path_Split('Ext', $_);
			}
			CheckSource_MetaData(%CheckSourceMMPFILESMetaData, $CurFile, "PRJ_$Section $SubSection", $originalSourceCase{$_}, $LineNum, $CheckSource_PhysicalCheck);
			$_ = &Path_Norm ($_);
			
#			path considered relative to the current file
			$Data{Path}=&Path_Split('Path', &Path_MakeAbs($CurFile, $_));

#			this function doesn't care whether the .MMPs are listed with their extensions or not
			$Data{Base}=&Path_Split('Base', $_);
			my $MmpFile= $Data{Path}.$Data{Base};
   
#			check the file isn't already specified
  			if ($Check{$MmpFile}) {
  				push @PrjFileDie, "$CurFile($LineNum) : duplicate $Data{Base} (from line $Check{$MmpFile})\n";
   				next;
   			}
  			$Check{$MmpFile}="$CurFile: $LineNum";

#			check the file exists
			unless (-e "$Data{Path}$Data{Base}$Data{Ext}") {
				WarnOrDie(\@PrjFileDie, "$CurFile($LineNum) : $Data{Path}$Data{Base}$Data{Ext} does not exist\n");
				next LINE;
			}
			

#			process the file's attributes
			if ($Section eq 'MMPFILES') {
				foreach (@$Line) {
					if (/^TIDY$/o) {
						$Data{Tidy}=1;
						next;
					}
					if (/^IGNORE$/o) {
						next LINE;
					}
					if (/^BUILD_AS_ARM$/o) {
					  $Data{BuildAsARM}="-arm";
					  next;
					}

					push @PrjFileDie, "$CurFile($LineNum) : Don't understand .MMP file argument \"$_\"\n";
				}
			}

#			process the test .MMP file's attributes
			elsif ($Section eq 'TESTMMPFILES') {
				foreach (@$Line) {
					if (/^TIDY$/o) {
						$Data{Tidy}=1;
						next;
					}
					if (/^IGNORE$/o) {
						next LINE;
					}
					if (/^BUILD_AS_ARM$/o) {
					  $Data{BuildAsARM}="-arm";
					  next;
					}
					if (/^MANUAL$/o) {
						$Data{Manual}=1;
						next;
					}
					if (/^SUPPORT$/o) {
						$Data{Support}=1;
						next;
					}
					push @PrjFileDie, "$CurFile($LineNum) : Don't understand test .MMP file argument \"$_\"\n";
				}
			}
		}		

#		store the data
		if (($Section eq 'MMPFILES') or ($Section eq 'EXTENSIONS')) {
			if ($IgnoreMissingExtensionMakefile and $Section eq 'EXTENSIONS')
			{
				# More than more ext makefile can be missing so reset indicator
				$IgnoreMissingExtensionMakefile = 0;
			}
			else
			{
				push @$DataRef, \%Data;
			}
			next LINE;
		}
		if (($Section eq 'TESTMMPFILES') or ($Section eq 'TESTEXTENSIONS')) {
			if ($IgnoreMissingExtensionMakefile and $Section eq 'TESTEXTENSIONS')
			{
				# More than more ext makefile can be missing so reset indicator
				$IgnoreMissingExtensionMakefile = 0;
			}
			else
			{
				push @$TestDataRef, \%Data;
			}
			next LINE;
		}
		
	}
#	line loop end

#	exit if there are errors
	if (@PrjFileDie) {
		print STDERR
			"\n\"${BldInfPath}$BldInfName\" FATAL ERROR(S):\n",
			@PrjFileDie
		;
		exit 1;
	}
}


sub FatalError (@) {

	print STDERR "BLDMAKE ERROR: @_\n";
	exit 1;
}

sub CreatePlatformPm ($$$$$$) {
	my ($BatchPath, $PlatsRef, $RealPlatsRef, $RealHRef, $AllPlatDataHRef, $AllPlatTestDataHRef)=@_;


# 	exclude GCCXML, EDG and CWTOOLS  from list of RealPlats
	my @RealPlats;
	foreach my $Plat (@$RealPlatsRef){
	unless (($Plat =~ /^gccxml/i)  or  ($Plat =~ /^edg/i) or  ($Plat =~ /^cwtools/i) or ($Plat =~ /^x86gcc/i) or ($Plat =~ /^x86gmp/i)) {
# 	exclude BPABI targets from list of RealPlats provided they are not specified in the platform list
				if (grep /^$Plat$/i, @OptionalPlats) {
					if (grep /^$Plat$/, @PlatsReq) {
						push @RealPlats, $Plat;
					}
					next;
				}
				push @RealPlats, $Plat;
			}
	}


	&Output(
		"# Bldmake-generated perl file - PLATFORM.PM\n",
		"\n",
		"# use a perl integrity checker\n",
		"use strict;\n",
		"\n",
		"package Platform;\n",
		"\n",
		"use vars qw(\@Plats \@RealPlats %Programs %TestPrograms %FeatureVariantSupportingPlats);\n",
		"\n",
		"\@Plats=(\'",join('\',\'',@$PlatsRef),"\');\n",
		"\n",
		"\@RealPlats=(\'", join('\',\'',@RealPlats),"\');\n",
		"\n",
		"%Programs=(\n"
	);
	my %All; # all programs for all platforms
	my $TmpStr;
	my $Plat;
	foreach $Plat (@$PlatsRef) {
		$TmpStr="	\'$Plat\'=>[";
		if (@{${$AllPlatDataHRef}{$$RealHRef{$Plat}}}) {
			my $ProgRef;
			foreach $ProgRef (@{${$AllPlatDataHRef}{$$RealHRef{$Plat}}}) {
				$TmpStr.="'$$ProgRef{Base}',";
				$All{$$ProgRef{Base}}=1;
			}
			chop $TmpStr;
			}
		&Output(
			"$TmpStr],\n"
		);
		}
	$TmpStr="	ALL=>[";
	if (keys %All) {
		my $Prog;
		foreach $Prog (keys %All) {
			$TmpStr.="'$Prog',";
		}
		chop $TmpStr;
	}
	&Output(
		"$TmpStr]\n",
		");\n",
		"\n",
		"%TestPrograms=(\n"
	);
	%All=();
	foreach $Plat (@$PlatsRef) {
		$TmpStr="	\'$Plat\'=>[";
		if (@{${$AllPlatTestDataHRef}{$$RealHRef{$Plat}}}) {
			my $ProgRef;
			foreach $ProgRef (@{${$AllPlatTestDataHRef}{$$RealHRef{$Plat}}}) {
				$TmpStr.="'$$ProgRef{Base}',";
				$All{$$ProgRef{Base}}=1;
			}
			chop $TmpStr;
		}
		&Output("$TmpStr],\n");
	}
	$TmpStr="	ALL=>[";
	if (keys %All) {
		my $Prog;
		foreach $Prog (keys %All) {
			$TmpStr.="'$Prog',";
		}
		chop $TmpStr;
	}
	&Output(
		"$TmpStr]\n",
		");\n",
		"\n"
	);

	&Output(
		"\n",
		"%FeatureVariantSupportingPlats=("
	);

	$TmpStr = "";
	foreach $Plat (@$PlatsRef)
		{
		$TmpStr .= "\n\t$Plat=>1," if (&Plat_SupportsFeatureVariants($Plat));
		}

	chop $TmpStr;

	&Output(
		"$TmpStr\n",
		");\n",
		"\n",
		"1;\n"
	);

#	write the PLATFORM.PM file
	&WriteOutFileL($BatchPath."PLATFORM.PM");
}

sub CreatePerlBat ($) {
	my ($BldInfPath)=@_;

#	create ABLD.BAT, which will call ABLD.PL
#   NB. must quote $BldInfPath because it may contain spaces, but we know it definitely
#       ends with \ so we need to generate "\foo\bar\\" to avoid quoting the close double quote
	&Output(
		"\@ECHO OFF\n",
		"\n", 
		"REM Bldmake-generated batch file - ABLD.BAT\n",
		"REM ** DO NOT EDIT **", 
		"\n",
		"\n",
		"perl -S abld.pl \"${BldInfPath}\\\" %1 %2 %3 %4 %5 %6 %7 %8 %9\n",
		"if errorlevel==1 goto CheckPerl\n",
		"goto End\n",
		"\n",
		":CheckPerl\n",
		"perl -v >NUL\n",
		"if errorlevel==1 echo Is Perl, version 5.003_07 or later, installed?\n",
		"goto End\n",
		"\n",
		":End\n"
	);

#	check that the .BAT file does not already exist and is read-only
	if ((-e "${BldInfPath}ABLD.BAT")  && !(-w "${BldInfPath}ABLD.BAT")) {
		warn "BLDMAKE WARNING: read-only ABLD.BAT will be overwritten\n";
		chmod 0222, "${BldInfPath}ABLD.BAT";
	}

#	create the .BAT file in the group directory
	&WriteOutFileL($BldInfPath."ABLD.BAT",1);

}

sub GetArchiveExportList($) {
	my ($ExportRef) = @_;
	my $Type = $ExportRef->{Type};
	my $Src = $ExportRef->{Source};
	my $Dest = $ExportRef->{Releasable};
	$Dest = '' if (!defined($Dest));
	my @list = ();
	if ($Type eq 'zip') {
		unless (open PIPE, "unzip -l $Src | ") {
			warn "Can't unzip $Src\n";
		}
		while (<PIPE>) {
			if (/^\s*\d+\s+\S+\s+\S+\s+(.*?)\s*$/) {
#				ignore empty lines and anything that finishes with / 
				unless(($1=~/\/\s*$/) || ($1=~/^$/)) {

					my $member = $1;
					$member =~ s/\$/\$\$/g;
					if (!$Dest){
						push @list, "$ENV{EPOCROOT}$member";
					}
					else{
						push @list, "$Dest\\$member";
					}
				}
			}
		}
		close PIPE;
	}
	return @list;
}

sub CreateExportMak ($$$) {
	my ($Makefile, $ExportsRef, $ExpDirsRef)=@_;

#	create EXPORT.MAKE

	my $erasedefn = "\@erase";
	$erasedefn = "\@erase 2>>nul" if ($ENV{OS} eq "Windows_NT");
	&Output(
		"ERASE = $erasedefn\n",
		"\n",
		"\n",
		"EXPORT : EXPORTDIRS"
	);
	my $ref;
	if (@$ExportsRef) {
		foreach $ref (@$ExportsRef) {
			if ($$ref{Type} eq 'zip') {
				my @list = &GetArchiveExportList($ref);
				foreach (@list) {
					my $dst=$_;
					&Output(
						" \\\n",
						"\t$dst"
					);
				}
			} else {
				my $name=&Path_Quote($$ref{Releasable});
				&Output(
					" \\\n",
					"\t$name"
				);
			}
		}
	}
	else {
		&Output(
			" \n",
			"\t\@echo Nothing to do\n"
		);
	}
	&Output(
		"\n",
		"\n",
		"\n",
		"EXPORTDIRS :"
	);
	my $dir;
	foreach $dir (@$ExpDirsRef) {
		$dir=&Path_Quote($dir);
		&Output(
			" $dir"
		);
	}
	&Output(
		"\n",
		"\n"
	);
	foreach $dir (@$ExpDirsRef) {
		&Output(
			"$dir :\n",
			    "\t\@perl -S emkdir.pl \"\$\@\"\n",
			"\n"
		);
	}
	&Output(
		"\n",
		"\n"
	);
	foreach $ref (@$ExportsRef) {
		my $unzipoption = $$ref{UnzipOption};
		CheckSource_ExportedIncludes($$ref{Source}, $$ref{Releasable}, %CheckSourceEXPORTSIncludes);
		
		if ($$ref{Type} eq 'zip') {
			my $src = &Path_Quote($$ref{Source});
			my $destdir = &Path_Quote($$ref{Releasable});
			$destdir=$ENV{EPOCROOT} if (!defined($destdir) or ($destdir eq ''));
			my @list = &GetArchiveExportList($ref);
  			foreach (@list) {  				
  				my $dst=$_;
  				&Output(
 					"$dst : $src\n",
  				);
  			}
			if ($unzipoption =~ /overwrite/i){
				&Output(
				"\t- unzip -o $src -d \"$destdir\"\n",
				);
			}
			else{	
				&Output(
				"\t- unzip -u  -o $src -d \"$destdir\"\n",
				);
			}
		} else {
			my $dst=&Path_Quote($$ref{Releasable});
			my $src=&Path_Quote($$ref{Source});
			&Output(
				"$dst : $src\n",
					"\tcopy \"\$?\" \"\$\@\"\n",
				"\n"
			);
		}
	}
	&Output(
		"\n",
		"\n"
	);
	if (@$ExportsRef) {
		&Output(
			"CLEANEXPORT :\n"
		);
		foreach $ref (@$ExportsRef) {
			if ($$ref{Type} eq 'zip') {
				my @list = &GetArchiveExportList($ref);
				foreach (@list) {
					my $dst=$_;
					$dst =~ s/\//\\/go;
					&Output(
						"\t-\$(ERASE) \"$dst\"\n"
					);
				}
			} else {
				my $dst = $$ref{Releasable};
				$dst =~ s/\//\\/go;
				&Output(
					"\t-\$(ERASE) \"$dst\"\n"
				);
			}
		}
		&Output(
			"\n",
			"WHAT :\n"
		);
		foreach $ref (@$ExportsRef) {
			if ($$ref{Type} eq 'zip') {
				my @list = &GetArchiveExportList($ref);
				foreach (@list) {
					my $dst=$_;
					$dst =~ s/\//\\/go;
					&Output(
						"\t\@echo \"$dst\"\n"
					);
				}
			} else {
				my $dst = $$ref{Releasable};
				$dst =~ s/\//\\/go;
				&Output(
					"\t\@echo \"$dst\"\n"
				);
			}
		}
	}
	else {
		&Output(
			"CLEANEXPORT :\n",
			"\t\@echo Nothing to do\n",
			"WHAT :\n",
			"\t\@rem do nothing\n"
		);
	}

	&Output(
		"\nCHECKSOURCE :\n"
	);
	
	&Output (CheckSource_MakefileOutput(%CheckSourceEXPORTSMetaData));
	&Output (CheckSource_MakefileOutput(%CheckSourceEXPORTSIncludes));

	&Output("\n");
	
#	write EXPORT.MAKE
	&WriteOutFileL($Makefile);
}

sub CreatePlatExports ($$) {
	my ($RealPlat,$Exports)=@_;
	my $Ref;
	&Output(
 	"\n# Rules which handle the case when \$(CFG) is not defined\n\n" ,
 	"EXPORT:		\tEXPORTUREL EXPORTUDEB\n", 
 	"EXPORTCLEAN:	\tEXPORTCLEANUREL EXPORTCLEANUDEB\n",
 	"EXPORTWHAT:	\tEXPORTWHATUREL EXPORTWHATUDEB\n",
  
 	"\n# definitions \n",
 	"DATAx = $ENV{EPOCROOT}epoc32\\data\n",
 	"EMULx = $ENV{EPOCROOT}epoc32\\$RealPlat\n",
 	"URELx = $ENV{EPOCROOT}epoc32\\release\\$RealPlat\\urel\n",
 	"UDEBx = $ENV{EPOCROOT}epoc32\\release\\$RealPlat\\udeb\n",
 	"\n"
	);
	
	&Output( 
	"# Exports to emulated drive A: to Y \n\n",
	"EXPORTGENERIC : EXPORTDIRSGENERIC",
	);
	 
	my @dirs;
	my @expgen;
	my %dirsg;
	foreach $Ref (@$Exports) {
	 	if ($$Ref{emReleasable}=~/^([A-Y])(\\.*)$/){
		   my $exp="\\$$Ref{emReleasable}";
	 	   if ($$Ref{Type} eq 'zip') {	
			 my @list = &GetArchiveExportList($Ref);	
			  foreach (@list) {
				my $dst=&Path_Quote($_);
				if ($dst=~/([^\\]*)$/o){
					$dst=$1;
				}
				my $zipdest=$dst;
				$zipdest =~ s/\//\\/g;
			    push  @expgen, "$exp\\$zipdest";
			    my $zippath= &Path_Chop(&Path_Split('Path', $zipdest));
				if(!$zippath){
					$dirsg{$exp}=$exp;
				}
				else{
					 $dirsg{"$exp\\$zippath"}="$exp\\$zippath";
				}
				&Output(" \\\n","\t\$(EMULx)$exp\\$zipdest");
			 }      
		 }
		 else { 
		     my $dir =  &Path_Chop(&Path_Split('Path', $exp));  
			 push @expgen,  $exp;	
			 $dirsg{$dir}=$dir;
			 &Output(" \\\n", "\t\$(EMULx)$exp "); 
		 }
	   }
	}
	&Output("\n\nEXPORTDIRSGENERIC : ");
	foreach (keys %dirsg){
			 push @dirs, "\$(EMULx)$dirsg{$_}";
			 &Output(" \\\n", "\t\$(EMULx)$_"); 
	} 
	&Output("\n\n");
	foreach (@expgen){	 
			&Output( 
			"\$(EMULx)$_ : \t\$(DATAx)$_ \n",
			"\tcopy \"\$?\" \"\$@\" \n"
			);
	}	
	&Output("\nEXPORTCLEANGENERIC :\n");		
	foreach (@expgen){	 
			&Output("\t-@\$(ERASE) \$(EMULx)$_ \n");
	} 			
	&Output("\nEXPORTWHATGENERIC :\n");			
	foreach (@expgen){	   
			&Output("\t\@echo \$(EMULx)$_ \n");
	}
		
	&Output( 
	 		"\n\n# Exports to emulated drive Z: - UREL version \n\n",
	 		"EXPORTUREL : EXPORTDIRSUREL",
	 	   );
	
	my @expurel; 
	my %dirsurel;
	foreach $Ref (@$Exports) {
		if ($$Ref{emReleasable}=~/^(Z)(\\.*)$/){
			my $exp="\\$$Ref{emReleasable}";
	 	    if ($$Ref{Type} eq 'zip') {
			  my @list = &GetArchiveExportList($Ref);
			  foreach (@list) {
				my $dst=&Path_Quote($_);
				if ($dst=~/([^\\]*)$/o){
					$dst=$1;
				}
				my $zipdest=$dst;
				$zipdest=~ s/\//\\/g;
				push  @expurel, "$exp\\$zipdest"; 
				my $zippath= &Path_Chop(&Path_Split('Path', $zipdest)); 
				if(!$zippath){
				   $dirsurel{$exp}=$exp;
				}
				else{
					$dirsurel{"$exp\\$zippath"}="$exp\\$zippath";
				}
				&Output(" \\\n","\t\$(URELx)$exp\\$zipdest");
			}  
		}
		else {
			  my $dir =  &Path_Chop(&Path_Split('Path', $exp));  
			  push @expurel,  $exp; 
			  $dirsurel{$dir}=$dir;
			  &Output(" \\\n", "\t\$(URELx)$exp "); 
		}
	  }
	} 
	&Output("\n\nEXPORTDIRSUREL : ");
	foreach (keys %dirsurel){
			push @dirs, "\$(URELx)$dirsurel{$_}";
   			&Output(" \\\n", "\t\$(URELx)$_ "); 
	}
	&Output("\n\n");	
	foreach (@expurel){
			 &Output( 
					"\$(URELx)$_ : \t\$(DATAx)$_ \n",
					"\tcopy \"\$?\" \"\$@\" \n"
			 );
	}		
	&Output("\nEXPORTCLEANUREL :\n"); 		
	foreach (@expurel){	
			 &Output("\t-@\$(ERASE) \$(URELx)$_ \n"); 
	}  
	&Output("\nEXPORTWHATUREL :\n");	
	foreach (@expurel){	
			 &Output( "\t\@echo \$(URELx)$_ \n"); 	
	}
	   
	&Output( 
	 		"\n\n# Exports to emulated drive Z: - UDEB version \n\n",
	 		"EXPORTUDEB : EXPORTDIRSUDEB",
	);  
	 	   
	my %dirsudeb=%dirsurel;          
	my @expudeb=@expurel;
	foreach (@expudeb){
	         &Output(" \\\n", "\t\$(UDEBx)$_ ");		  
	}
	&Output("\n\nEXPORTDIRSUDEB : ");
	foreach(keys %dirsudeb){
	  		push @dirs, "\$(UDEBx)$dirsudeb{$_}";
	  		&Output(" \\\n", "\t\$(UDEBx)$_ "); 
	  	
	}
	&Output("\n\n");
	foreach (@expudeb){
	 		 &Output( 
					"\$(UDEBx)$_ : \t\$(DATAx)$_ \n",
					"\tcopy \"\$?\" \"\$@\" \n"
			 );
	}			
	&Output("\nEXPORTCLEANUDEB :\n");
	foreach (@expudeb){	
			 &Output("\t-@\$(ERASE) \$(UDEBx)$_ \n"); 
	}  
	&Output("\nEXPORTWHATUDEB :\n");	
	foreach (@expudeb){	
			 &Output("\t\@echo \$(UDEBx)$_ \n"); 	
	}
	
    &Output("\n# Directories \n\n");  
	&Output(join (" \\\n", @dirs)." :")       
	&Output("\n\t\@perl -S emkdir.pl \$@\n\n");			
		   		
}

sub CreatePlatMak ($$$$$$$$$;$) {
	my ($BatchPath, $E32MakePath, $DataRef, $Plat, $RealPlat, $RomDir, $Module, $BldInfPath, $Exports, $Test, $FeatureVariant)=@_;
	$FeatureVariant = "" if (!$FeatureVariant);

	unless ($Test) {
		$Test='';
	}
	else {
		$Test='TEST';
	}

	my $Ref;
	my $eDrive=0;
	if ($RealPlat =~ /^WINS/) {
	    foreach $Ref (@$Exports) {
			if ($$Ref{emReleasable}=~/^([A-Z])(\\.*)$/) {
				$eDrive=1;
				last;
			}
		}
	} 


	my $OutRomFile="$RomDir$RealPlat$Test.IBY";
	my $GCCDir="gcc\$(PBUILDPID)\\bin";
	
	my $erasedefn = "\@erase";
	$erasedefn = "\@erase 2>>nul" if ($ENV{OS} eq "Windows_NT");

# Get the root platform name to support hierarchy of customizations	
	my $root = Plat_Root($Plat);

	my $config_file = "";

	if (grep /^$root$/i, @BPABIPlats) {
		$config_file = BPABIutl_Config_Path($root);
	}

	my $rvct_path = "";

	if ( $config_file ) {

		if ($root =~ /^ARMV\d/) {

			unless ( RVCT_plat2set::compiler_exists($Plat) )
			{
				FatalError("Can't find any RVCT installation.");
			}

			my $rvct_ver = RVCT_plat2set::get_version_string($Plat);

			if ($Plat =~ "^ARMV5" && $rvct_ver lt "2.2.559")
			{
				warn "BLDMAKE WARNING: ARMV5 requires at least RVCT 2.2.559.";
				OutText();
				return;
			}

			if ($Plat =~ "^ARMV6" && $rvct_ver lt "2.2.559")
			{
				warn "BLDMAKE WARNING: ARMV6 requires at least RVCT 2.2.559.";
				OutText();
				return;
			}

			if ($Plat =~ "^ARMV7" && $rvct_ver lt "3.1.674")
			{
				warn "BLDMAKE WARNING: ARMV7 requires at least RVCT 3.1.674.";
				OutText();
				return;
			}

			my $rvct_bin_name = RVCT_plat2set::get_bin_name($Plat);
			my $rvct_bin_path = RVCT_plat2set::get_bin_path($Plat);
			my $rvct_inc_name = RVCT_plat2set::get_inc_name($Plat);
			my $rvct_inc_path = RVCT_plat2set::get_inc_path($Plat);
			my $rvct_lib_name = RVCT_plat2set::get_lib_name($Plat);
			my $rvct_lib_path = RVCT_plat2set::get_lib_path($Plat);

			main::Output("export $rvct_bin_name:=$rvct_bin_path\n");
			main::Output("export $rvct_inc_name:=$rvct_inc_path\n");
			main::Output("export $rvct_lib_name:=$rvct_lib_path\n");

			my ($rvct_M, $rvct_m, $rvct_b) = RVCT_plat2set::get_version_list($Plat);

			Output( "\n" );
			Output( "export RVCT_VER_MAJOR:=$rvct_M\n" );
			Output( "export RVCT_VER_MINOR:=$rvct_m\n" );
			Output( "export RVCT_VER_BUILD:=$rvct_b\n" );

			$rvct_path = "\$($rvct_bin_name);"; # Example: '$(RVCT22BIN);'.
		}

		&Output(
			"\n",
			"export PLAT:=${Plat}\n\n",
			"include $config_file\n\n"
		);
	}
# modified start: makefile improvement 
	unless($FeatureVariant eq "")
	{
# modified by SV start: makefile improvement 
		foreach $Ref (@$DataRef) {
			&Output(
			"include $BatchPath"."FeatureVariantInfo\\".uc($Plat)."\\"."$Plat$FeatureVariant.$$Ref{Base}.info\n\n",
			);
		}
# modified by SV end: makefile improvement 
	}
# modified end: makefile improvement 
	# Don't hardcode the rvct path if rvct auto switch feature is not enabled.
	if ($ENV{ABLD_PLAT_INI}) 
	{
		&Output(
		'export Path:=',&main::Path_Drive,$E32env::Data{EPOCPath},$GCCDir,";", $rvct_path,"\$(Path)\n",
		"export PATH:=\$(Path)\n"
		);
	}
	else
	{
		&Output(
		'export Path:=',&main::Path_Drive,$E32env::Data{EPOCPath},$GCCDir,";", "\$(Path)\n",
		"export PATH:=\$(Path)\n"
		);
	}

	&Output(		
		"\n",
		"# prevent MAKEFLAGS variable from upsetting calls to NMAKE\n",
		"unexport MAKEFLAGS\n",
		"\n",
		"ERASE = $erasedefn\n",
		"\n",
		"\n",
		"ifdef EFREEZE_ALLOW_REMOVE\n",
		"REMOVEMACRO := EFREEZE_ALLOW_REMOVE=-remove\n",
		"endif\n",
		"\n",
		"\n"
	);

	if ($eDrive) {
		# Generate exports into emulated drives
		&CreatePlatExports($RealPlat,$Exports);
	}
	my $Command;
	foreach $Command (qw(CLEAN CLEANMAKEFILE CLEANALL FINAL FREEZE LIBRARY MAKEFILE RESOURCE SAVESPACE TARGET LISTING WHATMAKEFILE)) {
		&Output(
			"$Command :"
		);

			 if ($eDrive and $Command eq 'CLEAN'){
				 &Output(" EXPORTCLEANGENERIC EXPORTCLEAN\$(CFG) ");
				 foreach $Ref (@$DataRef) {
					 &Output(" $Command$$Ref{Base}");
				 }
			 }	    	 
			 elsif ($eDrive and $Command eq 'RESOURCE'){
				 &Output(" EXPORTGENERIC EXPORT\$(CFG) ");
			   	 foreach $Ref (@$DataRef) {
					 &Output(" $Command$$Ref{Base}");
				 }
				 
				 foreach $Ref (@$DataRef) {
					 &Output("\n\nRESOURCE$$Ref{Base} : EXPORTGENERIC EXPORT\$(CFG)");
				 }
			  }
			  else {
			        if(@$DataRef){
			        	foreach $Ref (@$DataRef) {
			        		&Output(" $Command$$Ref{Base}");
			           }
			          } 		
			         else {
			         	&Output("\n","\t\@echo Nothing to do\n");
			         }
			  }  
		&Output("\n","\n");
	}
	
	&Output(
		"WHAT :"
	);
	if($eDrive){
	  &Output(" EXPORTWHATGENERIC EXPORTWHAT\$(CFG) ");
	}
	my $whatcount=0;
	foreach $Ref (@$DataRef) {
		unless ($$Ref{Tidy}) {
			$whatcount++;
			&Output(
				" WHAT$$Ref{Base}"
			);
		}
	}
	if ($whatcount==0 and !$eDrive) {
		&Output(
			"\n",
			"\t\@rem do nothing\n" 
		);
	}

	&Output(
		"\n",
		"\n",
		"CHECKSOURCE :"
	);
	my $CheckSource=' CHECKSOURCE_GENERIC';
	foreach $Ref (@$DataRef) {
		$CheckSource.=" CHECKSOURCE$$Ref{Base}" if ($$Ref{Ext} eq ".MMP");
	}
	&Output(
		"$CheckSource\n"
	);

	&Output(
		"\n",
		"CHECKSOURCE_GENERIC :\n"
	);

	if ($CheckSourceBldInfIncludes{$Plat})
		{
		my %dummy;
		$dummy{$CheckSourceBldInfIncludes{$Plat}} = 1;
		&Output (CheckSource_MakefileOutput(%dummy));		
		}

	&Output (CheckSource_MakefileOutput(%CheckSourceMMPFILESMetaData));
	&Output (CheckSource_MakefileOutput(%CheckSourceEXTENSIONSMetaData));
	
	&Output(
		"\n",
		"\n",
		"TIDY :"
	);
	my $Tidy='';
	foreach $Ref (@$DataRef) {
		if ($$Ref{Tidy}) {
			$Tidy.=" TIDY$$Ref{Base}";
		}
	}
	if ($Tidy) {
		&Output(
			"$Tidy\n"
		);
	}
	else {
		&Output(
			"\n",
			"\t\@echo Nothing to do\n"
		);
	}
	&Output(
		"\n",
		"\n"
	);
#	change for non-EPOC platforms
	if ($RealPlat=~/^(WINS|WINSCW|WINC|TOOLS|TOOLS2)$/o) {
		&Output(
			"ROMFILE :\n"
		);
	}
	else {
		&Output(
			'ROMFILE : STARTROMFILE'
		);
		foreach $Ref (@$DataRef) {
			&Output(
				" ROMFILE$$Ref{Base}"
			);
		}
		&Output(
			"\n",
			"\n",
			"STARTROMFILE :\n",
			    "\t\@perl -S emkdir.pl \"", &Path_Chop($RomDir), "\"\n",
			    "\t\@echo // $OutRomFile > $OutRomFile\n",
			    "\t\@echo // >> $OutRomFile\n"
		);
		if ($Test) {
			my ($Auto, $Manual);
			foreach $Ref (@$DataRef) {
				++$Auto unless ($$Ref{Manual} or $$Ref{Support});
				++$Manual if ($$Ref{Manual});
			}
			if ($Auto) {
				my $IbyTextFrom="data=$BatchPath$Plat.AUTO.BAT";
				my $IbyTextTo="Test\\$Module.AUTO.BAT";
				my $Spaces= 60>length($IbyTextFrom) ? 60-length($IbyTextFrom) : 1; 
				&Output("\t\@echo ", $IbyTextFrom, ' 'x$Spaces, $IbyTextTo, ">> $OutRomFile\n");
			}
			if ($Manual) {
				my $IbyTextFrom="data=$BatchPath$Plat.MANUAL.BAT";
				my $IbyTextTo="Test\\$Module.MANUAL.BAT";
				my $Spaces= 60>length($IbyTextFrom) ? 60-length($IbyTextFrom) : 1; 
				&Output("\t\@echo ", $IbyTextFrom, ' 'x$Spaces, $IbyTextTo, ">> $OutRomFile\n");
			}
		}
	}
	&Output(
		"\n",
		"\n"
	);
	my $CallNmake='nmake -nologo -x - $(VERBOSE) $(KEEPGOING)';
	my $CallGNUmake='$(MAKE) $(VERBOSE) $(KEEPGOING)';

	my %PlatHash;
	&Plat_GetL($RealPlat, \%PlatHash);
	my $CallMake=$CallNmake;
	if ($PlatHash{MakeCmd} eq "make") {
		$CallMake="$CallGNUmake -r";
	}
	&Plat_GetL($Plat, \%PlatHash);
	
	foreach $Ref (@$DataRef) {

#		standard commands
		unless ($$Ref{Makefile}) {
			my $MakefilePath=join('', &Path_Chop($E32MakePath), $BldInfPath, $$Ref{Base}, "\\", $RealPlat, "\\");
# modified start: makefile improvement 
			my $RealMakefile;
			if($FeatureVariant eq "")
			{
				$RealMakefile="-f \"$MakefilePath$$Ref{Base}.$RealPlat$FeatureVariant\"";
			}
			else{
				$RealMakefile="-f \"$MakefilePath$$Ref{Base}.$RealPlat.\$(VARIANT_PLAT_NAME_$$Ref{Base})\"";
			}
# modified end: makefile improvement 
			my $MakefileBase="$MakefilePath$$Ref{Base}";		

			if($Plat eq 'VS6' || $Plat eq 'VS2003')
			{
				$CallMake .= "-f ";
				$RealMakefile = "$MakefileBase$PlatHash{Ext}";
			}
			&Output(
				"MAKEFILE$$Ref{Base}_FILES= \\\n",
					"\t\"$MakefileBase$PlatHash{Ext}$FeatureVariant\"",
			);
#			changes for WINS/WINSCW/WINC and VC6
			if ($Plat =~ /^VC6/) {
				&Output(
					" \\\n\t\"$MakefileBase.DSW\"",
					" \\\n\t\"$MakefileBase.SUP.MAKE\""
				);
			}
			if ($Plat eq 'CW_IDE') {
				&Output(
					" \\\n\t\"$MakefileBase.pref\""		# Defect: actually uses $BaseTrg, not mmp file name
				);
			}
			if ($RealPlat=~/^(WINS|WINSCW|WINC)$/o) {
				&Output(
					" \\\n\t\"$MakefileBase.UID.CPP\""	# Defect: actually uses $BaseTrg, not mmp file name
				);
			}
			
  			my $bld_flags="";
			$bld_flags="\$(ABLD_FLAGS)" if (($Plat =~ /^ARMV5(_ABIV1)?$/ || grep /$Plat/i, @BPABIPlats) || (Plat_Root($Plat) =~ /^ARMV5(_ABIV1)?$/ || grep /$Plat/i, @BPABIPlats));
			
			
			my $build_as_arm_arg="";
			$build_as_arm_arg = $$Ref{BuildAsARM} if ($$Ref{BuildAsARM});

			# Compiler Wrapper option Support  
			# Generate the flag to keep the Compiler Wrapper option information
			my $cmp_wrap_flag="";
			if (($Plat =~ /^ARMV5(_ABIV1)?$/ || grep /$Plat/i, @BPABIPlats) || ($Plat=~/^WINSCW$/) || (Plat_Root($Plat) =~ /^ARMV5(_ABIV1)?$/ || grep /$Plat/i, @BPABIPlats))
			{
				# for armv5 , armv5_abiv1, winscw and all bpabi plaforms
				$cmp_wrap_flag="\$(ABLD_COMPWRAP_FLAG)" ;
			}

			&Output(
				"\n",
				"\n",
				"MAKEFILE$$Ref{Base} :\n",
				    "\tperl -S makmake.pl \$(NO_DEPENDENCIES) -D $$Ref{Path}$$Ref{Base} $Plat$FeatureVariant $build_as_arm_arg $bld_flags $cmp_wrap_flag\n",

				"\n",
				"CLEANMAKEFILE$$Ref{Base} :\n",
				    "\t-\$(ERASE) \$(MAKEFILE$$Ref{Base}_FILES)\n",
				"\n",
				"WHATMAKEFILE$$Ref{Base} :\n",
				    "\t\@echo \$(MAKEFILE$$Ref{Base}_FILES)\n",
				"\n",
				"TARGET$$Ref{Base} :\n",
				    "\t$CallMake $RealMakefile \$(CFG)\n",
				"\n",
				"SAVESPACE$$Ref{Base} :\n",
				    "\t$CallMake $RealMakefile \$(CFG) CLEANBUILD\$(CFG)\n",
				"\n",
				"LISTING$$Ref{Base} :\n",
				    "\t$CallMake $RealMakefile MAKEWORK\$(CFG) LISTING\$(CFG)\$(SOURCE)\n",
				"\n",
				"FINAL$$Ref{Base} :\n",
				    "\t\@rem do nothing\n",
				"\n",
			);
			foreach $Command (qw(CLEANALL)) {
				&Output(
					"CLEANALL$$Ref{Base} :\n",
					"\tperl -S ermdir.pl $MakefilePath\n",
					"\n",
				);
			}
			foreach $Command (qw(CLEAN RESOURCE)) {
				&Output(
					"$Command$$Ref{Base} :\n",
					    "\t$CallMake $RealMakefile $Command\$(CFG)\n",
					"\n"
				);
			}
			foreach $Command (qw(LIBRARY)) {
				&Output(
					"$Command$$Ref{Base} :\n",
					    "\t$CallMake $RealMakefile $Command\n",
					"\n"
				);
			}
			foreach $Command (qw(FREEZE)) {
				&Output(
					"$Command$$Ref{Base} :\n",
					    "\t$CallMake $RealMakefile $Command \$(REMOVEMACRO)\n",
					"\n"
				);
			}
			unless ($$Ref{Tidy}) {
				&Output(
					"WHAT$$Ref{Base} :\n",
					    "\t\@$CallMake -s $RealMakefile WHAT\$(CFG)\n",
					"\n"
				);
			}
			else {
				&Output(
					"TIDY$$Ref{Base} :\n",
					    "\t$CallMake $RealMakefile CLEANRELEASE CLEANLIBRARY\n",
					"\n"
				);
			}
			
			&Output(
				"CHECKSOURCE$$Ref{Base} :\n",
					"\t\@$CallMake -s $RealMakefile CHECKSOURCE\n",
					"\t\@$CallMake -s $RealMakefile CHECKSOURCE\$(CFG)\n",
				"\n"
				);
			&Output(
				"ROMFILE$$Ref{Base} :\n",
				    "\t\@$CallMake $RealMakefile ROMFILE >> $OutRomFile\n",
				"\n",
				"\n"
			);
		}

#		calls to custom makefiles
		else {
			my $ChopRefPath=&Path_Chop($$Ref{Path});
			my $ChopBldInfPath=&Path_Chop($BldInfPath);
			my $MakefileCall;
			if ($$Ref{Makefile}==2) {
				$MakefileCall="cd $ChopRefPath;$CallNmake";
			} else {
				$MakefileCall="$CallGNUmake -C $ChopRefPath";
			}
			$MakefileCall.=" -f \"$$Ref{Base}$$Ref{Ext}\" TO_ROOT=";
			$MakefileCall.=&Path_Chop(&Path_UpToRoot($$Ref{Path}));
			$MakefileCall.=" EPOCBLD=";
			$MakefileCall.=join('', &Path_Chop(&Path_UpToRoot($$Ref{Path})), &Path_Chop($E32MakePath), $BldInfPath, $$Ref{Base}, "\\", $RealPlat);
			$MakefileCall.=" TO_BLDINF=";
			$MakefileCall.=join('', &Path_Chop(&Path_UpToRoot($$Ref{Path})), $ChopBldInfPath);
			if ($$Ref{ExtensionRoot}) {
				$MakefileCall.=" EXTENSION_ROOT=".&Path_Chop($$Ref{ExtensionRoot});
			}
			if ($$Ref{BuildAsARM}) {
			  $MakefileCall.=" BUILD_AS_ARM=1";
			}			  
			&Output(
# should change to MAKEFILE
				"MAKEFILE$$Ref{Base} :\n",
				    "\t$MakefileCall PLATFORM=$Plat MAKMAKE\n",
				"\n",
# should call in custom makefiles maybe
				"CLEANMAKEFILE$$Ref{Base} :\n",
				"#	$MakefileCall PLATFORM=$Plat CLEANMAKEFILE\n",
				"\n",
				"WHATMAKEFILE$$Ref{Base} :\n",
				"#	\@$MakefileCall -s PLATFORM=$Plat WHATMAKEFILE\n",
				"\n",
# should change to TARGET
				"TARGET$$Ref{Base} :\n",
				    "\t$MakefileCall PLATFORM=$RealPlat CFG=\$(CFG) BLD\n",
				"\n",
# should ignore this target and just call the TARGET target instead?
				"SAVESPACE$$Ref{Base} :\n",
				    "\t$MakefileCall PLATFORM=$RealPlat CFG=\$(CFG) SAVESPACE\n",
				"\n",
				"LISTING$$Ref{Base} :\n",
				"\n",
				"\n",
# should change to LIBRARY
				"LIBRARY$$Ref{Base} :\n",
				    "\t$MakefileCall PLATFORM=$RealPlat LIB\n",
				"\n",
				"FREEZE$$Ref{Base} :\n",
					"\t$MakefileCall PLATFORM=$RealPlat FREEZE \$(REMOVEMACRO)\n",
				"\n",
			);

			foreach $Command (qw(CLEANALL)) {
				&Output(
					"$Command$$Ref{Base} :\n",
					"\t$MakefileCall PLATFORM=$RealPlat CFG=\$(CFG) CLEAN\n",
					"\n"
				);
			}

			foreach $Command (qw(CLEAN RESOURCE FINAL)) {
				&Output(
					"$Command$$Ref{Base} :\n",
					    "\t$MakefileCall PLATFORM=$RealPlat CFG=\$(CFG) $Command\n",
					"\n"
				);
			}
			unless ($$Ref{Tidy}) {
# should change to WHAT
				&Output(
					"WHAT$$Ref{Base} :\n",
					    "\t\@$MakefileCall -s PLATFORM=$RealPlat CFG=\$(CFG) RELEASABLES\n",
					"\n"
				);
			}
			else {
				&Output(
					"TIDY$$Ref{Base} :\n",
					    "\t$MakefileCall PLATFORM=$RealPlat TIDY\n",
# should change to CLEANLIBRARY
					    "\t$MakefileCall CLEANLIB\n",
					"\n"
				);
			}
			&Output(
				"ROMFILE$$Ref{Base} :\n",
				    "\t\@$MakefileCall PLATFORM=$RealPlat ROMFILE >> $OutRomFile\n",
				"\n",
				"\n"
			);
		}

	}
	
	&WriteOutFileL("$BatchPath$Plat$FeatureVariant$Test.MAKE");
}

sub CreatePlatBatches ($$$) {
	my ($OutDir, $DataRef, $Plat)=@_;

#	create the test batch files
#	this function won't work properly if the target basename is different from the .MMP basename
#	so perhaps it should call makmake on the .mmp file to check

	my $AutoText;
	my $ManualText;

	my $Ref;
	foreach $Ref (@$DataRef) {
		if ($$Ref{Manual}) {
			$ManualText.="$$Ref{Base}\n";
			next;
		}
		if ($$Ref{Ext} eq ".MK") {
			next;
		}
		if ($$Ref{Support}) {
			next;
		}
		else {
			$AutoText.="$$Ref{Base}\n";
		}
	}

	if ($AutoText) {
		&Output($AutoText);
		&WriteOutFileL("$OutDir$Plat.AUTO.BAT");
	}

	if ($ManualText) {
		&Output($ManualText);
		&WriteOutFileL("$OutDir$Plat.MANUAL.BAT");
	}
}

sub WriteOutFileL ($$) { # takes batch file and boolean read-only flag
	my ($BATFILE, $ReadOnly)=@_;

	$BATFILE=~ s/\//\\/g;  # convert unix slashes from wrappermakefile.pm

	eval { &Path_MakePathL($BATFILE); };
	&FatalError($@) if $@;

	open BATFILE,">$BATFILE" or &FatalError("Can't open or create Batch File \"$BATFILE\"");
	print BATFILE &OutText or &FatalError("Can't write output to Batch File \"$BATFILE\"");
	close BATFILE or &FatalError("Can't close Batch File \"$BATFILE\"");
}