package SymMake; use lib $ENV{RAZZLETOOLPATH} . "\\sp"; use lib $ENV{RAZZLETOOLPATH} . "\\PostBuildScripts"; use lib $ENV{RAZZLETOOLPATH}; use strict; use Carp; use IO::File; use Data::Dumper; use File::Basename; use File::Find; use Logmsg; # Data structure # pdbname.binext => [(var)pdbpath,size,$binext] # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * # # Package SymMake # - create symbols.inf, symbols(n).ddf and symbols.cdf # # in this version, the symmake.pm creates full package's and update package's symbols.inf, symbols.ddf and symbols.cdf # # for doing this, the symmake.pm first reads the symbol list from arch, then reads another one from symbolscd.txt; # every reading, it store the record to $self->{'SYM'} and store the new file extension to $self->{'EXT'} # # then, we according $pkinfoptr to write relate information to symbols.inf, symbols(n).ddf and symbols.cdf # # [Data Relations] # $pkinfoptr and $self->{'SYM'} are the two mainly hash we operating with # $pkinfoptr - the package information includes the cab, ddf, inf's file names and the file handles # $self->{'SYM'} - the symbols records that are from archive or ntpostbld # # basically, we looping for each record in $self->{'SYM'}, # according the $self->{'SYM'}->{"$symbol\.$installpath"}->[0] to get the kb term ('ARCH' or 'NTPB') # then, we use %revpktypes to get $pktype ('FULL' or 'UPDATE') # # or, we according $pktype ('FULL' or 'UPDATE') to get kb term from $pktypes # then, we can access the root of arch or ntpostbld ($self->{'KB'}->{$pktype}) # # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * # # $DDFHandle - the .ddf file handle for writing symbols(n).ddf # $CATHandle - the .cat file handle for writing content to .cdf for creating .cat(alog) # $INFHandle - the .inf file handle for writing symbols.inf # my ($DDFHandle, $CATHandle, $INFHandle); # # Package Types # FULL - from ARCH # UPDATE - from NTPOSTBLD # my %pktypes = ( FULL => 'ARCH', UPDATE => 'NTPB' ); # # %revpktypes = ( # reverse the key - value relation for %pktypes # 'ARCH' => 'FULL' # 'NTPB' => 'UPDATE' # ) # my %revpktypes = reverse %pktypes; # # $obj = new SymMake $archpath, $ntpostbldpath # - object creator # # ( we assign the root and use the relation path in symbolcd.txt and archiver dump list ) # return: # $obj-> # 'KB' -> # Knowledge Base # 'ARCH' => \\arch\archive\.... # 'NTPB' => %_NTPOSTBLD% # 'SYM' -> (empty) # The file lists of symbols, hash structure is listed in below: # # $self->{'SYM'}-> # # $symbol\.$installpath -> # such as 'ntoskrnl.pdb.exe' # # [kbterm, symbol subpath, symbol filesize, $installpath] # such as ['ARCH', "\\symbols\\retail\\exe\\ntoskrnl.pdb", 4189184, 'exe'] # 'EXT' -> (empty) # The file lists of symbols' file extension, hash structu is listed in below: # # $self->{'EXT'}-> # # $pktype -> #such as 'FULL' # # $installpath -> 1 # such as 'EXE' -> 1 # 'HANDLE' -> (empty) # The file handles # 'PKTYPE' -> undef # package type # sub new { my $class = shift; my $instance = { KB => { "$pktypes{'FULL'}" => $_[0], "$pktypes{'UPDATE'}" => $_[1] }, SYM => {}, EXT => {}, HANDLE => {}, PKTYPE => undef }; return bless $instance, $class; } # # $obj->ReadSource($symbolcd) # - read the source file ($symbolcd) to $self->{'SYM'} and $self->{'EXT'} # IN: $symbolcd # REF: $self->{'PKTYPE'} : current package type # # # sub ReadSource { my ($self, $symbolcd) = @_; my ($fh, $kbterm, $mykey, @mylist); local $_; $kbterm = $pktypes{$self->{'PKTYPE'}}; $symbolcd = "$self->{'KB'}->{$kbterm}\\symbolcd\\symbolcd.txt" if (!defined $symbolcd); $symbolcd = "$ENV{'TEMP'}\\symbolcd.txt" if (!-e $symbolcd); if ($self->{'PKTYPE'} =~ /FULL/i) { # # for full package, we look for $symbolcd exist # if exist, we load it # if (-e $symbolcd) { ($self->{'SYM'}, $self->{'EXT'}) = @{do $symbolcd}; # do command will load the script and evaluate (similar as 'require' command) } else { # # if not exist, we create one in run time from archive path ($self->{'KB'}->{$kbterm}) # $self->HashArchServer($self->{'KB'}->{$kbterm}); # # for reuse it next time, we store it to $ENV{'TEMP'}\\symbolcd.txt # # so, if we want to create one and store to archive server, we can do below: # [a.pl] # use SymMake; # ($the_archived_source_root, $my_dump_path_in_archive) = @ARGV; # $symmake = new SymMake $the_archived_source_root, $ENV{_NTPOSTBLD}; # $symmake->ReadSource("$ENV{'TEMP'}\\symbolcd.txt"); # sytem("copy \"$ENV{'TEMP'}\\symbolcd.txt\" $my_dump_path_in_archive # and call something similar as: # perl a.pl "\\\\arch\\archive\\ms\\windows\\windows_xp\\rtm\\2600\\$BuildType\\all\\$BuildArch\\pub" "\\\\arch\\archive\\ms\\windows\\windows_xp\\rtm\\2600\\$BuildType\\all\\$BuildArch\\pub\\symbolcd\\symbolcd.txt" # $Data::Dumper::Indent=1; $Data::Dumper::Terse=1; $fh = new IO::File $symbolcd, 'w'; if (!defined $fh) { logmsg "Cannot open $symbolcd\."; } else { print $fh 'return ['; print $fh Dumper($self->{'SYM'}); print $fh ",\n"; print $fh Dumper($self->{'EXT'}); print $fh '];'; $fh->close(); } } } else { # # if is from update, we store $self->{'SYM'} and $self->{'EXT'} from symbolcd.txt # $self->HashSymbolCD($symbolcd); } return; # # $Data::Dumper::Indent=1; # $Data::T # print Dumper($self->{'SYM'}, qw(sym) } # # $obj->HashSymbolCD($file) # - store symbolcd.txt($file) to $self->{'SYM'} hash and $self->{'EXT'} hash # # IN: $file - the full filespec of the symbolcd.txt # OUT: none # sub HashSymbolCD { my ($self, $file) = @_; my ($fh, $bin, $symbol, $subpath, $installpath, $kbterm); local $_; $kbterm = $pktypes{$self->{'PKTYPE'}}; $fh = new IO::File $file; if (!defined $fh) { logmsg "Cannot open symbolcd.txt ($file)"; return; } while(<$fh>) { chomp; ($bin,$symbol,$subpath,$installpath)=split(/\,/,$_); $self->{'SYM'}->{lc"$symbol\.$installpath"} = [$kbterm, "\\" . $subpath, (-s $self->{'KB'}->{$kbterm} . '\\' . $subpath), lc$installpath]; for (keys %pktypes) { $self->{'EXT'}->{$_}->{lc$installpath} = 1; } } $fh->close(); } # # $obj->HashArchServer($path) # - store the list of symbols under $path to $self->{'SYM'} hash and $self->{'EXT'} hash # # IN: $path - the full path of the symbols (such as "\\\\arch\\archive\\ms\\windows\\windows_xp\\rtm\\2600\\$BuildType\\all\\$BuildArch\\pub") # OUT: none # sub HashArchServer { my ($self, $path) = @_; my ($fh, $bin, $symbol, $subpath, $installpath, $kbterm, $pdbsize); local $_; $kbterm = $pktypes{$self->{'PKTYPE'}}; $fh = new IO::File "dir /s/b/a-d $path\\*.*|"; if (!defined $fh) { logmsg "Cannot access to $path\."; } while (<$fh>) { chomp; $pdbsize = (-s); $_ = substr($_, length($path) + 1); /\\/; ($symbol, $subpath, $installpath) = ($',$_,$`); $self->{'SYM'}->{lc"$symbol\.$installpath"} = [$kbterm, '\\' . $subpath, $pdbsize, $installpath]; $self->{'EXT'}->{$self->{'PKTYPE'}}->{$installpath} = 1; } $fh->close(); } # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * # # for Create_Symbols_CDF, and Create_Symbols_INF # # we create two writer, one is writing to update's and full package's files, # another is only writing to full package's file # # so, when we call &{$mywriter{'FULL'} - it only write to full package's file # when we call &{$mywriter{'UPDATE'} - it write to both files # # and, $mysepwriter is for writing separately # $mysepwriter{'FULL'} - write to full package # $mysepwriter{'UPDATE'} - write to update package # # we base on the writer function to loop our big symbols hash ($self->{'SYM'}) one time to # generate two documents # # for Create_Symbols_DDF # # we only need to write each record to one target file (that either in full package, or update # package to create symbols.cab). So, we don't use writer to write the DDF # # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * # # Create_Symbols_CDF($pkinfoptr) # - Create symbols.CDF which is for creating the symbols.cat # # IN: $pkinfoptr-> # see RegisterPackage() # $pkname -> # such as 'FULL' or 'UPDATE' # 'CDFHANDLE' -> cdf file handle # 'CATNAME' -> the symbols catalog file name # 'INFNAME' -> the symbols inf file name # sub Create_Symbols_CDF { my ($self, $pkinfoptr) = @_; my ($mykbterm, $mypkname, $fhandle, $fullpdb, %mywriter); local $_; &Open_Private_Handle($pkinfoptr, 'CDF'); for $mypkname (keys %{$pkinfoptr}) { # create the writer if ($mypkname ne 'FULL') { $mywriter{$mypkname} = &Writer($pkinfoptr->{$mypkname}->{'CDFHANDLE'}, $pkinfoptr->{'FULL'}->{'CDFHANDLE'}); } else { $mywriter{$mypkname} = &Writer($pkinfoptr->{'FULL'}->{'CDFHANDLE'}); } # write the head to each cdf file handle &Create_CDF_Head($pkinfoptr->{$mypkname}->{'CDFHANDLE'}, $pkinfoptr->{$mypkname}->{'CATNAME'}, $pkinfoptr->{$mypkname}->{'INFNAME'}); } # write each record for (sort keys %{$self->{'SYM'}}) { $mykbterm = $self->{'SYM'}->{$_}->[0]; $mypkname = $revpktypes{$mykbterm}; $fullpdb = $self->{'KB'}->{$mykbterm} . $self->{'SYM'}->{$_}->[1]; &{$mywriter{$mypkname}}("\" . $fullpdb . '=' . $fullpdb . "\n"); } &Close_Private_Handle($pkinfoptr, 'CDF'); } # # Create_Symbols_DDF($pkinfoptr) # - Create symbols.DDF which is for creating symbols.cab # # IN: $pkinfoptr-> # see RegisterPackage() # $pkname -> # 'CABNAME' => # 'CABDEST' => # 'CABSIZE' => # 'DDFNAME' => # 'CABCOUNT' => # 'DDFLIST' => (return cab list) # 'DDFHANDLE' => # sub Create_Symbols_DDF { my ($self, $pkinfoptr) = @_; my ($symkey, $symptr, $kbterm, $subpath, $pktype, $mypkinfoptr, $cabname, $ddfname, $cabcount, $DDFHandle, $myddfname, $mycabname); local $_; # initialization map({$_->{'CURSIZE'} = $_->{'CABSIZE'}} values %{$pkinfoptr}); for (sort keys %{$self->{'SYM'}}) { $symkey = $_; $symptr = $self->{'SYM'}->{$_}; # base on $self->{'SYM'}->{$symbol\.$installpath}->[0] to reference %revpktypes and get the package type ($pktype) ($kbterm, $subpath) = ($symptr->[0],$symptr->[1]); $pktype = $revpktypes{$kbterm}; # don't generate something not specify next if (!exists $pkinfoptr->{$pktype}); $mypkinfoptr = $pkinfoptr->{$pktype}; # # According uncompressed files total size to seperate the cab # 'CURSIZE' - current size # 'CABSIZE' - is the approx. size # $mypkinfoptr->{'CURSIZE'}+=$symptr->[2]; # if this cab list (ddf) is full if ($mypkinfoptr->{'CURSIZE'} >= $mypkinfoptr->{'CABSIZE'}) { # initial the current size to the current symbol's file size $mypkinfoptr->{'CURSIZE'} = $symptr->[2]; ($cabname, $ddfname, $cabcount) = ( $mypkinfoptr->{'CABNAME'}, $mypkinfoptr->{'DDFNAME'}, ++$mypkinfoptr->{'CABCOUNT'} ); $myddfname = $ddfname . $cabcount . '.ddf'; $mycabname = $cabname . $cabcount . '.cab'; # create the new file and its DDF file head # the old file will automatically close by Perl $mypkinfoptr->{'DDFHANDLE'} = new IO::File $myddfname, 'w'; if (!defined $mypkinfoptr->{'DDFHANDLE'}) { logmsg "Cannot open DDF file $myddfname\."; } &Create_DDF_Head($mypkinfoptr->{'DDFHANDLE'}, $mycabname); $mypkinfoptr->{'DDFLIST'}->{$myddfname} = $mycabname; } # write the record to the ddf file handle $DDFHandle = $mypkinfoptr->{'DDFHANDLE'}; print $DDFHandle '"' . $self->{'KB'}->{$kbterm} . $subpath . '" "' . $symkey . "\"\n"; } &Close_Private_Handle($pkinfoptr, 'DDF'); } # # Create_Symbols_INF($pkinfoptr) # - Create symbols.inf # # # pkinfoptr-> # see RegisterPackage() # $pkname -> # INFNAME => # CDFNAME => # CABNAME => # CATNAME => # INFHANDLE => # sub Create_Symbols_INF { my ($self, $pkinfoptr) = @_; my ($mypkname, $mypkinfoptr, $INFHandle, %mywriter, %mysepwriter, %h, %cabnames); local $_; &Open_Private_Handle($pkinfoptr, 'INF'); for $mypkname (keys %{$pkinfoptr}) { ($mypkinfoptr, $INFHandle) = ($pkinfoptr->{$mypkname}, $pkinfoptr->{$mypkname}->{'INFHANDLE'}); # create the writer if ($mypkname ne 'FULL') { $mywriter{$mypkname} = &Writer($INFHandle, $pkinfoptr->{'FULL'}->{'INFHANDLE'}); } else { $mywriter{$mypkname} = &Writer($INFHandle); } $mysepwriter{$mypkname} = &Writer($INFHandle); &Create_INF_Version($INFHandle, $mypkinfoptr->{'CATNAME'}); &Create_INF_Install($INFHandle, $self->{'EXT'}->{$mypkname}); $cabnames{$mypkname} = (FileParse($mypkinfoptr->{'CABNAME'}))[0]; } # the extension tag (ex. [FILES.EXE]) needs to write separately depends on update / full package has this extension's symbols or not # so, we need to use separately writer for this kind information &Create_INF_Files($self->{'SYM'}, \%mysepwriter, \%mywriter); &Create_INF_SourceDisks($self->{'SYM'}, \%cabnames, \%mysepwriter, \%mywriter); &Close_Private_Handle($pkinfoptr, 'INF'); } # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * # # Create_DDF_Head - create the .ddf's file head for symbols(n).ddf # Create_CDF_Head - create the .cdf's file head for symbols.cdf # Create_INF_Version - create the .inf's Version section # Create_INF_Install - create the .inf's [DefaultInstall], [DefaultInstall.Quiet] # [BeginPromptSection], [EndPromptSection], [RegVersion] # [SymCust], [CustDest], [CustDest.2], [DestinationDirs] # # Create_INF_Files - create the .inf's [Files.] sections # Create_INF_SourceDisks - create the [SourceDisksNames], [SourceDisksFiles] # # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * sub Create_DDF_Head { my ($DDFHandle, $cabname) = @_; my ($mycabname, $mycabdest) = FileParse($cabname); print $DDFHandle <$infname\.inf=$infname\.inf CDFHEAD } sub Create_INF_Version { my ($INFHandle, $catname) = @_; $catname = (FileParse($catname))[0]; print $INFHandle <{$a}->[3] cmp $symptr->{$b}->[3]) or ($a cmp $b)} keys %{$symptr}) { $mykbterm = $symptr->{$_}->[0]; $mypkname = $revpktypes{$mykbterm}; # if is a new tag name, if ($symptr->{$_}->[3] ne $tags{$mypkname}->[0]) { $tags{$mypkname} = [$symptr->{$_}->[3], - length($symptr->{$_}->[3]) -1]; &{$sepwriter->{$mypkname}}("\n\[Files\.$tags{$mypkname}->[0]\]\n"); } # if its from update list and different with the current full list's file extension # we need to create a new tag in full list if ($symptr->{$_}->[3] ne $tags{'FULL'}->[0]) { $tags{'FULL'} = [$symptr->{$_}->[3], - length($symptr->{$_}->[3]) -1]; &{$sepwriter->{'FULL'}}("\n\[Files\.$tags{'FULL'}->[0]\]\n"); } # $popwriter will write update ($mypkname) to both files and full to full package list &{$popwriter->{$mypkname}}(substr($_, 0, $tags{$mypkname}->[1]) . "\,$_\,\,4\n"); } } sub Create_INF_SourceDisks { my ($symptr, $cabnameptr, $sepwriter, $popwriter) = @_; # $pkinfoptr) = @_; my ($INFHandle, $cabname, $mypkname); local $_; for (keys %{$cabnameptr}) { $cabname = $cabnameptr->{$_}; &{$sepwriter->{$_}}(<{$_}->[0]}; &{$popwriter->{$mypkname}}($_ . "=1\n"); } } # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * # # Reusable subruntines # # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * # # RegisterPackage($pkinfoptr, $pktype,$hptr) # - register $hptr to $pkinfoptr->{$pktype} # - registerpackage is the function to verify and store $pkinfoptr # - the cabname, ddfname, infname, cdfname, catname - are equal to 'SYMBOLS' normally # - cabsize is the total size for include files into one cab, we cannot compute the compressed # file size, so we compute with the uncompress file size # # - cabhandle, ddfhandle, infhandle - the file handles for .cab, .ddf and .inf; we create them # inside this module # - DDFLIST - the lists of the ddf files we plan to create # # $pkinfoptr-> # $pktype -> # CABNAME # DDFNAME # INFNAME # CDFNAME # CATNAME # CABSIZE # # CABHANDLE - (reserved) # DDFHANDLE - (reserved) # INFHANDLE - (reserved) # # DDFLIST - (return) # sub RegisterPackage { my ($pkinfoptr, $pktype, $hptr) = @_; my ($mykey); my @chklists = qw(CABNAME DDFNAME INFNAME CDFNAME CATNAME CABSIZE); # register to $pkinfoptr $pkinfoptr->{$pktype} = $hptr; # check we have enough information for $mykey (@chklists) { die "$mykey not defined in $pktype" if (!exists $pkinfoptr->{$pktype}->{$mykey}); } } # # $obj = Writer(@handles) # - the writer generator; generates a function to write one data to each @handles # keep @handles in parent program and $_[0] in sub {....} is the data we send to &{$obj}($data) # sub Writer { my (@handles) = @_; my ($hptr)=\@handles; return sub { my ($myhandle); for $myhandle (@{$hptr}) { print $myhandle $_[0]; } }; } # # Open_Private_Handle($pkinfoptr, $ftype) # - open each pkname of each $ftype name's file and store the handle to $pkinfoptr->{$pkname}->{$ftype . 'HANDLE'} # - for example, if we call Open_Private_Handle($pkinfoptr, 'DDF') # - it open $pkinfoptr->{'FULL'}->{'DDFNAME'} and $pkinfoptr->{'UPDATE'}->{'DDFNAME'} # - and store the file handle to $pkinfoptr->{'FULL'}->{'DDFHANDLE'} and $pkinfoptr->{'UPDATE'}->{'DDFHANDLE'} # sub Open_Private_Handle { my ($pkinfoptr, $ftype) = @_; my ($pkname); for $pkname (keys %{$pkinfoptr}) { $pkinfoptr->{$pkname}->{$ftype . 'HANDLE'} = new IO::File $pkinfoptr->{$pkname}->{$ftype . 'NAME'} . '.' . $ftype, 'w'; if (!defined $pkinfoptr->{$pkname}->{$ftype . 'HANDLE'}) { logmsg "Cannot open " . $pkinfoptr->{$pkname}->{$ftype . 'NAME'} . '.' . $ftype . "."; } } } sub Close_Private_Handle { my ($pkinfoptr, $ftype) = @_; my ($pkname); for $pkname (keys %{$pkinfoptr}) { $pkinfoptr->{$pkname}->{$ftype . 'HANDLE'}->close() if (defined $pkinfoptr->{$pkname}->{$ftype . 'HANDLE'}); delete $pkinfoptr->{$pkname}->{$ftype . 'HANDLE'}; } } sub FileParse { my ($name, $path, $ext) = fileparse(shift, '\.[^\.]+$'); $ext =~ s/^\.//; return $name, $path, $ext; } 1;