sbsv1/abld/makmake/mmp.pm
changeset 629 541af5ee3ed9
parent 599 fa7a3cc6effd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sbsv1/abld/makmake/mmp.pm	Wed Aug 18 17:23:33 2010 +0300
@@ -0,0 +1,2410 @@
+# Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies).
+# All rights reserved.
+# This component and the accompanying materials are made available
+# under the terms of "Eclipse Public License v1.0"
+# which accompanies this distribution, and is available
+# at the URL "http://www.eclipse.org/legal/epl-v10.html".
+#
+# Initial Contributors:
+# Nokia Corporation - initial contribution.
+#
+# Contributors:
+#
+# Description:
+# Processes an mmp file and sets up subroutines to return the data
+# 
+#
+
+package Mmp;
+
+require Exporter;
+@ISA=qw(Exporter);
+
+@EXPORT=qw(
+	Mmp_SetVerbose
+	Mmp_ProcessL
+
+	Mmp_ABI
+	Mmp_AifStruct
+	Mmp_AllowDllData
+	Mmp_CompressTarget
+	Mmp_ASSPExports
+	Mmp_ASSPLibList
+	Mmp_BuildAsARM
+	Mmp_BitMapStruct 
+	Mmp_CallDllEntryPoints
+	Mmp_Capability
+	Mmp_CapabilityFlags
+	Mmp_DataLinkAddress
+	Mmp_DebugLibList 
+	Mmp_Def
+	Mmp_DocHash
+	Mmp_ExportUnfrozen
+	Mmp_FirstLib
+	Mmp_FixedProcess
+	Mmp_HeapSize
+	Mmp_LibList 
+	Mmp_LinkAs
+	Mmp_LinkAsBase
+	Mmp_ExportLibrary
+	Mmp_NewLib
+	Mmp_NoExportLibrary
+	Mmp_Macros
+	Mmp_MmpFlag
+	Mmp_PlatTxt2D
+	Mmp_ProcessPriority
+	Mmp_ResourceStruct 
+	Mmp_SmpSafe
+	Mmp_RamTargets
+	Mmp_RomTargets
+	Mmp_SourceStruct 
+	Mmp_StackSize
+	Mmp_StatLibList 
+	Mmp_SysIncPaths
+	Mmp_Trg
+	Mmp_TrgType
+	Mmp_UidList
+	Mmp_UserIncPaths
+	Mmp_SrcDbg
+	Mmp_StdCpp
+	Mmp_NoStdCpp
+	Mmp_WarningLevel
+	Mmp_LinkerOptions
+	Mmp_Reset
+	Mmp_Uids
+	Mmp_Version
+	Mmp_SecureId
+	Mmp_VendorId
+	Mmp_Replace
+	Mmp_ARMFPU	
+	Mmp_SetIgnoreMissingDef 
+	Mmp_StringTable
+	Mmp_CompressTargetMode
+	Mmp_CodePagingTargetMode
+	Mmp_DataPagingTargetMode
+	Mmp_CheckSourceMMPMetaData
+	Mmp_CheckSourceMMPIncludes
+	Mmp_IsWideCharMain
+	Mmp_IsDebuggable
+	Mmp_IsFeatureVariant
+	Mmp_TestedMacros
+);
+
+
+use Genutl;
+use Prepfile;
+use Pathutl;
+use Trgtype;
+use CheckSource;
+use Cwd;
+
+my %ProcessPriorityNames = (
+	LOW=>'Low',
+	BACKGROUND=>'Background',
+	FOREGROUND=>'Foreground',
+	HIGH=>'High',
+	WINDOWSERVER=>'WindowServer',
+	FILESERVER=>'FileServer',
+	REALTIMESERVER=>'RealTimeServer',
+	SUPERVISOR=>'SuperVisor'
+);
+
+my %CapabilityNames;
+
+my %Mode;
+
+my $ABI;
+my @AifStruct;
+my $AllowDllData=0;
+
+use constant COMPRESS => 0;
+use constant NOCOMPRESS => 1;
+my $CompressTarget=COMPRESS;	# compress the target binary (negative logic)
+use constant NOCOMPRESSIONMETHOD => 0;
+use constant INFLATECOMPRESSIONMETHOD => 1;
+use constant BYTEPAIRCOMPRESSIONMETHOD => 2;
+my $CompressTargetMethod=NOCOMPRESSIONMETHOD;   #NONE
+
+my $ASSPABISwitch=0;
+my $ASSPExports=0;
+my @ASSPLibList;
+my @BitMapStruct;
+my $BuildAsARM=0;
+my $CallDllEntryPoints=0;
+my $Capability;
+my @CapabilityFlags=(0,0);
+my %CurSource;
+my $DataLinkAddress='';
+my @DebugLibList;
+my %Def;
+$Def{Path}='';
+$Def{Base}='';
+$Def{Ext}='';
+my %DocHash;
+my $ExportUnfrozen=0;
+my $FirstLib;
+my $FixedProcess=0;
+my %HeapSize;
+my @LangList;
+my @LibList;
+my $LinkAs;
+my $LinkAsBase;
+my $ExportLibrary;
+my $NoExportLibrary;
+my @Macros;
+my %MmpFlag;
+my $NewLib;
+my @PlatTxt2D;
+my $ProcessPriority='';
+my @ResourceStruct;
+my @RamTargets;
+my @RomTargets=({}); # include default
+my $SmpSafe = 0;
+my @SourceStruct;
+my $StackSize='';
+my @StatLibList;
+my $StdCpp = 0;
+my $NoStdCpp = 0;
+my @SysIncPaths;
+my $Trg;
+my %TrgType;
+my @UidList;
+my @UserIncPaths;
+my $SrcDbg=0;
+my %Compiler;
+my %LinkerOptions;
+my %Version;
+my $SecureId;
+my $VendorId;
+my %Replace;
+my $ARMFPU;
+my $IgnoreMissingDef=0;
+my @StringTableStruct;
+my $CodePagingTargetMode;	# 0-N/A, 1-UNPAGED, 2-PAGED
+my $DataPagingTargetMode;	# 0-N/A, 1-UNPAGED, 2-PAGED
+my $WideCharMain=0;
+my $IsDebuggable;	# 0-NON_DEBUGGABLE, 1-DEBUGGABLE,2-DEBUGGABLE_UDEBONLY
+my $FeatureVariant=0;
+use constant NOTPAGED => 0;
+use constant UNPAGED => 1;
+use constant PAGED => 2;
+use constant NON_DEBUGGABLE => 0;
+use constant DEBUGGABLE => 1;
+use constant DEBUGGABLE_UDEBONLY => 2;
+my %CheckSourceMMPMetaData;
+my %CheckSourceMMPIncludes;
+my %mmptestedMacrosHash;
+
+# List of deprecated 2nd UIDs. These are checked in a Secure Platform
+my %deprecatedUIDs=
+    (
+    "0x10005e32" => "Unmigrated FEP detected from use of UID 0x10005e32 ",
+    "0x10004cc1" => "Unmigrated Application Initaliser (CEikLibrary deriver) detected from use of UID 0x10004cc1 ",
+    "0x10003a30" => "Unmigrated Conarc plugin detected from use of UID 0x10003a30",
+    "0x10003a19" => "Unmigrated Recogniser detected from use of UID 0x10003a19",
+    "0x10003a37" => "Unmigrated Recogniser detected from use of UID 0x10003a37",
+    "0x10003a34" => "Unmigrated CTL detected from use of UID 0x10003a34"
+    );
+
+
+sub Mmp_Reset() {
+	undef $ABI;
+	undef @AifStruct;
+	$AllowDllData=0;
+	$CompressTarget=COMPRESS;
+    $CompressTargetMethod=NOCOMPRESSIONMETHOD;
+	$ASSPABISwitch=0;
+	$ASSPExports=0;
+	$BuildAsARM=0;
+	undef @ASSPLibList;
+	undef @BitMapStruct;
+	$CallDllEntryPoints=0;
+	undef $Capability;
+	@CapabilityFlags=(0,0);
+	undef $VendorId;
+	$DataLinkAddress='';
+	undef @DebugLibList;
+	undef %Def;
+	$Def{Path}='';
+	$Def{Base}='';
+	$Def{Ext}='';
+	undef %DocHash;
+	$ExportUnfrozen=0;
+	undef $FirstLib;
+	$FixedProcess=0;
+	undef %HeapSize;
+	undef @LangList;
+	undef @LibList;
+	undef $LinkAs;
+	undef $LinkAsBase;
+	undef $ExportLibrary;
+	undef @Macros;
+	undef %MmpFlag;
+	undef $NewLib;
+	undef @PlatTxt2D;
+	$ProcessPriority='';
+	undef @ResourceStruct;
+	undef @RamTargets;
+	@RomTargets=({}); # include default
+	undef @SourceStruct;   
+	$StackSize='';
+	undef @StatLibList;
+	$SmpSafe = 0;
+	$StdCpp = 0;
+	$NoStdCpp = 0;
+	undef @SysIncPaths;
+	undef $Trg;
+	undef %TrgType;
+	undef @UidList;
+	undef @UserIncPaths;
+	$SrcDbg=0;
+	undef %Compiler;
+	undef %LinkerOptions;
+    undef %Version;
+    $IgnoreMissingDef=0;
+    undef $ARMFPU;
+	undef @StringTableStruct;
+	$CodePagingTargetMode=0;	# default N/A
+	$DataPagingTargetMode=0;	# default N/A
+	$IsDebuggable=0; # Default = 0 (Not debuggable)
+	undef %mmptestedMacrosHash;
+}
+
+BEGIN {
+	$Mode{'Verbose'}=0;
+
+	%CapabilityNames = (
+
+		TCB =>					(1<<0),
+		COMMDD =>				(1<<1),
+		POWERMGMT =>			(1<<2),
+		MULTIMEDIADD =>			(1<<3),
+		READDEVICEDATA =>		(1<<4),
+		WRITEDEVICEDATA =>		(1<<5),
+		DRM =>					(1<<6),
+		TRUSTEDUI =>			(1<<7),
+		PROTSERV =>				(1<<8),
+		DISKADMIN =>			(1<<9),
+		NETWORKCONTROL =>		(1<<10),
+		ALLFILES =>				(1<<11),
+		SWEVENT =>				(1<<12),
+		NETWORKSERVICES =>		(1<<13),
+		LOCALSERVICES =>		(1<<14),
+		READUSERDATA =>			(1<<15),
+		WRITEUSERDATA =>		(1<<16),
+		LOCATION =>				(1<<17),
+		SURROUNDINGSDD =>		(1<<18),
+		USERENVIRONMENT =>		(1<<19),
+#old capability names have zero value
+		ROOT =>				0,
+		MEDIADD =>			0,
+		READSYSTEMDATA =>	0,
+		WRITESYSTEMDATA =>	0,
+		SOUNDDD =>			0,
+		UIDD =>				0,
+		KILLANYPROCESS =>	0,
+		DEVMAN =>			0,
+		PHONENETWORK =>		0,
+		LOCALNETWORK =>		0
+	);
+	my $all=0;
+	foreach (keys %CapabilityNames)
+		{
+		$all = $all | $CapabilityNames{$_};
+		}
+	$CapabilityNames{"ALL"} = $all;
+}
+
+sub Mmp_SetVerbose () {
+	$Mode{'Verbose'}=1;
+}
+
+sub Mmp_SetIgnoreMissingDef() {
+    $IgnoreMissingDef = 1;
+}
+
+sub Mmp_ProcessL ($$$$) {
+	my ($EPOCPath, $MMPFILE, $Plat_ref, $BuildIncList)=@_;
+	my $emulator = $$Plat_ref{OS} ne 'EPOC32';
+
+	if ($Mode{Verbose}) {
+		&Prepfile_SetVerbose;
+	}
+
+	# Generate macro usage information and add variant build includes
+	my $options = '-dU';
+	foreach ( @$BuildIncList )
+		{
+		$options .= ' -I ' . &Path_PrefixWithDriveAndQuote($_);
+		}
+
+#	preprocess the project description file
+	my @Mmp2D;
+	Prepfile_SetCmdOptions($options);
+	eval { &Prepfile_ProcessL(\@Mmp2D, $MMPFILE, &main::VariantFile(), @{$$Plat_ref{MmpMacros}}); };
+	Prepfile_SetCmdOptions('');
+	die $@ if $@;
+
+	my @checkSourceMacros;
+	foreach (@{$$Plat_ref{MmpMacros}}) {
+		$_=uc $_;
+		push @checkSourceMacros, " $_=_____$_";
+	}
+
+	my $current = cwd;
+	$current =~ s/\//\\/g;
+	$current =~ s/^[a-zA-Z]://;
+
+	# Mimic what the current parsing routine does in terms of include paths and .mmp files
+	my @checkSourceUserIncludePaths;
+	push @checkSourceUserIncludePaths, "$ENV{EPOCROOT}epoc32\\include";
+	push @checkSourceUserIncludePaths, "\.";
+	push @checkSourceUserIncludePaths, $current;
+	my @checkSourceSystemIncludePaths = ();
+
+	CheckSource_Includes($MMPFILE, %CheckSourceMMPIncludes, &main::VariantFile(), @checkSourceMacros, @checkSourceUserIncludePaths, @checkSourceSystemIncludePaths, $CheckSource_NoUserSystemDistinction);
+
+	my $NoStrictDef=0;
+	my $TrgPath='';
+	my $TrgType;
+
+	my $MmpPath=&Path_Split('Path', $MMPFILE);
+
+	my @MmpDie=();
+	my @MmpWarn=();
+	my @MmpDiagnostic=();
+	my @MmpMigrationNote=();
+
+	my (%CheckAif, %CheckASSPLib, %CheckDoc, %CheckLang, %CheckLib, %CheckMacro, %CheckResrc, %CheckSrc, %CheckStatLib, %CheckSysInc, %CheckSysResrc, %CheckUserInc);
+
+	my ($CheckDef);
+
+	my ($CheckRamTargets, $CheckRomTargets);
+
+	my ($OtherPlatSwitch, $PlatTxtSwitch);
+
+	my (%CurBitMap, $CurBitMapSrcPath);
+	$CurBitMapSrcPath=$MmpPath;
+
+	my (%CurResource, %ResourceCheckLang);
+
+	my %CurStringTable;
+	my ($CurSrcPath, $MmpMacro, $Line);
+	$CurSrcPath=$MmpPath;
+
+#	include the .MMP file as a document
+	@{$DocHash{$MmpPath}}=(&Path_Split('File', $MMPFILE));
+	$CheckDoc{$MMPFILE}='zero - specified by default';
+
+# process the data structure
+	my $CurFile=$MMPFILE;
+	LINE: foreach $Line (@Mmp2D) {
+		my $LineNum=shift @$Line;
+		$_=shift @$Line;
+		if ($LineNum eq '#') {
+			$CurFile=$_;
+			next LINE;
+		}
+
+		# Get details of tested macros affecting the MMP file
+		# We get this from -dU preprocessor option
+		if (/^#define$/ && "@$Line" =~ /^(\w+(?:\([^\)]*\))?)(?:\s(\S.*?))?\s*$/)
+			{
+			# Record macro details for defined tested macro
+			$mmptestedMacrosHash{$1} = $2 ? "\'$2\'" : 'defined';
+			next LINE;
+			}
+		elsif (/^#undef$/)
+			{
+			# Record macro details for undefined tested macro
+			$mmptestedMacrosHash{"@$Line"} = 'undefined';
+			next LINE;
+			}
+				
+		$CurFile = &Path_Norm ($CurFile);
+		
+		$_=uc $_;
+
+		my $mainElement = $_;
+		
+		if ($PlatTxtSwitch) {
+			if (/^END$/o) {
+				$PlatTxtSwitch=0;
+				next LINE;
+			}
+			push @PlatTxt2D, [ "$CurFile($LineNum)", $_, @$Line ];
+			next LINE;
+		}
+		if ($OtherPlatSwitch) {
+			if (/^END$/o) {
+				$OtherPlatSwitch=0;
+				next LINE;
+			}
+			next LINE;
+		}
+		# ----------- handle body of START BITMAP ... END -------------
+		if (%CurBitMap) {
+			if (/^SOURCE$/o) {
+				unless (@$Line>1) {
+					push @MmpDie, "$CurFile($LineNum) : Not enough arguments for Bitmap keyword SOURCE\n";
+				}
+				my $cldepth = shift @$Line;
+				my @ClDepths = split(/,/, $cldepth);
+				foreach (@ClDepths) {
+					$_ = lc $_; # bmconv can't handle upper-case 'C's
+					unless (/^c?\d\d?a?$/o) {
+						push @MmpDie, "$CurFile($LineNum) : BITMAP color depth \"$_\" - unexpected format\n";
+					}
+				}
+				@ClDepths = (@ClDepths) x @$Line;	# make a sufficiently long list of color depths
+				foreach (@$Line) {
+					CheckSource_MetaData(%CheckSourceMMPMetaData, $CurFile, "BITMAP SOURCE", $_, $LineNum, $CheckSource_PhysicalCheck, $CurBitMapSrcPath);
+					$_ = &Path_Norm($_);
+					
+					$_=lc $_;	# bmconv generates a header with case-sensitive enums
+					my $bitmap = "$CurBitMapSrcPath$_";
+					push @{$CurBitMap{Source}}, { # sources must be kept in order
+						Src=>$bitmap,
+						ClDepth=>shift @ClDepths	# take next color depth from the list
+					};
+					unless (-e $bitmap) {
+						push @MmpWarn, "$CurFile($LineNum) : BITMAP source \"$CurBitMapSrcPath$_\" not found\n";
+					}
+				}
+				next LINE;
+			}
+			if (/^END$/o) {
+				$CurBitMapSrcPath=$MmpPath;
+				my %BitMap=%CurBitMap;
+				undef %CurBitMap;
+				push @BitMapStruct, \%BitMap;
+				next LINE;
+			}
+			if (/^SOURCEPATH$/o) {
+				unless ($CurBitMapSrcPath=shift @$Line) {
+					push @MmpDie, "$CurFile($LineNum) : No path specified with Bitmap keyword SOURCEPATH\n";
+					next LINE;
+				}
+				CheckSource_MetaData(%CheckSourceMMPMetaData, $CurFile, "BITMAP SOURCEPATH", $CurBitMapSrcPath, $LineNum, $CheckSource_PhysicalCheck);
+				$CurBitMapSrcPath = &Path_Norm($CurBitMapSrcPath);
+				
+				$CurBitMapSrcPath=~s-^(.*[^\\])$-$1\\-o;   # ensure path ends with a backslash
+				$CurBitMapSrcPath=&Path_MakeAbs($CurFile,$CurBitMapSrcPath);
+				if (@$Line) {
+					push @MmpWarn, "$CurFile($LineNum) : Too many arguments for Bitmap keyword SOURCEPATH\n";
+				}
+				next LINE;
+			}
+			if (/^HEADER$/o) {
+				if ($CurBitMap{Hdr}) {
+					push @MmpWarn, "$CurFile($LineNum) : Bitmap HEADER already specified at line $CurBitMap{Hdr}\n";
+					next LINE;
+				}
+				$CurBitMap{Hdr}="$CurFile($LineNum)";
+				if (@$Line) {
+					push @MmpWarn, "$CurFile($LineNum) : Bitmap keyword HEADER takes no arguments\n";
+				}
+				next LINE;
+			}
+
+
+			if (/^TARGETPATH$/o) {
+				if ($CurBitMap{TrgPath}) {
+					push @MmpWarn, "$CurFile($LineNum) : Bitmap TARGETPATH already specified\n";
+					next LINE;
+				}
+				unless ($CurBitMap{TrgPath}=shift @$Line) {
+					push @MmpDie, "$CurFile($LineNum) : No path specified with Bitmap keyword TARGETPATH\n";
+					next LINE;
+				}
+				CheckSource_MetaData(%CheckSourceMMPMetaData, $CurFile, "BITMAP TARGETPATH", $CurBitMap{TrgPath}, $LineNum);
+				$CurBitMap{TrgPath} = &Path_Norm($CurBitMap{TrgPath});
+
+				$CurBitMap{TrgPath}=~s-^\\(.*)$-$1-o;        # remove leading backslash, if any
+				$CurBitMap{TrgPath}=~s-^(.*[^\\])$-$1\\-o;   # ensure path ends with a backslash
+				$CurBitMap{TrgPath}="z\\$CurBitMap{TrgPath}";
+				if (@$Line) {
+					push @MmpWarn, "$CurFile($LineNum) : Too many arguments for Bitmap keyword TARGETPATH\n";
+				}
+				next LINE;
+			}
+			push @MmpWarn, "$CurFile($LineNum) : Unrecognised Bitmap Keyword \"$_\"\n";
+		}
+		
+		# ----------- handle body of START RESOURCE ... END -------------
+		if (%CurResource) {
+			if (/^END$/o) {
+				$CurResource{SrcPath}=$CurSrcPath;
+				my %Resource=%CurResource;
+				undef %CurResource;
+				push @ResourceStruct, \%Resource;
+				undef %ResourceCheckLang;
+				next LINE;
+			}
+			if (/^HEADER$/o) {
+				if ($CurResource{Hdr}) {
+					push @MmpWarn, "$CurFile($LineNum) : Resource HEADER already specified at line $CurResource{Hdr}\n";
+					next LINE;
+				}
+				elsif ($CurResource{Hdronly}) {
+					push @MmpWarn, "$CurFile($LineNum) : Resource HEADERONLY already specified at line $CurResource{Hdr}\n";
+					next LINE;
+				}
+				$CurResource{Hdr}="$CurFile($LineNum)";
+				if (@$Line) {
+					push @MmpWarn, "$CurFile($LineNum) : Resource keyword HEADER takes no arguments\n";
+				}
+				next LINE;
+			}
+			if (/^HEADERONLY$/o) {
+				if ($CurResource{Hdronly}) {
+					push @MmpWarn, "$CurFile($LineNum) : Resource HEADERONLY already specified at line $CurResource{Hdr}\n";
+					next LINE;
+				}
+				elsif ($CurResource{Hdr}) {
+					push @MmpWarn, "$CurFile($LineNum) : Resource HEADER already specified at line $CurResource{Hdr}\n";
+					next LINE;
+				}
+				$CurResource{Hdronly}="$CurFile($LineNum)";
+				if (@$Line) {
+					push @MmpWarn, "$CurFile($LineNum) : Resource keyword HEADERONLY takes no arguments\n";
+				}
+				next LINE;
+			}
+
+			if (/^LANG$/o) {
+				if (@$Line) {
+					my $Candidate;
+					foreach $Candidate (@$Line) {
+						if ($ResourceCheckLang{$Candidate}) {
+							push @MmpWarn, "$CurFile($LineNum) : Duplicate Language \"$Candidate\" at line $ResourceCheckLang{$Candidate}\n";
+							next; 
+						}
+						push @{$CurResource{Lang}}, $Candidate;
+						$ResourceCheckLang{$Candidate}="$CurFile($LineNum)";
+					}
+					next LINE;
+				}
+				push @MmpWarn, "$CurFile($LineNum) : No Languages specified for keyword LANG\n";
+				next LINE;
+			}
+			if (/^TARGET$/o) {
+				if ($CurResource{BaseTrg}) {
+					push @MmpWarn, "$CurFile($LineNum) : Resource TARGET already specified\n";
+					next LINE;
+				}
+			    my $src=shift @$Line;
+			    CheckSource_MetaData(%CheckSourceMMPMetaData, $CurFile, "RESOURCE TARGET", $src, $LineNum);
+			    
+			    $src = &Path_Norm($src);
+				$CurResource{BaseTrg}=&Path_Split('Base',$src);
+				unless ($CurResource{BaseTrg}) {
+					push @MmpDie, "$CurFile($LineNum) : No name specified with Resource keyword TARGET\n";
+					next LINE;
+				}
+				if (@$Line) {
+					push @MmpWarn, "$CurFile($LineNum) : Too many arguments for Resource keyword TARGET\n";
+				}
+				next LINE;
+			}
+			if (/^TARGETPATH$/o) {
+				if ($CurResource{TrgPath}) {
+					push @MmpWarn, "$CurFile($LineNum) : Resource TARGETPATH already specified\n";
+					next LINE;
+				}
+				unless ($CurResource{TrgPath}=shift @$Line) {
+					push @MmpDie, "$CurFile($LineNum) : No path specified with Resource keyword TARGETPATH\n";
+					next LINE;
+				}
+				CheckSource_MetaData(%CheckSourceMMPMetaData, $CurFile, "RESOURCE TARGETPATH", $CurResource{TrgPath}, $LineNum);
+				$CurResource{TrgPath} = &Path_Norm($CurResource{TrgPath});
+				
+				$CurResource{TrgPath}=~s-^\\(.*)$-$1-o;        # remove leading backslash, if any
+				$CurResource{TrgPath}=~s-^(.*[^\\])$-$1\\-o;   # ensure path ends with a backslash
+				$CurResource{TrgPath}="z\\$CurResource{TrgPath}";
+				if (@$Line) {
+					push @MmpWarn, "$CurFile($LineNum) : Too many arguments for Resource keyword TARGETPATH\n";
+				}
+				next LINE;
+			}
+			if (/^UID$/o) {
+				if (@$Line) {
+					if (scalar @$Line>2) {
+						push @MmpWarn, "$CurFile($LineNum) : Can't specify more than 2 Uids\n";
+						next LINE;
+					}
+					foreach (@$Line) {
+						$_=&Genutl_AnyToHex($_);
+						if (defined $_) {
+							push @{$CurResource{Uids}}, $_;
+							next;
+						}
+						push @MmpDie, "$CurFile($LineNum) : Uid doesn't fit expected number format\n";
+						next LINE;
+					}
+					next LINE;
+				}
+				push @MmpWarn, "$CurFile($LineNum) : No Uids specified for Resource keyword UID\n";
+				next LINE;
+			}
+			if (/^DEPENDS$/o) {
+				# Note that DEPENDS lines are tolerated, but ignored - these are only relevant to build systems
+				# other than ABLD
+				next LINE;
+			}
+			
+			push @MmpWarn, "$CurFile($LineNum) : Unrecognised Resource Keyword \"$_\"\n";
+		}
+		if (/^WCHARENTRYPOINT$/o) {
+			if (@$Line) {
+				push @MmpWarn, "$CurFile($LineNum) : Keyword $_ takes no arguments\n";
+			}
+			if ($TrgType=~/^STDEXE$/io) {
+				$WideCharMain=1;	
+			}
+			else{
+				push @MmpWarn, "$CurFile($LineNum) : WCHARENTRYPOINT is supported only for Target Type STDEXE\n";
+			}
+			next LINE;
+		}
+		# ----------- handle body of START STRINGTABLE ... END -------------
+		if(%CurStringTable)
+		{	
+			if (/^END$/o) {
+				my %stringtable = %CurStringTable;
+				push @StringTableStruct, \%stringtable;
+				undef %CurStringTable;
+				next LINE;
+			}
+			if (/^EXPORTPATH/o) {
+				if (scalar @$Line != 1) {
+					push @MmpDie, "$CurFile($LineNum) : Wrong number of arguments for EXPORTPATH\n";
+				} 
+				if(defined $CurStringTable{ExportPath})	{
+					push @MmpDie, "$CurFile($LineNum) : Redefinition of EXPORTPATH\n";
+				}
+				else {
+				    my $src=shift @$Line;
+					$CurStringTable{ExportPath} = $src;
+
+					CheckSource_MetaData(%CheckSourceMMPMetaData, $CurFile, "STRINGTABLE EXPORTPATH", $CurStringTable{ExportPath}, $LineNum);
+					
+					$CurStringTable{ExportPath} = &Path_Norm($CurStringTable{ExportPath});					
+
+					$CurStringTable{ExportPath} =~ s/\\$//o;   # ensure path doesn't end with a backslash (cl_generic.pm will add one)
+					$CurStringTable{ExportPath} = &Path_MakeEAbs($EPOCPath,$CurFile,$CurStringTable{ExportPath});
+
+				}
+				next LINE;
+			}
+			if (/^HEADERONLY$/o) {
+				if ($CurStringTable{Hdronly}) {
+					push @MmpWarn, "$CurFile($LineNum) : Stringtable HEADERONLY already specified\n";
+					next LINE;
+				}
+				$CurStringTable{Hdronly}="$CurFile($LineNum)";
+				if (@$Line) {
+					push @MmpWarn, "$CurFile($LineNum) : Stringtable keyword HEADERONLY takes no arguments\n";
+				}
+				next LINE;
+			}
+		}
+		# ----------- handle top-level MMP file -------------------	
+		if (/^START$/) {
+			unless ($_=uc shift @$Line) {
+				push @MmpWarn, "$CurFile($LineNum) : No Argument specified for START block\n";
+				next LINE;
+			}
+			foreach $MmpMacro (@{$$Plat_ref{MmpMacros}}) {
+				if ($_ eq $MmpMacro) {
+					$PlatTxtSwitch="$CurFile($LineNum)";
+					next LINE;
+				}
+			}
+			if ($_ eq 'BITMAP') {
+				unless ($CurBitMap{Trg}=shift @$Line) {
+					push @MmpWarn, "$CurFile($LineNum) : No Bitmap Target specified\n";
+					$CurBitMap{Trg}='NoTargetSpecified';
+				}
+				if (@$Line) {
+					push @MmpWarn, "$CurFile($LineNum) : Too many arguments for START BITMAP clause\n";
+				}
+
+				CheckSource_MetaData(%CheckSourceMMPMetaData, $CurFile, "START BITMAP", $CurBitMap{Trg}, $LineNum);
+				$CurBitMap{Trg} = &Path_Norm($CurBitMap{Trg});
+				next LINE;
+			}
+			if ($_ eq 'RESOURCE') {
+				if (scalar @$Line != 1) {
+					push @MmpWarn, "$CurFile($LineNum) : Wrong number of arguments for START RESOURCE clause\n";
+				} 
+				else {
+				    my $src=shift @$Line;
+					CheckSource_MetaData(%CheckSourceMMPMetaData, $CurFile, "START RESOURCE", $src, $LineNum, $CheckSource_PhysicalCheck, $CurSrcPath);
+					$src = &Path_Norm($src);
+				    
+					$CurResource{Source}="$CurSrcPath$src";
+				}
+
+				
+				next LINE;
+			}
+			if ($_ eq 'STRINGTABLE') {
+				if (scalar @$Line != 1) {
+					push @MmpWarn, "$CurFile($LineNum) : Wrong number of arguments for START STRINGTABLE clause\n";
+				} 
+				else {
+				    my $Candidate = shift @$Line;
+				    CheckSource_MetaData(%CheckSourceMMPMetaData, $CurFile, "START STRINGTABLE", $Candidate, $LineNum, $CheckSource_PhysicalCheck, $CurSrcPath);
+					$Candidate = &Path_Norm($Candidate);
+				    
+					$Candidate =~ s/^\\//;	# remove leading \, if any	
+					$CurStringTable{BaseTrg}=&Path_Split('Base',$Candidate);
+					my $path=&Path_Split('Path',$Candidate);
+					if($path){
+					  $CurStringTable{STPath}=&Path_MakeAbs($CurSrcPath,&Path_Split('Path',$Candidate));
+					}
+					else {
+					  $CurStringTable{STPath}=$CurSrcPath;
+					}
+					$CurStringTable{STFile}=&Path_Split('File',$Candidate);
+
+				}
+				next LINE;
+			}
+			$OtherPlatSwitch="$CurFile($LineNum)";
+			next LINE;
+		}
+
+		if (/^AIF$/o) {
+			my ($trg, $dir, $file, $clDepth, @bitmaps)=@$Line;
+			unless ($file) { # bitmaps aren't essential
+				push @MmpDie, "$CurFile($LineNum) : Not enough arguments for keyword AIF\n";
+				next LINE;
+			}
+			my %Data;
+			$Data{Trg} = $trg;	# Defect: may include directory
+			$dir=~s-^(.*[^\\])$-$1\\-o;   # ensure path ends with a backslash
+			$Data{Source}=&Path_MakeAbs($CurFile, "$dir$file");
+			unless (-e "$Data{Source}") {
+				push @MmpWarn, "$CurFile($LineNum) : AIF source \"$Data{Source}\" not found\n";
+			}
+			if ($CheckAif{$Data{Trg}}) {
+				push @MmpDie, "$CurFile($LineNum) : Duplicate Aif \"$Data{Trg}\" at line $CheckAif{$Data{Trg}}\n";
+				next LINE;
+			}
+			$CheckAif{$Data{Trg}}="$CurFile($LineNum)";
+			push @AifStruct, \%Data;
+			next LINE unless (@bitmaps);
+			# handle bitmaps
+			my @ClDepths = split(/,/, $clDepth);
+			foreach (@ClDepths) {
+				$_ = lc $_; # bmconv can't handle upper-case 'C's
+				unless (/^c?\d\d?$/o) {
+					push @MmpDie, "$CurFile($LineNum) : AIF color depth \"$_\" - unexpected format\n";
+				}
+			}
+			@ClDepths = (@ClDepths) x @bitmaps;	# make a sufficiently long list of color depths
+			foreach $file (@bitmaps) {
+				if ($file !~ /^\\/) {
+					$file = &Path_MakeAbs($CurFile, "$dir$file");
+				}
+				push @{$Data{BitMaps}}, { # sources must be kept in order
+					Src=>$file,
+					ClDepth=>shift @ClDepths	# take next color depth from the list
+				};
+				unless (-e $file) {
+					push @MmpWarn, "$CurFile($LineNum) : AIF source \"$file\" not found\n";
+				}
+			}
+			next LINE;
+		}
+		if (/^ASSPABI$/o) {
+			if (@$Line) {
+				push @MmpWarn, "$CurFile($LineNum) : Keyword $_ takes no arguments\n";
+			}
+			$ASSPABISwitch=1;
+			next LINE;
+		}
+		if (/^ASSPEXPORTS$/o) {
+			if (@$Line) {
+				push @MmpWarn, "$CurFile($LineNum) : Keyword $_ takes no arguments\n";
+			}
+			$ASSPExports=1;
+			next LINE;
+		}
+		if (/^ASSPLIBRARY$/o) {
+			if (@$Line) {
+				my $Candidate;
+				foreach $Candidate (@$Line) {
+					CheckSource_MetaData(%CheckSourceMMPMetaData, $CurFile, $mainElement, $Candidate, $LineNum);
+					$Candidate = &Path_Norm($Candidate);
+					
+					unless ($emulator or &Genutl_ParseVersionedName(\$Candidate)) {
+						push @MmpWarn, "$CurFile($LineNum) : Bad version in ASSPLIBRARY\n";
+					}
+					if ($CheckASSPLib{$Candidate}) {
+						push @MmpWarn, "$CurFile($LineNum) : Duplicate Library \"$Candidate\" at line $CheckASSPLib{$Candidate}\n";
+						next; 
+					}
+					
+					push @ASSPLibList, $Candidate;
+					$CheckASSPLib{$Candidate}="$CurFile($LineNum)";
+				}
+				next LINE;
+			}
+			push @MmpWarn, "$CurFile($LineNum) : No Libraries specified for keyword ASSPLIBRARY\n";
+			next LINE;
+		}
+		if (/^CAPABILITY$/o) {
+			if (defined($Capability)) {
+				push @MmpWarn, "$CurFile($LineNum) : Attempt to redefine CAPABILITY\n";
+				next LINE;
+			}
+			if (scalar @$Line == 0) {
+				push @MmpWarn, "$CurFile($LineNum) : Wrong number of arguments for CAPABILITY\n";
+				next LINE;
+			}
+			foreach my $capname (@$Line) {
+				my $invert = 0;
+				if ($capname =~ /^-(\w*)/) {
+					$invert = 0xffffffff;
+					$capname = $1;
+				}
+				my $capFlag = 0;
+				if (defined($CapabilityNames{uc $capname})) {
+					$capFlag = $CapabilityNames{uc $capname};
+					if (not $capFlag) {
+						push @MmpDiagnostic, "$CurFile($LineNum) : Use of old capability name, \"$capname\" ignored\n";
+						next;
+					}
+					if ("ALL" eq uc $capname and $invert) {
+						push @MmpWarn, "Capability '-ALL' not allowed";
+						next;
+					}
+				}
+				elsif ("NONE" eq uc $capname) {
+					if($invert) {
+						push @MmpWarn, "Capability '-NONE' not allowed";
+						next;
+					}
+				}
+				else {
+					push @MmpWarn, "$CurFile($LineNum) : Unknown capability \"$capname\" ignored\n";
+					next;
+				}
+				# append name to capability string
+				if (defined($Capability) and not $invert) { $Capability .= "+"; }
+				if($invert) { $Capability .= "-"; }
+				$Capability .= $capname;
+
+				# add capability mask to flags
+				$CapabilityFlags[0] = $CapabilityFlags[0] ^ $invert;
+				$CapabilityFlags[0] = $CapabilityFlags[0] | $capFlag;
+				$CapabilityFlags[0] = $CapabilityFlags[0] ^ $invert;
+				$CapabilityFlags[1] = 0;
+				next;
+			}
+			next LINE;
+		}
+		if (/^DEBUGLIBRARY$/o) {
+			if (@$Line) {
+				my $Candidate;
+				foreach $Candidate (@$Line) {
+					CheckSource_MetaData(%CheckSourceMMPMetaData, $CurFile, $mainElement, $Candidate, $LineNum);
+					$Candidate = &Path_Norm($Candidate);
+					
+					unless ($emulator or &Genutl_ParseVersionedName(\$Candidate)) {
+						push @MmpWarn, "$CurFile($LineNum) : Bad version in DEBUGLIBRARY\n";
+					}
+					if ($CheckLib{$Candidate}) {
+						push @MmpWarn, "$CurFile($LineNum) : Duplicate Library \"$Candidate\" at line $CheckLib{$Candidate}\n";
+						next; 
+					}
+					
+					push @DebugLibList, $Candidate;
+					$CheckLib{$Candidate}="$CurFile($LineNum)";
+				}
+				next LINE;
+			}
+			push @MmpWarn, "$CurFile($LineNum) : No Libraries specified for keyword DEBUGLIBRARY\n";
+			next LINE;
+		}
+		if (/^DEFFILE$/o)  {
+			if ($CheckDef) {
+				push @MmpWarn, "$CurFile($LineNum) : Attempt to redefine DEFFILE\n";
+				next LINE;
+			}
+			$CheckDef=1;
+			unless ($_=shift @$Line) {
+				push @MmpWarn, "$CurFile($LineNum) : No file specified for keyword DEFFILE\n";
+				next LINE;
+			}
+
+			$Def{CheckSource_MMPEntry} = $_;		
+			$Def{CheckSource_LineNumber} = $LineNum;
+			$Def{CheckSource_MMPFile} = $CurFile;
+			
+			$_ = &Path_Norm($_);
+			
+			$Def{Base}=&Path_Split('Base',$_);
+			$Def{Ext}=&Path_Split('Ext',$_);
+			$Def{Path}=&Path_Split('Path',$_);
+			
+			if ($Def{Path}) {
+				$Def{Path}=&Path_MakeEAbs($EPOCPath,$CurFile,$Def{Path});
+			}
+
+			if ($Def{Base} =~ /\{|\}/) {
+				push @MmpDie, "$CurFile($LineNum) : Bad DEFFILE name\n";
+			}
+			next LINE;
+		}
+		if (/^DOCUMENT$/o) {
+			if (@$Line) {
+				my $Candidate;
+				foreach $Candidate (@$Line) {
+					CheckSource_MetaData(%CheckSourceMMPMetaData, $CurFile, $mainElement, $Candidate, $LineNum, $CheckSource_PhysicalCheck, $CurSrcPath);
+					$Candidate = &Path_Norm($Candidate);
+					
+					if ($CheckDoc{"$CurSrcPath$Candidate"}) {
+						push @MmpWarn, "$CurFile($LineNum) : Duplicate Document \"$CurSrcPath$Candidate\" at line ", $CheckDoc{"$CurSrcPath$Candidate"}, "\n";
+						next; 
+					}
+					unless (-e "$CurSrcPath$Candidate") {
+						push @MmpWarn, "$CurFile($LineNum) : DOCUMENT \"$CurSrcPath$Candidate\" not found\n";
+					}
+					
+					push @{$DocHash{$CurSrcPath}}, $Candidate;
+					$CheckDoc{"$CurSrcPath$Candidate"}="$CurFile($LineNum)";
+				}
+				next LINE;
+			}
+			push @MmpWarn, "$CurFile($LineNum) : No Files specified for keyword DOCUMENT\n";
+			next LINE;
+		}
+		if (/^EPOCALLOWDLLDATA$/o) {
+			if (@$Line) {
+				push @MmpWarn, "$CurFile($LineNum) : Keyword $_ takes no arguments\n";
+			}
+			$AllowDllData=1;
+			next LINE;
+		}
+		if (/^ALWAYS_BUILD_AS_ARM$/o) {
+			if (@$Line) {
+				push @MmpWarn, "$CurFile($LineNum) : Keyword $_ takes no arguments\n";
+			}
+			$BuildAsARM=1;
+			next LINE;
+		}
+		if (/^EPOCCALLDLLENTRYPOINTS$/o) {
+			if (@$Line) {
+				push @MmpWarn, "$CurFile($LineNum) : Keyword $_ takes no arguments\n";
+			}
+			$CallDllEntryPoints=1;
+			next LINE;
+		}
+		if (/^EPOCDATALINKADDRESS$/o) {
+			if (@$Line) { 
+				my $temp=&main::Genutl_AnyToHex(shift @$Line);
+				if (defined $temp) {
+					$DataLinkAddress=$temp;
+					next LINE;
+				}
+				push @MmpDie, "$CurFile($LineNum) : Data link address doesn't fit expected number format\n";
+			}
+			push @MmpWarn, "$CurFile($LineNum) : No data link address specified for keyword $_\n";
+			next LINE;
+		}
+		if (/^EPOCFIXEDPROCESS$/o) {
+			if (@$Line) {
+				push @MmpWarn, "$CurFile($LineNum) : Keyword $_ takes no arguments\n";
+			}
+			$FixedProcess=1;
+			next LINE;
+		}
+		if (/^EPOCHEAPSIZE$/o) {
+			if (@$Line) {
+				my $tempMin=&main::Genutl_AnyToHex(shift @$Line);
+				if (defined $tempMin) {
+					if (@$Line) {
+						my $tempMax=&main::Genutl_AnyToHex(shift @$Line);
+						if (defined $tempMax) {
+							$HeapSize{Min}=$tempMin;
+							$HeapSize{Max}=$tempMax;
+							next LINE;
+						}
+						push @MmpDie, "$CurFile($LineNum) : maximum heap size doesn't fit expected number format\n";
+						next LINE;
+					}
+					push @MmpDie, "$CurFile($LineNum) : No maximum heap size specified for keyword $_\n";
+					next LINE;
+				}
+				push @MmpDie, "$CurFile($LineNum) : minimum heap size doesn't fit expected number format\n";
+				next LINE;
+			}
+			push @MmpWarn, "$CurFile($LineNum) : No minimum heap size specified for keyword $_\n";
+			next LINE;
+		}
+		if (/^EPOCPROCESSPRIORITY$/o) {
+			if ($ProcessPriority) {
+				push @MmpWarn, "$CurFile($LineNum) : Attempt to redefine EPOCPROCESSPRIORITY\n";
+				next LINE;
+			}
+			if ($ProcessPriority=shift @$Line) {
+				if (defined($ProcessPriorityNames{uc $ProcessPriority})) {
+					$ProcessPriority = $ProcessPriorityNames{uc $ProcessPriority}; # canonical form
+					next LINE;
+				}
+				push @MmpDie, "$CurFile($LineNum) : ProcessPriority \"$ProcessPriority\" not supported\n";
+				next LINE;
+			}
+			push @MmpWarn, "$CurFile($LineNum) : No Priority specified for keyword EPOCPROCESSPRIORITY\n";
+			next LINE;
+		}
+		if (/^EPOCSTACKSIZE$/o) {
+			if (@$Line) {
+				my $temp=&main::Genutl_AnyToHex(shift @$Line);
+				if (defined $temp) {
+					$StackSize=$temp;
+					next LINE;
+				}
+				push @MmpDie, "$CurFile($LineNum) : Stack size doesn't fit expected number format\n";
+				next LINE;
+			}
+			push @MmpWarn, "$CurFile($LineNum) : No stack size specified for keyword STACKSIZE\n";
+			next LINE;
+		}
+		if (/^COMPRESSTARGET$/o) {
+			if (@$Line) {
+				push @MmpWarn, "$CurFile($LineNum) : Keyword $_ takes no arguments\n";
+			}
+			$CompressTarget=COMPRESS;
+            $CompressTargetMethod=NOCOMPRESSIONMETHOD; # means 'use default'
+			next LINE;
+		}
+		if (/^NOCOMPRESSTARGET$/o) {
+			if (@$Line) {
+				push @MmpWarn, "$CurFile($LineNum) : Keyword $_ takes no arguments\n";
+			}
+			$CompressTarget=NOCOMPRESS;
+            $CompressTargetMethod=NOCOMPRESSIONMETHOD;
+			next LINE;
+		}
+        if (/^INFLATECOMPRESSTARGET$/o) {
+			if (@$Line) {
+				push @MmpWarn, "$CurFile($LineNum) : Keyword $_ takes no arguments\n";
+			}
+            $CompressTarget=COMPRESS;
+			$CompressTargetMethod=INFLATECOMPRESSIONMETHOD;
+			next LINE;
+		}
+		if (/^BYTEPAIRCOMPRESSTARGET$/o) {
+			if (@$Line) {
+				push @MmpWarn, "$CurFile($LineNum) : Keyword $_ takes no arguments\n";
+			}
+            $CompressTarget=COMPRESS;
+			$CompressTargetMethod=BYTEPAIRCOMPRESSIONMETHOD;
+			next LINE;
+		}
+		if (/^EXPORTUNFROZEN$/o) {
+			if (@$Line) {
+				push @MmpWarn, "$CurFile($LineNum) : Keyword $_ takes no arguments\n";
+			}
+			$ExportUnfrozen=1;
+			next LINE;
+		}
+		if (/^FIRSTLIB$/o) {
+			if ($FirstLib) {
+				push @MmpWarn, "$CurFile($LineNum) : Attempt to redefine FIRSTLIB\n";
+				next LINE;
+			}
+			if ($FirstLib=shift @$Line) {
+				CheckSource_MetaData(%CheckSourceMMPMetaData, $CurFile, $mainElement, $FirstLib, $LineNum);
+				$FirstLib = &Path_Norm($FirstLib);
+				next LINE;
+			}
+			
+			push @MmpWarn, "$CurFile($LineNum) : Nothing specified for keyword FIRSTLIB\n";
+			next LINE;
+		}
+		if (/^LANG$/o) {
+			if (@$Line) {
+				my $Candidate;
+				foreach $Candidate (@$Line) {
+					if ($CheckLang{$Candidate}) {
+						push @MmpWarn, "$CurFile($LineNum) : Duplicate Language \"$Candidate\" at line $CheckLang{$Candidate}\n";
+						next; 
+					}
+					push @LangList, $Candidate;
+					$CheckLang{$Candidate}="$CurFile($LineNum)";
+				}
+				next LINE;
+			}
+			push @MmpWarn, "$CurFile($LineNum) : No Languages specified for keyword LANG\n";
+			next LINE;
+		}
+		if (/^LIBRARY$/o) {
+			if (@$Line) {
+				my $Candidate;
+				foreach $Candidate (@$Line) {
+					CheckSource_MetaData(%CheckSourceMMPMetaData, $CurFile, $mainElement, $Candidate, $LineNum);
+					$Candidate = &Path_Norm($Candidate);
+					
+					unless ($emulator or &Genutl_ParseVersionedName(\$Candidate)) {
+						push @MmpWarn, "$CurFile($LineNum) : Bad version in LIBRARY\n";
+					}
+					if ($CheckLib{$Candidate}) {
+						push @MmpWarn, "$CurFile($LineNum) : Duplicate Library \"$Candidate\" at line $CheckLib{$Candidate}\n";
+						next; 
+					}
+					
+					push @LibList, $Candidate;
+					push @DebugLibList, $Candidate;	    # appears in both
+					$CheckLib{$Candidate}="$CurFile($LineNum)";
+				}
+				next LINE;
+			}
+			push @MmpWarn, "$CurFile($LineNum) : No Libraries specified for keyword LIBRARY\n";
+			next LINE;
+		}
+		if (/^LINKAS$/o) {
+			if ($LinkAs) {
+				push @MmpWarn, "$CurFile($LineNum) : Attempt to redefine LINKAS\n";
+				next LINE;
+			}
+			if ($LinkAs=shift @$Line) {
+				next LINE;
+			}
+			push @MmpWarn, "$CurFile($LineNum) : No name specified for keyword LINKAS\n";
+			next LINE;
+		}
+		if (/^EXPORTLIBRARY$/o) {
+			if ($NoExportLibrary) {
+				push @MmpDie, "$CurFile($LineNum) : Can't specify both EXPORTLIBRARY and NOEXPORTLIBRARY\n";
+				next LINE;
+			}
+			if ($ExportLibrary) {
+				push @MmpWarn, "$CurFile($LineNum) : Attempt to redefine EXPORTLIBRARY\n";
+				next LINE;
+			}
+			if ($ExportLibrary=shift @$Line) {
+				CheckSource_MetaData(%CheckSourceMMPMetaData, $CurFile, $mainElement, $ExportLibrary, $LineNum);
+				$ExportLibrary = &Path_Norm($ExportLibrary);
+				$ExportLibrary=&Path_Split('Base',$ExportLibrary);
+				next LINE;
+			}
+			push @MmpWarn, "$CurFile($LineNum) : No name specified for keyword EXPORTLIBRARY\n";
+			next LINE;
+		}
+		if (/^NOEXPORTLIBRARY$/o) {
+			if ($ExportLibrary) {
+				push @MmpDie, "$CurFile($LineNum) : Can't specify both EXPORTLIBRARY and NOEXPORTLIBRARY\n";
+				next LINE;
+			}
+			$NoExportLibrary = 1;
+			next LINE;
+		}
+		if (/^NEWLIB$/o) {
+			if ($NewLib) {
+				push @MmpWarn, "$CurFile($LineNum) : Attempt to redefine NEWLIB\n";
+				next LINE;
+			}
+			if ($NewLib = shift @$Line) {
+				CheckSource_MetaData(%CheckSourceMMPMetaData, $CurFile, $mainElement, $NewLib, $LineNum);
+				$NewLib = &Path_Norm($NewLib);
+				next LINE;
+			}
+			push @MmpWarn, "$CurFile($LineNum) : No library specified for keyword NEWLIB\n";
+			next LINE;
+		}
+		if (/^MACRO$/o) {
+			if (@$Line) {
+				my $Candidate;
+				foreach $Candidate (@$Line) {
+					if ($CheckMacro{$Candidate}) {
+						push @MmpWarn, "$CurFile($LineNum) : Duplicate Macro \"$Candidate\" at line $CheckMacro{$Candidate}\n";
+						next; 
+					}
+					push @Macros, $Candidate;
+					$CheckMacro{$Candidate}="$CurFile($LineNum)";
+				}
+				next LINE;
+			}
+			next LINE; 
+		}
+		if (/^NOSTRICTDEF$/o) {
+			if ($NoStrictDef) {
+				push @MmpWarn, "$CurFile($LineNum) : NOSTRICTDEF already set\n";
+				next LINE;
+			}
+			if (@$Line) {
+				push @MmpWarn, "$CurFile($LineNum) : Keyword NOSTRICTDEF takes no arguments\n";
+			}
+			$NoStrictDef=1;
+			next LINE;
+		}
+		if (/^RAMTARGET$/o) {
+			if ($CheckRamTargets) {
+				push @MmpWarn, "$CurFile($LineNum) : RAM targets already specified at line $CheckRamTargets\n";
+				next LINE;
+			}
+			$CheckRamTargets="$CurFile($LineNum)";
+			unless (@$Line) {
+				push @MmpWarn, "$CurFile($LineNum) : No targets specified for keyword RAMTARGET\n";
+				next LINE;
+			}
+			if ($$Line[0] eq '+') {
+				my %Data=();
+				push @RamTargets, \%Data;	# include default
+				shift @$Line;
+			}
+			my $Elem;
+			foreach $Elem (@$Line) {
+				my %Data=();
+				$Data{File}=&Path_Split('File',$Elem);
+				$Data{Path}=&Path_Split('Path',$Elem);
+				push @RamTargets, \%Data;
+			}
+			next LINE;
+		}
+		if (/^RESOURCE$/o) {
+			if (@$Line) {
+				my $Candidate;
+				foreach $Candidate (@$Line) {			
+					CheckSource_MetaData(%CheckSourceMMPMetaData, $CurFile, $mainElement, $Candidate, $LineNum, $CheckSource_PhysicalCheck, $CurSrcPath);
+					$Candidate = &Path_Norm($Candidate);
+					
+					if ($CheckResrc{$Candidate}) {
+						push @MmpDie, "$CurFile($LineNum) : Duplicate Resource $Candidate at line $CheckResrc{$Candidate}\n";
+						next;
+					}
+					
+					$CheckResrc{$Candidate}="$CurFile($LineNum)";
+					my $source="$CurSrcPath$Candidate";
+					unless (-e $source) {
+						push @MmpWarn, "$CurFile($LineNum) : RESOURCE source \"$source\" not found\n";
+					}
+					
+					$CurResource{BaseTrg}=&Path_Split('Base',$Candidate);
+					$CurResource{Source}=$source;
+					$CurResource{Hdr}="$CurFile($LineNum)";
+					# $CurResource{TrgPath} will be filled in at the end;
+					my %Resource=%CurResource;
+					undef %CurResource;
+					push @ResourceStruct, \%Resource;
+				}
+				next LINE;
+			}
+			push @MmpWarn, "$CurFile($LineNum) : No Resources specified for keyword RESOURCE\n";
+			next LINE; 
+		}
+		if (/^ROMTARGET$/o) {
+			if ($CheckRomTargets) {
+				push @MmpWarn, "$CurFile($LineNum) : ROM targets already specified at line $CheckRomTargets\n";
+				next LINE;
+			}
+			$CheckRomTargets="$CurFile($LineNum)";
+			unless (@$Line) {
+				@RomTargets=();
+				next LINE;
+			}
+			if ($$Line[0] eq '+') {
+				shift @$Line;
+			}
+			else {
+				@RomTargets=(); # remove default
+			}
+			my $Elem;
+			foreach $Elem (@$Line) {
+				my %Data=();
+				$Data{File}=&Path_Split('File',$Elem);
+				$Data{Path}=&Path_Split('Path',$Elem);
+				push @RomTargets, \%Data;
+			}
+			next LINE;
+		}
+		if (/^SMPSAFE$/o) {
+			if (@$Line) {
+				push @MmpWarn, "$CurFile($LineNum) : Keyword $_ takes no arguments\n";
+			}
+			$SmpSafe = 1;
+			next LINE;
+		}
+		if (/^SOURCE$/o) {
+			if (@$Line) {
+				my $Candidate;
+				foreach $Candidate (@$Line) {
+					CheckSource_MetaData(%CheckSourceMMPMetaData, $CurFile, $mainElement, $Candidate, $LineNum, $CheckSource_PhysicalCheck, $CurSrcPath);
+					$Candidate = &Path_Norm($Candidate);
+					
+					$Candidate =~ s/^\\//;	# remove leading \, if any	
+					$CurSource{BaseTrg}=&Path_Split('Base',$Candidate);
+					my $path=&Path_Split('Path',$Candidate);
+					if($path){
+					  $CurSource{SrcPath}=&Path_MakeAbs($CurSrcPath,&Path_Split('Path',$Candidate));
+					}
+					else {
+					  $CurSource{SrcPath}=$CurSrcPath;
+					}
+					$CurSource{CurFile}=&Path_Split('File',$Candidate);
+
+					my %Source=%CurSource;
+					undef %CurSource;
+					push @SourceStruct, \%Source;
+					
+				}
+				next LINE;
+			}
+			push @MmpWarn, "$CurFile($LineNum) : No Sources specified for keyword SOURCE\n";
+			next LINE; 
+		}
+		if (/^SOURCEPATH$/o) {
+			if ($CurSrcPath=shift @$Line) {
+				CheckSource_MetaData(%CheckSourceMMPMetaData, $CurFile, $mainElement, $CurSrcPath, $LineNum, $CheckSource_PhysicalCheck);				
+				$CurSrcPath = &Path_Norm($CurSrcPath);
+				
+				$CurSrcPath=~s-^(.*[^\\])$-$1\\-o;	# in case no terminating backslash
+				$CurSrcPath=&Path_MakeEAbs($EPOCPath,$CurFile,$CurSrcPath);
+				if (-d &Path_Chop($CurSrcPath)) {
+					next LINE;
+				}
+				push @MmpWarn, "$CurFile($LineNum) : SOURCEPATH \"$CurSrcPath\" not found\n";
+				next LINE;
+			}
+			push @MmpDie, "$CurFile($LineNum) : No Source Path specified for keyword SOURCEPATH\n";
+			next LINE;
+		}
+		if (/^STATICLIBRARY$/o) {
+			if (@$Line) {
+				my $Candidate;
+				foreach $Candidate (@$Line) {
+					CheckSource_MetaData(%CheckSourceMMPMetaData, $CurFile, $mainElement, $Candidate, $LineNum);
+					$Candidate = &Path_Norm($Candidate);
+					
+					if ($CheckStatLib{$Candidate}) {
+						push @MmpWarn, "$CurFile($LineNum) : Duplicate Library \"$Candidate\" at line $CheckStatLib{$Candidate}\n";
+						next;
+					}
+					
+					push @StatLibList, $Candidate;
+					$CheckStatLib{$Candidate}="$CurFile($LineNum)";
+				}
+				next LINE;
+			}
+			push @MmpWarn, "$CurFile($LineNum) : No Libraries specified for keyword STATICLIBRARY\n";
+			next LINE;
+		}
+		if (/^STDCPP$/o) {
+			if (@$Line) {
+				push @MmpWarn, "$CurFile($LineNum) : Keyword $_ takes no arguments\n";
+			}
+			$StdCpp=1;
+			next LINE;
+		}
+  		if (/^NOSTDCPP$/o) {
+  			if (@$Line) {
+  				push @MmpWarn, "$CurFile($LineNum) : Keyword $_ takes no arguments\n";
+  			}
+  			$NoStdCpp=1;
+  			next LINE;
+  		}
+		if (/^STRICTDEPEND$/o) {
+			if ($MmpFlag{StrictDepend}) {
+				push @MmpWarn, "$CurFile($LineNum) : STRICTDEPEND already set\n";
+				next LINE;
+			}
+			if (@$Line) {
+				push @MmpWarn, "$CurFile($LineNum) : Keyword STRICTDEPEND takes no arguments\n";
+			}
+			$MmpFlag{StrictDepend}=1;
+			next LINE;
+		}
+		if (/^SYSTEMINCLUDE$/o){
+			if (@$Line) {
+				my $Candidate;
+				foreach $Candidate (@$Line) {
+					CheckSource_MetaData(%CheckSourceMMPMetaData, $CurFile, $mainElement, $Candidate, $LineNum, $CheckSource_PhysicalCheck);
+					$Candidate = &Path_Norm($Candidate);
+					
+					$Candidate=~s-^(.*[^\\])$-$1\\-o;   # ensure path ends with a backslash
+					$Candidate=&Path_MakeEAbs($EPOCPath,$CurFile,$Candidate);
+					if ($CheckSysInc{$Candidate}) {
+						next; 
+					}
+					push @SysIncPaths,$Candidate;
+					$CheckSysInc{$Candidate}="$CurFile($LineNum)";
+					if (-d &Path_Chop($Candidate)) {
+						next;
+					}
+					push @MmpWarn, "$CurFile($LineNum) : SYSTEMINCLUDE path \"$Candidate\" not found\n";
+				}
+				next LINE;
+			}
+			push @MmpWarn, "$CurFile($LineNum) : No Paths specified for keyword SYSTEMINCLUDE\n";
+			next LINE;
+		}
+		if (/^SYSTEMRESOURCE$/o) {
+			if (@$Line) {
+				my $Candidate;
+				foreach $Candidate (@$Line) {
+					CheckSource_MetaData(%CheckSourceMMPMetaData, $CurFile, $mainElement, $Candidate, $LineNum, $CheckSource_PhysicalCheck, $CurSrcPath);
+					$Candidate = &Path_Norm($Candidate);
+					
+					if ($CheckSysResrc{$Candidate}) {
+						push @MmpDie, "$CurFile($LineNum) : Duplicate Resource \"$Candidate\" at line $CheckSysResrc{$Candidate}\n";
+						next; 
+					}
+					$CheckSysResrc{$Candidate}="$CurFile($LineNum)";
+					my $source="$CurSrcPath$Candidate";
+					unless (-e $source) {
+						push @MmpWarn, "$CurFile($LineNum) : SYSTEMRESOURCE source \"$source\" not found\n";
+					}
+					$CurResource{BaseTrg}=&Path_Split('Base',$Candidate);
+					$CurResource{Source}=$source;
+					$CurResource{Hdr}="$CurFile($LineNum)";
+					$CurResource{TrgPath}="z\\system\\data\\";	# needs to match e32env.pm
+					my %Resource=%CurResource;
+					undef %CurResource;
+					push @ResourceStruct, \%Resource;
+				}
+				if (&main::EPOCSecurePlatform()) {    
+					push @MmpMigrationNote, "Obsolete SYSTEMRESOURCE keyword specified in \"$MMPFILE\"";
+				}
+				next LINE;
+			}
+			push @MmpWarn, "$CurFile($LineNum) : No Resources specified for keyword SYSTEMRESOURCE\n";
+			next LINE; 
+		}
+		if (/^TARGET$/o) {
+			if ($Trg) {
+				push @MmpWarn, "$CurFile($LineNum) : Attempt to redefine TARGET\n";
+				next LINE;
+			}
+			if ($Trg=shift @$Line) {
+				CheckSource_MetaData(%CheckSourceMMPMetaData, $CurFile, $mainElement, $Trg, $LineNum);
+				$Trg = &Path_Norm($Trg);
+				next LINE;
+			}
+			push @MmpWarn, "$CurFile($LineNum) : No Target specified for keyword TARGET\n";
+			next LINE;
+		}
+		if (/^TARGETPATH$/o) {
+			if ($TrgPath) {
+				push @MmpWarn, "$CurFile($LineNum) : Attempt to redefine TARGETPATH\n";
+				next LINE;
+			}
+			unless ($TrgPath=shift @$Line) {
+				push @MmpWarn, "$CurFile($LineNum) : No Path specified for keyword TARGETPATH\n";
+				next LINE;
+			}
+
+			CheckSource_MetaData(%CheckSourceMMPMetaData, $CurFile, $mainElement, $TrgPath, $LineNum);
+			$TrgPath = &Path_Norm($TrgPath);
+			
+			$TrgPath=~s-^\\(.*)$-$1-o;
+			$TrgPath=~s-^(.*[^\\])$-$1\\-o;
+			$TrgPath="z\\$TrgPath";
+			if (@$Line) {
+				push @MmpWarn, "$CurFile($LineNum) : Too many arguments for keyword TARGETPATH\n";
+			}
+			next LINE;
+		}
+		if (/^TARGETTYPE$/o) {
+			if ($TrgType) {
+				push @MmpWarn, "$CurFile($LineNum) : Attempt to redefine TARGETTYPE\n";
+				next LINE;
+			}
+			unless ($TrgType=shift @$Line) {
+				push @MmpWarn, "$CurFile($LineNum) : No Type specified for keyword TARGETTYPE\n";
+				next LINE;
+			}
+			eval { &Trg_GetL($TrgType, \%TrgType); };
+			if ($@) {
+				push @MmpDie, "$CurFile($LineNum) : $@";
+			}
+			next LINE;
+		}
+		if (/^UID$/o) {
+			if (@$Line) {
+				foreach (@$Line) {
+					if ($#UidList>=1) {
+						push @MmpWarn, "$CurFile($LineNum) : Can't specify more than 2 Uids\n";
+						next LINE;
+					}
+					$_=&Genutl_AnyToHex($_);
+					if (defined $_) {
+						push @UidList, $_;
+						next;
+					}
+					push @MmpDie, "$CurFile($LineNum) : Uid doesn't fit expected number format\n";
+					next LINE;
+				}
+				next LINE;
+			}
+			push @MmpWarn, "$CurFile($LineNum) : No Uids specified for keyword UID\n";
+			next LINE;
+		}
+		if (/^SECUREID$/o) {
+			if ($SecureId) {
+				push @MmpWarn, "$CurFile($LineNum) : Attempt to redefine SECUREID\n";
+				next LINE;
+			}
+			if (@$Line) {
+				$SecureId = &Genutl_AnyToHex(shift @$Line);
+				if (!defined $SecureId) {
+					push @MmpDie, "$CurFile($LineNum) : SECUREID doesn't fit expected number format\n";
+					next LINE;
+				}
+				if (@$Line) {
+					push @MmpWarn, "$CurFile($LineNum) : Too many arguments for keyword SECUREID\n";
+				}
+				next LINE;
+			}
+			push @MmpWarn, "$CurFile($LineNum) : Missing argument for keyword SECUREID\n";
+			next LINE;
+		}
+		if (/^VENDORID$/o) {
+			if (defined($VendorId)) {
+				push @MmpWarn, "$CurFile($LineNum) : Attempt to redefine VENDORID\n";
+				next LINE;
+			}
+			if (@$Line) {
+				$VendorId = &Genutl_AnyToHex(shift @$Line);
+				if (!defined $VendorId) {
+					push @MmpDie, "$CurFile($LineNum) : VENDORID doesn't fit expected number format\n";
+					next LINE;
+				}
+				if (@$Line) {
+					push @MmpWarn, "$CurFile($LineNum) : Too many arguments for keyword VENDORID\n";
+				}
+				next LINE;
+			}
+			push @MmpWarn, "$CurFile($LineNum) : Missing argument for keyword VENDORID\n";
+			next LINE;
+		}
+		if (/^USERINCLUDE$/o) {
+			if (@$Line) {
+				my $Candidate;
+				foreach $Candidate (@$Line) {
+					CheckSource_MetaData(%CheckSourceMMPMetaData, $CurFile, $mainElement, $Candidate, $LineNum, $CheckSource_PhysicalCheck);
+					$Candidate = &Path_Norm($Candidate);
+					
+					$Candidate=~s-^(.*[^\\])$-$1\\-o;   # ensure path ends with a backslash
+					$Candidate=&Path_MakeEAbs($EPOCPath,$CurFile,$Candidate);
+					if ($CheckUserInc{$Candidate}) {
+						next; 
+					}
+					push @UserIncPaths,$Candidate;
+					$CheckUserInc{$Candidate}="$CurFile($LineNum)";
+					if (-d &Path_Chop($Candidate)) {
+						next;
+					}
+					push @MmpWarn, "$CurFile($LineNum) : USERINCLUDE path \"$Candidate\" not found\n";
+				}
+				next LINE;
+			}
+			push @MmpWarn, "$CurFile($LineNum) : No Paths specified for keyword USERINCLUDE\n";
+			next LINE;
+		}
+		if (/^SRCDBG$/o) {
+			if (@$Line) {
+				push @MmpWarn, "$CurFile($LineNum) : Keyword $_ takes no arguments\n";
+			}
+			$SrcDbg=1;
+			next LINE;
+		}
+		if (/^VERSION$/o) {
+			if (%Version) {
+				push @MmpDie, "$CurFile($LineNum) : Attempt to redefine VERSION\n";
+				next LINE;
+			}
+			unless (@$Line) {
+				push @MmpDie, "$CurFile($LineNum) : Missing argument to VERSION\n";
+				next LINE;
+			}
+			%Version = &Genutl_StringToVersion(shift @$Line);
+			if (!%Version) {
+				push @MmpDie, "$CurFile($LineNum) : Bad VERSION number\n";
+				next LINE;
+			}
+			if (@$Line) {
+				if ((lc $$Line[0]) eq 'explicit') {
+					$Version{explicit} = 1;
+					shift @$Line;
+				}
+				if (scalar(@$Line)) {
+					push @MmpDie, "$CurFile($LineNum) : Garbage after VERSION number\n";
+				}
+			}
+			next LINE;
+		}
+		
+		if (/^OPTION$/oi ) {
+			if (@$Line >= 2) {
+				my $compilerkey= uc shift @$Line;
+				if (!defined($Compiler{$compilerkey})) {
+					# first use of "OPTION xxx"
+					$Compiler{$compilerkey}=shift @$Line;
+				}
+				# concatenate extra stuff
+				while (@$Line) {
+					$Compiler{$compilerkey}.=" ".shift @$Line;
+				}
+				next LINE;
+			}
+			push @MmpWarn, "$CurFile($LineNum) : Keyword OPTION requires at least two arguments\n";
+			next LINE;
+			}
+		if (/^LINKEROPTION$/oi ) {
+			if (@$Line >= 2) {
+			        # first parameter is the compiler
+				my $compilerkey= uc shift @$Line;
+				if (!defined($Compiler{$compilerkey})) {
+					# first use of "LINKEROPTION xxx"
+					$LinkerOptions{$compilerkey}=shift @$Line;
+				}
+				# concatenate extra stuff
+				while (@$Line) {
+					$LinkerOptions{$compilerkey}.=" ".shift @$Line;
+				}
+				next LINE;
+			}
+			push @MmpWarn, "$CurFile($LineNum) : Keyword LINKEROPTION requires at least two arguments\n";
+			next LINE;
+			}
+		if (/^OPTION_Replace$/oi ) {
+			if (@$Line >= 2) {
+				my $compilerkey= uc shift @$Line;
+				my $repOptions= shift @$Line;
+				while (@$Line) 
+				{
+					$repOptions.= " ".shift @$Line;
+				}
+				$repOptions =~ s/=\s*/=/g;
+				push @{$Replace{$compilerkey}},$repOptions;
+				
+				next LINE;
+			}
+			push @MmpWarn, "$CurFile($LineNum) : Keyword OPTION_Replace requires at least two arguments\n";
+			next LINE;
+			}
+		if (/^ARMFPU$/oi) {
+			if ($ARMFPU) {
+				push @MmpWarn, "$CurFile($LineNum) : Attempt to redefine ARMFPU\n";
+				next LINE;
+			}
+			if ($ARMFPU=shift @$Line) {
+				if (($ARMFPU !~ /^SOFTVFP$/oi) && ($ARMFPU !~ /^VFPV2$/oi)) {
+					push @MmpWarn, "$CurFile($LineNum) : ARMFPU can only specify SOFTVFP or VFPV2 as an argument\n";
+					undef $ARMFPU;
+				}
+				next LINE;
+			}
+			push @MmpWarn, "$CurFile($LineNum) : ARMFPU must specify either SOFTVFP or VFPV2 as an argument\n";
+			next LINE;
+		}
+		if( /^PAGED$/o) {
+			#revert "PAGED" keyword from code and data paged to code paged only
+			#if ($CodePagingTargetMode == PAGED or $DataPagingTargetMode == PAGED) {
+			if ($CodePagingTargetMode == PAGED) {
+				push @MmpWarn, "$CurFile($LineNum) : duplicate paging setting\n";
+			}
+			#revert "PAGED" keyword from code and data paged to code paged only
+			#if ($CodePagingTargetMode == UNPAGED or $DataPagingTargetMode == UNPAGED) {
+			if ($CodePagingTargetMode == UNPAGED) {
+				push @MmpWarn, "$CurFile($LineNum) : conflict paging setting\n";
+			}
+			$CodePagingTargetMode = PAGED;
+			#revert "PAGED" keyword from code and data paged to code paged only
+			#$DataPagingTargetMode = PAGED;
+			next LINE;
+			}
+		if( /^UNPAGED$/o) {
+			if ($CodePagingTargetMode == UNPAGED or $DataPagingTargetMode == UNPAGED) {
+				push @MmpWarn, "$CurFile($LineNum) : duplicate paging setting\n";
+			}
+			if ($CodePagingTargetMode == PAGED or $DataPagingTargetMode == PAGED) {
+				push @MmpWarn, "$CurFile($LineNum) : conflict paging setting\n";
+			}
+			$CodePagingTargetMode = UNPAGED;
+			$DataPagingTargetMode = UNPAGED;
+			next LINE;
+			}
+		if( /^PAGEDCODE$/o) {
+			if ($CodePagingTargetMode == PAGED) {
+				push @MmpWarn, "$CurFile($LineNum) : duplicate paging setting\n";
+			}
+			if ($CodePagingTargetMode == UNPAGED) {
+				push @MmpWarn, "$CurFile($LineNum) : conflict paging setting\n";
+			}
+			$CodePagingTargetMode = PAGED;
+			next LINE;
+			}
+		if( /^UNPAGEDCODE$/o) {
+			if ($CodePagingTargetMode == UNPAGED) {
+				push @MmpWarn, "$CurFile($LineNum) : duplicate paging setting\n";
+			}
+			if ($CodePagingTargetMode == PAGED) {
+				push @MmpWarn, "$CurFile($LineNum) : conflict paging setting\n";
+			}
+			$CodePagingTargetMode = UNPAGED;
+			next LINE;
+			}
+		if( /^PAGEDDATA$/o) {
+			if ($DataPagingTargetMode == PAGED) {
+				push @MmpWarn, "$CurFile($LineNum) : duplicate paging setting\n";
+			}
+			if ($DataPagingTargetMode == UNPAGED) {
+				push @MmpWarn, "$CurFile($LineNum) : conflict paging setting\n";
+			}
+			$DataPagingTargetMode = PAGED;
+			next LINE;
+			}
+		if( /^UNPAGEDDATA$/o) {
+			if ($DataPagingTargetMode == UNPAGED) {
+				push @MmpWarn, "$CurFile($LineNum) : duplicate paging setting\n";
+			}
+			if ($DataPagingTargetMode == PAGED) {
+				push @MmpWarn, "$CurFile($LineNum) : conflict paging setting\n";
+			}
+			$DataPagingTargetMode = UNPAGED;
+			next LINE;
+			}
+		if( /^DEBUGGABLE_UDEBONLY$/o) {
+			$IsDebuggable = DEBUGGABLE_UDEBONLY;
+			next LINE;
+			}
+		if( /^DEBUGGABLE$/o) {
+			if ($IsDebuggable != DEBUGGABLE_UDEBONLY)
+			{
+				$IsDebuggable = DEBUGGABLE;
+			}
+			next LINE;
+			}
+		if( /^FEATUREVARIANT$/o) {
+			push @MmpDie, "$CurFile($LineNum) : FEATUREVARIANT specified multiple times" if ($FeatureVariant);
+			$FeatureVariant = 1;
+			next LINE;
+			}
+
+		push @MmpWarn, "$CurFile($LineNum) : Unrecognised Keyword \"$_\"\n";
+	}
+
+	undef $Line;
+	undef $MmpMacro;
+
+
+	# test the mmp contents
+	#----------------------
+	if ($PlatTxtSwitch || $OtherPlatSwitch)	{
+		push @MmpDie, $PlatTxtSwitch ? $PlatTxtSwitch : $OtherPlatSwitch, ": Unterminated START ... END block\n";
+	}
+	undef $PlatTxtSwitch;
+	undef $OtherPlatSwitch;
+
+	# Check the consistency of the mmp file contents
+	unless ($Trg) {
+		unless($TrgType=~/^NONE$/io){
+			push @MmpDie, "ERROR: No Target specified\n";
+		}
+	}
+	unless ($TrgType) {
+		push @MmpDie, "ERROR: No TargetType specified\n";
+	}
+  	if( $StdCpp && $NoStdCpp ) {
+  		push @MmpWarn, "WARNING: Keyword statement STDCPP not permitted with keyword NOSTDCPP.\n";
+  		push @MmpWarn, "WARNING: Keyword statements STDCPP and NOSTDCPP ignored.\n";
+  		$StdCpp = 0;
+  		$NoStdCpp = 0;
+  	}
+
+	if (($TrgType =~ /^LIB$/io) && (scalar(@DebugLibList) || scalar(@LibList) || scalar(@StatLibList))) {
+		push @MmpWarn, "WARNING: Library statements (DEBUGLIBRARY, LIBRARY or STATICLIBRARY) not permitted with TARGETTYPE LIB.";
+		push @MmpWarn, "WARNING: Library statements (DEBUGLIBRARY, LIBRARY or STATICLIBRARY) ignored.\n";
+		undef @DebugLibList;
+		undef @LibList;
+		undef @StatLibList;
+	}
+
+	if ($TrgType =~ /^LIB$/io && $NewLib) {
+		push @MmpWarn, "WARNING: Library statement NEWLIB not permitted with TARGETTYPE LIB.";
+		push @MmpWarn, "WARNING: Library statement NEWLIB ignored.\n";
+		undef @DebugLibList;
+		undef @LibList;
+		undef @StatLibList;
+	}
+
+	if ($TrgType =~ /^STD(EXE|DLL)$/io && $NewLib) {
+		push @MmpWarn, "WARNING: Library statement NEWLIB not permitted with TARGETTYPE STD*.";
+		push @MmpWarn, "WARNING: Library statement NEWLIB ignored.\n";
+		undef @DebugLibList;
+		undef @LibList;
+		undef @StatLibList;
+	}
+
+	if (($TrgType=~/^IMPLIB$/io) || ($TrgType=~/^NONE$/io)) {
+		push @MmpDie, "ERROR: SOURCE not permitted with TARGETTYPE $TrgType\n" if (@SourceStruct);
+	} elsif (!@SourceStruct) {
+		push @MmpDie, "ERROR: No Sources specified\n";
+	}
+	if ($TrgType{NeedUID3} && $#UidList<1) {
+		push @MmpWarn, "WARNING: No Uid3 specified in \"$MMPFILE\" for TargetType \"$TrgType\"\n";
+	}
+	if ($Trg && $Trg=~/\{|\}/) {
+		push @MmpDie, "ERROR: Bad TARGET name specified\n";
+	}
+	if ($ExportLibrary and $ExportLibrary=~/\{|\}/) {
+		push @MmpDie, "ERROR: Bad EXPORTLIBRARY name specified\n";
+	}
+	if ($LinkAs and $LinkAs=~/\{|\}/) {
+		push @MmpDie, "ERROR: Bad LINKAS name specified\n";
+	}
+	# Make sure a valid compression method has been chosen if target is going to be paged
+	if ($CodePagingTargetMode == PAGED && $CompressTarget == COMPRESS) {
+		if ($CompressTargetMethod == INFLATECOMPRESSIONMETHOD) {
+			push @MmpWarn, "Incompatible keywords! The INFLATECOMPRESSTARGET is not allowed with PAGED!\n";
+			push @MmpWarn, "Changing compression method to BYTEPAIRCOMPRESSTARGET\n";
+		}
+		$CompressTargetMethod = BYTEPAIRCOMPRESSIONMETHOD;
+	}
+
+  # Ensure 2nd UID is not deprecated
+  if (&main::EPOCSecurePlatform() && $TrgType{Basic} eq "DLL")
+    {
+    $deprecatedUIDs{"0x100039ce"} = "Unmigrated dll style application detected from use of UID 0x100039ce";
+    }
+  
+  # Do not need UIDs for a resource only (i.e. TARGETTYPE is "none") mmp file
+  if  ((!$TrgType=~/^NONE$/io) && &main::EPOCSecurePlatform() && defined($deprecatedUIDs{$UidList[0]}))
+    {
+    push @MmpMigrationNote, "$deprecatedUIDs{$UidList[0]}\n";
+    }
+
+#	PUT IN SOME DEFAULTS
+
+	if ($TrgPath eq '') {
+		# set up default path from $TrgType 
+		$TrgPath = $TrgType{Path};
+	}
+	my $ResourceTrgPath = $TrgType{ResourcePath};
+	if ($ResourceTrgPath eq '') {
+		$ResourceTrgPath = $TrgPath;	# default to TrgPath, as before
+	}
+	unless (%Version) {
+	    if ($$Plat_ref{DefFile} =~ /^\s*EABI\s*/i ) {
+			$Version{major} = 10;	# Start major versions at 10 for EABI to allow coexistence with GCC98r2 ABI
+		} else {
+			$Version{major} = 1;
+		}
+		$Version{minor} = 0;
+	}
+
+	# check for languages
+	@LangList=('SC') unless @LangList;
+
+  if (&main::EPOCSecurePlatform() && !defined($Capability) &&
+    (($TrgType{Basic} eq "DLL") || (uc($TrgType) eq "EXEXP")))
+    {
+    push @MmpMigrationNote, "No Capabilities set in \"$MMPFILE\" for TargetType: $TrgType\n";
+    }
+
+	# Supply default capability mask if not set explicitly, and convert to hex string
+	$Capability = "none" unless (defined($Capability));
+	$CapabilityFlags[0] = 0 unless (defined($CapabilityFlags[0]));
+        $CapabilityFlags[1] = 0 unless (defined($CapabilityFlags[1]));
+
+        $CapabilityFlags[0] = sprintf("0x%08x", $CapabilityFlags[0]);
+        $CapabilityFlags[1] = sprintf("0x%08x", $CapabilityFlags[1]);
+
+#	ensure all bitmaps have targetpaths and check for duplicate bitmaps
+	my %BitMapCheck;
+	my $BitMapRef;
+	foreach $BitMapRef (@BitMapStruct) {
+		unless ($$BitMapRef{TrgPath}) {
+			$$BitMapRef{TrgPath}=$ResourceTrgPath;
+		}
+		if ($BitMapCheck{"$$BitMapRef{TrgPath}$$BitMapRef{Trg}"}) {
+			push @MmpDie, "ERROR: Duplicate bitmap target \"$$BitMapRef{TrgPath}$$BitMapRef{Trg}\"\n";
+			next;
+		}
+		$BitMapCheck{"$$BitMapRef{TrgPath}$$BitMapRef{Trg}"}=1;
+	}
+
+#	ensure all resources have targetpaths, expand language list and check for duplicates
+	my %ResourceCheck;
+	my $ResourceRef;
+	my @PerLanguageResourceStruct;
+	foreach $ResourceRef (@ResourceStruct) {
+					
+		unless ($$ResourceRef{BaseTrg}) {
+			$$ResourceRef{BaseTrg}=&Path_Split('Base',$$ResourceRef{Source});
+		}
+		unless ($$ResourceRef{TrgPath}) {
+			$$ResourceRef{TrgPath}=$ResourceTrgPath;
+		}
+		unless ($$ResourceRef{Lang}) {
+			@{$$ResourceRef{Lang}}=@LangList;
+		}
+		# generate one instance per language.
+		my @list = @{$$ResourceRef{Lang}};
+		my $base = "$$ResourceRef{TrgPath}$$ResourceRef{BaseTrg}";
+		foreach my $lang (@list) {
+			my %newResourceRef = %{$ResourceRef};
+			$newResourceRef{Lang} = $lang;
+			$newResourceRef{Trg} = $base.lc("\.R$lang");
+			push @PerLanguageResourceStruct, \%newResourceRef;
+		}
+	}
+	@ResourceStruct = @PerLanguageResourceStruct;
+	undef @PerLanguageResourceStruct;
+	
+	foreach $ResourceRef (@ResourceStruct) {
+		if ($ResourceCheck{"$$ResourceRef{TrgPath}$$ResourceRef{Trg}"}) {
+			push @MmpDie, "ERROR: Duplicate Resource target \"$$ResourceRef{TrgPath}$$ResourceRef{Trg}\"\n";
+			next;
+		}
+		$ResourceCheck{"$$ResourceRef{TrgPath}$$ResourceRef{Trg}"}=1;
+	}
+
+	my $Plat=&main::Plat;
+
+	if (@MmpDie || @MmpWarn || @MmpDiagnostic || @MmpMigrationNote) {
+		warn "\nMMPFILE \"$MMPFILE\"\n";
+		if (@MmpDiagnostic) {
+			warn
+				"DIAGNOSTIC MESSAGE(S)\n",
+				@MmpDiagnostic,
+				"\n"
+			;
+			}
+
+		foreach my $printedWarning (@MmpWarn)
+			{
+			print "WARNING: $printedWarning\n";
+			}
+		foreach my $printedError (@MmpDie)
+			{
+			print "FATAL ERROR: $printedError\n";
+			}
+
+		foreach my $printedMigrationNote (@MmpMigrationNote)
+			{
+			print "MIGRATION_NOTE: $printedMigrationNote\n";
+			}		
+
+		if (@MmpDie)
+			{
+			die;
+			}
+	}
+
+
+#	COMPLETE THE UID LIST
+
+	while ($#UidList<1) {
+		push @UidList, '0x00000000';
+	}
+
+	# check the second UID, or set it
+	if ($TrgType{UID2}) {
+		if ($UidList[0]=~/^0x00000000$/o) {
+			# put in second uid for known targetypes without them
+			$UidList[0]=$TrgType{UID2};
+		}
+		elsif ($UidList[0] ne $TrgType{UID2}) {
+			# text comparison is OK here because UIDs have both been through AnyToHex function
+			if ($UidList[0] ne '0x01111111') {
+				# issue warning (but not if UID == 0x01111111 (to allow test code to deliberately set incorrect UID)
+				warn(
+					"WARNING: Second Uid is $UidList[0], but\n",
+					" expected value for Target Type $TrgType is $TrgType{UID2}\n"
+				);
+			}
+		}
+	}
+
+#	Put in the first UID in the list
+	if ($TrgType{Basic}=~/^DLL$/o) {
+		unshift @UidList, '0x10000079';
+	}
+	elsif ($TrgType{Basic}=~/^(EXE)$/o) {
+		unshift @UidList, '0x1000007a';
+	}
+	elsif ($TrgType{Basic}=~/^(EXEDLL)$/o) {
+# EXE on EPOC32, DLL on Emulator
+# NOTE: On EKA1 EXEDLL used EXE UID1 on emulator
+# On EKA2 this is unacceptable
+		if (!$emulator) {
+			unshift @UidList, '0x1000007a';
+		} else {
+			unshift @UidList, '0x10000079';
+		}
+	}
+	else {
+		unshift @UidList, '0x00000000';
+	}
+
+# If no SID specified use UID3
+	$SecureId = $UidList[2] unless(defined $SecureId);
+
+#	SORT OUT TARGET TYPE DATA STRUCTURE
+
+	# set the target path
+	if ($TrgPath) {
+		$TrgType{Path}=$TrgPath;
+	}
+
+#	put in the $Linkas default
+	if (!$LinkAs and $TrgType=~/^IMPLIB$/io) {
+		$LinkAs = $Trg;
+		$LinkAs =~ s/\.lib$/\.dll/i;
+	} else {
+		$LinkAs = $Trg unless $LinkAs;
+	}
+	$LinkAsBase = $LinkAs;
+	unless ($emulator) {
+		$LinkAs = &DecorateWithVersion($LinkAs, %Version);
+	}
+
+# If explicit version add to target
+	if ($Version{explicit} && !$emulator) {
+		$Trg = &DecorateWithVersion($Trg, %Version);
+	}
+
+
+#	Reconcile any EXEDLL targettypes - must be done after first UIDs put in the list
+#	and after the $LinkAs keyword has been defined/defaulted
+	if ($TrgType{Basic} eq 'EXEDLL') {
+		$TrgType{Basic} = $emulator ? 'DLL' : 'EXE';
+		$Trg=&Path_Split('Base',$Trg).'.EXE';
+		$LinkAsBase=&Path_Split('Base',$LinkAsBase).'.EXE';
+		$LinkAs=&Path_Split('Base',$LinkAs).'.EXE';
+	}
+
+#	put in the $ExportLibrary default
+	$ExportLibrary=&Path_Split('Base',$Trg) unless $ExportLibrary;
+	unless ($emulator) {
+		$ExportLibrary = &DecorateWithVersion($ExportLibrary, %Version);
+	}
+
+#	Get the EPOC entrypoint static library
+	unless ($FirstLib) {
+		unless ($TrgType{FirstLib}) {
+			$FirstLib="E$TrgType{Basic}.LIB";
+		}
+		else {
+			$FirstLib=$TrgType{FirstLib};
+		}
+	}
+
+
+#	 WORK OUT THE ASSP IMPLICATIONS
+
+#	Nullify ASSP-specific API things for WINS/WINC since the API should always be
+#	the same for WINC as for WINS,
+	if ($emulator) {
+		$ASSPABISwitch=0;
+		$ASSPExports=0;
+		unshift @LibList, @ASSPLibList;
+		unshift @DebugLibList, @ASSPLibList;	# keep DebugLibList in sync with LibList
+		@ASSPLibList=();
+	}
+	else {
+#		Force ASSPABISwitch for known kernel components or if ASSPEXPORTS or ASSPLIBRARY specified in .MMP file
+		if ($TrgType{Kernel} or $ASSPExports or @ASSPLibList) {
+			$ASSPABISwitch=1;
+		}
+	}
+
+#	Switch the ABI if necessary
+	unless ($ASSPABISwitch) {
+#		apply the standard ABI
+		$ABI=$$Plat_ref{ABI};
+	}
+	else {
+#		kernel-specific stuff
+#		check that the platform is not generic
+		if ($$Plat_ref{Generic}) {
+			die "ERROR: Can't apply ASSPABI, ASSPEXPORTS or ASSPLIBRARY for generic platform \"$$Plat_ref{Name}\n";
+		}
+
+#		apply the ASSP-specific ABI
+		$ABI=$$Plat_ref{ASSPABI};
+	}
+
+#	COMPLETE THE .DEF FILE STRUCTURE
+
+	# apply the default .DEF filename, and
+	# check as far as possible that the project is frozen
+	if (($TrgType{NeedDeffile} or $CheckDef)) {
+		unless ($Def{Path} and $Def{Path} !~ /\\\~\\$/) {
+			my $augment;
+			if ($ASSPExports) {
+			    if ($$Plat_ref{DefFile} =~ /^\s*EABI\s*/i ) {
+					$augment = $$Plat_ref{ASSP};
+			    } else {
+					$augment = "B$$Plat_ref{ASSP}";
+			    }
+			} else {
+			    if ($$Plat_ref{DefFile} =~ /^\s*EABI\s*/i ) {
+					$augment = lc($$Plat_ref{DefFile});
+			    } else {
+					$augment = lc("B$$Plat_ref{DefFile}");
+			    }
+			}
+			if ($Def{Path} =~ /\\\~\\$/) {
+				$Def{Path} =~ s/\\\~\\$/\\$augment\\/;
+			} else {
+				$Def{Path}=&Path_Strip(&Path_Split('Path',$MMPFILE)."..\\$augment\\");
+			}
+		}
+		unless ($Def{Base}) {
+			$Def{Base} = &Path_Split('Base',$LinkAsBase);
+		}
+		unless ($Def{Ext}) {
+			$Def{Ext}='.def';
+		}
+#		now that we've dumped narrow, treat the 'u' basename suffix as part of the frozen
+#		.DEF file basename.  Once we've dumped the suffix we won't have to store the extension
+#		separately either
+		if (!$emulator && $Version{explicit}) {
+			$Def{Base} .= &Genutl_VersionToFileAugment(%Version);
+		} elsif (!$NoStrictDef) {
+			$Def{Base}.='u';
+		}
+
+		if ($Def{CheckSource_MMPEntry})
+		{
+			my $type = "DEFFILE";
+			$type .= " (NOSTRICTDEF)" if ($NoStrictDef);
+			
+			# for GCCXML and X86GCC, ignore check source errors for missing .def file
+			my $checkdef = (($IgnoreMissingDef) && ($Def{Path} =~ /[\\\/]bmarm[\\\/]/ || $$Plat_ref{Name} eq "X86GCC" || $$Plat_ref{Name} eq "X86GMP")) ? 0 : 1;
+			
+			if( $checkdef ) {
+			  CheckSource_MetaData(%CheckSourceMMPMetaData, $Def{CheckSource_MMPFile}, $type, $Def{CheckSource_MMPEntry}, $Def{CheckSource_LineNumber}, $CheckSource_PhysicalCheck, $Def{Path});
+			}
+		}
+			
+#		check deffile exists,
+        unless (-e "$Def{Path}$Def{Base}$Def{Ext}") {
+            if($IgnoreMissingDef == 0) {
+                warn "WARNING: Frozen .def file $Def{Path}$Def{Base}$Def{Ext} not found - project not frozen\n";
+            }
+     	}
+	}
+}
+
+sub Mmp_ABI () {
+	$ABI;
+}
+sub Mmp_AifStruct () {
+	\@AifStruct;
+}
+sub Mmp_AllowDllData () {
+	$AllowDllData;
+}
+sub Mmp_CompressTarget () {
+	$CompressTarget;
+}
+sub Mmp_CompressTargetMode () {
+	$CompressTargetMethod;
+}
+sub Mmp_ASSPExports () {
+	$ASSPExports;
+}
+sub Mmp_ASSPLibList () {
+	@ASSPLibList;
+}
+sub Mmp_BitMapStruct () {
+	\@BitMapStruct;
+}
+sub Mmp_BuildAsARM () {
+	$BuildAsARM;
+}
+sub Mmp_CallDllEntryPoints () {
+	$CallDllEntryPoints;
+}
+sub Mmp_Capability () {
+	$Capability;
+}
+sub Mmp_CapabilityFlags () {
+	@CapabilityFlags;
+}
+sub Mmp_DataLinkAddress () {
+	$DataLinkAddress;
+}
+sub Mmp_DebugLibList () {
+	\@DebugLibList;
+}
+sub Mmp_Def () {
+	\%Def;
+}
+sub Mmp_DocHash () {
+	\%DocHash;
+}
+sub Mmp_ExportUnfrozen () {
+	$ExportUnfrozen;
+}
+sub Mmp_FirstLib () {
+	$FirstLib;
+}
+sub Mmp_FixedProcess () {
+	$FixedProcess;
+}
+sub Mmp_HeapSize () {
+	\%HeapSize;
+}
+sub Mmp_LibList () {
+	\@LibList;
+}
+sub Mmp_LinkAs () {
+	$LinkAs;
+}
+sub Mmp_LinkAsBase () {
+	$LinkAsBase;
+}
+sub Mmp_ExportLibrary () {
+	$ExportLibrary;
+}
+sub Mmp_NoExportLibrary () {
+	$NoExportLibrary;
+}
+sub Mmp_NewLib () {
+	$NewLib;
+}
+sub Mmp_Macros () {
+	@Macros;
+}
+sub Mmp_MmpFlag () {
+	\%MmpFlag;
+}
+sub	Mmp_PlatTxt2D () {
+	@PlatTxt2D;
+}
+sub Mmp_ProcessPriority () {
+	$ProcessPriority;
+}
+sub Mmp_RamTargets () {
+	@RamTargets;
+}
+sub Mmp_ResourceStruct () {
+	\@ResourceStruct;
+}
+sub Mmp_RomTargets () {
+	@RomTargets;
+}
+sub Mmp_SourceStruct () {
+	\@SourceStruct;
+}
+sub Mmp_StackSize () {
+	$StackSize;
+}
+sub Mmp_StatLibList () {
+	@StatLibList;
+}
+sub Mmp_StdCpp () {
+	$StdCpp;
+}
+sub Mmp_NoStdCpp () {
+	$NoStdCpp;
+}
+sub Mmp_SysIncPaths () {
+	@SysIncPaths;
+}
+sub Mmp_Trg () {
+	$Trg;
+}
+sub Mmp_TrgType () {
+	\%TrgType;
+}
+sub Mmp_UidList () {
+	@UidList;
+}
+sub Mmp_UserIncPaths () {
+	@UserIncPaths;
+}
+sub Mmp_SrcDbg () {
+	$SrcDbg;
+}
+
+sub Mmp_WarningLevel() {
+     %Compiler; 
+} 
+
+sub Mmp_LinkerOptions() {
+    %LinkerOptions
+}
+
+sub Mmp_Version() {
+	%Version;
+}
+
+sub Mmp_SecureId() {
+	$SecureId;
+}
+
+sub Mmp_VendorId () {
+	$VendorId;
+}
+
+sub DecorateWithVersion($$) {
+	my ($name, %ver) = @_;
+	my $base = &Path_Split('Base', $name);
+	my $ext = &Path_Split('Ext', $name);
+	unless ($base =~ /\{(\d|a|b|c|d|e|f){8}\}$/i) {
+		$base .= &Genutl_VersionToFileAugment(%Version);
+	}
+	return "$base$ext";
+}
+
+sub Mmp_Replace() {
+	%Replace;
+}
+
+sub Mmp_SmpSafe() {
+	$SmpSafe;
+}
+
+sub Mmp_StringTable() {
+	\@StringTableStruct;
+}
+sub Mmp_ARMFPU() {
+	$ARMFPU;
+}
+
+sub Mmp_CheckSourceMMPMetaData() {
+	%CheckSourceMMPMetaData;
+}
+
+sub Mmp_CheckSourceMMPIncludes() {
+	%CheckSourceMMPIncludes;
+}
+
+sub Mmp_CodePagingTargetMode() {
+	$CodePagingTargetMode;
+}
+
+sub Mmp_DataPagingTargetMode() {
+	$DataPagingTargetMode;
+}
+
+sub Mmp_IsWideCharMain() {
+	$WideCharMain;
+}
+
+sub Mmp_IsDebuggable() {
+	$IsDebuggable;
+}
+
+sub Mmp_IsFeatureVariant() {
+	$FeatureVariant;
+}
+
+# Return the macros tested in the MMP file
+sub Mmp_TestedMacros() {
+	return \%mmptestedMacrosHash;
+}
+
+1;