sbsv1/abld/e32util/fixsource.bat
author Mike Kinghan <mikek@symbian.org>
Thu, 25 Nov 2010 13:59:07 +0000
changeset 40 68f68128601f
permissions -rw-r--r--
1) Add the sbsv1 components from sftools/dev/build to make the linux_build package independent of the obsolete buildtools package. 2) Enhance romnibus.pl so that it generate the symbol file for the built rom when invoked by Raptor 3) Make the maksym.pl tool portable for Linux as well as Windows. 4) Remove the of armasm2as.pl from the e32tools component in favour of the copy now exported from sbsv1/e32util.

@rem
@rem Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
@rem All rights reserved.
@rem This component and the accompanying materials are made available
@rem under the terms of "Eclipse Public License v1.0"
@rem which accompanies this distribution, and is available
@rem at the URL "http://www.eclipse.org/legal/epl-v10.html".
@rem
@rem Initial Contributors:
@rem Nokia Corporation - initial contribution.
@rem
@rem Contributors:
@rem
@rem Description:
@rem
@rem = '--*-Perl-*--
@echo off
if "%OS%" == "Windows_NT" goto WinNT
perl -x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9
goto endofperl
:WinNT
perl -x -S "%0" %*
if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" goto endofperl
if %errorlevel% == 9009 echo You do not have Perl in your PATH.
goto endofperl
@rem ';
#!perl
#line 14


use Getopt::Long;
use File::Spec;
use File::Copy;
use File::Path;
use File::Basename;

my $toolVersion = "2.1";

# Example warnings:
#
# Incorrect slash
#	\src\common\generic\comms-infras\commsfw\group\bld.inf:10: Incorrect slash in PRJ_EXPORTS - '..\inc\commschan.h'.
#	\src\cedar\generic\base\f32\group\ecomp.mmp:14: Incorrect slash in SYSTEMINCLUDE - '..\inc'.
#	\src\common\generic\syncml\framework\TransportProvision\HttpWsp\SmlHttpBase.cpp:13: Incorrect slash in #include - 'http\rhttpheaders.h'.
#
# Incorrect case for epoc32 tree
#	\src\common\generic\syslibs\pwrcli\group\bld.inf:23: Incorrect case for epoc32 tree in PRJ_EXPORTS - '\epoc32\rom\include\PwrCli.IBY'.
#	\src\common\generic\security\crypto\group\hash.mmp:8: Incorrect case for epoc32 tree in TARGET - 'hash.DLL'.
#	\src\common\generic\syslibs\ecom\ongoing\Framework\frame\Discoverer.cpp:20: Incorrect case for epoc32 tree in #include - 'BaSPI.h'.
#
# Incorrect case versus filesystem
#	\src\common\generic\comms-infras\commdb\commdbshim\group\BLD.INF:20: Incorrect case versus filesystem in PRJ_EXPORTS - '..\inc\cdblen.h' vs. \src\common\generic\comms-infras\commdb\commdbshim\INC\CDBLEN.H.
#	\src\common\generic\syslibs\ecom\ongoing\Framework\MMPFiles\EComServer.mmp:45: Incorrect case versus filesystem in USERINCLUDE - '..\..\framework\inc' vs. \src\common\generic\syslibs\ecom\ongoing\Framework\inc.
#	\src\common\generic\comms-infras\commdb\commdbshim\INC\CDBOVER.H:16: Incorrect case versus filesystem in #include - 'cdbpreftable.h' vs. \src\common\generic\comms-infras\commdb\commdbshim\INC\CDBPREFTABLE.H.
#
# Incorrect case for epoc32 tree from default export (i.e. source export case will replicated under \epoc32)
#	\src\common\generic\application-protocols\http\group\bld.inf:22: Incorrect case for epoc32 tree from default export in PRJ_EXPORTS - '..\inc\HTTPSocketConstants.h'.
#	\src\common\generic\messaging\email\smtpservermtm\group\bld.inf:11: Incorrect case for epoc32 tree from default export in PRJ_EXPORTS - '..\inc\smts.h' vs. \src\common\generic\messaging\email\smtpservermtm\inc\SMTS.H.
#
# Incorrect case versus exclusion list
#	\src\common\generic\Multimedia\openmax\group\bld.inf:14: Incorrect case versus exclusion list in PRJ_EXPORTS - '\epoc32\include\openmax\il\OMX_Types.h' vs. OMX_TYPES.H.
#	\src\common\generic\Multimedia\openmax\inc\openmax\il\OMX_Audio.h:41: Incorrect case versus exclusion list in #include - 'OMX_Types.h' vs. OMX_TYPES.H.
#
# Can't find physical file match for
#	\src\common\generic\app-framework\conarc\group\BLD.INF:48: Can't find physical file match for PRJ_TESTMMPFILES MMP - '..\tsrc\tcon3_V2.mpp' on filesystem.
#


