releasing/cbrtools/perl/MrpData.pm
changeset 607 378360dbbdba
parent 602 3145852acc89
equal deleted inserted replaced
591:22486c9c7b15 607:378360dbbdba
       
     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 MrpData;
       
    17 
       
    18 use strict;
       
    19 use base qw(Symbian::CBR::MRPInterface);
       
    20 use File::Find;
       
    21 use File::Copy;
       
    22 use File::Basename;
       
    23 use File::Spec;
       
    24 use Utils;
       
    25 use Cwd;
       
    26 use Symbian::CBR::MRP::Reader;
       
    27 use IniData;
       
    28 
       
    29 use Carp;
       
    30 
       
    31 use constant MAX_PATH_LENGTH => 245; #If a file name has a path > MAX_PATH_LENGTH an error will be produced.
       
    32 
       
    33 
       
    34 
       
    35 use File::Path;
       
    36 use XML::Simple;
       
    37 use POSIX qw(strftime);
       
    38 use Data::Dumper;
       
    39 
       
    40 
       
    41 our $cache = {}; # Persistent (private) cache
       
    42 
       
    43 sub New {
       
    44   my $pkg = shift;
       
    45   my $self = {};
       
    46   bless $self, $pkg;
       
    47   $self->{mrpName} = shift;
       
    48   $self->{ver} = shift;
       
    49   $self->{intVer} = shift;
       
    50   $self->{iniData} = shift || IniData->New();
       
    51   $self->{verbose} = shift || 0;
       
    52   $self->{fixMissingLibs} = shift;
       
    53   $self->ExpandMrpName();
       
    54   
       
    55   # This will be used by the IPR stuff, so that it can popultate the MrpData object without circular stuff
       
    56   my $doNotRead = shift;
       
    57   
       
    58   #remove the / from mrpname
       
    59   $self->{mrpName} =~ s/^[\\\/]//;
       
    60   
       
    61   # Lowercase the MRP name so that the case is consistent when checking the cache
       
    62   my $lcMrpName = lc ($self->{mrpName});
       
    63   
       
    64   if ($cache->{$lcMrpName}) {
       
    65     $cache->{$lcMrpName}->{ver} = $self->{ver} if ($self->{ver});
       
    66     $cache->{$lcMrpName}->{intVer} = $self->{intVer} if ($self->{intVer});             
       
    67     return $cache->{$lcMrpName};
       
    68   }
       
    69 
       
    70   $cache->{$lcMrpName} = $self;
       
    71   
       
    72   if (!$doNotRead) {
       
    73     $self->ReadMrp();
       
    74   }
       
    75   
       
    76   return $self;
       
    77 }
       
    78 
       
    79 sub Populated {
       
    80   my $self = shift;
       
    81   
       
    82   return $self->{populated};
       
    83 }
       
    84 
       
    85 
       
    86 sub ExpandMrpName {
       
    87   my $self = shift;
       
    88 
       
    89   unless ($self->{mrpName} =~ /.mrp$/i) {
       
    90     $self->{mrpName} .= '.mrp';
       
    91   }
       
    92 }
       
    93 
       
    94 sub Component {
       
    95   my $self = shift;
       
    96   unless (exists $self->{comp}) {
       
    97     die "Error: Component name not found in mrp\n";
       
    98   }
       
    99   return $self->{comp};
       
   100 }
       
   101 
       
   102 sub MrpName {
       
   103   my $self = shift;
       
   104   return $self->{mrpName};
       
   105 }
       
   106 
       
   107 sub ExternalVersion {
       
   108   my $self = shift;
       
   109   return $self->{ver};
       
   110 }
       
   111 
       
   112 sub InternalVersion {
       
   113   my $self = shift;
       
   114   return $self->{intVer};
       
   115 }
       
   116 
       
   117 sub NotesSource {
       
   118   my $self = shift;
       
   119   unless (exists $self->{notes_src}) {
       
   120     die "Error: Notes source not found in mrp for $self->{comp}\n";
       
   121   }
       
   122   return $self->{notes_src};
       
   123 }
       
   124 
       
   125 sub ClassifySource {
       
   126   my $self = shift;
       
   127   return if defined $self->{src};
       
   128 
       
   129   foreach my $item (keys %{$self->{srcitems}}) {
       
   130     if (-d Utils::PrependSourceRoot($item)) {
       
   131       $self->HandleSourceDir($item);
       
   132     }
       
   133     elsif (-f Utils::PrependSourceRoot($item)) {
       
   134       $self->HandleSourceFile($item);
       
   135     }
       
   136     else {
       
   137       die "Error: \"$item\" is not a file or directory in $self->{mrpName}\n";
       
   138     }
       
   139   }
       
   140 }
       
   141 
       
   142 sub SourceCategories {
       
   143   my $self = shift;
       
   144   $self->ClassifySource();
       
   145   if (defined $self->{src}) {
       
   146     my @categories = keys %{$self->{src}};
       
   147     return \@categories;
       
   148   }
       
   149   return [];
       
   150 }
       
   151 
       
   152 sub Source {
       
   153   my $self = shift;
       
   154   my $category = uc(shift);
       
   155   $self->ClassifySource();
       
   156   unless (defined $category) {
       
   157     $category = 'unfiltered';
       
   158   }
       
   159   if (defined $self->{src}->{$category}) {
       
   160     return $self->{src}->{$category};
       
   161   }
       
   162   return [];
       
   163 }
       
   164 
       
   165 sub SourceFilterErrors {
       
   166   my $self = shift;
       
   167   $self->ClassifySource();
       
   168   if (defined $self->{sourceFilterErrors}) {
       
   169     return $self->{sourceFilterErrors};
       
   170   }
       
   171   return [];
       
   172 }
       
   173 
       
   174 sub BinaryCategories {
       
   175   my $self = shift;
       
   176 
       
   177   $self->ProcessBinaries();
       
   178 
       
   179   if ($self->{bins}) {
       
   180     my @categories = sort keys %{$self->{bins}};
       
   181     return \@categories;
       
   182   }
       
   183   return [];
       
   184 }
       
   185 
       
   186 sub Binaries {
       
   187   my $self = shift;
       
   188   my $category = shift;
       
   189 
       
   190   $self->ProcessBinaries();
       
   191 
       
   192   my @binList = ();
       
   193   if ($category) {
       
   194     die unless (exists $self->{bins}->{$category});
       
   195     foreach my $thisBin (sort keys %{$self->{bins}->{$category}}) {
       
   196       push (@binList, $self->{bins}->{$category}->{$thisBin});
       
   197     }
       
   198   }
       
   199   else {
       
   200     foreach my $thisCategory (@{$self->BinaryCategories()}) {
       
   201       push (@binList, @{$self->Binaries($thisCategory)});
       
   202     }
       
   203   }
       
   204   return \@binList;
       
   205 }
       
   206 
       
   207 sub ExportCategories {
       
   208   my $self = shift;
       
   209   if ($self->{iniData}->CategoriseExports()) {
       
   210     $self->ProcessExports();
       
   211     if ($self->{exports}) {
       
   212       my @categories = sort keys %{$self->{exports}};
       
   213       return \@categories;
       
   214     }
       
   215   }
       
   216   return [];
       
   217 }
       
   218 
       
   219 sub Exports {
       
   220   my $self = shift;
       
   221   my $category = uc(shift);
       
   222 
       
   223   $self->ProcessExports();
       
   224 
       
   225   my @exportList = ();
       
   226   if ($self->{iniData}->CategoriseExports()) {
       
   227     if ($category) {
       
   228       die unless (exists $self->{exports}->{$category});
       
   229       push (@exportList, @{$self->{exports}->{$category}});
       
   230     }
       
   231     else {
       
   232         foreach my $thisCategory (@{$self->ExportCategories()}) {
       
   233           push (@exportList, @{$self->Exports($thisCategory)});
       
   234         }
       
   235     }
       
   236   }
       
   237   elsif ($category) {
       
   238     die; # There are never any export categories if export categorisation is not enabled. Caller didn't call ExportCategories prior to this.
       
   239   }
       
   240   return \@exportList;
       
   241 }
       
   242 
       
   243 
       
   244 sub ClassifyAutomaticExports {
       
   245   # Classify exports that were specified using the 'exports' keyword.
       
   246   #
       
   247   # This is a bit ugly. The problem's root is that it's not possible to ask the build tools where exports originated
       
   248   # from. This information is needed to be able to categorise exports. To get it, the release tools therefore have
       
   249   # to go 'under the hood' of the build tools. Yuk!
       
   250   #
       
   251   # Implementation choices were a) Parse bld.inf files, and b) Parse the generated export makefiles. Option (b) was
       
   252   # chosen because bld.infs are notoriously difficult to robustly parse.
       
   253 
       
   254   my $self = shift;
       
   255   if (exists $self->{exports}->{automatic}) { # Only perform the classification if we haven't already done it.
       
   256     foreach my $thisAbldPath (@{$self->{exports}->{abldPaths}}) {
       
   257       $thisAbldPath = Utils::PrependSourceRoot($thisAbldPath);
       
   258       # Scan the makefile looking for the exports we're expecting (from invoking 'abld -w exports' in HandleExports).
       
   259 
       
   260       my $exportMakeFile;
       
   261       my $testExportMakeFile;
       
   262       $self->ExportMakeFile($thisAbldPath, \$exportMakeFile, \$testExportMakeFile);
       
   263 
       
   264       if ($exportMakeFile){
       
   265         $self->ProcessExportMakeFile($exportMakeFile, $thisAbldPath);
       
   266 
       
   267       }
       
   268       if ($testExportMakeFile){
       
   269         $self->ProcessExportMakeFile($testExportMakeFile, $thisAbldPath);
       
   270       }
       
   271     }
       
   272 
       
   273   }
       
   274 
       
   275   if (scalar(keys %{$self->{exports}->{automatic}}) > 0) {
       
   276     foreach my $unprocessed_export (keys %{$self->{exports}->{automatic}})
       
   277 	{
       
   278 		print "UNPROCESSED EXPORT: $unprocessed_export\n";
       
   279 	}
       
   280 
       
   281     die "Error: Problem extracting export IPR categories for \"$self->{mrpName}\"\n";
       
   282   }
       
   283 
       
   284   delete $self->{exports}->{automatic};
       
   285   delete $self->{exports}->{abldPaths};
       
   286 }
       
   287 
       
   288 sub ProcessExportMakeFile {
       
   289 	my $self = shift;
       
   290 	my $exportMakeFile = shift;
       
   291         my $abldPath = shift;
       
   292         my $errors = 0;
       
   293         open (MAKEFILE, $exportMakeFile) or die "Error: Couldn't open \"$exportMakeFile\": $!\n";
       
   294         while (my $line = <MAKEFILE>) {
       
   295           $line =~ s/\\ / /g; # Get rid of escaped spaces.
       
   296           if ($line =~ /^(.*)\s+:\s+(.*)/) {
       
   297             # Found a possibility - need to see if it's one of the exports we're looking for.
       
   298             my $destination = $1;
       
   299             my $source = $2;
       
   300             if (Utils::WithinEpocRoot($destination)) {
       
   301               $destination = Utils::RemoveEpocRoot($destination);
       
   302             }
       
   303             if (exists $self->{exports}->{automatic}->{lc($destination)}) {
       
   304               $source = Utils::RemoveSourceRoot($source);
       
   305               # Add to exports to be processed - source and destination
       
   306               push @{$self->{exportsToBeProcessed}}, {source => $source,
       
   307                                                       destination => $destination,
       
   308                                                       abldPath => $abldPath};
       
   309               
       
   310               delete $self->{exports}->{automatic}->{lc($destination)};
       
   311             }
       
   312           }
       
   313           elsif ($line =~ /unzip\s+-u\s+-o\s+(.*)\s+-d\s+\"(.*)\"/) {
       
   314             # Looks like a zip file being exported - check contents.
       
   315             my $zipFileName = $1;
       
   316             my $destinationDir = $2;
       
   317             my $zipContents = Utils::ListZip($zipFileName);
       
   318             $zipFileName = Utils::RemoveSourceRoot($zipFileName);
       
   319             foreach my $thisExport (@$zipContents) {
       
   320               $thisExport = Utils::ConcatenateDirNames($destinationDir, $thisExport);
       
   321               if (Utils::WithinEpocRoot($thisExport)) {
       
   322                 $thisExport = Utils::RemoveEpocRoot($thisExport);
       
   323               }
       
   324               if (exists $self->{exports}->{automatic}->{lc($thisExport)}) {
       
   325                 # Add to exports to be processed - source and destination
       
   326                 push @{$self->{exportsToBeProcessed}}, {source => $zipFileName,
       
   327                                                         destination => $thisExport,
       
   328                                                         abldPath => $abldPath};
       
   329                 
       
   330                 delete $self->{exports}->{automatic}->{lc($thisExport)};
       
   331               }
       
   332             }
       
   333           }
       
   334         }
       
   335         close (MAKEFILE);
       
   336 }
       
   337 
       
   338 sub ExportMakeFile {
       
   339   # Invoke 'bldmake bldfiles -v' to find the full path to the EXPORT.MAKE file.
       
   340   my $self = shift;
       
   341   my $abldPath = shift;
       
   342   my $exportMakeFileRef = shift;
       
   343   my $testExportMakeFileRef = shift;
       
   344   my $cwd = cwd();
       
   345   my $last = 0;
       
   346   chdir $abldPath or die "Error: Couldn't change working directory to \"$abldPath\": $!\n";
       
   347   open (BLDMAKE, 'bldmake bldfiles -v |') or die "Error: Couldn't run \"bldmake bldfiles -v |\" in \"abldPath\": $!\n";
       
   348   my $exportMakeFile;
       
   349   my $testExportMakeFile;
       
   350   while (my $line = <BLDMAKE>) {
       
   351     if ($line =~ /Creating \"(.*EXPORT.MAKE)\"/) {
       
   352       $exportMakeFile = $1;
       
   353       if ($last == 1){ # found both EXPORT.MAKE and EXPORTTEST.MAKE
       
   354         last;
       
   355       }
       
   356       $last = 1;
       
   357     }
       
   358     elsif ($line =~ /Creating \"(.*EXPORTTEST.MAKE)\"/) {
       
   359       $testExportMakeFile = $1;
       
   360       if ($last == 1){ # found both EXPORT.MAKE and EXPORTTEST.MAKE
       
   361         last;
       
   362       }
       
   363       $last = 1;
       
   364     }
       
   365   }
       
   366   close (BLDMAKE);
       
   367   chdir $cwd or die "Error: Couldn't change working directory to \"$cwd\": $!\n";
       
   368   unless ($exportMakeFile || $testExportMakeFile) {
       
   369     die "Error: Unable to find \"EXPORT.MAKE\" or \"EXPORTTEST.MAKE\" for \"$abldPath\"\n";
       
   370   }
       
   371   $$exportMakeFileRef = $exportMakeFile;
       
   372   $$testExportMakeFileRef = $testExportMakeFile;
       
   373 }
       
   374 
       
   375 sub ClassifyManualExports {
       
   376   my $self = shift;
       
   377   if (exists $self->{exports}->{manual}) { # Only perform the classification if we haven't already done it.
       
   378     foreach my $thisSource (keys %{$self->{exports}->{manual}}) {
       
   379       push @{$self->{exportsToBeProcessed}}, {source => $thisSource,
       
   380                                               destination => $self->{exports}->{manual}->{$thisSource}};
       
   381     }
       
   382     delete $self->{exports}->{manual};
       
   383   }
       
   384 }
       
   385 
       
   386 sub ExportInfoForCat {
       
   387   my $self = shift;
       
   388   my $category = uc(shift);
       
   389 
       
   390   $self->ProcessExports();
       
   391 
       
   392   return $self->{exportinfo}->{$category};
       
   393 }
       
   394 
       
   395 sub ExportSourceFileInfoForCat {
       
   396   my $self = shift;
       
   397   my $category = uc(shift);
       
   398   my $exportfile = shift;
       
   399 
       
   400   # In AddExport $category is called $class and $exportfile is $destination
       
   401   return $self->{exportinfo}->{$category}->{$exportfile};
       
   402 }
       
   403 
       
   404 sub AddExport {
       
   405   my $self = shift;
       
   406   my $source = shift;
       
   407   my $destination = shift;
       
   408   my $successfullyAdded = 0;
       
   409 
       
   410   my ($class) = Utils::ClassifyPath($self->{iniData}, $source, $self->{verbose}, 0, $self->Component());
       
   411   $class = uc($class);
       
   412 
       
   413   if ($class) {
       
   414     $successfullyAdded = 1;
       
   415     push (@{$self->{exports}->{$class}}, $destination);
       
   416   }
       
   417   else {
       
   418     print "Error: Can't find IPR category for export \"$destination\" in \"$self->{mrpName}\"
       
   419        It should correspond to source file \"$source\"\n";
       
   420   }
       
   421 
       
   422   # Need to record the src paths.
       
   423   $self->{exportinfo}->{$class}->{$destination} = $source;
       
   424 
       
   425   return $successfullyAdded;
       
   426 }
       
   427 
       
   428 sub BinariesAndExports {
       
   429   my $self = shift;
       
   430   # Exports need to be processed first.  If exports are not to be categorised then
       
   431   # they are treated as binary files.
       
   432   my $list = $self->Exports();
       
   433   push (@$list, @{$self->Binaries()});
       
   434   return $list;
       
   435 }
       
   436 
       
   437 sub SourceItems {
       
   438   my $self = shift;
       
   439   return $self->{srcitems};
       
   440 }
       
   441 
       
   442 sub ReadMrp {
       
   443   my $self = shift;
       
   444   my $mrpName = $self->{mrpName};
       
   445   my $cwd = cwd();
       
   446   # If there are mappings and the source root is \\, perform mappings on filename. Otherwise prepend source root.
       
   447   if($self->{iniData}->HasMappings() && Utils::SourceRoot() eq "\\") {
       
   448     $mrpName = $self->{iniData}->PerformMapOnFileName($mrpName);
       
   449   }
       
   450   else{
       
   451     $mrpName = Utils::PrependSourceRoot($mrpName);
       
   452   }
       
   453   
       
   454   my $mrpDir = dirname($mrpName);
       
   455   
       
   456   chdir($mrpDir) or die "Error: Couldn't change working directory to \"$mrpDir\": $!\n";
       
   457  
       
   458   my $reader = Symbian::CBR::MRP::Reader->instance();
       
   459   $reader->SetVerbose() if ($self->{verbose});
       
   460 
       
   461   $reader->ReadFile($mrpName, 'MRPDATA');
       
   462   
       
   463   chdir($cwd) or die "Error: Couldn't change working directory back to \"$cwd\": $!\n";
       
   464   if ($@) {
       
   465     die $@;
       
   466   }
       
   467 }
       
   468 
       
   469 
       
   470 sub HandleSourceFile {
       
   471   my $self = shift;
       
   472   my $srcFile = shift;
       
   473 
       
   474   my $logErrors = !$self->{iniData}->IgnoreSourceFilterErrors();
       
   475   my ($cat, $errors) = Utils::ClassifyPath($self->{iniData}, $srcFile, $self->{verbose}, $logErrors, $self->Component());
       
   476 
       
   477   if ($self->{verbose}) {
       
   478     print "Handling source file $srcFile...\n";
       
   479   }
       
   480   
       
   481   push @{$self->{sourceFilterErrors}}, @$errors if @$errors;
       
   482   push @{$self->{src}->{uc($cat)}}, $srcFile;
       
   483 }
       
   484 
       
   485 sub HandleSourceDir {
       
   486   my $self = shift;
       
   487   my $srcDir = Utils::PrependSourceRoot(shift);
       
   488 
       
   489   if ($self->{verbose}) {
       
   490     print "Filtering source directory $srcDir into categories...\n";
       
   491   }
       
   492  
       
   493   # Create function to handle files in a directory ($File::Find::dir)
       
   494   # Takes: List of items (files and dirs) in the directory 
       
   495   my $dirhandler = sub {
       
   496     my @entries = @_;
       
   497     my $hasdistpol = scalar(grep(lc($_) eq "distribution.policy", @entries));
       
   498     
       
   499     @entries = grep(lc($_) ne "distribution.policy", @entries); # remove distribution.policy entries
       
   500     
       
   501     foreach my $entry (@entries) {
       
   502       if (Utils::CheckForUnicodeCharacters($entry)) {
       
   503           die "Error: \"$File::Find::dir\\$entry\" contains unicode characters, which are incompatible with the CBR Tools. This file can not be included in this release.\n"; 
       
   504       }    
       
   505     }
       
   506     
       
   507     my @files = grep(-f File::Spec->catfile($File::Find::dir,$_), @entries);
       
   508     
       
   509     # Remove the abld entries from the source
       
   510     $self->RemoveAbldFromSource($File::Find::dir, \@files);
       
   511     
       
   512     if (scalar(@files) > 0) {    
       
   513       
       
   514       # Tag all the entries in this directory with that category
       
   515       foreach my $entry (@files) {
       
   516         next if $entry =~ /^\.\.?$/; # Skip . and ..
       
   517         my $entry = File::Spec->catfile($File::Find::dir, $entry);
       
   518         Utils::TidyFileName(\$entry);
       
   519         
       
   520         $entry = Utils::RemoveSourceRoot($entry); # remove source root path or it will be added twice!
       
   521         my ($category, $errors) = Utils::ClassifyPath($self->{iniData}, $entry, $self->{verbose}, $self->{iniData}->IgnoreSourceFilterErrors(), $self->Component());
       
   522         push @{$self->{sourceFilterErrors}}, @$errors; # There will be no errors in @$errors if IgnoreSourceFilterErrors was set
       
   523         
       
   524         # (Optionally) guard against unclassified source
       
   525         if (lc($category) eq "x" and $self->{iniData}->DisallowUnclassifiedSource()) {
       
   526           die "Error: \"$File::Find::dir\" contains unclassified source code\n";
       
   527         }
       
   528         
       
   529         push @{$self->{src}->{uc($category)}}, $entry;
       
   530       } 
       
   531     } else {
       
   532       # There are no files to categorise here
       
   533       if (($hasdistpol) and (!($self->{iniData}->IgnoreSourceFilterErrors()))) {
       
   534         push @{$self->{sourceFilterErrors}}, "Note: unnecessary policy file in $File::Find::dir\n";
       
   535       }
       
   536     }
       
   537     
       
   538     # Return full list of entries to continue scan
       
   539     return @entries;
       
   540   };
       
   541 
       
   542   # Traverse the directory tree in $srcDir calling &$dirhandler on all directories
       
   543   find({"wanted"=>sub{}, "preprocess"=>$dirhandler, "no_chdir" => 1}, $srcDir);
       
   544 }
       
   545 
       
   546 sub RemoveAbldFromSource {
       
   547   my $self = shift;
       
   548   my $dir = shift;
       
   549   my $files = shift;
       
   550   
       
   551   $dir = File::Spec->canonpath($dir);
       
   552 
       
   553   foreach my $entry (@{$self->{binaryStatements}}, @{$self->{exportsStatements}}) {
       
   554     if ($entry->{abldPath} eq $dir) {
       
   555       @$files = grep $_ !~ /abld.bat/i, @$files;
       
   556       return;
       
   557     }
       
   558   }  
       
   559 }
       
   560 
       
   561 sub HandleBinDirOrFile {
       
   562   my $self = shift;
       
   563   my $remove = shift;
       
   564   my $category = shift;
       
   565   my $file = shift;
       
   566   my $successRef = shift;
       
   567 
       
   568   if (-d $file) {
       
   569     $self->HandleBinDir($remove, $category, $file, $successRef);
       
   570   }
       
   571   elsif ($file) {
       
   572     $self->HandleBinFile($remove, $category, $file, $successRef);
       
   573   }
       
   574 }
       
   575 
       
   576 sub HandleBinFile {
       
   577   my $self = shift;
       
   578   my $remove = shift;
       
   579   my $category = shift;
       
   580   my $file = Utils::RemoveEpocRoot(shift);
       
   581   my $successRef = shift;
       
   582 
       
   583   my $lcFile = lc($file); # Note, duplicate check is performed on lower case file name. Original case is preserved within the hash.
       
   584   Utils::TidyFileName(\$file);
       
   585 
       
   586   die "No category was provided" unless $category;
       
   587 
       
   588   if ($remove) {
       
   589     foreach my $thisClassification (keys %{$self->{bins}}) {
       
   590       if (exists $self->{bins}->{$thisClassification}->{$lcFile}) {
       
   591         if ($self->{verbose} > 1) { print "Excluding binary file \"$file\" from $thisClassification...\n"; }
       
   592         delete $self->{bins}->{$thisClassification}->{$lcFile};
       
   593         $$successRef = 1;
       
   594       }
       
   595     }
       
   596   }
       
   597   else {
       
   598     unless ($self->IsDuplicateBin($file)) {
       
   599       if ($self->{verbose} > 1) { print "Adding binary file \"$file\" to category $category...\n"; }
       
   600       $self->{bins}->{$category}->{$lcFile} = $file;
       
   601       $$successRef = 1;
       
   602     }
       
   603   }
       
   604 
       
   605   }
       
   606 
       
   607 
       
   608 sub HandleBinDir {
       
   609   my $self = shift;
       
   610   my $remove = shift;
       
   611   my $category = shift;
       
   612   my $binDir = shift;
       
   613   my $successRef = shift;
       
   614 
       
   615   find($self->ProcessBinFile($remove, $category, $successRef), $binDir);
       
   616 }
       
   617 
       
   618 sub ProcessBinFile {
       
   619   my $self = shift;
       
   620   my $remove = shift;
       
   621   my $category = shift;
       
   622   my $successRef = shift;
       
   623   return sub {
       
   624     my $file = $File::Find::name;
       
   625     
       
   626     if (Utils::CheckForUnicodeCharacters($file)) {
       
   627       die "Error: \"$file\" contains unicode characters, which are incompatible with the CBR Tools. This file can not be included in this release.\n"; 
       
   628     }    
       
   629     
       
   630     if (-f $file) {
       
   631       Utils::TidyFileName(\$file);
       
   632       $self->HandleBinFile($remove, $category, $file, $successRef);
       
   633     }
       
   634   }
       
   635 }
       
   636 
       
   637 sub IsDuplicateBin {
       
   638   my $self = shift;
       
   639   my $fileName = shift;
       
   640   my $fileNameLc = lc ($fileName);
       
   641 
       
   642   my $duplicate = 0;
       
   643   foreach my $thisCategory (keys %{$self->{bins}}) {
       
   644     if (exists $self->{bins}->{$thisCategory}->{$fileNameLc}) {
       
   645       # This file has already been handled once, so it must be a duplicate.
       
   646       # Therefore move it to the 'unclassified' category to ensure it doesn't get released twice.
       
   647       if ($thisCategory ne 'unclassified') {
       
   648 	if ($self->{verbose} > 1) {
       
   649 	  print "Moving binary file \"$fileName\" to from category $thisCategory to 'unclassified'...\n";
       
   650 	}
       
   651 	$self->{bins}->{unclassified}->{$fileNameLc} = $fileName;
       
   652 	delete $self->{bins}->{$thisCategory}->{$fileNameLc};
       
   653       }
       
   654       $duplicate = 1;
       
   655       last;
       
   656     }
       
   657   }
       
   658 
       
   659   return $duplicate;
       
   660 }
       
   661 
       
   662 sub HandleBinSet {
       
   663   my $self = shift;
       
   664   my $remove = shift;
       
   665   my $test = shift;
       
   666   if ($test) {
       
   667     $test = 'test';
       
   668   }
       
   669   else {
       
   670     $test = '';
       
   671   }
       
   672   my $successRef = shift;
       
   673   my $abldPath = shift;
       
   674   $abldPath = SourceRootPath($abldPath);
       
   675   my $plat = shift;
       
   676   my $var = '';
       
   677   if ($_[0] and $_[0] =~ /(u?(?:deb|rel))/i) {
       
   678     $var = shift;
       
   679   }
       
   680   my $mmp = shift;
       
   681   unless ($mmp) {
       
   682     $mmp = '';
       
   683   }
       
   684   
       
   685   $self->ProcessCache($abldPath, $test) if (!exists($self->{abldcache}->{loaded}->{$abldPath}));
       
   686 
       
   687   my $plats = $self->ResolveAlias($abldPath, $plat);
       
   688   my $vars;
       
   689   foreach $plat (@$plats) {
       
   690     if ($var) {
       
   691       $vars = [$var];
       
   692     } elsif ($plat =~ /^tools2?$/i) {
       
   693       # Hard-coded and nasty
       
   694       $vars = [ 'deb', 'rel' ];
       
   695     } else {
       
   696       $vars = [ 'udeb', 'urel' ];
       
   697     }
       
   698     foreach $var (@$vars) {
       
   699       push @{$self->{binsets}}, {
       
   700         path => Utils::RemoveSourceRoot($abldPath),
       
   701         plat => $plat,
       
   702         var => $var,
       
   703         mmp => $mmp,
       
   704         test => $test
       
   705       } unless ($remove);
       
   706 
       
   707       $self->ReadBinaries($abldPath, $test, $plat, $var, $mmp, $remove, $successRef);
       
   708     }
       
   709   }
       
   710 }
       
   711 
       
   712 
       
   713 sub ProcessCache {
       
   714   my $self = shift;
       
   715   my $abldPath = shift;
       
   716   my $test = shift;
       
   717   
       
   718   $self->CheckBuildSystem($abldPath) if(!$self->{buildSystem});
       
   719 
       
   720   if($self->{buildSystem} == 2){
       
   721     print "current build system is Raptor...\n" if ($self->{verbose});
       
   722     $self->ProcessRaptorCache($abldPath, $test);
       
   723   }
       
   724   else{
       
   725     print "current build system is Abld...\n" if ($self->{verbose});
       
   726     $self->ProcessAbldCache($abldPath);
       
   727   }
       
   728 }
       
   729 
       
   730 #check which build system would be using
       
   731 sub CheckBuildSystem {
       
   732   my $self = shift;
       
   733   my $abldPath = shift;
       
   734   my $buildSystem = $self->{iniData}->BuildSystemVersion($self->{verbose});
       
   735 
       
   736   if($buildSystem eq "1") {
       
   737     if ($self->AbldAvailable($abldPath)){
       
   738       $self->{buildSystem} = 1;
       
   739     }
       
   740     else{
       
   741       die "Abld build system isn't available.\n";
       
   742     }
       
   743   }
       
   744   else{
       
   745     if($buildSystem ne "2") {
       
   746 	    print "Warning: the value of build system is neither 1 nor 2 so we try to use Raptor.\n" if ($self->{verbose});
       
   747     }
       
   748     
       
   749     if ($self->RaptorAvailable()){
       
   750       $self->{buildSystem} = 2;
       
   751     }
       
   752     elsif($buildSystem ne "2") {
       
   753       print "Warning: Raptor is not available and we try to use Abld.\n" if ($self->{verbose});
       
   754 	  	
       
   755       if ($self->AbldAvailable($abldPath)){
       
   756         $self->{buildSystem} = 1;
       
   757       }
       
   758       else{
       
   759         die "Neither Abld nor Raptor is available.\n";
       
   760       }
       
   761     }
       
   762     else{
       
   763       die "Raptor build system is not available.\n";
       
   764     }
       
   765   }
       
   766 }
       
   767 
       
   768 sub ProcessAbldCache {
       
   769   my $self = shift;
       
   770   my $abldPath = shift;
       
   771   if (exists $ENV{ABLDCACHE}) {
       
   772     $self->{abldcache}->{loaded}->{$abldPath}= 1;
       
   773     my $cachefile=File::Spec->catdir($ENV{ABLDCACHE},$abldPath,"cache");
       
   774     if (-f $cachefile) {
       
   775       print "Reading ABLD Cache from $cachefile\n" if ($self->{verbose});
       
   776 	
       
   777       open(CACHE, $cachefile) or die "Couldn't open abld cache data file '$cachefile'\n";
       
   778       my @cache = <CACHE>;
       
   779       close(CACHE);
       
   780       eval (join("",@cache)) or die "Error: Couldn't parse abld cache data in '$cachefile': $@\n";
       
   781     }
       
   782   }
       
   783 }
       
   784 
       
   785 sub ProcessRaptorCache {
       
   786   my $self = shift;
       
   787   my $abldPath = shift;
       
   788   my $test = shift;
       
   789 
       
   790   my $cwd = cwd();
       
   791   my $driver = $cwd;
       
   792   $driver =~ /^(.:)(.*)/;
       
   793   $driver = $1."\\raptorcache";
       
   794   my $logfile = File::Spec->catdir($driver.$abldPath,"info.xml");
       
   795   if(! -f $logfile){
       
   796     my $makefile = File::Spec->catdir($driver.$abldPath,"Makefile");
       
   797     print "execute SBS to create Raptor XML log file: $logfile\n" if($self->{verbose});
       
   798     chdir $abldPath or die "Error: Couldn't change working directory to \"$abldPath\": $!\n";
       
   799     my $cmd = $self->RaptorLogCmd($abldPath, $logfile, $makefile, $test);
       
   800     open (SBS, $cmd) or die "Error: Couldn't run \"$cmd\" in \"$abldPath\": $!\n";
       
   801     my $foundLog;
       
   802     my $errmsg;
       
   803     while (my $line = <SBS>) {
       
   804       $errmsg = $1 if ($line =~ /sbs : errors: (\d+)/ and $1 > 0);
       
   805       $foundLog = 1 if ($line =~ /sbs: build log in (\w+)/);
       
   806     }
       
   807     close (SBS);
       
   808 			  
       
   809     if($errmsg){
       
   810       my $trycount = 50;
       
   811       my $errtag = 0;
       
   812       while($trycount > 0){
       
   813         print "try to run sbs again: $trycount\n";
       
   814         open (SBS, $cmd) or die "Error: Couldn't run \"$cmd\" in \"$abldPath\": $!\n";
       
   815         $errtag = 0;
       
   816         while (my $line = <SBS>) {
       
   817           if ($line =~ /sbs : errors: (\d+)/ and $1 > 0){
       
   818             $errtag = 1;
       
   819             $trycount = $trycount - 1;
       
   820           }
       
   821           $foundLog = 1 if ($line =~ /sbs: build log in (\w+)/);
       
   822         }
       
   823         $trycount =0 if($errtag < 1);
       
   824         close (SBS);
       
   825       }
       
   826       if($errtag == 1 and $trycount == 0) {
       
   827       	die "SBS Error: Couldn't run \"$cmd\" in \"$abldPath\"\n";
       
   828       }
       
   829     }
       
   830     chdir $cwd or die "Error: Couldn't change working directory to \"$cwd\": $!\n";
       
   831     unless ($foundLog) {
       
   832       die "Error: Unable to execute \"SBS\" in \"$abldPath\"\n";
       
   833     }
       
   834   }
       
   835 
       
   836   $self->ParseRaptorXmlLog($logfile);
       
   837   $self->PrintCache() if($self->{verbose});
       
   838   $self->{abldcache}->{loaded}->{$abldPath}= 1;
       
   839   print "cache is generated successfully\n" if($self->{verbose});
       
   840 }
       
   841 
       
   842 sub RaptorLogCmd {
       
   843   my $self = shift;
       
   844   my $abldPath = shift;
       
   845   my $logfile = shift;
       
   846   my $makefile = shift;
       
   847   my $test = shift;
       
   848   if ($test) {
       
   849     $test = 'test';
       
   850   }
       
   851   else {
       
   852     $test = '';
       
   853   }
       
   854 
       
   855   my $plat = "all";
       
   856   my $iniAll = $self->{iniData}->TargetPlatforms($plat);
       
   857   my $cmd = "SBS -b bld.inf -m $makefile -f $logfile -c default";
       
   858   $cmd = $cmd." -c default.test" if ($test ne '');
       
   859   foreach my $e (@$iniAll) {
       
   860     $cmd = $cmd." -c tools_rel -c tools_deb" if ($e eq "TOOLS");
       
   861     $cmd = $cmd." -c tools2_rel -c tools2_deb" if ($e eq "TOOLS2");
       
   862     $cmd = $cmd." -c armv5.smp" if ($e eq "ARMV5SMP");
       
   863   }
       
   864   $cmd = $cmd." WHAT |";
       
   865   print "Raptor command: $cmd\n";
       
   866   return $cmd;
       
   867 }
       
   868 
       
   869 #check whether Abld build system is available
       
   870 sub AbldAvailable {
       
   871   my $self = shift;
       
   872   my $abldPath = shift;
       
   873   my $path = File::Spec->catdir($abldPath,"");
       
   874   my $foundPlats = 0;
       
   875 
       
   876   my $cwd = cwd();
       
   877   chdir $abldPath or die "Error: Couldn't change working directory to \"$abldPath\": $!\n";
       
   878   open (BLDMAKE, "bldmake bldfiles |") or die "Error: Couldn't run \"bldmake bldfiles\" in \"$abldPath\": $!\n";
       
   879   while (my $line = <BLDMAKE>) {
       
   880     chomp $line;
       
   881   }
       
   882   close(BLDMAKE);
       
   883 	
       
   884   open (ABLD, "abld help |") or die "Error: Couldn't run \"abld help\" in \"$abldPath\": $!\n";
       
   885   while (my $line = <ABLD>) {
       
   886     chomp $line;
       
   887     $foundPlats = 1 if ($line =~ /project platforms:/);
       
   888   }
       
   889   close (ABLD);
       
   890   chdir $cwd or die "Error: Couldn't change working directory to \"$cwd\": $!\n";
       
   891   
       
   892   return $foundPlats;
       
   893 }
       
   894 
       
   895 #check whether Raptor build system is available
       
   896 sub RaptorAvailable {
       
   897   my $self = shift;
       
   898   my $maxver = 0;
       
   899   my $midver = 0;
       
   900   my $minver = 0;
       
   901   
       
   902   return 0 if(!-f "\\epoc32\\data\\buildinfo.txt" and !-f "\\epoc32\\data\\kif.xml");
       
   903 
       
   904   open (SBS, "sbs -version |") or die "Error: Couldn't run \"sbs -version\": $!\n";
       
   905   while (my $line = <SBS>) {
       
   906     chomp $line;
       
   907     if ($line =~ /^sbs version (\d+)\.(\d+)\.(\d+)/){
       
   908       $maxver = $1;
       
   909       $midver = $2;
       
   910       $minver = $3;
       
   911     }
       
   912   }
       
   913   close (SBS);
       
   914   if ($maxver == 0 and $midver == 0 and $minver == 0) {
       
   915     return 0;
       
   916   }
       
   917   elsif ($maxver < 2 or ($maxver == 2 and $midver < 7)) {
       
   918     die "Error: Raptor build system version must be 2.7.0 or higher.\n";
       
   919   }
       
   920   return 1;
       
   921 }
       
   922 
       
   923 sub ParseRaptorXmlLog {
       
   924   my $self = shift;
       
   925   my $xmlfile = shift;
       
   926 
       
   927   my $xmldata;
       
   928   my $trycount = 20;
       
   929 
       
   930   while ($trycount > 0) {
       
   931     eval {
       
   932       $xmldata = XMLin($xmlfile);
       
   933     };
       
   934     if ($@) {
       
   935       $trycount = $trycount - 1;
       
   936       print "Try to open raptor log file [$trycount]: $xmlfile\n";
       
   937     }
       
   938     else{
       
   939       $trycount = 0;
       
   940     }
       
   941   }
       
   942 
       
   943   my $whatLogElements = $self->WrapVarToArray($xmldata->{whatlog});
       
   944   foreach  my $whatLogElement (@$whatLogElements) {
       
   945     $self->ProcessWhatLogElement($whatLogElement);
       
   946   }
       
   947   
       
   948   foreach my $param (keys %{$self->{abldcache}->{exports}}) {
       
   949     foreach my $destination (keys %{$self->{abldcache}->{exports}->{$param}}) {
       
   950       push @{$self->{abldcache}->{$param}}, [$destination, $self->{abldcache}->{exports}->{$param}->{$destination}];
       
   951     }
       
   952   }
       
   953   delete $self->{abldcache}->{exports};
       
   954   
       
   955   foreach my $param (keys %{$self->{abldcache}->{builds}}) {
       
   956     foreach my $buildItem (keys %{$self->{abldcache}->{builds}->{$param}}) {
       
   957       push @{$self->{abldcache}->{$param}}, $buildItem;
       
   958     }
       
   959   }
       
   960   delete $self->{abldcache}->{builds};
       
   961   
       
   962   foreach my $platform (keys %{$self->{abldcache}->{platforms}}) {
       
   963     push @{$self->{abldcache}->{plats}}, uc($platform);
       
   964   }
       
   965   delete $self->{abldcache}->{platforms};
       
   966 }
       
   967 
       
   968 sub ProcessWhatLogElement {
       
   969   my $self = shift;
       
   970   my $aWhatLogElement = shift;
       
   971   
       
   972   my $bldinf = $aWhatLogElement->{bldinf};
       
   973   my $bldinfDir = $bldinf;
       
   974   $bldinfDir =~ s/\//\\/g;
       
   975   $bldinfDir =~ /^.:(.+)\\(.*)/;
       
   976   $bldinfDir = $1;
       
   977   
       
   978   my $mmp = $aWhatLogElement->{mmp};
       
   979   my $config = $aWhatLogElement->{config};
       
   980   
       
   981   my $platform = "";
       
   982   my $variant = "";
       
   983   my $test;
       
   984   
       
   985   if ($config =~ /^(\w+)_(\w+)\.test/){
       
   986     $platform = $1;
       
   987     $variant = $2;
       
   988     $test = "test";
       
   989   }
       
   990   elsif ($config =~ /^(\w+)_(\w+)*/){
       
   991     $platform = $1;
       
   992     $variant = $2;
       
   993   }
       
   994 
       
   995   if($aWhatLogElement->{export}){
       
   996     my $exports = $self->WrapVarToArray($aWhatLogElement->{export});
       
   997     foreach  my $export (@$exports) {
       
   998       $self->StoreExportItem ($bldinfDir, $export->{source}, $export->{destination}, $test);
       
   999     }
       
  1000   }
       
  1001   if($aWhatLogElement->{archive}){
       
  1002     my $archives = $self->WrapVarToArray($aWhatLogElement->{archive});
       
  1003     foreach my $archive (@$archives){
       
  1004       foreach  my $member (@{$archive->{member}}) {
       
  1005         $self->StoreExportItem ($bldinfDir, $archive->{zipfile}, $member, $test);
       
  1006       }
       
  1007     }
       
  1008   }
       
  1009   if($aWhatLogElement->{build}){
       
  1010     my $buildItems = $self->WrapVarToArray($aWhatLogElement->{build});
       
  1011     foreach  my $buildItem (@$buildItems) {
       
  1012       $self->StoreBuildItem ($bldinfDir, $buildItem, $platform, $variant, $test);
       
  1013     }
       
  1014   }
       
  1015   if($aWhatLogElement->{resource}){
       
  1016     my $resources = $self->WrapVarToArray($aWhatLogElement->{resource});
       
  1017     foreach  my $buildItem (@$resources) {
       
  1018       if($buildItem =~ /[\\|\/]epoc32[\\|\/]release[\\|\/]winscw[\\|\/](urel|udeb)[\\|\/]/g){
       
  1019         $variant = $1;
       
  1020       }
       
  1021       else{
       
  1022         $variant = "ALL"; 
       
  1023       }
       
  1024       $self->StoreBuildItem ($bldinfDir, $buildItem, $platform, $variant, $test);
       
  1025     }
       
  1026   }
       
  1027   if($aWhatLogElement->{bitmap}){
       
  1028     my $bitmaps = $self->WrapVarToArray($aWhatLogElement->{bitmap});
       
  1029     foreach  my $buildItem (@$bitmaps) {
       
  1030       $self->StoreBuildItem ($bldinfDir, $buildItem, $platform, "ALL", $test);
       
  1031     }
       
  1032   }
       
  1033   if($aWhatLogElement->{stringtable}){
       
  1034     my $stringTables = $self->WrapVarToArray($aWhatLogElement->{stringtable});
       
  1035     foreach  my $buildItem (@$stringTables) {
       
  1036       $self->StoreBuildItem ($bldinfDir, $buildItem, $platform, $variant, $test);
       
  1037     }
       
  1038   }
       
  1039   
       
  1040   $self->{abldcache}->{platforms}->{$platform} = 1 if($platform ne "ALL");
       
  1041 
       
  1042   my $param = "$bldinfDir ";
       
  1043   $param = $param."test " if ($test);
       
  1044   $param = $param."export -what";
       
  1045   if(!$self->{abldcache}->{$param}){
       
  1046     pop @{$self->{abldcache}->{$param}};
       
  1047   }
       
  1048 }
       
  1049 
       
  1050 sub StoreExportItem {
       
  1051   my $self = shift;
       
  1052   my $bldinfDir = shift;
       
  1053   my $aSource = shift;
       
  1054   my $aDestination =shift;
       
  1055   my $test = shift;
       
  1056   $aSource = $self->ReleasableItem($aSource);
       
  1057   $aDestination = $self->ReleasableItem($aDestination);
       
  1058   my $param = "$bldinfDir ";
       
  1059   $param = $param."test " if ($test);
       
  1060   $param = $param."export -what";
       
  1061   $self->{abldcache}->{exports}->{$param}->{$aDestination} = $aSource;
       
  1062 }
       
  1063 
       
  1064 sub StoreBuildItem {
       
  1065   my $self = shift;
       
  1066   my $bldinfDir = shift;
       
  1067   my $aBuildItem = shift;
       
  1068   my $aPlatform = shift;
       
  1069   my $aVariant = shift;
       
  1070   my $test = shift;
       
  1071 	
       
  1072   if($aPlatform ne "ALL" and $aVariant eq "ALL"){
       
  1073     $self->StoreBuildItem($bldinfDir, $aBuildItem, $aPlatform, "urel", $test);
       
  1074     $self->StoreBuildItem($bldinfDir, $aBuildItem, $aPlatform, "udeb", $test);
       
  1075   }
       
  1076   else{
       
  1077     $aBuildItem = $self->ReleasableItem($aBuildItem);
       
  1078     my $param = "$bldinfDir ";
       
  1079     $param = $param."test " if ($test);
       
  1080     $param = $param."target $aPlatform $aVariant -what";
       
  1081     $self->{abldcache}->{builds}->{$param}->{$aBuildItem} = 1;
       
  1082     $self->{abldcache}->{platforms}->{$aPlatform} = 1 if($aPlatform ne "ALL");
       
  1083   }
       
  1084 }
       
  1085 
       
  1086 sub ReleasableItem {
       
  1087   my $self = shift;
       
  1088   my $aBuildItem = shift;
       
  1089   $aBuildItem =~ s/\/\//\\/g;
       
  1090   $aBuildItem =~ s/\//\\/g;
       
  1091   $aBuildItem =~ s/\"//g;
       
  1092   $aBuildItem =~ /^.:(.+)/;
       
  1093   return $1;
       
  1094 }
       
  1095 
       
  1096 
       
  1097 sub WrapVarToArray {
       
  1098   my $self = shift;
       
  1099   my $var = shift;
       
  1100   my @result;
       
  1101   
       
  1102   if($var){
       
  1103     if($var =~/^ARRAY*/){
       
  1104       return $var;	
       
  1105     }
       
  1106     else{
       
  1107       push (@result, $var);
       
  1108     }
       
  1109   }
       
  1110   return \@result;
       
  1111 }
       
  1112 
       
  1113 sub PrintCache {
       
  1114   my $self = shift;
       
  1115   print "print cache content\n" if($self->{verbose});
       
  1116   foreach my $item (keys %{$self->{abldcache}}) {
       
  1117     if($item ne "loaded"){
       
  1118       print "\$self->{abldcache}->{\'$item\'} =\n";
       
  1119       print " [\n";
       
  1120       my $first = 1;
       
  1121       
       
  1122       foreach my $cachedata (@{$self->{abldcache}->{$item}}) {
       
  1123       	print ",\n" if($first > 1);
       
  1124       	$first = $first+1;
       
  1125         if($cachedata=~/^ARRAY*/){
       
  1126     	    print " [\'@$cachedata[0]\', \'@$cachedata[1]\']";
       
  1127         }
       
  1128         else{
       
  1129     	    print " \'$cachedata\'";
       
  1130         } 
       
  1131       }
       
  1132       print "\n ];\n\n";
       
  1133     }
       
  1134   }
       
  1135 }
       
  1136 
       
  1137 # Support for target alias file
       
  1138 # If the MRP specifies 'ALL' then the intersection of the
       
  1139 # definition of 'ALL' and the output of abld help is used
       
  1140 # as the platform list
       
  1141 sub ResolveAlias {
       
  1142   my $self = shift;
       
  1143   my $abldPath = shift;
       
  1144   my $plat = shift;
       
  1145   my @plats = ();
       
  1146 
       
  1147   if (lc $plat eq 'all' || $self->{iniData}->HasTargetPlatforms($plat)) {
       
  1148     if ($self->{iniData}->HasTargetPlatforms($plat)) {
       
  1149       if (lc $plat eq 'all') {
       
  1150         # ALL and HasTargetPlatforms()
       
  1151         # Do the set intersection with the output of abld help
       
  1152         my $iniAll = $self->{iniData}->TargetPlatforms($plat);
       
  1153         my $abldHelp = $self->GetPlatforms($abldPath);
       
  1154         my %count;
       
  1155         foreach my $e (@$iniAll) {
       
  1156           $count{$e} = 1;
       
  1157         }
       
  1158         foreach my $e (@$abldHelp) {
       
  1159           if (exists $count{$e} and $count{$e} == 1) {
       
  1160             push @plats, $e;
       
  1161           }
       
  1162         }
       
  1163         $self->RemoveIDEPlatforms(\@plats);
       
  1164         if ($self->{verbose} > 1) {
       
  1165           print "Intersection of \"ALL\" alias and abld help is \"@plats\"\n";
       
  1166         }
       
  1167       } else {
       
  1168         # NOT ALL and HasTargetPlatforms()
       
  1169         # Use the list of platforms from the iniData and this alias
       
  1170         @plats = @{$self->{iniData}->TargetPlatforms($plat)};
       
  1171         if ($self->{verbose} > 1) {
       
  1172           print "Resolution of \"$plat\" alias is \"@plats\"\n";
       
  1173         }
       
  1174       }
       
  1175     } else {
       
  1176       # ALL and NOT HasTargetPlatforms() so just use
       
  1177       # the results of abld help
       
  1178       @plats = @{$self->GetPlatforms($abldPath)};
       
  1179       $self->RemoveIDEPlatforms(\@plats);
       
  1180       if ($self->{verbose} > 1) {
       
  1181         print "Resolution of \"ALL\" alias from abld help is \"@plats\"\n";
       
  1182       }
       
  1183     }
       
  1184   } else {
       
  1185     # NOT ALL and NOT HasTargetPlatforms() so use this as the platform
       
  1186     @plats = $plat;
       
  1187     if ($self->{verbose} > 1) {
       
  1188       print "Platform specified is \"@plats\"\n";
       
  1189     }
       
  1190   }
       
  1191   return \@plats;
       
  1192 }
       
  1193 
       
  1194 sub RemoveIDEPlatforms {
       
  1195   my $self = shift;
       
  1196   my $plats = shift;
       
  1197 
       
  1198   # Ugly hard-coded yukkiness
       
  1199   @$plats = grep { !m/^cw_ide$/i && !m/^vc\d/i } @$plats;
       
  1200 }
       
  1201 
       
  1202 sub GetPlatforms {
       
  1203   my $self = shift;
       
  1204   my $bldInfDir = shift;
       
  1205 
       
  1206   if (exists $self->{abldcache}->{"plats"}) {
       
  1207     return $self->{abldcache}->{"plats"};
       
  1208   }
       
  1209   $self->CallBldMakeIfNecessary($bldInfDir);
       
  1210 
       
  1211  TRYAGAIN:
       
  1212   my $foundPlats = 0;
       
  1213   my @plats;
       
  1214   my @errorLines;
       
  1215 
       
  1216   my @abldOutput = `($bldInfDir\\abld help | perl -pe "s/^/stdout: /") 2>&1`; # Adds 'stdout: ' to the beginning of each STDOUT line, nothing is added to output on STDERR.
       
  1217 
       
  1218   foreach my $line (@abldOutput) {
       
  1219     chomp $line;
       
  1220       
       
  1221     if ($line =~ s/^stdout: //) { # Output from STDOUT
       
  1222       if ($foundPlats) {
       
  1223         if ($self->{verbose}) { print "Found platforms: $line\n"; }
       
  1224         $line =~ s/^\s*//; # Strip off leading whitespace.
       
  1225         # Force platforms to upper case to match IniData::TargetPlatforms()
       
  1226         $line = uc $line;
       
  1227         @plats = split /\s+/, $line;
       
  1228         last;
       
  1229       }
       
  1230       if ($line =~ /project platforms:/) {
       
  1231         $foundPlats = 1;
       
  1232       }
       
  1233     }
       
  1234  
       
  1235     else { # Output from STDERR
       
  1236       if ($line =~ m/project bldmake directory.*does not exist/i) {
       
  1237         $self->CallBldMake($bldInfDir);
       
  1238         goto TRYAGAIN;
       
  1239       }  
       
  1240       elsif ($line =~ /Can't find ABLD.PL on PATH/i) {
       
  1241         push @errorLines, "Error: Couldn't run $bldInfDir\\abld: $line\n";      
       
  1242       }
       
  1243       else {
       
  1244         push @errorLines, "$line\n";
       
  1245       }
       
  1246     }
       
  1247   }
       
  1248 
       
  1249   if (scalar @errorLines > 0) {
       
  1250     die @errorLines;
       
  1251   }
       
  1252 
       
  1253   die "Error: didn't find any platforms\n" unless $foundPlats;
       
  1254 
       
  1255   $self->{abldcache}->{"plats"} = \@plats;
       
  1256 
       
  1257   return \@plats;
       
  1258 }
       
  1259 
       
  1260 sub ReadBinaries {
       
  1261   my $self = shift;
       
  1262   my $abldPath = shift;
       
  1263   my $test = lc(shift);
       
  1264   my $plat = lc(shift);
       
  1265   my $var = lc(shift);
       
  1266   my $mmp = shift;
       
  1267   my $remove = shift;
       
  1268   my $successRef = shift;
       
  1269   my $command = "target";
       
  1270   my $opts = "-what";
       
  1271   $command = "$test $command" if $test;
       
  1272   $opts = "$mmp $opts" if $mmp;
       
  1273   if ($self->{verbose}) { print "Extracting target info from \"$abldPath\\abld.bat\" using \"$command $plat $var\"...\n";  }
       
  1274 
       
  1275   my $bins = $self->GatherAbldOutput($abldPath, $plat, $command, $var, $test, $opts);
       
  1276   my $category = 'unclassified';
       
  1277   if ($self->{iniData}->CategoriseBinaries() and not $plat =~ /^tools2?$/i) {
       
  1278     $category = $plat . '_' . $var;
       
  1279     if ($test) {
       
  1280       $category = $test . '_' . $category;
       
  1281     }
       
  1282   }
       
  1283 
       
  1284   $self->AddBins($remove, $category, $bins, $successRef);
       
  1285 }
       
  1286 
       
  1287 sub HandleExports {
       
  1288   my $self = shift;
       
  1289   my $abldPath = shift;
       
  1290   my $test = shift;
       
  1291 
       
  1292   $test = $test?"test ":"";
       
  1293 
       
  1294   if ($self->{verbose}) {
       
  1295     print "Extracting ${test}export info from $abldPath\\abld.bat...\n";
       
  1296   }
       
  1297 
       
  1298   my $exports = $self->GatherAbldOutput($abldPath, "", "${test}export", "", $test, "-what");
       
  1299   if ($self->{iniData}->CategoriseExports()) {
       
  1300     foreach my $thisExport (@$exports) {
       
  1301       if ($self->{verbose} > 1) { print "Found export \"$thisExport\"...\n"; }
       
  1302       if (Utils::WithinEpocRoot($thisExport)) {
       
  1303 	$thisExport = Utils::RemoveEpocRoot($thisExport);
       
  1304       }
       
  1305       else {
       
  1306 	print "Warning: Exported file \"$thisExport\" is not within EPOCROOT\n";
       
  1307       }
       
  1308 
       
  1309       # Note, the hash key is case lowered to ensure duplicates are rejected.
       
  1310       # The original case is preserved in the hash values.
       
  1311       my $thisExportLc = lc($thisExport);
       
  1312       Utils::TidyFileName(\$thisExportLc);
       
  1313 
       
  1314       # Note, the exports are not yet classified because this is done using the source code classifications.
       
  1315       # At this point we don't know if we've handled all the 'source' mrp keywords yet. Classification will
       
  1316       # occur when the exports are asked for.
       
  1317       $self->{exports}->{automatic}->{$thisExportLc} = $thisExport;
       
  1318     }
       
  1319     Utils::AbsolutePath(\$abldPath);
       
  1320     push (@{$self->{exports}->{abldPaths}}, Utils::RemoveSourceRoot($abldPath));
       
  1321   }
       
  1322   else {
       
  1323     # Handle exports treating them as binary files. Note, for a short while this code was changed to handle
       
  1324     # exported directories (not just files). This functionality has been removed because bldmake doesn't
       
  1325     # appear to cope with exported directories (it concatenates all the files in the specified directory into
       
  1326     # a single file due to weird use of the 'copy' command).
       
  1327     foreach my $thisExport (@$exports) {
       
  1328       $self->HandleBinFile(0, 'unclassified', $thisExport); # 0 = don't remove.
       
  1329     }
       
  1330   }
       
  1331 }
       
  1332 
       
  1333 sub HandleExportFile {
       
  1334   my $self = shift;
       
  1335   my $source = shift;
       
  1336   my $destination = shift;
       
  1337   my $remove = shift;
       
  1338 
       
  1339   if ($self->{iniData}->CategoriseExports()) {
       
  1340     if ($remove) {
       
  1341       my $destinationLc = lc(Utils::RemoveEpocRoot($destination));
       
  1342       Utils::TidyFileName(\$destinationLc);
       
  1343       if (exists $self->{exports}->{automatic}->{$destinationLc}) {
       
  1344 	print "Excluding export \"$destination\"...\n" if ($self->{verbose});
       
  1345 	delete $self->{exports}->{automatic}->{$destinationLc};
       
  1346       } else {
       
  1347         my $comp = $self->{comp} || "component name unknown";
       
  1348         print "Warning: ($comp) -export_file: could not remove $destination, as it hadn't been added. Perhaps the lines in your MRP are in the wrong order, or you meant -binary?\n";
       
  1349       }
       
  1350     }
       
  1351     else {
       
  1352       Utils::CheckExists($source);
       
  1353       Utils::CheckIsFile($source);
       
  1354       Utils::CheckExists($destination);
       
  1355       Utils::CheckIsFile($destination);
       
  1356       $self->{exports}->{manual}->{Utils::RemoveSourceRoot($source)} = Utils::RemoveEpocRoot($destination);
       
  1357     }
       
  1358   }
       
  1359   else {
       
  1360     $self->HandleBinFile($remove, 'unclassified', $destination);
       
  1361   }
       
  1362 }
       
  1363 
       
  1364 sub AddBins {
       
  1365   my $self = shift;
       
  1366   my $remove = shift;
       
  1367   my $category = shift;
       
  1368   my $bins = shift;
       
  1369   my $successRef = shift;
       
  1370 
       
  1371   foreach my $file (@$bins) {
       
  1372     $self->HandleBinDirOrFile($remove, $category, $file, $successRef);
       
  1373   }
       
  1374 }
       
  1375 
       
  1376 sub EnsureDoesNotExist {
       
  1377   my $self = shift;
       
  1378 
       
  1379   my $relDir = $self->{iniData}->PathData->LocalArchivePathForExistingOrNewComponent($self->{comp}, $self->{ver});
       
  1380   if (-e $relDir) {
       
  1381     die "Error: $self->{comp} $self->{ver} already exists\n";
       
  1382   }
       
  1383 }
       
  1384 
       
  1385 sub Validate {
       
  1386   my $self = shift;
       
  1387   my $warnNotError = shift; # produce warnings instead of errors for some messages
       
  1388 
       
  1389   return if $self->{validated};
       
  1390   $self->{validated} = 1;
       
  1391 
       
  1392   $self->EnsureDoesNotExist;
       
  1393 
       
  1394   unless (defined $self->{comp}) {
       
  1395     die "Error: No 'component' keyword specified in $self->{mrpName}\n";
       
  1396   }
       
  1397 
       
  1398   $self->NotesSource(); # will die if we can't find a notes_source tag
       
  1399 
       
  1400   my @errors;
       
  1401   my @warnings;
       
  1402   
       
  1403   foreach my $bin (@{$self->Binaries()}) {    
       
  1404     my $file = Utils::PrependEpocRoot(lc($bin));
       
  1405     
       
  1406     if (my $result = $self->CheckPathLength($file)) {
       
  1407       if ($warnNotError) {
       
  1408         push (@warnings, "Warning: $result\n");
       
  1409       } else { 
       
  1410         push (@errors, "Error: $result\n");
       
  1411       }
       
  1412     }
       
  1413     
       
  1414     if ($self->{fixMissingLibs}) {
       
  1415       unless (-e $file) {
       
  1416         if ($file =~ /$ENV{EPOCROOT}epoc32\\release\\armi\\(\S+)\\(\S+\.lib)/) {
       
  1417           my $fileToCopy = "$ENV{EPOCROOT}epoc32\\release\\thumb\\$1\\$2";
       
  1418           print "Copying $fileToCopy to $file...\n";
       
  1419           copy ($fileToCopy, $file) or push (@errors, "Error: Problem copying \"$fileToCopy\" to \"$file\": $!\n");
       
  1420         }
       
  1421         else {
       
  1422           push (@errors, "Error: \"$file\" does not exist\n");
       
  1423         }
       
  1424       }
       
  1425     }
       
  1426     else {
       
  1427       unless (-e $file) {
       
  1428         push (@errors, "Error: \"$file\" does not exist\n");
       
  1429       }
       
  1430     }
       
  1431   }
       
  1432 
       
  1433   foreach my $thisCategory (@{$self->ExportCategories()}) {
       
  1434     foreach my $thisExport (@{$self->Exports($thisCategory)}) {
       
  1435       $thisExport = Utils::PrependEpocRoot($thisExport); 
       
  1436       
       
  1437       if (my $result = $self->CheckPathLength($thisExport)) {
       
  1438         if ($warnNotError) {
       
  1439           push (@warnings, "Warning:  $result\n");
       
  1440         } else { 
       
  1441           push (@errors, "Error:  $result\n");
       
  1442         }
       
  1443       }
       
  1444 
       
  1445       unless (-e $thisExport) {
       
  1446         push (@errors, "Error: \"$thisExport\" does not exist\n");
       
  1447       }
       
  1448     }
       
  1449   }
       
  1450  
       
  1451   foreach my $thisSourceCategory (@{$self->SourceCategories()}) {
       
  1452     foreach my $thisSourceFile (@{$self->Source($thisSourceCategory)}) {
       
  1453       if (my $result = $self->CheckPathLength($thisSourceFile)) {
       
  1454         if ($warnNotError) {
       
  1455           push (@warnings, "Warning:  $result\n");
       
  1456         } else { 
       
  1457           push (@errors, "Error:  $result\n");
       
  1458         }
       
  1459       }
       
  1460     }
       
  1461   }
       
  1462   
       
  1463   if (@warnings) {
       
  1464     print @warnings;
       
  1465   }
       
  1466   
       
  1467   if (@errors and $#errors != -1) {
       
  1468     if ($#errors == 0) {
       
  1469       die $errors[0];
       
  1470     }
       
  1471     else {
       
  1472       print @errors;
       
  1473       my $firstError = $errors[0];
       
  1474       chomp $firstError;
       
  1475       die "Multiple errors (first - $firstError)\n";
       
  1476     }
       
  1477   }
       
  1478 }
       
  1479 
       
  1480 sub CallBldMakeIfNecessary {
       
  1481   my $self = shift;
       
  1482   my $abldPath = shift;
       
  1483   if (-e "$abldPath\\abld.bat") {
       
  1484     # Check to see if bld.inf has been modifed since bldmake was last run.
       
  1485     my $abldMTime = Utils::FileModifiedTime("$abldPath\\abld.bat");
       
  1486     my $bldInfMTime = Utils::FileModifiedTime("$abldPath\\bld.inf");
       
  1487     if ($bldInfMTime > $abldMTime) {
       
  1488       $self->CallBldMake($abldPath);
       
  1489     }
       
  1490   }
       
  1491   else {
       
  1492     $self->CallBldMake($abldPath);
       
  1493   }
       
  1494 }
       
  1495 
       
  1496 sub GatherAbldOutput {
       
  1497   my $self = shift;
       
  1498   my $abldPath = shift;
       
  1499   my $plat = shift;
       
  1500   my $abldCmnd = shift;
       
  1501   my $var = shift;
       
  1502   my $test = shift;
       
  1503   my $opts = shift;
       
  1504   my @output;
       
  1505 
       
  1506   my $abldParms = $abldCmnd;
       
  1507   $abldParms .= " $plat" if $plat;
       
  1508   $abldParms .= " $var" if $var;
       
  1509   $abldParms .= " $opts" if $opts;
       
  1510 
       
  1511   $abldPath=~s/\//\\/s; # Normalise all slashes to backslashes
       
  1512 
       
  1513   $self->ProcessCache($abldPath, $test) if (!exists($self->{abldcache}->{loaded}->{$abldPath}));
       
  1514   
       
  1515   if ($self->{abldcache}->{$abldPath." ".$abldParms}) {
       
  1516     # Why do we bother with a cache?
       
  1517     # Because you might see this in an MRP:
       
  1518     #   binary \blah all
       
  1519     #   -binary \blah mfrumpy
       
  1520     # The "all" will be expanded to multiple calls to GatherAbldOutput, if we've got CategoriseBinaries on
       
  1521     
       
  1522     # The codes below are added to make MakeCBR follow cachefiles created by Raptor
       
  1523     if($abldCmnd eq "export" and $opts eq "-what"){
       
  1524         my $exports = $self->{abldcache}->{$abldPath." ".$abldParms};
       
  1525         if(@$exports[0]){
       
  1526           my $firstExportFile = @$exports[0];
       
  1527           if($firstExportFile=~/^ARRAY*/){
       
  1528             foreach my $thisExport (@$exports) {
       
  1529                 push (@output, @$thisExport[0]);
       
  1530                 push @{$self->{exportsToBeProcessed}}, {source => @$thisExport[1],
       
  1531                                                         destination => @$thisExport[0],
       
  1532                                                         abldPath => Utils::PrependSourceRoot($abldPath)};
       
  1533             }
       
  1534             $self->{raptorcache} = 1;
       
  1535             return \@output;
       
  1536           }
       
  1537         }
       
  1538     }
       
  1539     
       
  1540     return $self->{abldcache}->{$abldPath." ".$abldParms};
       
  1541   }
       
  1542 
       
  1543   # Remove repeat guards - these stop CallBldMake and CallMakMake from being called
       
  1544   #                        forever if a fatal error occurs with a build script.
       
  1545   delete $self->{bldMakeCalled};
       
  1546   delete $self->{"makMakeCalled_$plat"};
       
  1547 
       
  1548   $self->CallBldMakeIfNecessary($abldPath);
       
  1549 
       
  1550  TRYAGAIN:
       
  1551 
       
  1552   my @errorLines; # Used to store the error
       
  1553 
       
  1554   my $cmd = "$abldPath\\abld $abldParms";
       
  1555   print "Executing command: $cmd\n" if $self->{verbose} > 1;
       
  1556 
       
  1557   my @abldOutput = `($cmd | perl -pe "s/^/stdout: /") 2>&1`;
       
  1558 
       
  1559   foreach my $line (@abldOutput) {
       
  1560     chomp $line;
       
  1561 
       
  1562     if ($line =~ s/^stdout: //) { # Output from STDOUT 
       
  1563       if ($self->{verbose} > 1) { print "ABLD: $line\n"; }    
       
  1564       
       
  1565       if ($line =~ /(^(make|make\[\d+\]): .*)/) {
       
  1566         print "Warning: $1\n";
       
  1567       }
       
  1568       elsif ($line =~ /given more than once in the same rule/) {
       
  1569         print "$line\n";      
       
  1570       }
       
  1571       elsif ($line =~ m/\.\./) {
       
  1572         my $oldpath = cwd();
       
  1573         eval {
       
  1574           chdir($abldPath);
       
  1575           Utils::AbsoluteFileName(\$line);
       
  1576         };
       
  1577         chdir($oldpath);
       
  1578         if ($@) {
       
  1579           print "Warning: could not convert path \"$line\" to an absolute path because: $@\n";
       
  1580           # Do nothing. We just can't convert it to an absolute path. We'll push it onto the
       
  1581           # output anyway because in some circumstances it will work out OK.
       
  1582         }
       
  1583         push (@output, $line);
       
  1584       } else {
       
  1585         # Lines without .. we don't bother making absolute, because it involves 4 chdir operations
       
  1586         # so is a bit heavyweight.
       
  1587         push (@output, $line);
       
  1588       }
       
  1589     }
       
  1590 
       
  1591     else { # Output from STDERR
       
  1592       if ($self->{verbose} > 1) { print "ABLD: $line\n"; }    
       
  1593   
       
  1594       # Catch errors that look like the makefile isn't present.
       
  1595       # Note, different versions of the build tools produce different things, so the regular expression below is a bit evil.
       
  1596       if ($line =~ /^(U1052|make\[1\]: (?i:\Q$ENV{EPOCROOT}\EEPOC32\\BUILD\\.*): No such file or directory|make: \*\*\* \[.*\] Error 2)$/) {
       
  1597   
       
  1598         # Makefile not present, so generate it.
       
  1599   
       
  1600         $self->CallMakMake($abldPath, $plat, $test);
       
  1601   
       
  1602         goto TRYAGAIN;
       
  1603       }        
       
  1604       elsif ($line =~ /^ABLD ERROR: Project Bldmake directory .* does not exist$/
       
  1605           or $line =~ /^ABLD ERROR: .* not yet created$/
       
  1606           or $line =~ /abld\.bat does not exist/) {
       
  1607   
       
  1608         #BldMake needs to be run.
       
  1609         $self->CallBldMake($abldPath);
       
  1610         goto TRYAGAIN;
       
  1611       }
       
  1612       elsif ($line =~ /^This project does not support platform/) {
       
  1613         push @errorLines, "Error: Platform \"$plat\" not supported\n";
       
  1614       }
       
  1615       elsif ($line =~ /^MISSING:/) {
       
  1616         print "$line\n";
       
  1617       }
       
  1618       elsif ($line =~ /Can't find ABLD.PL on PATH/i) {
       
  1619         push @errorLines, "Error: Couldn't run abld $abldParms: $line\n";      
       
  1620       }
       
  1621       else {
       
  1622         push @errorLines, "$line\n";
       
  1623       }
       
  1624     }
       
  1625   }
       
  1626   
       
  1627   if (scalar @errorLines > 0) {
       
  1628     die @errorLines;
       
  1629   }
       
  1630   
       
  1631   $self->{abldcache}->{$abldPath." ".$abldParms} = \@output;
       
  1632 
       
  1633   return \@output;
       
  1634 }
       
  1635 
       
  1636 sub CallBldMake {
       
  1637   my $self = shift;
       
  1638   my $abldPath = shift;
       
  1639 
       
  1640   if (exists $self->{bldMakeCalled}) {
       
  1641     die "Error: Problem calling bldmake in \"$abldPath\"\n";
       
  1642   }
       
  1643   else {
       
  1644     $self->{bldMakeCalled} = 1;
       
  1645   }
       
  1646 
       
  1647   if ($self->{verbose}) {
       
  1648     print "Calling bldmake in $abldPath...\n";
       
  1649   }
       
  1650   my $cwd = cwd();
       
  1651   chdir $abldPath or die "Error: Couldn't change working directory to $abldPath: $!\n";
       
  1652   system "bldmake bldfiles";
       
  1653   chdir $cwd;
       
  1654   die "Error: \"bldmake bldfiles\" failed in \"$abldPath\" (exit status $?)\n" if ($?);
       
  1655 }
       
  1656 
       
  1657 sub CallMakMake {
       
  1658   my $self = shift;
       
  1659   my $abldPath = shift;
       
  1660   my $plat = shift;
       
  1661   my $test = shift;
       
  1662 
       
  1663   my $repeatGuard = "makMakeCalled_$plat";
       
  1664   if ($test) {
       
  1665     $test = 'test';
       
  1666     $repeatGuard .= '_test';
       
  1667   }
       
  1668   else {
       
  1669     $test = '';
       
  1670   }
       
  1671 
       
  1672   if (exists $self->{$repeatGuard}) {
       
  1673     if ($test) {
       
  1674       die "Error: Problem generating makefile for $test $plat in \"$abldPath\"\n";
       
  1675     }
       
  1676     else {
       
  1677       die "Error: Problem generating makefile for $plat in \"$abldPath\"\n";
       
  1678     }
       
  1679   }
       
  1680   else {
       
  1681     $self->{$repeatGuard} = 1;
       
  1682   }
       
  1683 
       
  1684   if ($self->{verbose}) {
       
  1685     if ($test) {
       
  1686       print "Generating makefile for $test $plat...\n";
       
  1687     }
       
  1688     else {
       
  1689       print "Generating makefile for $plat...\n";
       
  1690     }
       
  1691   }
       
  1692   system "$abldPath\\abld $test makefile $plat > NUL";
       
  1693 }
       
  1694 
       
  1695 sub BinSets {
       
  1696   my $self = shift;
       
  1697   
       
  1698   $self->ProcessBinaries();
       
  1699   
       
  1700   return $self->{binsets};
       
  1701 }
       
  1702 
       
  1703 sub SourceRootPath {
       
  1704   my $fileName = shift;
       
  1705   if (Utils::IsAbsolute($fileName)) {
       
  1706     $fileName = Utils::PrependSourceRoot($fileName);
       
  1707   }
       
  1708   else {
       
  1709     Utils::AbsoluteFileName(\$fileName);
       
  1710   }
       
  1711   Utils::CheckWithinSourceRoot($fileName);
       
  1712   $fileName =~ s/\\.$//;
       
  1713   return $fileName;
       
  1714 }
       
  1715 
       
  1716 sub WarnRedundantMRPLine {
       
  1717   my $self = shift;
       
  1718   my $remove = shift;
       
  1719   my $line = shift;
       
  1720   my $comp = $self->{comp} || "component name unknown";
       
  1721   my $sign = "";
       
  1722   my $action = "add";
       
  1723 
       
  1724   if($remove) {
       
  1725     $action = "remove";
       
  1726   }
       
  1727   print "Remark: ($comp) The MRP line \"$line\" does not $action any files. Therefore is this line necessary?\n";
       
  1728 }
       
  1729 
       
  1730 sub CheckPathLength {
       
  1731   my $self = shift;
       
  1732   my $path = shift;
       
  1733   
       
  1734   if (length ($path) > MAX_PATH_LENGTH) {
       
  1735      return "The component \"$self->{comp}\" is pending release and contains a path which is " . length($path) . " characters long and will prevent the component from being released: \"$path\"."
       
  1736   }
       
  1737 
       
  1738   return 0;
       
  1739 }
       
  1740 
       
  1741 sub SetIPR {
       
  1742     my $self = shift;
       
  1743     my $category = shift;
       
  1744     my $path = shift || 'default';
       
  1745     my $exportRestricted = (shift) ? 1 : 0;
       
  1746     
       
  1747     if (!$category || shift) {
       
  1748       # caller(0))[3] gives the package and the method called, e.g. MrpData::SetIPR
       
  1749       croak "Invalid number of arguments passed to " . (caller(0))[3] . "\n";
       
  1750     }
       
  1751 
       
  1752     $path = File::Spec->canonpath($path); # Normalise the path
       
  1753     
       
  1754     # remove trailing slashes
       
  1755     $path =~ s/[\\\/]$//;
       
  1756     
       
  1757     if ($path ne 'default') {
       
  1758       $path = SourceRootPath($path);
       
  1759     }
       
  1760 
       
  1761     if($self->{iniData}->HasMappings()){
       
  1762       $path = $self->{iniData}->PerformMapOnFileName($path);
       
  1763     }
       
  1764     
       
  1765     $path = Utils::RemoveSourceRoot($path) if ($path ne 'default');
       
  1766     
       
  1767     if (exists $self->{unresolvedIPR}->{$path}) {
       
  1768       return 0;
       
  1769     }
       
  1770     
       
  1771     $self->{unresolvedIPR}->{$path} = {
       
  1772                     category => uc($category),
       
  1773                     exportRestricted => $exportRestricted};
       
  1774     
       
  1775     return 1;
       
  1776 }
       
  1777 
       
  1778 sub SetComponent {
       
  1779     my $self = shift;
       
  1780     my $operand = shift;
       
  1781 
       
  1782     croak "Invalid number of arguments passed to " . (caller(0))[3] . "\n" if (shift);
       
  1783 
       
  1784     if (exists $self->{comp}) {
       
  1785         return 0;
       
  1786     }
       
  1787     
       
  1788     $self->{comp} = $operand;
       
  1789     
       
  1790     return 1;
       
  1791 }
       
  1792 
       
  1793 sub SetNotesSource {
       
  1794     my $self = shift;
       
  1795     my $operand = shift;
       
  1796    
       
  1797     if (!$operand || shift) {
       
  1798       croak "Invalid number of arguments passed to " . (caller(0))[3] . "\n";
       
  1799     }
       
  1800     
       
  1801     if (exists $self->{notes_src}) {
       
  1802         return 0;
       
  1803     }
       
  1804 
       
  1805     $operand = File::Spec->canonpath($operand); # Normalise the path
       
  1806     
       
  1807     $operand = SourceRootPath($operand);
       
  1808     
       
  1809     if($self->{iniData}->HasMappings()){
       
  1810         $operand = $self->{iniData}->PerformMapOnFileName($operand);
       
  1811     }
       
  1812     
       
  1813     Utils::CheckExists($operand);
       
  1814     Utils::CheckIsFile($operand);
       
  1815     $self->{notes_src} = Utils::RemoveSourceRoot($operand);
       
  1816     
       
  1817     return 1;
       
  1818 }
       
  1819 
       
  1820 sub SetSource {
       
  1821     my $self = shift;
       
  1822     my $operand = shift;
       
  1823     
       
  1824     croak "Invalid number of arguments passed to " . (caller(0))[3] . "\n" if (shift);
       
  1825 
       
  1826     if ($operand =~ /distribution\.policy$/i) {
       
  1827       print "\nREMARK: Distribution Policy file included as source in \"$self->{mrpName}\"\n";
       
  1828       return 1;
       
  1829     }
       
  1830 
       
  1831     $operand = File::Spec->canonpath($operand); # Normalise the path
       
  1832         
       
  1833     #remove trailing slashes
       
  1834     $operand =~ s/[\\\/]$//;
       
  1835     
       
  1836     $operand = SourceRootPath($operand);
       
  1837 
       
  1838     if($self->{iniData}->HasMappings()){
       
  1839       $operand = $self->{iniData}->PerformMapOnFileName($operand);
       
  1840     }
       
  1841     
       
  1842     Utils::CheckExists($operand);
       
  1843     $self->{srcitems}->{Utils::RemoveSourceRoot($operand)} = 1;
       
  1844     # No longer classify the source. We do this on-demand later.
       
  1845     
       
  1846     return 1;
       
  1847 }
       
  1848 
       
  1849 sub SetBinary {
       
  1850     my $self = shift;
       
  1851     my @words =  @{shift()};
       
  1852     my $test = (shift) ? 1 : 0;
       
  1853     my $remove = (shift) ? 1 : 0;
       
  1854 
       
  1855     croak "Invalid number of arguments passed to " . (caller(0))[3] . "\n" if (shift);
       
  1856 
       
  1857     my $path = shift @words;
       
  1858 
       
  1859     $path = File::Spec->canonpath($path); # Normalise the path
       
  1860     
       
  1861     # tranfer to absolute path
       
  1862     $path = SourceRootPath($path);
       
  1863 
       
  1864     if (scalar @words) {
       
  1865         if($self->{iniData}->HasMappings()){
       
  1866             $path = $self->{iniData}->PerformMapOnFileName($path);
       
  1867         }
       
  1868     }
       
  1869 
       
  1870     push @{$self->{binaryStatements}}, {
       
  1871                         abldPath => $path,
       
  1872                         test     => $test,
       
  1873                         remove   => $remove,
       
  1874                         words    => [@words]};
       
  1875 }
       
  1876 
       
  1877 sub SetExports {
       
  1878     my $self = shift;
       
  1879     my $abldPath = shift;
       
  1880     my $test = (shift) ? 1 : 0;
       
  1881     my $dependantComponent = shift;
       
  1882 
       
  1883     if (!$abldPath || shift) {
       
  1884         croak "Invalid number of arguments passed to " . (caller(0))[3] . "\n";
       
  1885     }
       
  1886     
       
  1887     if ($dependantComponent) {
       
  1888     	push (@{$self->{_dependantComponents}->{$dependantComponent}}, $abldPath);
       
  1889     }
       
  1890 
       
  1891     $abldPath = File::Spec->canonpath($abldPath); # Normalise the path
       
  1892         
       
  1893     $abldPath = SourceRootPath($abldPath);
       
  1894 
       
  1895     if($self->{iniData}->HasMappings()){
       
  1896       $abldPath = $self->{iniData}->PerformMapOnFileName($abldPath);
       
  1897     }
       
  1898 
       
  1899     Utils::CheckExists($abldPath);
       
  1900     
       
  1901     push @{$self->{exportsStatements}}, { abldPath => $abldPath,
       
  1902                                            test => $test};
       
  1903 }
       
  1904 
       
  1905 sub SetExportFile {
       
  1906     my $self = shift;
       
  1907     my $source = shift;
       
  1908     my $destination = shift;
       
  1909     my $remove = (shift) ? 1 : 0;
       
  1910     my $dependantComponent = shift;
       
  1911  
       
  1912     croak "Invalid number of arguments passed to " . (caller(0))[3] . "\n" if (shift);
       
  1913         
       
  1914     unless ($source and $destination) {
       
  1915 	croak "Error: Incorrect syntax to 'export_file' keyword in \"$self->{mrpName}\"\n";
       
  1916     }
       
  1917 
       
  1918     if ($dependantComponent) {
       
  1919     	push (@{$self->{_dependantComponents}->{$dependantComponent}}, $source);
       
  1920     }
       
  1921     
       
  1922     $source = File::Spec->canonpath($source); # Normalise the path
       
  1923     $destination = File::Spec->canonpath($destination);
       
  1924         
       
  1925     $source = SourceRootPath($source);
       
  1926 
       
  1927     if($self->{iniData}->HasMappings()){
       
  1928       $source = $self->{iniData}->PerformMapOnFileName($source);
       
  1929       $destination = $self->{iniData}->PerformMapOnFileName($destination);
       
  1930     }
       
  1931 
       
  1932     $destination = Utils::PrependEpocRoot($destination);
       
  1933     
       
  1934     push @{$self->{unprocessedExportFiles}}, { source => $source,
       
  1935                                            destination => $destination,
       
  1936                                            remove => $remove};
       
  1937 }
       
  1938 
       
  1939 sub GetIPRInformation {
       
  1940     my $self = shift;
       
  1941     
       
  1942     if (exists $self->{IPR}) {
       
  1943         return $self->{IPR};
       
  1944     }
       
  1945     else {
       
  1946         return {};
       
  1947     }
       
  1948 }
       
  1949 
       
  1950 sub GetExportComponentDependencies {
       
  1951     my $self = shift;	
       
  1952 
       
  1953     if (defined $self->{_dependantComponents}) {
       
  1954             return (keys %{$self->{_dependantComponents}}); # Return an array of the dependencies
       
  1955     }
       
  1956     
       
  1957     return undef;
       
  1958 }
       
  1959 
       
  1960 sub ValidateParsing {
       
  1961     my $self = shift;
       
  1962     
       
  1963     # This flag stops the reader from trying to populate the object more than once
       
  1964     $self->{populated} = 1;
       
  1965     
       
  1966     if (exists $self->{srcitems} && !exists $self->{unresolvedIPR}) {
       
  1967         # If no IPR information exists in the MRP file then we set the IPR category
       
  1968         # for each source item to undef.  This is so that incorrect IPR information is
       
  1969         # not returned.  A flag is also set to indicate that IPR information exists.  The
       
  1970         # flag will be used to ensure other parts other parts of validation should will
       
  1971         # not be performed (e.g. validating exports).
       
  1972         
       
  1973         $self->{noIprInformation} = 1;
       
  1974         
       
  1975         foreach my $sourceItem (keys %{$self->{srcitems}}) {
       
  1976             $self->{IPR}->{$sourceItem} = {
       
  1977                                            category => undef,
       
  1978                                            exportRestricted => undef,
       
  1979                                            };
       
  1980         }
       
  1981     }
       
  1982     else {
       
  1983         # Reconcile the IPR information here so that any warnings are produced sooner...
       
  1984         # IPR information can only be included if it matches a source line in the MRP file
       
  1985         # All other IPR lines will be ignored.  The reconciliation is done here as IPR
       
  1986         # lines may appear before source lines in the MRP file.
       
  1987 
       
  1988         if (!defined $self->{srcitems} && exists $self->{unresolvedIPR}->{default}) {
       
  1989             carp "Warning: The default IPR entry does not apply to any source statements in \"$self->{mrpName}\"\n";
       
  1990         }
       
  1991        
       
  1992         # Match IPR against source statement by using the length...
       
  1993         foreach my $sourceItem (keys %{$self->{srcitems}}) {
       
  1994             # The sort below sorts by longest line first, not shortest line first. Note $b <=> $a, not $a <=> $b...
       
  1995             # This allows us to match the most relevant line first, based on longest length/best match 
       
  1996             foreach my $iprItem (sort {length($b) <=> length($a)} keys %{$self->{unresolvedIPR}}) {
       
  1997                 next if ($iprItem eq 'default');
       
  1998                 # If the source item contains the IPR path then it is a match 
       
  1999                 if ($sourceItem =~ m/^\Q$iprItem\E([\\\/]|$)/i) {
       
  2000                     $self->{IPR}->{$sourceItem} = $self->{unresolvedIPR}->{$iprItem};
       
  2001                     
       
  2002                     last;   
       
  2003                 }
       
  2004             }
       
  2005                  
       
  2006             # If it didn't match an IPR then we assign the default
       
  2007             if (!exists $self->{IPR}->{$sourceItem}) {
       
  2008                 $self->{IPR}->{$sourceItem} = $self->{unresolvedIPR}->{default};
       
  2009             }
       
  2010         }
       
  2011 
       
  2012         delete $self->{unresolvedIPR}->{default};
       
  2013 	    
       
  2014         # Find IPR entries which do live under a source folder...
       
  2015         foreach my $iprItem (keys %{$self->{unresolvedIPR}}) {
       
  2016             next if (exists $self->{IPR}->{$iprItem});
       
  2017 	    
       
  2018             foreach my $sourceItem (keys %{$self->{srcitems}}) {
       
  2019                 if ($iprItem =~ /^\Q$sourceItem\E/i) {
       
  2020                     $self->{IPR}->{$iprItem} = $self->{unresolvedIPR}->{$iprItem};
       
  2021                     last;
       
  2022                 }
       
  2023             }
       
  2024 	    
       
  2025             if (!grep /\Q$iprItem\E/i, (keys %{$self->{IPR}})) {
       
  2026                 # Otherwise this IPR statement does not apply to this MRP file...
       
  2027                 carp "Warning: The IPR entry for \"$iprItem\" does not apply to any source statements in \"$self->{mrpName}\"\n";
       
  2028             }
       
  2029         }    
       
  2030     }
       
  2031   
       
  2032     delete $self->{unresolvedIPR};
       
  2033 }
       
  2034 
       
  2035 
       
  2036 sub ProcessExports {
       
  2037   my $self = shift;
       
  2038   my $confirmExportIprInformation = shift;
       
  2039   
       
  2040   return if ($self->{exportsProcessed});
       
  2041   
       
  2042   $self->{exportsProcessed} = 1;
       
  2043   
       
  2044   foreach my $export (@{$self->{exportsStatements}}) {
       
  2045     $self->HandleExports($export->{abldPath}, $export->{test});
       
  2046   }
       
  2047   
       
  2048   foreach my $exportFile (@{$self->{unprocessedExportFiles}}) {    
       
  2049     if($self->{raptorcache}){
       
  2050       my $isHandle = 0;
       
  2051       foreach my $export (@{$self->{exportsToBeProcessed}}) {
       
  2052         if($export->{source} eq $exportFile->{source}){
       
  2053           if (exists $self->{exports}->{automatic}->{lc(Utils::RemoveEpocRoot($export->{destination}))}) {
       
  2054             $self->HandleExportFile($export->{source}, $export->{destination}, $exportFile->{remove});
       
  2055             $isHandle = 1;
       
  2056           }
       
  2057         }
       
  2058       }
       
  2059       if($isHandle == 0){
       
  2060         foreach my $export (@{$self->{exportsToBeProcessed}}) {
       
  2061           if(lc($export->{destination}) eq lc($exportFile->{destination})){
       
  2062             foreach my $tempExport (@{$self->{exportsToBeProcessed}}) {
       
  2063               if($export->{source} eq $tempExport->{source}){
       
  2064                 if (exists $self->{exports}->{automatic}->{lc(Utils::RemoveEpocRoot($tempExport->{destination}))}) {
       
  2065                   $self->HandleExportFile($tempExport->{source}, $tempExport->{destination}, $exportFile->{remove});
       
  2066                 }
       
  2067               }
       
  2068             }
       
  2069           }
       
  2070         }
       
  2071       }
       
  2072     }
       
  2073     else{
       
  2074       $self->HandleExportFile($exportFile->{source}, $exportFile->{destination}, $exportFile->{remove});
       
  2075     } 
       
  2076   }
       
  2077   
       
  2078   delete $self->{unprocessedExportFiles};
       
  2079   
       
  2080   # If exports are to be classified, or the caller wants to confirm the IPR information for exports is correct...
       
  2081   if ($self->{iniData}->CategoriseExports() || $confirmExportIprInformation) {
       
  2082       $self->ClassifyManualExports();
       
  2083       
       
  2084       # The codes below are changed to make MakeCBR follow cachefiles created by Raptor
       
  2085       if(!$self->{raptorcache}){
       
  2086         $self->ClassifyAutomaticExports();
       
  2087       }
       
  2088       else{
       
  2089         my @tempExports;
       
  2090         foreach my $export (@{$self->{exportsToBeProcessed}}) {
       
  2091           if (exists $self->{exports}->{automatic}->{lc(Utils::RemoveEpocRoot($export->{destination}))}) {
       
  2092             push @tempExports, $export;
       
  2093           }
       
  2094         }
       
  2095         @{$self->{exportsToBeProcessed}} = @tempExports;
       
  2096 		
       
  2097         delete $self->{exports}->{automatic};
       
  2098         delete $self->{exports}->{abldPaths};
       
  2099       }
       
  2100       delete $self->{raptorcache};
       
  2101       
       
  2102       # If no IPR information exists in the MRP file then we do not validate the exports as we don't care about if
       
  2103       # we need dependant components
       
  2104       if (!$self->{noIprInformation}) {
       
  2105         # Check to see if the exports are owned by the component, or dependant components have been specified...
       
  2106         foreach my $export (@{$self->{exportsToBeProcessed}}) {
       
  2107           # check if the exports are included as source in this component
       
  2108           if (!grep keys %{$self->{srcitems}}, $export->{source}) {
       
  2109             # If not then check if another dependant component for the export has been specified
       
  2110             
       
  2111             # A dependant component is specified for either the export source or the exports abld path
       
  2112             my $whatToTest = 'source';
       
  2113             $whatToTest = 'abldPath' if (exists $export->{abldPath});
       
  2114        
       
  2115             my $dependencyExists = 0;
       
  2116   
       
  2117             foreach my $dependantComponent (keys %{$self->{_dependantComponents}}) {
       
  2118               if (grep /\Q$export->{$whatToTest}\E/i, (@{$self->{_dependantComponents}->{$dependantComponent}})) {
       
  2119                 $dependencyExists = 1;
       
  2120               }            
       
  2121             }
       
  2122             
       
  2123             if (!$dependencyExists) {
       
  2124               # If no dependency exists...
       
  2125               warn "Warning: ".$self->Component()." contains an export '". $export->{source} ."' which is not included as source for this component, and does not contain dependencies on another component\n";
       
  2126             }
       
  2127           }
       
  2128         }
       
  2129       }
       
  2130       
       
  2131       # If we only processed exports to that we can confirm the IPR information, but
       
  2132       # we don't actually want to categorise exports then we delete them
       
  2133       if (!$self->{iniData}->CategoriseExports() && $confirmExportIprInformation) {
       
  2134         delete $self->{exportsToBeProcessed};
       
  2135       }
       
  2136   }
       
  2137 
       
  2138   my $errors;
       
  2139   
       
  2140   foreach my $export (@{$self->{exportsToBeProcessed}}) {
       
  2141     if (!$self->AddExport($export->{source}, $self->RemoveRoot($export->{destination}))) {
       
  2142       $errors = 1;
       
  2143     }        
       
  2144   }
       
  2145   if ($errors) {
       
  2146     die "Aborting due to above error(s)\n";
       
  2147   }  
       
  2148 
       
  2149   delete $self->{exportsToBeProcessed};
       
  2150 
       
  2151   if ($self->{binariesProcessed}) {
       
  2152     # if binaries and exports have been processed then we delete the abldcach as
       
  2153     # it is no longer required and takes up a lot of memory
       
  2154     delete $self->{abldcache};
       
  2155   }
       
  2156 }
       
  2157 
       
  2158 sub RemoveRoot {
       
  2159   my $self = shift;
       
  2160   my $path = shift;
       
  2161   return $1 if($path =~ /^\\(.+)/);
       
  2162   return $path;
       
  2163 }
       
  2164 
       
  2165 sub ProcessBinaries {
       
  2166   my $self = shift;
       
  2167   
       
  2168   return if ($self->{binariesProcessed});
       
  2169   
       
  2170   $self->{binariesProcessed} = 1;  
       
  2171   
       
  2172   foreach my $binary (@{$self->{binaryStatements}}) {
       
  2173   
       
  2174     my $success = 0;
       
  2175 
       
  2176     if (!scalar(@{$binary->{words}})) {
       
  2177         $binary->{abldPath} = Utils::PrependEpocRoot($binary->{abldPath});
       
  2178         # Pass a reference of $success to HandleBinDirOrFile which can only be changed in HandleBinFile if the operation is successful.
       
  2179         $self->HandleBinDirOrFile($binary->{remove}, "unclassified", $binary->{abldPath}, \$success);
       
  2180         if ($success == 0 )
       
  2181         {
       
  2182             my $line;
       
  2183             $line = 'test' if ($binary->{test});
       
  2184             $line .=  'binary ' . join ' ', @{$binary->{words}};
       
  2185             $self->WarnRedundantMRPLine($binary->{remove}, $line);
       
  2186         }
       
  2187     }
       
  2188     else {
       
  2189         # Pass a reference of $success to HandleBinSet which can only be changed in HandleBinFile if the operation is successful.
       
  2190         $self->HandleBinSet($binary->{remove}, $binary->{test}, \$success, $binary->{abldPath}, @{$binary->{words}});
       
  2191         if ($success == 0 )
       
  2192         {
       
  2193             my $line;
       
  2194             $line = 'test' if ($binary->{test});
       
  2195             $line .=  'binary ' . join ' ', @{$binary->{words}};
       
  2196             $self->WarnRedundantMRPLine($binary->{remove}, $line);
       
  2197         }
       
  2198     }
       
  2199   }
       
  2200   
       
  2201   if ($self->{exportsProcessed}) {
       
  2202     # if binaries and exports have been processed then we delete the abldcache as
       
  2203     # it is no longer required and takes up a lot of memory
       
  2204     delete $self->{abldcache};
       
  2205   }
       
  2206 }
       
  2207 
       
  2208 
       
  2209 1;
       
  2210 
       
  2211 __END__
       
  2212 
       
  2213 =head1 NAME
       
  2214 
       
  2215 MrpData.pm - Provides an interface to the contents of a component's MakeRel project (mrp) file.
       
  2216 
       
  2217 =head1 DESCRIPTION
       
  2218 
       
  2219 Once a C<MrpData> object has been created using the C<New> method, the remaining methods can be used to access the F<.mrp> data.
       
  2220 
       
  2221 =head1 INTERFACE
       
  2222 
       
  2223 =head2 New
       
  2224 
       
  2225 Expects to be passed the name of the mrp file. This doesn't necessarily have to have a F<.mrp> extension. The parser supports the following keyword / value pairs:
       
  2226 
       
  2227   component    <component_name>
       
  2228   source       <source_file|source_directory>
       
  2229   binary       [<abld_path> <platform> [<variant> <program>]] | [<binary_file>] | [<binary_directory>]
       
  2230   -binary      [<abld_path> <platform> [<variant> <program>]] | [<binary_file>] | [<binary_directory>]
       
  2231   testbinary   <abld_path> <platform> [<variant> <program>]
       
  2232   -testbinary  <abld_path> <platform> [<variant> <program>]
       
  2233   exports      <abld_path>
       
  2234   notes_source <release_notes_source_path>
       
  2235   ipr          [<export-restricted>] type [<directory>]
       
  2236 
       
  2237 =head2 Component
       
  2238 
       
  2239 Returns a string containing the name of the component.
       
  2240 
       
  2241 =head2 MrpName
       
  2242 
       
  2243 Returns a string containing the full path name of the component's F<mrp> file.
       
  2244 
       
  2245 =head2 ExternalVersion
       
  2246 
       
  2247 Returns a string containing the external version of the component to be released.
       
  2248 
       
  2249 =head2 InternalVersion
       
  2250 
       
  2251 Returns a string containing the internal version of the component to be released.
       
  2252 
       
  2253 =head2 SourceCategories
       
  2254 
       
  2255 Returns a reference to a list of source IPR categories present in the component. Each of these may be used as an input to C<Source>. These categories are defined in 'distribution.policy' files.
       
  2256 
       
  2257 =head2 Source
       
  2258 
       
  2259 Expects to be passed a scalar containing the required source category. Returns a reference to a list of source files corresponding to the specified category for this component.
       
  2260 
       
  2261 =head2 SourceItems
       
  2262 
       
  2263 Expects no arguments. Returns a list of the operands of all the "source" statements found in the MRP file. This is then stored in the RelData file and is later used by validation to work out which director(y|ies) to check for added files.
       
  2264 
       
  2265 =head2 BinaryCategories
       
  2266 
       
  2267 Returns a reference to a list of binary categories present in the component. Each of these may be used as an input to C<Binaries>. The binary categories are determined by the structure of the F<mrp> file. For example, the statement C<binary \mycomp thumb urel> will result in the associated binaries being classified and C<thumb_urel>. The statement C<testbinary \mycomp arm4> will generate two categories - C<test_arm4_udeb> and C<test_arm4_urel>. Any binary files or directories that are explictly referenced (e.g. C<binary \epoc32\myfile.txt> or C<binary \epoc32\mydir>) are categorised as C<unclassified>. Also, any binary files that are found to be common between any two categories and re-categorised as C<unclassified>. This is to ensure that each binary F<zip> file contains a unique set of files.
       
  2268 
       
  2269 If the C<categorise_binaries> keyword has not been specified in the user's F<reltools.ini> file, this interface will always return a reference to a list with a single entry in it - C<unclassified>.
       
  2270 
       
  2271 =head2 Binaries
       
  2272 
       
  2273 Returns a reference to a list of binary files for this component. May optionally be passed a scalar containing the required binary category, in which case it returns a list of just the binaries in the specified category. Dies if the requested category is not present.
       
  2274 
       
  2275 =head2 ExportCategories
       
  2276 
       
  2277 Returns a reference to a list of export categories present in the component. If the C<categorise_exports> keyword has not been specified in the user's F<reltools.ini> file, this list will contain a single entry - C<unclassified>. Otherwise, each exported file will be categorised according to the rules used for categorising source code. The returned list will in this case contain the set of exported source categories present in the component. Elements in this list may be used as inputs to the method below (C<Exports>).
       
  2278 
       
  2279 =head2 Exports
       
  2280 
       
  2281 Returns a reference to a list of exported file for this component. May optionally be passed a scalar containing the required export category, in which case it returns a list of just the exports in the specified category. Dies if the requested category is not present.
       
  2282 
       
  2283 =head2 ExportInfoForCat
       
  2284 
       
  2285 Expects a category to be passed. Returns the exportinfo for the category.
       
  2286 
       
  2287 =head2 BinariesAndExports
       
  2288 
       
  2289 Returns a reference to a list of all the binaries and exports of this component. Note, unlike the methods C<Binaries> and C<Exports>, this method does not allow a category to be specified. This is because binaries are categorised according to build type and exports are categorised according to source intellectual property rights rules. They two types of categorisation are incompatible.
       
  2290 
       
  2291 =head2 NotesSource
       
  2292 
       
  2293 Returns a string containing the path and name of the release notes source file for this component.
       
  2294 
       
  2295 =head2 BinSets
       
  2296 
       
  2297 Returns a reference to an array of hashes, representing each "binary <path> <platform> <variant>" line. The hashes have these fields: path, plat, var, mmp (often ''), and test (a Boolean). This method is used by C<MakeRel> and C<MakeEnv> to build the component before release.
       
  2298 
       
  2299 =head2 EnsureDoesNotExist
       
  2300 
       
  2301 Checks that the version given does not already exist in an archive.
       
  2302 
       
  2303 =head2 Validate
       
  2304 
       
  2305 Checks that all the files shown in the MRP do actually exist.
       
  2306 
       
  2307 =head2 ClassifyAutomaticExports
       
  2308 
       
  2309 Classify exports that were specified using the 'exports' or 'testexports' keyword in the mrp file.
       
  2310 
       
  2311 =head2 ProcessExportMakeFile
       
  2312 
       
  2313 Expect EXPORT.MAKE/EXPORTTEST.MAKE file, classify exports that were specified using the 'exports'/'testexports' keyword in the mrp file.
       
  2314 
       
  2315 =head2 WarnRedundantMRPLine
       
  2316 
       
  2317 Output warnings about redundant MRP lines (full redundancy).
       
  2318 
       
  2319 =head2 GetIPRInformation()
       
  2320 
       
  2321 Returns a hash containing the IPR information for the component.
       
  2322 
       
  2323 The format is the returned data is a hash:
       
  2324 
       
  2325     Path = (
       
  2326                     category = char,
       
  2327                     exportRestricted = boolean
       
  2328             )
       
  2329 
       
  2330 =head2 SetBinary(@arguments, test, remove)
       
  2331 
       
  2332 Sets the binary information.  @arguments is an array containing the arguments
       
  2333 from the MRP line, in the order in which they appeared.  
       
  2334 
       
  2335 =head2 SetComponent(componentName)
       
  2336 
       
  2337 Sets the name of the component to componentName.
       
  2338 
       
  2339 =head2 SetExportFile(source, destination, remove, dependantComponent)
       
  2340 
       
  2341 Sets the export file information.  The source and destination arguments are both
       
  2342 required, if they are not specified a fatal error will be produced.  The source
       
  2343 file will also be checked to see if it exists and that it has not already been
       
  2344 specified as an export file.
       
  2345 
       
  2346 If the export file is not included as source for the current MRP component then
       
  2347 the dependant component will also need to be specified.
       
  2348 
       
  2349 =head2 SetExports(path, test, dependantComponent)
       
  2350 
       
  2351 Sets the location of the bld.inf from where the export information can be derived.
       
  2352 The location will be checked to see if it exists and that it has not already been
       
  2353 specified.
       
  2354 
       
  2355 If the exports are not included as source for the current MRP component then
       
  2356 the dependant component will also need to be specified.
       
  2357 
       
  2358 =head2 SetIPR(category, path, exportRestricted)
       
  2359 
       
  2360 Sets the IPR information for the component.  If no path is specified then the
       
  2361 IPR category is set to be the default category for the component.  The
       
  2362 exportRestricted argument is boolean.
       
  2363 
       
  2364 If the same path is specified more than once a fatal error will be produced.
       
  2365 
       
  2366 =head2 SetNotesSource(noteSourcePath)
       
  2367 
       
  2368 Sets the notes source to the notesSourcePath specified.  If the notes source has
       
  2369 already been set, or the path does not exist, a fatal error will be produced.
       
  2370 
       
  2371 =head2 SetSource(sourcePath)
       
  2372 
       
  2373 Adds the sourcePath to the list of included source entries for the component.
       
  2374 If the source path does not exist or the path has already been added then a
       
  2375 fatal error will be produced.
       
  2376 
       
  2377 =head2 ValidateParsing()
       
  2378 
       
  2379 This method needs to be called once the parser has finished setting all the
       
  2380 information.  Currently this method reconciles IPR statements against the
       
  2381 components source, and also checks that required dependant components have
       
  2382 been set.
       
  2383 
       
  2384 If this method is not run then IPR information will be unavailable.
       
  2385 
       
  2386 =head2 GetExportComponentDependencies()
       
  2387 
       
  2388 Returns an array containing the any components which the current component has
       
  2389 dependencies on.
       
  2390 
       
  2391 =head2 Populated()
       
  2392 
       
  2393 The MRP file is parsed by a reader, which then populates this MRP object.  The
       
  2394 Populated method returns a boolean value indicating if the object has been
       
  2395 populated.
       
  2396 
       
  2397 =head1 KNOWN BUGS
       
  2398 
       
  2399 None.
       
  2400 
       
  2401 =head1 COPYRIGHT
       
  2402 
       
  2403  Copyright (c) 2000-2009 Nokia Corporation and/or its subsidiary(-ies).
       
  2404  All rights reserved.
       
  2405  This component and the accompanying materials are made available
       
  2406  under the terms of the License "Eclipse Public License v1.0"
       
  2407  which accompanies this distribution, and is available
       
  2408  at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
  2409  
       
  2410  Initial Contributors:
       
  2411  Nokia Corporation - initial contribution.
       
  2412  
       
  2413  Contributors:
       
  2414  
       
  2415  Description:
       
  2416  
       
  2417 
       
  2418 =cut