# Copyright (c) 2001-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:
# Makmake-module for creating XML files which can be imported as CodeWarrior IDE projects
# 
#

package Ide_cw;

my $CW_minimum_supported_version = 2.8;

# declare variables global for module
my $BaseAddress="";
my %IdeBlds=();
my %PrjHdrs=();
my @Win32LibList=();
my $Win32Resrc;
my $Win32StdHeaders;

my $ExtraFilesPath="";
my @KnownRoots=();

my @addedFiles=();
my $addHeaders = 1;
my $addDocuments = 1;

my %processedPlatforms;

require Exporter;
@ISA=qw(Exporter);

@EXPORT=qw(
	PMHelp_Mmp

	PMCheckPlatformL

	PMPlatProcessMmp

	PMStartBldList
		PMBld
			PMResrcBld

	PMEndSrcList
);

require Cl_bpabi;
use BPABIutl;
use E32Variant;
use E32Plat; 
use Winutl;
use Armutl;
use Pathutl;
use Win32API::Registry qw( :ALL );
use Preprocessor;
use RVCT_plat2set;

sub PMHelp_Mmp {
	&Winutl_Help_Mmp;
}

my $RVCTMajorVersion = Armutl_RVCTMajorVersion();
my $RVCTMinorVersion = Armutl_RVCTMinorVersion();
my $RVCTVersion = "${RVCTMajorVersion}_${RVCTMinorVersion}";

my $oP = "--";
$oP = "-" if ($RVCTMajorVersion == 2 && $RVCTMinorVersion < 2);

my $cfgMacro = &Variant_GetMacro();

use FindBin;
use lib $FindBin::Bin."\\perllib";

#use the DOM xml module
use XML::DOM;

my $projectTemplate;
my $linkDescriptorTemplate;

my $xmlParser;
my $xmlProjectDoc;
my $xmlLinkDescriptorDoc;
my $xmlLinkDescriptorCommandParent;
my $xmlLinkDescriptorSymbolParent;
my $xmlLinkDescriptorDumpFileParent;

my $xmlLinkOrder;		# for accumulating the link order
my $xmlFileList;		# for accumulating the file list
my $xmlSourceGroup;		# for accumulating the source
my $xmlHeadersGroup;	# for accumulating local header files
my $xmlRootGroup;		# for accumulating files to be added outside of project groups
my $xmlLinkGroup;		# for accumulating the link descriptor files
my $xmlLibGroup;		# for accumulating the libraries
my $xmlResourcesGroup;	# for accumulating resource related files
my $xmlDocumentsGroup;	# for accumulating supported files specified with DOCUMENT in the .mmp file

my $MmpFile;
my $VariantFile;
my $PrefixFile;

my $CW_major_version;
my $CW_minor_version;
my $CW_libpath;
my @CW_librarypath;
my @ToolChainLibList;

# Hash to store configuration makefile data for furthur processing
my %configdata;

sub RoundUp1k($) {
	# Accept C hexadecimal number (0xNNN).  Convert argument to Kb
	# rounded up to the next 1kb boundary.
	use integer;
	return (hex($_[0]) + 1023) / 1024;
}

use Genutl;
use cl_generic;

my $ToolPrefix='';
my $HelperLib='';
my %PlatOpt=(
	'Dlltool'=>'',
	'Entry'=>'-e',
	'Gcc'=>'',
	'Ld'=>'',
	'Petran'=>'',
	'Optimize'=>'-O'
);
my $Dlltool;
my $Archive;
my $Link;
my $Objcopy;

my %CompatibleABIs=(
	ARMI=>['ARM4', 'THUMB'],
	ARM4=>['ARMI'],
	THUMB=>['ARMI'],
	ARMV5=>['ARMV5_ABIV1'],
	GCCE=>['GCCE'],
	BPABI=>['ARMV5_ABIV2']
);
my @CompatibleABIs;

my $Makecmd;
my %ABILibPath=();

sub SystemTarget() {
	return 1 if &main::SystemTrg;
	my $ExportLibrary=&main::ExportLibrary;
	# N.B. should get better way to detect kernel probably!!
	return 1 if ($ExportLibrary =~ /EKERN/i);
	
	return 0;
}

sub SysTrg () {
	return 1 if &main::SystemTrg;
	my $ExportLibrary=&main::ExportLibrary;
	return 1 if ($ExportLibrary =~ /EKERN/i);
	my $Trg=&main::Trg;
	return 1 if ($Trg =~ /KSRT/i);
	return 0;
}

sub PMUnderlyingABI($) {
	my ($ABI) = @_;
	if ($ABI eq 'ARM4T') {
		if (&main::BuildAsARM) {
			return 'ARMI';
		}
		elsif (SystemTarget()) {
			return 'ARM4';
		}
		else {
			return 'ARMV4';
		}
	}
	return $ABI;
}

##### ARMV5 specific options #####
my $diag_suppressions;
my $diag_warnings;
my $diag_errors;

#The options here are handcoded for ABIV1 mode.
my $contingentOptions;
my $exceptions = ' --exceptions --exceptions_unwind';
my $thumbOptions = '--thumb ';
my $armOptions = '--arm ';
my $kernelOptions = '--arm --no_exceptions --no_exceptions_unwind';
my $invariantOptions = 
  '--cpu 5T --enum_is_int -Ono_known_library --fpmode ieee_no_fenv --export_all_vtbl --no_vfe --apcs /inter';
$invariantOptions .= ' --dllimport_runtime' unless ($RVCTMajorVersion == 2 && $RVCTMinorVersion < 2);  
# specify floating point model here
my $floatingpointmodel = "softvfp";
if (&main::ARMFPU && (&main::ARMFPU =~ /^VFPV2$/i)) {
	$floatingpointmodel = "vfpv2";
}
my $floatingpoint = ' --fpu '.$floatingpointmodel.'';

# Set compiler settings
$diag_suppressions = '--diag_suppress 66,161,611,654,997,1152,1300,1464,1488,6318,6331';
$diag_warnings = '';
$diag_errors = '--diag_error 1267';
my $commonOptions = "$diag_suppressions $diag_warnings $diag_errors";  
# variables added to fetch the options from the BSF files
my @commonOptions;
my @thumbOptions;
my @armOptions;
my @kernelOptions;
my @invariantOptions;
my @linkerOptions;
my @archiverOptions;

my $linkCommand = "";		
# variables to process and store the default and BSF file options
my $invopts = $invariantOptions;
my $linkeropts;
my $archiveropts;
#varible to store the BSF add options
my $bsfaddoptions = undef;
#variable to store the compiler flags, defs and other options
my $CCFLAGS = undef;
my $CCDEFS = undef;
my $CCUREL = undef;
my $CCUDEB = undef;


#GCCE specific OPIONS

my $GCCE_CompilerOption = '-march=armv5t -mthumb-interwork -mapcs';
$GCCE_CompilerOption .= " -msoft-float -fexceptions -pipe -nostdinc -Wall -Wno-ctor-dtor-privacy -Wno-unknown-pragmas";
#Flag to check for the customized GCCE platform
my $CustGCCE=0;
#Variable to fetch the Customized platform
my %CustPlat=&main::PlatRec;
#Flags to read the options
my $kernelOption=0;
my $buildAsArmOption=0;
my $thumbOption=0;


my $ArmIncDir;
my @ArmLibList;
my $ArmRT;

my %BSF_keywords = (
		    COMMON_OPTIONS => 1,
		    THUMB_OPTIONS => 1,
		    ARM_OPTIONS => 1,
		    KERNEL_OPTIONS => 1,
		    INVARIANT_OPTIONS => 1,
			LD_OPTIONS => 1,
			AR_OPTIONS => 1
		   );
sub PMStartBldList($) 
{
	($Makecmd) = @_;
	my @BldList=&main::BldList;
	my $BasicTrgType=&main::BasicTrgType;
	my $ABI=&main::ABI;
	my $kernelOption=0;
	my $buildAsArmOption=0;
	my $thumbOption=0;
	my @MacroList=&main::MacroList();
	push @MacroList, "__SUPPORT_CPP_EXCEPTIONS__ ";
	my %plat = &main::PlatRec();

	$CCFLAGS = "";
	$CCDEFS = "";
	$CCUREL = "";
	$CCUDEB = "";
	
	$VariantFile=&main::VariantFile();	

	#read the configuration make file into the hash for only the BPABI platforms
	my $config_file = BPABIutl_Config_Path(&main::Plat);
	if ($config_file) {
		collect_config_data($config_file);
	}
		
	Read_BSF_Options() if ($plat{'CUSTOMIZES'});

	if (SysTrg()) {
		$kernelOption=1;
	} elsif (main::BuildAsARM() or ($ABI eq 'ARMV4')) {
		$buildAsArmOption=1;
	} else {
		$thumbOption=1;
	}

	my $OtherOpts = undef;
	my $toolchain = $configdata{COMPILER_PLAT};
	
	$OtherOpts = &main::CompilerOption($toolchain);
	
	if($kernelOption==1)
	{
		if(@kernelOptions) {
# Kernel options as read from BSF file (KERNEL_OPTIONS keyword)
			Set_BSF_Options('KERNEL_OPTIONS',\@kernelOptions);
		}
		$OtherOpts .= " ".fetch_config_data($configdata{KERNEL_OPTIONS});
	}
	elsif($buildAsArmOption==1)
	{	
		if(@armOptions) {
# Arm options as read from BSF file (ARM_OPTIONS keyword)
			Set_BSF_Options('ARM_OPTIONS',\@armOptions);
		}
		$OtherOpts .= " ".fetch_config_data($configdata{ARM_OPTIONS});	
	}
	elsif($thumbOption==1)
	{
		if(@thumbOptions) {
# Thumb options as read from BSF file (THUMB_OPTIONS keyword)
			Set_BSF_Options('THUMB_OPTIONS',\@thumbOptions);
		}
		$OtherOpts .= " ".fetch_config_data($configdata{THUMB_OPTIONS});
	}
	
	if($thumbOption==1 || $buildAsArmOption==1)
	{
		if (&main::ARMFPU && (&main::ARMFPU =~ /^VFPV2$/i))
		{
			$OtherOpts .= "	".$configdata{VFP2MODE_OPTION};
		}
		else
		{
			$OtherOpts .= "	".$configdata{SOFTVFPMODE_OPTION};
		}
	}

	if ($thumbOption==1)
	{
		$OtherOpts .= "	".$configdata{COMPILER_THUMB_DEFINES};
	}
	
	$OtherOpts .= "	".$configdata{COMPILER_INTERWORK_DEFINES};
	
	if((&main::Plat eq "ARMV5_ABIV1") || (&main::Plat eq "ARMV5" && !$cfgMacro)
	|| ($CustPlat{'CUSTOMIZES'} 
	&& (($CustPlat{'ROOTPLATNAME'} eq "ARMV5_ABIV1" && $cfgMacro) 
	|| ($CustPlat{'ROOTPLATNAME'} eq "ARMV5" && !$cfgMacro)))) {
		ComputeCompilerOpts();
	}
	elsif($config_file) {
		$CCFLAGS = &Cl_bpabi::getConfigVariable('CCFLAGS');
	}
	$CCFLAGS .= $OtherOpts;

	if(@invariantOptions)
	{
	# Invariant options as read from BSF file (INVARIANT_OPTIONS keyword)
		Set_BSF_Options('INVARIANT_OPTIONS',\@invariantOptions);
	}
	if(@commonOptions)
	{
	# Common options as read from BSF file (COMMON_OPTIONS keyword)
		Set_BSF_Options('COMMON_OPTIONS',\@commonOptions);
	}
	if(@linkerOptions)
	{
	# Invariant options as read from BSF file (LD_OPTIONS keyword)
		Set_BSF_Options('LD_OPTIONS',\@linkerOptions);
	}
	if ($BasicTrgType=~/^LIB$/o) {
		if(@archiverOptions)
		{
		# Invariant options as read from BSF file (AR_OPTIONS keyword)
			Set_BSF_Options('AR_OPTIONS',\@archiverOptions);
		}	
	}

	if($bsfaddoptions)
	{
		fixbsfoptions($bsfaddoptions);
	}
	$CCFLAGS .= $bsfaddoptions;
	$CCDEFS = $configdata{COMPILER_DEFINES};
	$CCDEFS .= printlist("-D", @MacroList);
	$CCDEFS .= $configdata{PLATFORM_DEFINES};

	if($kernelOption==1)
	{
		$CCDEFS .= -D__KERNEL_MODE__;
	}
	
	if($VariantFile) { 
		$CCDEFS .= " -D__PRODUCT_INCLUDE__="."\\\"".&main::Path_Split('File',$VariantFile)."\\\"";
	}
	
	foreach (@BldList) 
	{
		if($kernelOption == 0)
		{
			if (/DEB$/o) {
				$CCUDEB .= " ".$configdata{EXCEPTIONS};
			}
			else {
				$CCUREL .= " ".$configdata{EXCEPTIONS};
			}
		}
		#collect the options and macro's depending on the whether it is a UREL or UDEB		
		my @ml = &main::MacroList($_);
		if (/DEB$/o) {
			$CCUDEB .= " ".$CCFLAGS;
			$CCUDEB .= printlist("-D", @ml);
			$CCUDEB .= " ".$CCDEFS;
		}
		else {
			$CCUREL .= " ".$CCFLAGS;
			$CCUREL .= printlist("-D", @ml);
			$CCUREL .= " ".$CCDEFS;
		}
	}
}

sub ComputeCompilerOpts() {
	my %plat = &main::PlatRec();
	my $ABI=&main::ABI;
	my $TrgType=&main::TrgType;

	if (SysTrg()) {
	        $contingentOptions = $kernelOptions;
        } elsif (main::BuildAsARM() or ($ABI eq 'ARMV4')) {
	        $contingentOptions = $armOptions.$floatingpoint.$exceptions;
	    } else {
			$contingentOptions = $thumbOptions.$floatingpoint.$exceptions.' -D__MARM_THUMB__';	
	}
	# change support for ARMV4
	if ($ABI eq 'ARMV4') {
		$invopts =~ s/5T/4/;
		$invopts =~ s/inter/nointer/;
	} else {
		$contingentOptions .= ' -D__MARM_INTERWORK__';
	}
	$CCFLAGS = $commonOptions.' '.$contingentOptions.' '.$invopts.' -c '.' '.&main::CompilerOption("ARMCC");
}

sub GetMajorVersion ($)
	{
	my ($versionString) = @_;

	if ($versionString =~ /^\d\.\d\.\d$/)
		{
		$versionString =~ s/\.\d$//;
		}

	return $versionString;
	}

sub GetMinorVersion ($)
	{
	my ($versionString) = @_;

	if ($versionString =~ /^\d\.\d\.\d$/)
		{
		$versionString =~ s/^\d\.\d\.//;
		}
	else
		{
		$versionString = 0;
		}

	return $versionString;
	}

sub PMCheckPlatformL {

	# check version of CodeWarrior for Symbian OS

	my @compatibleCWInstallations;

	$CW_major_version = 0;
	$CW_minor_version = 0;

	my $minimumMajorVersion = GetMajorVersion ($CW_minimum_supported_version);
	my $minimumMinorVersion = GetMinorVersion ($CW_minimum_supported_version);

	if (defined $ENV{CW_SYMBIAN_VERSION})
		{
		# CW_SYMBIAN_VERSION is set - either MAKMAKE is being executed by an IDE's .mmp Importer,
		# or the user is specifying a specific CW version to target from the command line.
		# Either way, we've been given a single version to target, so we attempt to do just that.

		$CW_major_version = GetMajorVersion ($ENV{CW_SYMBIAN_VERSION});
		$CW_minor_version = GetMinorVersion ($ENV{CW_SYMBIAN_VERSION});
		
		push @compatibleCWInstallations, $ENV{CW_SYMBIAN_VERSION};
		}
	else
		{
		# CW_SYMBIAN_VERSION isn't set - either MAKMAKE is being executed by a pre-OEM3.0 IDE .mmp
		# Importer or from the command line.  Either way, we delve into the registry and attempt to
		# target the latest CW for Symbian OS installed, recording all CW for Symbian OS installations
		# too.

		my $regKeyHandle;
		my $topLevelKey = HKEY_LOCAL_MACHINE;
		my $productVersionKey = 'SOFTWARE\\Metrowerks\\CodeWarrior\\Product Versions';

		if (!RegOpenKeyEx($topLevelKey, $productVersionKey, 0, KEY_READ, $regKeyHandle))
			{		    		
			die "Can't read \"HKEY_LOCAL_MACHINE\\$productVersionKey\" : ", regLastError(), "\n";
		    }

		my $subKeyIndex = 0;
		my $subKeySize = 0;
		my $subKeyName;

		my @installedCWForSymbianKeys;

		while (RegEnumKeyEx($regKeyHandle, $subKeyIndex, $subKeyName, $subKeySize, [], [], [], []))
			{
			push (@installedCWForSymbianKeys, $productVersionKey."\\".$subKeyName) unless ($subKeyName !~ /Symbian/);
			$subKeyIndex++;
			}

		RegCloseKey($regKeyHandle) || print STDERR "WARNING: Could not close registry key.";

		my $versionType;
		my $versionValue;

		foreach my $installedCWForSymbianKey (@installedCWForSymbianKeys)
			{
			if (!RegOpenKeyEx($topLevelKey, $installedCWForSymbianKey, 0, KEY_READ, $regKeyHandle))
				{		    		
				die "Can't read \"HKEY_LOCAL_MACHINE\\$installedCWForSymbianKey\" : ", regLastError(), "\n";
			    }

			if (!RegQueryValueEx($regKeyHandle, "VERSION", [], $versionType, $versionValue, []))
				{
				die "Can't read \"HKEY_LOCAL_MACHINE\\$installedCWForSymbianKey\\VERSION\" : ", regLastError(), "\n";
				}

			my $temp_major_version = GetMajorVersion ($versionValue);
			my $temp_minor_version = GetMinorVersion ($versionValue);

			if (($temp_major_version > $CW_major_version) ||
				(($temp_minor_version > $CW_minor_version) &&
				   ($temp_major_version >= $CW_major_version)))
				{
				$CW_major_version = $temp_major_version;
				$CW_minor_version = $temp_minor_version;
				}
			
			if (($temp_major_version > $minimumMajorVersion) ||
				(($temp_minor_version > $minimumMinorVersion) &&
				   ($temp_major_version >= $minimumMajorVersion)))
				{
				push @compatibleCWInstallations, $versionValue;
				}

			RegCloseKey($regKeyHandle);
			}
		}

	# We've determined a CW version to target, now we validate if we actually support this

	if (!$CW_major_version ||
		($CW_major_version < $minimumMajorVersion) ||
		(($CW_major_version >= $minimumMajorVersion) && ($CW_minor_version < $minimumMinorVersion)))
		{
		if (defined $ENV{CW_SYMBIAN_VERSION})
			{
			die "Error: CW_SYMBIAN_VERSION is set to $ENV{CW_SYMBIAN_VERSION}.\n       The minimum version supported by these tools is $CW_minimum_supported_version.\n";
			}
		else
			{
			die "ERROR: Unable to identify a compatible CodeWarrior for Symbian OS installation.\n";
			}
		}

	if (@compatibleCWInstallations > 1)
		{
		my $targetVersion = $CW_major_version;
		$targetVersion .= ".$CW_minor_version" if $CW_minor_version;
			
		print ("Info: More than one compatible CodeWarrior for Symbian OS installation has been detected.\n");
		print ("      The generated project will target $targetVersion - to override this, set the CW_SYMBIAN_VERSION\n");
		print ("      environment variable to the version number you wish to target and re-run this command.\n");
		print ("      Supported version numbers detected : @compatibleCWInstallations.\n");
		}
	else
		{
		print ("Info: Detected CodeWarrior Version Major=$CW_major_version Minor=$CW_minor_version\n");
		}

	# CW version has been validated, tailor generated projects on this basis

	$CW_libpath = 'Symbian_Support\Win32-x86 Support\Libraries\Win32 SDK';
 	push @CW_librarypath,$CW_libpath;
 	# Lib path to support the Carbide runtime libraries
 	$CW_libpath = 'Symbian_Support\Runtime\Runtime_x86\Runtime_Win32\Libs';
 	push @CW_librarypath,$CW_libpath;
		
	if ($CW_major_version == 2.8)
		{
		$projectTemplate = "CW_project_template_v3.xml";
		$linkDescriptorTemplate = "cw_link_descriptor_template.cwlink";
		}
	else
		{
		$projectTemplate = "CW_project_template_v4.xml";
		$linkDescriptorTemplate = "cw_link_descriptor_template_v2.cwlink";		
		}
		
	$xmlParser = new XML::DOM::Parser; 
	$xmlProjectDoc = $xmlParser->parsefile ($FindBin::Bin."\\$projectTemplate");
}

# Check if a platform is a customization
sub IsCustomization($) {
	my ($plat) = @_;
	return 1 if (Plat_Customizes($plat));
	return 0;
}

