releasing/cbrtools/perl/CheckBc.pm
changeset 607 378360dbbdba
parent 602 3145852acc89
equal deleted inserted replaced
591:22486c9c7b15 607:378360dbbdba
       
     1 # Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 # All rights reserved.
       
     3 # This component and the accompanying materials are made available
       
     4 # under the terms of the License "Eclipse Public License v1.0"
       
     5 # which accompanies this distribution, and is available
       
     6 # at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 # 
       
     8 # Initial Contributors:
       
     9 # Nokia Corporation - initial contribution.
       
    10 # 
       
    11 # Contributors:
       
    12 # 
       
    13 # Description:
       
    14 # 
       
    15 #
       
    16 
       
    17 use strict;
       
    18 
       
    19 package CheckBc;
       
    20 
       
    21 
       
    22 #
       
    23 # Public.
       
    24 #
       
    25 
       
    26 sub New {
       
    27   my $pkg = shift;
       
    28   my $self = {};
       
    29   bless $self, $pkg;
       
    30   my $bldInfDir1 = shift;
       
    31   my $bldInfDir2 = shift;
       
    32   Utils::AbsoluteFileName(\$bldInfDir1);
       
    33   Utils::AbsoluteFileName(\$bldInfDir2);
       
    34   $self->{verbose} = shift;
       
    35   $self->{compName} = shift;
       
    36   $self->{additionalHeaders} = shift;
       
    37   $self->{additionalIncludePaths} = shift;
       
    38   my $ignoreClasses = shift;
       
    39   foreach my $thisClass (@$ignoreClasses) {
       
    40     $self->{ignoreClasses}->{$thisClass} = 1;
       
    41   }
       
    42   $self->{ignoreR3Unused} = shift;
       
    43   $self->{bldInf1} = BldInf->New($bldInfDir1, $self->{verbose});
       
    44   $self->{bldInf2} = BldInf->New($bldInfDir2, $self->{verbose});
       
    45   return $self;
       
    46 }
       
    47 
       
    48 sub CheckAll {
       
    49   my $self = shift;
       
    50   my $passed = 1;
       
    51   unless ($self->CheckDefFiles()) {
       
    52     $passed = 0;
       
    53   }
       
    54   unless ($self->CheckClassSizes()) {
       
    55     $passed = 0;
       
    56   }
       
    57   unless ($self->CheckVTables()) {
       
    58     $passed = 0;
       
    59   }
       
    60   return $passed;
       
    61 }
       
    62 
       
    63 sub CheckDefFiles {
       
    64   my $self = shift;
       
    65   return $self->{bldInf1}->CheckDefFiles($self->{bldInf2}, $self->{ignoreR3Unused});
       
    66 }
       
    67 
       
    68 sub CheckClassSizes {
       
    69   my $self = shift;
       
    70   my $classSizes1 = $self->GetClassSizes($self->{bldInf1});
       
    71   my $classSizes2 = $self->GetClassSizes($self->{bldInf2});
       
    72   return $classSizes1->Check($classSizes2);
       
    73 }
       
    74 
       
    75 sub CheckVTables {
       
    76   my $self = shift;
       
    77   my $vtable1 = $self->GetVTable($self->{bldInf1});
       
    78   my $vtable2 = $self->GetVTable($self->{bldInf2});
       
    79   return $vtable1->Check($vtable2);
       
    80 }
       
    81 
       
    82 
       
    83 #
       
    84 # Private.
       
    85 #
       
    86 
       
    87 sub GetClassSizes {
       
    88   my $self = shift;
       
    89   my $bldInf = shift;
       
    90   my $constructorsToCheck = $self->GetConstructorsToCheck($bldInf->ListConstructors());
       
    91   my @headers;
       
    92   if ($self->{additionalHeaders}) {
       
    93     push (@headers, @{$self->{additionalHeaders}});
       
    94   }
       
    95   foreach my $thisExport (@{$bldInf->ListExports()}) {
       
    96     if ($thisExport =~ /\.h$/i) {
       
    97       push (@headers, $thisExport);
       
    98     }
       
    99   }
       
   100   my $includes = $bldInf->ListIncludes();
       
   101   if ($self->{additionalIncludePaths}) {
       
   102     push (@$includes, @{$self->{additionalIncludePaths}});
       
   103   }
       
   104   return ClassSize->New($constructorsToCheck, \@headers, $includes, $self->{verbose}, $self->{compName}, $bldInf->{dir});
       
   105 }
       
   106 
       
   107 sub GetVTable {
       
   108   my $self = shift;
       
   109   my $bldInf = shift;
       
   110   my $constructorsToCheck = $self->GetConstructorsToCheck($bldInf->ListConstructors());
       
   111   return VTable->New($bldInf->{dir}, $constructorsToCheck, $self->{verbose});
       
   112 }
       
   113 
       
   114 sub GetConstructorsToCheck {
       
   115   my $self = shift;
       
   116   my $constructors = shift;
       
   117   my @constructorsToCheck;
       
   118   foreach my $thisConstructor (@$constructors) {
       
   119     unless (exists $self->{ignoreClasses}->{$thisConstructor}) {
       
   120       push (@constructorsToCheck, $thisConstructor);
       
   121     }
       
   122   }
       
   123   return \@constructorsToCheck;
       
   124 }
       
   125 
       
   126 
       
   127 #
       
   128 # BldInf
       
   129 #
       
   130 
       
   131 package BldInf;
       
   132 
       
   133 
       
   134 #
       
   135 # Public.
       
   136 #
       
   137 
       
   138 sub New {
       
   139   my $pkg = shift;
       
   140   my $self = {};
       
   141   bless $self, $pkg;
       
   142   $self->{dir} = shift;
       
   143   $self->{verbose} = shift;
       
   144   $self->Parse();
       
   145   return $self;
       
   146 }
       
   147 
       
   148 sub CheckDefFiles {
       
   149   my $self = shift;
       
   150   my $other = shift;
       
   151   my $ignoreR3Unused = shift;
       
   152   my $passed = 1;
       
   153   foreach my $thisMmp (keys %{$self->{mmps}}) {
       
   154     if (exists $other->{mmps}->{$thisMmp}) {
       
   155       unless ($self->{mmps}->{$thisMmp}->CheckDefFile($other->{mmps}->{$thisMmp}, $ignoreR3Unused)) {
       
   156 	$passed = 0;
       
   157       }
       
   158     }
       
   159     else {
       
   160       print "Mmp file \"$thisMmp\" missing for bld.inf \"$other->{dir}\"\n";
       
   161       $passed = 0;
       
   162     }
       
   163   }
       
   164   return $passed;
       
   165 }
       
   166 
       
   167 sub ListConstructors {
       
   168   my $self = shift;
       
   169   my @constructors = ();
       
   170   foreach my $thisMmp (keys %{$self->{mmps}}) {
       
   171     push (@constructors, @{$self->{mmps}->{$thisMmp}->ListConstructors()});
       
   172   }
       
   173   return \@constructors;
       
   174 }
       
   175 
       
   176 sub ListExports {
       
   177   my $self = shift;
       
   178   if (exists $self->{exports}) {
       
   179     return $self->{exports};
       
   180   }
       
   181   return [];
       
   182 }
       
   183 
       
   184 sub ListIncludes {
       
   185   my $self = shift;
       
   186   my %includes = ();
       
   187   foreach my $thisMmp (keys %{$self->{mmps}}) {
       
   188     foreach my $thisInclude (@{$self->{mmps}->{$thisMmp}->ListIncludes()}) {
       
   189       $includes{$thisInclude} = 1;
       
   190     }
       
   191   }
       
   192   my @includes = keys %includes;
       
   193   return \@includes;
       
   194 }
       
   195 
       
   196 
       
   197 #
       
   198 # Private.
       
   199 #
       
   200 
       
   201 sub Parse {
       
   202   my $self = shift;
       
   203   if ($self->{verbose}) {  print "Parsing $self->{dir}\\bld.inf...\n"; }
       
   204   Utils::PushDir($self->{dir});
       
   205   my $fullName = "$self->{dir}\\bld.inf";
       
   206   unless (open (BLDINF, "cpp -DARM -DMARM $fullName|")) {
       
   207     Utils::PopDir();
       
   208     die "Error: Couldn't open \"cpp -DARM -DMARM $fullName\": $!\n";
       
   209   }
       
   210   my $foundMmps = 0;
       
   211   my $foundExports = 0;
       
   212   my $doDie = 0;
       
   213   my $currentDir = $self->{dir};
       
   214   while (my $line = <BLDINF>) {
       
   215     if ($line =~ /^# \d+ "(.*)" \d+?/) {
       
   216 	my $newFile = $1;
       
   217 	$newFile =~ s/\\\\/\\/g;
       
   218 	$newFile =~ s/\\$//;
       
   219 	Utils::AbsoluteFileName(\$newFile);
       
   220 	($currentDir) = Utils::SplitFileName($newFile);
       
   221 	next;
       
   222       }
       
   223     if ($line =~ /^#/ or $line =~ /^\s*$/) {	
       
   224 	# Ignore lines starting with '#' or those filled with white space.
       
   225 	next;
       
   226       }
       
   227     chomp $line;
       
   228 
       
   229     if ($line =~ /PRJ_MMPFILES/i) {
       
   230       $foundMmps = 1;
       
   231       $foundExports = 0;
       
   232       next;
       
   233     }
       
   234     elsif ($line =~ /PRJ_EXPORTS/i) {
       
   235       $foundMmps = 0;
       
   236       $foundExports = 1;
       
   237       next;
       
   238     }
       
   239     elsif ($line =~ /PRJ_/i) {
       
   240       $foundMmps = 0;
       
   241       $foundExports = 0;
       
   242       next;
       
   243     }
       
   244     if ($foundMmps) {
       
   245       if ($line =~ /makefile\s+(\S+)/i) {
       
   246 	if ($self->{verbose}) { print "Info: \"makefile $1\" found in \"$self->{dir}\\bld.inf\", ignoring.\n"; }
       
   247 	next;
       
   248       }
       
   249 
       
   250       $line =~ /\s*(\S+)/;
       
   251       my $mmpName = lc($1);
       
   252       if (not $mmpName =~ /\.mmp$/) {
       
   253 	$mmpName .= '.mmp';
       
   254       }
       
   255       unless (-e $mmpName) {
       
   256 	if (-e "$currentDir\\$mmpName") {
       
   257 	  $mmpName = "$currentDir\\$mmpName";
       
   258 	}
       
   259 	elsif (-e "$self->{dir}\\$mmpName") {
       
   260 	  $mmpName = "$self->{dir}\\$mmpName";
       
   261 	}
       
   262 	else {
       
   263 	  print "Warning: Couldn't find location of \"$mmpName\n";
       
   264 	  next;
       
   265 	}
       
   266       }
       
   267       Utils::AbsoluteFileName(\$mmpName);
       
   268       (my $path, my $name, my $ext) = Utils::SplitFileName($mmpName);
       
   269       eval {
       
   270 	$self->{mmps}->{lc("$name$ext")} = Mmp->New($mmpName, $self->{verbose});
       
   271       };
       
   272       if ($@) {
       
   273 	$doDie = 1;
       
   274 	print "$@";
       
   275       }
       
   276       next;
       
   277     }
       
   278     elsif ($foundExports) {
       
   279       my $thisExport;
       
   280       if ($line =~  /^\s*\"([^\"]*)/) {
       
   281 	$thisExport = $1;
       
   282       }
       
   283       elsif ($line =~ /\s*(\S+)/) {
       
   284 	$thisExport = $1;
       
   285       }
       
   286       else {
       
   287 	die;
       
   288       }
       
   289       unless (-e $thisExport) {
       
   290 	if (-e "$currentDir\\$thisExport") {
       
   291 	  $thisExport = "$currentDir\\$thisExport";
       
   292 	}
       
   293 	elsif (-e "$self->{dir}\\$thisExport") {
       
   294 	  $thisExport = "$self->{dir}\\$thisExport";
       
   295 	}
       
   296 	else {
       
   297 	  print "Warning: Couldn't find location of \"$thisExport\n";
       
   298 	  next;
       
   299 	}
       
   300       }
       
   301       Utils::AbsoluteFileName(\$thisExport);
       
   302       push (@{$self->{exports}}, $thisExport);
       
   303     }
       
   304   }
       
   305   close (BLDINF);
       
   306   Utils::PopDir();
       
   307   if ($doDie) {
       
   308     die "Aborting due to above errors\n";
       
   309   }
       
   310 }
       
   311 
       
   312 
       
   313 #
       
   314 # Mmp
       
   315 #
       
   316 
       
   317 package Mmp;
       
   318 
       
   319 
       
   320 #
       
   321 # Public.
       
   322 #
       
   323 
       
   324 sub New {
       
   325   my $pkg = shift;
       
   326   my $self = {};
       
   327   bless $self, $pkg;
       
   328   $self->{name} = shift;
       
   329   $self->{verbose} = shift;
       
   330   $self->Parse();
       
   331   return $self;
       
   332 }
       
   333 
       
   334 sub CheckDefFile {
       
   335   my $self = shift;
       
   336   my $other = shift;
       
   337   my $ignoreR3Unused = shift;
       
   338   if ($self->{def}) {
       
   339     return $self->{def}->Check($other->{def}, $ignoreR3Unused);
       
   340   }
       
   341   return 1;
       
   342 }
       
   343 
       
   344 sub ListConstructors {
       
   345   my $self = shift;
       
   346   if ($self->{def}) {
       
   347     return $self->{def}->ListConstructors();
       
   348   }
       
   349   return [];
       
   350 }
       
   351 
       
   352 sub ListIncludes {
       
   353   my $self = shift;
       
   354   if (exists $self->{includes}) {
       
   355     my @includes = keys %{$self->{includes}};
       
   356     return \@includes;
       
   357   }
       
   358   return [];
       
   359 }
       
   360 
       
   361 
       
   362 #
       
   363 # Private.
       
   364 #
       
   365 
       
   366 sub Parse {
       
   367   my $self = shift;
       
   368   if ($self->{verbose}) {  print "Parsing $self->{name}...\n"; }
       
   369   (my $path) = Utils::SplitFileName($self->{name});
       
   370   $path =~ s/(.*)\\.*/$1/; # Extract path.
       
   371   Utils::PushDir($path);
       
   372   unless (open (MMP, "cpp -DARM -DMARM $self->{name}|")) {
       
   373     Utils::PopDir();
       
   374     die "Error: Couldn't open \"cpp -DARM -DMARM $self->{name}\": $!\n";
       
   375   }
       
   376   my $noStrictDef = 0;
       
   377   my $targetType = '';
       
   378   while (my $line = <MMP>) {
       
   379     if ($line =~ /^#/ or $line =~ /^\s*$/) {	
       
   380 	# Ignore lines starting with '#' or those filled with white space.
       
   381 	next;
       
   382       }
       
   383     chomp $line;
       
   384     if ($line =~ /^\s*targettype\s+(\S*)\s*$/i) {
       
   385 	$targetType = $1;
       
   386     }
       
   387     elsif ($line =~ /^\s*deffile\s+(\S*)\s*$/i) {
       
   388       die if exists $self->{defFileName};
       
   389       $self->{defFileName} = $1;
       
   390     }	 
       
   391     elsif ($line =~ /nostrictdef/i) {
       
   392       $noStrictDef = 1;
       
   393     }
       
   394     elsif ($line =~ /^\s*userinclude\s+(.+)/i) {
       
   395       my @userIncludes = split (/\s+/, $1);
       
   396       foreach my $thisUserInclude (@userIncludes) {
       
   397 	$thisUserInclude =~ s/\+/$ENV{EPOCROOT}epoc32/;
       
   398 	Utils::AbsoluteFileName(\$thisUserInclude);
       
   399 	$self->{includes}->{lc($thisUserInclude)} = 1;
       
   400       }
       
   401     }
       
   402     elsif ($line =~ /^\s*systeminclude\s+(.+)/i) {
       
   403       my @systemIncludes = split (/\s+/, $1);
       
   404       foreach my $thisSystemInclude (@systemIncludes) {
       
   405 	$thisSystemInclude =~ s/\+/$ENV{EPOCROOT}epoc32/;
       
   406 	Utils::AbsoluteFileName(\$thisSystemInclude);
       
   407 	$self->{includes}->{lc($thisSystemInclude)} = 1;
       
   408       }
       
   409     }
       
   410   }
       
   411   close (MMP);
       
   412 
       
   413   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) {
       
   414     # Don't bother looking for the deffile.
       
   415     Utils::PopDir();
       
   416     return;
       
   417   }
       
   418   
       
   419   (my $mmpPath, my $mmpBase) = Utils::SplitFileName($self->{name});
       
   420   if (exists $self->{defFileName}) {
       
   421     (my $path, my $base, my $ext) = Utils::SplitFileName($self->{defFileName});
       
   422     if ($base eq '') {
       
   423       $base = $mmpBase;
       
   424     }
       
   425     if ($ext eq '') {
       
   426       $ext = '.def';
       
   427     }
       
   428     if ($path eq '') {
       
   429       $path = $mmpPath;
       
   430     }
       
   431     unless ($noStrictDef) {
       
   432       $base .= 'u';
       
   433     }
       
   434     unless (-e "$path$base$ext") {
       
   435       $path = "$path..\\bmarm\\";
       
   436     }
       
   437     unless (-e "$path$base$ext") {
       
   438       $path = $mmpPath . $path;
       
   439     }
       
   440     $self->{defFileName} = "$path$base$ext";
       
   441     Utils::AbsoluteFileName(\$self->{defFileName});
       
   442   }
       
   443   else {
       
   444     # Assume default.
       
   445     $self->{defFileName} = $mmpBase;
       
   446     unless ($noStrictDef) {	
       
   447       $self->{defFileName} .= 'u';
       
   448     }
       
   449     $self->{defFileName} .= '.def';
       
   450     $self->AddDefaultDefFilePath();
       
   451   }
       
   452 
       
   453   if ($self->{defFileName}) {
       
   454     $self->{def} = Def->New($self->{defFileName}, $self->{verbose});
       
   455   }
       
   456 
       
   457   Utils::PopDir();
       
   458 }
       
   459 
       
   460 sub AddDefaultDefFilePath {
       
   461   my $self = shift;
       
   462   (my $path) = Utils::SplitFileName($self->{name});
       
   463   $self->{defFileName} = "$path\\..\\bmarm\\$self->{defFileName}";
       
   464   if (-e $self->{defFileName}) {
       
   465     Utils::AbsoluteFileName(\$self->{defFileName});
       
   466   }
       
   467   else {
       
   468     print "Warning: Unable to find def file in \"$self->{name}\"\n";
       
   469     delete $self->{defFileName};
       
   470   }
       
   471 }
       
   472 
       
   473 
       
   474 #
       
   475 # Def
       
   476 #
       
   477 
       
   478 package Def;
       
   479 
       
   480 
       
   481 #
       
   482 # Public.
       
   483 #
       
   484 
       
   485 sub New {
       
   486   my $pkg = shift;
       
   487   my $self = {};
       
   488   bless $self, $pkg;
       
   489   $self->{name} = shift;
       
   490   $self->{verbose} = shift;
       
   491   $self->Parse();
       
   492   $self->DemangleNames();
       
   493   return $self;
       
   494 }
       
   495 
       
   496 sub Check {
       
   497   my $self = shift;
       
   498   my $other = shift;
       
   499   my $ignoreR3Unused = shift;
       
   500   if ($self->{verbose}) { print "Checking DEF file \"$self->{name}\" against \"$other->{name}\"...\n"; }
       
   501   my $passed = 1;
       
   502   if (exists $self->{data}) {
       
   503     for (my $ii = 0; $ii < scalar(@{$self->{data}}); ++$ii) {
       
   504       my $ordinal = $ii + 1;
       
   505       if ($ii >= scalar @{$other->{data}}) {
       
   506 	print "Failure reason: \"$self->{name}\" has more exports than \"$other->{name}\"\n";
       
   507 	$passed = 0;
       
   508 	last;
       
   509       }
       
   510       my $selfRaw = $self->{data}->[$ii]->{raw};
       
   511       my $otherRaw = $other->{data}->[$ii]->{raw};
       
   512       if ($ignoreR3Unused) {
       
   513 	$selfRaw =~ s/R3UNUSED //;
       
   514 	$otherRaw =~ s/R3UNUSED //;
       
   515       }
       
   516       unless ($selfRaw eq $otherRaw) {
       
   517 	$passed = 0;
       
   518 	print "Failure reason: Def file mismatch between \"$self->{name}\" and \"$other->{name}\" at $ordinal\n";
       
   519 	if ($self->{verbose}) {
       
   520 	  print "\t$self->{data}->[$ii]->{raw}\n\t$other->{data}->[$ii]->{raw}\n";
       
   521 	}
       
   522       }
       
   523     }
       
   524   }
       
   525   return $passed;
       
   526 }
       
   527 
       
   528 sub ListConstructors {
       
   529   my $self = shift;
       
   530   my @constructors = ();
       
   531   if (exists $self->{data}) {
       
   532     my $ordinal = 0;
       
   533     foreach my $thisEntry (@{$self->{data}}) {
       
   534       $ordinal++;
       
   535       die unless (exists $thisEntry->{function});
       
   536       if ($thisEntry->{function} =~ /(.+)::(.+)\(/) {
       
   537 	if ($1 eq $2) {
       
   538 	  push (@constructors, $1);
       
   539 	}
       
   540       }
       
   541     }
       
   542   }
       
   543   return \@constructors;
       
   544 }
       
   545 
       
   546 
       
   547 #
       
   548 # Private.
       
   549 #
       
   550 
       
   551 sub Parse {
       
   552   my $self = shift;
       
   553   open (DEF, $self->{name}) or die "Error: Couldn't open \"$self->{name}\" for reading: $!\n";
       
   554   my $lineNum = 0;
       
   555   while (my $thisLine = <DEF>) {
       
   556     ++$lineNum;
       
   557     chomp $thisLine;
       
   558     if ($thisLine =~ /^(EXPORTS|;|\s*$)/) {
       
   559       next;
       
   560     }
       
   561 	my $entry = {};
       
   562     $entry->{raw} = $thisLine;
       
   563 	     
       
   564     push (@{$self->{data}}, $entry);
       
   565   }
       
   566       close (DEF);
       
   567 }
       
   568 
       
   569 sub DemangleNames {
       
   570   my $self = shift;
       
   571   open (FILT, "type $self->{name} | c++filt |") or die "Error: Couldn't open \"type $self->{name} | c++filt |\": $!\n";
       
   572   my $lineNum = 0;
       
   573   while (my $line = <FILT>) {
       
   574     ++$lineNum;
       
   575     chomp $line;
       
   576     next if ($line =~ /^(EXPORT|;|\s*$)/);
       
   577     if ($line =~ /^\s+(\"(.+)\"|(.+)) @ (\d+)/) {
       
   578       my $function;
       
   579       if ($2) {
       
   580 	$function = $2;
       
   581       }
       
   582       else {
       
   583 	die unless $3;
       
   584 	$function = $3;
       
   585       }
       
   586       my $ordinal = $4;
       
   587       $self->{data}->[$ordinal - 1]->{function} = $function;
       
   588     }
       
   589     else {
       
   590       die "Error: Unable to parse c++filt output for \"$self->{name}\" at line $lineNum\n";
       
   591     }
       
   592   }
       
   593   close (FILT);
       
   594 }
       
   595 
       
   596 
       
   597 #
       
   598 # ClassSize
       
   599 #
       
   600 
       
   601 package ClassSize;
       
   602 
       
   603 
       
   604 #
       
   605 # Public.
       
   606 #
       
   607 
       
   608 sub New {
       
   609   my $pkg = shift;
       
   610   my $self = {};
       
   611   bless $self, $pkg;
       
   612   $self->{classes} = shift;
       
   613   $self->{headers} = shift;
       
   614   $self->{includes} = shift;
       
   615   $self->{verbose} = shift;
       
   616   $self->{compName} = shift;
       
   617   $self->{bldInfDir} = shift;
       
   618   if (scalar @{$self->{classes}} > 0) {
       
   619     $self->GetClassSizes();
       
   620   }
       
   621   return $self;
       
   622 }
       
   623 
       
   624 sub Check {
       
   625   my $self = shift;
       
   626   my $other = shift;
       
   627   if ($self->{verbose}) { print "Comparing class sizes of \"$self->{bldInfDir}\" against \"$other->{bldInfDir}\"..\n"; }
       
   628   my $passed = 1;
       
   629   foreach my $thisClass (keys %{$self->{classSizes}}) {
       
   630     if ($self->{verbose}) { print "Examining class sizes of \"$thisClass\"...\n"; }
       
   631     unless (exists $other->{classSizes}->{$thisClass}) {
       
   632       print "Failure reason: \"$thisClass\" not found (possibly renamed)\n";
       
   633       $passed = 0;
       
   634       next;
       
   635     }
       
   636     unless ($self->{classSizes}->{$thisClass} == $other->{classSizes}->{$thisClass}) {
       
   637       $passed = 0;
       
   638       print "Failure reason: Class \"$thisClass\" has changed size from $self->{classSizes}->{$thisClass} to $other->{classSizes}->{$thisClass}\n";
       
   639     }
       
   640   }
       
   641   return $passed;
       
   642 }
       
   643 
       
   644 
       
   645 #
       
   646 # Private.
       
   647 #
       
   648 
       
   649 sub GetClassSizes {
       
   650   my $self = shift;
       
   651   eval {
       
   652     $self->GenerateCode();
       
   653     $self->CompileCode();
       
   654     $self->GetOutput();
       
   655   };
       
   656   $self->CleanUp();
       
   657   if ($@) {
       
   658     die $@;
       
   659   }
       
   660 }
       
   661 
       
   662 sub GenerateCode {
       
   663   my $self = shift;
       
   664   open (CODE, '>__ClassSize.cpp') or die "Error: Couldn't open \"__ClassSize.cpp\" for writing: $!\n";
       
   665   print CODE "#include <stdio.h>\n";
       
   666   print CODE "#include <e32std.h>\n";
       
   667   print CODE "#include <e32def.h>\n";
       
   668   print CODE "#include <e32base.h>\n";
       
   669   foreach my $thisHeader (@{$self->{headers}}) {
       
   670     print CODE "#include <$thisHeader>\n";
       
   671   }
       
   672   print CODE "int main(int argc, char* argv[]) {\n";
       
   673   foreach my $thisClass (@{$self->{classes}}) {
       
   674     print CODE "\tprintf(\"$thisClass\\t%d\\n\", sizeof($thisClass));\n";
       
   675   }
       
   676   print CODE "\treturn 0; }\n";
       
   677   close (CODE);
       
   678 }
       
   679 
       
   680 sub CompileCode {
       
   681   my $self = shift;
       
   682   my $command = 'cl ';
       
   683   foreach my $thisInclude (@{$self->{includes}}) {
       
   684     $command .= " /I$thisInclude";
       
   685   }
       
   686   $command .= " /D__VC32__ /D__WINS__ /D__SYMBIAN32__ /DWIN32 /D_WINDOWS /D_UNICODE __ClassSize.cpp";
       
   687   unless ($self->{verbose}) {
       
   688     $command .= ' /nologo 2>&1 > NUL';
       
   689   }
       
   690   if (system ($command)) {
       
   691     if (exists $self->{compName} and $self->{compName}) {
       
   692       rename ("__ClassSize.cpp", "$self->{compName}.cpp");
       
   693     }
       
   694     else {
       
   695       rename ("__ClassSize.cpp", "unknown.cpp");
       
   696     }
       
   697     die "Error: Problem executing \"$command\"\n";
       
   698   }
       
   699 }
       
   700 
       
   701 sub GetOutput {
       
   702   my $self = shift;
       
   703   open (OUTPUT, '__ClassSize.exe|') or die "Error: Couldn't run \"__ClassSize.exe\": $!\n";
       
   704   while (my $thisLine = <OUTPUT>) {
       
   705     chomp $thisLine;
       
   706     next if ($thisLine =~ /^\s*$/);
       
   707     if ($thisLine =~ /^(\S+)\t(\d+)$/) {
       
   708       $self->{classSizes}->{$1} = $2;
       
   709     }
       
   710     else {
       
   711       die "Error: Problem parsing output of \"__ClassSize.exe\"\n";
       
   712     }
       
   713   }
       
   714   close (OUTPUT);
       
   715 }
       
   716 
       
   717 sub CleanUp {
       
   718   my $self = shift;
       
   719   DeleteFile('__ClassSize.cpp');
       
   720   DeleteFile('__ClassSize.obj');
       
   721   DeleteFile('__ClassSize.exe');
       
   722 }
       
   723 
       
   724 sub DeleteFile {
       
   725   my $file = shift;
       
   726   if (-e $file) {
       
   727     unlink ($file) or die "Error: Couldn't delete \"$file\"\n";
       
   728   }
       
   729 }
       
   730 
       
   731 
       
   732 #
       
   733 # VTable
       
   734 #
       
   735 
       
   736 package VTable;
       
   737 
       
   738 
       
   739 #
       
   740 # Public.
       
   741 #
       
   742 
       
   743 sub New {
       
   744   my $pkg = shift;
       
   745   my $self = {};
       
   746   bless $self, $pkg;
       
   747   $self->{bldInfDir} = shift;
       
   748   my $classes = shift;
       
   749   foreach my $class (@$classes) {
       
   750     $self->{classes}->{$class} = 1;
       
   751   }
       
   752   $self->{verbose} = shift;
       
   753 
       
   754   Utils::PushDir($self->{bldInfDir});
       
   755   eval {
       
   756     $self->BuildAssemblerListings();
       
   757     $self->ParseAssemblerListings();
       
   758     $self->DeleteAssemblerListings();
       
   759     };
       
   760   Utils::PopDir();
       
   761   if ($@) {
       
   762     die $@;
       
   763   }
       
   764   return $self;
       
   765 }
       
   766 
       
   767 sub Check {
       
   768   my $self = shift;
       
   769   my $other = shift;
       
   770   if ($self->{verbose}) { print "Comparing vtable layout of \"$self->{bldInfDir}\" against \"$other->{bldInfDir}\"..\n"; }
       
   771   my $passed = 1;
       
   772   foreach my $class (keys %{$self->{vtables}}) {
       
   773     if (exists $other->{vtables}->{$class}) {
       
   774       if ($self->{verbose}) { print "Examining vtable of class \"$class\"...\n"; }
       
   775       for (my $ii = 0; $ii < scalar (@{$self->{vtables}->{$class}}); ++$ii) {
       
   776 	my $thisVTableEntry = $self->{vtables}->{$class}->[$ii];
       
   777 	if ($ii >= scalar (@{$other->{vtables}->{$class}})) {
       
   778 	  print "Failure reason: Unexpected vtable entry \"$thisVTableEntry\"\n";
       
   779 	  $passed = 0;
       
   780 	  last;
       
   781 	}
       
   782 	my $otherVTableEntry = $other->{vtables}->{$class}->[$ii];
       
   783 	if ($thisVTableEntry eq $otherVTableEntry) {
       
   784 	  if ($self->{verbose}) { print "\tMatched vtable entry \"$thisVTableEntry\"\n"; }
       
   785 	}
       
   786 	else {
       
   787 	  print "Failure reason: Mismatched vtable entries in class \"$class\"\n\t$thisVTableEntry\n\t$otherVTableEntry\n";
       
   788 	  $passed = 0;
       
   789 	}
       
   790       }
       
   791     }
       
   792     else {
       
   793       print "Failure reason: Vtable for \"$class\" missing from $other->{bldInfDir}\n";
       
   794       $passed = 0;
       
   795     }
       
   796   }
       
   797   return $passed;
       
   798 }
       
   799 
       
   800 
       
   801 
       
   802 #
       
   803 # Private.
       
   804 #
       
   805 
       
   806 sub BuildAssemblerListings {
       
   807   my $self = shift;
       
   808   if ($self->{verbose}) { print "Calling \"bldmake bldfiles\" in \"$self->{bldInfDir}\"\n"; }
       
   809   open (BLDMAKE, "bldmake bldfiles 2>&1 |") or die "Error: Couldn't run \"bldmake bldfiles\" in \"$self->{bldInfDir}\": $!\n";
       
   810   while (my $line = <BLDMAKE>) {
       
   811     if ($line) {
       
   812       if ($self->{verbose}) { print "\t$line"; }
       
   813       die "Error: Problem running \"bldmake bldfiles\" in \"$self->{bldInfDir}\"\n";
       
   814     }
       
   815   }
       
   816   close (BLDMAKE);
       
   817 
       
   818   if ($self->{verbose}) { print "Calling \"abld makefile arm4\" in \"$self->{bldInfDir}\"\n"; }
       
   819   open (ABLD, "abld makefile arm4 2>&1 |") or die "Error: Couldn't run \"abld makefile arm4\" in \"$self->{bldInfDir}\": $!\n";
       
   820   while (my $line = <ABLD>) {
       
   821     if ($line) {
       
   822       if ($self->{verbose}) { print "\t$line"; }
       
   823     }
       
   824   }
       
   825   close (ABLD);
       
   826   
       
   827   if ($self->{verbose}) { print "Calling \"abld listing arm4 urel\" in \"$self->{bldInfDir}\"\n"; }
       
   828   open (ABLD, "abld listing arm4 urel 2>&1 |") or die "Error: Couldn't run \"abld listing arm4 urel\" in \"$self->{bldInfDir}\": $!\n";
       
   829   while (my $line = <ABLD>) {
       
   830     if ($line) {
       
   831       if ($self->{verbose}) { print "\t$line"; }
       
   832       if ($line =~ /^Created (.*)/) {
       
   833 	my $listingFile = $1;
       
   834 	push (@{$self->{listingFiles}}, $listingFile);
       
   835       }
       
   836     }
       
   837   }
       
   838   close (ABLD);
       
   839 }
       
   840 
       
   841 sub ParseAssemblerListings {
       
   842   my $self = shift;
       
   843   foreach my $listing (@{$self->{listingFiles}}) {
       
   844     open (LISTING, $listing) or die "Error: Couldn't open \"$listing\" for reading: $!\n";
       
   845     while (my $line = <LISTING>) {
       
   846       if ($line =~ /^\s.\d+\s+__vt_\d+(\D+):$/) {  # If start of vtable section.
       
   847 	my $class = $1;
       
   848 	if (exists $self->{classes}->{$class}) { # If one of the classes we're interested in.
       
   849 	  while (my $line2 = <LISTING>) {
       
   850 	    if ($line2 =~ /^\s.\d+\s[\da-fA-F]{4}\s[\da-fA-F]{8}\s+\.word\s+(.*)/) {  # If this is a valid vtable entry.
       
   851 	      my $vtableEntry = $1;
       
   852 	      push (@{$self->{vtables}->{$class}}, $vtableEntry);
       
   853 	    }
       
   854 	    else {
       
   855 	      last;
       
   856 	    }
       
   857 	  }
       
   858 	}
       
   859       }
       
   860     }
       
   861     close (LISTING);
       
   862   }
       
   863 }
       
   864 
       
   865 sub DeleteAssemblerListings {
       
   866   my $self = shift;
       
   867   foreach my $listing (@{$self->{listingFiles}}) {
       
   868     unlink $listing or die "Error: Unable to delete \"$listing\": $!\n";
       
   869   }
       
   870 }
       
   871 
       
   872 
       
   873 #
       
   874 # Utils.
       
   875 #
       
   876 
       
   877 package Utils;
       
   878 
       
   879 use File::Basename;
       
   880 use Cwd 'abs_path', 'cwd';
       
   881 use Win32;
       
   882 
       
   883 sub AbsoluteFileName {
       
   884   my $fileName = shift;
       
   885   unless (-e $$fileName) {
       
   886     die "Error: \"$$fileName\" does not exist\n";
       
   887   }
       
   888   (my $base, my $path) = fileparse($$fileName);
       
   889   my $absPath = abs_path($path);
       
   890   $$fileName = $absPath;
       
   891   unless ($$fileName =~ /[\\\/]$/) {
       
   892     $$fileName .= "\\";
       
   893   }
       
   894   $$fileName .= $base;
       
   895   TidyFileName($fileName);
       
   896 }
       
   897 
       
   898 sub SplitFileName {
       
   899   my $fileName = shift;
       
   900   my $path = '';
       
   901   my $base = '';
       
   902   my $ext = '';
       
   903 
       
   904   if ($fileName =~ /\\?([^\\]*?)(\.[^\\\.]*)?$/) {
       
   905     $base = $1;
       
   906   }
       
   907   if ($fileName =~ /^(.*\\)/) {
       
   908     $path = $1;
       
   909   }
       
   910   if ($fileName =~ /(\.[^\\\.]*)$/o) {
       
   911     $ext =  $1;
       
   912   }
       
   913 
       
   914   die unless ($fileName eq "$path$base$ext");
       
   915   return ($path, $base, $ext);
       
   916 }
       
   917 
       
   918 sub TidyFileName {
       
   919   my $a = shift;
       
   920   $$a =~ s/\//\\/g;      # Change forward slashes to back slashes.
       
   921   $$a =~ s/\\\.\\/\\/g;  # Change "\.\" into "\".
       
   922 
       
   923   if ($$a =~ /^\\\\/) {  # Test for UNC paths.
       
   924     $$a =~ s/\\\\/\\/g;  # Change "\\" into "\".
       
   925     $$a =~ s/^\\/\\\\/;  # Add back a "\\" at the start so that it remains a UNC path.
       
   926   }
       
   927   else {
       
   928     $$a =~ s/\\\\/\\/g;  # Change "\\" into "\".
       
   929   }
       
   930 }
       
   931 
       
   932 my @dirStack;
       
   933 
       
   934 sub PushDir {
       
   935   my $dir = shift;
       
   936   my $cwd = cwd();
       
   937   chdir ($dir) or die "Error: Couldn't change working directory to \"$dir\": $!\n";
       
   938   push (@dirStack, $cwd);
       
   939 }
       
   940 
       
   941 sub PopDir {
       
   942   if (scalar @dirStack > 0) {
       
   943     my $dir = pop @dirStack;
       
   944     chdir ($dir) or die "Error: Couldn't change working directory to \"$dir\": $!\n";
       
   945   }
       
   946   else {
       
   947     die "Error: Directory stack empty";
       
   948   }
       
   949 }
       
   950 
       
   951 
       
   952 1;
       
   953 
       
   954 =head1 NAME
       
   955 
       
   956 CheckBc.pm - A module that runs some simple tests to see if one component source tree is backwards compatible another.
       
   957 
       
   958 =head1 SYNOPSIS
       
   959 
       
   960   my $checkBc = CheckBc->New('\branch1\comp\group', '\branch2\comp\group', 0);
       
   961   unless ($checkBc->CheckAll()) {
       
   962     print "Check failed\n";
       
   963   }
       
   964 
       
   965 =head1 DESCRIPTION
       
   966 
       
   967 C<CheckBc> does the following checks to see if a backwards compatibility breaking change has been introduced:
       
   968 
       
   969 =over 4
       
   970 
       
   971 =item 1
       
   972 
       
   973 Compares the ARM F<.def> files to ensure that only new lines have been added to the end of the file.
       
   974 
       
   975 =item 2
       
   976 
       
   977 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<sizeof> operator to print the relevant class sizes to C<STDOUT>. Compilation is done using the MSVC++ compiler.
       
   978 
       
   979 =item 3
       
   980 
       
   981 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.
       
   982 
       
   983 =back
       
   984 
       
   985 =head1 LIMITATIONS
       
   986 
       
   987 =over 4
       
   988 
       
   989 =item 1
       
   990 
       
   991 The component's headers must compile using Microsoft's Visual C++ compiler.
       
   992 
       
   993 =item 2
       
   994 
       
   995 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.
       
   996 
       
   997 =item 3
       
   998 
       
   999 Declarations of the component's exported C++ constructors must be found in one of the exported headers.
       
  1000 
       
  1001 =item 4
       
  1002 
       
  1003 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.
       
  1004 
       
  1005 =item 5
       
  1006 
       
  1007 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.
       
  1008 
       
  1009 =back
       
  1010 
       
  1011 =head1 KNOWN BUGS
       
  1012 
       
  1013 F<bld.inf>, F<.mmp> and F<.def> file parsing is probably not as industrial strength as it should be.
       
  1014 
       
  1015 =head1 COPYRIGHT
       
  1016 
       
  1017  Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies).
       
  1018  All rights reserved.
       
  1019  This component and the accompanying materials are made available
       
  1020  under the terms of the License "Eclipse Public License v1.0"
       
  1021  which accompanies this distribution, and is available
       
  1022  at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
  1023  
       
  1024  Initial Contributors:
       
  1025  Nokia Corporation - initial contribution.
       
  1026  
       
  1027  Contributors:
       
  1028  
       
  1029  Description:
       
  1030  
       
  1031 
       
  1032 =cut