# 1. Check arguments, output help etc.

my $analyse = 0;
my $list = 0;
my $update = 0;
my $warnings = "";
my $verbose = 0;
my $debug = 0;
my $debugUpdate = 0;
GetOptions ('analyse|a' => \$analyse, 'list|l' => \$list, 'update|u' => \$update, 'warnings|w=s' => \$warnings,
			'verbose|v' => \$verbose, 'debug|d' => \$debug, 'debugupdate|du' => \$debugUpdate);

if (@ARGV == 0)
	{
	print (STDERR "\nFIXSOURCE.BAT - Version $toolVersion\n");

	print STDERR << 'END_OF_HELP';

Usage: fixsource.bat -analyse|-list|-update|-warnings buildlog_1.log [buildlog_n.log] 

Parses the output from the specified build logs to locate warnings produced via
"abld -checksource".  Provides the option to update source automatically to comply
with Symbian's Filename Policy.

-analyse | -a	             List and describe all warnings that cannot be addressed
                             by this tool.
-list    | -l	             List all source files that can be updated by this tool
                             using "-update".
-update  | -u	             Update source files, as output by "-list", to comply with
                             Symbian's Filename Policy.
-warnings| -w [all|fixable]  Output all unique "-checksource" warnings present in the
                             specified logs:
                                all     - every warning, regardless of whether this
                                          tool can fix them.
                                fixable - only warnings that this tool can fix.
-verbose | -v                Additional verbose output for the "-update" option.

NOTES:
* The tool assumes that the original build source and layout is present on the drive
  where it is being executed.
* With the exception of the "-warnings all" output, any warnings for files under the
  known release and build locations of %EPOCROOT%epoc32\include and
  %EPOCROOT%epoc32\build are discarded by default.

END_OF_HELP

	}


# 2. Parse the logs storing all GNU format warnings and errors


my %ActionableFilenamePolicyWarnings;	# Hash Key (filename)
										#	Hash Key (line number)
										#		Hash Key (problematic text)
										#			Hash Key ORIGINAL_WARNINGS
										#				Hash Key (original warning)
										#					1
										#			Hash Key ITEM
										#				item type that has generated the warning
										#			Hash Key SEARCH_TEXT
										#				quotemeta version of problematic text
										#			Hash Key UNIX_SLASH
										#				1
										#			Hash Key LOWERCASE
										#				1
										#			Hash Key PHYSICAL
										#				1
										#			Hash Key EXCLUSION
										#				1
										#			Hash Key PHYSICAL_REALITY
										#				Hash Key (physical reality)
										#					1
										#				fully pathed filesystem reality used in physical checking
										#			Hash Key EXCLUSION_LISTING
										#				required format of reference as dictated from an exclusion list
										#			Hash Key ACTUAL_TEST
										#				test used for check when this differed from that in actual source
										#			Hash Key DEFAULT_EXPORT
										#				special case - a PRJ_EXPORTS line without a destination will break
										#				the filename policy if the source line is not lowercase
my %NonActionableFilenamePolicyWarnings;
my %AllFilenamePolicyWarnings;
my %OtherWarningsAndErrors;