sub PMPlatProcessMmp (@) {
	
	my $currentPlat=&main::Plat;

	return if ($processedPlatforms{$currentPlat});
	$processedPlatforms{$currentPlat}=1;
	@ToolChainLibList = &GetLibList;
	my $TrgType=&main::TrgType;
	if ($CustPlat{'CUSTOMIZES'} && ($CustPlat{'ROOTPLATNAME'} eq "GCCE"))
	{
		$CustGCCE=1;
	}

	if ($currentPlat =~ /^WINSCW$/)
		{	
		my $includes = $ENV{MWCWinx86Includes};
		&Winutl_DoMmp_Parse(\@_, $includes);
		@Win32LibList=&Winutl_Win32LibList;
		$Win32Resrc=&Winutl_Win32Resrc;
		$Win32StdHeaders=&Winutl_Win32StdHeaders;
		$BaseAddress=&Winutl_BaseAddress unless ($TrgType eq 'EXE');
		}
	elsif ($currentPlat =~ /^ARMV5/ || IsCustomization($currentPlat))
		{	
		&Armutl_DoMmp(@_);
		$ArmIncDir = &Armutl_ArmIncDir;
		&main::SetStdIncPaths($ArmIncDir);
		@ArmLibList = &Armutl_ArmLibList;
		$ArmRT = &Armutl_ArmRT;
		}

	my $BaseTrg=&main::BaseTrg;
	my $MakeFilePath=&main::MakeFilePath;
	$MmpFile=&main::MmpFile;
	$VariantFile=&main::VariantFile();		

	# Set up the list of known roots

	my $epocroot=&main::Path_Drive . &main::EPOCPath;
	$epocroot =~ s/EPOC32\\$//i;

	if ($currentPlat eq "GCCE" || $CustGCCE)
	{
		$PrefixFile=$epocroot.'epoc32\\include\\gcce\\gcce.h';
	}
	else
	{
		$PrefixFile=$epocroot.'epoc32\\include\\rvct'.$RVCTVersion.'\\rvct'.$RVCTVersion.'.h';
	}
	my $mmproot = &main::Path_Drive . &main::Path_Split('Path',$MmpFile);
	my $mmprootname = "MMPDir";
	my $mmpisglobal = 0;

	if (defined $ENV{CW_ROOT_NAME})
		{
		# generate KnownRoots suitable for the IDE MMP importer
		# This has a global source tree for EPOCROOT, and puts the
		# project next to the MMP file

		addKnownRoot($ENV{CW_ROOT_NAME}, 1, $epocroot, "");
		$mmprootname = "Project";
		$mmpisglobal = 1;
		}
	else
		{
		# generate KnownRoots suitable for command-line generated XML files
		# We will add a user source tree for MMPDir and EPOCROOT, but can't use
		# EPOCROOT for the OutputDirectory

		addKnownRoot("EPOCROOT", 0, $epocroot, "");
		}
	addKnownRoot($mmprootname, $mmpisglobal, $mmproot, "");

	# Allow for MMP files in component subdirectories by matching multiple levels
	# up to get {MMPDir}..\whatever paths. Stop one level down from the root,
	# since "everything on this drive" seems a bit extreme
	#
	my $tmppath = $mmproot;
	my $dotdots = '';
	while ($tmppath =~ /^(.:.+\\)[^\\]+\\$/i)
		{
		$tmppath = $1;
		$dotdots .= "..\\";
		addKnownRoot($mmprootname, $mmpisglobal, $tmppath, $dotdots);
		}
}

sub findTarget($$$) {
	my ($name,$platbld,$abibld) = @_;

	my @targets = $xmlProjectDoc->getElementsByTagName("TARGET",1); 

	foreach my $target (@targets)
		{
		
		my $element = $target->getElementsByTagName("NAME",0)->item(0);
		$element = $element->getFirstChild;
		
	#   here we want to get the plat build that is specified in the cw project tempalte
	#   and not the abi build - there are more platbuilds than abibuilds so other targets
	#   (e.g. GCCE) can get the wrong tempalte "NAME" (i.e. it will be ARMV5 rather than GCCE, which is not want we want)
		if ($element->getData() =~ /$platbld$/)
			{
			
			my $newtarget=$target->cloneNode(1);
			$target->getParentNode()->appendChild($newtarget);
			$element = $newtarget->getElementsByTagName("NAME",0)->item(0);
			$element = $element->getFirstChild;
			$element->setData("$platbld");
			
			# remember name as an attribute: this is removed before
			# writing out the project.
			$newtarget->setAttribute("NAME", "$platbld");
			return $newtarget;
			}
			else
			{
				my $newtarget=$target->cloneNode(1);
				my $newplat=&main::Plat." ".&main::Bld;
				$target->getParentNode()->appendChild($newtarget);
				$element = $newtarget->getElementsByTagName("NAME",0)->item(0);
				$element = $element->getFirstChild;
				$element->setData("$newplat");
	 			
				# remember name as an attribute: this is removed before
				# writing out the project.
				$newtarget->setAttribute("NAME", "$newplat");
				return $newtarget;
			}
		}
	return undef;
}

sub newList($$$) {
	my ($target, $tag, $replace) = @_;

	my $newlist = new XML::DOM::Element($xmlProjectDoc,$tag);
	if ($replace==1)
		{
		my $elements = $target->getElementsByTagName($tag,0);
		my $element = $elements->item(0);
		$target->replaceChild($newlist, $element);
		}
	else
		{
		$target->appendChild($newlist);
		}
	return $newlist;
}

sub changeValue($$) {
	my ($setting, $replacement) = @_;
	my $value = $setting->getElementsByTagName("VALUE",0)->item(0);

	if (defined $value)
		{
		if ($value->hasChildNodes)
			{
			$value->getFirstChild->setData($replacement);
			}
		else
			{
			$value->addText($replacement);
			}
		}
}

sub textSetting($$$$) {
	my ($element,$name,$value,$insertionpoint)=@_;

	my $setting = new XML::DOM::Element($xmlProjectDoc,"SETTING");
	&textElement($setting, "NAME", $name);
	&textElement($setting, "VALUE", $value);
	$element->insertBefore($setting, $insertionpoint);
	$element->addText("\n");
}

sub addKnownRoot($$$$) {
	my ($rootname, $global, $rootpath, $pathprefix) = @_;
	$rootpath=&main::Path_Chop($rootpath);
	push @KnownRoots, [$rootname, $global, quotemeta($rootpath), $pathprefix];
}

sub addRootedPath($$$) {
	my ($setting,$needglobal,$path) = @_;
	my $root = "Absolute";

	if ($path =~ /^\\/)
		{
		$path = &main::Path_Drive . $path;	# ensure it has a drive letter
		}

	foreach (@KnownRoots)
		{
		my ($rootname, $global, $rootpath, $pathprefix) = @{$_};

		next if ($needglobal && !$global);
		if ($path =~ /^$rootpath\\/i)
			{			
			$path =~ s/^$rootpath\\/$pathprefix/i;
			$root = $rootname;
			last;
			}
		}
	$path=&main::Path_Chop($path);
	if ($root eq "Absolute" && $path =~ /^(.:)$/)
		{
		$path .= "\\";
		}

	&textSetting($setting, "Path", $path);
	&textSetting($setting, "PathFormat", "Windows");
	&textSetting($setting, "PathRoot", $root);
}

sub changePathSetting($$$) {	
	my ($setting,$global,$value) = @_;

	my @oldstuff = $setting->getElementsByTagName("SETTING",0);
	foreach my $old (@oldstuff)
		{
		&removeNode($old);
		}

	&addRootedPath($setting,$global,$value);
}

sub addSourceTrees($) {
	my ($node) = @_;

	my $element = $node->getElementsByTagName("VALUE",0)->item(0);
	&removeNode($element) if (defined $element);

	if (defined $ENV{CW_ROOT_NAME})
		{
		return;	# paths were converted to be relative to global source trees
		}

	my $sourcepath = new XML::DOM::Element($xmlProjectDoc,"SETTING");
	$sourcepath->addText("\n");
	&textSetting($sourcepath, "Name", "EPOCROOT");
	&textSetting($sourcepath, "Kind", "AbsolutePath");

	my $pathdata = new XML::DOM::Element($xmlProjectDoc,"SETTING");
	&textElement($pathdata, "NAME", "Path"); 
	$pathdata->addText("\n");

	my $epocroot=&main::EPOCPath;
	$epocroot =~ s/\\EPOC32\\$/\\/i;

	&addRootedPath($pathdata, 1, $epocroot);

	$sourcepath->appendChild($pathdata);
	$node->appendChild($sourcepath);

	my $mmproot = &main::Path_Split('Path',$MmpFile);
	my $sourcepath2 = new XML::DOM::Element($xmlProjectDoc,"SETTING");
	$sourcepath2->addText("\n");
	&textSetting($sourcepath2, "Name", "MMPDir");
	&textSetting($sourcepath2, "Kind", "AbsolutePath");

	my $pathdata2 = new XML::DOM::Element($xmlProjectDoc,"SETTING");
	&textElement($pathdata2, "NAME", "Path"); 
	$pathdata2->addText("\n");
	&addRootedPath($pathdata2, 1, $mmproot);

	$sourcepath2->appendChild($pathdata2);
	$node->appendChild($sourcepath2);

}

sub addUserSearchPaths($) {
	my ($node) = @_;

	my @elements = $node->getElementsByTagName("SETTING",0);
	foreach (@elements)
		{
		&removeNode($_);
		}

	my %ordereddirs;
	my @ordereddirlist=();
	foreach my $dir (&main::UserIncPaths)
		{
		next if (!defined $dir);
		$dir = &main::Path_Chop($dir)."\\";
		my $key = uc $dir;
		if (! defined($ordereddirs{$key}))
			{
			$ordereddirs{$key}=1;
			push @ordereddirlist, $dir;
			}
		}

	# now add the directories used to find source files
	
	my %dirs;
	my $SourceStructRef=&main::SourceStructRef;
	foreach my $SourceRef (@$SourceStructRef)
		{
		 $dirs{$$SourceRef{SrcPath}}=1;
		}
	my $DefFile = &main::DefFile;
	if ($DefFile)
		{			
		$DefFile = &main::Path_Split('Path',$DefFile);
		$dirs{$DefFile}=1;
		}

	my $MmpFilePath = &main::Path_Split('Path',$MmpFile);
	$dirs{$MmpFilePath}=1;
		
	$dirs{$ExtraFilesPath}=1;
		

	foreach my $srcdir (sort keys %dirs)
		{
		if (!defined($ordereddirs{uc $srcdir}))
			{
			push @ordereddirlist, $srcdir;
			}
		}
		
	foreach my $srcdir (@ordereddirlist)
		{
		my $accesspath = new XML::DOM::Element($xmlProjectDoc,"SETTING");
		$accesspath->addText("\n");
		my $pathdata = new XML::DOM::Element($xmlProjectDoc,"SETTING");
		&textElement($pathdata, "NAME", "SearchPath"); 
		$pathdata->addText("\n");
		&addRootedPath($pathdata, 0, $srcdir);

		$accesspath->appendChild($pathdata);
		&textSetting($accesspath, "Recursive", "false");
		&textSetting($accesspath, "FrameworkPath", "false");
		&textSetting($accesspath, "HostFlags", "All");
		$node->appendChild($accesspath);
		}
}

sub addSystemSearchPaths($) {
	my ($node) = @_;

	my @elements = $node->getElementsByTagName("SETTING",0);
	foreach (@elements)
		{
		&removeNode($_);
		}

	my $ASSPLinkPath;
	$ASSPLinkPath = &main::ASSPLinkPath if (&main::ASSPLibList);

	my @extraIncPaths=();
	push @extraIncPaths, $ArmIncDir if $ArmIncDir;

	my %ordereddirs;
	my @ordereddirlist=();
	
	foreach my $dir (&main::SysIncPaths, @extraIncPaths, &main::StatLinkPath, $ASSPLinkPath, &main::LinkPath)
		{
		next if (!defined $dir);
		$dir = &main::Path_Chop($dir)."\\";
		my $key = uc $dir;
		if (! defined($ordereddirs{$key}))
			{
			$ordereddirs{$key}=1;
			push @ordereddirlist, $dir;
			}
		}

	my %dirs;

	if ($VariantFile)
		{
		my $VariantFilePath = &main::Path_Split('Path',$VariantFile);
		$dirs{$VariantFilePath}=1;
		}

	if (((&main::Plat =~ /^ARMV5/) || (&main::Plat =~ /^GCCE$/) ||(IsCustomization(&main::Plat))) && $PrefixFile) 
		{
		my $PrefixFilePath = &main::Path_Split('Path',$PrefixFile);
		$dirs{$PrefixFilePath}=1;
		}

	foreach my $srcdir (sort keys %dirs)
		{
		if (!defined($ordereddirs{uc $srcdir}))
			{
			push @ordereddirlist, $srcdir;
			}
		}
		
	foreach my $srcdir (@ordereddirlist)
		{
		my $accesspath = new XML::DOM::Element($xmlProjectDoc,"SETTING");
		$accesspath->addText("\n");
		my $pathdata = new XML::DOM::Element($xmlProjectDoc,"SETTING");
		&textElement($pathdata, "NAME", "SearchPath"); 
		$pathdata->addText("\n");
		&addRootedPath($pathdata, 0, $srcdir);

		$accesspath->appendChild($pathdata);
		&textSetting($accesspath, "Recursive", "false");
		&textSetting($accesspath, "FrameworkPath", "false");
		&textSetting($accesspath, "HostFlags", "All");
		$node->appendChild($accesspath);
		}
	
	if (&main::Plat =~ /^WINSCW$/)
	{
 		my $lpath;
 		foreach $lpath (@CW_librarypath)
 		{
 			# only add Win32 SDK for WINSCW system access paths	
 			my $accesspath = new XML::DOM::Element($xmlProjectDoc,"SETTING");
 			$accesspath->addText("\n");
 			my $pathdata = new XML::DOM::Element($xmlProjectDoc,"SETTING");
 			&textElement($pathdata, "NAME", "SearchPath"); 
 			$pathdata->addText("\n");
 			
 			&textSetting($pathdata, "Path", $lpath);
 			&textSetting($pathdata, "PathFormat", "Windows");
 			&textSetting($pathdata, "PathRoot", "CodeWarrior");
 			$accesspath->appendChild($pathdata);
 			&textSetting($accesspath, "Recursive", "false");
 			&textSetting($accesspath, "FrameworkPath", "false");
 			&textSetting($accesspath, "HostFlags", "All");
 			$node->appendChild($accesspath);
 		}
	}
	if ($CustPlat{'CUSTOMIZES'} && ($CustPlat{'ROOTPLATNAME'} eq "GCCE"))
	{
		$CustGCCE=1;
	}
	if (&main::Plat =~ /^GCCE$/ || $CustGCCE)
	{
		my $accesspath = new XML::DOM::Element($xmlProjectDoc,"SETTING");
		$accesspath->addText("\n");
		my $pathdata = new XML::DOM::Element($xmlProjectDoc,"SETTING");
		&textElement($pathdata, "NAME", "SearchPath"); 
		$pathdata->addText("\n");
		
		my $GCCE_IncludePath = GetGCCELibPath("-print-libgcc-file-name");
		$GCCE_IncludePath .= '\\include';
		&textSetting($pathdata, "Path", $GCCE_IncludePath);
		
		&textSetting($pathdata, "PathFormat", "Windows");
		&textSetting($pathdata, "PathRoot", "Absolute");
		$accesspath->appendChild($pathdata);
		&textSetting($accesspath, "Recursive", "false");
		&textSetting($accesspath, "FrameworkPath", "false");
		&textSetting($accesspath, "HostFlags", "All");
		$node->appendChild($accesspath);
	}
	
}

sub addDownloadFileList($) {
	my ($node, @DownloadList) = @_;
	
	my @elements = $node->getElementsByTagName("SETTING",0);
	foreach (@elements)
		{
		&removeNode($_);
		}

	my $epocdata = &main::EPOCPath . "data\\";
	foreach my $srcfile (@DownloadList)
		{
		my $targetpath = $srcfile;
		$targetpath =~ s/^Z\\/C:\\/i;
		
		my $download = new XML::DOM::Element($xmlProjectDoc,"SETTING");
		$download->addText("\n");
		my $pathdata = new XML::DOM::Element($xmlProjectDoc,"SETTING");
		&textElement($pathdata, "NAME", "HostFilePath"); 
		$pathdata->addText("\n");
		&addRootedPath($pathdata, 0, "$epocdata$srcfile");

		$download->appendChild($pathdata);
		&textSetting($download, "TargetFilePath", $targetpath);
		$node->appendChild($download);
		}
	}

sub kitRelativePath ($) {
	my ($kitRootBasedPath) = @_;
		
	my $kitRoot = &main::EPOCPath;
	$kitRoot =~ s/EPOC32\\$//i;
	$kitRoot = quotemeta (&main::Path_Chop($kitRoot));

	$kitRootBasedPath =~ s/^$kitRoot//i;

	$kitRootBasedPath;
}
	
