kerneltest/f32test/loader/dlltree.pl
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 30 8aab599e3476
parent 0 a41df078684a
child 43 c1f20ce4abcf
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

# Copyright (c) 2001-2009 Nokia Corporation and/or its subsidiary(-ies).
# 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:
# Nokia Corporation - initial contribution.
#
# Contributors:
#
# Description:
# \f32test\loader\dlltree.pl
# Generate header and build files for one or more trees
# of DLLs and EXEs according to a description file.
# 
#

require Cwd;

my $dllbasename='dllt';
my $dllext='.dll';
my $exebasename='exet';
my $exeext='.exe';
my $mmpext='.mmp';
my $defext='.def';
my $libext='.lib';

my $source0ext='.cpp';
my $source1ext='.cia';
my $xippath="sys\\bin\\";
my $nonxippath="sys\\bin\\";

my $hostpath="\\Epoc32\\Release\\##MAIN##\\##BUILD##\\";

my $flag_value_exe=1;
my $flag_value_fixed=2;
my $flag_value_data=4;
my $flag_value_xip=8;
my $flag_value_dll_in_cycle=16;
my $flag_value_data_in_tree=32;
my $flag_value_xip_data_in_tree=64;
my $flag_value_exports=128;
my $flag_value_pagedcode=256;
my $flag_value_unpagedcode=512;
my $flag_value_idrive=1024;
my $flag_value_vdrive=2048;
my $flag_value_bytepair=4096;
my $flag_value_uncompressed=8192;
my $flag_value_targetonly=16384;
my $flag_value_pageddata=32768;
my $flag_value_unpageddata=65536;

my $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst;
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime;
my $copy_end=$year+1900;

my $argc=scalar(@ARGV);
($argc==1 or $argc==2 or $argc==3) or die "Usage: perl dlltree.pl <filename> <dir> [-allowbad]\n";
my $infile=$ARGV[0];
open IN, $infile or die "Cannot open input file $infile\n";
my @in;
while (<IN>)	{
	push @in, $_;
}
close IN;
my $destdir='';
if ($argc>=2) {
	$destdir=$ARGV[1];
	unless (-d $destdir) {
		mkdir $destdir, 0777 or die "Can't create directory $destdir\n";
	}
	chdir $destdir or die "Can't chdir to directory $destdir\n";
}
my $allowbad;
if ($argc==3) {
	if ($ARGV[2] eq '-allowbad') {
		$allowbad = 1;
	}
}
$cwd=Cwd::getcwd();
$destpath=$cwd;
$destpath=~s/\//\\/g;
if ($destpath=~/^\w\:(.*)/) {
	$destpath=$1;
}
unless ($destpath=~/^(.*?)\\$/) {
	$destpath.='\\';
}
#print "$cwd\n";
#print "$destpath\n";

