diff -r 6d08f4a05d93 -r 3145852acc89 releasing/cbrtools/perl/CheckBc.pm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/releasing/cbrtools/perl/CheckBc.pm Fri Jun 25 18:37:20 2010 +0800 @@ -0,0 +1,1032 @@ +# Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies). +# All rights reserved. +# This component and the accompanying materials are made available +# under the terms of the License "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: +# +# + +use strict; + +package CheckBc; + + +# +# Public. +# + +sub New { + my $pkg = shift; + my $self = {}; + bless $self, $pkg; + my $bldInfDir1 = shift; + my $bldInfDir2 = shift; + Utils::AbsoluteFileName(\$bldInfDir1); + Utils::AbsoluteFileName(\$bldInfDir2); + $self->{verbose} = shift; + $self->{compName} = shift; + $self->{additionalHeaders} = shift; + $self->{additionalIncludePaths} = shift; + my $ignoreClasses = shift; + foreach my $thisClass (@$ignoreClasses) { + $self->{ignoreClasses}->{$thisClass} = 1; + } + $self->{ignoreR3Unused} = shift; + $self->{bldInf1} = BldInf->New($bldInfDir1, $self->{verbose}); + $self->{bldInf2} = BldInf->New($bldInfDir2, $self->{verbose}); + return $self; +} + +sub CheckAll { + my $self = shift; + my $passed = 1; + unless ($self->CheckDefFiles()) { + $passed = 0; + } + unless ($self->CheckClassSizes()) { + $passed = 0; + } + unless ($self->CheckVTables()) { + $passed = 0; + } + return $passed; +} + +sub CheckDefFiles { + my $self = shift; + return $self->{bldInf1}->CheckDefFiles($self->{bldInf2}, $self->{ignoreR3Unused}); +} + +sub CheckClassSizes { + my $self = shift; + my $classSizes1 = $self->GetClassSizes($self->{bldInf1}); + my $classSizes2 = $self->GetClassSizes($self->{bldInf2}); + return $classSizes1->Check($classSizes2); +} + +sub CheckVTables { + my $self = shift; + my $vtable1 = $self->GetVTable($self->{bldInf1}); + my $vtable2 = $self->GetVTable($self->{bldInf2}); + return $vtable1->Check($vtable2); +} + + +# +# Private. +# + +sub GetClassSizes { + my $self = shift; + my $bldInf = shift; + my $constructorsToCheck = $self->GetConstructorsToCheck($bldInf->ListConstructors()); + my @headers; + if ($self->{additionalHeaders}) { + push (@headers, @{$self->{additionalHeaders}}); + } + foreach my $thisExport (@{$bldInf->ListExports()}) { + if ($thisExport =~ /\.h$/i) { + push (@headers, $thisExport); + } + } + my $includes = $bldInf->ListIncludes(); + if ($self->{additionalIncludePaths}) { + push (@$includes, @{$self->{additionalIncludePaths}}); + } + return ClassSize->New($constructorsToCheck, \@headers, $includes, $self->{verbose}, $self->{compName}, $bldInf->{dir}); +} + +sub GetVTable { + my $self = shift; + my $bldInf = shift; + my $constructorsToCheck = $self->GetConstructorsToCheck($bldInf->ListConstructors()); + return VTable->New($bldInf->{dir}, $constructorsToCheck, $self->{verbose}); +} + +sub GetConstructorsToCheck { + my $self = shift; + my $constructors = shift; + my @constructorsToCheck; + foreach my $thisConstructor (@$constructors) { + unless (exists $self->{ignoreClasses}->{$thisConstructor}) { + push (@constructorsToCheck, $thisConstructor); + } + } + return \@constructorsToCheck; +} + + +# +# BldInf +# + +package BldInf; + + +# +# Public. +# + +sub New { + my $pkg = shift; + my $self = {}; + bless $self, $pkg; + $self->{dir} = shift; + $self->{verbose} = shift; + $self->Parse(); + return $self; +} + +sub CheckDefFiles { + my $self = shift; + my $other = shift; + my $ignoreR3Unused = shift; + my $passed = 1; + foreach my $thisMmp (keys %{$self->{mmps}}) { + if (exists $other->{mmps}->{$thisMmp}) { + unless ($self->{mmps}->{$thisMmp}->CheckDefFile($other->{mmps}->{$thisMmp}, $ignoreR3Unused)) { + $passed = 0; + } + } + else { + print "Mmp file \"$thisMmp\" missing for bld.inf \"$other->{dir}\"\n"; + $passed = 0; + } + } + return $passed; +} + +sub ListConstructors { + my $self = shift; + my @constructors = (); + foreach my $thisMmp (keys %{$self->{mmps}}) { + push (@constructors, @{$self->{mmps}->{$thisMmp}->ListConstructors()}); + } + return \@constructors; +} + +sub ListExports { + my $self = shift; + if (exists $self->{exports}) { + return $self->{exports}; + } + return []; +} + +sub ListIncludes { + my $self = shift; + my %includes = (); + foreach my $thisMmp (keys %{$self->{mmps}}) { + foreach my $thisInclude (@{$self->{mmps}->{$thisMmp}->ListIncludes()}) { + $includes{$thisInclude} = 1; + } + } + my @includes = keys %includes; + return \@includes; +} + + +# +# Private. +# + +sub Parse { + my $self = shift; + if ($self->{verbose}) { print "Parsing $self->{dir}\\bld.inf...\n"; } + Utils::PushDir($self->{dir}); + my $fullName = "$self->{dir}\\bld.inf"; + unless (open (BLDINF, "cpp -DARM -DMARM $fullName|")) { + Utils::PopDir(); + die "Error: Couldn't open \"cpp -DARM -DMARM $fullName\": $!\n"; + } + my $foundMmps = 0; + my $foundExports = 0; + my $doDie = 0; + my $currentDir = $self->{dir}; + while (my $line = ) { + if ($line =~ /^# \d+ "(.*)" \d+?/) { + my $newFile = $1; + $newFile =~ s/\\\\/\\/g; + $newFile =~ s/\\$//; + Utils::AbsoluteFileName(\$newFile); + ($currentDir) = Utils::SplitFileName($newFile); + next; + } + if ($line =~ /^#/ or $line =~ /^\s*$/) { + # Ignore lines starting with '#' or those filled with white space. + next; + } + chomp $line; + + if ($line =~ /PRJ_MMPFILES/i) { + $foundMmps = 1; + $foundExports = 0; + next; + } + elsif ($line =~ /PRJ_EXPORTS/i) { + $foundMmps = 0; + $foundExports = 1; + next; + } + elsif ($line =~ /PRJ_/i) { + $foundMmps = 0; + $foundExports = 0; + next; + } + if ($foundMmps) { + if ($line =~ /makefile\s+(\S+)/i) { + if ($self->{verbose}) { print "Info: \"makefile $1\" found in \"$self->{dir}\\bld.inf\", ignoring.\n"; } + next; + } + + $line =~ /\s*(\S+)/; + my $mmpName = lc($1); + if (not $mmpName =~ /\.mmp$/) { + $mmpName .= '.mmp'; + } + unless (-e $mmpName) { + if (-e "$currentDir\\$mmpName") { + $mmpName = "$currentDir\\$mmpName"; + } + elsif (-e "$self->{dir}\\$mmpName") { + $mmpName = "$self->{dir}\\$mmpName"; + } + else { + print "Warning: Couldn't find location of \"$mmpName\n"; + next; + } + } + Utils::AbsoluteFileName(\$mmpName); + (my $path, my $name, my $ext) = Utils::SplitFileName($mmpName); + eval { + $self->{mmps}->{lc("$name$ext")} = Mmp->New($mmpName, $self->{verbose}); + }; + if ($@) { + $doDie = 1; + print "$@"; + } + next; + } + elsif ($foundExports) { + my $thisExport; + if ($line =~ /^\s*\"([^\"]*)/) { + $thisExport = $1; + } + elsif ($line =~ /\s*(\S+)/) { + $thisExport = $1; + } + else { + die; + } + unless (-e $thisExport) { + if (-e "$currentDir\\$thisExport") { + $thisExport = "$currentDir\\$thisExport"; + } + elsif (-e "$self->{dir}\\$thisExport") { + $thisExport = "$self->{dir}\\$thisExport"; + } + else { + print "Warning: Couldn't find location of \"$thisExport\n"; + next; + } + } + Utils::AbsoluteFileName(\$thisExport); + push (@{$self->{exports}}, $thisExport); + } + } + close (BLDINF); + Utils::PopDir(); + if ($doDie) { + die "Aborting due to above errors\n"; + } +} + + +# +# Mmp +# + +package Mmp; + + +# +# Public. +# + +sub New { + my $pkg = shift; + my $self = {}; + bless $self, $pkg; + $self->{name} = shift; + $self->{verbose} = shift; + $self->Parse(); + return $self; +} + +sub CheckDefFile { + my $self = shift; + my $other = shift; + my $ignoreR3Unused = shift; + if ($self->{def}) { + return $self->{def}->Check($other->{def}, $ignoreR3Unused); + } + return 1; +} + +sub ListConstructors { + my $self = shift; + if ($self->{def}) { + return $self->{def}->ListConstructors(); + } + return []; +} + +sub ListIncludes { + my $self = shift; + if (exists $self->{includes}) { + my @includes = keys %{$self->{includes}}; + return \@includes; + } + return []; +} + + +# +# Private. +# + +sub Parse { + my $self = shift; + if ($self->{verbose}) { print "Parsing $self->{name}...\n"; } + (my $path) = Utils::SplitFileName($self->{name}); + $path =~ s/(.*)\\.*/$1/; # Extract path. + Utils::PushDir($path); + unless (open (MMP, "cpp -DARM -DMARM $self->{name}|")) { + Utils::PopDir(); + die "Error: Couldn't open \"cpp -DARM -DMARM $self->{name}\": $!\n"; + } + my $noStrictDef = 0; + my $targetType = ''; + while (my $line = ) { + if ($line =~ /^#/ or $line =~ /^\s*$/) { + # Ignore lines starting with '#' or those filled with white space. + next; + } + chomp $line; + if ($line =~ /^\s*targettype\s+(\S*)\s*$/i) { + $targetType = $1; + } + elsif ($line =~ /^\s*deffile\s+(\S*)\s*$/i) { + die if exists $self->{defFileName}; + $self->{defFileName} = $1; + } + elsif ($line =~ /nostrictdef/i) { + $noStrictDef = 1; + } + elsif ($line =~ /^\s*userinclude\s+(.+)/i) { + my @userIncludes = split (/\s+/, $1); + foreach my $thisUserInclude (@userIncludes) { + $thisUserInclude =~ s/\+/$ENV{EPOCROOT}epoc32/; + Utils::AbsoluteFileName(\$thisUserInclude); + $self->{includes}->{lc($thisUserInclude)} = 1; + } + } + elsif ($line =~ /^\s*systeminclude\s+(.+)/i) { + my @systemIncludes = split (/\s+/, $1); + foreach my $thisSystemInclude (@systemIncludes) { + $thisSystemInclude =~ s/\+/$ENV{EPOCROOT}epoc32/; + Utils::AbsoluteFileName(\$thisSystemInclude); + $self->{includes}->{lc($thisSystemInclude)} = 1; + } + } + } + close (MMP); + + if ($targetType =~ /^(app|ani|ctl|ctpkg|epocexe|exe|exedll|fsy|kdll|kext|klib|ldd|lib|ecomiic|mda|mdl|notifier|opx|pdd|pdl|rdl|var|wlog)$/i) { + # Don't bother looking for the deffile. + Utils::PopDir(); + return; + } + + (my $mmpPath, my $mmpBase) = Utils::SplitFileName($self->{name}); + if (exists $self->{defFileName}) { + (my $path, my $base, my $ext) = Utils::SplitFileName($self->{defFileName}); + if ($base eq '') { + $base = $mmpBase; + } + if ($ext eq '') { + $ext = '.def'; + } + if ($path eq '') { + $path = $mmpPath; + } + unless ($noStrictDef) { + $base .= 'u'; + } + unless (-e "$path$base$ext") { + $path = "$path..\\bmarm\\"; + } + unless (-e "$path$base$ext") { + $path = $mmpPath . $path; + } + $self->{defFileName} = "$path$base$ext"; + Utils::AbsoluteFileName(\$self->{defFileName}); + } + else { + # Assume default. + $self->{defFileName} = $mmpBase; + unless ($noStrictDef) { + $self->{defFileName} .= 'u'; + } + $self->{defFileName} .= '.def'; + $self->AddDefaultDefFilePath(); + } + + if ($self->{defFileName}) { + $self->{def} = Def->New($self->{defFileName}, $self->{verbose}); + } + + Utils::PopDir(); +} + +sub AddDefaultDefFilePath { + my $self = shift; + (my $path) = Utils::SplitFileName($self->{name}); + $self->{defFileName} = "$path\\..\\bmarm\\$self->{defFileName}"; + if (-e $self->{defFileName}) { + Utils::AbsoluteFileName(\$self->{defFileName}); + } + else { + print "Warning: Unable to find def file in \"$self->{name}\"\n"; + delete $self->{defFileName}; + } +} + + +# +# Def +# + +package Def; + + +# +# Public. +# + +sub New { + my $pkg = shift; + my $self = {}; + bless $self, $pkg; + $self->{name} = shift; + $self->{verbose} = shift; + $self->Parse(); + $self->DemangleNames(); + return $self; +} + +sub Check { + my $self = shift; + my $other = shift; + my $ignoreR3Unused = shift; + if ($self->{verbose}) { print "Checking DEF file \"$self->{name}\" against \"$other->{name}\"...\n"; } + my $passed = 1; + if (exists $self->{data}) { + for (my $ii = 0; $ii < scalar(@{$self->{data}}); ++$ii) { + my $ordinal = $ii + 1; + if ($ii >= scalar @{$other->{data}}) { + print "Failure reason: \"$self->{name}\" has more exports than \"$other->{name}\"\n"; + $passed = 0; + last; + } + my $selfRaw = $self->{data}->[$ii]->{raw}; + my $otherRaw = $other->{data}->[$ii]->{raw}; + if ($ignoreR3Unused) { + $selfRaw =~ s/R3UNUSED //; + $otherRaw =~ s/R3UNUSED //; + } + unless ($selfRaw eq $otherRaw) { + $passed = 0; + print "Failure reason: Def file mismatch between \"$self->{name}\" and \"$other->{name}\" at $ordinal\n"; + if ($self->{verbose}) { + print "\t$self->{data}->[$ii]->{raw}\n\t$other->{data}->[$ii]->{raw}\n"; + } + } + } + } + return $passed; +} + +sub ListConstructors { + my $self = shift; + my @constructors = (); + if (exists $self->{data}) { + my $ordinal = 0; + foreach my $thisEntry (@{$self->{data}}) { + $ordinal++; + die unless (exists $thisEntry->{function}); + if ($thisEntry->{function} =~ /(.+)::(.+)\(/) { + if ($1 eq $2) { + push (@constructors, $1); + } + } + } + } + return \@constructors; +} + + +# +# Private. +# + +sub Parse { + my $self = shift; + open (DEF, $self->{name}) or die "Error: Couldn't open \"$self->{name}\" for reading: $!\n"; + my $lineNum = 0; + while (my $thisLine = ) { + ++$lineNum; + chomp $thisLine; + if ($thisLine =~ /^(EXPORTS|;|\s*$)/) { + next; + } + my $entry = {}; + $entry->{raw} = $thisLine; + + push (@{$self->{data}}, $entry); + } + close (DEF); +} + +sub DemangleNames { + my $self = shift; + open (FILT, "type $self->{name} | c++filt |") or die "Error: Couldn't open \"type $self->{name} | c++filt |\": $!\n"; + my $lineNum = 0; + while (my $line = ) { + ++$lineNum; + chomp $line; + next if ($line =~ /^(EXPORT|;|\s*$)/); + if ($line =~ /^\s+(\"(.+)\"|(.+)) @ (\d+)/) { + my $function; + if ($2) { + $function = $2; + } + else { + die unless $3; + $function = $3; + } + my $ordinal = $4; + $self->{data}->[$ordinal - 1]->{function} = $function; + } + else { + die "Error: Unable to parse c++filt output for \"$self->{name}\" at line $lineNum\n"; + } + } + close (FILT); +} + + +# +# ClassSize +# + +package ClassSize; + + +# +# Public. +# + +sub New { + my $pkg = shift; + my $self = {}; + bless $self, $pkg; + $self->{classes} = shift; + $self->{headers} = shift; + $self->{includes} = shift; + $self->{verbose} = shift; + $self->{compName} = shift; + $self->{bldInfDir} = shift; + if (scalar @{$self->{classes}} > 0) { + $self->GetClassSizes(); + } + return $self; +} + +sub Check { + my $self = shift; + my $other = shift; + if ($self->{verbose}) { print "Comparing class sizes of \"$self->{bldInfDir}\" against \"$other->{bldInfDir}\"..\n"; } + my $passed = 1; + foreach my $thisClass (keys %{$self->{classSizes}}) { + if ($self->{verbose}) { print "Examining class sizes of \"$thisClass\"...\n"; } + unless (exists $other->{classSizes}->{$thisClass}) { + print "Failure reason: \"$thisClass\" not found (possibly renamed)\n"; + $passed = 0; + next; + } + unless ($self->{classSizes}->{$thisClass} == $other->{classSizes}->{$thisClass}) { + $passed = 0; + print "Failure reason: Class \"$thisClass\" has changed size from $self->{classSizes}->{$thisClass} to $other->{classSizes}->{$thisClass}\n"; + } + } + return $passed; +} + + +# +# Private. +# + +sub GetClassSizes { + my $self = shift; + eval { + $self->GenerateCode(); + $self->CompileCode(); + $self->GetOutput(); + }; + $self->CleanUp(); + if ($@) { + die $@; + } +} + +sub GenerateCode { + my $self = shift; + open (CODE, '>__ClassSize.cpp') or die "Error: Couldn't open \"__ClassSize.cpp\" for writing: $!\n"; + print CODE "#include \n"; + print CODE "#include \n"; + print CODE "#include \n"; + print CODE "#include \n"; + foreach my $thisHeader (@{$self->{headers}}) { + print CODE "#include <$thisHeader>\n"; + } + print CODE "int main(int argc, char* argv[]) {\n"; + foreach my $thisClass (@{$self->{classes}}) { + print CODE "\tprintf(\"$thisClass\\t%d\\n\", sizeof($thisClass));\n"; + } + print CODE "\treturn 0; }\n"; + close (CODE); +} + +sub CompileCode { + my $self = shift; + my $command = 'cl '; + foreach my $thisInclude (@{$self->{includes}}) { + $command .= " /I$thisInclude"; + } + $command .= " /D__VC32__ /D__WINS__ /D__SYMBIAN32__ /DWIN32 /D_WINDOWS /D_UNICODE __ClassSize.cpp"; + unless ($self->{verbose}) { + $command .= ' /nologo 2>&1 > NUL'; + } + if (system ($command)) { + if (exists $self->{compName} and $self->{compName}) { + rename ("__ClassSize.cpp", "$self->{compName}.cpp"); + } + else { + rename ("__ClassSize.cpp", "unknown.cpp"); + } + die "Error: Problem executing \"$command\"\n"; + } +} + +sub GetOutput { + my $self = shift; + open (OUTPUT, '__ClassSize.exe|') or die "Error: Couldn't run \"__ClassSize.exe\": $!\n"; + while (my $thisLine = ) { + chomp $thisLine; + next if ($thisLine =~ /^\s*$/); + if ($thisLine =~ /^(\S+)\t(\d+)$/) { + $self->{classSizes}->{$1} = $2; + } + else { + die "Error: Problem parsing output of \"__ClassSize.exe\"\n"; + } + } + close (OUTPUT); +} + +sub CleanUp { + my $self = shift; + DeleteFile('__ClassSize.cpp'); + DeleteFile('__ClassSize.obj'); + DeleteFile('__ClassSize.exe'); +} + +sub DeleteFile { + my $file = shift; + if (-e $file) { + unlink ($file) or die "Error: Couldn't delete \"$file\"\n"; + } +} + + +# +# VTable +# + +package VTable; + + +# +# Public. +# + +sub New { + my $pkg = shift; + my $self = {}; + bless $self, $pkg; + $self->{bldInfDir} = shift; + my $classes = shift; + foreach my $class (@$classes) { + $self->{classes}->{$class} = 1; + } + $self->{verbose} = shift; + + Utils::PushDir($self->{bldInfDir}); + eval { + $self->BuildAssemblerListings(); + $self->ParseAssemblerListings(); + $self->DeleteAssemblerListings(); + }; + Utils::PopDir(); + if ($@) { + die $@; + } + return $self; +} + +sub Check { + my $self = shift; + my $other = shift; + if ($self->{verbose}) { print "Comparing vtable layout of \"$self->{bldInfDir}\" against \"$other->{bldInfDir}\"..\n"; } + my $passed = 1; + foreach my $class (keys %{$self->{vtables}}) { + if (exists $other->{vtables}->{$class}) { + if ($self->{verbose}) { print "Examining vtable of class \"$class\"...\n"; } + for (my $ii = 0; $ii < scalar (@{$self->{vtables}->{$class}}); ++$ii) { + my $thisVTableEntry = $self->{vtables}->{$class}->[$ii]; + if ($ii >= scalar (@{$other->{vtables}->{$class}})) { + print "Failure reason: Unexpected vtable entry \"$thisVTableEntry\"\n"; + $passed = 0; + last; + } + my $otherVTableEntry = $other->{vtables}->{$class}->[$ii]; + if ($thisVTableEntry eq $otherVTableEntry) { + if ($self->{verbose}) { print "\tMatched vtable entry \"$thisVTableEntry\"\n"; } + } + else { + print "Failure reason: Mismatched vtable entries in class \"$class\"\n\t$thisVTableEntry\n\t$otherVTableEntry\n"; + $passed = 0; + } + } + } + else { + print "Failure reason: Vtable for \"$class\" missing from $other->{bldInfDir}\n"; + $passed = 0; + } + } + return $passed; +} + + + +# +# Private. +# + +sub BuildAssemblerListings { + my $self = shift; + if ($self->{verbose}) { print "Calling \"bldmake bldfiles\" in \"$self->{bldInfDir}\"\n"; } + open (BLDMAKE, "bldmake bldfiles 2>&1 |") or die "Error: Couldn't run \"bldmake bldfiles\" in \"$self->{bldInfDir}\": $!\n"; + while (my $line = ) { + if ($line) { + if ($self->{verbose}) { print "\t$line"; } + die "Error: Problem running \"bldmake bldfiles\" in \"$self->{bldInfDir}\"\n"; + } + } + close (BLDMAKE); + + if ($self->{verbose}) { print "Calling \"abld makefile arm4\" in \"$self->{bldInfDir}\"\n"; } + open (ABLD, "abld makefile arm4 2>&1 |") or die "Error: Couldn't run \"abld makefile arm4\" in \"$self->{bldInfDir}\": $!\n"; + while (my $line = ) { + if ($line) { + if ($self->{verbose}) { print "\t$line"; } + } + } + close (ABLD); + + if ($self->{verbose}) { print "Calling \"abld listing arm4 urel\" in \"$self->{bldInfDir}\"\n"; } + open (ABLD, "abld listing arm4 urel 2>&1 |") or die "Error: Couldn't run \"abld listing arm4 urel\" in \"$self->{bldInfDir}\": $!\n"; + while (my $line = ) { + if ($line) { + if ($self->{verbose}) { print "\t$line"; } + if ($line =~ /^Created (.*)/) { + my $listingFile = $1; + push (@{$self->{listingFiles}}, $listingFile); + } + } + } + close (ABLD); +} + +sub ParseAssemblerListings { + my $self = shift; + foreach my $listing (@{$self->{listingFiles}}) { + open (LISTING, $listing) or die "Error: Couldn't open \"$listing\" for reading: $!\n"; + while (my $line = ) { + if ($line =~ /^\s.\d+\s+__vt_\d+(\D+):$/) { # If start of vtable section. + my $class = $1; + if (exists $self->{classes}->{$class}) { # If one of the classes we're interested in. + while (my $line2 = ) { + if ($line2 =~ /^\s.\d+\s[\da-fA-F]{4}\s[\da-fA-F]{8}\s+\.word\s+(.*)/) { # If this is a valid vtable entry. + my $vtableEntry = $1; + push (@{$self->{vtables}->{$class}}, $vtableEntry); + } + else { + last; + } + } + } + } + } + close (LISTING); + } +} + +sub DeleteAssemblerListings { + my $self = shift; + foreach my $listing (@{$self->{listingFiles}}) { + unlink $listing or die "Error: Unable to delete \"$listing\": $!\n"; + } +} + + +# +# Utils. +# + +package Utils; + +use File::Basename; +use Cwd 'abs_path', 'cwd'; +use Win32; + +sub AbsoluteFileName { + my $fileName = shift; + unless (-e $$fileName) { + die "Error: \"$$fileName\" does not exist\n"; + } + (my $base, my $path) = fileparse($$fileName); + my $absPath = abs_path($path); + $$fileName = $absPath; + unless ($$fileName =~ /[\\\/]$/) { + $$fileName .= "\\"; + } + $$fileName .= $base; + TidyFileName($fileName); +} + +sub SplitFileName { + my $fileName = shift; + my $path = ''; + my $base = ''; + my $ext = ''; + + if ($fileName =~ /\\?([^\\]*?)(\.[^\\\.]*)?$/) { + $base = $1; + } + if ($fileName =~ /^(.*\\)/) { + $path = $1; + } + if ($fileName =~ /(\.[^\\\.]*)$/o) { + $ext = $1; + } + + die unless ($fileName eq "$path$base$ext"); + return ($path, $base, $ext); +} + +sub TidyFileName { + my $a = shift; + $$a =~ s/\//\\/g; # Change forward slashes to back slashes. + $$a =~ s/\\\.\\/\\/g; # Change "\.\" into "\". + + if ($$a =~ /^\\\\/) { # Test for UNC paths. + $$a =~ s/\\\\/\\/g; # Change "\\" into "\". + $$a =~ s/^\\/\\\\/; # Add back a "\\" at the start so that it remains a UNC path. + } + else { + $$a =~ s/\\\\/\\/g; # Change "\\" into "\". + } +} + +my @dirStack; + +sub PushDir { + my $dir = shift; + my $cwd = cwd(); + chdir ($dir) or die "Error: Couldn't change working directory to \"$dir\": $!\n"; + push (@dirStack, $cwd); +} + +sub PopDir { + if (scalar @dirStack > 0) { + my $dir = pop @dirStack; + chdir ($dir) or die "Error: Couldn't change working directory to \"$dir\": $!\n"; + } + else { + die "Error: Directory stack empty"; + } +} + + +1; + +=head1 NAME + +CheckBc.pm - A module that runs some simple tests to see if one component source tree is backwards compatible another. + +=head1 SYNOPSIS + + my $checkBc = CheckBc->New('\branch1\comp\group', '\branch2\comp\group', 0); + unless ($checkBc->CheckAll()) { + print "Check failed\n"; + } + +=head1 DESCRIPTION + +C does the following checks to see if a backwards compatibility breaking change has been introduced: + +=over 4 + +=item 1 + +Compares the ARM F<.def> files to ensure that only new lines have been added to the end of the file. + +=item 2 + +Compares the sizes of any classes that have an exported C++ constructor. This is done by compiling some generated C++ code that uses the C operator to print the relevant class sizes to C. Compilation is done using the MSVC++ compiler. + +=item 3 + +Compares the v-table layouts of any classes that have an exported C++ constructor. This is done by compiling each source code set to ARM4 assembler listings, comparing the v-table sections. + +=back + +=head1 LIMITATIONS + +=over 4 + +=item 1 + +The component's headers must compile using Microsoft's Visual C++ compiler. + +=item 2 + +The component's exported headers must compile when they are all #include'd into a single F<.cpp> file. If this is not the case, then additional headers and include paths can be passed into the constructor. + +=item 3 + +Declarations of the component's exported C++ constructors must be found in one of the exported headers. + +=item 4 + +F<.def> file lines are expected to be identical. This can lead to checks failing falsely because, for example, the name of a function may be changed without breaking BC provided the F<.def> file is carefully edited. + +=item 5 + +The components must compile as ARM4. This is likely to mean that each set of source code needs to be accompanied with a suitable F<\epoc32> tree that allows it to be built. The simplest way to acheive this is to prepare a pair of subst'd drives. + +=back + +=head1 KNOWN BUGS + +F, F<.mmp> and F<.def> file parsing is probably not as industrial strength as it should be. + +=head1 COPYRIGHT + + Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies). + All rights reserved. + This component and the accompanying materials are made available + under the terms of the License "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: + + +=cut