sub PMBld() {

	my %changedsettings;

	my $ABI=&main::ABI;
	my $Bld=&main::Bld;
	my $Plat=&main::Plat;
	my $BaseName=&main::BaseMak;
	my @SrcList=&main::SrcList;
	my $BaseTrg=&main::BaseTrg;
	my @BldList=&main::BldList;
	my $DefFile=&main::DefFile;
	my $FirstLib=&main::FirstLib;
	# IsCustomDllUseCase() subroutine is called to check if the given executable 
	# is a custom dll or not.
	my $IsCustomDll = Cl_bpabi::IsCustomDllUseCase();
	# ABI flags set depending on the ENABLE_ABIV2_MODE macro set in the variant file.
	my $ABIV1 = 0;
	my $ABIV2 = 0;
	if (($Plat eq "ARMV5_ABIV1" && $cfgMacro) || ($Plat eq "ARMV5" && !$cfgMacro)
	|| ($CustPlat{'CUSTOMIZES'} 
	&& (($CustPlat{'ROOTPLATNAME'} eq "ARMV5_ABIV1" && $cfgMacro) || ($CustPlat{'ROOTPLATNAME'} eq "ARMV5" && !$cfgMacro))))
	{
		$ABIV1=1;
	}
	elsif (($Plat eq "ARMV5_ABIV2" && !$cfgMacro) || ($Plat eq "ARMV5" && $cfgMacro)
	|| ($CustPlat{'CUSTOMIZES'} 
	&& (($CustPlat{'ROOTPLATNAME'} eq "ARMV5_ABIV2" && !$cfgMacro) || ($CustPlat{'ROOTPLATNAME'} eq "ARMV5" && $cfgMacro))))
	{
		$ABIV2=1;
	}
	
	if ($CustPlat{'CUSTOMIZES'} && ($CustPlat{'ROOTPLATNAME'} eq "GCCE"))
	{
		$CustGCCE=1;
	}

 	if ($ABIV1 && ($RVCTMajorVersion == 2 && $RVCTMinorVersion < 2)) {
	        # Temporary Workaround for RVCT2.1 static libs problem with RVCT2.2 builds
		# Rename FirstLib.lib static lib used with RVCT2.1 as FirstLib2_1.lib
		if ($FirstLib=~/^\s*(\S+)(\.lib)$/io) {
		        if ($1!~/$RVCTVersion/i) {
				$FirstLib=$1.$RVCTVersion.".lib";
			}
		}
	}	

	my $BasicTrgType=&main::BasicTrgType;
	my @LibList;
	my @StatLibList=&main::StatLibList;
	if ($ABIV1 && ($RVCTMajorVersion == 2 && $RVCTMinorVersion < 2)) {
	        # Temporary Workaround for RVCT2.1 static libs problem with RVCT2.2 builds
		# Rename all the static libs used with RVCT2.1 as libname2_1.lib
		for (my $i =0; $i < scalar(@StatLibList); $i++) {
		        if ($StatLibList[$i]=~/^\s*(\S+)(\.lib)$/io) {
			        if ($1!~/$RVCTVersion/i) {
				        $StatLibList[$i]=$1.$RVCTVersion.".lib";
				}
			}
		}
	}
	

	my @ASSPLibList = &main::ASSPLibList;
	my $Trg=&main::Trg;
	if ($ABIV1 && ($RVCTMajorVersion == 2 && $RVCTMinorVersion < 2)) {
		if ($BasicTrgType=~/^LIB$/o) {
			# Temporary Workaround for RVCT2.1 static libs problem with RVCT2.2 builds
			# Rename all the static libs produced with RVCT2.1 as {libname}2_1.lib
		        if ($Trg=~/^\s*(\S+)(\.lib)$/io) {
			        if ($1!~/$RVCTVersion/i) {
				        $Trg=$1.$RVCTVersion.".lib";
				}
			}
			if ($BaseTrg!~/$RVCTVersion/i) {
			        $BaseTrg .= $RVCTVersion;
			}
		}
	}

	my $TrgPath=&main::TrgPath;
	my $TrgType=&main::TrgType;
	my $epocroot=&main::Path_Drive . &main::EPOCPath;
	$epocroot =~ s/EPOC32\\$//i;
	my $UIDFile;

	$ExtraFilesPath = &main::MakeFilePath;

	if ($Bld =~ /DEB/) {
		@LibList=&main::DebugLibList;
	} else {
		@LibList=&main::LibList;
	}
	my $xmlTarget;
	if ($ABI =~ /BPABI/)
	{
		$ABI = "GCCE";
	}
	
	if ($Plat eq "GCCE" || $CustGCCE || $ABIV2) {
	
		if ($CW_major_version < 3.1) {
			die "FATAL ERROR: Target $Plat requires CodeWarrior for Symbian release 3.1 at minimum.\n";
		}
	}
	
	if ($ABIV2 && ($RVCTMajorVersion == 2 && $RVCTMinorVersion < 2))
	{
		die "FATAL ERROR: Target ARMV5_ABIV2 requires RVCT version 2.2 and greater. Detected RVCT $RVCTMajorVersion.$RVCTMinorVersion.\n";
	}
	
	if (($RVCTMajorVersion == 2 && $RVCTMinorVersion >= 2) && $CW_major_version < 3.1 && $ABIV2)
	{
		die "FATAL ERROR: Detected RVCT Version $RVCTMajorVersion.$RVCTMinorVersion and CodeWarrior version $CW_major_version. RVCT 2.2 and greater requies CodeWarrior version 3.1 at minimum.\n";
	}
	
    $xmlTarget = findTarget($BaseName, "$Plat $Bld", "$ABI $Bld");
		
	return if (!defined($xmlTarget));
	
	my $targetname = $xmlTarget->getAttribute("NAME");

	my $UnderlyingABI=PMUnderlyingABI($ABI);
	my @ChopRTWSysIncPaths=&main::Path_Chop(&main::Path_RltToWork(&main::SysIncPaths));
	my @ChopRTWUserIncPaths=&main::Path_Chop(&main::Path_RltToWork(&main::UserIncPaths));
	my $EPOCPath=&main::EPOCPath;
	my $LinkAs=&main::LinkAs;
	my $LibPath=&main::LibPath;
	my @UidList=&main::UidList;	
	my $WarningLevelGCC=&main::CompilerOption("GCC");
	my $ExportLibrary=&main::ExportLibrary;
	my $NoExportLibrary=&main::NoExportLibrary;
	my $SystemTrg = SystemTarget();
	my %Version = &main::Version();
	my $ExtraExportLibrary;
	my $PrimaryExportLibrary = $ExportLibrary;
	unless ($Version{explicit}) {
		$ExtraExportLibrary = $ExportLibrary;
		$ExtraExportLibrary =~ s/\{(\d|a|b|c|d|e|f){8}\}//i;
		$PrimaryExportLibrary = $ExtraExportLibrary;
	}

	my $ChopBldPath=&main::Path_Chop(&main::BldPath);
	my $EPOCIncPath=&main::EPOCIncPath;
	my $ChopRelPath=&main::Path_Chop(&main::RelPath);
	my $RelPath=&main::RelPath;
	my $StatLinkPath=&main::StatLinkPath;
	my $ParentPlat;
	
# 	Check if a platform is customization, if yes find the parent platform.
	my $IsPlatCustomization=IsCustomization($Plat);
	if ($IsPlatCustomization) {
		$ParentPlat = Plat_Customizes($Plat);
	}
	
	my @RTLibList;
	if ($ABIV1) {
	    @RTLibList = ('dfpaeabi.lib', "dfprvct${RVCTVersion}.lib", 'drtaeabi.lib', 'drtaeabi.lib(VtblExports.o)');
	    if ($RVCTMajorVersion == 2 && $RVCTMinorVersion >= 2) {
		# The scppnwdl.lib should come before drtrvct2_2.lib
		push @RTLibList, "scppnwdl.lib";
		push @RTLibList, "drtrvct${RVCTVersion}.lib";
	    }
	    else
	    {
		push @RTLibList, "dfprvct${RVCTVersion}-thunk.lib";
		push @RTLibList, "drtrvct${RVCTVersion}.lib";
	    }    
	}
	elsif ($ABIV2)
	{
		@RTLibList = ('drtaeabi.dso', 'dfpaeabi.dso', "dfprvct${RVCTVersion}.dso");
	    if ($RVCTMajorVersion == 2 && $RVCTMinorVersion >= 2) {
		# The scppnwdl.lib should come before drtrvct2_2.lib
		push @RTLibList, "scppnwdl.dso";
		push @RTLibList, "drtrvct${RVCTVersion}.dso";
	    }
	}
	
	my @compatibleDOCUMENTExtensions = ("cfg", "h", "hrh", "iby", "inf", "ini", "loc", "mmpi", "policy", "ra", "rh", "rls", "rss", "script", "txt");
	my @DocList = &main::DocList;
	@addedFiles=();


#	set up LinkAs
	$UidList[2]=~/^0x(.*)$/o;
	if ($1 ne '00000000') { # have to make sure than series of noughts in brackets doesn't appear in name for null uids
		$LinkAs=join '', &main::Path_Split('Base',$LinkAs),"[$1]",&main::Path_Split('Ext',$LinkAs);
	}

#	set up dlltool flag hash
	my %ABIDlltool=(
		ARMI=>'-m arm_interwork',
		ARM4=>'-m arm',
		THUMB=>'-m thumb'
	);

#	work out the flags for various platforms
	if ($ABI eq 'ARMI') {
		$PlatOpt{Gcc}='-march=armv4t -mthumb-interwork';
		$PlatOpt{Dlltool}=$ABIDlltool{ARMI};
	}
	elsif ($ABI eq 'ARM4T') {
		if (&main::BuildAsARM) {
			$PlatOpt{Gcc}='-march=armv4t -mthumb-interwork';
			$PlatOpt{Dlltool}=$ABIDlltool{ARMI};
		}
		elsif ($SystemTrg) {
			$PlatOpt{Gcc}='-march=armv4';
#			allow thumb for ARM4 ABI where necessary
			unless (&main::PlatABI eq 'ARM4') {
				$PlatOpt{Gcc}.='t';
			}
			$PlatOpt{Dlltool}=$ABIDlltool{ARM4};
		}
		else {
			$PlatOpt{Gcc}='-mthumb-interwork -D__MARM_THUMB__';
			$PlatOpt{Dlltool}=$ABIDlltool{THUMB};
		}
	}
	elsif ($ABI eq 'ARM4') {
		$PlatOpt{Gcc}='-march=armv4';
#		allow thumb for ARM4 ABI where necessary
		unless (&main::PlatABI eq 'ARM4') {
			$PlatOpt{Gcc}.='t';
		}
		$PlatOpt{Dlltool}=$ABIDlltool{ARM4};
	}
	elsif ($ABI eq 'THUMB') {
		$PlatOpt{Gcc}='-mthumb-interwork';
		$PlatOpt{Dlltool}=$ABIDlltool{THUMB};
	}
	
	elsif ($Plat ne 'WINSCW' && $Plat ne 'ARMV5' && !$IsPlatCustomization && $Plat ne 'GCCE' && $Plat ne 'ARMV5_ABIV2' && $Plat ne 'ARMV5_ABIV1') { 
		&main::FatalError("Platform module - ABI \"$ABI\" unrecognised");
	}
	
	if ($Plat ne 'WINSCW') {
		@CompatibleABIs=@{$CompatibleABIs{$UnderlyingABI}};
	}

#	set up CompatibleABI lib path hash
	foreach (@CompatibleABIs) {
		$ABILibPath{$_}=&main::Path_Strip("$LibPath..\\..\\$_\\");
	}

	$Dlltool=$ToolPrefix.'dlltool.exe';
	$Archive=$ToolPrefix.'ar.exe';
	$Link=$ToolPrefix.'ld.exe';
	$Objcopy=$ToolPrefix.'objcopy.exe';

	my $WarningLevelCW=&main::CompilerOption("CW");

	$xmlFileList = newList($xmlTarget,"FILELIST",1);
	$xmlFileList->addText("\n");

	$xmlLinkOrder = newList($xmlTarget,"LINKORDER",1);
	$xmlLinkOrder->addText("\n");

	# Create temporary sublists, which will be
	# removed during finaliseProject

	$xmlSourceGroup = newList($xmlTarget,"SOURCEGROUP",0);
	$xmlSourceGroup->setAttribute("TARGET", $targetname);
	$xmlSourceGroup->addText("\n");

	$xmlHeadersGroup = newList($xmlTarget,"HEADERSGROUP",0);
	$xmlHeadersGroup->setAttribute("TARGET", $targetname);
	$xmlHeadersGroup->addText("\n");

	$xmlRootGroup = newList($xmlTarget,"ROOTGROUP",0);
	$xmlRootGroup->setAttribute("TARGET", $targetname);
	$xmlRootGroup->addText("\n");
		
	$xmlLinkGroup = newList($xmlTarget,"LINKGROUP",0);
	$xmlLinkGroup->setAttribute("TARGET", $targetname);
	$xmlLinkGroup->addText("\n");

	$xmlLibGroup = newList($xmlTarget,"LIBGROUP",0);
	$xmlLibGroup->setAttribute("TARGET", $targetname);
	$xmlLibGroup->setAttribute("PLAT", $Plat);
	$xmlLibGroup->addText("\n");

	$xmlResourcesGroup = newList($xmlTarget,"RESOURCESGROUP",0);
	$xmlResourcesGroup->setAttribute("TARGET", $targetname);
	$xmlResourcesGroup->addText("\n");

	$xmlDocumentsGroup = newList($xmlTarget,"DOCUMENTSGROUP",0);
	$xmlDocumentsGroup->setAttribute("TARGET", $targetname);
	$xmlDocumentsGroup->addText("\n");

	my $debug="";
	$debug="Debug" if ($Bld =~ /DEB$/);

	my @RuntimeLibs = ();	# add platform-specific runtime libraries here
	if (&main::PlatCompiler eq "GCC32")
		{
		if ($BasicTrgType=~/^DLL$/o) 
			{ # Add the DLL stub library
			push @RuntimeLibs, "EDLLSTUB.LIB";
			}
		if ($BasicTrgType=~/^(DLL|EXE)/o) 
			{ # Add the GCC helper fns
			push @RuntimeLibs, "EGCC.LIB";
			}
		}
	
	if ($Plat eq "GCCE" || $CustGCCE)
		{
			push @RuntimeLibs, "usrt2_2.lib";    # UDEB/UREL Specific
			push @RuntimeLibs, "dfpaeabi.dso";
			push @RuntimeLibs, "dfprvct2_2.dso";
			push @RuntimeLibs, "drtaeabi.dso"; 
			push @RuntimeLibs, "scppnwdl.dso"; 
			push @RuntimeLibs, "drtrvct2_2.dso";
			if ($BasicTrgType=~/^DLL$/o) { # Add the DLL stub library
				push @RuntimeLibs, "EDLLSTUB.LIB";   # UDEB/UREL Specific
				}
		}

	addFile(&main::Path_Split('File',$MmpFile), "Text", "", "", "Root");

	# Create the uid.cpp file	
	if ($Plat eq "WINSCW" && $BasicTrgType=~/^(EXE|DLL)$/oi)
		{
		my @UidList=&main::UidList;
		
		# create the UID source file
		my $priority = "EPriorityForeground";
		if (&main::ProcessPriority) {
			$priority="EPriority".&main::ProcessPriority;
		}

		my $UidText=join(
			"\n",
			'// Makmake-generated uid source file',
			'#include <e32cmn.h>',
			'#pragma data_seg(".SYMBIAN")',
			'__EMULATOR_IMAGE_HEADER2('
		);
		foreach (@UidList) {
			$UidText.="$_,";
		}
		my $vstr = "0x".&Genutl_VersionToHexString(&main::Version);
		my $vid = &main::VendorId;
		if(!$vid) { $vid="0"; }
		$UidText.="$priority,".(&main::CapabilityFlags)[0]."u,".(&main::CapabilityFlags)[1]."u,".&main::SecureId.",".$vid.",$vstr,";	# second capability word always 0 for now
		if (&main::AllowDllData) {
			$UidText.="1,";
		} else {
			$UidText.="0,";
		}
		chop $UidText;
		$UidText.=")\n";
		$UidText.="#pragma data_seg()\n";

		$UIDFile = $BaseTrg.'_UID_.cpp';
		&main::CreateExtraFile("${ExtraFilesPath}$UIDFile", $UidText);
		}


	if (-e $DefFile)
		{
		addFile(&main::Path_Split('File',$DefFile), "Text", "", &main::DefFileType."\\");
		}

	# Add resources: rsc files, mbm files and aif files

	my $mmpdir = &main::Path_Split('Path',$MmpFile);
	$changedsettings{"SymbianResourcesMMPFileLocation"} = "{0}$mmpdir";
	my $ResourcesText="";
	my @ResourceDownloadList=();
	
	# --- MBM files
	
	my $BitMapStructRef = &main::BitMapStructRef();
	my $BitMapRef;
	
	foreach my $BitMapRef (@$BitMapStructRef) {
		my $trgfile = $$BitMapRef{Trg};
# change - only use colour resource files
		next if ($trgfile =~ /\.MBW$/i);	# ignore greyscale MBM files
		$trgfile =~ s/\.MCL$/.MBM/;			# convert MCL to MBM for immediate use
		my $entry = "  <mbm targetfile = \"$trgfile\"";
		$entry .= " targetpath = \"".&main::Path_Chop($$BitMapRef{TrgPath})."\"";
		push @ResourceDownloadList, $$BitMapRef{TrgPath}.$trgfile;
		if (defined $$BitMapRef{Hdr})
			{
			$entry .= " header = \"true\"";
			}
		else
			{
			$entry .= " header = \"false\"";
			}
		$entry .= ">\n";
		foreach my $SrcRef (@{$$BitMapRef{Source}}) {
			$entry .= "    <bmp bpp = \"$$SrcRef{ClDepth}\"";
			my $bmpfile = &main::Path_Split('File',$$SrcRef{Src});
			my $bmppath = &main::Path_Split('Path',$$SrcRef{Src});
			my $sourcepath = &main::Path_Chop(&main::Path_MakeRltToBase($mmpdir,$bmppath));
			$entry .= " sourcepath = \"$sourcepath\"";
			$entry .= " sourcefile = \"$bmpfile\"/>\n";
		}
		$ResourcesText .= $entry . "  </mbm>\n";
	}
	
	# --- AIF files

	my $AifStructRef = &main::AifStructRef();
	my $AifRef;

	foreach $AifRef (@$AifStructRef) {
# regression change - workaround lack of AIF directory
		my $trgpath=&main::TrgPath;
		my $trgfile=&main::Path_Split('File',$$AifRef{Trg});
		my $path=&main::Path_Split('Path',"$trgpath$$AifRef{Trg}");
		$path=&main::Path_Chop($path);  
# change - only use colour resource files
		next if ($trgfile =~ /\.ABW$/i);	# ignore greyscale AIF files
		$trgfile =~ s/\.ACL$/.AIF/;			# convert ACL to AIF for immediate use
 		my $rssfile = &main::Path_Split('File',$$AifRef{Source});
 		my $rsspath = &main::Path_Split('Path',$$AifRef{Source});
 		my $sourcepath=&main::Path_Chop(&main::Path_MakeRltToBase($mmpdir,$rsspath));
 		my $entry = "  <aif sourcefile = \"$rssfile\"";
   		$entry .= " sourcepath = \"$sourcepath\"";
   		$entry .= " targetfile = \"$trgfile\" targetpath = \"$path\">\n";
   		push @ResourceDownloadList, "$path\\$trgfile";
 		foreach my $BitmapRef (@{$$AifRef{BitMaps}}) {
 			$entry .= "    <bmp bpp = \"$$BitmapRef{ClDepth}\"";
 			my $bmpfile = &main::Path_Split('File',$$BitmapRef{Src});
 			my $bmppath = &main::Path_Split('Path',$$BitmapRef{Src});
 			$sourcepath = &main::Path_Chop(&main::Path_MakeRltToBase($mmpdir,$bmppath));
 			$entry .= " sourcepath = \"$sourcepath\"";
 			$entry .= " sourcefile = \"$bmpfile\"/>\n";
		}
   		$ResourcesText .= $entry . "  </aif>\n";

   	}

	
	# --- RSC files, which must come after .MBM files since they may use the .MBG header files
	
	my $ResourceStructRef=&main::ResourceStructRef;
	my @resourcefiles;
	my %resourcetargets;

	# NOTE: An <rsc/> block is now created for each START RESOURCE blocks LANG entries.  This
	# shouldn't be necessary as <rsc/> blocks support multiple <language/> tags, and the generation
	# of separate localised binaries should be dealt with by the Symbian Resources IDE plugin.
	# However, a defect in the plugin's processing of <rsc/> "targetfile" attributes means that is
	# doesn't correctly generate separate localised binaries with per-LANG extensions.

	my %headerProcessed;

	foreach my $ResourceRef (@$ResourceStructRef) {
		my $fullsource=$$ResourceRef{Source};
		my $rssfile=&main::Path_Split('File', $fullsource);
		my $rsspath=&main::Path_Split('Path', $fullsource);
		my $entry = "  <rsc sourcefile = \"$rssfile\"";
		$entry .= " targetpath = \"".&main::Path_Chop($$ResourceRef{TrgPath})."\"";
		
		#############################################################
		# if CW version is 3.1 or greater, add TARGET file if present
		# tkelly 4-May-05
		if ($CW_major_version >= 3.1)
		{
			my $trgfile=&main::Path_Split('File',$$ResourceRef{Trg}); 
			$entry .= " targetfile = \"$trgfile\"\n"; #tk
		}
		##############################################################
		if ((defined $$ResourceRef{Hdr}) && (!$headerProcessed{$fullsource}))
			{
			$entry .= " header = \"true\"";
			$headerProcessed{$fullsource} = 1;
			}
		else
			{
			$entry .= " header = \"false\"";
			}
		my $sourcepath=&main::Path_Chop(&main::Path_MakeRltToBase($mmpdir,$rsspath));
		$entry .= " sourcepath = \"$sourcepath\">\n";
		# ignore the UidList for now..
		$resourcetargets{$fullsource} = $$ResourceRef{TrgPath}.&main::Path_Split('Base', $rssfile);

		$entry .= "    <language id = \"$$ResourceRef{Lang}\"/>\n";
		push @resourcefiles, $entry;
		push @ResourceDownloadList, $resourcetargets{$fullsource}.".R".$$ResourceRef{Lang};
	}

 	foreach my $resourceEntry (@resourcefiles) {	
 			$ResourcesText .= $resourceEntry . "  </rsc>\n";
 			}

	# --- If required, generate .resources file per platform
	
	if ($ResourcesText ne "")
		{
		my $resourcesfile = "$BaseTrg$Plat.resources";
		&main::CreateExtraFile("${ExtraFilesPath}$resourcesfile", "<resources>\n$ResourcesText</resources>\n");
		addFile($resourcesfile, "Text", "", "", "Resources");
		}
		
	# Build the rest of the file list

	if ($BasicTrgType!~/^LIB$/o)
		{
		addFile($FirstLib, "Library", $debug, "$Bld\\");	# static library, build-specific
		}
	
	my $file;
	foreach $file (@SrcList)
		{
		# ensure the case of the extension is what GCC expects
		$file =~ s/\.CPP$/.cpp/i;
		$file =~ s/\.C$/.c/i;
		$file =~ s/\.s$/.S/i;
		my $srcfile=&main::Path_Split('File',$file);
		addFile($srcfile, "Text", $debug, "");
		}

	# If required, add the uid.cpp file so that it appears after all other source files in the link order
	if (defined $UIDFile)
		{
		addFile($UIDFile, "Text", "", "");
		}
	
	if ($Plat ne "GCCE" && !$CustGCCE)
	{
	# linking with GCCE, Runtime libs need to be at the end to match with make, otherwise evalid can fail.
	foreach $file (@RuntimeLibs)
		{
		next if ( $file eq $FirstLib );		#skip if file equals FirstLib.
		addFile($file, "Library", $debug, "$Bld\\"); # static library, build specific
		}
	}
				
	foreach $file (@StatLibList)
		{
		next if ( $file eq $FirstLib );		#skip if file equals FirstLib.
		addFile($file, "Library", $debug, "$Bld\\"); # static library, build specific
		}
	foreach $file (@ASSPLibList, @LibList)
		{
		next if ( $file eq $FirstLib );		#skip if file equals FirstLib.
		if ($Plat eq "GCCE" or $ABIV2 or $CustGCCE) {
			$file =~ s/\.LIB$/.DSO/;
			$file =~ s/\.lib$/.dso/;
			}
		addFile($file, "Library", $debug, "");
		}
		
	if ($Plat eq "GCCE" || $CustGCCE)
	{
		foreach $file (@RuntimeLibs)
		{
			next if ( $file eq $FirstLib );		#skip if file equals FirstLib.
			
			#change to prevent multiple libs being listed when they are shared between targets.
			if ($file eq "usrt2_2.lib" || $file eq "EDLLSTUB.LIB") {
				addFile($file, "Library", $debug, "$Bld\\"); # static library, build specific
			}
			else {
				addFile($file, "Library", $debug, ""); # static library, build non-specific
			}
		}
	}
		
	if ($Plat eq "WINSCW")
		{
		my $defaults = $ENV{'MWSym2LibraryFiles'};
		# look out for paths?
		foreach $file (@Win32LibList)
			{
			# skip default libs and FirstLib
			next if ( ($defaults =~ /;$file/) || ($file eq $FirstLib) );
			addFile($file, "Library", $debug, "");
			}
		}

	
	# Update the project settings

	$changedsettings{"UserSourceTrees"} = "{}";
	$changedsettings{"UserSearchPaths"} = "{}";
	$changedsettings{"SystemSearchPaths"} = "{}";
	$changedsettings{"Targetname"} = $targetname;

	my $outputdir = $RelPath;
	if ($Plat eq "WINSCW")
		{
		my $trgpath = &main::TrgPath;
		&Winutl_AdjustTargetPath(\$trgpath);
		$outputdir .= $trgpath;
		}
	$changedsettings{"OutputDirectory"} = "{1}".&main::Path_Chop($outputdir);
	$changedsettings{"SymbianInstallationContentSearchLocation"} = "{0}".&main::Path_Chop($RelPath);

	$changedsettings{"SymbianResourcesHeaderFileOutputLocation"} = "{0}".&main::Path_Chop(&main::EPOCIncPath());
	if ($Plat eq "WINSCW")
		{
		$changedsettings{"SymbianResourcesBinaryOutputLocation"} = "{0}".&main::Path_Chop($RelPath);
		}
	else
		{
		$changedsettings{"SymbianResourcesBinaryOutputLocation"} = "{0}".&main::Path_Chop(&main::EPOCDataPath());
		}
		
	if ($Plat eq "WINSCW")
		{
		if ($TrgType eq "EXE")
			{	
			# IDE would do the right thing, but we might as well make sure...
			$changedsettings{"MWRuntimeSettings_HostApplication"} = "{0}$outputdir$Trg";
			}
		else
			{
			$changedsettings{"MWRuntimeSettings_HostApplication"} = "{0}${RelPath}epoc.exe";
			}
		}


	$changedsettings{"SymbianEpocToolsPath"} = "{0}${epocroot}";

	if ($Plat ne "WINSCW")
		{
		my $downloadpath = &main::TrgPath;
		if (&main::EPOCSecurePlatform && $downloadpath !~ /^Z\\sys\\bin\\/)
			{
			my @hrhMacros = &Variant_GetMacroList;
			if (grep /^SYMBIAN_IGNORE_BIN_TARGETPATH\s*$/, @hrhMacros)
				{
				$downloadpath = "Z\\sys\\bin\\";
				}
			}
		$downloadpath =~ s/^Z\\/C:\\/i;
		$changedsettings{"DownloadPath"} = $downloadpath;
		$changedsettings{"FileList"} = "{}";
		}

	my @MacroList;

	@MacroList = &main::MacroList();			
		
	push @MacroList, "__${Plat}__" if ($Plat ne $ABI);
	push @MacroList, "__SUPPORT_CPP_EXCEPTIONS__" if ($Plat eq "WINSCW");
	push @MacroList, "__SUPPORT_CPP_EXCEPTIONS__" if (($Plat eq "ARMV5") || ($Plat eq "ARMV5_ABIV2") || ($Plat eq "ARMV5_ABIV1") || ($Plat eq "GCCE") || $IsPlatCustomization);

	# Symbian Compiler Panel
			
	my $compiler="";
	my $compilerargs="";
	my $macros="";

	if ((($Plat eq "ARMV5") || ($Plat eq "ARMV5_ABIV2") || ($Plat eq "ARMV5_ABIV1") || ($Plat eq "GCCE") || $IsPlatCustomization) && $VariantFile)
		{
		push @MacroList, '__PRODUCT_INCLUDE__=\\"'.&main::Path_Split('File',$VariantFile).'\\"' if $VariantFile;
		}

	foreach (@MacroList)
		{
		$_ =~ s/\s+$//;
		$_ =~ s/^\s+//;
		$macros .= "$_,";
		}

###############################
# WINSCW compilation settings #
###############################

	if ($Plat eq "WINSCW")
		{
		$compiler = "WINSCW";
		$compilerargs .= "-wchar_t off -align 4 -warnings on -w nohidevirtual, nounusedexpr -msgstyle gcc -enum int -str pool ";
		$compilerargs .= "-exc ms ";
		$compilerargs .= "-trigraphs on ";

		if ($Bld =~ /DEB/)
			{
			$compilerargs .= "-O0 ";

			# euser change to apply inlining on the _NAKED functions
			if ($BaseTrg !~ /^EUSER$/oi)
				{
				$compilerargs .= "-inline off ";
				}
			}
		else
			{
			$compilerargs .= "-O4,s ";
			}
			
		if ($Win32StdHeaders || $Win32Resrc ) 
			{
			$macros .= "WIN32,_WINDOWS,";
			# Callisto defect workaround
			# NOTE: persisting with this for consistency
			$compilerargs .= "-stdinc ";
			}
		else
			{
			$compilerargs .= "-nostdinc ";
			}
				
		$compilerargs .= &main::CompilerOption("CW");
		$changedsettings{"Macros"} = $macros;
		if ($VariantFile)
			{
			$changedsettings{"PrefixFile"} = &main::Path_Split('File',$VariantFile);
			}			
		}

#############################
# RVCT compilation settings #
#############################
		
	elsif ((($Plat eq "ARMV5") || ($Plat eq "ARMV5_ABIV2") || ($Plat eq "ARMV5_ABIV1") || $ABIV1 || $ABIV2) && ($CW_major_version >= 3)) #|| $IsPlatCustomization
		{
		
		if ($RVCTMajorVersion == 2 && $RVCTMinorVersion < 2) {
			$changedsettings{"CompilerXMLDescriptor"} = "ARM RVCT";
		}
		else {
			if(($CW_major_version == 3))
			{ 
				$changedsettings{"CompilerXMLDescriptor"} = "ARM RVCT";
			}
			else
			{
				# RVCT 2.2
				$changedsettings{"CompilerXMLDescriptor"} = "ARM RVCT2_2";
			}
		}	
		if ($Bld =~ /REL$/)
			{
				$compilerargs .= $configdata{"REL_OPTIMISATION"}." ".$configdata{"RUNTIME_SYMBOL_VISIBILITY_OPTION"}.$CCUREL;		
			}
		else
			{
			unless (&main::SrcDbg)
				{
					$compilerargs .= $configdata{"DEBUG_OPTIMISATION"}." ".$configdata{"RUNTIME_SYMBOL_VISIBILITY_OPTION"}.$CCUDEB;		
				}
			}
		$changedsettings{"PrefixFile"} = &main::Path_Split('File',$PrefixFile);
		}

############################
# GCC compilation settings #
############################
		
	elsif ($Plat eq "ARM4")
		{
		$compiler = "ARM";
			
		if ($Bld =~ /REL$/)
			{
			$compilerargs .= "-s -fomit-frame-pointer -O "
			}
		else
			{
			unless (&main::SrcDbg)
				{
				$compilerargs .= "-O ";
				}
			}
		
		if ($ABI eq "ARMI")
			{
			$compilerargs .= "-march=armv4t -mthumb-interwork";
			}
		elsif ($ABI eq "ARM4T")
			{
			if (&main::BuildAsARM)
				{
				$compilerargs .= "-march=armv4t -mthumb-interwork";
				}
			elsif ($SystemTrg)
				{
				$compilerargs .= "-march=armv4";

				unless (&main::PlatABI eq "ARM4")
					{
					$compilerargs .= "t";
					}
				}
			else
				{
				$compiler = "THUMB";
				$compilerargs .= "-mthumb-interwork";
				$macros .= "__MARM_THUMB__,";
				}
			}
		elsif ($ABI eq "ARM4")
			{
			$compilerargs .= "-march=armv4";

			unless (&main::PlatABI eq "ARM4")
				{
				$compilerargs .= "t";
				}
			}
		elsif ($ABI eq "THUMB")
			{
			$compiler = "THUMB";
			$compilerargs .= "-mthumb-interwork";
			}
	
		if ($VariantFile)
			{
			$changedsettings{"PrefixFile"} = &main::Path_Split('File',$VariantFile);
			}
		}

############################
# GCCE BPABI compilation settings #
############################

		
	elsif ((($Plat eq "GCCE") || $CustGCCE)) # || $IsPlatCustomization) && ($CW_major_version >= 3)) 
		{
		$compiler = "ARM GCCE";
		#Change setting CompilerXMLDescriptor is only good for CW 3.0 and greater.
		$changedsettings{"CompilerXMLDescriptor"} = "ARM GCCE";	
		
		if ($Bld =~ /REL$/)
		{
			$compilerargs .= $configdata{"REL_OPTIMISATION"}." ".$configdata{"RUNTIME_SYMBOL_VISIBILITY_OPTION"}.$CCUREL;
		}
		else
		{
			unless (&main::SrcDbg)
			{
				$compilerargs .= $configdata{"DEBUG_OPTIMISATION"}." ".$configdata{"RUNTIME_SYMBOL_VISIBILITY_OPTION"}.$CCUDEB
			}
		}
		$changedsettings{"PrefixFile"} = &main::Path_Split('File',$PrefixFile);
		}
####################
# General settings #
####################

	$macros =~ s/,$//;
	$compilerargs =~ s/ $//;

	$changedsettings{"Compiler"} = $compiler; # in CW 3.0, "Compiler" no longer exists. This line has no effect on those versions
	$changedsettings{"Arguments"} = $compilerargs;

	# Symbian Linker Panel
	$changedsettings{"LinkOutputFile"} = $Trg;
	
	if ($Plat eq "GCCE" || $CustGCCE || $ABIV2) {
		$changedsettings{"SymbianImportLibrary"} = $ExportLibrary.'.dso';
	}
	else {
		$changedsettings{"SymbianImportLibrary"} = $ExportLibrary.'.lib';
	}
	
	# Template defaults for canDebug/canRun are both "true"
	if ($Bld =~ /REL/)
		{
		$changedsettings{"canDebug"} = "false";
		}

	if ($Plat eq "WINSCW")
		{
		if ($TrgType ne "APP" && $TrgType ne "EXE" && $TrgType ne "EXEDLL" && $TrgType ne "EPOCEXE")
			{
			$changedsettings{"canDebug"} = "false";
			$changedsettings{"canRun"} = "false";
			}
		}
	else
		{
		$changedsettings{"canRun"} = "false";

		if ($TrgType eq "LIB" || $TrgType eq "KLIB")
			{
			$changedsettings{"canDebug"} = "false";
			}
		}
		
		
	$xmlLinkDescriptorDoc = $xmlParser->parsefile ($FindBin::Bin."\\$linkDescriptorTemplate");
	$xmlLinkDescriptorCommandParent = $xmlLinkDescriptorDoc->getElementsByTagName("array",1)->item(0);

	if ($CW_major_version >= 3)
		{
		$xmlLinkDescriptorSymbolParent = $xmlLinkDescriptorDoc->getElementsByTagName("array",1)->item(1);
		$xmlLinkDescriptorDumpFileParent = $xmlLinkDescriptorDoc->getElementsByTagName("array",1)->item(2);
		}
	
	my $linkDescriptorFile = "$BaseTrg$Plat$Bld.cwlink";

	my $copyCommand = 'perl.exe -S ecopyfile.pl ';
	my $deleteCommand = 'cmd.exe /C del ';

	my $tempFilenameRoot = '${var:IMPORT_LIBRARY_NO_EXT}';

	if ($CW_major_version < 3)
		{
		$tempFilenameRoot = $ExportLibrary;
		}	

	my $exportUnfrozenWarningMessage = 'Created "${var:KIT_EPOCROOT}'.kitRelativePath($LibPath).'${var:IMPORT_LIBRARY}" '.
										'from "${target.data}\\'.$tempFilenameRoot.'.def" as EXPORTUNFROZEN specified.';



############################################
# WINSCW library generation and link stage #
############################################

	if ($Plat eq "WINSCW")
		{
		# Generate library
		if ($DefFile and !$NoExportLibrary)
			{
			unless (&main::ExportUnfrozen)
				{
					my $LibLinkAs = ($BasicTrgType=~/^IMPLIB$/io) ? $LinkAs : $Trg;

					$linkCommand = 'perl.exe -S prepdef.pl "${var:DEF_FILE}" "${target.data}\\'.$tempFilenameRoot.'.prep.def"';
					addLinkDescriptorCommand ($linkCommand);

					$linkCommand = 'mwldsym2.exe "${target.data}\\'.$tempFilenameRoot.'.prep.def" -importlib -o "'.
									'${var:KIT_EPOCROOT}'.kitRelativePath($LibPath).'${var:IMPORT_LIBRARY}" -addcommand "out:'.$LibLinkAs.'" -warnings off';
					addLinkDescriptorCommand ($linkCommand);
				}
			}

		if ((2.8 == $CW_major_version) && (0 == $CW_minor_version))
			{
			# For OEM2.8, create a file containing all objects required in the link.  This is used in all
			# calls to mwldsym2 in order to avoid exceeding the Windows command line
			# length in projects containing a large amount of source files
			$linkCommand ='cmd.exe /C echo ${var:LINK_OBJS}>"${target.data}\${output.file.root}.lst"';
			addLinkDescriptorCommand ($linkCommand);
			}
			
		my $stage1linkflags = "";
		my $linkflags = "";
		my $commonLinkFlags = '-msgstyle gcc -stdlib';
		my $libPath = "epoc32\\release\\winscw\\".lc $Bld;
		if ($SystemTrg){
			$commonLinkFlags .=" ${libPath}\\scppnwdl_kern.lib";
		}
		else{
			$commonLinkFlags .=" ${libPath}\\scppnwdl.lib";
		}
		if ($BasicTrgType=~/^(EXE|DLL)$/o) {
					$commonLinkFlags .= ' ${var:FIRST_LIB} '
			}

		foreach my $lib (@Win32LibList)
			{
			my $win32lib = $lib;
			$win32lib = "-l$win32lib" unless ($win32lib =~ /\\/);
			$commonLinkFlags .= " ". lc $win32lib;
			}

		if ($BasicTrgType =~ /^DLL$/o || $TrgType =~ /^EXEXP$/o)
			{
			if ($BaseAddress ne "")
				{
				$commonLinkFlags .= " -imagebase $BaseAddress";
				}
			
			$commonLinkFlags .= ' -noentry -shared';
			}
		elsif ($BasicTrgType =~ /^EXE$/o)
			{
			$commonLinkFlags .= ' -m "?_E32Bootstrap@@YGXXZ"';
			}

		$commonLinkFlags .= ' -subsystem windows';

		if (&main::HeapSize)
			{
			my %HeapSize = &main::HeapSize;
			$commonLinkFlags .= ' -heapreserve='.RoundUp1k($HeapSize{Max}).' -heapcommit='.RoundUp1k($HeapSize{Min}); 
			}

		if ($BasicTrgType =~ /^(DLL|EXE)$/o)
			{
			if ($Bld =~ /DEB$/o)
				{
				$commonLinkFlags .= ' -g';
				}
			}
			
		my $EntrySymbol='';
		if ($BasicTrgType=~/^DLL$/o || $TrgType=~/^EXEXP$/o)
			{
			my $Include="";
			if ($BasicTrgType=~/^DLL$/o)
				{
				$Include="-m __E32Dll";
				$EntrySymbol='__E32Dll';
				}
			else
				{
				$Include="-m __E32Startup";
				$EntrySymbol='__E32Startup';
				}
				
			$stage1linkflags = $commonLinkFlags.' ${var:LIBS}'.
				' -o "${target.data}\\${output.file.name}"'. 
				' -export dllexport '.
				$Include.
				' -nocompactimportlib'. 
				' -implib "${target.data}\\${var:IMPORT_LIBRARY}"'.
				' -addcommand "out:${output.file.name}"'.
				' -warnings off';
			}
				
		my $AbsentSubst = "";
		if ($EntrySymbol)
			{
			$AbsentSubst = " -absent $EntrySymbol";
			}

		$linkflags = $commonLinkFlags.' ${var:LIBS}'.
			' -o "${output}\\${output.file.name}"';
			
		if ($Bld=~/REL$/o && $BasicTrgType!~/^LIB$/o)
			{
			# Generate map file for release build executables
			$linkflags .= ' -map "${output}\\${output.file.name}.map"';
			}
		
		if ($BasicTrgType=~/^DLL$/o || $TrgType=~/^EXEXP$/o)
			{
			$linkflags .= ' -f "${target.data}\\'.$tempFilenameRoot.'.def"';
			
			if (&main::ExportUnfrozen)
				{
				$linkflags .= ' -implib "${var:KIT_EPOCROOT}'.kitRelativePath($LibPath).'${var:IMPORT_LIBRARY}"'.
					' -addcommand "out:${output.file.name}" -warnings off';
				}
			else
				{
				$linkflags .= ' -noimplib ';
				}
			}
			else
			{
			$linkflags .= ' -noimplib ';
			}

		if ($BasicTrgType=~/^DLL$/o || $TrgType=~/^EXEXP$/o)
			{
			if (($CW_major_version >= 3) ||
				((2.8 == $CW_major_version) && ($CW_minor_version >= 1)))
				{
				# For OEM2.8.1 onwards, make use of ${var:LINK_OBJS_NO_PATH} in order to reduce link
				# command line lengths
				$linkCommand = 'mwldsym2.exe '.$stage1linkflags.' ${var:COMMON_LINK_FLAGS} -l "${target.data}\\ObjectCode" -search ${var:LINK_OBJS_NO_PATH}';				
				}
			else
				{
				$linkCommand = 'mwldsym2.exe '.$stage1linkflags.' ${var:COMMON_LINK_FLAGS} @"${target.data}\${output.file.root}.lst"';
				}

			addLinkDescriptorCommand ($linkCommand, undef, undef, undef, $FirstLib);
				
			$linkCommand = $deleteCommand.'"${target.data}\\${output.file.name}"';
			addLinkDescriptorCommand ($linkCommand);

			my $show_options = 'names,unmangled,verbose';
			$linkCommand = 'mwldsym2.exe -S -show only,'.$show_options.' -o "${target.data}\\'.$tempFilenameRoot.'.inf" "${target.data}\\${var:IMPORT_LIBRARY}"';
			addLinkDescriptorCommand ($linkCommand);

			$linkCommand = 'perl.exe -S makedef.pl '.$AbsentSubst.' -Inffile "${target.data}\\'.$tempFilenameRoot.'.inf"';
			if (-e $DefFile)
				{
				$linkCommand .= ' -Frzfile "'.$DefFile.'"';
				}
				
			my $Export;
			my $Ordinal=1;
			foreach $Export (&main::Exports)
				{
				$linkCommand .= " -$Ordinal $Export";
				$Ordinal++;
				}					

			$linkCommand .= ' "${target.data}\\'.$tempFilenameRoot.'.def"';					
			addLinkDescriptorCommand ($linkCommand);
			
			$linkCommand = $deleteCommand.'"${target.data}\\'.$tempFilenameRoot.'.inf"';
			addLinkDescriptorCommand ($linkCommand);
			
			$linkCommand = $deleteCommand.'"${target.data}\\${var:IMPORT_LIBRARY}"';
			addLinkDescriptorCommand ($linkCommand);
			}

		$linkCommand = "mwldsym2.exe ";

		if ($BasicTrgType =~/^LIB$/o)
			{
			$linkCommand .= '-library ';
			}

		if (($CW_major_version >= 3) ||
			((2.8 == $CW_major_version) && ($CW_minor_version >= 1)))
			{
			# For OEM2.8.1 onwards, make use of ${var:LINK_OBJS_NO_PATH} in order to reduce link
			# command line lengths
			$linkCommand .= $linkflags.' ${var:COMMON_LINK_FLAGS} -l "${target.data}\\ObjectCode" -search ${var:LINK_OBJS_NO_PATH}';	
			}
		else
			{
			$linkCommand .= $linkflags.'${var:COMMON_LINK_FLAGS} @"${target.data}\${output.file.root}.lst"';				
			}

		my $warningMessage;
		
		if (&main::ExportUnfrozen)
			{
			$warningMessage = $exportUnfrozenWarningMessage;
			}

		if ($BasicTrgType =~/^LIB$/o)
			{				
			addLinkDescriptorCommand ($linkCommand, undef, undef, undef, undef, $warningMessage);
			}
		else
			{
			addLinkDescriptorCommand ($linkCommand, undef, undef, undef, $FirstLib, $warningMessage);
			}

		if (&Winutl_CopyForStaticLinkage)
			{
			$linkCommand = $copyCommand.
							'"${output}\\${output.file.name}" '.
							'"${var:KIT_EPOCROOT}'.kitRelativePath($RelPath).'${output.file.name}"';
			addLinkDescriptorCommand ($linkCommand, "false", "false");
			}
		}

##########################################
# RVCT library generation and link stage #
##########################################

	elsif ($ABIV1 && ($CW_major_version >= 3)) 
		{
		my $InterWorking = ($ABI eq 'ARMV4') ? "" : "--inter";
			
		# Generate library

		if ($DefFile and !$NoExportLibrary)
			{
			unless (&main::ExportUnfrozen)
				{
				$linkCommand = 'perl.exe -S prepdef.pl "${var:DEF_FILE}" "${target.data}\\'.$tempFilenameRoot.'.prep.def"';
				addLinkDescriptorCommand ($linkCommand);

				$linkCommand = 'def2dll.bat --path="${var:KIT_EPOCROOT}'.kitRelativePath($LibPath).'\\" --bldpath="${target.data}" --import='.$tempFilenameRoot.' '.
					'--deffile="${target.data}\\'.$tempFilenameRoot.'.prep.def" --linkAs='.$LinkAs.' '.$InterWorking;
				addLinkDescriptorCommand ($linkCommand);

				if ($ExtraExportLibrary)
					{
					$linkCommand = $copyCommand.'"${var:KIT_EPOCROOT}'.kitRelativePath($LibPath).'${var:IMPORT_LIBRARY}" '.
									'"${var:KIT_EPOCROOT}'.kitRelativePath($LibPath).$ExtraExportLibrary.'.lib"';
					addLinkDescriptorCommand ($linkCommand,"false", "false");
					}
				}
			}

		return if ($BasicTrgType=~/^IMPLIB$/io);
		

		# Create custom symbols only required in RVCT builds
		my $implibs_no_path_vtblexport = "";

		foreach my $lib (@LibList)
			{
			$implibs_no_path_vtblexport.="$lib(VtblExports.o) ";
			}

		addLinkDescriptorSymbol ('${var:IMPLIBS_NO_PATH_VTBLEXPORT}', $implibs_no_path_vtblexport);

		my $AbsentSubst = '';
		my $EntrySymbol;
		my $Link = '';

		if ($BasicTrgType=~/^DLL$/o) {
			$EntrySymbol = '_E32Dll';
		}
		elsif ($BasicTrgType=~/^EXE$/o) {
			$EntrySymbol = '_E32Startup';
		}
		if ($EntrySymbol) {
			$AbsentSubst = " -absent $EntrySymbol";
		}

		$Link = 'armlink '.$oP.'diag_suppress 6331,6780'.$linkeropts.' ';

		if ($Bld =~ /DEB$/o)
			{
			$Link .= ' '.$oP.'debug';
			}


	    # TARGET *.IN
	    #------------

		# Create "via" file containing all link objects in order to reduce link
		# command line length
		addLinkDescriptorDumpFile ('${var:LINK_OBJS}', '${target.data}\\${output.file.root}_'.$Bld.'_objects.via');
		
		if ($BasicTrgType!~/^LIB$/o) {
			$linkCommand = $Link .' '.$oP.'partial -o ${var:COMMON_LINK_FLAGS} "${target.data}\\${output.file.root}.in" '.$oP.'via "${target.data}\\${output.file.root}_'.$Bld.'_objects.via"';
			addLinkDescriptorCommand ($linkCommand);
		}

		# Perform link

	    if ($BasicTrgType=~/^DLL$/o || $TrgType=~/^EXEXP$/o || $TrgType=~/^EXEDLL$/o) {
	#		reorder the .DEF file taking frozen exports into account if there are any

			$linkCommand = 'perl -S elf2inf.pl -o "${target.data}\\'.$tempFilenameRoot.'.inf" "${target.data}\\${output.file.root}.in"';
			addLinkDescriptorCommand ($linkCommand);

			$linkCommand = 'perl -S makedef.pl '.$AbsentSubst.' -Inf "${target.data}\\'.$tempFilenameRoot.'.inf"';

	    	if (!$DefFile || $NoExportLibrary) {    			
	    		$linkCommand .= ' -ignore_unfrozen_noncallable ';
	    	}
		if (SysTrg()) {
	    		$linkCommand .= ' -SystemTargetType ';
	    	}		
		
		    if (-e $DefFile) {	# effectively "if project frozen ..."
		        $linkCommand .= " -Frzfile \"".'${var:DEF_FILE}'."\"";
		    }
		    # freeze ordinals, a maximum of 2, for polymorphic dlls
		    my $Ordinal;
		    my $Num=1;
		    foreach $Ordinal (&main::Exports) {
		    	$linkCommand .= " -$Num $Ordinal";
			    $Num++;
		    }

		    $linkCommand.= ' "${target.data}\\'.$tempFilenameRoot.'.def"';
		    addLinkDescriptorCommand ($linkCommand);
    
		    my $theDefFile = '"${target.data}\\'.$tempFilenameRoot.'.def"';
		    $theDefFile = '"${var:DEF_FILE}"' if (-e $DefFile && !&main::ExportUnfrozen);

			$linkCommand = 'def2dll.bat'.$AbsentSubst.' --path="${target.data}" --bldpath="${target.data}" --export='.$tempFilenameRoot.' --deffile='.$theDefFile.' --linkAs='.$LinkAs.' '.$InterWorking;
			addLinkDescriptorCommand ($linkCommand);
	    }

        if ($BasicTrgType=~/^DLL$/o || $TrgType=~/^EXEXP$/o || $TrgType=~/^EXEDLL$/o) {
#		generate an export object from the ordered .DEF file
		if (&main::ExportUnfrozen) {
			$linkCommand = 'def2dll.bat --path="${var:KIT_EPOCROOT}'.kitRelativePath($LibPath).'\\" --bldpath="${target.data}" --import='.$tempFilenameRoot.
				' --deffile="${target.data}\\'.$tempFilenameRoot.'.def" --linkAs='.$LinkAs.' '.$InterWorking;
		    addLinkDescriptorCommand ($linkCommand, undef, undef, undef, undef, $exportUnfrozenWarningMessage);
			}
        }

#       get rid of any -symbols produced .map file
        if ($BasicTrgType=~/^(DLL|EXE)/o) {
			$linkCommand = $deleteCommand.'"${output}\\${output.file.name}.map"';
			addLinkDescriptorCommand ($linkCommand, "false", "false", undef, undef, undef, undef, (0,1));
		}

        if ($BasicTrgType=~/^(DLL|EXE)/o) {
	        my $datalinkbase = "0x400000";
	        $datalinkbase = &main::DataLinkAddress if (&main::DataLinkAddress);
		
	        $linkCommand = "$Link ".$oP.'shl '.$oP.'reloc '.$oP.'split '.$oP."rw-base $datalinkbase ".$oP.'noscanlib '."$PlatOpt{Ld}";
	        
	        if ($BasicTrgType=~/^DLL$/o) {
	            # get the right object file for the entry point
	            my $ObjFile = "UC_DLL_.o";
	            if ($FirstLib =~ /EDEV/i) {
		            $ObjFile = "D_ENTRY_.o";
	            }
	            if ($FirstLib =~ /EKLL/i) {
		            $ObjFile = "L_ENTRY_.o";
	            }
	            if ($FirstLib =~ /EEXT/i) {
		            $ObjFile = "X_ENTRY_.o";
	            }
	            if ($FirstLib =~ /EVAR/i) {
		            $ObjFile = "V_ENTRY_.o";
	            }

#		    If platform is a customization, take libs from parent directory.		   
			$linkCommand .= $oP.'entry _E32Dll "'.$FirstLib.'('.$ObjFile.')"';
		    if($IsCustomDll)
		    {
				$linkCommand .=	' "${target.data}\\'.$tempFilenameRoot.'.exp"';
			}
		    
	        } elsif ($BasicTrgType=~/^EXE$/o || $TrgType=~/^EXEXP$/o) {
			    # get the right object file for the entry point
			    my $ObjFile = "UC_EXE_.o" ;
			    if ($FirstLib =~ /KC_EXE/i) {
					$ObjFile = "K_ENTRY_.o";
			    }

			if($IsPlatCustomization) 
			{
				$linkCommand .= $oP.'entry _E32Startup "'.$FirstLib.'('.$ObjFile.')"';
			} 
			else
			{
			    	$linkCommand .= $oP.'entry _E32Startup "'.$FirstLib.'('.$ObjFile.')"';
			}
		    
			    if ($TrgType=~/^EXEXP$/o || $TrgType=~/^EXEDLL$/o) {
					$linkCommand .= ' "${target.data}\\'.$tempFilenameRoot.'.exp"';
				}
			}

			$linkCommand .= ' -o "${target.data}\\${output.file.name}"';

			$linkCommand .= ' '.$oP.'symbols '.$oP.'list "${output}\\${output.file.name}.map"';
			$linkCommand .= ' "${target.data}\\${output.file.root}.in"';


			if ($BasicTrgType=~/^DLL$/o) { # Add the DLL stub library
				if($IsPlatCustomization) 
				{
				if ($RVCTMajorVersion == 2 && $RVCTMinorVersion < 2) {
				   $linkCommand .= ' "${var:KIT_EPOCROOT}\\EPOC32\RELEASE\ARMV5\\'.$Bld.'\EDLLSTUB'.$RVCTVersion.'.LIB"';
				}
				else {
				   $linkCommand .= ' "${var:KIT_EPOCROOT}\\EPOC32\RELEASE\ARMV5\\'.$Bld.'\EDLLSTUB.LIB"';
				}
				}
				else
				{
				if ($RVCTMajorVersion == 2 && $RVCTMinorVersion < 2) {
				   $linkCommand .= ' "${var:KIT_EPOCROOT}\\EPOC32\RELEASE\ARMV5\\'.$Bld.'\EDLLSTUB'.$RVCTVersion.'.LIB"';
				}
				else {
				   $linkCommand .= ' "${var:KIT_EPOCROOT}\\EPOC32\RELEASE\ARMV5\\'.$Bld.'\EDLLSTUB.LIB"';
				}
				}
			}

			$linkCommand .= ' ${var:LIBS}';
			
			
			my $runtimeLibs = "";
			my $StaticRTLib = "usrt".$RVCTVersion;
			
			# use ksrt for system code and for user ARM code
			$StaticRTLib = "ksrt".$RVCTVersion if ($SystemTrg);
			$runtimeLibs .= $StaticRTLib.".lib" unless ($Trg =~ /(U|K)SRT/i || ($BasicTrgType=~/^LIB$/o));
		
			unless ($ArmRT || ($BasicTrgType=~/^LIB$/o)) {
				my $TargLib = "$ExportLibrary.lib";
				$TargLib =~ s/\{(\d|a|b|c|d|e|f){8}\}//i;
				unless ($SystemTrg) {
					foreach (@RTLibList) {
						$runtimeLibs .= " ".$_ unless ($_ =~ /$TargLib/i);
					}
			    }
			}

			foreach (@ArmLibList)
				{
				$runtimeLibs.= " ".$_;
				}
			
			addLinkDescriptorSymbol ('${var:RUNTIME_LIBS}', $runtimeLibs);
				
			if($IsPlatCustomization) 
			{
			     $linkCommand .= ' --userlibpath "${var:KIT_EPOCROOT}\\EPOC32\RELEASE\\'.$ParentPlat.'\\'.$Bld.'","${var:KIT_EPOCROOT}\\EPOC32\RELEASE\\'.$ParentPlat.'\LIB" ${var:RUNTIME_LIBS} ${var:IMPLIBS_NO_PATH_VTBLEXPORT}';
			}
			else
				{
				addLinkDescriptorSymbol ('${var:RUNTIME_LIBS}', 'usrt'.$RVCTVersion.'.lib dfpaeabi.lib dfprvct'.$RVCTVersion.'.lib dfprvct'.$RVCTVersion.'-thunk.lib drtaeabi.lib drtaeabi.lib(VtblExports.o) drtrvct'.$RVCTVersion.'.lib');
				}
				
			$linkCommand .= ' --userlibpath "${var:KIT_EPOCROOT}\\EPOC32\RELEASE\ARMV5\\'.$Bld.'","${var:KIT_EPOCROOT}\\EPOC32\RELEASE\ARMV5\LIB" ${var:RUNTIME_LIBS} ${var:IMPLIBS_NO_PATH_VTBLEXPORT}';
			
			addLinkDescriptorCommand ($linkCommand, undef, undef, undef, $FirstLib);

	    	if ($Bld=~/^U?DEB$/o) {
			$linkCommand = $copyCommand. ' "${target.data}\\${output.file.name}" "${output}\\${output.file.root}.sym"'; 
			addLinkDescriptorCommand ($linkCommand, "false", "false");
	    	}
	
			$linkCommand = 'elftran -version '. &Genutl_VersionToUserString(%Version).' -sid '. &main::SecureId(); 
			if (&main::CompressTarget) {
				$linkCommand .= ' -nocompress ';
			}
			# change - exexps are allowed data, but they look like dlls to elftran....
			if (&main::AllowDllData || $TrgType=~/^EXEXP$/o || $TrgType=~/^EXEDLL$/o) {
				$linkCommand.=' -allow';
			}
			if (not &main::CallDllEntryPoints ) {
				$linkCommand.=' -nocall';
			}
			if (&main::DataLinkAddress) {
				$linkCommand.=' -datalinkaddress '.&main::DataLinkAddress;
			}
			if (&main::FixedProcess) {
				$linkCommand.=' -fixed';
			}
			if (&main::HeapSize) {
				my %HeapSize=&main::HeapSize;
				$linkCommand.=' -heap '.$HeapSize{Min}.' '.$HeapSize{Max};
			}
			if (&main::ProcessPriority) {
				$linkCommand.=' -priority '.&main::ProcessPriority;
			}
			if (&main::StackSize) {
				$linkCommand.=' -stack '.&main::StackSize;
			}

			my $i=1;
			foreach (@UidList) {
				$linkCommand.=" -uid$i $_";
				$i++;
			}
			if(&main::VendorId) {
				$linkCommand.=' -vid '.&main::VendorId;
			}

			$linkCommand.=' -fpu '.$floatingpointmodel;

			$linkCommand.=' -capability '.&main::Capability. ' "${target.data}\\${output.file.name}" "${output}\\${output.file.name}"';
		
			addLinkDescriptorCommand ($linkCommand, "false");
        	}
        elsif ($BasicTrgType=~/^LIB$/o) {
			$linkCommand = 'armar '.$oP.'create "${output}\\${output.file.name}" '.$oP.'via "${target.data}\\${output.file.root}_'.$Bld.'_objects.via"'.' '.$archiveropts;
			addLinkDescriptorCommand ($linkCommand);
			}
		}

##############################################
# BPABI library generation and link stage    #
# Assumes use of RVCT 2.2					 #
##############################################

	elsif ($ABIV2 && ($CW_major_version >= 3.1)) {
		
		# prolly don't need this...
		my $InterWorking = ($ABI eq 'ARMV4') ? "" : "--inter";
			
		return if ($BasicTrgType=~/^IMPLIB$/io);
		
		if ($BasicTrgType=~/^LIB$/o) {
			$linkCommand = 'armar --create ${output}\\${output.file.name} ${var:LINK_OBJS} ${var:LIBS}'.' '.$archiveropts;
	        addLinkDescriptorCommand ($linkCommand);
         }
		else
		{
		my $AbsentSubst = '';
		my $EntrySymbol;
		my $Link = '';

		if ($BasicTrgType=~/^DLL$/o) {
			$EntrySymbol = '_E32Dll';
		}
		elsif ($BasicTrgType=~/^EXE$/o) {
			$EntrySymbol = '_E32Startup';
		}
		if ($EntrySymbol) {
			$AbsentSubst = " -absent $EntrySymbol";
		}

		$Link = 'armlink '.$oP.'diag_suppress 6331,6780'.$linkeropts.' ';

		if ($Bld =~ /DEB$/o)
			{
			$Link .= ' '.$oP.'debug';
			}


	    # TARGET *.IN
	    #------------

		# Create "via" file containing all link objects in order to reduce link
		# command line length
		addLinkDescriptorDumpFile ('${var:LINK_OBJS}', '${target.data}\\${output.file.root}_'.$Bld.'_objects.via');
		
		if ($BasicTrgType!~/^LIB$/o) {
			$linkCommand = $Link .' '.$oP.'partial -o ${var:COMMON_LINK_FLAGS} "${target.data}\\${output.file.root}.in" '.$oP.'via "${target.data}\\${output.file.root}_'.$Bld.'_objects.via"';

		}


#       get rid of any -symbols produced .map file
        if ($BasicTrgType=~/^(DLL|EXE)/o) {
			$linkCommand = $deleteCommand.'"${output}\\${output.file.name}.map"';
			addLinkDescriptorCommand ($linkCommand, "false", "false", undef, undef, undef, undef, (0,1));
		}

        if ($BasicTrgType=~/^(DLL|EXE)/o) {
	        my $datalinkbase = "0x400000";
			my $librarylist;
			my $expr;
			@ToolChainLibList = &GetLibList;

			foreach $expr (@ToolChainLibList) {
				$librarylist .= $expr.' ';
			}
	        
  	        $datalinkbase = &main::DataLinkAddress if (&main::DataLinkAddress);
	        $linkCommand = "$Link ".$oP.'bpabi '.$oP.'reloc '.$oP.'split '.$oP.'no_scanlib '.$oP.'datacompressor=off '.$oP."rw-base $datalinkbase "."$PlatOpt{Ld}";
			$linkCommand .= ' --dll --symver_soname --soname '.$LinkAs.' ';
			
			
	        if ($BasicTrgType=~/^DLL$/o) {
	            # get the right object file for the entry point
	            my $ObjFile = "UC_DLL_.o";
	            if ($FirstLib =~ /EDEV/i) {
		            $ObjFile = "D_ENTRY_.o";
	            }
	            if ($FirstLib =~ /EKLL/i) {
		            $ObjFile = "L_ENTRY_.o";
	            }
	            if ($FirstLib =~ /EEXT/i) {
		            $ObjFile = "X_ENTRY_.o";
	            }
	            if ($FirstLib =~ /EVAR/i) {
		            $ObjFile = "V_ENTRY_.o";
	            }

#		    If platform is a customization, take libs from parent directory.		   
		    if($IsCustomDll)
		    {
		    
			$linkCommand .= $oP.'entry _E32Dll "'.$FirstLib.'('.$ObjFile.')"'.
					' "${target.data}\\'.$tempFilenameRoot.'.exp"';
			    
		    }
		    else
		    {
		    # ARMV5 hardcoded here...
		    $linkCommand .= $oP.'entry _E32Dll "'.$FirstLib.'('.$ObjFile.')"';
		    }

	        } elsif ($BasicTrgType=~/^EXE$/o || $TrgType=~/^EXEXP$/o) {
			    # get the right object file for the entry point
			    my $ObjFile = "UC_EXE_.o" ;
			    if ($FirstLib =~ /KC_EXE/i) {
					$ObjFile = "K_ENTRY_.o";
			    }

		    # Should this use $ParentPlat rather than hardcoded ARMV5 dir?
		   $linkCommand .= $oP.'entry _E32Startup "'.$FirstLib.'('.$ObjFile.')"';
		
			}

			$linkCommand .= ' -o "${target.data}\\${output.file.name}"';

			if ($BasicTrgType=~/^DLL$/o) { # Add the DLL stub library
				if($IsPlatCustomization) 
				{
				if ($RVCTMajorVersion == 2 && $RVCTMinorVersion < 2) {
				   $linkCommand .= ' "${var:KIT_EPOCROOT}\\EPOC32\RELEASE\\'.$ParentPlat.'\\'.$Bld.'\EDLLSTUB'.$RVCTVersion.'.LIB"';
				}
				else {
				   $linkCommand .= ' "${var:KIT_EPOCROOT}\\EPOC32\RELEASE\\'.$ParentPlat.'\\'.$Bld.'\EDLLSTUB.LIB"';
				}
				}
				else
				{
				if ($RVCTMajorVersion == 2 && $RVCTMinorVersion < 2) {
				   $linkCommand .= ' "${var:KIT_EPOCROOT}\\EPOC32\RELEASE\ARMV5\\'.$Bld.'\EDLLSTUB'.$RVCTVersion.'.LIB"';
				}
				else {
				   $linkCommand .= ' "${var:KIT_EPOCROOT}\\EPOC32\RELEASE\ARMV5\\'.$Bld.'\EDLLSTUB.LIB"';
				}
				}
			}

			$linkCommand .= ' ${var:LIBS}';
			
			
			my $runtimeLibs = "";
			my $StaticRTLib = "usrt".$RVCTVersion;
			
			# use ksrt for system code and for user ARM code
			$StaticRTLib = "ksrt".$RVCTVersion if ($SystemTrg);
			$runtimeLibs .= $StaticRTLib.".lib" unless ($Trg =~ /(U|K)SRT/i || ($BasicTrgType=~/^LIB$/o));
		
			unless ($ArmRT || ($BasicTrgType=~/^LIB$/o)) {
				my $TargLib = "$ExportLibrary.lib";
				$TargLib =~ s/\{(\d|a|b|c|d|e|f){8}\}//i;
				unless ($SystemTrg) {
					foreach (@RTLibList) {
						$runtimeLibs .= " ".$_ unless ($_ =~ /$TargLib/i);
					}
			    }
			}

			foreach (@ArmLibList)
				{
				$runtimeLibs.= " ".$_;
				}
			
			addLinkDescriptorSymbol ('${var:RUNTIME_LIBS}', $runtimeLibs);
				
			$linkCommand .= ' --userlibpath "${var:KIT_EPOCROOT}\\EPOC32\RELEASE\ARMV5\\'.$Bld.'","${var:KIT_EPOCROOT}\\EPOC32\RELEASE\ARMV5\LIB" '.$oP.'via "${target.data}\\${output.file.root}_'.$Bld.'_objects.via" ${var:RUNTIME_LIBS} '.$librarylist.' ';
			
			addLinkDescriptorCommand ($linkCommand, undef, undef, undef, $FirstLib);

		}
		
		#### Create the .sym file
		$linkCommand = 'cmd.exe /C copy "${target.data}\\${output.file.name}" "${output}\\${output.file.root}.sym"';
		addLinkDescriptorCommand ($linkCommand);
		
		#### copy the project .def file for prep
		if ($DefFile and !$NoExportLibrary)
		{
			unless (&main::ExportUnfrozen)
			{
        	$linkCommand = 'cmd.exe /C copy "${var:DEF_FILE}" "${target.data}\\${var:IMPORT_LIBRARY_NO_EXT}.prep.def"';
			addLinkDescriptorCommand ($linkCommand);
			}
		}
		
		
		#### ELF2E32 POST-LINK COMMAND ####
		# Section needs to be generic for BPABI (e.g. GCCE & ARMV5_ABIV2)
		$linkCommand  = '${var:KIT_EPOCROOT}\\epoc32\\tools\\elf2e32.exe ';		
		
		# Change - exexps are allowed data, but they look like dlls to elftran....
		if (&main::AllowDllData || $TrgType=~/^EXEXP$/o || $TrgType=~/^EXEDLL$/o) {
			$linkCommand .= ' --dlldata';
		}

		if (&main::DataLinkAddress) {
			$linkCommand .= ' --datalinkaddress=',&main::DataLinkAddress;
		}
		if (&main::FixedProcess) {
			$linkCommand .=	' --fixedaddress';
		}
		
		$linkCommand .= ' --sid='.&main::SecureId();
		$linkCommand .= ' --version='. &Genutl_VersionToUserString(%Version);
		
		if (&main::HeapSize) {
			my %HeapSize=&main::HeapSize;
			$linkCommand.=' --heap '.$HeapSize{Min} .','.$HeapSize{Max};
		}
		
		if (&main::ProcessPriority) {
			$linkCommand .=	' --priority='.&main::ProcessPriority;
		}
		
		if (&main::StackSize) {
			$linkCommand .= ' --stack='.&main::StackSize;
		}
		
		my $i=1;
		foreach (@UidList) {
			$linkCommand .= " --uid$i=$_";
			$i++;
		}
		if (&main::VendorId) {
			$linkCommand .= ' --vid='.&main::VendorId;
		}
		
		$linkCommand .= ' --capability='.&main::Capability;

		# ARMFPU only currently supported for RVCT BPABI builds
		if (&main::ARMFPU && (&main::ARMFPU =~ /^VFPV2$/i)) {
			$linkCommand .= ' --fpu=vfpv2'
		}
		else {
			$linkCommand .= ' --fpu=softvfp'
		}
		
	
		if(($BasicTrgType=~/^DLL/ && $TrgType!~/^DLL/ ) || $TrgType=~/^EXEXP/) {
	 		$linkCommand .= ' --targettype='.$TrgType;
 		}
 		else {
 			$linkCommand .= ' --targettype='.$BasicTrgType;
 		}
		
		$linkCommand .= ' --output="${output}\\${output.file.name}"';
		
		my $warningMessage;
		if ($BasicTrgType=~/^DLL$/o || $TrgType=~/^EXEXP$/o || $TrgType=~/^EXEDLL$/o) {
			if ($DefFile and !$NoExportLibrary) {
				$linkCommand .= ' --definput="${target.data}\\${var:IMPORT_LIBRARY_NO_EXT}.prep.def"';
			}
			$linkCommand .= ' --dso=';
			$linkCommand .= '"${var:KIT_EPOCROOT}\\EPOC32\\RELEASE\\ARMV5\\LIB\\${var:IMPORT_LIBRARY}"';
				
			$linkCommand .= ' --defoutput=';
			$linkCommand .= '"${target.data}\\${var:IMPORT_LIBRARY_NO_EXT}.def"';
				
			if (&main::ExportUnfrozen) {
				$warningMessage = $exportUnfrozenWarningMessage;
				$linkCommand .= ' --unfrozen';
			}
		}
		
		$linkCommand .= ' --elfinput="${target.data}\\${output.file.name}"';
		$linkCommand .= ' --linkas='.$LinkAs;
		#Change - LIB path is hardcoded here...
		$linkCommand .= ' --libpath="${var:KIT_EPOCROOT}\\EPOC32\\RELEASE\\ARMV5\\LIB"';
		
        if ($BasicTrgType=~/^DLL$/o && $TrgType!~/^DLL/){
			my $Export;
			my $Ordinal=1;
			foreach $Export (&main::Exports)
				{
				if ($Ordinal eq 1) {
					$linkCommand .= ' --sysdef=';
				}
				elsif ($Ordinal ne 1) {
					$linkCommand .= ';';
					}
					
				$linkCommand .= "$Export,".$Ordinal;
				$Ordinal++;
				}
		}
		
		addLinkDescriptorCommand ($linkCommand, undef, undef, undef, undef, $warningMessage);
		
		}
		
		# copy def file output
		if ($BasicTrgType=~/^DLL$/o || $TrgType=~/^EXEXP$/o || $TrgType=~/^EXEDLL$/o) {
			$linkCommand = 'cmd.exe /C copy  "${target.data}\\${var:IMPORT_LIBRARY_NO_EXT}.def" "${Project}\${var:IMPORT_LIBRARY_NO_EXT}.def"';
			addLinkDescriptorCommand($linkCommand);
		}
		
		# copy the import lib (dso) created
		if ($BasicTrgType=~/^DLL$/o || $TrgType=~/^EXEXP$/o || $TrgType=~/^EXEDLL$/o) {
			if ($DefFile and !$NoExportLibrary) {
				$linkCommand = 'cmd.exe /C copy ${var:KIT_EPOCROOT}\\EPOC32\\RELEASE\\ARMV5\\LIB\\${var:IMPORT_LIBRARY} ${var:KIT_EPOCROOT}\\EPOC32\\RELEASE\\ARMV5\\LIB\\${output.file.root}.dso';
			}
			addLinkDescriptorCommand($linkCommand);
		}
	
	}
			
#########################################
# GCC library generation and link stage #
#########################################

	elsif ($Plat eq "ARM4")
		{
		# Generate library

		if ($DefFile and !$NoExportLibrary)
			{
			unless (&main::ExportUnfrozen)
				{
				$linkCommand = 'perl.exe -S prepdef.pl "${var:DEF_FILE}" "${target.data}\\'.$tempFilenameRoot.'.prep.def"';
				addLinkDescriptorCommand ($linkCommand);

				$linkCommand = "$Dlltool $PlatOpt{Dlltool}".' --output-lib "${var:KIT_EPOCROOT}'.kitRelativePath($LibPath).'${var:IMPORT_LIBRARY}" --def "'.
						'${target.data}\\'.$tempFilenameRoot.'.prep.def" --dllname "'.$LinkAs.'"';
				addLinkDescriptorCommand ($linkCommand);

				if ($ExtraExportLibrary)
					{
					$linkCommand = $copyCommand.'"${var:KIT_EPOCROOT}'.kitRelativePath($LibPath).'${var:IMPORT_LIBRARY}" '.
									'"${var:KIT_EPOCROOT}'.kitRelativePath($LibPath).$ExtraExportLibrary.'.lib"';
					addLinkDescriptorCommand ($linkCommand,"false", "false");
					}

				foreach (@CompatibleABIs)
					{
					$linkCommand = "$Dlltool $ABIDlltool{$_}".' --output-lib "${var:KIT_EPOCROOT}'.kitRelativePath($ABILibPath{$_}).'UREL\\${var:IMPORT_LIBRARY}" --def "'.
							'${target.data}\\'.$tempFilenameRoot.'.prep.def" --dllname "'.$LinkAs.'"';
					addLinkDescriptorCommand ($linkCommand);

					if ($ExtraExportLibrary)
						{
						$linkCommand = $copyCommand.'"${var:KIT_EPOCROOT}'.kitRelativePath($ABILibPath{$_}).'UREL\\${var:IMPORT_LIBRARY}" '.
										'"${var:KIT_EPOCROOT}'.kitRelativePath($ABILibPath{$_}).'UREL\\'.$ExtraExportLibrary.'.lib"';
						addLinkDescriptorCommand ($linkCommand,"false", "false");
						}
					}
				}
			}


		# TARGET *.IN
		#------------
		$linkCommand = $deleteCommand.'"${target.data}\\${output.file.root}.in"';
		addLinkDescriptorCommand ($linkCommand, "false", "false", undef, undef, undef, undef, (0,1));
				
		$linkCommand = 'ar.exe cr "${target.data}\\${output.file.root}.in" ${var:LINK_OBJS}';
		addLinkDescriptorCommand ($linkCommand);
		

		# Perform Link

	#	Establish the entry point symbol
		my $EntrySymbol;
		if ($BasicTrgType=~/^DLL$/o) {
			$EntrySymbol = '_E32Dll';
		}
		elsif ($BasicTrgType=~/^EXE$/o) {
			$EntrySymbol = '_E32Startup';
		}
		my $AbsentSubst = '';
		if ($EntrySymbol) {
			$AbsentSubst = " -absent $EntrySymbol";
		}

		if ($BasicTrgType=~/^DLL$/o || $TrgType=~/^EXEXP$/o || $TrgType=~/^EXEDLL$/o) {

	#		generate a .DEF file from the objects and static libraries
			$linkCommand = "$Dlltool $PlatOpt{Dlltool} --output-def ".'"${target.data}\\'.$tempFilenameRoot.'.inf" "${target.data}\\${output.file.root}.in"';

			foreach (@StatLibList) {
				$linkCommand .= ' "${var:KIT_EPOCROOT}'.kitRelativePath($StatLinkPath).$_.'"';
			}

			addLinkDescriptorCommand ($linkCommand);

	#		reorder the .DEF file taking frozen exports into account if there are any
	#			call perl on the script here so nmake will die if there are errors - this doesn't happen if calling perl in a batch file
			$linkCommand = 'perl.exe -S makedef.pl -Deffile "${target.data}\\'.$tempFilenameRoot.'.inf" '.$AbsentSubst;
			if (-e $DefFile) { # effectively "if project frozen ..."
				$linkCommand .= " -Frzfile \"".'${var:DEF_FILE}'."\"";
			}
			# freeze ordinals, a maximum of 2, for polymorphic dlls
			my $Ordinal;
			my $Num=1;
			foreach $Ordinal (&main::Exports) {
				$linkCommand .= " -$Num $Ordinal";
				$Num++;
			}
			$linkCommand .= ' "${target.data}\\'.$tempFilenameRoot.'.def"';
			addLinkDescriptorCommand ($linkCommand);

	#		delete the unordered definition file
			$linkCommand = $deleteCommand.'"${target.data}\\'.$tempFilenameRoot.'.inf"';
			addLinkDescriptorCommand ($linkCommand, "false", "false");

	#		generate an export object from the ordered .DEF file
			$linkCommand = "$Dlltool $PlatOpt{Dlltool} --def".' "${target.data}\\'.$tempFilenameRoot.'.def"'.
				' --output-exp "${target.data}\\'.$tempFilenameRoot.'.exp"'.
				" --dllname \"$LinkAs\"";

			my $warningMessage;
				
			if (&main::ExportUnfrozen) {
				$warningMessage = $exportUnfrozenWarningMessage;
				$linkCommand .= ' --output-lib "${var:KIT_EPOCROOT}'.kitRelativePath($LibPath).'${var:IMPORT_LIBRARY}"';
			}
			
			addLinkDescriptorCommand ($linkCommand, undef, undef, undef, undef, $warningMessage);
		}

	#	call ld to do base relocations (and dll exports)
		if ($BasicTrgType=~/^(DLL|EXE)/o) {
			$linkCommand = "$Link $PlatOpt{Ld} -s";	
			if ($BasicTrgType=~/^DLL$/o) {
				$linkCommand .= " $PlatOpt{Entry} $EntrySymbol -u $EntrySymbol ".'"${target.data}\\'.$tempFilenameRoot.'.exp" --dll ';
			}
			elsif ($BasicTrgType=~/^EXE$/o) {
				$linkCommand .= " $PlatOpt{Entry} $EntrySymbol -u $EntrySymbol ";
			}
	#		--whole-archive is required here apparently because of a defect in the gcc toolchain
	#		the flag can probably be removed with a later version of gcc

			$linkCommand .= '--base-file "${target.data}\\${output.file.root}.bas" -o "${target.data}\\${output.file.name}" '.
				'${var:FIRST_LIB} --whole-archive "${target.data}\\${output.file.root}.in" '.
				"--no-whole-archive";
			$linkCommand .= ' ${var:COMMON_LINK_FLAGS} ${var:LIBS}';
			addLinkDescriptorCommand ($linkCommand, undef, undef, undef, $FirstLib);

	#		delete temporary files
			if ($BasicTrgType=~/^DLL$/o || $TrgType=~/^EXEXP$/o || $TrgType=~/^EXEDLL$/o) {
				$linkCommand = $deleteCommand.'"${target.data}\\'.$tempFilenameRoot.'.exp"';
				addLinkDescriptorCommand ($linkCommand, "false", "false", undef, undef, undef, undef, (0,1));
			}
			$linkCommand = $deleteCommand.'"${target.data}\\${output.file.name}"';
			addLinkDescriptorCommand ($linkCommand, "false", "false");

	#		call dlltool to do base relocations (and dll exports)
			$linkCommand = "$Dlltool $PlatOpt{Dlltool} ";
			if ($BasicTrgType=~/^DLL$/o || $TrgType=~/^EXEXP$/o || $TrgType=~/^EXEDLL$/o) {
				$linkCommand .= '--def "${target.data}\\'.$tempFilenameRoot.'.def" '.
					"--dllname \"$LinkAs\" ";
			}
			$linkCommand .= '--base-file "${target.data}\\${output.file.root}.bas" '.
				'--output-exp "${target.data}\\'.$tempFilenameRoot.'.exp" ';
			addLinkDescriptorCommand ($linkCommand);

	#		delete temporary files
			$linkCommand = $deleteCommand.'"${target.data}\\${output.file.root}.bas"';
			addLinkDescriptorCommand ($linkCommand, "false", "false");

	#		call ld to link the target
			$linkCommand = "$Link $PlatOpt{Ld}";
			if ($Bld=~/^U?REL$/o) {
				$linkCommand .= " -s";
			}
			if ($BasicTrgType=~/^DLL$/o) {
				$linkCommand .= " $PlatOpt{Entry} $EntrySymbol -u $EntrySymbol --dll ";
			}
			elsif ($BasicTrgType=~/^EXE$/o) {
				$linkCommand .= " $PlatOpt{Entry} $EntrySymbol -u $EntrySymbol ";
			}
	#		--whole-archive is required here apparently because of a defect in the gcc toolchain
	#		the flag can probably be removed with a later version of gcc
			$linkCommand .= '"${target.data}\\'.$tempFilenameRoot.'.exp" '.
				'-Map "${output}\\${output.file.name}.map" -o "${target.data}\\${output.file.name}" '.
				'${var:FIRST_LIB} --whole-archive "${target.data}\\${output.file.root}.in" '.
				"--no-whole-archive";
			$linkCommand .= ' ${var:LIBS}';

			if ($BasicTrgType=~/^LIB$/o) {				
			addLinkDescriptorCommand ($linkCommand, undef, undef, undef, undef);
			}
			else {
			addLinkDescriptorCommand ($linkCommand, undef, undef, undef, $FirstLib);
			}

	#		delete temporary files
			$linkCommand = $deleteCommand.'"${target.data}\\'.$tempFilenameRoot.'.exp"';
			addLinkDescriptorCommand ($linkCommand, "false", "false");

			if ($Bld=~/DEB$/o) {
				$linkCommand = $Objcopy.' -X "${target.data}\\${output.file.name}" "${output}\\${output.file.root}.sym"';
				addLinkDescriptorCommand ($linkCommand);
			}

			$linkCommand = "petran.exe $PlatOpt{Petran} -version ". &Genutl_VersionToUserString(%Version). " -sid ". &main::SecureId(). ' "${target.data}\\${output.file.name}" "${output}\\${output.file.name}" ';

			if (&main::CompressTarget) {
				$linkCommand .= ' -nocompress';
			}
			if (&main::AllowDllData) {
				$linkCommand .= ' -allow';
			}
			if (not &main::CallDllEntryPoints) {
				$linkCommand .= ' -nocall';
			}
			if (&main::DataLinkAddress) {
				$linkCommand .= ' -datalinkaddress '.&main::DataLinkAddress;
			}
			if (&main::FixedProcess) {
				$linkCommand .= ' -fixed';
			}
			if (&main::HeapSize) {
				my %HeapSize=&main::HeapSize;
				$linkCommand .= ' -heap '.$HeapSize{Min}.' '.$HeapSize{Max};
			}
			if (&main::ProcessPriority) {
				$linkCommand .= ' -priority '.&main::ProcessPriority;
			}
			if (&main::StackSize) {
				$linkCommand .= ' -stack '.&main::StackSize;
			}
			my $i=1;
			foreach (@UidList) {
				$linkCommand .= " -uid$i $_";
				$i++;
			}
				
			$linkCommand .= ' -capability '.&main::Capability;
			
			if (&main::VendorId) {
			$linkCommand .= ' -vid '.&main::VendorId;
			}
		
			addLinkDescriptorCommand ($linkCommand, "false");
			
			$linkCommand = $deleteCommand.'"${target.data}\\${output.file.name}"';
			addLinkDescriptorCommand ($linkCommand, "false", "false");
		}
		elsif ($BasicTrgType=~/^LIB$/o) {
			$linkCommand = $copyCommand.'"${target.data}\\${output.file.root}.in" "${var:KIT_EPOCROOT}'.kitRelativePath($StatLinkPath).'${output.file.name}"';
			addLinkDescriptorCommand ($linkCommand,"false", "false");
		}
	}
	
###############################################
# GCCE library generation and link stage      #
# GCCE only supported for CW 3.1 and greater #
###############################################

	elsif ($Plat eq "GCCE" || $CustGCCE)
	{	
	
        if ($BasicTrgType=~/^LIB$/o) {
	        $linkCommand = 'ar cr ${output}\\${output.file.name} ${var:LINK_OBJS} ${var:LIBS}';
	        addLinkDescriptorCommand ($linkCommand);
         }
         
		elsif ($BasicTrgType=~/^(DLL|EXE)/o) {
        
        $linkCommand = 'arm-none-symbianelf-ld';
        my $GCCE_LibGCCPath = ' -L';
        $GCCE_LibGCCPath .= '"'.GetGCCELibPath("-print-file-name=libsupc++.a").'"';
        $GCCE_LibGCCPath .= ' -L';
        $GCCE_LibGCCPath .= '"'.GetGCCELibPath("-print-libgcc-file-name").'"';
		$linkCommand .= $GCCE_LibGCCPath;
		$linkCommand .=	' ${var:COMMON_LINK_FLAGS}';
		$linkCommand .= ' --target1-abs --no-undefined -nostdlib -Ttext 0x8000 -Tdata 0x400000';
		$linkCommand .= ' -shared --default-symver -soname '.$LinkAs." ";
		
		if ($Bld=~/REL$/o) {
			$linkCommand .= ' -Map "${output}\\${output.file.name}.map"';
		}
		
		if ($BasicTrgType=~/^DLL$/o)
			{
			$linkCommand .= ' --entry _E32Dll -u _E32Dll';
			}
		elsif ($BasicTrgType=~/^EXE$/o)
			{
			$linkCommand .= ' --entry _E32Startup -u _E32Startup';
			}		

		$linkCommand .= ' ${var:FIRST_LIB}';
		$linkCommand .= ' -o "${target.data}\\${output.file.name}" ${var:LINK_OBJS} ${var:LIBS}';
		$linkCommand .= ' -lsupc++ -lgcc'.' '.$linkeropts; 
		addLinkDescriptorCommand ($linkCommand, undef, undef, undef, $FirstLib);
		
		
		$linkCommand = 'cmd.exe /C copy "${target.data}\\${output.file.name}" "${output}\\${output.file.root}.sym"';
		addLinkDescriptorCommand ($linkCommand);
		
		# copy the project .def file for prep
		if ($DefFile and !$NoExportLibrary)
		{
			unless (&main::ExportUnfrozen)
			{
        	$linkCommand = 'cmd.exe /C copy "${var:DEF_FILE}" "${target.data}\\${var:IMPORT_LIBRARY_NO_EXT}.prep.def"';
			addLinkDescriptorCommand ($linkCommand);
			}
		}
		
		$linkCommand  = '${var:KIT_EPOCROOT}\\epoc32\\tools\\elf2e32.exe ';		
		
		# Change - exexps are allowed data, but they look like dlls to elftran....
		if (&main::AllowDllData || $TrgType=~/^EXEXP$/o || $TrgType=~/^EXEDLL$/o) {
			$linkCommand .= ' --dlldata';
		}

		if (&main::DataLinkAddress) {
			$linkCommand .= ' --datalinkaddress=',&main::DataLinkAddress;
		}
		if (&main::FixedProcess) {
			$linkCommand .=	' --fixedaddress';
		}
		
		$linkCommand .= ' --sid='.&main::SecureId();
		
		if (&main::HeapSize) {
			my %HeapSize=&main::HeapSize;
			$linkCommand.=' --heap '.$HeapSize{Min} .','.$HeapSize{Max};
		}
		
		if (&main::ProcessPriority) {
			$linkCommand .=	' --priority='.&main::ProcessPriority;
		}
		
		if (&main::StackSize) {
			$linkCommand .= ' --stack='.&main::StackSize;
		}
		
		my $i=1;
		foreach (@UidList) {
			$linkCommand .= " --uid$i=$_";
			$i++;
		}
		if (&main::VendorId) {
			$linkCommand .= ' --vid='.&main::VendorId;
		}
		
		$linkCommand .= ' --capability='.&main::Capability;
		
	
		if(($BasicTrgType=~/^DLL/ && $TrgType!~/^DLL/ ) || $TrgType=~/^EXEXP/) {
	 		$linkCommand .= ' --targettype='.$TrgType;
 		}
 		else {
 			$linkCommand .= ' --targettype='.$BasicTrgType;
 		}
		
		$linkCommand .= ' --output="${output}\\${output.file.name}"';
		
		my $warningMessage;
		if ($BasicTrgType=~/^DLL$/o || $TrgType=~/^EXEXP$/o || $TrgType=~/^EXEDLL$/o) {
			if ($DefFile and !$NoExportLibrary) {
				$linkCommand .= ' --definput="${target.data}\\${var:IMPORT_LIBRARY_NO_EXT}.prep.def"';
			}
			$linkCommand .= ' --dso=';
			$linkCommand .= '"${var:KIT_EPOCROOT}\\EPOC32\\RELEASE\\ARMV5\\LIB\\${var:IMPORT_LIBRARY}"';
				
			$linkCommand .= ' --defoutput=';
			$linkCommand .= '"${target.data}\\${var:IMPORT_LIBRARY_NO_EXT}.def"';
				
			if (&main::ExportUnfrozen) {
				$warningMessage = $exportUnfrozenWarningMessage;
				$linkCommand .= ' --unfrozen';
			}
		}
		
		$linkCommand .= ' --elfinput="${target.data}\\${output.file.name}"';
		$linkCommand .= ' --linkas='.$LinkAs;
		#Change - LIB path is hardcoded here...
		$linkCommand .= ' --libpath="${var:KIT_EPOCROOT}\\EPOC32\\RELEASE\\ARMV5\\LIB"';
		
        if ($BasicTrgType=~/^DLL$/o && $TrgType!~/^DLL/){
			my $Export;
			my $Ordinal=1;
			foreach $Export (&main::Exports)
				{
				if ($Ordinal eq 1) {
					$linkCommand .= ' --sysdef=';
				}
				elsif ($Ordinal ne 1) {
					$linkCommand .= ';';
					}
					
				$linkCommand .= "$Export,".$Ordinal;
				$Ordinal++;
				}
		}
		
		addLinkDescriptorCommand ($linkCommand, undef, undef, undef, undef, $warningMessage);
				
		} # end...elsif if ($BasicTrgType=~/^(DLL|EXE)/o)
		
		# copy def file output
		if ($BasicTrgType=~/^DLL$/o || $TrgType=~/^EXEXP$/o || $TrgType=~/^EXEDLL$/o) {
			$linkCommand = 'cmd.exe /C copy  "${target.data}\\${var:IMPORT_LIBRARY_NO_EXT}.def" "${Project}\${var:IMPORT_LIBRARY_NO_EXT}.def"';
			addLinkDescriptorCommand($linkCommand);
		}
		
		# copy the import lib (dso) created
		if ($BasicTrgType=~/^DLL$/o || $TrgType=~/^EXEXP$/o || $TrgType=~/^EXEDLL$/o) {
			if ($DefFile and !$NoExportLibrary) {
				$linkCommand = 'cmd.exe /C copy ${var:KIT_EPOCROOT}\\EPOC32\\RELEASE\\ARMV5\\LIB\\${var:IMPORT_LIBRARY} ${var:KIT_EPOCROOT}\\EPOC32\\RELEASE\\ARMV5\\LIB\\${output.file.root}.dso';
			}
			addLinkDescriptorCommand($linkCommand);
		}
		
	
	} # end  GCCE link stage... elsif ($Plat eq "GCCE")
	
	
	if ($addHeaders)
		{
		# Ideally we would do this for all targets, both UREL and UDEB.  This would,
		# however, be very slow - so we just do it for the first target we come to.
		my $cpp = &PreprocessorToUseExe();
		my $cppCommandLine = "$cpp.EXE -M -MG -nostdinc ";
		my $preInclude = "";

		if (($Plat eq "ARMV5" || $Plat eq "ARMV5_ABIV2" || $Plat eq "ARMV5_ABIV1" || $Plat eq "GCCE" || $IsPlatCustomization) && $PrefixFile)
			{
			$preInclude = $PrefixFile;
			$preInclude =~ s/^.*://;
			}
		elsif($VariantFile)
			{	    
		    $preInclude = $VariantFile;
			}

		$cppCommandLine .= '-include '.Path_RltToWork($preInclude).' ' if $preInclude;

		foreach (&main::UserIncPaths, &main::SysIncPaths)
			{
			$cppCommandLine .= '-I '.&Path_Chop(&Path_RltToWork($_)).' ';
			}

		foreach (@MacroList)
			{
			#ARMCC requires escaped '"', but CPP doesn't like them
			s/\\\"/\"/g if /^__PRODUCT_INCLUDE__/; 
			
			$cppCommandLine .= '-D'.$_.' ';
			}
		
		my $SourceStructRef=&main::SourceStructRef;
		my %localIncludes;

		foreach my $SourceRef (@$SourceStructRef)
	   		{
			$file = Path_RltToWork(($$SourceRef{SrcPath}.$$SourceRef{CurFile}));

			# ensure the case of the extension is what GCC expects
			$file =~ s/\.CPP$/.cpp/i;
			$file =~ s/\.C$/.c/i;
			$file =~ s/\.s$/.S/i;

			open CPPPIPE,$cppCommandLine.$file." |" or die "ERROR: Can't invoke CPP.EXE\n";

			while (<CPPPIPE>)
				{
				#convert any Unix slashes found in CPP output to DOS slashes
				s/\//\\/g;	
				while (/\.(\.\\|\\){1}\S+\.(hrh|h|inl){1}/gi)
					{
					my $file = $&;
					my $filePath = &main::Path_Split('Path',$file);
						
					# Ignore files that are clearly not local to the project
					next if ($filePath =~ /\\epoc32\\/i);

					# Ignore files that are #included with intermediate directories -
					# we can't guarantee these will be on an Access Path.
					next if ($filePath =~ /\w+\\\.\./);

					# Finally confirm that the file we have is definitely on an Access Path
					my $presentOnAccessPath = 0;
					foreach my $accessPath (&main::UserIncPaths, &main::SysIncPaths)
						{
						my $accessPathCompare = $accessPath;
						$accessPathCompare =~ s/\\/_/g;

						my $filePathCompare = $filePath;
						$filePathCompare =~ s/(\.\\|\.\.\\)//g;
						$filePathCompare =~ s/\\/_/g;

						$presentOnAccessPath = 1 if ($accessPathCompare =~ /$filePathCompare$/i);
						}
					next if (!$presentOnAccessPath);

					# Maintain availability of original case of filename using a lc keyed hash
					my $localInclude = &main::Path_Split('Base',$file).&main::Path_Split('Ext',$file);					
					$localIncludes{lc ($localInclude)} = $localInclude unless (!-e $file);
					}
				}
			}

		foreach my $localInclude (sort keys %localIncludes)
			{
			addFile($localIncludes{$localInclude}, "Text", 0, "", "Headers");
			}

		$addHeaders = 0;
		}


	# Add DOCUMENT specified files that we know we can add - we only add these for one target,
	# as they should be identical through-out

	if ($addDocuments)
		{
		foreach my $document (@DocList)
			{
			# Only add files as Documents if they haven't already been added to
			# the target (it's not possible to have duplicate entries) and if they
			# have an extension we know about.

			next if (grep (/$document/i, @addedFiles));

			my $extension = $document;
			$extension =~ s/^.*\.//;

			next if (!grep (/$extension/i, @compatibleDOCUMENTExtensions));
			
			addFile($document, "Text", "", "", "Documents");
			}

		$addDocuments = 0;
		}


	# Create the link descriptor file

	$xmlLinkDescriptorCommandParent->addText("\n\t\t\t");

	if ($CW_major_version >= 3)
		{
		$xmlLinkDescriptorDumpFileParent->addText("\n\t\t\t");
		}

	&main::CreateExtraFile("${ExtraFilesPath}$linkDescriptorFile", $xmlLinkDescriptorDoc->toString);
	addFile($linkDescriptorFile, "Text", "", "", "Link");	
	
	# Apply the changed settings

	my $settinglist = $xmlTarget->getElementsByTagName("SETTINGLIST",0)->item(0);
	my @settingnodes = $settinglist->getElementsByTagName("SETTING",0);
	foreach my $setting (@settingnodes)
		{
		my $element = $setting->getElementsByTagName("NAME",0)->item(0);
		my $settingname = $element->getFirstChild->getData();
		my $replacement = $changedsettings{$settingname};
		if (defined $replacement) 
			{
			if ($replacement eq "{}")
				{
				if ($settingname eq "UserSearchPaths")
					{
					&addUserSearchPaths($setting);
					}
				elsif ($settingname eq "SystemSearchPaths")
					{
					&addSystemSearchPaths($setting);
					}
				elsif ($settingname eq "UserSourceTrees")
					{
					&addSourceTrees($setting);
					}
				elsif ($settingname eq "FileList")
					{
					&addDownloadFileList($setting, @ResourceDownloadList);
					}
				}
			elsif ($replacement =~ /^{(.+)}(.+)$/)
				{					
				&changePathSetting($setting,$1,$2);
				}
			else
				{
				&changeValue($setting,$replacement);
				}
			}
		}
	}


