releasing/cbrtools/perl/EnvDb.pm
changeset 602 3145852acc89
equal deleted inserted replaced
600:6d08f4a05d93 602:3145852acc89
       
     1 # Copyright (c) 2000-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 package EnvDb;
       
    17 
       
    18 use strict;
       
    19 use MLDBM::Sync;                       # this gets the default, SDBM_File
       
    20 use MLDBM qw(DB_File Storable);        # use Storable for serializing
       
    21 use MLDBM qw(MLDBM::Sync::SDBM_File);  # use extended SDBM_File, handles values > 1024 bytes
       
    22 use Fcntl qw(:DEFAULT);                # import symbols O_CREAT & O_RDWR for use with DBMs
       
    23 use Cwd;
       
    24 use File::Find;
       
    25 use File::Copy;
       
    26 use File::Basename;
       
    27 use File::Path;
       
    28 use File::Spec;
       
    29 use Fcntl;
       
    30 use MrpData;
       
    31 use RelData;
       
    32 use DirHandle; # we're recursing in CrossCheckSourceDirectory, so this is slightly nicer than DIRHANDLEs
       
    33                 # (though not actually necessary, as it happens)
       
    34 use Utils;
       
    35 use CatData;
       
    36 use Carp;
       
    37 use Symbian::CBR::Component::Manifest;
       
    38 
       
    39 #
       
    40 # Constants.
       
    41 #
       
    42 
       
    43 use constant DB_NAME => "\\epoc32\\relinfo\\envdb";
       
    44 use constant STATUS_CLEAN => 0;
       
    45 use constant STATUS_DIRTY => 1;
       
    46 use constant STATUS_PENDING_RELEASE => 2;
       
    47 use constant STATUS_INFORMATION_ONLY => 5;
       
    48 use constant STATUS_NOT_INSTALLED => 3;
       
    49 use constant STATUS_DIRTY_SOURCE => 4;
       
    50 use constant STATUS_STRING_PASSED => "clean";
       
    51 use constant STATUS_STRING_FAILED => "dirty";
       
    52 use constant STATUS_STRING_MISSING => "missing";
       
    53 use constant STATUS_STRING_PENDING_RELEASE => "pending release";
       
    54 use constant STATUS_STRING_NOT_INSTALLED => "not installed";
       
    55 use constant STATUS_STRING_DIRTY_SOURCE => "binaries clean, source dirty";
       
    56 use constant STATUS_STRING_INFORMATION_ONLY => "Information only";
       
    57 use constant SCAN_PROGRESS_TUNER => 50;
       
    58 use constant ACCEPTABLE_EVALID_FAILURES => "abld.bat"; # this is a regexp - use | to add more items. It is case-insensitive.
       
    59 
       
    60 #
       
    61 # Public.
       
    62 #
       
    63 
       
    64 sub Open {
       
    65   my $pkg = shift;
       
    66   my $iniData = shift;
       
    67   my $verbose = shift;
       
    68   
       
    69   # Check that the environment is not on an illegal volume - INC105548
       
    70   Utils::CheckIllegalVolume($iniData);
       
    71 
       
    72   my $dbName = Utils::PrependEpocRoot(DB_NAME);
       
    73   my $dbDir = dirname($dbName);
       
    74   unless (-e $dbDir) {
       
    75     Utils::MakeDir($dbDir);
       
    76   }
       
    77   my $db;
       
    78   {
       
    79     local $^W = 0;
       
    80     tie (%{$db}, 'MLDBM::Sync', $dbName, O_CREAT|O_RDWR, 0666) || die "Couldn't open database DB_NAME: $!\n";
       
    81   }
       
    82 
       
    83   my $self = {iniData => $iniData,
       
    84 	      db => $db,
       
    85         mrpcache => {},
       
    86 	      verbose => ($verbose || 0)};
       
    87   bless $self, $pkg;
       
    88   return $self;
       
    89 }
       
    90 
       
    91 sub Close {
       
    92   my $self = shift;
       
    93   untie %{$self};
       
    94 }
       
    95 
       
    96 sub ComponentExistsInDatabase {
       
    97   my $self = shift;
       
    98   my $comp = shift;
       
    99   
       
   100   return 1 if (exists $self->{db}->{$comp});
       
   101 }
       
   102 
       
   103 sub Version {
       
   104   my $self = shift;
       
   105   my $comp = shift;
       
   106   my $includeInformationOnlyEntries = shift;
       
   107   
       
   108   $comp = lc($comp); # Note, component names are always stored in lower case.
       
   109   my $entry = $self->{db}->{$comp};
       
   110   
       
   111   if (defined $entry) {
       
   112     if (!$includeInformationOnlyEntries && $entry->{status} eq STATUS_INFORMATION_ONLY) {
       
   113       # Some callers are not interested in information only entries
       
   114       return undef;
       
   115     }
       
   116     
       
   117     return $entry->{ver};
       
   118   }
       
   119   return undef;
       
   120 }
       
   121 
       
   122 sub VersionInfo {
       
   123   my $self = shift;
       
   124   my $includeInformationOnlyEntries = shift;
       
   125   
       
   126   my $versionInfo;
       
   127   foreach my $thisKey (keys %{$self->{db}}) {
       
   128     if (!$includeInformationOnlyEntries) {
       
   129       # Some callers are not interested in information only entries
       
   130       next if ($self->{db}->{$thisKey}->{status} eq STATUS_INFORMATION_ONLY);
       
   131     }
       
   132     
       
   133     $versionInfo->{$thisKey} = $self->{db}->{$thisKey}->{ver};
       
   134   }
       
   135   return $versionInfo;
       
   136 }
       
   137 
       
   138 sub SetVersion {
       
   139   my $self = shift;
       
   140   my $comp = lc(shift);
       
   141   my $ver = shift;
       
   142 
       
   143   my $entry = $self->{db}->{$comp};
       
   144   if (defined $ver) {
       
   145     if (defined $entry->{ver} and $entry->{status} != STATUS_PENDING_RELEASE) {
       
   146       $self->DeleteSignature($comp, $entry->{ver});
       
   147     }
       
   148     $entry->{ver} = $ver;
       
   149 
       
   150     # Write entry to database.
       
   151     $self->{db}->{$comp} = $entry;
       
   152   }
       
   153   else {
       
   154     # undefined version, so remove entry from database (if it was present).
       
   155     if (defined $entry) {
       
   156       delete $self->{db}->{$comp}
       
   157     }
       
   158   }
       
   159 }
       
   160 
       
   161 sub InternalVersion {
       
   162   my $self = shift;
       
   163   my $comp = shift;
       
   164   $comp = lc($comp); # Note, component names are always stored in lower case.
       
   165   my $entry = $self->{db}->{$comp};
       
   166   if (defined $entry) {
       
   167     return $entry->{intVer};
       
   168   }
       
   169   return undef;
       
   170 }
       
   171 
       
   172 sub SetInternalVersion {
       
   173   my $self = shift;
       
   174   my $comp = lc(shift);
       
   175   my $intVer = shift;
       
   176 
       
   177   my $entry = $self->{db}->{$comp};
       
   178   unless (defined $entry) {
       
   179     die "Error: $comp not found in environment database\n";
       
   180   }
       
   181   $entry->{intVer} = $intVer;
       
   182 
       
   183   # Write entry to database.
       
   184   $self->{db}->{$comp} = $entry;
       
   185 }
       
   186 
       
   187 sub Status {
       
   188   my $self = shift;
       
   189   my $comp = lc(shift);
       
   190 
       
   191   my $entry = $self->{db}->{$comp};
       
   192   unless (defined $entry) {
       
   193     die "Error: $comp not found in environment database\n";
       
   194   }
       
   195 
       
   196   return $entry->{status};
       
   197 }
       
   198 
       
   199 sub SetStatus {
       
   200   my $self = shift;
       
   201   my $comp = lc(shift);
       
   202   my $status = shift;
       
   203 
       
   204   my $entry = $self->{db}->{$comp};
       
   205   unless (defined $entry) {
       
   206     die "Error: $comp not found in environment database\n";
       
   207   }
       
   208   $entry->{status} = $status;
       
   209 
       
   210   # Write entry to database.
       
   211   $self->{db}->{$comp} = $entry;
       
   212 }
       
   213 
       
   214 sub StatusString {
       
   215   my $status = shift;
       
   216   if ($status == STATUS_CLEAN) {
       
   217     return STATUS_STRING_PASSED;
       
   218   }
       
   219   elsif ($status == STATUS_DIRTY) {
       
   220     return STATUS_STRING_FAILED;
       
   221   }
       
   222   elsif ($status == STATUS_PENDING_RELEASE) {
       
   223     return STATUS_STRING_PENDING_RELEASE;
       
   224   }
       
   225   elsif ($status == STATUS_DIRTY_SOURCE) {
       
   226     return STATUS_STRING_DIRTY_SOURCE;
       
   227   }
       
   228   elsif ($status == STATUS_INFORMATION_ONLY) {
       
   229     return STATUS_STRING_INFORMATION_ONLY;
       
   230   }  
       
   231 }
       
   232 
       
   233 sub CheckCompName {
       
   234   my $self = shift;
       
   235   my $comp = shift;
       
   236   die "Error: Component name can't begin with .(dot) \"$comp\".\n" if ($comp =~ m/^\./);
       
   237 }
       
   238 
       
   239 sub MrpName {
       
   240   my $self = shift;
       
   241   my $comp = lc(shift);
       
   242 
       
   243   my $entry = $self->{db}->{$comp};
       
   244   unless (defined $entry) {
       
   245     die "Error: $comp not found in environment database\n";
       
   246   }
       
   247 
       
   248   return $entry->{mrpName};
       
   249 }
       
   250 
       
   251 sub SetMrpName {
       
   252   my $self = shift;
       
   253   my $comp = lc(shift);
       
   254   my $mrpName = shift;
       
   255 
       
   256   my $entry = $self->{db}->{$comp};
       
   257   unless (defined $entry) {
       
   258     die "Error: $comp not found in environment database\n";
       
   259   }
       
   260 
       
   261   $entry->{mrpName} = $mrpName;
       
   262 
       
   263   # Write entry to database.
       
   264   $self->{db}->{$comp} = $entry;
       
   265 }
       
   266 
       
   267 sub ComponentsPendingRelease {
       
   268   my $self = shift;
       
   269   my %comps;
       
   270   foreach my $thisComp (keys %{$self->{db}}) {
       
   271     my $thisEntry = $self->{db}->{$thisComp};
       
   272     if ($thisEntry->{status} == STATUS_PENDING_RELEASE) {
       
   273       $comps{$thisComp} = {mrpName => $thisEntry->{mrpName},
       
   274 			   ver => $thisEntry->{ver},
       
   275 			   intVer => $thisEntry->{intVer}};
       
   276     }
       
   277   }
       
   278   return \%comps;
       
   279 }
       
   280 
       
   281 sub GenerateSignature {
       
   282   my $self = shift;
       
   283   my $comp = lc (shift);
       
   284   my $ver = shift;
       
   285   my $sigName = SignatureName($comp, $ver);
       
   286   open (SIG, ">$sigName") or die "Error: Couldn't open $sigName: $!\n";
       
   287   foreach my $thisBinZip (@{$self->RelevantBinaryZips($comp, $ver)}) {
       
   288     foreach my $file (@{Utils::ListZip($thisBinZip, 1)}) {
       
   289       my $fileER = Utils::PrependEpocRoot($file);
       
   290       if (-f $fileER) {
       
   291         (my $mTime, my $size) = Utils::FileModifiedTimeAndSize($fileER);
       
   292         unless (defined $size) {
       
   293           die "Error: Problem reading stats of \"$fileER\"\n";
       
   294         }
       
   295         if ($self->{verbose} > 1) {
       
   296           print "Adding signature entry for \"$file\"\n";
       
   297           print "\tmTime: $mTime (", scalar gmtime($mTime), "\n";
       
   298           print "\tsize:  $size\n";
       
   299         }
       
   300         print SIG "$file\t$mTime\t$size\n";
       
   301       }
       
   302       else {
       
   303         print "Warning: Unexpected entry in \"$thisBinZip\": \"$file\"\n         $comp $ver could be corrupt or tampered with\n";
       
   304       }
       
   305     }
       
   306   }
       
   307   close (SIG);
       
   308 }
       
   309 
       
   310 sub GenerateFakeSignature {
       
   311 # As GenerateSignature, except the mtime and size of each file is set to zero.
       
   312 # This is intended to be used when validating against an external baseline.
       
   313   my $self = shift;
       
   314   my $comp = lc (shift);
       
   315   my $ver = shift;
       
   316   my $sigName = SignatureName($comp, $ver);
       
   317   open (SIG, ">$sigName") or die "Error: Couldn't open $sigName: $!\n";
       
   318   foreach my $thisBinZip (@{$self->RelevantBinaryZips($comp, $ver)}) {
       
   319     foreach my $file (@{Utils::ListZip($thisBinZip)}) {
       
   320       print SIG "$file\t0\t0\n";
       
   321     }
       
   322   }
       
   323   close (SIG);
       
   324 }
       
   325 
       
   326 sub GenerateEmptySignature {
       
   327   my $self = shift;
       
   328   my $comp = lc (shift);
       
   329   my $ver = shift;
       
   330   my $sigName = SignatureName($comp, $ver);
       
   331   open (SIG, ">$sigName") or die "Error: Couldn't open $sigName: $!\n";
       
   332   close (SIG);
       
   333 }
       
   334 
       
   335 sub RemoveComponent {
       
   336   my $self = shift;
       
   337   my $comp = lc(shift);
       
   338 
       
   339   # Read database entry.
       
   340   my $entry = $self->{db}->{$comp};
       
   341 
       
   342   if (defined $entry) {
       
   343     # Remove installed binaries.
       
   344     if ($self->{verbose}) { print "Removing binaries from $comp $entry->{ver}...\n"; }
       
   345     $self->DeleteFilesInSignature($comp, $entry->{ver});
       
   346     $self->DeleteSignature($comp, $entry->{ver});
       
   347 
       
   348     # Remove the database entry.
       
   349     delete $self->{db}->{$comp};
       
   350   }
       
   351   else {
       
   352     print "$comp not currently installed, aborting removal of binaries\n";
       
   353   }
       
   354 }
       
   355 
       
   356 sub RefreshComponent {
       
   357   my $self = shift;
       
   358   my $comp = lc(shift);
       
   359   my $overwrite = shift;
       
   360 
       
   361   # Read database entry.
       
   362   my $entry = $self->{db}->{$comp};
       
   363 
       
   364   if (!defined $entry) {
       
   365     print "$comp not currently installed; aborting refreshing of binaries\n";
       
   366   } elsif ($entry->{status} == STATUS_PENDING_RELEASE) {
       
   367     print "$comp is pending release and cannot be refreshed; use 'preprel' to remove it from your environment\n";
       
   368   } else {
       
   369     my $ver = $entry->{ver};
       
   370 
       
   371     my $relData = RelData->Open($self->{iniData}, $comp, $ver, $self->{verbose}); # Dies if release not in archive
       
   372     $relData->WarnIfReleaseTooNew();
       
   373 
       
   374     print "Removing $comp $ver..\n";
       
   375     if ($self->{verbose}) { print "Removing binaries from $comp $ver...\n"; }
       
   376     $self->DeleteFilesInSignature($comp, $entry->{ver});
       
   377 
       
   378     print "Installing $comp $ver...\n";
       
   379     $self->UnpackBinaries($comp, $ver, Utils::EpocRoot(), $overwrite);
       
   380 
       
   381     my $status = ($self->CheckComp($comp))[0];
       
   382     if ($status == STATUS_DIRTY) {
       
   383       print "WARNING: Installed component does not match existing signature; updating signature\n";
       
   384       $self->GenerateSignature($comp, $ver);
       
   385     }
       
   386   }
       
   387 }
       
   388 
       
   389 sub DeleteSource {
       
   390   my $self = shift;
       
   391   my $thisComp = shift;
       
   392   my $dryrun = shift;
       
   393   my $force = shift;
       
   394 
       
   395   my $ver = $self->Version($thisComp);
       
   396 
       
   397   if(!defined $ver) {
       
   398     die "ERROR: Unable to obtain version for $thisComp\n";
       
   399   }
       
   400 
       
   401   my $reldata = RelData->Open($self->{iniData}, $thisComp, $ver, $self->{verbose});
       
   402 
       
   403   my $srcitems = $reldata->SourceItems;
       
   404   foreach my $thisSrcItem (keys %$srcitems) {
       
   405     # If there are mappings and the source root is \\, perform mappings on filename. Otherwise prepend source root.
       
   406     if($self->{iniData}->HasMappings() && Utils::SourceRoot() eq "\\") {
       
   407       $thisSrcItem = $self->{iniData}->PerformMapOnFileName($thisSrcItem);
       
   408     }
       
   409     else{
       
   410       $thisSrcItem = Utils::PrependSourceRoot($thisSrcItem);
       
   411     }
       
   412 
       
   413     if ($self->{verbose} || $dryrun) {
       
   414       my $dir = (-d $thisSrcItem)?" (directory)":"";
       
   415       my $exists = (-e $thisSrcItem)?"":" (doesn't exist)";
       
   416       my $verb = $dryrun?"Would remove":"Removing";
       
   417       print "$verb $thisSrcItem$dir$exists\n";
       
   418     }
       
   419     {
       
   420         local $SIG{__WARN__} = sub {
       
   421             my $warn = shift;
       
   422 	    $warn =~ s/ at .*?EnvDb\.pm line \d+//;
       
   423 	    print STDERR "WARNING: $warn";
       
   424         };
       
   425         rmtree($thisSrcItem, 0, !$force) unless $dryrun;
       
   426     }
       
   427     my $directory = dirname($thisSrcItem);
       
   428 	
       
   429 	my @items = @{Utils::ReadDir($directory)};
       
   430 
       
   431 	if (scalar @items == 1 && $items[0] =~ /^distribution\.policy$/i) {
       
   432 	  unlink File::Spec->catdir($directory, shift @items) unless $dryrun;
       
   433 	}
       
   434 
       
   435     if (-e $directory && (!scalar @items)) { # No items in dir or just a distribution.policy file in dir
       
   436       rmdir $directory or die "Error: Could not remove directory $directory: $!";
       
   437       while (($directory = dirname($directory)) && -e $directory && !scalar @{Utils::ReadDir($directory)}) {
       
   438         rmdir $directory or die "Error: Could not remove directory $directory: $!";
       
   439       }
       
   440     }
       
   441   }
       
   442 }
       
   443 
       
   444 sub CheckEnv {
       
   445   my $self = shift;
       
   446   my $displayProgress = shift;
       
   447   my $ignoreStandardIgnores = shift;
       
   448   my $warnNotError = shift; # When validating the MrpData, warnings will be produced
       
   449                            # instead of errors when checking paths lengths DEF099673
       
   450   
       
   451   unless (defined $displayProgress) {
       
   452     $displayProgress = 0;
       
   453   }
       
   454   unless (defined $ignoreStandardIgnores) {
       
   455     $ignoreStandardIgnores = 0;
       
   456   }
       
   457 
       
   458   my $overallStatus = STATUS_CLEAN;
       
   459   my @dirtyComps;
       
   460 
       
   461   if ($displayProgress) {
       
   462     print "Scanning environment";
       
   463   }
       
   464 
       
   465   $self->InitIgnores($ignoreStandardIgnores);
       
   466   $self->ScanEnv($displayProgress);
       
   467 
       
   468   my @mrpData;
       
   469   my @errors;
       
   470   foreach my $thisComp (sort keys %{$self->{db}}) {
       
   471     (my $status, my $mrpData) = $self->CheckComp($thisComp, undef, $warnNotError);
       
   472     my $ver = $self->{db}->{$thisComp}->{ver};
       
   473     if ($status == STATUS_DIRTY || $status == STATUS_DIRTY_SOURCE) {
       
   474       $overallStatus = STATUS_DIRTY;
       
   475       push (@dirtyComps, {comp => $thisComp, ver => $ver});
       
   476     }
       
   477     elsif ($status == STATUS_PENDING_RELEASE) {
       
   478       unless ($overallStatus == STATUS_DIRTY) {
       
   479         $overallStatus = STATUS_PENDING_RELEASE;
       
   480       }
       
   481       if (defined $mrpData) {
       
   482         push @mrpData, $mrpData;
       
   483       }
       
   484       else {
       
   485         push @errors, "Error: Problem extracting mrp data from $thisComp\n";
       
   486       }
       
   487     }
       
   488     if ($displayProgress and not $self->{verbose}) {
       
   489       print '.';
       
   490     }
       
   491   }
       
   492   if ($displayProgress and not $self->{verbose}) {
       
   493     print "\n";
       
   494   }
       
   495 
       
   496   if ($#errors >= 0) {
       
   497     chomp $errors[$#errors];
       
   498     print @errors;
       
   499     die "\n";
       
   500   }
       
   501 
       
   502   $self->RemoveBinsToIgnore();
       
   503 
       
   504   my $unaccountedFiles = $self->UnaccountedEnvFiles();
       
   505   if (scalar(@$unaccountedFiles) >= 1) {
       
   506     $overallStatus = STATUS_DIRTY;
       
   507   }
       
   508 
       
   509   my $duplicates = $self->Duplicates(\@mrpData);
       
   510   if (scalar(@$duplicates) >= 1) {
       
   511     $overallStatus = STATUS_DIRTY;
       
   512   }
       
   513 
       
   514   return ($overallStatus, \@mrpData, \@dirtyComps, $unaccountedFiles, $duplicates);
       
   515 }
       
   516 
       
   517 sub CheckComp {
       
   518   my $self = shift;
       
   519   my $comp = lc(shift);
       
   520   my $keepGoing = shift;
       
   521   my $warnNotError = shift;
       
   522   
       
   523   unless (defined $keepGoing) {
       
   524     $keepGoing = 1;
       
   525   }
       
   526 
       
   527   my $entry = $self->{db}->{$comp};
       
   528   if (!defined $entry || $self->{db}->{$comp}->{status} == STATUS_INFORMATION_ONLY) {
       
   529     return (STATUS_NOT_INSTALLED);
       
   530   }
       
   531   my $oldstatus = $entry->{status};
       
   532   my $ver = $entry->{ver};
       
   533   die unless $ver;
       
   534   my $passed = 1;
       
   535 
       
   536   my $doCheck = sub {
       
   537     my $file = shift;
       
   538     my $sigMTime = shift;
       
   539     my $sigSize = shift;
       
   540 
       
   541     if (-e $file) { # Files might be installed in directories other than \epoc32, so do an explicit check.
       
   542       $self->CheckFileAgainstEnvScan($file);
       
   543       # Check the signature information against what is physically present in the environment.
       
   544       (my $actualMTime, my $actualSize) = Utils::FileModifiedTimeAndSize($file);
       
   545       if ($sigMTime != $actualMTime or $sigSize != $actualSize) {
       
   546         # File failed check.
       
   547         $passed = 0;
       
   548         if ($self->{verbose}) {
       
   549           print "$comp $ver $file failed check\n";
       
   550         }
       
   551         if ($self->{verbose} > 1) {
       
   552           my $printableActualMTime = gmtime($actualMTime);
       
   553           my $printableSigMTime = gmtime($sigMTime);
       
   554           print "\tcurrent mtime:   $printableActualMTime\n";
       
   555           print "\tsignature mtime: $printableSigMTime\n";
       
   556           print "\tcurrent size:    $actualSize\n";
       
   557           print "\tsignature size:  $sigSize\n";
       
   558         }
       
   559         unless ($keepGoing) {
       
   560           return 0;
       
   561         }
       
   562       }
       
   563       else {
       
   564         # File passed check.
       
   565         if ($self->{verbose} > 1) {
       
   566           print "$comp $ver $file passed\n";
       
   567         }
       
   568       }
       
   569     }
       
   570     else {
       
   571       # File missing.
       
   572       $passed = 0;
       
   573       if ($self->{verbose}) {
       
   574         print "$comp $ver $file missing\n";
       
   575       }
       
   576       unless ($keepGoing) {
       
   577         return 0;
       
   578       }
       
   579     }
       
   580 
       
   581     return 1;
       
   582   };
       
   583 
       
   584   my $mrpData;
       
   585   die unless defined $entry->{status};
       
   586   if ($entry->{status} == STATUS_PENDING_RELEASE) {
       
   587     eval {
       
   588       unless (defined $entry->{mrpName}) {
       
   589         die "Error: mrp name not specified for $comp\n";
       
   590       }
       
   591       $mrpData = $self->GetMrpData($comp);
       
   592       $mrpData->Validate($warnNotError);
       
   593       foreach my $thisBin (@{$mrpData->BinariesAndExports()}) {
       
   594 	$thisBin = Utils::PrependEpocRoot($thisBin);
       
   595         $self->CheckFileAgainstEnvScan($thisBin);
       
   596       }
       
   597     };
       
   598     if ($@) {
       
   599       $mrpData = undef; # splat the MrpData in order to stop
       
   600                         # the envinfo/cleanenv.
       
   601                         # We need to do this because the only
       
   602                         # way we have of returning an error is to
       
   603                         # fail to return the MRP.
       
   604       if ($self->{verbose} == 0) {
       
   605         print "\n";
       
   606       }
       
   607       print "$comp: $@";
       
   608     }
       
   609   }
       
   610   else {
       
   611     ExecuteSignature(SignatureName($comp, $ver), $doCheck);
       
   612 
       
   613     if ($passed) {
       
   614       if ($oldstatus == STATUS_DIRTY) {
       
   615         $self->SetStatus($comp, STATUS_CLEAN);
       
   616       } else {
       
   617         # Here we return the original status from the environment database,
       
   618         # which is probably STATUS_CLEAN but might by STATUS_DIRTY_SOURCE
       
   619         $self->SetStatus($comp, $oldstatus);
       
   620       }
       
   621     }
       
   622     else {
       
   623       $self->SetStatus($comp, STATUS_DIRTY);
       
   624     }
       
   625   }
       
   626 
       
   627   return ($self->Status($comp), $mrpData);
       
   628 }
       
   629 
       
   630 sub ValidateEnv {
       
   631   my $self = shift;
       
   632   my $comp = lc(shift);
       
   633   my $ver = shift;
       
   634   my $validatesource = shift;
       
   635   my $fullbincheck = shift;
       
   636 
       
   637   my $validatingExternalEnv = 0;
       
   638   my $compsToValidate;
       
   639   if (defined $comp and defined $ver) {
       
   640     if (scalar (keys %{$self->{db}}) > 0) {
       
   641       die "Error: Can't validate against an external environment, because the current environment database is not empty\n";
       
   642     }
       
   643     $validatingExternalEnv = 1;
       
   644     my $relData = RelData->Open($self->{iniData}, $comp, $ver, $self->{verbose});
       
   645     $compsToValidate = $relData->Environment();
       
   646   }
       
   647   else {
       
   648     # Use the current environment.
       
   649     foreach my $thisComp (sort keys %{$self->{db}}) {
       
   650       $compsToValidate->{$thisComp} = $self->{db}->{$thisComp}->{ver};
       
   651     }
       
   652   }
       
   653 
       
   654   my @failedComps;
       
   655   foreach my $thisComp (sort keys %{$compsToValidate}) {
       
   656     my $thisVer = $compsToValidate->{$thisComp};
       
   657     my $result = $self->ValidateComp($thisComp, $thisVer, 0, $validatesource, 0, $fullbincheck);
       
   658     if ($result == STATUS_DIRTY || $result == STATUS_DIRTY_SOURCE) {
       
   659       push (@failedComps, $thisComp);
       
   660       if ($validatingExternalEnv) {
       
   661         # Add an entry even of components that failed. This makes it easier for the user to specify what needs to be re-released.
       
   662         $self->SetVersion($thisComp, $thisVer);
       
   663         if ($result == STATUS_DIRTY) {
       
   664           $self->GenerateFakeSignature($thisComp, $thisVer);
       
   665         } elsif ($result == STATUS_DIRTY_SOURCE) {
       
   666           $self->GenerateSignature($thisComp, $thisVer);
       
   667         }
       
   668         $self->SetStatus($thisComp, $result);
       
   669         my $relData = RelData->Open($self->{iniData}, $thisComp, $thisVer, $self->{verbose});
       
   670         $self->SetMrpName($thisComp, $relData->MrpName());
       
   671         $self->SetInternalVersion($thisComp, $relData->InternalVersion());
       
   672       }
       
   673     }
       
   674   }
       
   675 
       
   676   return \@failedComps;
       
   677 }
       
   678 
       
   679 sub ValidateCompOld {
       
   680   my $self = shift;
       
   681   my $comp = lc(shift);
       
   682   my $ver = shift;
       
   683   my $keepGoing = shift;
       
   684   my $validatesource = shift;
       
   685   my $keeptemp = shift;
       
   686   my $fullbincheck = shift;
       
   687   unless (defined $keepGoing) {
       
   688     $keepGoing = 1;
       
   689   }
       
   690 
       
   691   my $status = STATUS_CLEAN;
       
   692   die unless defined $ver;
       
   693 
       
   694   my $entry = $self->{db}->{$comp};
       
   695   if (defined $entry and $entry->{status} == STATUS_PENDING_RELEASE) {
       
   696     if ($ver eq $entry->{ver}) { # allow validation against other versions even if we're pending release
       
   697       return STATUS_PENDING_RELEASE;
       
   698     }
       
   699   }
       
   700 
       
   701   my $relData = RelData->Open($self->{iniData}, $comp, $ver, $self->{verbose});
       
   702 
       
   703   # Always validate binaries
       
   704   # I initially added an option to turn this off, but I decided that was overcomplexity
       
   705   # and I couldn't think of any use cases except tinkering with the release tools...
       
   706   print "Validating binaries $comp $ver...\n";
       
   707   Utils::InitialiseTempDir($self->{iniData});
       
   708   eval {
       
   709     # Get a temporary copy of the released binaries.
       
   710     my $tempDir = Utils::TempDir();
       
   711     $self->UnpackBinaries($comp, $ver, $tempDir, 1); # 1 = overwrite
       
   712 
       
   713     # Call evalid to compare these with those installed in the environment.
       
   714     # We now validate everything in the temp dir, not just \epoc32,
       
   715     # because some components release binaries outside \epoc32.
       
   716     my $clean = $self->EvalidateDirectories($tempDir, Utils::PrependEpocRoot('.'), $keepGoing);
       
   717     $status = ($clean)?(STATUS_CLEAN):(STATUS_DIRTY);
       
   718 
       
   719     if ($clean and $fullbincheck) {
       
   720       # Ask the current mrp file for a list of binaries (using abld -what)
       
   721       my $mrpData;
       
   722 
       
   723       my $mrpPath = $relData->MrpName();
       
   724       if($self->{iniData}->HasMappings() && Utils::SourceRoot() eq "\\") {
       
   725         $mrpPath = $self->{iniData}->PerformMapOnFileName($mrpPath);
       
   726       }
       
   727       else{
       
   728         $mrpPath = Utils::PrependSourceRoot($mrpPath);
       
   729       }
       
   730       if (!-f $mrpPath) {
       
   731         print "Not checking for new binaries; MRP file not present\n";
       
   732       } else {
       
   733         eval {
       
   734           $mrpData = New MrpData($relData->MrpName(), undef, undef, $self->{iniData}, $self->{verbose}); # undef = we're not preprel-ing it
       
   735         };
       
   736 
       
   737         if (!defined($mrpData)) {
       
   738           my $error = $@;
       
   739           $error =~ s/\s*$//;
       
   740           print "Not checking for new binaries; $error\n";
       
   741         } else {
       
   742           my @binaries = @{$mrpData->Binaries()};
       
   743           push @binaries, @{$mrpData->Exports()};
       
   744 
       
   745           # Get list of binaries in the temporary copy
       
   746           my %oldbinaries;
       
   747 
       
   748           my $sub = sub { # Subroutine to add files to %oldbinaries
       
   749             return if -d $_; # Check it's not a directory
       
   750             s/^\Q$tempDir\E[\/\\]?//; # Strip the temp dir path off
       
   751             s/\\/\//g; # Convert backslashes
       
   752             $oldbinaries{lc($_)}=1 unless (/^\.\.?$/) # Add to hash (unless it's .. or .)
       
   753           };
       
   754 
       
   755           find( {wanted=>$sub, no_chdir=>1}, $tempDir); # Use no_chdir and s/.../ to get a full relative path. Second s/.../ converts backslashes to normal slashes
       
   756           foreach my $binary (@binaries) {
       
   757             $binary = lc($binary);
       
   758             $binary =~ s/\\/\//g; # Convert backslashes to normal slashes
       
   759             if (exists $oldbinaries{$binary}) {
       
   760               delete $oldbinaries{$binary};
       
   761             } else {
       
   762               print "New binary file: $binary\n";
       
   763               $status = STATUS_DIRTY;
       
   764             }
       
   765           }
       
   766           foreach my $oldbinary (keys(%oldbinaries)) {
       
   767             print "Binary file no longer built: $oldbinary\n";
       
   768 	    $status = STATUS_DIRTY;
       
   769           }
       
   770 	}
       
   771       }
       
   772     }
       
   773   };
       
   774 
       
   775   if ($keeptemp) {
       
   776     print "Old release stored in \"".Utils::TempDir()."\"\n";
       
   777   } else {
       
   778     Utils::RemoveTempDir();
       
   779   }
       
   780   if ($@) {
       
   781     die $@;
       
   782   }
       
   783 
       
   784   # We need to check if the categories for exports has changed or not...
       
   785   if ($status == STATUS_CLEAN) {
       
   786     foreach my $thisBinZip (@{$self->RelevantBinaryZips($comp, $ver)}) {
       
   787 
       
   788       if($thisBinZip =~ /exports([a-z]).zip/i) {
       
   789         my $catInArchive = $1;
       
   790         # Open and read the corresponding exports category info file in the archive
       
   791         my $catData = CatData->Open($self->{iniData}, $comp, $ver, $catInArchive);
       
   792         my $catWriteInCatDataFile;
       
   793 
       
   794         # Obtain the category written the exports category info file, if unable to read skip check
       
   795         eval {
       
   796           $catWriteInCatDataFile = $catData->Category();
       
   797         };
       
   798         if ($@) {
       
   799           last;
       
   800         }
       
   801         # Check the categories match
       
   802         if($catInArchive !~ /^$catWriteInCatDataFile$/i){
       
   803           die "ERROR: Mismatch in category found in exports$catInArchive.txt for $comp $ver\n";
       
   804         }
       
   805 
       
   806         my $exportinfo = $catData->ExportInfo();
       
   807         my $destinationDirBuffer;
       
   808 
       
   809         # Using the export infomation as read for the exports category info file check the category of the export file.
       
   810         foreach my $export (sort(keys %{$exportinfo})) {
       
   811           my $destinationDir;
       
   812           my $classifySourceFlag = 1; # Classify source using function ClassifySourceFile only if set as 1;
       
   813           my $destination = $catData->ExportSource($export);
       
   814 
       
   815           # Consider any mappings if defined
       
   816           if($self->{iniData}->HasMappings()){
       
   817             $destination = $self->{iniData}->PerformMapOnFileName($destination);
       
   818           }
       
   819 
       
   820           if(defined $destinationDirBuffer){
       
   821             ($destinationDir) = Utils::SplitFileName($destination);
       
   822 
       
   823             if($destinationDirBuffer =~ /^\Q$destinationDir\E$/i){
       
   824               $classifySourceFlag = 0;
       
   825             }
       
   826           }
       
   827 
       
   828           my $absolute_path = Utils::PrependSourceRoot($destination);
       
   829 
       
   830     	  # validate only if source validation is requested or the source is present
       
   831     	  if($classifySourceFlag and ($validatesource or -e $absolute_path)){
       
   832                 # Obtain the category from the source destinaton extracted for the exports category info file
       
   833     	    my ($catInEnv, $errors) = Utils::ClassifyPath($self->{iniData}, $destination, 0, 0, $comp); # verbose = 0 and logErrors = 0
       
   834     	    if ($catInEnv !~ /^$catInArchive$/i){
       
   835                   print "Change in category found (ENV) \"$catInEnv\" : (Archive) \"$catInArchive\" using $thisBinZip for file $export\n";
       
   836     	      $status = STATUS_DIRTY;
       
   837     	      last;
       
   838     	    }
       
   839 
       
   840     	    $destinationDirBuffer = Utils::SplitFileName($destination);
       
   841     	  }
       
   842         }
       
   843       }
       
   844     }
       
   845   }
       
   846 
       
   847   # We only bother validating source if we've discovered the binaries are clean.
       
   848   # This implies that STATUS_DIRTY means the binaries are dirty, but the status of
       
   849   # the source code is undefined.
       
   850   if ($validatesource && $status == STATUS_CLEAN) {
       
   851     print "Validating source for $comp $ver...\n";
       
   852     Utils::InitialiseTempDir($self->{iniData});
       
   853     eval {
       
   854       # Get a temporary copy of the released source.
       
   855       my $tempDir = Utils::TempDir();
       
   856 
       
   857       my $changeInCat = $self->UnpackSource($comp, $ver, $tempDir, 1, 0, 1); # 1 = overwrite, 0 = do not show progress, 1 = validate
       
   858 
       
   859       if($changeInCat){
       
   860 	print "Change in category found for $comp...\n";
       
   861         $status = STATUS_DIRTY_SOURCE;
       
   862       }
       
   863 
       
   864       # The following code is the only place where a component can have its
       
   865       # status set to "dirty source code". This status was added when
       
   866       # the -s switch was added to ValidateEnv/Rel to validate source code.
       
   867       # It would have been simpler to just set a component to 'dirty' when
       
   868       # the source code was dirty, but this was not possible for the following
       
   869       # reason. When envinfo -f gathers the state information of a component
       
   870       # (or, for that matter, some other command checks the environment is clean)
       
   871       # this calls the CheckComp function. This ignores the status stored in
       
   872       # the environment database, and works it out afresh from the timestamps
       
   873       # on the individual files. Hence we needed to add a new status which
       
   874       # CheckComp would propagate through, so it can report the status
       
   875       # on envinfo. (Otherwise we would have to change CheckComp
       
   876       # so it also checked the status of each source code file eacb
       
   877       # time).
       
   878       #
       
   879       # It would be nice here to ensure we have all the source
       
   880       # installed, but I don't think there's a nice way of finding
       
   881       # out the directory that the source comes in. (Not without
       
   882       # unzipping the zip, and we might as well just evalidate it...)
       
   883       #
       
   884       # This grim \. thing is not homage to the Great Geek Website
       
   885       # It is because evalid gets grumpy if you give it either \ or ''
       
   886       # as an argument. The first time is because \\ isn't a valid
       
   887       # directory separator in Win32 (!!) and the second is because
       
   888       # Perl doesn't think '' is a valid directory (which is probably
       
   889       # fair enough). Rather than file a defect against Windows,
       
   890       # let's pass in slightly silly arguments to evalid.
       
   891       if ($status == STATUS_CLEAN) {
       
   892         print "Checking for changed or removed files\n" if ($self->{verbose});
       
   893         my $clean = $self->EvalidateDirectories($tempDir, Utils::PrependSourceRoot('.'), $keepGoing);
       
   894         $status = STATUS_DIRTY_SOURCE unless ($clean);
       
   895       }
       
   896       # The above checks will only have found changed or removed files.
       
   897       # Files that have been added to the source won't be in the $tempDir,
       
   898       # so evalid won't pick them up and test them. So we have to
       
   899       # explicitly check for added files.
       
   900       # Only bother doing this if we haven't found problems already.
       
   901       if ($status == STATUS_CLEAN) {
       
   902         # Recurse through each directory in the temp dir, listing the
       
   903         # equivalent dir on the drive (i.e. the latest source). If there
       
   904         # are more files on the drive than in the source tree, source
       
   905         # is dirty.
       
   906         print "Checking for added files\n" if ($self->{verbose});
       
   907         eval {
       
   908           $status = STATUS_DIRTY_SOURCE if ($self->CheckForAddedFiles($relData, $tempDir));
       
   909         };
       
   910         if ($@) {
       
   911           print "Warning: skipping the check for added files, for the component \"$comp\". All other source code validation checks passed. The reason is: $@";
       
   912         }
       
   913       }
       
   914     };
       
   915     Utils::RemoveTempDir();
       
   916     if ($@) {
       
   917       die $@;
       
   918     }
       
   919   }
       
   920 
       
   921   if ($status == STATUS_CLEAN) {
       
   922     # Previously this SetVersion line was wrapped in an "if", so that
       
   923     # it didn't happen if $entry was defined - i.e. it was already in the
       
   924     # environment database. After discussion with Joe and James this behaviour
       
   925     # has been changed.
       
   926     $self->SetVersion($comp, $ver);
       
   927     $self->SetStatus($comp, $status);
       
   928     $self->GenerateSignature($comp, $ver);
       
   929     $self->SetMrpName($comp, $relData->MrpName());
       
   930     $self->SetInternalVersion($comp, $relData->InternalVersion());
       
   931   }
       
   932   elsif ($entry && $entry->{status} &&
       
   933     $entry->{status} == STATUS_PENDING_RELEASE) {
       
   934     # Old status was pending release; so we don't do anything
       
   935   }
       
   936   elsif ($status == STATUS_DIRTY) {
       
   937     if (defined $entry) {
       
   938       # The component used to be in the environment database
       
   939       # We set its status in case it used to be STATUS_DIRTY_SOURCE
       
   940       # and it's now STATUS_DIRTY.
       
   941       $self->SetStatus($comp, $status);
       
   942     }
       
   943     # This component wasn't previously in the environment database;
       
   944     # do nothing
       
   945   }
       
   946   elsif ($status == STATUS_DIRTY_SOURCE) {
       
   947     if (defined $entry) {
       
   948       $self->SetStatus($comp, $status);
       
   949       $self->GenerateSignature($comp, $ver);
       
   950       # Because otherwise any 'envinfo' will reset a component status
       
   951       # to dirty, even if only its source is dirty
       
   952     }
       
   953   }
       
   954   print "Status ", StatusString($status), "\n";
       
   955   return $status;
       
   956 }
       
   957 
       
   958 sub ValidateComp {
       
   959 	my $self = shift;
       
   960 	my $comp = lc(shift);
       
   961 	my $ver = shift;
       
   962 	my $keepGoing = shift;
       
   963 	my $validatesource = shift;
       
   964 	my $keeptemp = shift;
       
   965 	my $fullbincheck = shift;
       
   966 	unless ( defined $keepGoing ) {
       
   967 	  $keepGoing = 1;
       
   968 	}
       
   969 	my $manifestFromThisComponent = undef;
       
   970 	my $status = STATUS_CLEAN;
       
   971 	die unless defined $ver;
       
   972 
       
   973 	my $entry = $self->{db}->{$comp};
       
   974 	if (defined $entry and $entry->{status} == STATUS_PENDING_RELEASE) {
       
   975 		if ($ver eq $entry->{ver}) { # allow validation against other versions even if we're pending release
       
   976 			return STATUS_PENDING_RELEASE;
       
   977 		}
       
   978 	}
       
   979 
       
   980 	#Create a relData object for retrieving the mrpPath required for building the manifest object
       
   981 	my $relData = RelData->Open( $self->{iniData}, $comp, $ver, $self->{verbose} );
       
   982 
       
   983 	#Find the archive location for release and build the file path for loading the manifest file from the location
       
   984 	my $relDir = $relData->{iniData}->PathData->LocalArchivePathForExistingComponent( $comp, $ver );
       
   985 	my $manifestPath = File::Spec->catfile( $relDir, MANIFEST_FILE );
       
   986 
       
   987 	#Check if manifest file exists
       
   988 	if (-e $manifestPath) {
       
   989 	#Define callback to validate files which don't have checksum defined in manifest file.
       
   990 	my $callback = sub {
       
   991 		my $filesToValidate = shift;
       
   992 		my $manifestObject = shift;
       
   993 		my $keepGoing = shift;
       
   994 		{
       
   995 			local $" = ", ";
       
   996 			print "No checksum found for file(s) @{$filesToValidate} - reverting to old evalid process.\n";
       
   997 		}
       
   998 		Utils::InitialiseTempDir($self->{iniData});
       
   999 		my $tempDir = Utils::TempDir();
       
  1000 		my $epocFilePath = Utils::EpocRoot();
       
  1001 		my $sourceFilePath = Utils::SourceRoot();
       
  1002 		my $fullEvalidName = Utils::FindInPath('evalid.bat');
       
  1003 		my $clean = 1;
       
  1004 		my @files;
       
  1005 		foreach  my $thisFile (@{$filesToValidate}) {
       
  1006 			my $zipName;
       
  1007 			my $file;
       
  1008 			my $fileContentType = $manifestObject->GetFileInfo($thisFile, CONTENT_TYPE);
       
  1009 			if ($fileContentType  eq 'source' or $fileContentType  eq 'export') {
       
  1010 				my $cat = $manifestObject->GetFileInfo($thisFile, IPR_CATEGORY);
       
  1011 				if ($fileContentType eq 'source') {
       
  1012 					$zipName = "source".$cat;
       
  1013 					$file = File::Spec->catfile($sourceFilePath, $thisFile) 
       
  1014 				} else {
       
  1015 					$zipName = "exports".$cat;
       
  1016 					$file = File::Spec->catfile($epocFilePath, $thisFile);
       
  1017 				}
       
  1018 			}
       
  1019 			elsif ($fileContentType eq 'binary') {
       
  1020 				my $platForm = $manifestObject->{files}{$thisFile}{'platform'}; 
       
  1021 				if (defined $platForm) {
       
  1022 					$zipName = "binaries"."_".$platForm;
       
  1023 				}
       
  1024 				else {
       
  1025 					$zipName = "binaries";
       
  1026 				}
       
  1027 				$file = File::Spec->catfile($epocFilePath, $thisFile);
       
  1028 			}
       
  1029 			$zipName = $zipName.".zip";
       
  1030 			my $zipPath = File::Spec->catfile($relDir,$zipName);  
       
  1031 			Utils::UnzipSingleFile($zipPath,$thisFile, $tempDir, $self->{verbose}, 1, $comp); #overwrite = 1
       
  1032 			push @files, [$thisFile, $file];
       
  1033 		}
       
  1034 		foreach my $thisFile (@files) {
       
  1035 			my $firstPath = File::Spec->catfile($tempDir,shift(@$thisFile));
       
  1036 			my $secondPath = shift(@$thisFile);
       
  1037 			open EVALID, "$fullEvalidName -c $firstPath $secondPath|" or die "Error: Couldn't run EValid: $!\n";
       
  1038 			my $thisLine;
       
  1039 			my $acceptablefailures = ACCEPTABLE_EVALID_FAILURES;
       
  1040 			while ($thisLine = <EVALID>) {
       
  1041 				if ($thisLine =~ m/MISSING:|FAILED:|PROBLEM:/ && $thisLine !~ m/$acceptablefailures/i) {
       
  1042 					print $thisLine  if ($self->{verbose});
       
  1043 					$clean = 0;
       
  1044 					unless ($keepGoing) {
       
  1045 						Utils::RemoveTempDir();
       
  1046 						return $clean;
       
  1047 					}
       
  1048 				}
       
  1049 			}
       
  1050 		}
       
  1051 		Utils::RemoveTempDir();
       
  1052 		return $clean;
       
  1053 	};
       
  1054 
       
  1055 	#Load the manifest file to create a manifest object
       
  1056 	my $manifestFromBaselineComponent = Symbian::CBR::Component::Manifest->new( $manifestPath );
       
  1057 
       
  1058 	my $mrpPath = Utils::RelativeToAbsolutePath( $relData->MrpName(), $self->{iniData}, SOURCE_RELATIVE );
       
  1059 
       
  1060 	if ($fullbincheck && -e $mrpPath) {
       
  1061 		$manifestFromThisComponent = Symbian::CBR::Component::Manifest->new($mrpPath);
       
  1062 	} else {
       
  1063 		if ($fullbincheck) {
       
  1064 			print "Not checking for new binaries; MRP file not present\n";
       
  1065 		}
       
  1066 
       
  1067 		$manifestFromThisComponent = Symbian::CBR::Component::Manifest->new($manifestPath);
       
  1068 		$manifestFromThisComponent->RefreshMetaData($comp, $ver);
       
  1069 	}
       
  1070 
       
  1071 	#Compare the manifest objects
       
  1072 	eval {$status = $manifestFromThisComponent->Compare($manifestFromBaselineComponent, $validatesource, $keepGoing,$callback)};
       
  1073 
       
  1074 	#Check if Compare() completed without errors
       
  1075 	if (!$@) {
       
  1076 
       
  1077 		#If $keeptemp set, unpack binaries to temp location
       
  1078 		if ( $keeptemp ) {
       
  1079 
       
  1080 			Utils::InitialiseTempDir($self->{iniData});
       
  1081 			# Get a temporary copy of the released binaries.
       
  1082 			my $tempDir = Utils::TempDir();
       
  1083 			$self->UnpackBinaries($comp, $ver, $tempDir, 1); # 1 = overwrite
       
  1084 
       
  1085 			#If $validatesource is set, get temp copy of released sources
       
  1086 			$self->UnpackSource($comp, $ver, $tempDir, 1, 0, 1) if $validatesource;
       
  1087 
       
  1088 			print "Old release stored in \"".Utils::TempDir()."\"\n";
       
  1089 		}
       
  1090 
       
  1091 		#If status is dirty, save manifest to temp location
       
  1092 		$self->SaveManifestToTempDir($comp, $manifestFromThisComponent) if $status == STATUS_DIRTY;
       
  1093 
       
  1094 		#Update the environemnt as done by validatecompold
       
  1095 		$self->UpdateEnvironment( $status, $entry, $relData );
       
  1096 
       
  1097 		print "Status ", StatusString($status), "\n";
       
  1098 		return $status;
       
  1099 	}
       
  1100 
       
  1101 	else {
       
  1102 		print "$@Continuing with old validaterel process..\n";
       
  1103 	}
       
  1104 
       
  1105 	}
       
  1106 	else {
       
  1107 		print "Manifest file does not exist in the version $ver for component $comp..\nContinuing with old validaterel process..\n";
       
  1108 	}
       
  1109 
       
  1110 	#Call the old validaterel process if manifest comparison is not possible
       
  1111 	$status = $self->ValidateCompOld( $comp, $ver, $keepGoing, $validatesource, $keeptemp, $fullbincheck );
       
  1112 
       
  1113 	#If status is dirty during validatecompold, still we want to save manifest to temp location
       
  1114 	if ( defined $manifestFromThisComponent and ($status == STATUS_DIRTY or $status == STATUS_DIRTY_SOURCE) ) {
       
  1115 		$self->SaveManifestToTempDir($comp, $manifestFromThisComponent);
       
  1116 	}
       
  1117 
       
  1118 	return $status;
       
  1119 }
       
  1120 
       
  1121 sub UpdateEnvironment {
       
  1122 	my $self = shift;
       
  1123 	my $status = shift;
       
  1124 	my $entry = shift;
       
  1125 	my $relData = shift;
       
  1126 
       
  1127 	my $comp = $relData->Component();
       
  1128 	my $ver = $relData->Version();
       
  1129 
       
  1130 	if ($status == STATUS_CLEAN) {
       
  1131 		# Previously this SetVersion line was wrapped in an "if", so that
       
  1132 		# it didn't happen if $entry was defined - i.e. it was already in the
       
  1133 		# environment database. After discussion with Joe and James this behaviour
       
  1134 		# has been changed.
       
  1135 		$self->SetVersion( $comp, $ver );
       
  1136 		$self->SetStatus( $comp, $status );
       
  1137 		$self->GenerateSignature( $comp, $ver );
       
  1138 		$self->SetMrpName( $comp, $relData->MrpName() );
       
  1139 		$self->SetInternalVersion( $comp, $relData->InternalVersion() );
       
  1140 	}
       
  1141 	elsif ($entry && $entry->{status} &&
       
  1142 		$entry->{status} == STATUS_PENDING_RELEASE) {
       
  1143 		# Old status was pending release; so we don't do anything
       
  1144 	}
       
  1145 	elsif ($status == STATUS_DIRTY) {
       
  1146 		if (defined $entry) {
       
  1147 			# The component used to be in the environment database
       
  1148 			# We set its status in case it used to be STATUS_DIRTY_SOURCE
       
  1149 			# and it's now STATUS_DIRTY.
       
  1150 			$self->SetStatus( $comp, $status );
       
  1151 		}
       
  1152 		# This component wasn't previously in the environment database;
       
  1153 		# do nothing
       
  1154 	}
       
  1155 	elsif ($status == STATUS_DIRTY_SOURCE) {
       
  1156 		if (defined $entry) {
       
  1157 			$self->SetStatus( $comp, $status );
       
  1158 			$self->GenerateSignature( $comp, $ver );
       
  1159 			# Because otherwise any 'envinfo' will reset a component status
       
  1160 			# to dirty, even if only its source is dirty
       
  1161 		}
       
  1162 	}
       
  1163 }
       
  1164 
       
  1165 sub SaveManifestToTempDir {
       
  1166 	my $self = shift;
       
  1167 	my $comp = shift;
       
  1168 	my $manifestFromThisComponent = shift;
       
  1169 
       
  1170 	my $manifestTempFile = "manifest_".$comp.".xml";
       
  1171 	my $manifestFile = $manifestFromThisComponent->Save( File::Spec->tmpdir(), $manifestTempFile );
       
  1172 #	my $manifestTempFile = File::Spec->catfile( File::Spec->tmpdir(), "manifest_".$comp.".xml" );
       
  1173 #	rename( $manifestFile, $manifestTempFile );
       
  1174 }
       
  1175 
       
  1176 sub Duplicates {
       
  1177   my $self = shift;
       
  1178   my $mrpData = shift;
       
  1179   my $installedComps = $self->VersionInfo();
       
  1180   my %binHash;
       
  1181   my @duplicates;
       
  1182 
       
  1183   # First cross-check against the components about to be released.
       
  1184   foreach my $thisMrp (@{$mrpData}) {
       
  1185     my $comp = lc($thisMrp->Component());
       
  1186     my $bins = $thisMrp->BinariesAndExports();
       
  1187     foreach my $thisBin (@$bins) {
       
  1188       $thisBin = lc(Utils::PrependEpocRoot($thisBin));
       
  1189 
       
  1190       print "Checking $thisBin for duplicateness (pending release)\n" if ($self->{verbose}>1);
       
  1191       if (exists $binHash{$thisBin}) {
       
  1192  	push @duplicates, [$thisBin, $comp, $binHash{$thisBin}]; # $comp attempting to release $thisBin which has already been released by $binHash{$thisBin}";
       
  1193       }
       
  1194       else {
       
  1195 	$binHash{$thisBin} = $comp;
       
  1196       }
       
  1197     }
       
  1198     delete $installedComps->{$comp};
       
  1199   }
       
  1200 
       
  1201   # Now cross-check against the other components in the environment.
       
  1202   foreach my $thisComp (keys %{$installedComps}) {
       
  1203     my $doCheck = sub {
       
  1204       my $file = lc(shift);
       
  1205       print "Checking $file for duplicateness\n" if ($self->{verbose}>1);
       
  1206       if (exists $binHash{$file}) {
       
  1207 	push @duplicates, [$file, $binHash{$file}, $thisComp]; #"$binHash{$file} attempting to release $file which has already been released by $thisComp";
       
  1208       }
       
  1209       else {
       
  1210 	$binHash{$file} = $thisComp;
       
  1211       }
       
  1212     };
       
  1213     my $sigName = SignatureName($thisComp, $installedComps->{$thisComp});
       
  1214     ExecuteSignature($sigName, $doCheck);
       
  1215   }
       
  1216 
       
  1217   return \@duplicates;
       
  1218 }
       
  1219 
       
  1220 sub BinaryInfo {
       
  1221   my $self = shift;
       
  1222   my $binary = shift;
       
  1223   unless (-e $binary) {
       
  1224     die "Error: \"$binary\" does not exist\n";
       
  1225   }
       
  1226 
       
  1227   (my $currentMTime, my $currentSize) = Utils::FileModifiedTimeAndSize($binary);
       
  1228   my $sigMTime;
       
  1229   my $sigSize;
       
  1230   my $sigName;
       
  1231 
       
  1232   my $findBin = sub {
       
  1233     my $file = shift;
       
  1234     if (lc($binary) eq lc($file)) {
       
  1235       $sigMTime = shift;
       
  1236       $sigSize = shift;
       
  1237       $sigName = shift;
       
  1238       return 0;
       
  1239     }
       
  1240     return 1; # Means continue;
       
  1241   };
       
  1242   ExecuteAllSignatures($findBin);
       
  1243 
       
  1244   my $comp;
       
  1245   my $ver;
       
  1246   my $pendingRelease = 0;
       
  1247 
       
  1248   if (defined $sigMTime and defined $sigName) {
       
  1249     ($comp, $ver) = $self->DecodeSignatureName($sigName);
       
  1250   }
       
  1251   else {
       
  1252     # Binary not found in the signatures, so check for components pending release.
       
  1253     if (Utils::WithinEpocRoot($binary)) {
       
  1254 	      $binary = Utils::RemoveEpocRoot($binary); # remove EPOCROOT
       
  1255     }
       
  1256     $binary =~ s!^[\\\/]!!; # remove leading slash
       
  1257 
       
  1258     foreach my $thisComp (keys %{$self->{db}}) {
       
  1259       if ($self->Status($thisComp) == STATUS_PENDING_RELEASE) {
       
  1260         my $thisVer = $self->{db}->{$thisComp}->{ver};
       
  1261         my $thisMrpData = $self->GetMrpData($thisComp);
       
  1262         $thisMrpData->EnsureDoesNotExist();
       
  1263 
       
  1264         if (grep /^\Q$binary\E$/i, @{$thisMrpData->Binaries()}) {
       
  1265           $pendingRelease = 1;
       
  1266           $comp = $thisComp;
       
  1267           $ver = $thisVer;
       
  1268           last;
       
  1269         }
       
  1270         elsif (grep /^\Q$binary\E$/i, @{$thisMrpData->Exports()}) {
       
  1271           $pendingRelease = 1;
       
  1272           $comp = $thisComp;
       
  1273           $ver = $thisVer;
       
  1274           last;
       
  1275         }
       
  1276       }
       
  1277     }
       
  1278     unless (defined $comp and defined $ver) {
       
  1279       my $ignoreList = $self->{iniData}->BinariesToIgnore();
       
  1280       push (@$ignoreList, Utils::PrependEpocRoot('\\epoc32\\relinfo\\*'));
       
  1281       foreach my $ignore (@$ignoreList) {
       
  1282       $ignore =~ s/\\/\\\\/g;
       
  1283       $ignore =~ s/\./\\\./g;
       
  1284       $ignore =~ s/\*/\.\*/g;
       
  1285 
       
  1286       if ($binary !~ /^\\/) {
       
  1287         $ignore =~ s/^\\*//;
       
  1288       }
       
  1289 
       
  1290       if ($binary =~ /^$ignore$/i) {
       
  1291         die "Error: no information available for \"$binary\". It is not part of any component, but it is ignored by the 'ignore_binary' rule '$ignore'. This rule might be in your reltools.ini, or it might be one of the standard ignores.\n";
       
  1292       }
       
  1293       }
       
  1294       die "Error: No information available for \"$binary\". It's not even one of the files/directories that are ignored as standard.\n";
       
  1295     }
       
  1296   }
       
  1297 
       
  1298   my $info;
       
  1299   push (@$info, ['Component:', $comp]);
       
  1300   push (@$info, ['Version:', $ver]);
       
  1301   if ($pendingRelease) {
       
  1302     push (@$info, ['Status:', 'pending release']);
       
  1303   }
       
  1304   elsif ($currentMTime == $sigMTime and $currentSize == $sigSize) {
       
  1305     push (@$info, ['Status:', 'clean']);
       
  1306   }
       
  1307   else {
       
  1308     push (@$info, ['Status:', 'dirty']);
       
  1309   }
       
  1310 
       
  1311   return $info;
       
  1312 }
       
  1313 
       
  1314 sub ListBins {
       
  1315   my $self = shift;
       
  1316   my $comp = shift;
       
  1317   my $ver = $self->Version($comp);
       
  1318   die unless $ver;
       
  1319 
       
  1320   if ($self->Status($comp) == STATUS_PENDING_RELEASE) {
       
  1321     $self->ListBinsPendingRelease($comp, $ver);
       
  1322   } else {
       
  1323     $self->ListBinsStandard($comp, $ver);
       
  1324   }
       
  1325 }
       
  1326 
       
  1327 sub GetMrpData {
       
  1328   my $self = shift;
       
  1329   my $compname = lc(shift);
       
  1330   my $entry = $self->{db}->{$compname};
       
  1331   die "Invalid component name \"$compname\"" unless $entry;
       
  1332 
       
  1333   my $name = $entry->{mrpName};
       
  1334   unless ($self->{mrpcache}->{$name}) {
       
  1335     my $mrpData = MrpData->New($entry->{mrpName}, $entry->{ver}, $entry->{intVer}, $self->{iniData}, $self->{verbose});
       
  1336     my $namefrommrp = $mrpData->Component();
       
  1337     die "Error: Component name in MRP file is \"$namefrommrp\" whilst the name of this component in the environment database is \"$compname\".\n" unless (lc $compname eq lc $namefrommrp);
       
  1338     $self->{mrpcache}->{$name} = $mrpData;
       
  1339   }
       
  1340   return $self->{mrpcache}->{$name};
       
  1341 }
       
  1342 
       
  1343 
       
  1344 sub GetMRPLocations {
       
  1345   my $self = shift;
       
  1346   my $componentName = lc(shift);
       
  1347   
       
  1348   # If only the MRP location for a specified component is required...
       
  1349   if ($componentName) {
       
  1350     if (exists $self->{db}->{$componentName}) {
       
  1351       return (Utils::PrependSourceRoot($self->{db}->{$componentName}->{mrpName}));
       
  1352     }
       
  1353     else {
       
  1354       return undef;
       
  1355     }
       
  1356   }
       
  1357 
       
  1358   # Otherwise all MRP locations are returned to the caller
       
  1359   my @mrpLocations;
       
  1360   
       
  1361   foreach my $component (keys %{$self->{db}}) {
       
  1362     push @mrpLocations, Utils::PrependSourceRoot($self->{db}->{$component}->{mrpName});
       
  1363   }
       
  1364   
       
  1365   return @mrpLocations;  
       
  1366 }
       
  1367 
       
  1368 #
       
  1369 # Private.
       
  1370 #
       
  1371 
       
  1372 sub ListBinsStandard {
       
  1373   my $self = shift;
       
  1374   my $comp = shift;
       
  1375   my $ver = shift;
       
  1376 
       
  1377   my $info;
       
  1378   push (@$info, ['File', 'Status']);
       
  1379 
       
  1380   my $sigName = SignatureName($comp, $ver);
       
  1381   my $gatherInfo = sub {
       
  1382     my $file = shift;
       
  1383     my $sigMTime = shift;
       
  1384     my $sigSize = shift;
       
  1385 
       
  1386     if (-e $file) {
       
  1387       (my $actualMTime, my $actualSize) = Utils::FileModifiedTimeAndSize($file);
       
  1388       if (!defined $actualMTime or !defined $actualSize) {
       
  1389 	die "Error: Problem stating \"$file\"\n";
       
  1390       }
       
  1391       elsif ($sigMTime != $actualMTime or $sigSize != $actualSize) {
       
  1392 	push (@$info, [$file, STATUS_STRING_FAILED]);
       
  1393       }
       
  1394       else {
       
  1395 	push (@$info, [$file, STATUS_STRING_PASSED]);
       
  1396       }
       
  1397     }
       
  1398     else {
       
  1399       push (@$info, [$file, STATUS_STRING_MISSING]);
       
  1400     }
       
  1401 
       
  1402     return 1; # Means continue with next line in signature.
       
  1403   };
       
  1404 
       
  1405   ExecuteSignature($sigName, $gatherInfo);
       
  1406   return $info;
       
  1407 }
       
  1408 
       
  1409 sub ListBinsPendingRelease {
       
  1410   my $self = shift;
       
  1411   my $comp = shift;
       
  1412   my $ver = shift;
       
  1413 
       
  1414   my $mrpData = $self->GetMrpData($comp);
       
  1415 
       
  1416   my @info;
       
  1417   push @info, ['File', 'Status', 'Category'];
       
  1418   foreach my $cat (@{$mrpData->BinaryCategories()}) {
       
  1419     foreach my $file (@{$mrpData->Binaries($cat)}) {
       
  1420       push @info, [$file, 'pending release', $cat];
       
  1421     }
       
  1422   }
       
  1423   foreach my $cat (@{$mrpData->ExportCategories()}) {
       
  1424     foreach my $file (@{$mrpData->Exports($cat)}) {
       
  1425       push @info, [$file, 'pending release', $cat];
       
  1426     }
       
  1427   }
       
  1428   # To do ideally: add another column to report which bld.inf each binary
       
  1429   # comes from (if any). This requires quite a lot of internal restructuring
       
  1430   # of MrpData.pm so will probably never happen... It's not worth the benefits.
       
  1431   return \@info;
       
  1432 }
       
  1433 
       
  1434 sub DESTROY {
       
  1435   my $self = shift;
       
  1436   $self->Close();
       
  1437 }
       
  1438 
       
  1439 sub EvalidateDirectories {
       
  1440   my $self = shift;
       
  1441   my $firstdirectory = shift;
       
  1442   my $seconddirectory = shift;
       
  1443   my $keepGoing = shift;
       
  1444 
       
  1445   my $clean = 1;
       
  1446   my $fullEvalidName = Utils::FindInPath('evalid.bat');
       
  1447 
       
  1448   # Call evalid to compare these with those installed in the environment.
       
  1449   if ($self->{verbose} > 1) {
       
  1450     print "Evalid command is $fullEvalidName -c $firstdirectory $seconddirectory\n";
       
  1451   }
       
  1452   open EVALID, "$fullEvalidName -c $firstdirectory $seconddirectory|" or die "Error: Couldn't run EValid: $!\n";
       
  1453   my $thisLine;
       
  1454   while ($thisLine = <EVALID>) {
       
  1455     my $acceptablefailures = ACCEPTABLE_EVALID_FAILURES;
       
  1456     if ($thisLine =~ m/MISSING:|FAILED:|PROBLEM:/ && $thisLine !~ m/$acceptablefailures/i) {
       
  1457       if ($self->{verbose}) { print $thisLine; }
       
  1458       $clean = 0;
       
  1459       unless ($keepGoing) {
       
  1460         last;
       
  1461       }
       
  1462     }
       
  1463     elsif ($self->{verbose} > 1) {
       
  1464       print $thisLine;
       
  1465     }
       
  1466   }
       
  1467   close EVALID;
       
  1468 
       
  1469   return $clean;
       
  1470 }
       
  1471 
       
  1472 sub ScanEnv {
       
  1473   my $self = shift;
       
  1474   my $displayProgress = shift;
       
  1475   my $progressTuner = 0;
       
  1476 
       
  1477   my $processFileSub = sub {
       
  1478     if ($displayProgress) {
       
  1479       ++$progressTuner;
       
  1480       if ($progressTuner >= SCAN_PROGRESS_TUNER) {
       
  1481         $progressTuner = 0;
       
  1482         select STDOUT; $|=1;
       
  1483         print ".";
       
  1484       }
       
  1485     }
       
  1486     my $thisFile = lc($File::Find::name);
       
  1487     Utils::TidyFileName(\$thisFile);
       
  1488     if (-f $thisFile) {
       
  1489       $self->{envFileList}->{$thisFile} = 1;
       
  1490     }
       
  1491     elsif (-d $thisFile and $self->CheckIgnoreDir($thisFile)) {
       
  1492       $File::Find::prune = 1;
       
  1493     }
       
  1494     elsif (-d $thisFile && !@{Utils::ReadDir($thisFile)}) {
       
  1495       # This is an empty directory.  It is not possible to own empty directories,
       
  1496       #so this will be included in the unowned list
       
  1497       $self->{envFileList}->{$thisFile} = 1;
       
  1498     }
       
  1499   };
       
  1500 
       
  1501   my $cwd = cwd();
       
  1502   $cwd =~ s/:$/:\\/; # Needed because if at root, cwd() just returns drive_letter:
       
  1503   find($processFileSub, Utils::PrependEpocRoot('\\epoc32'));
       
  1504   chdir ($cwd);
       
  1505   if ($displayProgress and $self->{verbose}) {
       
  1506     print "\n";
       
  1507   }
       
  1508 }
       
  1509 
       
  1510 sub CheckIgnoreDir {
       
  1511   my $self = shift;
       
  1512   my $dir = shift;
       
  1513   if (exists $self->{ignoreDirs}->{$dir}) {
       
  1514     return 1;
       
  1515   }
       
  1516   return 0;
       
  1517 }
       
  1518 
       
  1519 # Classify the ignores according to whether they correspond to directories or files. This allows the
       
  1520 # File::Find scan to prune directories to be ignored efficiently.
       
  1521 sub InitIgnores {
       
  1522   my $self = shift;
       
  1523   my $ignoreStandardIgnores = shift;
       
  1524   my $ignoreList;
       
  1525   unless ($ignoreStandardIgnores) {
       
  1526     $ignoreList = $self->{iniData}->BinariesToIgnore();
       
  1527   }
       
  1528   push (@$ignoreList, '\\epoc32\\relinfo\\*'); # Need to always ignore \epoc32\relinfo since this contains the environment database.
       
  1529 
       
  1530   foreach my $thisIgnore (@$ignoreList) {
       
  1531     if ($thisIgnore =~ /(.*)\\\*$/) {
       
  1532       my $dir = $1;
       
  1533       Utils::TidyFileName(\$dir);
       
  1534       $self->{ignoreDirs}->{lc(Utils::PrependEpocRoot($dir))} = 1;  # Store dirs in a hash so they can be looked up fast.
       
  1535     }
       
  1536     else {
       
  1537       push (@{$self->{ignoreFiles}}, Utils::PrependEpocRoot($thisIgnore));
       
  1538     }
       
  1539   }
       
  1540 }
       
  1541 
       
  1542 sub CheckFileAgainstEnvScan {
       
  1543   my $self = shift;
       
  1544   my $file = lc(shift);
       
  1545   my $ok = 1;
       
  1546 
       
  1547   if (exists $self->{envFileList}) {
       
  1548     if (exists $self->{envFileList}->{$file}) {
       
  1549       # Exists, so remove from envFileList hash - any file names left in here at the end will be reported to the user.
       
  1550       delete $self->{envFileList}->{$file};
       
  1551     }
       
  1552     else {
       
  1553       $ok = 0;
       
  1554     }
       
  1555   }
       
  1556   elsif (not -e $file) {
       
  1557     $ok = 0;
       
  1558   }
       
  1559   return $ok;
       
  1560 }
       
  1561 
       
  1562 sub RemoveBinsToIgnore {
       
  1563   my $self = shift;
       
  1564   foreach my $thisIgnore (@{$self->{ignoreFiles}}) {
       
  1565     $thisIgnore =~ s/\\/\\\\/g;
       
  1566     $thisIgnore =~ s/\./\\\./g;
       
  1567     $thisIgnore =~ s/\*/\.\*/g;
       
  1568     foreach my $thisFile (keys %{$self->{envFileList}}) {
       
  1569       if ($thisFile =~ /$thisIgnore/i) {
       
  1570 	delete $self->{envFileList}->{$thisFile};
       
  1571       }
       
  1572     }
       
  1573   }
       
  1574 }
       
  1575 
       
  1576 sub UnaccountedEnvFiles {
       
  1577   my $self = shift;
       
  1578   my @unaccountedFiles = sort keys %{$self->{envFileList}};
       
  1579   return \@unaccountedFiles;
       
  1580 }
       
  1581 
       
  1582 sub DeleteSignature {
       
  1583   my $self = shift;
       
  1584   my $comp = shift;
       
  1585   my $ver = shift;
       
  1586   
       
  1587   if ($self->{verbose} > 1) { print "Deleting signature file for $comp $ver\n"; }
       
  1588   my $sigName = SignatureName($comp, $ver);
       
  1589   unlink ($sigName) or print "Warning: Couldn't delete $sigName: $!\n";
       
  1590 }
       
  1591 
       
  1592 sub ExecuteAllSignatures {
       
  1593   my $sub = shift;
       
  1594 
       
  1595   opendir(DIR, Utils::PrependEpocRoot("\\epoc32\\relinfo")) or die "Error: Couldn't open directory \"" . Utils::PrependEpocRoot("\\epoc32\\relinfo") . "\": $!\n";
       
  1596   while (defined(my $file = readdir(DIR))) {
       
  1597     if ($file =~ /\.sig$/) {
       
  1598       my $continue = ExecuteSignature(Utils::PrependEpocRoot("\\epoc32\\relinfo\\$file"), $sub);
       
  1599       unless ($continue) {
       
  1600 	last;
       
  1601       }
       
  1602     }
       
  1603   }
       
  1604   closedir(DIR);
       
  1605 }
       
  1606 
       
  1607 sub ExecuteSignature {
       
  1608 # For each line in the signature file, parse and call the given subroutine with the parsed variables.
       
  1609 
       
  1610   my $sigName = shift;
       
  1611   my $filessub = shift;
       
  1612   my $directoriesSub = shift;
       
  1613 
       
  1614   my %directories;
       
  1615 
       
  1616   my $continue = 1;
       
  1617   open (SIG, $sigName) or die "Couldn't open $sigName for reading: $!\n";
       
  1618   while (my $line = <SIG>) {
       
  1619     # Parse signature line.
       
  1620     (my $file, my $mTime, my $size) = split (/\t/, $line);
       
  1621     unless (defined $file and defined $mTime and defined $size) {
       
  1622       die "Error: Invalid line in signature file $sigName\n";
       
  1623     }
       
  1624     $directories{dirname($file)} = 1;
       
  1625     # Call subroutine.
       
  1626     $continue = &$filessub(Utils::PrependEpocRoot($file), $mTime, $size, $sigName);
       
  1627     unless ($continue) {
       
  1628       last;
       
  1629     }
       
  1630   }
       
  1631   close (SIG);
       
  1632 
       
  1633   if ($directoriesSub) {
       
  1634     foreach my $directory (sort keys %directories) {
       
  1635       &$directoriesSub(Utils::PrependEpocRoot($directory), $sigName);
       
  1636     }
       
  1637   }
       
  1638 
       
  1639   return $continue;
       
  1640 }
       
  1641 
       
  1642 sub DeleteFilesInSignature {
       
  1643   my $self = shift;
       
  1644   my $comp = shift;
       
  1645   my $ver = shift;
       
  1646   my $sigName = SignatureName($comp, $ver);
       
  1647   my $filesDeletionSub = sub {
       
  1648     my $file = shift;
       
  1649     if (-e $file) {
       
  1650       if ($self->{verbose} > 1) { print "Deleting \"$file\"...\n"; }
       
  1651       unlink ($file) or die "Error: Couldn't delete \"$file\": $!\n";
       
  1652     }
       
  1653     return 1;
       
  1654   };
       
  1655   my $directoriesDeletionSub = sub {
       
  1656     my $directory = shift;
       
  1657 
       
  1658     if (-e $directory && !scalar @{Utils::ReadDir($directory)} ) {
       
  1659       print "Removing directory $directory...\n" if ($self->{verbose});
       
  1660       rmdir $directory or die "Error: Could not remove directory $directory: $!\n";
       
  1661       while (($directory = dirname($directory)) && -e $directory && !scalar @{Utils::ReadDir($directory)}) {
       
  1662         print "Removing directory $directory...\n" if ($self->{verbose});
       
  1663         rmdir $directory or die "Error: Could not remove directory $directory: $!\n";
       
  1664       }
       
  1665     }
       
  1666   };
       
  1667 
       
  1668   ExecuteSignature($sigName, $filesDeletionSub, $directoriesDeletionSub);
       
  1669 }
       
  1670 
       
  1671 sub InstallComponent {
       
  1672   my $self = shift;
       
  1673   my $comp = lc(shift);
       
  1674   my $ver = shift;
       
  1675   my $overwrite = shift;
       
  1676 
       
  1677   my $relData = RelData->Open($self->{iniData}, $comp, $ver, $self->{verbose});
       
  1678   $relData->WarnIfReleaseTooNew();
       
  1679   $self->UnpackBinaries($comp, $ver, Utils::EpocRoot(), $overwrite);
       
  1680   $self->GenerateSignature($comp, $ver);
       
  1681   $self->SetVersion($comp, $ver);
       
  1682   $self->SetMrpName($comp, $relData->MrpName());
       
  1683   $self->SetInternalVersion($comp, $relData->InternalVersion());
       
  1684   $self->SetStatus($comp, STATUS_CLEAN);
       
  1685 }
       
  1686 
       
  1687 sub UnpackBinaries {
       
  1688   my $self = shift;
       
  1689   my $comp = shift;
       
  1690   my $ver = shift;
       
  1691   my $where = shift;
       
  1692   my $overwrite = (shift || $self->{overwrite});
       
  1693   foreach my $thisBinZip (@{$self->RelevantBinaryZips($comp, $ver)}) {
       
  1694     $overwrite = Utils::Unzip($thisBinZip, $where, $self->{verbose}, $overwrite);
       
  1695   }
       
  1696   
       
  1697   $self->{overwrite} = $overwrite;
       
  1698 }
       
  1699 
       
  1700 sub RelevantBinaryZips {
       
  1701   my $self = shift;
       
  1702   my $comp = shift;
       
  1703   my $ver = shift;
       
  1704   $self->PathData()->CheckReleaseExists($comp, $ver);
       
  1705 
       
  1706   my $requiredBinaries = $self->{iniData}->RequiredBinaries($comp);
       
  1707   my $relDir = $self->PathData->LocalArchivePathForExistingOrNewComponent($comp, $ver);
       
  1708   my @relevantBinaries = ();
       
  1709   foreach my $thisRelFile (@{Utils::ReadDir($relDir)}) {
       
  1710     if ($thisRelFile eq 'binaries.zip') {
       
  1711       push (@relevantBinaries, "$relDir\\$thisRelFile");
       
  1712       next;
       
  1713     }
       
  1714     if ($thisRelFile =~ /^binaries_(.*)\.zip$/) {
       
  1715       my $category = $1;
       
  1716       if ($requiredBinaries) {
       
  1717 	foreach my $requiredBinary (@$requiredBinaries) {
       
  1718 	  if (($category =~ /^$requiredBinary\_/) || ($category eq $requiredBinary)) {
       
  1719 	    push (@relevantBinaries, "$relDir\\$thisRelFile");
       
  1720 	    last;
       
  1721 	  }
       
  1722 	}
       
  1723       }
       
  1724       else {
       
  1725 	push (@relevantBinaries, "$relDir\\$thisRelFile");
       
  1726       }
       
  1727     }
       
  1728     elsif ($thisRelFile =~ /^exports[a-z].zip$/i) {
       
  1729       push (@relevantBinaries, "$relDir\\$thisRelFile");
       
  1730     }
       
  1731   }
       
  1732   return \@relevantBinaries;
       
  1733 }
       
  1734 
       
  1735 sub UnpackSource {
       
  1736   my $self = shift;
       
  1737   my $comp = shift;
       
  1738   my $ver = shift;
       
  1739   my $where = shift;
       
  1740   my $overwrite = shift;
       
  1741   my $skipinstall = 0;
       
  1742   unless (defined $overwrite) {
       
  1743     $overwrite = 0;
       
  1744   }
       
  1745   my $showProgress = shift;
       
  1746   unless (defined $showProgress) {
       
  1747     $showProgress = 0;
       
  1748   }
       
  1749   my $toValidate = shift;
       
  1750   unless (defined $toValidate) {
       
  1751     $toValidate = 0;
       
  1752   }
       
  1753 
       
  1754   my $changeInCat = 0;
       
  1755 
       
  1756   $self->PathData()->CheckReleaseExists($comp, $ver);
       
  1757 
       
  1758   if ($where eq "\\") {
       
  1759     $where = Utils::SourceRoot();
       
  1760   }
       
  1761 
       
  1762   # Unpack all categories of source code that are available.
       
  1763   my $relDir = $self->PathData->LocalArchivePathForExistingOrNewComponent($comp, $ver);
       
  1764 
       
  1765   opendir(RELDIR, $relDir) or die "Error: can't opendir $relDir\n";
       
  1766   my @srcZipNames = grep {/source[a-z]\.zip/i} map {"$relDir\\$_"} readdir(RELDIR);
       
  1767   close RELDIR;
       
  1768 
       
  1769   if ($self->{verbose} and scalar(@srcZipNames) == 0) {
       
  1770     print "No source available for $comp $ver\n";
       
  1771   }
       
  1772   else {
       
  1773     unless ($overwrite) {
       
  1774       my $checkFailed = 0;
       
  1775       foreach my $thisSrcZip (@srcZipNames) {
       
  1776         if (Utils::CheckZipFileContentsNotPresent($thisSrcZip, $where, $self->{iniData})) {
       
  1777           $checkFailed = 1;
       
  1778         }
       
  1779       }
       
  1780       if ($checkFailed) {
       
  1781         warn "Warning: Above errors found, skipping the unpacking of $comp zips...\n";
       
  1782         $skipinstall = 1;
       
  1783       }
       
  1784     }
       
  1785 
       
  1786     unless($skipinstall){
       
  1787       foreach my $thisSrcZip (@srcZipNames) {
       
  1788         if ($showProgress) {
       
  1789           my $significantDir = Utils::SignificantZipDir($thisSrcZip);
       
  1790           my $unzipDir  = Utils::ConcatenateDirNames($where, $significantDir);
       
  1791 
       
  1792           if($self->{iniData}->HasMappings()){
       
  1793             $unzipDir = $self->{iniData}->PerformMapOnFileName($unzipDir);
       
  1794           }
       
  1795 
       
  1796           print "\tUnpacking \"$thisSrcZip\" into \"$unzipDir\"...\n";
       
  1797         }
       
  1798 
       
  1799         $changeInCat = Utils::UnzipSource($thisSrcZip, $where, $self->{verbose}, $overwrite, $self->{iniData}, $toValidate, $comp);
       
  1800         if($changeInCat==1 && $toValidate ==1) {
       
  1801           last;
       
  1802 	}
       
  1803       }
       
  1804     }
       
  1805   }
       
  1806 
       
  1807   return $changeInCat; # 1 = change in cat found, 0 = change in cat not found. Return value only used for validation.
       
  1808 }
       
  1809 
       
  1810 sub SignatureName {
       
  1811   my $comp = shift;
       
  1812   my $ver = shift;
       
  1813   croak unless defined $ver;
       
  1814   return Utils::PrependEpocRoot("\\epoc32\\relinfo\\$comp.$ver.sig");
       
  1815 }
       
  1816 
       
  1817 sub DecodeSignatureName {
       
  1818   my $self = shift;
       
  1819   my $sigName = shift;
       
  1820   my $comp;
       
  1821   my $ver;
       
  1822   my $name = $sigName;
       
  1823   $name =~ s/.*\\epoc32\\relinfo\\(.*)\.sig/$1/;
       
  1824   foreach my $thisComp (keys %{$self->{db}}) {
       
  1825     my $thisVer = $self->{db}->{$thisComp}->{ver};
       
  1826     if ("$thisComp.$thisVer" eq $name) {
       
  1827       $comp = $thisComp;
       
  1828       $ver = $thisVer;
       
  1829     }
       
  1830   }
       
  1831 
       
  1832   unless (defined $comp and defined $ver) {
       
  1833     die "Error: Couldn't decode signature name \"$sigName\"\n";
       
  1834   }
       
  1835 
       
  1836   return ($comp, $ver);
       
  1837 }
       
  1838 
       
  1839 sub ComponentDir {
       
  1840   require Carp;
       
  1841   Carp->import;
       
  1842   confess ("Obsolete method called");
       
  1843 }
       
  1844 
       
  1845 sub ReleaseDir {
       
  1846   require Carp;
       
  1847   Carp->import;
       
  1848   confess ("Obsolete method called");
       
  1849 }
       
  1850 
       
  1851 sub PathData {
       
  1852   my $self = shift;
       
  1853   return $self->{iniData}->PathData();
       
  1854 }
       
  1855 
       
  1856 sub CheckForAddedFiles {
       
  1857   my $self = shift;
       
  1858   my $reldata = shift;
       
  1859   my $tempdir = shift;
       
  1860 
       
  1861   # Here we have been asked to search for files that exist in the real source directory,
       
  1862   # but don't exist in the temporary source directory.
       
  1863 
       
  1864   my $foundextra = 0; # let's hope this stays zero
       
  1865   foreach my $item (keys %{$reldata->SourceItems}) {
       
  1866     $item = Utils::PrependSourceRoot($item);
       
  1867     next unless -d $item; # we're only checking for added files, so we don't care unless this
       
  1868                           # is a directory in which there ought to be files.
       
  1869 
       
  1870     print "Looking for added files inside \"$item\"\n" if ($self->{verbose});
       
  1871     # Ah, the lovely Find::File
       
  1872     find(sub {
       
  1873       my $tempfile = Utils::ConcatenateDirNames($tempdir, Utils::RemoveSourceRoot($File::Find::name));
       
  1874       # Be careful with that line - an extra \\ anywhere and it breaks, such is DOS...
       
  1875 
       
  1876       print "Checking existence of \"$tempfile\"\n" if ($self->{verbose}>1);
       
  1877       unless (-e $tempfile) {
       
  1878         print "\"$File::Find::name\" only exists in new source code.\n" if ($self->{verbose});
       
  1879         $foundextra++;
       
  1880         $File::Find::prune = 1 unless ($self->{verbose}); # skip some of the rest
       
  1881       }
       
  1882     }, $item);
       
  1883 
       
  1884     return $foundextra if ($foundextra && !$self->{verbose});
       
  1885         # don't bother scanning other directories unless it's verbose
       
  1886   }
       
  1887     return $foundextra;
       
  1888 }
       
  1889 
       
  1890 sub GetReleaseSize {
       
  1891   my $self = shift;
       
  1892   my $comp = shift;
       
  1893   my $ver = shift;
       
  1894   $self->{relsize}->{$comp}->{$ver} = $self->AddUpReleaseSize($comp, $ver) unless defined $self->{relsize}->{$comp}->{$ver};
       
  1895   return $self->{relsize}->{$comp}->{$ver};
       
  1896 }
       
  1897 
       
  1898 sub AddUpReleaseSize {
       
  1899   my $self = shift;
       
  1900   my $comp = shift;
       
  1901   my $version = shift;
       
  1902   my $pathdata = $self->{iniData}->PathData();
       
  1903   my $path = $pathdata->LocalArchivePathForExistingComponent($comp, $version);
       
  1904   die "Component $comp $version didn't exist\n" unless $path;
       
  1905   opendir(DIR, $path) or die "Couldn't open directory \"$path\" because $!";
       
  1906   my @entries = grep { ! m/^\./ } readdir(DIR);
       
  1907   closedir DIR;
       
  1908   my $size = 0;
       
  1909   print "Adding up size of $comp $version\n" if ($self->{verbose});
       
  1910   foreach my $file (@entries) {
       
  1911     my $full = $path . "\\" . $file;
       
  1912     $size += -s $full;
       
  1913   }
       
  1914   return $size;
       
  1915 }
       
  1916 
       
  1917 sub GetEnvironmentSize {
       
  1918   my $self = shift;
       
  1919   my $comp = shift;
       
  1920   my $ver = shift;
       
  1921   my $deltasize = shift;
       
  1922   $self->{envsize}->{$comp}->{$ver} = $self->AddUpEnvSize($comp, $ver, $deltasize) if (!exists $self->{envsize}->{$comp}->{$ver});
       
  1923   return $self->{envsize}->{$comp}->{$ver};
       
  1924 }
       
  1925 
       
  1926 sub AddUpEnvSize {
       
  1927   my $self = shift;
       
  1928   my $maincomp = shift;
       
  1929   my $mainver = shift;
       
  1930   my $deltasize = shift;
       
  1931   my $relData = RelData->Open($self->{iniData}, $maincomp, $mainver, $self->{verbose});
       
  1932   die "Component $maincomp version $mainver didn't exist\n" unless $relData;
       
  1933   my $compsToValidate = $relData->Environment();
       
  1934   my $size = 0;
       
  1935   while ((my $comp, my $ver) = each %$compsToValidate) {
       
  1936     # If a delta size is requested and the component version does not
       
  1937     # match the main component version then don't increment the size
       
  1938     next if ($deltasize && ($mainver ne $ver));
       
  1939     
       
  1940     $size += $self->GetReleaseSize($comp, $ver);
       
  1941   }
       
  1942   return $size;
       
  1943 }
       
  1944 
       
  1945 1;
       
  1946 
       
  1947 __END__
       
  1948 
       
  1949 =head1 NAME
       
  1950 
       
  1951 EnvDb.pm - A database to keep track of component versions installed on a development drive.
       
  1952 
       
  1953 =head1 DESCRIPTION
       
  1954 
       
  1955 The database is implemented as a tied hash. It provides a persistent store of component / version pairs. Also provides facilities for checking and validating the contents of an environemnt.
       
  1956 
       
  1957 Each component has a status associated with it. The possible values are as follows:
       
  1958 
       
  1959 =over 4
       
  1960 
       
  1961 =item C<STATUS_CLEAN>: the binaries on disk match those in the release packet
       
  1962 
       
  1963 =item C<STATUS_DIRTY>: the binaries on disk don't appear to match those in the release packet
       
  1964 
       
  1965 =item C<STATUS_DIRTY_SOURCE>: the binaries match, but the source code doesn't
       
  1966 
       
  1967 =item C<STATUS_PENDING_RELEASE>: the component has been set to 'pending release'
       
  1968 
       
  1969 =back
       
  1970 
       
  1971 =head1 INTERFACE
       
  1972 
       
  1973 =head2 Object Management
       
  1974 
       
  1975 =head3 Open
       
  1976 
       
  1977 Expects to be passed an C<IniData> reference and a verbosity level. Opens the C<EnvDb> on the current drive. If not already present, an empty databse is created. This must be done before any of the following interfaces are used.
       
  1978 
       
  1979 =head3 Close
       
  1980 
       
  1981 Closes the database file.
       
  1982 
       
  1983 =head2 Data Management
       
  1984 
       
  1985 =head3 Version
       
  1986 
       
  1987 Expects to be passed a component name. Returns the version of the component that is currently installed. Returns undef if there is no version currently installed.
       
  1988 
       
  1989 =head3 VersionInfo
       
  1990 
       
  1991 Returns a reference to an in memory hash containing component component name / version pairs for every entry in the database.
       
  1992 
       
  1993 =head3 SetVersion
       
  1994 
       
  1995 Expects to be passed a component name and a optionally a version. If the version is specified, a database entry for the component is created, or, if it is already present, updated. If the version is not specified, and a database entry for the component entry exists, it is deleted.
       
  1996 
       
  1997 =head3 InternalVersion
       
  1998 
       
  1999 Expects to be passed a component name. Returns the internal version of the component that is currently installed. Returns undef if the component is not currently installed.
       
  2000 
       
  2001 =head3 SetInternalVersion
       
  2002 
       
  2003 Expects to be passed a component name and an internal version. Dies if an entry for the component is not already present in the database. Store the component's internal version.
       
  2004 
       
  2005 =head3 Status
       
  2006 
       
  2007 Expects to be passed a component name. Dies if an entry for the component is not already present in the database. Returns the component's last recorded status (which may be C<STATUS_CLEAN>, C<STATUS_DIRTY> or C<STATUS_PENDING_RELEASE>).
       
  2008 
       
  2009 =head3 SetStatus
       
  2010 
       
  2011 Expects to be passed a component name and a status integer. Dies if an entry for the component is not already present in the database. Updates the component's database entry with the new status.
       
  2012 
       
  2013 =head3 MrpName
       
  2014 
       
  2015 Expects to be passed a component name. Dies if an entry for the component is not already present in the database. Returns the corresponding F<mrp> name.
       
  2016 
       
  2017 =head3 SetMrpName
       
  2018 
       
  2019 Expects to be passed a component name and an F<mrp> name. Dies if an entry for the component is not already present in the database. Stores of the F<mrp> name of the component.
       
  2020 
       
  2021 =head3 ComponentsPendingRelease
       
  2022 
       
  2023 Returns a reference to a hash of hash references. The primary hash key is component name. The secondary hashes each containing details a component that is pending release. The secondary hashes contain the following keys:
       
  2024 
       
  2025  mrpName
       
  2026  ver
       
  2027  intVer
       
  2028 
       
  2029 =head3 BinaryInfo
       
  2030 
       
  2031 Expects to be passed the name of a binary file. Searches for this file name within the component signatures. If it is not found there, then checks for components that are pending release. C<MrpData> objects are then created for each of these to see if the binary file is about to be released. Dies if the file is still not found. Otherwise, returns a two dimentional array containing the component name, verion and current file status.
       
  2032 
       
  2033 =head3 ListBins
       
  2034 
       
  2035 Expects to be passed a component name. Returns a 2D array containing all the file names owned by component and their current status. These facts will be in the first and second column; subsequent columns may hold further information. The table contains a header row describing what each column is.
       
  2036 
       
  2037 =head2 Environment Scans
       
  2038 
       
  2039 =head3 CheckEnv
       
  2040 
       
  2041 Performs a scan of the F<\epoc32> tree building a hash of all file names. Calls C<CheckComp> for all the components installed on the drive. C<CheckComp> will remove files that pass the check from the F<\epoc32> tree hash. Any file names left in the hash after all components have been checked will be printed to warn the user, since their origin is unknown to the release tools. The F<reltools.ini> keyword C<ignore_binary> my be used to specify (using file names with DOS style wild characters) binary files to be ignored in the checking process. As standard, the following are ignored:
       
  2042 
       
  2043  \epoc32\relinfo\*
       
  2044  \epoc32\build\*
       
  2045  \epoc32\wins\c\*
       
  2046  \epoc32\release\*.ilk
       
  2047  \epoc32\release\*.bsc
       
  2048  \epoc32\data\emulator\epoc.sys.ini
       
  2049 
       
  2050 Returns the overall status of the environement after the check (which may be of value C<STATUS_CLEAN>, C<STATUS_DIRTY> or C<STATUS_PENDING_RELEASE>), a reference to a list of C<MrpData> objects that are pending release, a reference to a list of component that failed their check, and a reference to a list of unaccounted files.
       
  2051 
       
  2052 =head3 CheckComp
       
  2053 
       
  2054 Expects to be passed a component name and optionally a scalar flag indicating if the check should continue after the first failure is found (true means continue). Details of any files that fail their check will be printed. Returns the status of the component after the check (which may be of value C<STATUS_CLEAN>, C<STATUS_DIRTY>, C<STATUS_DIRTY_SOURCE> or C<STATUS_PENDING_RELEASE>), and a reference to an C<MrpData> object if the status is C<STATUS_PENDING_RELEASE>.
       
  2055 
       
  2056 CheckComp does not check the source code files. In fact, if it determines the binaries match, then it returns either C<STATUS_CLEAN> or C<STATUS_DIRTY_SOURCE> depending on what the environment database says. A component will only ever attain the status of C<STATUS_DIRTY_SOURCE> through the operation of ValidateComp: effectively CheckComp just passes that information through, if the component otherwise appears clean.
       
  2057 
       
  2058 =head3 ValidateEnv
       
  2059 
       
  2060 Calls C<ValidateComp> for all the components installed on the drive that don't have a status of I<pending release>. Returns a reference to a list of components names that failed. May optionally be passed a component name and version of an external environment against which to validate. This mode may only be used when the current environment database is empty. It causes a complete set of database entries to be written corresponding to the external environment. However, a dummy signature file will be generated for components that fail their validation which contains the names of the binaries released in the external environment, but zero last modified times and sizes. This is to ensure that C<CheckEnv> continues to report these components as C<STATUS_DIRTY>.
       
  2061 
       
  2062 =head3 ValidateComp
       
  2063 
       
  2064 Expects to be passed a component name, a version and optionally two scalar flags indicating:
       
  2065 
       
  2066 =over 4
       
  2067 
       
  2068 =item whether validation should continue after the first failure is found (true means continue)
       
  2069 
       
  2070 =item whether source code should be validated
       
  2071 
       
  2072 =back
       
  2073 
       
  2074 Makes use of Manifest.pm and constructs manifest object of the current environment using the mrp file for the components and another manifest object using the manifest file available in the archive location of the previous release for the component. These objects are compared for their similarity and shall return STATUS_CLEAN if everything validates OK and returns STATUS_DIRTY otherwise.
       
  2075 
       
  2076 If for some reasons, validation through manifest objects is not possible, then the call is transferred to the old process of validation described herewith as follows:
       
  2077 
       
  2078 The process returns the overall status of the release, which is C<STATUS_DIRTY> if there are dirty binaries, C<STATUS_DIRTY_SOURCE> if the binaries are clean but there is dirty source code, or C<CLEAN> if everything validates OK. C<STATUS_DIRTY_SOURCE> will only ever be set if source code validation is turned on; otherwise all components will be set to either C<CLEAN> or C<DIRTY>.
       
  2079 
       
  2080 If the validation passes, but there is currently no entry for the release in the database, an entry is created with details corresponding to the version being validated. Whether or not an entry previously existed, if the validation passes, the component's status is set to C<STATUS_CLEAN> and a signature file is generated. If the validation failed and there is already an entry in the database for the component, it's status is set to C<STATUS_DIRTY> or C<STATUS_DIRTY_SOURCE> as appropriate.
       
  2081 
       
  2082 If the overall process results in validating the component status as DIRTY, then the manifest information for the current environment will be generated and saved as a manifest file in a temporary location within local file system for use during release processes.
       
  2083 
       
  2084 =head2 Environment Management
       
  2085 
       
  2086 =head3 InstallComponent
       
  2087 
       
  2088 Expects to be passed a component name, and a version. Unpacks the component's binaries, and creates (or updates) a complete database entry from the provided version and information read out of the release's C<RelData> object.
       
  2089 
       
  2090 =head3 RemoveComponent
       
  2091 
       
  2092 Expects to be passed a component name. Removes all the binary files associated with the installed version of the component, the component's signature file and the component's environment database record.
       
  2093 
       
  2094 =head3 DeleteSource
       
  2095 
       
  2096 Expects to be passed a component name, a dryrun and a force flag. Removes all the source files associated with the installed version of the component. If dryrun is used the script just reports what it would do. If force is used the script would delete write-protected files.
       
  2097 
       
  2098 =head3 UnpackBinaries
       
  2099 
       
  2100 Expects to be passed a component name, a version and a directory in which the release should be installed. Unpacks the component's binaries into the specified directory. The environment database is neither consulted, nor modified by this interface. It is intended to allow a set of released binaries to be temporarily unpacked (for example, for validation purposes)
       
  2101 
       
  2102 =head3 UnpackSource
       
  2103 
       
  2104 Expects to be passed a component name, a version, a directory in which the release should be installed, a flag which represents the verbose level, a flag which represents to overwrite or not, an inidata and a flag which represent whether this process is for validation or not. Unpacks the component's source into the specified directory. The environment database is neither consulted, nor modified by this interface. Returns a change in category flag, when flag is 1 a change in category has been found. Change in category flag is only uses when a validation occurs.
       
  2105 
       
  2106 =head3 GetReleaseSize
       
  2107 
       
  2108 Takes a component name and a version number. Returns the total size (in bytes) of the zips in the local archive making up a release.
       
  2109 
       
  2110 =head3 GetEnvironmentSize
       
  2111 
       
  2112 Takes a component name and a version number. Returns the total size (in bytes) of the zips in the local archive making up the environment.
       
  2113 
       
  2114 =head2 Notable private methods
       
  2115 
       
  2116 =head3 EvalidateDirectories
       
  2117 
       
  2118 Expects to be passed two directory names; it will then run C<EValid> over the two. Returns a Boolean (whether the two directories match), and prints the results according to the verbosity level. If the verbosity level is 1 or greater, details of failures are printed. If the verbosity level is greater than 1, all C<EValid> output is printed.
       
  2119 
       
  2120 =head3 CheckForAddedFiles
       
  2121 
       
  2122 This method checks to see if any files have been added to a component since it was packetised. It's part of source validation. It uses C<Find::File> to list all the files that are in a component's source code directory, then checks each of them are in a temporary directory which has been unzipped from the release packet.
       
  2123 
       
  2124 =head1 KNOWN BUGS
       
  2125 
       
  2126 None.
       
  2127 
       
  2128 =head1 COPYRIGHT
       
  2129 
       
  2130  Copyright (c) 2000-2009 Nokia Corporation and/or its subsidiary(-ies).
       
  2131  All rights reserved.
       
  2132  This component and the accompanying materials are made available
       
  2133  under the terms of the License "Eclipse Public License v1.0"
       
  2134  which accompanies this distribution, and is available
       
  2135  at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
  2136  
       
  2137  Initial Contributors:
       
  2138  Nokia Corporation - initial contribution.
       
  2139  
       
  2140  Contributors:
       
  2141  
       
  2142  Description:
       
  2143  
       
  2144 
       
  2145 =cut