tools/static_dependencies.pl
author William Roberts <williamr@symbian.org>
Tue, 02 Nov 2010 17:03:32 +0000
changeset 105 96c15d318508
parent 80 3ab0df073c86
permissions -rw-r--r--
Regenerate static_dependencies.txt from first principles, to original files as well as stem_* versions

# Copyright (c) 2010 Symbian Foundation Ltd.
# All rights reserved.
# This component and the accompanying materials are made available
# under the terms of the License "Eclipse Public License v1.0"
# which accompanies this distribution, and is available
# at the URL "http://www.eclipse.org/legal/epl-v10.html".
#
# Initial Contributors:
# Symbian Foundation - initial contribution.
#
# Contributors:
#
# Description: 
# This script generates a list of static dependencies for files in a ROM

use strict;
use Getopt::Long;

my $inverted_table = 0;
my $existing_file;
GetOptions(
  "i|invert" => \$inverted_table,   # add the inverted table
  "u|update=s" => \$existing_file,  # update existing file
  );

my @existinglines;
if ($existing_file)
	{
	open EXISTING, "<$existing_file" or die("Cannot open $existing_file: $!\n");
	print STDERR "Reading existing dependencies from $existing_file\n";
	@existinglines = <EXISTING>;
	close EXISTING;
	}

my %romfiles;
my @contents;
my %need_details;

my $line;
while ($line = <>)
	{
  my ($romfile,$hostfile,$ibyfile,$package,$cmd,@columns) = split /,/, $line;		# first 5 fields are guaranteed to be simple
	next if ($romfile eq "ROM file");		# skip header line
	if (!defined $hostfile)
		{
		# is it perhaps a static_dependencies.txt line?
		my $dependencies;
		my $nothing;
		($romfile,$hostfile,$dependencies,$nothing) = split /\t/, $line;
		next if (defined $nothing || !defined $hostfile);
		$cmd = "";
		}
	
	if ($cmd =~ /slim$/i)
		{
		$need_details{lc $romfile} = 1;
		$cmd = "stem";		# slim implies stem
		}
	if ($cmd =~ /stem$/i && $hostfile !~ /stem_/)
		{
		push @contents, "$romfile\t$hostfile";	# calculate dependencies for the original file
		$hostfile =~ s/(\/|\\)([^\\\/]+)$/$1stem_$2/;			# then use stem version as well
		}
	push @contents, "$romfile\t$hostfile";
	$romfiles{lc $romfile} = $romfile;
	}

sub canonical_romfile($)
	{
	my ($romfile) = @_;
	my $canonical = $romfiles{lc $romfile};
	return $canonical if (defined $canonical);

	# New romfile not seen before - add to table
	$romfiles{lc $romfile} = $romfile;	# set the standard for others!
	return $romfile;	
	}

my %outputlines;
foreach my $existingline (@existinglines)
	{
	chomp $existingline;
	my ($romfile, $hostfile, $deps) = split /\t/, $existingline;
	if (defined $deps)
		{
		$romfile = canonical_romfile($romfile);
		$outputlines{"$romfile\t$hostfile"} = "$romfile\t$hostfile\t$deps";
		}
	}

my %dependents;

sub print_dependency($$@)
	{
	my ($romfile,$hostfile,@dependencies) = @_;
	$outputlines{"$romfile\t$hostfile"} = "$romfile\t$hostfile\t". join(":",@dependencies);
	
	next unless $inverted_table;
	
	# Create inverted table 
	foreach my $dependent (@dependencies)
		{
		next if ($dependent =~ /^sid=/);
		$dependent = lc $dependent;
		
		$dependent =~ s/^sys\\bin\\//;	# no directory => sys\bin anyway
		$dependent =~ s/\[\S+\]//;	# ignore the UIDs for now
		
		if (!defined $dependents{$dependent})
			{
			$dependents{$dependent} = $romfile;
			}
		else
			{
			$dependents{$dependent} .= "\t$romfile";
			}
		}
	}

sub summarise_list($)
	{
	my ($hashref) = @_;
	my @summary;
	my @list = sort {$a <=> $b} keys %$hashref;
	my $first = shift @list;
	my $latest = $first;
	foreach my $number (@list)
		{
		if ($number == $latest + 1)
			{
			# extends existing range by one
			$latest = $latest + 1;
			next;
			}
		# new range
		if ($first > -1)
			{
			if ($latest == $first)
				{
				# Range with one element
				push @summary, $first;
				}
			else
				{
				push @summary, "$first-$latest";
				}
			}
		$first = $number;
		$latest = $number;
		}
	if ($latest == $first)
		{
		push @summary, "$first";
		}
	else
		{
		push @summary, "$first-$latest";
		}
	return join(".", @summary);
	}