sub addLinkDescriptorCommand($$;$;$;$;$;$;$;) {		
	my ($linkCommand, $parseStdOut, $parseStdErr, $outputParser, $firstLibProcessing,
	    $linkWarning, $linkInformation, @successCodes) = @_;

	my $structIndent = "\n\t\t\t\t";
	my $settingIndent = "$structIndent\t";
	my $simpleIndent = "$settingIndent\t";
	my $successCodeArrayIndent = $simpleIndent;
	my $successCodeSimpleIndent = "$successCodeArrayIndent\t";

	my $structElement = new XML::DOM::Element($xmlLinkDescriptorDoc,"struct");
	$xmlLinkDescriptorCommandParent->addText("$structIndent");
	$xmlLinkDescriptorCommandParent->appendChild ($structElement);

	my $settingElementTemplate = new XML::DOM::Element($xmlLinkDescriptorDoc,"setting");
	$settingElementTemplate->setAttribute("uuid-alias", ".");
	my $simpleElementTemplate = new XML::DOM::Element($xmlLinkDescriptorDoc,"simple");

	my $settingElement;
	my $simpleElement;

	$settingElement = $settingElementTemplate->cloneNode(0);
	$simpleElement = $simpleElementTemplate->cloneNode(0);
	
	$settingElement->setAttribute ("entry", "linkCommand");
	$structElement->addText ($settingIndent);
	$structElement->appendChild ($settingElement);

	$simpleElement->addText ($linkCommand);

	$settingElement->addText ($simpleIndent);
	$settingElement->appendChild ($simpleElement);
	$settingElement->addText($settingIndent);
	
	if (defined $parseStdOut)
		{
		$settingElement = $settingElementTemplate->cloneNode(0);
		$simpleElement = $simpleElementTemplate->cloneNode(0);

		$settingElement->setAttribute("entry", "parseStdOut");
		$simpleElement->addText($parseStdOut);

		$structElement->addText ($settingIndent);
		$structElement->appendChild ($settingElement);

		$settingElement->addText ($simpleIndent);
		$settingElement->appendChild ($simpleElement);
		$settingElement->addText($settingIndent);
		}
	
	if (defined $parseStdErr)
		{
		$settingElement = $settingElementTemplate->cloneNode(0);
		$simpleElement = $simpleElementTemplate->cloneNode(0);

		$settingElement->setAttribute("entry", "parseStdErr");
		$simpleElement->addText($parseStdErr);

		$structElement->addText ($settingIndent);
		$structElement->appendChild ($settingElement);

		$settingElement->addText ($simpleIndent);
		$settingElement->appendChild ($simpleElement);
		$settingElement->addText($settingIndent);
		}

	if (defined $outputParser)
		{
		$settingElement = $settingElementTemplate->cloneNode(0);
		$simpleElement = $simpleElementTemplate->cloneNode(0);

		$settingElement->setAttribute("entry", "outputParser");
		$simpleElement->addText($outputParser);

		$structElement->addText ($settingIndent);
		$structElement->appendChild ($settingElement);

		$settingElement->addText ($simpleIndent);
		$settingElement->appendChild ($simpleElement);
		$settingElement->addText($settingIndent);
		}

	if (defined $firstLibProcessing)
		{
		$settingElement = $settingElementTemplate->cloneNode(0);
		$simpleElement = $simpleElementTemplate->cloneNode(0);

		$settingElement->setAttribute("entry", "firstLibProcessing");
		$simpleElement->addText($firstLibProcessing);

		$structElement->addText ($settingIndent);
		$structElement->appendChild ($settingElement);

		$settingElement->addText ($simpleIndent);
		$settingElement->appendChild ($simpleElement);
		$settingElement->addText($settingIndent);
		}

	if (defined $linkWarning)
		{
		$settingElement = $settingElementTemplate->cloneNode(0);
		$simpleElement = $simpleElementTemplate->cloneNode(0);

		$settingElement->setAttribute("entry", "linkWarning");
		$simpleElement->addText($linkWarning);

		$structElement->addText ($settingIndent);
		$structElement->appendChild ($settingElement);

		$settingElement->addText ($simpleIndent);
		$settingElement->appendChild ($simpleElement);
		$settingElement->addText($settingIndent);
		}

	if (defined $linkInformation)
		{
		$settingElement = $settingElementTemplate->cloneNode(0);
		$simpleElement = $simpleElementTemplate->cloneNode(0);

		$settingElement->setAttribute("entry", "linkInformation");
		$simpleElement->addText($linkInformation);

		$structElement->addText ($settingIndent);
		$structElement->appendChild ($settingElement);

		$settingElement->addText ($simpleIndent);
		$settingElement->appendChild ($simpleElement);
		$settingElement->addText($settingIndent);
		}

	if (@successCodes)
		{
		$settingElement = $settingElementTemplate->cloneNode(0);
		$settingElement->setAttribute("entry", "successCodes");

		my $arrayElement = new XML::DOM::Element($xmlLinkDescriptorDoc,"array");
		$arrayElement->setAttribute("inheritance", "none");
		
		foreach my $successCode (@successCodes)
			{
			$simpleElement = $simpleElementTemplate->cloneNode(0);
			$simpleElement->addText($successCode);
			$arrayElement->addText ($successCodeSimpleIndent);
			$arrayElement->appendChild ($simpleElement);
			}

		$arrayElement->addText ($successCodeArrayIndent);

		$settingElement->addText ($successCodeArrayIndent);
		$settingElement->appendChild ($arrayElement);
		$settingElement->addText($settingIndent);

		$structElement->addText ($settingIndent);
		$structElement->appendChild ($settingElement);
		}

	$structElement->addText($structIndent);
}


