srctools/distillsrc/distillsrc.pm
changeset 629 541af5ee3ed9
parent 602 3145852acc89
equal deleted inserted replaced
628:7c4a911dc066 629:541af5ee3ed9
       
     1 #!/bin/perl -w
       
     2 
       
     3 # Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 # All rights reserved.
       
     5 # This component and the accompanying materials are made available
       
     6 # under the terms of the License "Eclipse Public License v1.0"
       
     7 # which accompanies this distribution, and is available
       
     8 # at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     9 #
       
    10 # Initial Contributors:
       
    11 # Nokia Corporation - initial contribution.
       
    12 #
       
    13 # Contributors:
       
    14 #
       
    15 # Description:
       
    16 # distillsrc.pm - compiles a list of source used in .mrp files, and deletes
       
    17 # any unused source
       
    18 # 
       
    19 #
       
    20 
       
    21 package CDistillSrc;
       
    22 
       
    23 use strict;
       
    24 use File::Spec;
       
    25 use File::Path;
       
    26 use File::Basename;
       
    27 use FindBin;
       
    28 use lib $FindBin::Bin;
       
    29 use ReadMrp;
       
    30 
       
    31 use lib File::Spec->catdir($FindBin::Bin, '..', 'makecbr');
       
    32 use CConfig;
       
    33 
       
    34 
       
    35 
       
    36 # Constructor
       
    37 #
       
    38 # Parameters:
       
    39 #
       
    40 # $aSrcRoot : The root from which all src statements are based
       
    41 # $aSrcPath : The path under aSrcRoot to the source tree to be processed
       
    42 # $aSrcPrefix : An optional prefix which can be stripped from all src statements
       
    43 # $aPlatform : e.g 'beech' - used to locate the platform specific product directory
       
    44 #
       
    45 # Returns: The object (or undef if there was a problem)
       
    46 #
       
    47 sub New($$$$)
       
    48 	{
       
    49 	my $proto = shift;
       
    50 	my ($aSrcRoot, $aSrcPath, $aSrcPrefix, $aPlatform, $aCheckCase) = @_;
       
    51 
       
    52 	my $class = ref($proto) || $proto;
       
    53 
       
    54 	my $self = {};
       
    55 	bless($self, $class);
       
    56 
       
    57 	my $error = 0;
       
    58 
       
    59 	if (!defined($aSrcRoot))
       
    60 		{
       
    61 		print "ERROR: RealTimeBuild: A srcroot must be given, to specify where all 'source' declarations originate from\n";
       
    62 		$error = 1;
       
    63 		}
       
    64 		
       
    65 	if (!defined($aSrcPath))
       
    66 		{
       
    67 		print "ERROR: RealTimeBuild: A srcpath must be given, to specify which source under the srcroot is to be filtered. Use '\\' to filter the entire srcroot\n";
       
    68 		$error = 1;
       
    69 		}
       
    70 
       
    71 	if (!defined($aPlatform))
       
    72 		{
       
    73 		print "ERROR: RealTimeBuild: A platform must be given, to locate the product directory\n";
       
    74 		$error = 1;
       
    75 		}
       
    76 		
       
    77 	if ($error)
       
    78 		{
       
    79 		print "\n";
       
    80 		}
       
    81 	else
       
    82 		{
       
    83 		if ($aSrcPath =~ /\.\./)
       
    84 			{
       
    85 			print "ERROR: RealTimeBuild: The source path must be relative to the srcroot, and must not contain '..'\n";
       
    86 			$error = 1;
       
    87 			}
       
    88 	
       
    89 		$self->iSrcRoot($aSrcRoot);
       
    90 		$self->iSrcPath($aSrcPath);
       
    91 		$self->iSrcPrefix($aSrcPrefix);
       
    92 		$self->iPlatform($aPlatform);
       
    93 		$self->iSrcItems({});
       
    94 		$self->iCheckCase(!!$aCheckCase);
       
    95 
       
    96 		$self->AddSrcItem("os/buildtools/toolsandutils/productionbldtools/unref/orphan/cedprd/SuppKit", "non-shipped");
       
    97 		$self->AddSrcItem("os/buildtools/toolsandutils/productionbldtools/unref/orphan/cedprd/tools", "non-shipped");
       
    98 		$self->AddSrcItem("os/buildtools/toolsandutils/productionbldtools/unref/orphan/cedprd/DevKit", "non-shipped");
       
    99 		$self->AddSrcItem("os/buildtools/toolsandutils/productionbldtools", "non-shipped");
       
   100 		}
       
   101 
       
   102 	if ($error)
       
   103 		{
       
   104 		$self = undef;
       
   105 		}
       
   106 
       
   107 	return $self;
       
   108 	}
       
   109 
       
   110 # Object data
       
   111 #
       
   112 sub iSrcRoot()
       
   113 	{
       
   114 	my $self = shift;
       
   115 	if (@_) { $self->{iSRCROOT} = shift; }
       
   116 	return $self->{iSRCROOT};
       
   117 	}
       
   118 
       
   119 sub iSrcPath()
       
   120 	{
       
   121 	my $self = shift;
       
   122 	if (@_) { $self->{iSRCPATH} = shift; }
       
   123 	return $self->{iSRCPATH};
       
   124 	}
       
   125 
       
   126 sub iSrcPrefix()
       
   127 	{
       
   128 	my $self = shift;
       
   129 	if (@_) { $self->{iSRCPREFIX} = shift; }
       
   130 	return $self->{iSRCPREFIX};
       
   131 	}
       
   132 
       
   133 sub iPlatform()
       
   134 	{
       
   135 	my $self = shift;
       
   136 	if (@_) { $self->{iPLATFORM} = shift; }
       
   137 	return $self->{iPLATFORM};
       
   138 	}
       
   139 	
       
   140 sub iSrcItems()
       
   141 	{
       
   142 	my $self = shift;
       
   143 	if (@_) { $self->{iSRCITEMS} = shift; }
       
   144 	return $self->{iSRCITEMS};
       
   145 	}
       
   146 
       
   147 sub iCheckCase()
       
   148 	{
       
   149 	my $self = shift;
       
   150 	if (@_) { $self->{iCHECKCASE} = shift; }
       
   151 	return $self->{iCHECKCASE};
       
   152 	}
       
   153 
       
   154 sub iCorrectedCase()
       
   155 	{
       
   156 	my $self = shift;
       
   157 	if (@_) { $self->{iCORRECTEDCASE} = shift; }
       
   158 	return $self->{iCORRECTEDCASE};
       
   159 	}
       
   160 
       
   161 # LoadMrps - Records the source lines out of all .mrp files
       
   162 #
       
   163 # Parameters:
       
   164 # $aConfig - optional configuration file, as used by makecbr
       
   165 # $aLists - optional component lists, as used by makecbr
       
   166 # $aMrps - optional .mrp files
       
   167 #
       
   168 # Returns: True, if the load was successful. False otherwise
       
   169 #
       
   170 sub LoadMrps($$$)
       
   171 	{
       
   172 	my $self = shift;
       
   173 	my ($aConfig, $aLists, $aMrps) = @_;
       
   174 	# Load in config file
       
   175 
       
   176 	my @lists = @$aLists;
       
   177 	my @mrps;
       
   178 	foreach my $mrp (@$aMrps){
       
   179 		{
       
   180 		push @mrps, [$mrp, ''];
       
   181 		}
       
   182 	}
       
   183 	my @configMrps = ();
       
   184     if (defined($aConfig))
       
   185 		{
       
   186 		my @configs = $self->_LoadConfig($aConfig);
       
   187 
       
   188 		# Add mrps and lists (after planting them in srcroot)
       
   189 		push @lists, map($self->_PlantFile($_), @{$configs[0]});
       
   190 		@configMrps = map($self->_PlantFile($_), @{$configs[1]});
       
   191 		foreach my $mrp (@configMrps)
       
   192 			{
       
   193 			push @mrps, [$mrp, ''];
       
   194 			}
       
   195 		}
       
   196 	
       
   197 	# Load in mrp lists
       
   198 	foreach my $file (@lists)
       
   199 		{
       
   200 		if (open (MRPLIST, $file))
       
   201 			{
       
   202 			foreach my $line (<MRPLIST>)
       
   203 				{
       
   204 				chomp $line;
       
   205 				$line =~ s/#.*$//; # Remove comments
       
   206 				$line =~ s/^\s*//; # Remove extraneous spaces
       
   207 				$line =~ s/\s*$//;
       
   208 	
       
   209 				if ($line ne "")
       
   210 					{
       
   211 					my @parms = split(/\s+/, $line);
       
   212 	
       
   213 					if (scalar(@parms) != 2)
       
   214 						{
       
   215 						warn "ERROR: RealTimeBuild: Entries in component list '$file' should be of the form 'name mrp_location'. Problem in line: $line\n";
       
   216 						next;
       
   217 						}
       
   218 					else
       
   219 						{
       
   220 						# Ignore *nosource* entries
       
   221 						next if ($parms[1] eq '*nosource*');
       
   222 						
       
   223 						push @mrps, [$self->_PlantFile($parms[1]), $parms[0]];
       
   224 						}
       
   225 					}
       
   226 				}
       
   227 			close MRPLIST or warn "ERROR: RealTimeBuild: Couldn't close '$file' : $!\n";
       
   228 			}
       
   229 		else
       
   230 			{
       
   231 			warn "Couldn't open '$file' : $!\n";	
       
   232 			}
       
   233 		}
       
   234 
       
   235 	# Load all .mrp files
       
   236 	if (scalar(@mrps) == 0)
       
   237 		{
       
   238 		die "ERROR: RealTimeBuild: No .mrp files were specified\n";
       
   239 		}
       
   240 
       
   241 	my $loaded = 1;
       
   242 	
       
   243 	foreach my $mrp (@mrps)
       
   244 		{
       
   245 		# Get path of mrp file (from here)
       
   246 		my ($name, $path) = fileparse($mrp->[0]);
       
   247 		# Convert to path from source root
       
   248 		if (!($self->_RemoveBaseFromPath($self->iSrcRoot(), \$path)))
       
   249 			{
       
   250 			warn "ERROR: Mrp file $mrp->[0] isn't under the source root (".$self->iSrcRoot().")\n";
       
   251 			next;
       
   252 			}
       
   253 		
       
   254 		my $mrpobj;
       
   255         
       
   256         # To indicate the correct case and where the .mrp file comes from if failed to check letter case
       
   257         if (!($self->_CheckCase($mrp->[0]))) {
       
   258             my $mrp_error_source = "optional component list(by -f) or optional .mrp list(by -m)";
       
   259             foreach my $myName (@configMrps) {
       
   260                 if ($myName eq $mrp->[0]) {
       
   261                     $mrp_error_source = "config file '".$aConfig."'";
       
   262                     last;
       
   263                 }
       
   264             } 
       
   265             print "WARNING: Case of '".$mrp->[0]."' supplied in ".$mrp_error_source." does not match the file system. Should be ".$self->iCorrectedCase()."\n";
       
   266         }
       
   267         
       
   268 		if (!eval { $mrpobj = New ReadMrp($mrp->[0]) })
       
   269 			{
       
   270 			$loaded = 0;
       
   271 			my $message = $@;
       
   272 			$message =~ s/^(ERROR:\s*)?/ERROR: RealTimeBuild: /i;
       
   273 			print $message;
       
   274 			}
       
   275 		else
       
   276 			{
       
   277 			my $selfowned = 0;
       
   278 			my $mrpComponentName = $mrpobj->GetComponent();
       
   279 			if( ($mrp->[1] ne '') && (lc($mrp->[1]) ne lc($mrpComponentName)))
       
   280 				{
       
   281 				print "ERROR: RealTimeBuild: Component name \'$mrp->[1]\' does not match \'$mrpComponentName\' in $mrp->[0]\n";
       
   282 				}
       
   283 			foreach my $srcitem (@{$mrpobj->GetSrcItems()})
       
   284 				{
       
   285 				if ($srcitem =~ /^[\/\\]/)
       
   286 					{
       
   287 					# Remove source prefix
       
   288 					$srcitem = $self->_StripFile($srcitem);
       
   289 					}
       
   290 				else
       
   291 					{
       
   292 					# Relative source item
       
   293 					$srcitem = File::Spec->catdir($path, $srcitem);
       
   294 					}
       
   295 
       
   296 				my $rootedmrp = $path.$name;
       
   297 				if ($self->_RemoveBaseFromPath($srcitem, \$rootedmrp))
       
   298 					{
       
   299 					$selfowned = 1;
       
   300 					}
       
   301 
       
   302 				$self->AddSrcItem($srcitem, $mrpComponentName);
       
   303 				}
       
   304 			if ($self->iCheckCase())
       
   305 				{
       
   306 				foreach my $binexpitem (@{$mrpobj->GetBinExpItems()})
       
   307 					{
       
   308 					# Check lower case
       
   309 					if ($binexpitem =~ /[A-Z]/)
       
   310 						{
       
   311 						print "REMARK: [$mrpComponentName] Binary/export file $binexpitem should be lower case\n";
       
   312 						}
       
   313 					}
       
   314 				}
       
   315 
       
   316 			if (!$selfowned)
       
   317 				{
       
   318 				print "REMARK: .mrp file '$mrp->[0]' does not include itself as source\n"; 
       
   319 				}
       
   320 			}
       
   321 		}
       
   322 	return $loaded;
       
   323 	}
       
   324 	
       
   325 # AddSrcItem - Records a source file, usually taken from an .mrp file
       
   326 #
       
   327 # Parameters:
       
   328 # $aItem - the source file name
       
   329 # $aComponent - the name of the component which claimed the file
       
   330 #
       
   331 # Returns: None
       
   332 # Dies: Not normally; only if the source hash data structure gets corrupted
       
   333 sub AddSrcItem($$)
       
   334 	{
       
   335 	my $self = shift;
       
   336 	my ($aItem, $aComponent) = @_;
       
   337 
       
   338 	my $item = $aItem;
       
   339 
       
   340 	# Worth checking that the file exists
       
   341 	my $truePath = File::Spec->catdir($self->iSrcRoot(), $item);
       
   342 	if (($item !~ /^\\component_defs/i) && (!-e $truePath))
       
   343 		{
       
   344 		print "ERROR: RealTimeBuild: '$aComponent' owns $item, but that path doesn't exist\n";
       
   345 		$item = ""; # No point adding this path to the tree	
       
   346 		}
       
   347 	else
       
   348 		{
       
   349 		# Check case consistency
       
   350 		$self->_CheckCase($truePath) or print "WARNING: [$aComponent] Case of '".$truePath."' does not match the file system. Should be ".$self->iCorrectedCase()."\n";
       
   351 		}
       
   352 	
       
   353 	$item =~ s/^[\/\\]*//; # Remove preceding slashes
       
   354 
       
   355 	my @path = split(/[\/\\]+/,$item);
       
   356 
       
   357 	my $dir = $self->iSrcItems();
       
   358 	while ((scalar @path) > 0)
       
   359 		{
       
   360 		my $subdir = lc(shift @path);
       
   361 	
       
   362 		if (scalar(@path) == 0)
       
   363 			{
       
   364 			# Just enter the final path segment
       
   365 			if (exists($dir->{$subdir}))
       
   366 				{
       
   367 				# Someone already owns at least part of this path
       
   368 				if (!ref($dir->{$subdir}))
       
   369 					{
       
   370 					# Someone owns the whole of this path
       
   371 					my $conflict = $dir->{$subdir};
       
   372 
       
   373 					print "REMARK: $aComponent and $conflict both own $item\n";
       
   374 					}
       
   375 				else
       
   376 					{
       
   377 					if (ref($dir->{$subdir}) ne "HASH")
       
   378 						{
       
   379 						die "ERROR: Source hash is corrupted\n";
       
   380 						}
       
   381 					else
       
   382 						{
       
   383 						# Someone owns a child of this path
       
   384 						my $childtree = $dir->{$subdir};
       
   385 
       
   386 						my @conflicts = $self->_GetTreeComps($childtree);
       
   387 						print "REMARK: $aComponent owns $item, which is already owned by the following component(s): ".join(", ",@conflicts)."\n";
       
   388 						}
       
   389 					}
       
   390 				}
       
   391 			$dir->{$subdir} = $aComponent;
       
   392 			}
       
   393 		else
       
   394 			{
       
   395 			# Need to enter another subdirectory
       
   396 			
       
   397 			if (exists($dir->{$subdir}))
       
   398 				{
       
   399 				if (ref($dir->{$subdir}))
       
   400 					{
       
   401 					# Someone already has - just do a quick integrity check
       
   402 					
       
   403 					if (ref($dir->{$subdir}) ne "HASH")
       
   404 						{
       
   405 						die "ERROR: Source hash is corrupted\n";
       
   406 						}
       
   407 					}
       
   408 				else
       
   409 					{
       
   410 					# The path from this point on is already owned by a component
       
   411 					my $conflict = $dir->{$subdir};
       
   412 					
       
   413 					print "REMARK: $aComponent and $conflict both own $item\n";
       
   414 					last;
       
   415 					}
       
   416 				}
       
   417 			else
       
   418 				{
       
   419 				$dir->{$subdir} = {};
       
   420 				}
       
   421 			}
       
   422 
       
   423 		$dir = $dir->{$subdir};
       
   424 		}
       
   425 	}
       
   426 
       
   427 # DistillSrc - Compare the recorded source lines against the source path. Delete anything which doesn't match.
       
   428 #
       
   429 # Parameters:
       
   430 # $aDummy - A flag - non-zero means don't actually delete
       
   431 #
       
   432 # Returns: None
       
   433 sub DistillSrc($$)
       
   434 	{
       
   435 	my $self = shift;
       
   436 	my ($aDummy) = @_;
       
   437 
       
   438 	my $tree = $self->iSrcItems();
       
   439 	my $path = File::Spec->catdir($self->iSrcRoot(), $self->iSrcPath());
       
   440 
       
   441 	$path=~s/[\/\\]+/\\/; # Remove multiple slashes
       
   442 
       
   443 	# Pop the srcpath off the front of the tree
       
   444 	my @path = split(/[\/\\]/,$self->iSrcPath());
       
   445 
       
   446 	foreach my $dir (@path)
       
   447 		{
       
   448 		if ($dir eq ".")
       
   449 			{
       
   450 			next;
       
   451 			}
       
   452 		elsif (exists($tree->{lc($dir)}))
       
   453 			{
       
   454 			$tree = $tree->{lc($dir)};
       
   455 		
       
   456 			if (!ref($tree))
       
   457 				{
       
   458 				# Some component owns all of the srcpath
       
   459 				last;
       
   460 				}
       
   461 			}
       
   462 		else
       
   463 			{
       
   464 			# No mrp files claimed any of the source
       
   465 			$tree = undef;
       
   466 			last;
       
   467 			}
       
   468 		}
       
   469 
       
   470 	# Now recurse into the tree and delete files
       
   471 	if (defined($tree))
       
   472 		{
       
   473 		if (ref($tree))
       
   474 			{
       
   475 			$self->_DistillTree($tree, $path, $aDummy);
       
   476 			}
       
   477 		else
       
   478 			{
       
   479 			print "REMARK: All source owned by component '$tree'; no action\n";
       
   480 			}
       
   481 		}
       
   482 	else
       
   483 		{
       
   484 		print "WARNING: No .mrp files claim any source; removing $path\n";
       
   485 		$self->_DeletePath($path, $aDummy);
       
   486 		}
       
   487 	}
       
   488 
       
   489 # Print - Display the source tree
       
   490 #
       
   491 # Parameters:
       
   492 # $aDepth - The number of levels of the tree to show. 0 = all levels
       
   493 #
       
   494 # Returns: None
       
   495 sub Print($$)
       
   496 	{
       
   497 	my $self = shift;
       
   498 
       
   499 	my ($aDepth) = @_;
       
   500 
       
   501 	$self->_PrintTree("", $self->iSrcItems(), $aDepth);
       
   502 	}
       
   503 	
       
   504 # *** Private methods ***
       
   505 # *** 
       
   506 
       
   507 # _LoadConfig - (private) Reads a configuration file, as used by makecbr
       
   508 #
       
   509 # Parameters:
       
   510 # $aConfig - filename of the configuration file
       
   511 #
       
   512 # Returns:
       
   513 # (files, mrps) - where files and mrps are listrefs containing component lists and
       
   514 # mrp files respectively
       
   515 #
       
   516 sub _LoadConfig($)
       
   517 	{
       
   518 	my $self = shift;
       
   519 	my ($aConfig) = @_;
       
   520 	
       
   521 	my @files = ();
       
   522 	my @mrps = ();
       
   523 	
       
   524 	my $config = New CConfig($aConfig);
       
   525 
       
   526 	if (!defined $config)
       
   527 		{
       
   528 		die "Couldn't load config file '$aConfig'\n";
       
   529 		}
       
   530 		
       
   531 	# Extract the interesting items into our lists
       
   532 	push @mrps, $config->Get("gt+techview baseline mrp location");
       
   533 	push @mrps, $config->Get("gt only baseline mrp location");
       
   534 	push @files, $config->Get("techview component list");
       
   535 	push @files, $config->Get("gt component list");
       
   536 	
       
   537 	# Remove any items we couldn't find
       
   538 	@mrps = grep(defined($_), @mrps);
       
   539 	@files = grep(defined($_), @files);
       
   540 	
       
   541 	return (\@files, \@mrps);
       
   542 	}
       
   543 
       
   544 # _StripFile - (private) Remover of src prefix. Also maps product directories
       
   545 #
       
   546 # Parameters:
       
   547 # $aFile - Filename to process
       
   548 #
       
   549 # Returns: The processed filename
       
   550 #
       
   551 sub _StripFile($)
       
   552 	{
       
   553 	my $self = shift;
       
   554 	my ($aFile) = @_;
       
   555 
       
   556 	my $file = $aFile;
       
   557 
       
   558 	# Map the product dirs
       
   559 	my $platform = $self->iPlatform();
       
   560 	$file =~ s#^[\/\\]?product[\/\\]#/sf/os/unref/orphan/cedprd/#i;
       
   561 
       
   562 	# Remove the prefix
       
   563 	my $prefix = $self->iSrcPrefix();
       
   564 	
       
   565 	if (defined $prefix)
       
   566 		{
       
   567 		my $mapped = $file; # Keep a copy in case we can't remove the prefix
       
   568 		
       
   569 		if (!$self->_RemoveBaseFromPath($prefix, \$file))
       
   570 			{
       
   571 			$file = $mapped;
       
   572 			}
       
   573 		}
       
   574 	
       
   575 	return $file;
       
   576 	}
       
   577 	
       
   578 # _PlantFile - (private) Add src root to file. Also take off src prefix
       
   579 #
       
   580 # Parameters:
       
   581 # $aFile - Filename to process
       
   582 #
       
   583 # Returns: The processed filename
       
   584 #
       
   585 sub _PlantFile($)
       
   586 	{
       
   587 	my $self = shift;
       
   588 	my ($aFile) = @_;
       
   589 
       
   590 	my $file = $aFile;
       
   591 
       
   592 	# Remove the prefix
       
   593 	$file = $self->_StripFile($file);
       
   594 
       
   595 	# Plant the file in the src root
       
   596 	$file = File::Spec->catdir($self->iSrcRoot(), $file);
       
   597 	
       
   598 	# Ensure all slashes are normalised to a single backslash
       
   599 	$file =~ s/[\/\\]+/\\/; 
       
   600 	
       
   601 	return $file;
       
   602 	}
       
   603 
       
   604 # _RemoveBaseFromPath - (private) Remove a base path from the root of a filename.
       
   605 #
       
   606 # Parameters:
       
   607 # $aBase - The base path to remove
       
   608 # $$aFile - Filename to process (scalar reference)
       
   609 #
       
   610 # Returns: True if the file was under the base path, false otherwise
       
   611 #   $$aFile may be corrupted if the return is false
       
   612 sub _RemoveBaseFromPath($)
       
   613 	{
       
   614 	my $self = shift;
       
   615 	my ($aBase, $aFile) = @_;
       
   616 
       
   617 	my $base = $aBase;
       
   618 	$base =~ s/^[\/\\]*//; # Remove extra slashes
       
   619 	$base =~ s/[\/\\]*$//;
       
   620 
       
   621 	my @base = split(/[\/\\]+/, $base);
       
   622 
       
   623 	$$aFile =~ s/^[\/\\]*//; # Remove preceding slashes
       
   624 	
       
   625 	my $matched = 1;
       
   626 	my $filedir;
       
   627 	
       
   628 	foreach my $dir (@base)
       
   629 		{
       
   630 		if ($$aFile =~ /[\/\\]/)
       
   631 			{
       
   632 			# Split off the bottom dir
       
   633 			$$aFile =~ /([^\/\\]*)[\/\\]+(.*)$/;
       
   634 			($filedir, $$aFile) = ($1, $2, $3);
       
   635 			}
       
   636 		else
       
   637 			{
       
   638 			# Special case - no more dirs
       
   639 			$filedir = $$aFile;
       
   640 			$$aFile = "";
       
   641 			}
       
   642 		if (lc($filedir) ne lc($dir))
       
   643 			{
       
   644 			# Base doesn't match
       
   645 			$matched = 0;
       
   646 			last;
       
   647 			}
       
   648 		}
       
   649 	
       
   650 	return $matched;
       
   651 	}
       
   652 
       
   653 # _CheckCase - (private) Given a literal filename, compares the case of the
       
   654 #                        file on the filesystem against the filename i.e. it
       
   655 #                        can be used to enforce case sensitivity
       
   656 #
       
   657 # Parameters:
       
   658 # $aFilename - The literal filename
       
   659 #
       
   660 # Returns: True if the file matches the supplied case.
       
   661 #          True if the file doesn't exist at all (user is expected to check that separately)
       
   662 #          True if case checking has been disabled.
       
   663 #          False otherwise (if the file exists but under a differing case).
       
   664 #
       
   665 # If false, the correctly cased name is present through $self->iCorrectedCase()
       
   666 sub _CheckCase($)
       
   667 {
       
   668 	my $self = shift;
       
   669 	my ($aFile) = @_;
       
   670 
       
   671 	return 1 if !($self->iCheckCase()); # checking disabled
       
   672 	return 1 if ($^O !~ /win32/i); # only works on Windows anyway
       
   673 	
       
   674 	return 1 if (!-e $aFile); # file not found (under case-insensitive checking)
       
   675 	
       
   676 	$self->iCorrectedCase(Win32::GetLongPathName($aFile));
       
   677 	return ($aFile eq $self->iCorrectedCase());
       
   678 }
       
   679 
       
   680 # _DistillTree - (private) Given a src tree and a dir, clean out any unowned files
       
   681 #
       
   682 # Parameters:
       
   683 # %$aTree - The source tree (hash ref containing nested hash refs and string leaves)
       
   684 # $aDir - The directory to compare against
       
   685 # $aDummy - A flag - non-zero means don't do the actual deletion
       
   686 #
       
   687 # Returns: A flag - non-zero if there were any owned files present
       
   688 sub _DistillTree($$$)
       
   689 	{
       
   690 	my $self = shift;
       
   691 	my ($aTree, $aDir, $aDummy) = @_;
       
   692 
       
   693 
       
   694 	my $keptsome = 0;
       
   695 
       
   696 	if (opendir(DIR, $aDir))
       
   697 	{	
       
   698 		my $dir = $aDir;
       
   699 		$dir =~ s/[\/\\]*$//; # Remove trailing / from dir
       
   700 	
       
   701 		foreach my $entry (readdir(DIR))
       
   702 			{
       
   703 			my $path = $dir."\\".$entry;
       
   704 	
       
   705 			if ($entry =~ /^\.\.?$/)
       
   706 				{
       
   707 				next;
       
   708 				}
       
   709 			elsif (exists $aTree->{lc($entry)})
       
   710 				{
       
   711 				my $treeentry = $aTree->{lc($entry)};
       
   712 				if (ref($treeentry) eq "HASH")
       
   713 					{
       
   714 					# Part of this path is owned
       
   715 					if (-d $path)
       
   716 						{
       
   717 						# Recurse into path
       
   718 						my $keep = $self->_DistillTree($treeentry, $path, $aDummy);
       
   719 						if ($keep)
       
   720 							{
       
   721 							$keptsome = 1;
       
   722 							}
       
   723 						else
       
   724 							{
       
   725 							# Correction; none of this path was owned
       
   726 							$self->_DeletePath($path, $aDummy);
       
   727 							}
       
   728 						}
       
   729 					elsif (-f $path)
       
   730 						{
       
   731 						my @comps = $self->_GetTreeComps($treeentry);
       
   732 						print "ERROR: RealTimeBuild: $path is a file, yet is used as a directory in components: ".join(", ",@comps)."\n";
       
   733 						}
       
   734 					else
       
   735 						{
       
   736 						print "ERROR: $path has disappeared while it was being examined\n";
       
   737 						}
       
   738 					}
       
   739 				elsif (!ref($treeentry))
       
   740 					{
       
   741 					# This path is completely owned
       
   742 					$keptsome = 1;
       
   743 					next;
       
   744 					}
       
   745 				else
       
   746 					{
       
   747 					die "ERROR: Source hash is corrupted\n";
       
   748 					}
       
   749 				}
       
   750 			else
       
   751 				{
       
   752 				$self->_DeletePath($path, $aDummy);
       
   753 				}
       
   754 			}
       
   755 		
       
   756 		closedir(DIR);
       
   757 		}
       
   758 	else
       
   759 		{
       
   760 			warn "ERROR: RealTimeBuild: Couldn't open directory '$aDir' for reading\n";
       
   761 		}
       
   762 
       
   763 	return $keptsome;
       
   764 	}
       
   765 
       
   766 # _GetTreeComps - (private) Get all the leaves out of a tree (or component
       
   767 #                           names out of a source tree)
       
   768 # Parameters:
       
   769 # %$aTree - The source tree (hash ref containing nested hash refs and string leaves)
       
   770 # 
       
   771 # Returns: A list of strings found at the leaves (or component names)
       
   772 sub _GetTreeComps($)
       
   773 	{
       
   774 	my $self = shift;
       
   775 	my ($aTree) = @_;
       
   776 
       
   777 	my @comps = ();
       
   778 
       
   779 	foreach my $entry (keys(%$aTree))
       
   780 		{
       
   781 		if (ref($aTree->{$entry}) eq "HASH")
       
   782 			{
       
   783 			push @comps, $self->_GetTreeComps($aTree->{$entry});
       
   784 			}
       
   785 		elsif (!ref($aTree->{$entry}))
       
   786 			{
       
   787 			push @comps, $aTree->{$entry};
       
   788 			}
       
   789 		else
       
   790 			{
       
   791 			die "ERROR: Source hash is corrupted\n";
       
   792 			}
       
   793 		}
       
   794 		
       
   795 	return @comps;
       
   796 	}
       
   797 
       
   798 # _DeletePath - (private) Safe path deletion (file or dir)
       
   799 #
       
   800 # $aPath - The path to delet
       
   801 # $aDummy  - A flag - non-zero means don't actually delete
       
   802 #
       
   803 # Returns: None. Prints warnings if deletion fails. Dies only in exceptional circumstances
       
   804 sub _DeletePath($$)
       
   805 	{
       
   806 	my $self = shift;
       
   807 
       
   808 	my ($aPath, $aDummy) = @_;
       
   809 
       
   810 	if (-d $aPath)
       
   811 		{
       
   812 		if ($aDummy)
       
   813 			{
       
   814 			print "DUMMY: Directory $aPath is not specified in any .mrp file\n";
       
   815 			}
       
   816 		else
       
   817 			{
       
   818 			print "REMARK: Deleting directory $aPath; ";
       
   819 			my $files = rmtree($aPath);
       
   820 			if ($files)
       
   821 				{
       
   822 				print "$files items removed\n";
       
   823 				}
       
   824 			else
       
   825 				{
       
   826 				print "\nWARNING: Problem removing directory $aPath\n";
       
   827 				}
       
   828 			}
       
   829 		}
       
   830 	elsif (-f $aPath)
       
   831 		{
       
   832 		if ($aDummy)
       
   833 			{
       
   834 			print "DUMMY: File $aPath is not specified in any .mrp file\n";
       
   835 			}
       
   836 		else
       
   837 			{
       
   838 				unless($aPath =~ /distribution.policy.s60/i)
       
   839 				{
       
   840 					print "REMARK: Deleting file $aPath\n";
       
   841 					unlink $aPath or print "WARNING: Problem deleting file $aPath\n";
       
   842 				}
       
   843 			}
       
   844 		}
       
   845 	else
       
   846 		{
       
   847 		warn "ERROR: Can't delete path $aPath; not a file or directory\n";
       
   848 		}
       
   849 	}
       
   850 
       
   851 # _PrintTree - Display a subset of the source tree
       
   852 #
       
   853 # Parameters:
       
   854 # $aPrefix - The string to prefix all paths
       
   855 # $aDepth - The number of levels of the tree to show. 0 = all levels
       
   856 #
       
   857 # Returns: None
       
   858 sub _PrintTree($$$)
       
   859         {
       
   860 	my $self = shift;
       
   861 	
       
   862         my ($aPrefix, $aTree, $aDepth) = @_;
       
   863 
       
   864 	my $prefix = "";
       
   865 	
       
   866 	if ($aPrefix ne "")
       
   867 		{
       
   868 		$prefix = $aPrefix."\\";
       
   869 		}
       
   870 
       
   871         foreach my $key (sort(keys(%$aTree)))
       
   872                 {
       
   873                 if (ref($aTree->{$key}))
       
   874                         {
       
   875 			if ($aDepth!=1)
       
   876 				{
       
   877 				my $newprefix = $prefix.$key;
       
   878 				
       
   879 				if ($key eq "")
       
   880 					{
       
   881 					$newprefix.="{empty}";
       
   882 					}
       
   883 
       
   884                         	$self->_PrintTree($newprefix, $aTree->{$key}, $aDepth-1);
       
   885 				}
       
   886 			else
       
   887 				{
       
   888 				print $prefix.$key."\\...\n";
       
   889 				}
       
   890                         }
       
   891                 else
       
   892                         {
       
   893                         print $prefix.$key." = ".$aTree->{$key}."\n";
       
   894                         }
       
   895                 }
       
   896         }
       
   897 
       
   898 1;