my @modules;
my @testcases;
my $line=0;
my $modnum=0;
my $state=0;
my $concat;
foreach (@in)	{
	++$line;
	next if (/^\s*$/ or /^\s*\#/);
	if ($state==0)	{
		if (/^\!TESTCASES/) {
			$state=1;
			next;
		}
		my @moddesc=split;
		my $modname=shift @moddesc;
		if (grep {lc($$_{name}) eq lc($modname)} @modules) {
			die "Duplicate module specification at line $line\n";
		}
		my %module;
		$module{name}=$modname;
		$module{num}=$modnum;
		$module{exports}=1;
		$module{nclients}=0;	# number of modules which import from this one
		while (scalar(@moddesc)) {
			my $att=shift @moddesc;
			if ($att=~/^\:$/) {
				last;
			} elsif ($att=~/^X$/i) {
				$module{exe}=1;
			} elsif ($att=~/^F$/i) {
				$module{fixed}=1;
			} elsif ($att=~/^D$/i) {
				$module{data}=1;
			} elsif ($att=~/^P$/i) {
				$module{pagedcode}=1;
			} elsif ($att=~/^PD$/i) {
				$module{pageddata}=1;
			} elsif ($att=~/^B$/i) {
				$module{bytepair}=1;
			} elsif ($att=~/^U$/i) {
				$module{uncompressed}=1;
			} elsif ($att=~/^I$/i) {
				$module{idrive}=1;
			} elsif ($att=~/^V$/i) {
				$module{vdrive}=1;
			} elsif ($att=~/^N$/i) {
				$module{unpagedcode}=1;
			} elsif ($att=~/^T$/i) {
				$module{targetonly}=1;
			} elsif ($att=~/^R(.*?)$/i) {
				$module{xip}=1;
				my $attp=$1;
				if ($attp=~/^\,(.*?)$/) {
					$module{attpname}=$1;
				} elsif (!($attp=~/^\s*$/)) {
					die "Garbage following R attribute at line $line\n";
				}
			}
		}
		if ($module{fixed} && !$module{exe}) {
			die "Can't have FIXED without EXE at line $line\n";
		}
		if ($module{pagedcode} && $module{xip}) {
			die "Can't have PAGEDCODE with XIP at line $line\n";
		}

		# makmake won't support paged binaries which aren't bytepair or uncompressed.
		# However, P can be specified without them in dlltree.txt.  This program generates
		# an MMP file with neither flag.  The flag is added when the files are copied to
		# internal media by T_LDRTST.  (It would be pointless to copy the files to removable
		# media and then edit them because removable media doesn't support paging.)

		my $bothCodePageFlags = $module{pagedcode} && $module{unpagedcode};
		if ($bothCodePageFlags && !$module{idrive}) {
			die "Can't have PAGEDCODE and UNPAGEDCODE without INTERNAL DRIVE at line $line\n";
		}
		if ($module{unpagedcode} && $module{xip}) {
			die "Can't have UNPAGEDCODE with XIP at line $line\n";
		}
		if ($module{pageddata} && !$module{exe}) {
			die "Can't have PAGEDDATA without EXE at line $line\n";
		}
		if ($module{idrive} && $module{xip}) {
			die "Can't have INTERNAL DRIVE with XIP at line $line\n";
		}
		if ($module{vdrive} && $module{xip}) {
			die "Can't have REMOVABLE DRIVE with XIP at line $line\n";
		}
		if ($module{idrive} && $module{vdrive}) {
			die "Can't have INTERNAL DRIVE with REMOVABLE DRIVE at line $line\n";
		}
		if ($module{bytepair} && $module{uncompressed}) {
			die "Can't have BYTEPAIR with UNCOMPRESSED at line $line\n";
		}
		if ($module{exe}) {
			if ($module{attpname}) {
				warn "Attach process with EXE ignored at line $line\n";
				delete $module{attpname};
			}
			$module{basefilename}=$exebasename.$modnum;
			$module{filename}=$module{basefilename}.$exeext;
			$module{source0}=$exebasename.$source0ext;
			$module{source1}="";
			$module{trgtype}='exexp';
			$module{lib}=$module{basefilename}.$libext;
		} else {
			$module{basefilename}=$dllbasename.$modnum;
			$module{filename}=$module{basefilename}.$dllext;
			$module{source0}=$dllbasename.$source0ext;
			$module{source1}=$dllbasename.$source1ext;
			$module{trgtype}='dll';
			$module{lib}=$module{basefilename}.$libext;
		}
		$module{depnames}=\@moddesc;	# dependency names are left over
		$module{line}=$line;
		$module{mark}=0;
		push @modules, \%module;
		++$modnum;
	} elsif ($state<=2) {
		if ($state==1) {
			$concat="";
			die "Syntax error at line $line\n" unless (/^(\w+)\:/);
		}
		if (/^(.*?)\\$/) {
			$concat.=$1;
			$concat.=' ';
			$state=2;
			next;
		} else {
			$concat.=$_;
			$state=1;
		}
		my @modlist=split(/\s+/, $concat);
		my $tcname=shift @modlist;
		$tcname=~/^(.*?)\:$/ or die "???\n";
		$tcname=$1;
		my %tc;
		$tc{name}=$tcname;
		$tc{mod_names}=\@modlist;
		push @testcases, \%tc;
	}
}
foreach $modref (@modules) {
	my $moddepref=$$modref{depnames};
	my @deps;
	foreach (@$moddepref) {
		my $depname=lc($_);
		my @match=grep {lc($$_{name}) eq $depname} @modules;
		if (scalar(@match)==0) {
			die "Unknown dependent module $depname at line $$modref{line}\n";
		}
		my $depref=$match[0];
		if ($$modref{xip} and !$$depref{xip}) {
			die "Illegal dependency: $$modref{name} (XIP) on $$depref{name} (non-XIP)\n";
		}
		push @deps, $$depref{num};
		++$$depref{nclients};
	}
	$$modref{deps}=\@deps;
	my $attpname=$$modref{attpname};
	if ($attpname) {
		my @match=grep {lc($$_{name}) eq lc($attpname)} @modules;
		if (scalar(@match)==0) {
			die "Unknown attach process $attpname at line $$modref{line}\n";
		}
		my $attpref=$match[0];
		if (!$$attpref{exe}) {
			die "Specified attach process is not EXE at line $$modref{line}\n";
		} elsif (!$$attpref{xip}) {
			die "Specified attach process is not XIP at line $$modref{line}\n";
		}
		$$modref{attp}=$attpref;
	}
}
foreach $tcref (@testcases) {
	my $modlistref=$$tcref{mod_names};
	my @modnums;
	foreach $modname (@$modlistref) {
		next if ($modname=~/^\s*$/);
		my @match=grep {lc($$_{name}) eq lc($modname)} @modules;
		if (scalar(@match)==0) {
			die "Unknown module $modname in test case $$tcref{name}\n";
		}
		push @modnums, $match[0]->{num};
	}
	$$tcref{modules}=\@modnums;
	$$tcref{count}=scalar(@modnums);
}
my $next_mark=0;
foreach $modref (@modules) {
	if ($$modref{nclients}==0 and $$modref{exe}) {
		# EXE with no exports
		$$modref{exports}=0;
		$$modref{trgtype}='exe';
	}
	++$next_mark;
	my $modnum=$$modref{num};
	my @tcdeps;
	calc_tc(\@tcdeps, \@modules, $modref, $next_mark);
	$$modref{tcdeps}=\@tcdeps;
	if (grep {$_==$modnum} @tcdeps) {
		$$modref{cycle}=1;
	}
	my @exes=grep {$modules[$_]->{exe}} @tcdeps;
	my $nexes=scalar(@exes);

	unless ($allowbad) {
		if ($nexes>1) {
			die "Module $$modref{name} links to more than one EXE\n";
		} elsif ($nexes==1) {
			my $exeref=$modules[$exes[0]];
			$$modref{linkexe}=$exeref;
			if ($$modref{exe}) {
				if ($$exeref{num}!=$modnum) {
					die "EXE $modref{name} links to another EXE\n";
				}
			} else {
				if ($$modref{attp} and $$exeref{num}!=$modref->{attp}->{num}) {
					die "DLL $$modref{name} ($modnum) incompatible attach process\n";
				}
				if (!$$modref{attp}) {
					$$modref{attp}=$exeref;
				}
			}
		}
	}
	if ($$modref{exe}) {
		foreach $depnum (@tcdeps) {
			my $depref=$modules[$depnum];
			my $depattpref=$depref->{attp};
			if ($depattpref and $depattpref->{num}!=$modnum) {
				die "DEP DLL $$depref{name} ($$depref{num}) incompatible attach process\n";
			}
#			if (!$depattpref) {
#				$$depref{attp}=$modref;
#			}
		}
	}
}
foreach $modref (@modules) {
	my @total_deps;
	my $tcdepref=$$modref{tcdeps};
	push @total_deps, @$tcdepref;
	my $linkexe=$$modref{linkexe};
	if ($linkexe) {
		my $exetcdepref=$$linkexe{tcdeps};
		push @total_deps, @$exetcdepref;
	}
	push @total_deps, $$modref{num};
	my $data=scalar(grep {$modules[$_]->{data}} @total_deps);
	if ($data!=0) {
		$$modref{dataintree}=1;
	}
	my $xipdata=scalar(grep {$modules[$_]->{data} and $modules[$_]->{xip}} @total_deps);
	if ($xipdata!=0) {
		$$modref{xipdataintree}=1;
	}
}

#foreach $modref (@modules) {
#	print "Module $$modref{num}:\n";
#	print "\tName:     $$modref{name}\n";
#	print "\tFilename: $$modref{filename}\n";
#	my $depref=$$modref{deps};
#	my $ndeps=scalar(@$depref);
#	print "\t#Deps:    $ndeps\n";
#	print "\tDeps:     ",join(',',@$depref),"\n";
#}

my @bldinf;
my $bldname='dlltree.inf';
push @bldinf, "// $destpath$bldname\n";
push @bldinf, "//\n";
push @bldinf, "// Copyright (c) 2000-$copy_end Symbian Ltd. All rights reserved.\n";
push @bldinf, "//\n";
push @bldinf, "\n";
push @bldinf, "PRJ_PLATFORMS\n";
push @bldinf, "BASEUSERDEFAULT\n";
push @bldinf, "\n";
push @bldinf, "PRJ_TESTMMPFILES\n";
push @bldinf, "\n";

my @dlltree;
my $dlltreename='dlltree.h';
push @dlltree, "// $destpath$dlltreename\n";
push @dlltree, "//\n";
push @dlltree, "// Copyright (c) 2000-$copy_end Symbian Ltd. All rights reserved.\n";
push @dlltree, "//\n";
push @dlltree, "\n";
push @dlltree, "#ifndef __DLLTREE_H__\n";
push @dlltree, "#define __DLLTREE_H__\n";
push @dlltree, "#include <e32std.h>\n";
push @dlltree, "\n";
push @dlltree, "class MDllList;\n";
push @dlltree, "\n";

my $ibyname='ldrtest.iby';
my @iby;

foreach $modref (@modules) {
	my @mmp;
	my $num=$$modref{num};
	my $mmpname=$$modref{basefilename}.$mmpext;
	my $defname=$$modref{basefilename}.$defext;
	my $depsref=$$modref{deps};
	my $ndeps=scalar(@$depsref);
	push @mmp, "// $destpath$mmpname\n";
	push @mmp, "//\n";
	push @mmp, "// Copyright (c) 2000-$copy_end Symbian Ltd. All rights reserved.\n";
	push @mmp, "//\n";
	push @mmp, "// Generated from $$modref{name}\n";
	push @mmp, "\n";
	push @mmp, "macro             __DLLNUM$num\n";
	push @mmp, "target            $$modref{filename}\n";
	push @mmp, "targettype        $$modref{trgtype}\n";
	push @mmp, "sourcepath        .\n";
	push @mmp, "source            $$modref{source0} $$modref{source1}\n";
	push @mmp, "library           euser.lib efsrv.lib\n";
	push @mmp, "Capability		NONE\n";
	foreach (@$depsref) {
		my $depref=$modules[$_];
		push @mmp, "library           $$depref{lib}\n";
	}
	if ($$modref{exports}) {
		push @mmp, "deffile           ./$defname\n";
		push @mmp, "nostrictdef\n";
	}
	push @mmp, "OS_LAYER_SYSTEMINCLUDE_SYMBIAN   \n";
	push @mmp, "systeminclude     ../../../e32test/mmu   \n";
	push @mmp, "userinclude       . \n";
	if ($$modref{fixed}) {
		push @mmp, "epocfixedprocess\n";
	}

	# if both paged flags are set or the compression is not pageable then print neither.
	# T_LDRTST will add the required flags when it copies the file to internal media.

	my $bothCodePageFlags = $$modref{pagedcode} && $$modref{unpagedcode};
	my $pageableCompression = $$modref{bytepair} || $$modref{uncompressed};
	if (!$bothCodePageFlags && $pageableCompression) {
		push @mmp, "pagedcode\n" if $$modref{pagedcode};
		push @mmp, "unpagedcode\n" if $$modref{unpagedcode};
	}

	if ($$modref{exe}) {
		# make exes unpageddata by default
		if (!$$modref{pageddata}) {
			$$modref{unpageddata}=1;
		}
		if ($$modref{pageddata}) {
			push @mmp, "pageddata\n";
		}
		if ($$modref{unpageddata}) {
			push @mmp, "unpageddata\n";
		}
	}

	if ($$modref{bytepair}) {
		push @mmp, "bytepaircompresstarget\n";
	}

	if ($$modref{uncompressed}) {
		push @mmp, "nocompresstarget\n";
	}

	if ($$modref{data} && !$$modref{exe}) {
		push @mmp, "epocallowdlldata\n";
	}
	push @mmp, sprintf("uid               0x00000000 0x%08x\n", $num+256);
	push @mmp, "SMPSAFE\n";
	$$modref{mmp}=\@mmp;

	if ($$modref{exports}) {
		my @def;
		push @def, "EXPORTS\n";
		if ($$modref{exe}) {
			push @def, "\tRegisterConstructorCall @ 1 NONAME\n";
			push @def, "\tRegisterInitCall @ 2 NONAME\n";
			push @def, "\tRegisterDestructorCall @ 3 NONAME\n";
		} else {
			push @def, "\tInit$num @ 1 NONAME\n";
			push @def, "\tChkC$num @ 2 NONAME\n";
			push @def, "\tBlkI$num @ 3 NONAME\n";
			push @def, "\tGetGeneration @ 4 NONAME\n";
			push @def, "\tRBlkI$num @ 5 NONAME\n";
			push @def, "\tSetCloseLib @ 6 NONAME\n";
		}
		$$modref{def}=\@def;
	}

	push @bldinf, "$$modref{basefilename}\t\tsupport\n";

	if ($num==0) {
		push @dlltree, "#if defined(__DLLNUM$num)\n";
	} else {
		push @dlltree, "#elif defined(__DLLNUM$num)\n";
	}
	push @dlltree, "#define DLLNUM               $num\n";
	if ($$modref{exe}) {
		push @dlltree, "#define EXENUM               $num\n";
		push @dlltree, "_LIT(KServerName, \"$$modref{name}\");\n";
	}
	push @dlltree, "#define INITFUNC             Init$num\n";
	push @dlltree, "#define CHKCFUNC             ChkC$num\n";
	push @dlltree, "#define BLKIFUNC             BlkI$num\n";
	push @dlltree, "#define RBLKIFUNC            RBlkI$num\n";
	push @dlltree, "#define CHKDEPS(r)           (\\\n";
	foreach (@$depsref) {
		my $depref=$modules[$_];
		unless ($$depref{exe}) {
			my $func="ChkC$_";
			push @dlltree, "\t((r)=$func())!=0 ||\\\n";
		}
	}
	push @dlltree, "\t((r)=0)!=0 )\n";
	push @dlltree, "#define INITDEPS(r,l)        (\\\n";
	foreach (@$depsref) {
		my $depref=$modules[$_];
		unless ($$depref{exe}) {
			my $func="Init$_";
			push @dlltree, "\t((r)=$func(l))!=0 ||\\\n";
		}
	}
	push @dlltree, "\t((r)=0)!=0 )\n";
	my $link_to_exe;
	push @dlltree, "#define RBLKIFUNC_DEPS(i,g)  {\\\n";
	foreach (@$depsref) {
		my $depref=$modules[$_];
		if ($$depref{exe}) {
			$link_to_exe=1;
		} else {
			my $func="RBlkI$_";
			push @dlltree, "\t(i)=$func(i,g);\\\n";
		}
	}
	push @dlltree, "\t}\n";
	if ($link_to_exe) {
		push @dlltree, "#define __DLL_LINK_TO_EXE\n";
	}
	if ($$modref{cycle}) {
		push @dlltree, "#define __DLL_IN_CYCLE\n";
	}
	if ($$modref{data}) {
		push @dlltree, "#define __MODULE_HAS_DATA\n";
	}
	if ($$modref{exports}) {
		push @dlltree, "#define __MODULE_EXPORT\t\tEXPORT_C\n";
		push @dlltree, "#define __MODULE_IMPORT\t\tIMPORT_C\n";
	} else {
		push @dlltree, "#define __MODULE_EXPORT\n";
		push @dlltree, "#define __MODULE_IMPORT\n";
	}
	foreach (@$depsref) {
		my $depref=$modules[$_];
		if ($$depref{exe}) {
		} else {
			push @dlltree, "extern \"C\" IMPORT_C TInt Init$_(MDllList&);\n";
			push @dlltree, "extern \"C\" IMPORT_C TInt ChkC$_();\n";
			push @dlltree, "extern \"C\" IMPORT_C TInt RBlkI$_(TInt, TInt);\n";
		}
	}
	my $hostfullpathname=$hostpath.$$modref{filename};
	if ($$modref{xip}) {
		my $epocfullpathname=$xippath.$$modref{filename};
		my $flags;
		if ($$modref{attp}) {
			$flags='process '.$modref->{attp}->{filename};
		}
		push @iby, "file=$hostfullpathname\t\t$epocfullpathname\t\t$flags\n";
	} else {
		my $epocfullpathname=$nonxippath.$$modref{filename};
		push @iby, "data=$hostfullpathname\t\t$epocfullpathname\n";
	}
}

#push @dlltree, "#else\n";
#push @dlltree, "#error No __DLLNUM macro defined\n";
push @dlltree, "#endif\n";
push @dlltree, "\n";

my $module_count=scalar(@modules);
push @dlltree, "const TInt KNumModules=$module_count;\n";
push @dlltree, "\n";
push @dlltree, "#ifdef __INCLUDE_DEPENDENCY_GRAPH\n";
push @dlltree, "static const TText* const ModuleName[KNumModules] =\n";
push @dlltree, "\t\{\n";
foreach $modref (@modules) {
	my $string="\t(const TText*)L\"$$modref{name}\"";
	unless ($$modref{num}==$module_count-1) {
		$string.=',';
	}
	push @dlltree, $string;
	my $pad=41-length($string);
	$pad=($pad+3)/4;
	push @dlltree, "\t"x$pad, "\/*", $$modref{num}, "*\/\n";
}
push @dlltree, "\t\};\n";
push @dlltree, "\n";
push @dlltree, "#define MODULE_NAME(n)	TPtrC(ModuleName[n])\n";
push @dlltree, "\n";
push @dlltree, "static const TText* const ModuleFileName[KNumModules] =\n";
push @dlltree, "\t\{\n";
foreach $modref (@modules) {
	if ($$modref{idrive} || $$modref{vdrive}) {
		my $fn = $$modref{filename};
		my $used_nxip_path = "?:\\$nonxippath$fn";
		$used_nxip_path =~ s/\\/\\\\/g;		# double backslashes
		substr($used_nxip_path,0,1) = $$modref{idrive} ? "0" : "1";
		push @dlltree, "\t(const TText*)L\"$used_nxip_path\"";
	} else {
		push @dlltree, "\t(const TText*)L\"$$modref{filename}\"";
	}
	if ($$modref{num}==$module_count-1) {
		push @dlltree, "\n";
	} else {
		push @dlltree, ",\n";
	}
}
push @dlltree, "\t\};\n";
push @dlltree, "\n";
push @dlltree, "#define MODULE_FILENAME(n)	TPtrC(ModuleFileName[n])\n";
push @dlltree, "\n";
foreach $modref (@modules) {
	my $modnum=$$modref{num};
	my $tcdepsref=$$modref{tcdeps};
	my $numdeps=scalar(@$tcdepsref);
	push @dlltree, "static const TInt Module$modnum","Deps[] =\n";
	push @dlltree, "\t\{$numdeps";
	if ($numdeps) {
		push @dlltree, ",", join(',',@$tcdepsref)
	}
	push @dlltree, "\};\n";
}
push @dlltree, "static const TInt* const ModuleDependencies[KNumModules] =\n";
push @dlltree, "\t\{\n";
foreach $modref (@modules) {
	my $modnum=$$modref{num};
	push @dlltree, "\tModule$modnum","Deps";
	if ($$modref{num}==$module_count-1) {
		push @dlltree, "\n";
	} else {
		push @dlltree, ",\n";
	}
}
push @dlltree, "\t\};\n";
push @dlltree, "\n";
push @dlltree, sprintf "const TInt KModuleFlagExe=0x%04x;\n", $flag_value_exe;
push @dlltree, sprintf "const TInt KModuleFlagFixed=0x%04x;\n", $flag_value_fixed;
push @dlltree, sprintf "const TInt KModuleFlagData=0x%04x;\n", $flag_value_data;
#push @dlltree, "#ifdef __EPOC32__\n";
push @dlltree, sprintf "const TInt KModuleFlagXIP=0x%04x;\n", $flag_value_xip;
push @dlltree, sprintf "const TInt KModuleFlagPagedCode=0x%04x;\n", $flag_value_pagedcode;
push @dlltree, sprintf "const TInt KModuleFlagUnpagedCode=0x%04x;\n", $flag_value_unpagedcode;
push @dlltree, sprintf "const TInt KModuleFlagIDrive=0x%04x;\n", $flag_value_idrive;
push @dlltree, sprintf "const TInt KModuleFlagVDrive=0x%04x;\n", $flag_value_vdrive;
push @dlltree, sprintf "const TInt KModuleFlagBytePair=0x%04x;\n", $flag_value_bytepair;
push @dlltree, sprintf "const TInt KModuleFlagUncompressed=0x%04x;\n", $flag_value_uncompressed;
#push @dlltree, "#else\n";
#push @dlltree, sprintf "const TInt KModuleFlagXIP=0x%04x;\n", 0;	# no XIPs on emulator
#push @dlltree, "#endif\n";
push @dlltree, sprintf "const TInt KModuleFlagDllInCycle=0x%04x;\n", $flag_value_dll_in_cycle;
push @dlltree, sprintf "const TInt KModuleFlagDataInTree=0x%04x;\n", $flag_value_data_in_tree;
#push @dlltree, "#ifdef __EPOC32__\n";
push @dlltree, sprintf "const TInt KModuleFlagXIPDataInTree=0x%04x;\n", $flag_value_xip_data_in_tree;
#push @dlltree, "#else\n";
#push @dlltree, sprintf "const TInt KModuleFlagXIPDataInTree=0x%04x;\n", 0;
#push @dlltree, "#endif\n";
push @dlltree, sprintf "const TInt KModuleFlagExports=0x%04x;\n", $flag_value_exports;
push @dlltree, sprintf "const TInt KModuleFlagTargetOnly=0x%04x;\n", $flag_value_targetonly;
push @dlltree, sprintf "const TInt KModuleFlagPagedData=0x%04x;\n", $flag_value_pageddata;
push @dlltree, sprintf "const TInt KModuleFlagUnpagedData=0x%04x;\n", $flag_value_unpageddata;
push @dlltree, "static const TInt ModuleFlags[KNumModules] =\n";
push @dlltree, "\t\{\n";
foreach $modref (@modules) {
	my $flags=0;
	my @flagNames = ();

	push @flagNames, "KModuleFlagExe" if ($$modref{exe});
	push @flagNames, "KModuleFlagFixed" if ($$modref{fixed});
	push @flagNames, "KModuleFlagData" if ($$modref{data});
	push @flagNames, "KModuleFlagXIP" if ($$modref{xip});
	push @flagNames, "KModuleFlagPagedCode" if ($$modref{pagedcode});
	push @flagNames, "KModuleFlagUnpagedCode" if ($$modref{unpagedcode});
	push @flagNames, "KModuleFlagPagedData" if ($$modref{pageddata});
	push @flagNames, "KModuleFlagUnpagedData" if (!$$modref{pageddata});
	push @flagNames, "KModuleFlagIDrive" if ($$modref{idrive});
	push @flagNames, "KModuleFlagVDrive" if ($$modref{vdrive});
	push @flagNames, "KModuleFlagBytePair" if ($$modref{bytepair});
	push @flagNames, "KModuleFlagDllInCycle" if ($$modref{cycle});
	push @flagNames, "KModuleFlagDataInTree" if ($$modref{dataintree});
	push @flagNames, "KModuleFlagXIPDataInTree" if ($$modref{xipdataintree});
	push @flagNames, "KModuleFlagExports" if ($$modref{exports});
	push @flagNames, "KModuleFlagUncompressed" if ($$modref{uncompressed});
	push @flagNames, "KModuleFlagTargetOnly" if ($$modref{targetonly});

	@flagNames = qw(0) if (scalar(@flagNames) == 0);
	my $flagString = "/\* " . $$modref{num} . " \*/\t" . join(' | ', @flagNames);
	unless ($$modref{num}==$module_count-1) {
		$flagString.=',';
	}
	push @dlltree, $flagString . "\n";
}
push @dlltree, "\t\};\n";
push @dlltree, "\n";
foreach $modref (@modules) {
	my $modnum=$$modref{num};
	my @rblki;
	++$next_mark;
	calc_rblki(\@rblki, \@modules, $modref, $next_mark);
	my $rblki_count=scalar(@rblki);
	my $rblki_sum=0;
	foreach (@rblki) {
		$rblki_sum += $_;
	}
	push @dlltree, "static const TInt Module$modnum","RBlkIParams[2] = \{ $rblki_count, $rblki_sum \};\n";
}
push @dlltree, "\n";
push @dlltree, "static const TInt* const ModuleRBlkIParams[KNumModules] =\n";
push @dlltree, "\t\{\n";
foreach $modref (@modules) {
	my $modnum=$$modref{num};
	push @dlltree, "\tModule$modnum","RBlkIParams";
	if ($$modref{num}==$module_count-1) {
		push @dlltree, "\n";
	} else {
		push @dlltree, ",\n";
	}
}
push @dlltree, "\t\};\n";
push @dlltree, "\n";
foreach $modref (@modules) {
	my $modnum=$$modref{num};
	my $mod_attp=$modnum;
	my $mod_linkp=$modnum;
	unless ($$modref{exe}) {
		$mod_attp = ($$modref{attp}) ? ($modref->{attp}->{num}) : -1;
		$mod_linkp = ($$modref{linkexe}) ? ($modref->{linkexe}->{num}) : -1;
	}
	push @dlltree, "static const TInt Module$modnum","ExeInfo[2] = \{ $mod_attp, $mod_linkp \};\n";
}
push @dlltree, "\n";
push @dlltree, "static const TInt* const ModuleExeInfo[KNumModules] =\n";
push @dlltree, "\t\{\n";
foreach $modref (@modules) {
	my $modnum=$$modref{num};
	push @dlltree, "\tModule$modnum","ExeInfo";
	if ($$modref{num}==$module_count-1) {
		push @dlltree, "\n";
	} else {
		push @dlltree, ",\n";
	}
}
push @dlltree, "\t\};\n";

foreach $tcref (@testcases) {
	push @dlltree, "\n";
	my $modlistref=$$tcref{modules};
	my $count=$$tcref{count};
	my $arraysize=$count+1;
	push @dlltree, "static const TInt TC_$$tcref{name}\[$arraysize\]=\n\t\{\n";
	if ($count==0) {
		push @dlltree, "\t$count\n";
	} else {
		push @dlltree, "\t$count\,\n";
	}
	foreach (@$modlistref) {
		--$count;
		if ($count==0) {
			push @dlltree, "\t$_\n";
		} else {
			push @dlltree, "\t$_\,\n";
		}
	}
	push @dlltree, "\t\};\n";
}

push @dlltree, "#endif\n";
push @dlltree, "\n";
push @dlltree, "#endif\n";

foreach $modref (@modules) {
	my $mmpname=$$modref{basefilename}.$mmpext;
	my $mmpref=$$modref{mmp};
	open OUT, ">$mmpname" or die "Could not open $mmpname for output\n";
	print OUT @$mmpref;
	close OUT;
	if ($$modref{exports}) {
		my $defname=$$modref{basefilename}.$defext;
		my $defref=$$modref{def};
		open OUT, ">$defname" or die "Could not open $defname for output\n";
		print OUT @$defref;
		close OUT;
	}
}
if (!$allowbad) {
	push @bldinf, "t_ldrtst\n";
}
open OUT, ">$bldname" or die "Could not open $bldname for output\n";
print OUT @bldinf;
close OUT;
open OUT, ">$dlltreename" or die "Could not open $dlltreename for output\n";
print OUT @dlltree;
close OUT;
my $testbatch = "$ENV{EPOCROOT}epoc32\\build";
$destpath =~ s/\//\\/go;
$testbatch.="\\" unless ($destpath =~ /^\\/);
$testbatch.=$destpath;
$testbatch.="##MAIN##.auto.bat";
if (!$allowbad) {
	push @iby, "data=$testbatch\t\ttest\\loader.auto.bat\n";
}
open OUT, ">$ibyname" or die "Could not open $ibyname for output\n";
print OUT @iby;
close OUT;

#
# Accumulate list of dependency numbers
# 1st arg = \output list
# 2nd arg = \module list
# 3rd arg = \module to start from
# 4th arg = mark value to use
#
sub calc_tc($$$$) {
	my ($out, $mods, $modref, $mark)=@_;
	my $depsref=$$modref{deps};
	foreach $dep (@$depsref) {
		my $depref=$$mods[$dep];
		if ($$depref{mark} != $mark) {
			$$depref{mark}=$mark;
			unless ($$depref{exe}) {
				calc_tc($out, $mods, $depref, $mark);
			}
			push @$out, $dep;
		}
	}
}

#
# Accumulate RBlkI parameters
# 1st arg = \output list
# 2nd arg = \module list
# 3rd arg = \module to start from
# 4th arg = mark value to use
#
sub calc_rblki($$$$) {
	my ($out, $mods, $modref, $mark)=@_;
	if ($$modref{mark} != $mark) {
		$$modref{mark}=$mark;
		if (!$$modref{exe} and $$modref{data}) {
			push @$out, $$modref{num};
			my $depsref=$$modref{deps};
			foreach $dep (@$depsref) {
				calc_rblki($out, $mods, $$mods[$dep], $mark);
			}
		}
	}
}