sub addLinkDescriptorSymbol ($$) {
	my ($symbolName, $symbolValue) = @_;
	
	my $structIndent = "\n\t\t\t\t";
	my $settingIndent = "$structIndent\t";
	my $simpleIndent = "$settingIndent\t";

	my $structElement = new XML::DOM::Element($xmlLinkDescriptorDoc,"struct");
	$xmlLinkDescriptorSymbolParent->addText("$structIndent");
	$xmlLinkDescriptorSymbolParent->appendChild ($structElement);

	my $settingElementTemplate = new XML::DOM::Element($xmlLinkDescriptorDoc,"setting");
	$settingElementTemplate->setAttribute("uuid-alias", ".");
	my $simpleElementTemplate = new XML::DOM::Element($xmlLinkDescriptorDoc,"simple");

	my $symbolNameSettingElement;
	my $symbolNameSimpleElement;
	my $symbolValueSettingElement;
	my $symbolValueSimpleElement;

	$symbolNameSettingElement = $settingElementTemplate->cloneNode(0);
	$symbolNameSimpleElement = $simpleElementTemplate->cloneNode(0);
	$symbolValueSettingElement = $settingElementTemplate->cloneNode(0);
	$symbolValueSimpleElement = $simpleElementTemplate->cloneNode(0);

	$symbolNameSettingElement->setAttribute("entry", "symbolName");
	$symbolNameSimpleElement->addText ($symbolName);
	$symbolValueSettingElement->setAttribute("entry", "symbolValue");
	$symbolValueSimpleElement->addText ($symbolValue);

	$symbolNameSettingElement->addText ($simpleIndent);
	$symbolNameSettingElement->appendChild ($symbolNameSimpleElement);
	$symbolNameSettingElement->addText($settingIndent);
	$symbolValueSettingElement->addText ($simpleIndent);
	$symbolValueSettingElement->appendChild ($symbolValueSimpleElement);
	$symbolValueSettingElement->addText ($settingIndent);

	$structElement->addText ($settingIndent);
	$structElement->appendChild ($symbolNameSettingElement);
	$structElement->addText ($settingIndent);
	$structElement->appendChild ($symbolValueSettingElement);
	$structElement->addText ($structIndent);
	}