sub generate_elftran_dependencies($$)
	{
	my ($romfile,$hostfile) = @_;
	
	return if ($hostfile =~ /\/z\//); 	# data files in armv5\urel\z
	return if ($hostfile =~ /\.sis$/);	# not an e32 image file

	my @elftran = `elftran $hostfile`;
	
	my $sid;
	my @imports;
	my $dll;
	my $importing = 0;
	my %ordinals;
	my %exports;
	foreach my $line (@elftran)
		{
		#        Ordinal   318:  00010f9f
		if ($line =~ /Ordinal\s+(\d+):\s+(ABSENT)?/)
			{
			$exports{$1} = 1 unless ($2 eq "ABSENT");
			next;
			}

		# 2 imports from backend{00010001}[102828d5].dll
		# 17 imports from dfpaeabi{000a0000}.dll
		if ($line =~ /(\d+) imports from (\S+)\{.{8}\}(\S+)$/)
			{
			$dll = $2.$3;
			my $import_count = $1;
			my $exename = "sys\\bin\\". lc $dll;
			$exename =~ s/\[\S+\]//;	# ignore the UID
			if (defined $need_details{$exename})
				{
				# enable the tracking of the imported ordinals
				$importing = $import_count;
				%ordinals = ();
				}
			else
				{
				# Just report the simple reference to the imported dll
				$importing = 0;
				push @imports, $dll;
				}
			next;
			}
		if ($importing && $line =~ /^\s+(\d+)( offset by \d+)?$/)
			{
			$ordinals{$1} = 1;
			$importing -= 1;
			if ($importing == 0)
				{
				$dll = $dll. "@". summarise_list(\%ordinals);
				push @imports, $dll;
				}
			next;
			}
		if ($line =~ /^Secure ID: (\S+)$/)
			{
			$sid = $1; 	# presumably owns private/$sid  and various $sid.etxn files
			next;
			}
		}

	my @export_info = ();
	if (scalar keys %exports && $need_details{lc $romfile})
		{
		push @export_info, "exports=".summarise_list(\%exports);
		}
	print_dependency($romfile,$hostfile, @export_info, "sid=$sid",@imports);
	}

sub find_exe_names_dependencies($$)
	{
	my ($romfile,$hostfile) = @_;
	
	my @strings = `$ENV{"SBS_HOME"}\\win32\\mingw\\bin\\strings $hostfile`;
	
	my %executables;
	foreach my $string (@strings)
		{
		if ($string =~ /^(.*\\)?([^-\\]+\.(exe|dll))$/i)
			{
			my $exename = $2;
			$exename =~ s/^\s+//;		# strip off leading whitespace (e.g.length byte before "clock.exe")
			$exename = canonical_romfile("sys\\bin\\$exename");	# get the exact capitalisation
			# print STDERR "Found $exename in $string";
			$executables{$exename} = 1;
			}
		}
	if (%executables)
		{
		print_dependency($romfile,$hostfile,sort keys %executables);
		}
	else
		{
		print STDERR "No executable names found in system statup resource $hostfile\n";
		}
	}

sub find_dependency_in_sys_bin($$$)
	{
	my ($romfile,$hostfile,$basename) = @_;
	$basename = lc $basename;
	foreach my $extension (".exe",".dll")
		{
		my $dependency = "sys\\bin\\$basename$extension";
		if (defined $romfiles{$dependency})
			{
			print_dependency($romfile,$hostfile,$romfiles{$dependency});
			return;
			}
		}
	# grep in the contents list?
	# print_dependency($romfile,$hostfile,"unmatched sys\\bin\\$basename");
	}

foreach $line (@contents)
	{
	my ($romfile,$hostfile) = split /\t/, $line;
	
	if ($hostfile =~ /epoc32.release.arm/i)
		{
		generate_elftran_dependencies($romfile,$hostfile);
		next;
		}
	
	# App registration files
	if ($romfile =~ /private.10003a3f.*apps\\(.*)_reg\.rsc$/i)
		{
		my $dependency = "sys\\bin\\$1.exe";
		print_dependency($romfile,$hostfile,$dependency);
		next;
		}
	# app resources
	if ($romfile =~ /resource.apps\\(.*)(\.mif|\.mbm|\.rsc)$/)
		{
		my $executable = $1;
		$executable =~ s/_aif$//; 	# xxx_aif.mif
		$executable =~ s/_loc$//; 	# xxx_loc.rsc
		
		find_dependency_in_sys_bin($romfile,$hostfile,$executable);
		next;
		}

	# System state manager resource files
	if ($romfile =~ /private.2000d75b\\.*\.rsc$/i)
		{
		find_exe_names_dependencies($romfile,$hostfile);
		next;
		}
	
	# Assume that the rest don't depend on anything, and leave them out.
	}

foreach my $romfile ( sort keys %outputlines)
	{
	print $outputlines{$romfile}, "\n";
	}

if ($inverted_table)
	{
	print "\n";
	foreach my $inverted (sort keys %dependents)
		{
		print "x\t$inverted\t$dependents{$inverted}\n";
		}
	}