sbsv1_os/e32toolp/makmake/mmp.pm
author Mike Kinghan <mikek@symbian.org>
Tue, 16 Nov 2010 14:32:12 +0000
branchGCC_SURGE
changeset 79 f7dee603db09
parent 42 f532653c45c0
permissions -rw-r--r--
[GCCE] We need a way for the HAL config extension to parameterise the HAL config file (.hcf) that will be used, depending upon the toolchain we are building with. E.g. if we are building BeagleBoard with RVCT we can configure hardware floating point because we have ARM's vfp math libraries; if we are building it with GCC, we lack this library support.

# 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 strict;

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;