sub addLinkDescriptorDumpFile ($$) {
	my ($dumpFileContent, $dumpFileName) = @_;
	
	my $structIndent = "\n\t\t\t\t";
	my $settingIndent = "$structIndent\t";
	my $simpleIndent = "$settingIndent\t";

	my $structElement = new XML::DOM::Element($xmlLinkDescriptorDoc,"struct");
	$xmlLinkDescriptorDumpFileParent->addText("$structIndent");
	$xmlLinkDescriptorDumpFileParent->appendChild ($structElement);

	my $settingElementTemplate = new XML::DOM::Element($xmlLinkDescriptorDoc,"setting");
	$settingElementTemplate->setAttribute("uuid-alias", ".");
	my $simpleElementTemplate = new XML::DOM::Element($xmlLinkDescriptorDoc,"simple");

	my $dumpFileContentSettingElement;
	my $dumpFileContentSimpleElement;
	my $dumpFileNameSettingElement;
	my $dumpFileNameSimpleElement;

	$dumpFileContentSettingElement = $settingElementTemplate->cloneNode(0);
	$dumpFileContentSimpleElement = $simpleElementTemplate->cloneNode(0);
	$dumpFileNameSettingElement = $settingElementTemplate->cloneNode(0);
	$dumpFileNameSimpleElement = $simpleElementTemplate->cloneNode(0);

	$dumpFileContentSettingElement->setAttribute("entry", "dumpFileContent");
	$dumpFileContentSimpleElement->addText ($dumpFileContent);
	$dumpFileNameSettingElement->setAttribute("entry", "dumpFileName");
	$dumpFileNameSimpleElement->addText ($dumpFileName);

	$dumpFileContentSettingElement->addText ($simpleIndent);
	$dumpFileContentSettingElement->appendChild ($dumpFileContentSimpleElement);
	$dumpFileContentSettingElement->addText($settingIndent);
	$dumpFileNameSettingElement->addText ($simpleIndent);
	$dumpFileNameSettingElement->appendChild ($dumpFileNameSimpleElement);
	$dumpFileNameSettingElement->addText ($settingIndent);

	$structElement->addText ($settingIndent);
	$structElement->appendChild ($dumpFileContentSettingElement);
	$structElement->addText ($settingIndent);
	$structElement->appendChild ($dumpFileNameSettingElement);
	$structElement->addText ($structIndent);
	}