foreach my $BUILD_LOG (@ARGV)
	{
	open BUILD_LOG, "< $BUILD_LOG" or die "\nCannot read \"$BUILD_LOG\"!\n\n";

	while (<BUILD_LOG>)
		{
		chomp;
			
		if (/^\\\S+:.*: .+$/)
			{
			if (!/:\d+: (Incorrect case|Incorrect slash|Can\'t find) /)
				{
				$OtherWarningsAndErrors{$_} = 1 if (!/(unresolved api-item|unknown base class|unresolved xref|no image file|xm-replace_text)/);	# Ignore the noise of doc stuff...
				next;					
				}

			$AllFilenamePolicyWarnings{$_} = 1;
			
			if (/: Can\'t find /)
				{
				$NonActionableFilenamePolicyWarnings{$_} = 1;
				next;
				}

			my $originalWarning = $_;

			/(^.*):(\d+): Incorrect (case for epoc32 tree|case versus filesystem|case versus exclusion list|slash|case for epoc32 tree from default export) in (.+) - \'(.+?)\'/;

			my $filename = $1;
			my $lineNumber = $2;
			my $type = $3;
			my $item = $4;
			my $problematicText = $5;

			$type =~ s/case for epoc32 tree from default export/defaultexport/;
			$type =~ s/case for epoc32 tree/lowercase/;
			$type =~ s/case versus filesystem/physical/;
			$type =~ s/case versus exclusion list/exclusion/;

			my $actualTest = "";
			my $physicalReality = "";
			my $exclusionListing = "";
			$actualTest = $1 if (/\(actual test \'(.*)\'\)/);

			if (/ vs\. (.*)\./)
				{
				$physicalReality = $1 if ($type eq "physical");
				$exclusionListing = $1 if ($type eq "exclusion");
				}

			if ($debug)
				{
				print ("ORIGINAL WARNING   : $originalWarning\n");
				print ("FILENAME           : $filename\n");
				print ("LINENUMBER         : $lineNumber\n");
				print ("TYPE               : $type\n");
				print ("ITEM               : $item\n");
				print ("PROBLEMATIC TEXT   : $problematicText\n");
				print ("ACTUAL TEST        : $actualTest\n") if ($actualTest);
				print ("PHYSICAL REALITY   : $physicalReality\n") if ($physicalReality);
				print ("EXCLUSION LISTING  : $exclusionListing\n") if ($exclusionListing); 
				print ("\n");
				}

			next if ($warnings =~ /all/i);

	# Line Number

			my $lineNumberHashRef;
			if ($ActionableFilenamePolicyWarnings{$filename})
				{
				$lineNumberHashRef = $ActionableFilenamePolicyWarnings{$filename};
				}
			else
				{
				my %newHash;
				$lineNumberHashRef = \%newHash;
				$ActionableFilenamePolicyWarnings{$filename} = $lineNumberHashRef;
				}

	# Problematic Text

			my $problematicTextHashRef;
			if ($lineNumberHashRef->{$lineNumber})
				{
				$problematicTextHashRef = $lineNumberHashRef->{$lineNumber};
				}
			else
				{
				my %newHash;
				$problematicTextHashRef = \%newHash;
				$lineNumberHashRef->{$lineNumber} = $problematicTextHashRef;
				}

	# Attributes

			my $attributesHashRef;
			if ($problematicTextHashRef->{$problematicText})
				{
				$attributesHashRef = $problematicTextHashRef->{$problematicText};
				}
			else
				{
				my %newHash;
				$attributesHashRef = \%newHash;
				$problematicTextHashRef->{$problematicText} = $attributesHashRef;
				}

	# Attributes : Original Warnings

			my $originalWarningsHashRef;
			if ($attributesHashRef->{ORIGINAL_WARNINGS})
				{
				$originalWarningsHashRef = $attributesHashRef->{ORIGINAL_WARNINGS};
				}
			else
				{
				my %newHash;
				$originalWarningsHashRef = \%newHash;			
				$attributesHashRef->{ORIGINAL_WARNINGS} = $originalWarningsHashRef;
				}
			$originalWarningsHashRef->{$originalWarning} = 1;

	# Attributes : Item

			$attributesHashRef->{ITEM} = $item;

	# Attributes : Search Text

			$attributesHashRef->{SEARCH_TEXT} = quotemeta ($problematicText);

	# Attributes : Unix Slash

			$attributesHashRef->{UNIX_SLASH} = 1 if ($type eq "slash");

	# Attributes : Lowercase

			$attributesHashRef->{LOWERCASE} = 1 if ($type eq "lowercase");

	# Attributes : Physical

			$attributesHashRef->{PHYSICAL} = 1 if ($type eq "physical");

	# Attributes : Exclusion

			$attributesHashRef->{EXCLUSION} = 1 if ($type eq "exclusion");

	# Attributes : Physical Reality

			my $physicalRealityHashRef;
			if ($physicalReality)
				{
				if ($attributesHashRef->{PHYSICAL_REALITY})
					{
					$physicalRealityHashRef = $attributesHashRef->{PHYSICAL_REALITY};
					}
				else
					{
					my %newHash;
					$physicalRealityHashRef = \%newHash;			
					$attributesHashRef->{PHYSICAL_REALITY} = $physicalRealityHashRef;
					}
				$physicalRealityHashRef->{$physicalReality} = 1;
				}

	# Attributes : Actual Test

			$attributesHashRef->{ACTUAL_TEST} = $actualTest if ($actualTest);

	# Attributes : Exclusion Listing

			$attributesHashRef->{EXCLUSION_LISTING} = $exclusionListing if ($exclusionListing);

	# Attributes : Default Export

			$attributesHashRef->{DEFAULT_EXPORT} = 1 if ($type eq "defaultexport");
			}
		}

	close BUILD_LOG;
	}


# 3. Examine source and warnings and compile lists of files and warnings that we can/can't do anything about

my %WarningsNotMatchingSource;
my %WarningsWithMissingFiles;
my %WarningsForBothLowercaseAndPhysicalOnSameReference;
my %WarningsForMultiplePhysicalUpdates;


if ($analyse || $list || $update || $debugUpdate || $warnings =~ /^fixable$/i)
	{
		
	foreach my $SOURCE_FILE (sort keys %ActionableFilenamePolicyWarnings)
		{
			
	# Discard anything in known release locations

		if ($SOURCE_FILE =~ /\\epoc32\\(include|build)\\/i)
			{
			delete ($ActionableFilenamePolicyWarnings{$SOURCE_FILE});
			next;
			}

		my $lineNumbersHashRef = $ActionableFilenamePolicyWarnings{$SOURCE_FILE};

	# Discard warnings where source files cannot be found

		if (!(open SOURCE_FILE, "< $SOURCE_FILE"))
			{
			foreach my $lineNumber (sort keys (%$lineNumbersHashRef))
				{
				my $problematicTextHashRef = $lineNumbersHashRef->{$lineNumber};

				foreach my $problematicText (sort keys (%$problematicTextHashRef))
					{
					my $attributesHashRef = $problematicTextHashRef->{$problematicText};
					my $originalWarningsHashRef = $attributesHashRef->{ORIGINAL_WARNINGS};
					
					foreach my $originalWarning (keys (%$originalWarningsHashRef))
						{
						$WarningsWithMissingFiles{$originalWarning} = 1;
						}					
					}
				}
			delete ($ActionableFilenamePolicyWarnings{$SOURCE_FILE});
			next;
			}


	# Identify and discard warnings where, for the same reference:
	# (a) both lowercase and physical warnings are flagged and
	# (b) multiple, different, filesystem matches have been found
	# These will need to be resolved manually

		foreach my $lineNumber (sort keys (%$lineNumbersHashRef))
			{
			my $problematicTextHashRef = $lineNumbersHashRef->{$lineNumber};

			foreach my $problematicText (sort keys (%$problematicTextHashRef))
				{
				my $attributesHashRef = $problematicTextHashRef->{$problematicText};
				my $originalWarningsHashRef = $attributesHashRef->{ORIGINAL_WARNINGS};

				my $skipPhysicalUpdate = 0;
				my $skipLowercaseUpdate = 0;
				
				if ($attributesHashRef->{LOWERCASE} && $attributesHashRef->{PHYSICAL})
					{
					$skipPhysicalUpdate = 1;
					$skipLowercaseUpdate = 1;

					foreach my $originalWarning (keys %{$originalWarningsHashRef})
						{
						next if ($originalWarning !~ /Incorrect case/);

						$originalWarning =~ /\:(.*$)/;

						my $lowercaseAndPhysicalWarningsHashRef;	
						if ($WarningsForBothLowercaseAndPhysicalOnSameReference{$SOURCE_FILE})
							{
							$lowercaseAndPhysicalWarningsHashRef = $WarningsForBothLowercaseAndPhysicalOnSameReference{$SOURCE_FILE};
							}
						else
							{
							my %newHash;
							$lowercaseAndPhysicalWarningsHashRef = \%newHash;			
							$WarningsForBothLowercaseAndPhysicalOnSameReference{$SOURCE_FILE} = $lowercaseAndPhysicalWarningsHashRef;
							}
						$lowercaseAndPhysicalWarningsHashRef->{$1} = 1;						
						}
					}

				my $physicalRealityHashRef = $attributesHashRef->{PHYSICAL_REALITY};

				if ($physicalRealityHashRef && ((keys %{$physicalRealityHashRef}) > 1))
					{						
					my $physicalMatchCheck;
					if ($attributesHashRef->{ACTUAL_TEST})
						{
						$physicalMatchCheck = $attributesHashRef->{ACTUAL_TEST};
						}
					else
						{
						$physicalMatchCheck = $problematicText;
						$physicalMatchCheck =~ s/\.\.[\\|\/]//g;
						$physicalMatchCheck =~ s/\.[\\|\/]//g;
						}

					$physicalMatchCheck =~ s/\\\\/\\/g;		
					$physicalMatchCheck =~ s/\/\//\//g;
					$physicalMatchCheck =~ s/\//\\/g;
					$physicalMatchCheck = quotemeta($physicalMatchCheck);
					$physicalMatchCheck =~ s/\\\*/\\w\+/g;			# * -> \w+
					$physicalMatchCheck =~ s/\\\?/\\w\{1\}/g;		# ? -> \w{1}

					my %normalisedPhysicalReferences;
	
					foreach my $physicalReality (keys %{$physicalRealityHashRef})
						{
						$physicalReality =~ /($physicalMatchCheck)$/i;
						$normalisedPhysicalReferences{$1} = 1;
						}

					if ((keys (%normalisedPhysicalReferences)) > 1)
						{
						foreach my $originalWarning (keys %{$originalWarningsHashRef})
							{
							next if ($originalWarning !~ /Incorrect case versus/);

							$originalWarning =~ /\:(.*$)/;

							my $multiplePhysicalWarningsHashRef;	
							if ($WarningsForMultiplePhysicalUpdates{$SOURCE_FILE})
								{
								$multiplePhysicalWarningsHashRef = $WarningsForMultiplePhysicalUpdates{$SOURCE_FILE};
								}
							else
								{
								my %newHash;
								$multiplePhysicalWarningsHashRef = \%newHash;			
								$WarningsForMultiplePhysicalUpdates{$SOURCE_FILE} = $multiplePhysicalWarningsHashRef;
								}
							$multiplePhysicalWarningsHashRef->{$1} = 1;
							}
						$skipPhysicalUpdate = 1;
						}
					}

				$attributesHashRef->{LOWERCASE} = 0 if ($skipLowercaseUpdate);
				$attributesHashRef->{PHYSICAL} = 0 if ($skipPhysicalUpdate);

				if (!$attributesHashRef->{LOWERCASE} && !$attributesHashRef->{PHYSICAL} &&
					!$attributesHashRef->{UNIX_SLASH} && !$attributesHashRef->{DEFAULT_EXPORT} &&
					!$attributesHashRef->{EXCLUSION})
					{
					delete ($problematicTextHashRef->{$problematicText});
					}
				}

			delete ($lineNumbersHashRef->{$lineNumber}) if (!scalar (keys %{$problematicTextHashRef}));
			}

		my $lineNumber = 0;

		while (my $line = <SOURCE_FILE>)
			{
			$lineNumber++;

			next if (!($lineNumbersHashRef->{$lineNumber}));

			my $problematicTextHashRef = $lineNumbersHashRef->{$lineNumber};

			foreach my $text (keys %{$problematicTextHashRef})
				{
				my $attributesHashRef = $problematicTextHashRef->{$text};				

				chomp ($line);

				if ($line !~ /(\"|\<|^|\s){1}$attributesHashRef->{SEARCH_TEXT}/)
					{
					# Put warning(s) onto the failed list, as we can't find the required text
					# in the source present on the machine
						
					my $originalWarningsHashRef = $attributesHashRef->{ORIGINAL_WARNINGS};
					foreach my $originalWarning (keys %{$originalWarningsHashRef})
						{
						$WarningsNotMatchingSource{$originalWarning} = $line;
						}
						
					delete ($problematicTextHashRef->{$text});
					}
				}

			delete ($lineNumbersHashRef->{$lineNumber}) if (!scalar (keys %{$problematicTextHashRef}));
			}

		delete ($ActionableFilenamePolicyWarnings{$SOURCE_FILE}) if (!scalar (keys %{$lineNumbersHashRef}));
		close SOURCE_FILE;
		}
	}


# 4. Provide -warnings [all|fixable] output

if ($warnings =~ /^all$/i)
	{
	foreach my $warning (sort keys %AllFilenamePolicyWarnings)
		{
		print ("$warning\n");
		}
	}
elsif ($warnings =~ /^fixable$/i)
	{
	my %fixableWarnings;
		
	foreach my $sourceFile (keys %ActionableFilenamePolicyWarnings)
		{
		my $lineNumbersHashRef = $ActionableFilenamePolicyWarnings{$sourceFile};
		
		foreach my $lineNumber (keys (%$lineNumbersHashRef))
			{
			my $problematicTextHashRef = $lineNumbersHashRef->{$lineNumber};

			foreach my $text (keys %{$problematicTextHashRef})
				{
				my $attributesHashRef = $problematicTextHashRef->{$text};				
				my $originalWarningsHashRef = $attributesHashRef->{ORIGINAL_WARNINGS};

				foreach my $originalWarning (keys %{$originalWarningsHashRef})
					{						
					$fixableWarnings{$originalWarning} = 1;
					}
				}
			}
		}

	foreach my $fixableWarning (sort keys %fixableWarnings)
		{
		print ("$fixableWarning\n");
		}		
	}


# 5. Provide -list output

if ($list)
	{
	foreach my $sourceFile (sort keys %ActionableFilenamePolicyWarnings)
		{
		print ("$sourceFile\n");
		}
	}


# 6. Provide -analyse output

if ($analyse)
	{
	print ("\nFilename policy warnings with missing files\n".
		     "-------------------------------------------\n\n");
	foreach my $warningWithMissingFile (sort keys %WarningsWithMissingFiles)
		{
		print ("$warningWithMissingFile\n");
		}
	print ("NONE\n") if (!scalar (keys %WarningsWithMissingFiles));


	print ("\n\nFilename policy warnings that don't match source\n".
		       "------------------------------------------------\n\n");		
	foreach my $warningNotMatchingSource (sort keys %WarningsNotMatchingSource)
		{
		print ("$warningNotMatchingSource\n");
		print ("\tACTUAL LINE : \'$WarningsNotMatchingSource{$warningNotMatchingSource}\'\n");
		}
	print ("NONE\n") if (!scalar (keys %WarningsNotMatchingSource));

	print ("\n\nFilename policy warnings with both lowercase and physical warnings for the same reference\n".
		       "-----------------------------------------------------------------------------------------\n\n");		
	foreach my $sourceFile (sort keys %WarningsForBothLowercaseAndPhysicalOnSameReference)
		{
		print ("$sourceFile\n");
		foreach my $warning (sort keys %{$WarningsForBothLowercaseAndPhysicalOnSameReference{$sourceFile}})
			{
			print ("\t$warning\n");
			}
		}
	print ("NONE\n") if (!scalar (keys %WarningsForBothLowercaseAndPhysicalOnSameReference));

	print ("\n\nMultiple differing physical filename policy warnings for the same reference\n".
		       "---------------------------------------------------------------------------\n\n");		
	foreach my $sourceFile (sort keys %WarningsForMultiplePhysicalUpdates)
		{
		print ("$sourceFile\n");
		foreach my $warning (sort keys %{$WarningsForMultiplePhysicalUpdates{$sourceFile}})
			{
			print ("\t$warning\n");
			}
		}
	print ("NONE\n") if (!scalar (keys %WarningsForMultiplePhysicalUpdates));

	print ("\n\nNon-actionable filename policy warnings\n".
		     "---------------------------------------\n\n");		
	foreach my $nonActionableWarning (sort keys %NonActionableFilenamePolicyWarnings)
		{
		print ("$nonActionableWarning\n");
		}
	print ("NONE\n") if (!scalar (keys %NonActionableFilenamePolicyWarnings));


	print ("\n\nOther detected warnings unrelated to filename policy\n".
		     "----------------------------------------------------\n\n");		
	foreach my $otherWarningOrError (sort keys %OtherWarningsAndErrors)
		{
		print ("$otherWarningOrError\n");
		}
	print ("NONE\n") if (!scalar (keys %OtherWarningsAndErrors));

	print ("\n\n");
	}


# 7. Perform -update function

if ($update || $debugUpdate)
	{
	foreach my $SOURCE_FILE (sort keys %ActionableFilenamePolicyWarnings)
		{
		if (!(open SOURCE_FILE, "< $SOURCE_FILE"))
			{				
			print ("ERROR: Could not open $SOURCE_FILE to read.\n");
			next;
			}

		print ("Updating \'$SOURCE_FILE\'...\n") unless ($debugUpdate);

		my $lineNumbersHashRef = $ActionableFilenamePolicyWarnings{$SOURCE_FILE};
		my $lineNumber = 0;
		my @newSourceFile;

		while (my $line = <SOURCE_FILE>)
			{
			$lineNumber++;

			if ($lineNumbersHashRef->{$lineNumber})
				{				
				print ("\tOriginal : $line") if ($verbose);
					
				my $problematicTextHashRef = $lineNumbersHashRef->{$lineNumber};

				# We need to order the updates on a per-line basis so that, for example,
				# a search and update for 'nkern\arm\' occurs after one for 'include\nkern\arm\nk_plat.h'
				# We can do this by length, making sure the longest updates are performed first
				my @problematicTextOrderedHashKeys = sort {length $b <=> length $a} (keys %{$problematicTextHashRef});

				foreach my $problematicText (@problematicTextOrderedHashKeys)
					{
					my $attributesHashRef = $problematicTextHashRef->{$problematicText};
					my $revisedText = $problematicText;

		# Physical

					if ($attributesHashRef->{PHYSICAL})
						{
						my $physicalRealityHashRef = $attributesHashRef->{PHYSICAL_REALITY};

						my $physicalReality = (keys %{$physicalRealityHashRef})[0];
						my $physicalRealityUnixSlash = $physicalReality;
						$physicalRealityUnixSlash =~ s/\\/\//g;						
							
						if ($physicalReality =~ /($attributesHashRef->{SEARCH_TEXT})$/i ||
							$physicalRealityUnixSlash =~ /($attributesHashRef->{SEARCH_TEXT})$/i)
							{
							# Simple case - direct match with just case and slash differences
							my $replacement = $1;
							$replacement =~ s/\\/\//g;
							$revisedText =~ s/$attributesHashRef->{SEARCH_TEXT}/$replacement/;
							}
						else
							{
							# What we're looking at in the source file doesn't map directly
							# to what's physically on the file system.

							my $modifiedSearchText = $problematicText;
							$modifiedSearchText =~ s/\.\.[\\|\/]//g;
							$modifiedSearchText =~ s/\.[\\|\/]//g;

							my $physicalMatchCheck;
							if ($attributesHashRef->{ACTUAL_TEST})
								{
								$physicalMatchCheck = $attributesHashRef->{ACTUAL_TEST};
								}
							else
								{
								$physicalMatchCheck = $modifiedSearchText;
								}

							# The physical match check needs to remove double-slashing...
							$physicalMatchCheck =~ s/\\\\/\\/g;		
							$physicalMatchCheck =~ s/\/\//\//g;
							
							$modifiedSearchText = quotemeta($modifiedSearchText);
							$physicalMatchCheck = quotemeta($physicalMatchCheck);

							$physicalMatchCheck =~ s/\\\*/\\w\+/g;			# * -> \w+
							$physicalMatchCheck =~ s/\\\?/\\w\{1\}/g;		# ? -> \w{1}

							if ($physicalReality =~ /($physicalMatchCheck)$/i ||
								$physicalRealityUnixSlash =~ /($physicalMatchCheck)$/i )
								{
								my $replacement = $1;
								$replacement =~ s/\\/\//g;
								
								if ($attributesHashRef->{ACTUAL_TEST} &&
									($attributesHashRef->{ITEM} =~ /MMP$/ || $attributesHashRef->{ITEM} =~ /DEFFILE/))
									{
									# Both DEFFILE and PRJ_[TEST]MMPFILE entries may be specifed without extension
									$replacement =~ s/\.\w+$// if ($problematicText !~ /\.\w+$/);

									# DEFFILE entries may have eabi\bwins in the physical match and had a "u" inserted or appended 
									if ($attributesHashRef->{ITEM} =~ /DEFFILE/ && $replacement !~ /$modifiedSearchText$/i)
										{
										$replacement =~ s/(eabi|bwins)\//~\//i if ($problematicText =~ /~[\\|\/]/);
										$replacement =~ s/u(\.\w+)?$/$1/i if ($attributesHashRef->{ITEM} !~ /NOSTRICTDEF/);
										}
									}

								$revisedText =~ s/$modifiedSearchText/$replacement/;
								}
							else
								{
								print ("ERROR: Can\'t perform physical consistency updates for:");
								
								my $originalWarningsHashRef = $attributesHashRef->{ORIGINAL_WARNINGS};
								foreach my $originalWarning (keys %{$originalWarningsHashRef})
									{
									print ("\t$originalWarning") if ($originalWarning =~ /case versus/);
									}

								print ("\n");
								}
							}
						}

		# Exclusion

					if ($attributesHashRef->{EXCLUSION})
						{
						my $exclusionListingSearch = quotemeta($attributesHashRef->{EXCLUSION_LISTING});						
						$revisedText =~ s/$exclusionListingSearch/$attributesHashRef->{EXCLUSION_LISTING}/i;
						}						

		# Slash
					if ($attributesHashRef->{UNIX_SLASH})
						{
						$revisedText =~ s/\\/\//g;
						$revisedText =~ s/\/\//\//g;		# Don't allow replacements that lead to "//" in paths
						}

		# Lowercase

					if ($attributesHashRef->{LOWERCASE})
						{
						$revisedText = lc ($revisedText);
						}

		# Default Export

					if ($attributesHashRef->{DEFAULT_EXPORT})
						{
						my $exportedFilename = lc (basename ($problematicText));							
						$revisedText .= " \/epoc32\/include\/".$exportedFilename;
						}

					$line =~ s/(\"|\<|^|\s){1}$attributesHashRef->{SEARCH_TEXT}/$1$revisedText/;
					}

				print ("\tUpdated  : $line") if ($verbose);
				}
				
			push @newSourceFile, $line;
			}

		close SOURCE_FILE;

		if ($debugUpdate)
			{
			# Don't touch the original source, but create two trees for easy comparison
				
			my $baseDir = dirname ($SOURCE_FILE);				
			mkpath ("\\compare\\orig".$baseDir);
			mkpath ("\\compare\\updated".$baseDir);
			print ("Copying  \'\\compare\\orig".$SOURCE_FILE."\'...\n");
			copy ($SOURCE_FILE, "\\compare\\orig".$SOURCE_FILE);
			$SOURCE_FILE = "\\compare\\updated".$SOURCE_FILE;
			print ("Updating \'$SOURCE_FILE\'...\n");
			}

		if (!(open SOURCE_FILE, "> $SOURCE_FILE"))
			{				
			print ("ERROR: Could not open $SOURCE_FILE to write.\n");
			next;
			}

		foreach my $line (@newSourceFile)
			{
			print (SOURCE_FILE $line);
			}

		close SOURCE_FILE;
		}

	print ("\n") if ($verbose);
	}

__END__

:endofperl