sub ExtraPlat($) {

# Call PmBld again after reprocessing the MMP file and tweaking various main:: variables

	my ($Plat) = @_;
	
	&main::SetVarsFromMmp($Plat);
	&main::InitLinkPaths();

	foreach (&main::BldList) {
		&main::SetCurBld($_);
		&PMPlatProcessMmp(&main::PlatTxt2D);
		&PMStartBldList;
		&PMBld;
	}

}

sub disconnectNode($) {

# Remove a node from its parent, also removing the following text node (if any)
# The text node is assumed to contain whitespace for file formatting.
 
	my ($node)=@_;

	my $parent = $node->getParentNode;
	my $sibling = $node->getNextSibling;
	$parent->removeChild($node);
	if (defined $sibling && $sibling->getNodeType == TEXT_NODE)
		{
		$parent->removeChild($sibling);
		}
	return $node;
}

sub removeNode($) {

# disconnect the node and dispose of it

	my ($node) = @_;
	&disconnectNode($node);
	$node->dispose;		# setAttribute("disposed",1);
}

sub textElement($$$$) {
	my ($element,$name,$value,$insertionpoint)=@_;

	my $subElement = new XML::DOM::Element($xmlProjectDoc,$name);
	$subElement->appendChild($xmlProjectDoc->createTextNode($value));
	$element->insertBefore($subElement, $insertionpoint);
}

sub addFile($$$$;$) {    

	my ($src, $kind, $debug, $shared, $group) = @_;

	my $linkElement = new XML::DOM::Element($xmlProjectDoc,"FILEREF");

	&textElement($linkElement, "PATHTYPE",   "Name");
	&textElement($linkElement, "PATH",       $src);
	&textElement($linkElement, "PATHFORMAT", "Windows");

	my $fileElement = $linkElement->cloneNode(1);
	$fileElement->setTagName("FILE");
	&textElement($fileElement, "FILEKIND",   $kind);
	&textElement($fileElement, "FILEFLAGS",  "Debug") if ($debug);

	$xmlLinkOrder->appendChild($linkElement);
	$xmlFileList->appendChild($fileElement);

	$xmlLinkOrder->addText("\n");
	$xmlFileList->addText("\n");

	# Accumulate source group information

	my $groupfile = $linkElement->cloneNode(1);
	$groupfile->setAttribute("NAME", "$shared$src");			# convenience - remove this later!

	push (@addedFiles, $src) unless ($kind eq "Documents");

	if ($kind eq "Library")
		{
		$xmlLibGroup->appendChild($groupfile);
		$xmlLibGroup->addText("\n");
		}
	elsif (defined $group)
		{
		if ($group eq "Link")
			{
			$xmlLinkGroup->appendChild($groupfile);
			$xmlLinkGroup->addText("\n");
			}
		elsif ($group eq "Resources")
			{
			$xmlResourcesGroup->appendChild($groupfile);
			$xmlResourcesGroup->addText("\n");
			}
		elsif ($group eq "Root")
			{
			$xmlRootGroup->appendChild($groupfile);
			$xmlRootGroup->addText("\n");
			}
		elsif ($group eq "Headers")
			{
			$xmlHeadersGroup->appendChild($groupfile);
			$xmlHeadersGroup->addText("\n");
			}
		elsif ($group eq "Documents")
			{
			$xmlDocumentsGroup->appendChild($groupfile);
			$xmlDocumentsGroup->addText("\n");
			}
		}
	else
		{
		$xmlSourceGroup->appendChild($groupfile);
		$xmlSourceGroup->addText("\n");
		}
}

sub addGroup($$) {
	my ($grouplist,$name)=@_;

	my $group = new XML::DOM::Element($xmlProjectDoc,"GROUP");
	$grouplist->appendChild($group);
	$grouplist->addText("\n");

	&textElement($group, "NAME", $name);
	$group->addText("\n");
	return $group;
}

sub addSubTarget($$) {
	my ($subtargetlist,$name)=@_;

	my $subtarget = new XML::DOM::Element($xmlProjectDoc,"SUBTARGET");
	$subtargetlist->appendChild($subtarget);
	$subtargetlist->addText("\n");

	&textElement($subtarget, "TARGETNAME", $name);
}

sub addOrderedTarget($$) {
	my ($targetorder,$name)=@_;

	my $orderedtarget = new XML::DOM::Element($xmlProjectDoc,"ORDEREDTARGET");
	$targetorder->appendChild($orderedtarget);
	$targetorder->addText("\n");

	&textElement($orderedtarget, "NAME", $name);
}

sub finaliseProject {

	# Run through the project, removing all unused targets
	# and build up the TARGETORDER list and the "Build All" target

	my $target;
	my $targetname;

	my $xmlSubTargetList = new XML::DOM::Element($xmlProjectDoc,"SUBTARGETLIST");

	my $xmlTargetOrder = new XML::DOM::Element($xmlProjectDoc,"TARGETORDER");
	$xmlTargetOrder->addText("\n");

	my @targets = $xmlProjectDoc->getElementsByTagName("TARGET",1);
	my @emulatortargetnames;
	my @othertargetnames;
	
	foreach $target (@targets)
		{			
		$targetname = $target->getAttribute("NAME");

		if ($targetname eq "")
			{
			&removeNode($target);
			}
		else
			{
			$target->removeAttribute("NAME");

			if ($targetname =~ /^WINSCW/)
				{
				push (@emulatortargetnames, $targetname);
				}
			else
				{
				push (@othertargetnames, $targetname);
				}
			}
		}

	foreach $targetname ((sort @emulatortargetnames), (sort @othertargetnames))
		{
		&addSubTarget($xmlSubTargetList, $targetname);
		&addOrderedTarget($xmlTargetOrder, $targetname);
		}

	# Build the GROUPLIST
	
	my $xmlGroupList = new XML::DOM::Element($xmlProjectDoc,"GROUPLIST");

	# Build the "Root" group

	my %rootfiles;
	my @rootgroups = $xmlProjectDoc->getElementsByTagName("ROOTGROUP",1);
	foreach my $group (@rootgroups)
		{
		$targetname = $group->getAttribute("TARGET");

		my @files = $group->getElementsByTagName("FILEREF",0);
		foreach my $file (@files)
			{
			my $name = $file->getAttribute("NAME");
			if (!defined $rootfiles{$name})
				{
				# first occurrence - add to list
				$rootfiles{$name}=1;
				&textElement($file, "TARGETNAME", $targetname, $file->getFirstChild);
				$file->removeAttribute("NAME");
				&disconnectNode($file);					
				$xmlGroupList->appendChild($file);
				$xmlGroupList->addText("\n");
				}
			}
		&removeNode($group);
		}

	# Build the "Source" group

	my $xmlSourceGroup = &addGroup($xmlGroupList,"Source");
	my %sourcefiles;
	my @sourcegroups = $xmlProjectDoc->getElementsByTagName("SOURCEGROUP",1);
	foreach my $group (@sourcegroups)
		{
		$targetname = $group->getAttribute("TARGET");
		my @files = $group->getElementsByTagName("FILEREF",0);
		foreach my $file (@files)
			{
			my $name = $file->getAttribute("NAME");
			if (!defined $sourcefiles{$name})
				{
				# first occurrence - add to list
				$sourcefiles{$name}=1;
				&textElement($file, "TARGETNAME", $targetname, $file->getFirstChild);
				$file->removeAttribute("NAME");
				&disconnectNode($file);
				$xmlSourceGroup->appendChild($file);
				$xmlSourceGroup->addText("\n");
				}
			}
		&removeNode($group);
		}


	# Build the "Headers" group
			
	my $xmlHeadersGroup;
	my %headerfiles;
	my @headersgroups = $xmlProjectDoc->getElementsByTagName("HEADERSGROUP",1);
	foreach my $group (@headersgroups)
		{
		$targetname = $group->getAttribute("TARGET");
		my @files = $group->getElementsByTagName("FILEREF",0);
		foreach my $file (@files)
			{
			# Only create the "Headers" group if there are some files to add to it
			if (!defined $xmlHeadersGroup)
				{
				$xmlHeadersGroup = &addGroup($xmlGroupList,"Headers");
				}
				
			my $name = $file->getAttribute("NAME");
			if (!defined $headerfiles{$name})
				{
				# first occurrence - add to list
				$headerfiles{$name}=1;
				&textElement($file, "TARGETNAME", $targetname, $file->getFirstChild);
				$file->removeAttribute("NAME");
				&disconnectNode($file);
				$xmlHeadersGroup->appendChild($file);
				$xmlHeadersGroup->addText("\n");
				}
			}
		&removeNode($group);
		}


	# Build the "Resources" group
			
	my $xmlResourcesGroup;
	my %resourcesfiles;
	my @resourcesgroups = $xmlProjectDoc->getElementsByTagName("RESOURCESGROUP",1);
	foreach my $group (@resourcesgroups)
		{
		$targetname = $group->getAttribute("TARGET");
		my @files = $group->getElementsByTagName("FILEREF",0);
		foreach my $file (@files)
			{
			# Only create the main "Resources" groups if there are some files to add
			# to them
			if (!defined $xmlResourcesGroup)
				{
				$xmlResourcesGroup = &addGroup($xmlGroupList,"Resources");
				}
				
			my $name = $file->getAttribute("NAME");
			if (!defined $resourcesfiles{$name})
				{
				# first occurrence - add to list
				$resourcesfiles{$name}=1;
				&textElement($file, "TARGETNAME", $targetname, $file->getFirstChild);
				$file->removeAttribute("NAME");
				&disconnectNode($file);

				$xmlResourcesGroup->appendChild($file);
				$xmlResourcesGroup->addText("\n");
				}
			}
		&removeNode($group);
		}

		
	# Build the "Link" group
			
	my $xmlLinkGroup = &addGroup($xmlGroupList,"Link");
	my %linkfiles;
	my @linkgroups = $xmlProjectDoc->getElementsByTagName("LINKGROUP",1);
	foreach my $group (@linkgroups)
		{
		$targetname = $group->getAttribute("TARGET");
		my @files = $group->getElementsByTagName("FILEREF",0);
		foreach my $file (@files)
			{
			my $name = $file->getAttribute("NAME");
			if (!defined $linkfiles{$name})
				{
				# first occurrence - add to list
				$linkfiles{$name}=1;
				&textElement($file, "TARGETNAME", $targetname, $file->getFirstChild);
				$file->removeAttribute("NAME");
				&disconnectNode($file);
				$xmlLinkGroup->appendChild($file);
				$xmlLinkGroup->addText("\n");
				}
			}
		&removeNode($group);
		}


	# Build the "Documents" group
			
	my $xmlDocumentsGroup;
	my %documentfiles;
	my @documentgroups = $xmlProjectDoc->getElementsByTagName("DOCUMENTSGROUP",1);
	foreach my $group (@documentgroups)
		{
		$targetname = $group->getAttribute("TARGET");
		my @files = $group->getElementsByTagName("FILEREF",0);
		foreach my $file (@files)
			{				
			# Only create the "Documents" group if there are some files to add to it
			if (!defined $xmlDocumentsGroup)
				{
				$xmlDocumentsGroup = &addGroup($xmlGroupList,"Documents");
				}

			my $name = $file->getAttribute("NAME");

			
			if (!defined $documentfiles{$name})
				{
				# first occurrence - add to list
				$documentfiles{$name}=1;
				&textElement($file, "TARGETNAME", $targetname, $file->getFirstChild);
				$file->removeAttribute("NAME");
				&disconnectNode($file);
				$xmlDocumentsGroup->appendChild($file);
				$xmlDocumentsGroup->addText("\n");
				}
			}
		&removeNode($group);
		}


	# Build the "Lib" group and its subgroups

	my $xmlLibGroup = &addGroup($xmlGroupList, "Libraries");
	my %libplats;
	my @libgroups = $xmlProjectDoc->getElementsByTagName("LIBGROUP",1);
	foreach my $group (@libgroups)
		{
		$targetname = $group->getAttribute("TARGET");
		my $plat = $group->getAttribute("PLAT");
		if (!defined $libplats{$plat})
			{
			$libplats{$plat} = &addGroup($xmlLibGroup, $plat);
			}
		my $platgroup = $libplats{$plat};
		my @files = $group->getElementsByTagName("FILEREF",0);
		foreach my $file (@files)
			{
			my $name = $file->getAttribute("NAME");
			if (!defined $sourcefiles{"$plat\\$name"})
				{
				# first occurrence - add to list
				$sourcefiles{"$plat\\$name"}=1;
				&textElement($file, "TARGETNAME", $targetname, $file->getFirstChild);
				$file->removeAttribute("NAME");
				&disconnectNode($file);
				$platgroup->appendChild($file);
				$platgroup->addText("\n");
				}
			}
		&removeNode($group);
		}

	# Replace the GROUPLIST & TARGETORDER in the template document

	my $node = $xmlProjectDoc->getElementsByTagName("GROUPLIST",1)->item(0);
	$node->getParentNode->replaceChild($xmlGroupList, $node);

	$node = $xmlProjectDoc->getElementsByTagName("TARGETORDER",1)->item(0);
	$node->getParentNode->replaceChild($xmlTargetOrder, $node);

	# Insert the "Build All" target

	my $xmlBuildAll = new XML::DOM::Element($xmlProjectDoc,"TARGET");
	$xmlBuildAll->addText("\n");
	&textElement($xmlBuildAll, "NAME", "Build All");
	my $settinglist = new XML::DOM::Element($xmlProjectDoc,"SETTINGLIST");
	&textSetting($settinglist, "Linker", "None");
	&textSetting($settinglist, "Targetname", "Build All");
	$xmlBuildAll->appendChild($settinglist);
	$xmlBuildAll->addText("\n");
	&textElement($xmlBuildAll, "FILELIST", "");
	$xmlBuildAll->addText("\n");
	&textElement($xmlBuildAll, "LINKORDER", "");
	$xmlBuildAll->addText("\n");
	$xmlBuildAll->appendChild($xmlSubTargetList);

	&addOrderedTarget($xmlTargetOrder, "Build All");

	$node = $xmlProjectDoc->getElementsByTagName("TARGETLIST",1)->item(0);
	$node->appendChild($xmlBuildAll);

	# Output the result

	&main::Output(
		$xmlProjectDoc->toString
	);

}

sub PMEndSrcList {

	my @PlatList=&main::PlatOverrideList();
	
	if (scalar @PlatList == 0)
		{
		@PlatList = ("WINSCW", "ARM4", "ARMV5");

		if ($CW_major_version >= 3)
			{
			push @PlatList, "ARMV5_ABIV1";
			}
		}

	shift @PlatList;	# we've already done the first one in the list
	foreach (@PlatList)
		{
		ExtraPlat($_);
		}

	&finaliseProject();

}

sub GetGCCELibPath($) {
	my $gnulibgccPath;
	open PIPE, "arm-none-symbianelf-g++ $_[0] 2>&1 | ";
	while(<PIPE>){
		$gnulibgccPath = $_;
		$gnulibgccPath =~ s/\//\\/g;
	}
	close PIPE;
	my $SearchlibgccDir = &main::Path_Chop(&main::Path_Split('Path', $gnulibgccPath));

	return $SearchlibgccDir;
}

sub Read_BSF_Options() {
        my %plat = (main::PlatRec());
		my @Customization_Data = split(/\n/,$plat{'CUSTOMIZATION_DATA'});
	foreach my $option (@Customization_Data) {
			next if ($option =~ /^$/);
	        warn "Unrecognized BSF syntax: $option.\n"
		        unless ($option =~ /\s*(\S+)\s+(.+)$/);
		my $key = uc $1;
		my $val = $2;
	        warn "Unrecognized BSF keyword: $key.\n"
		        unless ($BSF_keywords{$key});
		if ($key =~ /COMMON_OPTIONS/) {
		        push @commonOptions, $val;
			next;
		}
		if ($key =~ /THUMB_OPTIONS/) {
		        push @thumbOptions, $val;
			next;
		}
		if ($key =~ /ARM_OPTIONS/) {
		        push @armOptions, $val;
			next;
		}
		if ($key =~ /KERNEL_OPTIONS/) {
		        push @kernelOptions, $val;
			next;
		}
		if ($key =~ /INVARIANT_OPTIONS/) {
		        push @invariantOptions, $val;
			next;
		}
		if ($key =~ /LD_OPTIONS/) {
		        push @linkerOptions, $val;
			next;
		}
		if ($key =~ /AR_OPTIONS/) {
		        push @archiverOptions, $val;
			next;
		}

	}
}

# Set the options passed from BSF file 
# @param OptionName    - BSF Keyword using which the options would be overridden in the BSF file
# @param Options       - List of options read from the BSF keyword
sub Set_BSF_Options($$)
{
	my ($OptionName,$Options) = @_;
	my @Fragments=();
	
	if ($CustPlat{'CUSTOMIZES'} && ($CustPlat{'ROOTPLATNAME'} eq "GCCE"))
	{
		$CustGCCE=1;
	}
	foreach my $val (@{$Options})
	{		
		# Check if the value of BSF option is to be set or added/removed.
		if($val =~ /\+\[.*\]\+|\-\[.*\]\-/)
		{
			if (@Fragments = Cl_bpabi::Split_BSF_Options($val,'RemoveOptions'))
			{
				foreach my $Opt (@Fragments) 
				{
					# Remove trailing white spaces
					$Opt =~ s/\s+$//;
					# Substitute '=' with '%' which is a wild card character in makefile.
					# This is required for cases where option to be removed contains '=' (e.g.'-march=armv5t').
					# When such options are to be removed, "$(INVARIANT_OPTIONS:-march=armv5t=)" is written in the makefile.
					# However, because of the occurence of '=', pattern match fails in the makefile and such options are not removed. 
					# To resolve this, '=' is replaced with '%'  in the makefile so that the substitution pattern looks like 
					# "$(INVARIANT_OPTIONS:-march%armv5t=)" in makefile (e.g."$(INVARIANT_OPTIONS:-march%armv5t=)").
					$Opt =~ s/=/%/;
					if((($OptionName =~ /COMMON_OPTIONS/)
					|| ($OptionName =~ /THUMB_OPTIONS/)
					|| ($OptionName =~ /ARM_OPTIONS/)
					|| ($OptionName =~ /KERNEL_OPTIONS/)
					|| ($OptionName =~ /INVARIANT_OPTIONS/))
					&& ($CustGCCE))
					{
						$GCCE_CompilerOption = RemoveBsfOptions($Opt,$GCCE_CompilerOption);
					}
					elsif($OptionName =~ /COMMON_OPTIONS/)
					{
						$CCFLAGS = RemoveBsfOptions($Opt,$CCFLAGS);
					}
					elsif(($OptionName =~ /THUMB_OPTIONS/)
					|| ($OptionName =~ /ARM_OPTIONS/)
					|| ($OptionName =~ /KERNEL_OPTIONS/))
					{
						$CCFLAGS = RemoveBsfOptions($Opt,$CCFLAGS);
					}
					elsif($OptionName =~ /INVARIANT_OPTIONS/)
					{
						$CCFLAGS = RemoveBsfOptions($Opt,$CCFLAGS);
					}
					elsif($OptionName =~ /LD_OPTIONS/)
					{
						$linkeropts = RemoveBsfOptions($Opt,$Link);
						$linkCommand = RemoveBsfOptions($Opt,$Link);
					}
					elsif($OptionName =~ /AR_OPTIONS/)
					{
						$archiveropts = RemoveBsfOptions($Opt,$linkCommand);
					}
				}					
				@Fragments=();
			}
			
			if (@Fragments = Cl_bpabi::Split_BSF_Options($val,'AddOptions')) 
			{
				my $v;
				foreach $v (@Fragments)
				{
					if((($OptionName =~ /COMMON_OPTIONS/)
					|| ($OptionName =~ /THUMB_OPTIONS/)
					|| ($OptionName =~ /ARM_OPTIONS/)
					|| ($OptionName =~ /KERNEL_OPTIONS/)
					|| ($OptionName =~ /INVARIANT_OPTIONS/))
					&& ($CustGCCE))
					{
						$GCCE_CompilerOption .= ' '.$v.' ';
					}
					elsif($OptionName =~ /COMMON_OPTIONS/)
					{
						$bsfaddoptions .= ' '.$v.' '; 						
					}
					elsif(($OptionName =~ /THUMB_OPTIONS/)
					|| ($OptionName =~ /ARM_OPTIONS/)
					|| ($OptionName =~ /KERNEL_OPTIONS/))
					{
						$bsfaddoptions .= ' '.$v.' ';	
					}
					elsif($OptionName =~ /INVARIANT_OPTIONS/)
					{
						$bsfaddoptions .= ' '.$v.' ';
					}
					elsif($OptionName =~ /LD_OPTIONS/)
					{
						$linkeropts .= ' '.$v.' ';
					}
					elsif($OptionName =~ /AR_OPTIONS/)
					{
						$archiveropts .= ' '.$v.' ';
					}
				}
				@Fragments=();
			}
		}
		else
		{
			if((($OptionName =~ /COMMON_OPTIONS/)
			|| ($OptionName =~ /THUMB_OPTIONS/)
			|| ($OptionName =~ /ARM_OPTIONS/)
			|| ($OptionName =~ /KERNEL_OPTIONS/)
			|| ($OptionName =~ /INVARIANT_OPTIONS/))
			&& ($CustGCCE))
			{
				$GCCE_CompilerOption .= ' '.$val.' ';
			}
			elsif($OptionName =~ /COMMON_OPTIONS/)
			{
				$bsfaddoptions .= ' '.$val.' ';
			}
			elsif(($OptionName =~ /THUMB_OPTIONS/)
			|| ($OptionName =~ /ARM_OPTIONS/)
			|| ($OptionName =~ /KERNEL_OPTIONS/))
			{
				$bsfaddoptions .= ' '.$val.' ';
			}
			elsif($OptionName =~ /INVARIANT_OPTIONS/)
			{
				$bsfaddoptions .= ' '.$val.' ';
			}
			elsif($OptionName =~ /LD_OPTIONS/)
			{
				$linkeropts .= ' '.$val.' ';
			}
			elsif($OptionName =~ /AR_OPTIONS/)
			{
				$archiveropts .= ' '.$val.' ';
			}
		}	
	}
}

sub RemoveBsfOptions($$)
{
	my ($Opt_to_replace,$Opt_replaced_in) = @_;
	
	$Opt_replaced_in =~ s/$Opt_to_replace//g;
	return $Opt_replaced_in;
}

# function to expand the macro as pass with appropriate options
sub printlist {
	my $option =shift @_;
	my @list = @_,
	my $data;
	my $finalval=undef;
	
	foreach $data (@list)
	{
		if($option =~ "-D") {
			$finalval .= " ".$option.$data;	
		}
		else {
			$finalval .= " ".$option." ".$data;
		}
	}
	return $finalval;
}

#read the configuration make file into the HASH and use them for further processing
sub collect_config_data {
	my($configfile) = @_;
	open(DATA, "<$configfile");
	while(<DATA>) 
	{	
		my $line = $_;
		if($line =~ /=/)
		{
			if ($line =~ /(.*):=(.*)/)
			{ 
				$configdata{$1}=$2;
			}
			elsif ($line =~ /(.*)=(.*=.*)/)
			{ 
				$configdata{$1}=$2;
			}
			elsif ($line =~ /(.*)=(.*)/)
			{ 
				$configdata{$1}=$2;
			}
		}
	}
	close(DATA)
}

#function is ti fetch the contents of the config data which is read from the configuration make file, 
# for ex: KERNEL_OPTIONS=$(ARM_INSTRUCTION_SET) $(NO_EXCEPTIONS), this function extracts the value for ARM_INSTRUCTION_SET & NO_EXCEPTIONS
sub fetch_config_data {
	my($list) = @_;
	my $op;
	my $op1;
	my $finaldata = undef;
	
	my @ip_options = split(/\s+/, $list);	
	foreach $op (@ip_options)
	{
		$op =~ s/\)//g;
		$op =~ s/\$\(//g;
		if($op =~ /-/) {
			$finaldata .= " ".$op;
		}
		else {
			$finaldata .= " ".$configdata{$op};
		}
	}
	return $finaldata;
}

# function to fix the bsf options, if the bsf option is already present in the CCFLAGS then remove from it so that it can be added from bsf,
# this is to avoid the duplication of the options passed to the compiler.
sub fixbsfoptions {
	my ($options) = @_;
	my $ccflgs = $CCFLAGS;
	my $d;
	my $Pattern = '-{1,2}\S+\s*(?!-)\S*';
	my @list = $options =~ /$Pattern/g;
	foreach $d (@list) {
		if($ccflgs =~ /$d/) {
				$ccflgs =~ s/$d//g;	
		}
		else {
			if($d =~ /(.*)\s+(.*)/) {
				my $a = $1;
				if($ccflgs =~ /$a\s+\S+/) {
					$ccflgs =~ s/$a\s+\S+//g;
				}
			}
		}
	}
	$CCFLAGS = $ccflgs;
}

# funtion to get the list if the libraries to be linked during linking
sub GetLibList() {
	my @LibList;
	my @StaticLibList;
	my $Plat=&main::Plat;
	unless(defined($ENV{RVCT_VER_MAJOR})){
		my ($rvct_M, $rvct_m, $rvct_b) = RVCT_plat2set::get_version_list($Plat);
		$ENV{RVCT_VER_MAJOR}=$rvct_M;
	}
	&Cl_bpabi::getVariableForNewPlat();
	my $list = &Cl_bpabi::getConfigVariable('STATIC_LIBS_LIST') ;
	
	if (length($list) >0)
	{
		@StaticLibList = split(/\s+/, $list);
	}
	if($Plat eq "ARMV5" || $Plat eq "ARMV5_ABIV2" || IsCustomization($Plat)) {
		@LibList=&Armutl_ArmLibList;
		if(@LibList==0) {
			my $LibDir = Armutl_ArmLibDir();
			if (@StaticLibList) {
				foreach my $lib (@StaticLibList) {
					push @LibList, ("$LibDir\\$lib");
				}
			}
		}
	}
	else
	{
		@LibList = ('$(STATIC_LIBS_LIST)');
	}
	return @LibList;
}

1;
