@REM ----------------------------------------------------------------- @REM @REM findinfdata - jtolman @REM Go through inf files and find install data for each file. @REM @REM Copyright (c) Microsoft Corporation. All rights reserved. @REM @REM ----------------------------------------------------------------- @perl -x "%~f0" %* @goto :EOF #!perl use strict; use lib $ENV{RAZZLETOOLPATH} . "\\PostBuildScripts"; use lib $ENV{RAZZLETOOLPATH} . "\\sp"; use lib $ENV{RAZZLETOOLPATH}; use Logmsg; use ParseArgs; use InfData; use InfParse; use CanonDir qw(%misses addPrefix removePrefix); sub Usage { print< /f /f: File to output the results to. /c: Use information from a change file. /w Include WOW files. /xs Ignore server skus. /xc Ignore client skus. /i Remake infscan.lst using infscan before reading it. /o Write using original directory structure. /s Read infs from service pack directory structure. /b Read infs from the binaries directory. /l Language to run for. USAGE my ($outfile, $dir, $infscan, $altpath, $template, $newfile, $wow, $binaries, $lang, $servpack); my @changes; my ($xserver, $xclient); parseargs('?' => \&Usage, 'f:'=> \$outfile, 'c:'=> \@changes, 'w' => \$wow, 'xs'=> \$xserver, 'xc'=> \$xclient, 'i' => \$infscan, 'o' => \$altpath, 's' => \$servpack, 'b' => \$binaries, 'l:'=> \$lang, \$dir ); undef $wow if $binaries and lc$ENV{_BUILDARCH} ne "ia64"; $wow = 1 if $binaries and lc$ENV{_BUILDARCH} eq "ia64"; if ($altpath) { undef $altpath; } else { $altpath = 1; } my $arch = $wow ? "ia64":"i386"; my $barch = $wow ? "ia64":"x86"; my $archd = $wow ? "ia64":"w32x86"; $lang = $ENV{LANG} if !defined $lang; $lang = "usa" if !defined $lang or $lang eq ""; my $slp = ""; my $slpdir; my $bindir; my $wowdir; if ( $binaries ) { $bindir = "$dir"; $wowdir = "$dir\\wowbins"; } elsif ( $servpack ) { $bindir = "$dir\\bin"; $slpdir = "$dir\\slp"; $wowdir = "$dir\\bin\\wowbins"; } else { $bindir = "$dir\\bin"; $slpdir = "$dir"; $wowdir = "$dir\\bin\\wowbins_uncomp"; } ###################################################################### # Define main data structures. my %files; # Main table of files and directories. my %relfiles; # All files with multiple versions. # List of skus to examine: BitMask SkuName CongealScriptsName my $skumax = 7; # Max # of bits in the this list. Change as needed. my @skus = (); my @skuletters = (); if ( !$xclient ) { push @skus, "1, pro, wks"; push @skus, "2, per, per" if $arch ne "ia64"; push @skuletters, ("p", "c"); } if ( !$xserver ) { push @skus, "4, srv, srv" if $arch ne "ia64"; push @skus, "8, ads, ent"; push @skus, "16, dtc, dtc"; # push @skus, "32, sbs, sbs" if $arch ne "ia64"; # push @skus, "64, bla, bla" if $arch ne "ia64"; push @skuletters, ("s", "a", "d"); # push @skuletters, ("l", "b"); } my $skumask = 0; my $skucount = 0; my $type; my $prod; my $boot; CanonDir::setup( $archd, $arch ); # List of info on relative paths to use with /r. my %relpath; if ( $altpath ) { %relpath = ( 0x1 => "ip\\", 0x2 => "ic\\", 0x4 => "is\\", 0x8 => "ia\\", 0x1c => "is\\", # Remove when il and ib are used. 0x7c => "is\\", 0x18 => "ia\\", 0x10 => "id\\", 0x20 => "il\\", 0x40 => "ib\\", ); } else { %relpath = ( 0x2 => "perinf\\", 0x7c => "srvinf\\", 0x18 => "entinf\\", 0x10 => "dtcinf\\", 0x20 => "sbsinf\\", 0x40 => "blainf\\", ); } my %infpath = ( "proinf" => 0x1, "perinf" => 0x2, "srvinf" => 0x7c, "entinf" => 0x18, "dtcinf" => 0x10, "blainf" => 0x20, "sbsinf" => 0x40 ); my @pathorder = ("sbsinf","blainf","dtcinf","perinf","entinf","srvinf", ""); my %buildorder = ( "pro" => "\\", "per" => "\\ \\perinf\\", "srv" => "\\ \\srvinf\\", "bla" => "\\ \\srvinf\\ \\blainf\\", "sbs" => "\\ \\srvinf\\ \\sbsinf\\", "ads" => "\\ \\srvinf\\ \\entinf\\", "dtc" => "\\ \\srvinf\\ \\entinf\\ \\dtcinf\\", ); # Format: ANSI_Codepage LCID my %langinfo = ( "ara" => 1256, "br" => 1252, "chh" => 950, "chs" => 936, "cht" => 950, "cs" => 1250, "da" => 1252, "el" => 1253, "es" => 1252, "fi" => 1252, "fr" => 1252, "ger" => 1252, "heb" => 1255, "hu" => 1250, "it" => 1252, "jpn" => 932, "kor" => 949, "nl" => 1252, "no" => 1252, "pl" => 1250, "psu" => 1253, "pt" => 1252, "ru" => 1251, "sv" => 1252, "tr" => 1254, "usa" => 1252 ); my $cpage = 1252; $cpage = $langinfo{$lang} if exists $langinfo{$lang}; my %layids; # List of dirid definitions for layout.inf. my %unknowns; # Used to keep track of warnings. ###################################################################### # Subroutines used for managing data in the %files data structure. # Add an installation directory to a file's list. sub addEntry { my ($source, $dirid, $path, $name, $bit) = @_; $source = lc $source; $name = lc $name; my $src = $source; $src = "$3$1" if $source =~ /^([^\\]*)(\\(.*\\))?/; my $entry = InfData->new($src, $dirid, $path, $name, $bit); foreach my $ent ( @{ $files{$source} } ) { return if $ent->addBit($entry); } if ( !exists $files{$source} ) { if ($dirid != 65619) { $files{$source} = [ () ]; } else { $unknowns{"$source\t$name"} = ""; return; } } push @{ $files{$source} }, $entry; } # Get a list of a file's installation directories. sub getEntries { my ($source) = @_; if ( !exists $files{$source} ) { print LOG "Unknown file: $source\n"; return (); } return @{ $files{$source} }; } ###################################################################### # Start processing. # Create a log file. $outfile =~ /^(.*\\)?([^\\]*)$/; my $outdir = $1; my $logfile; $logfile = "$outdir\Efindinfdata.$arch.$lang.log" if !defined $binaries; $logfile = "$ENV{TEMP}\\findinfdata.$arch$ENV{_BuildType}.$lang.out" if defined $binaries; if ( !open LOG, ">$logfile" ) { errmsg( "Unable to open logfile: $logfile" ); die; } # Run infscan for each sku, as needed. my $curbuild = ""; my $tmp = "$ENV{TEMP}\\infscan.$arch$ENV{_BuildType}.$lang"; foreach my $skudata ( @skus ) { my ($bit, $sku, $cgsku) = split(/\s*,\s*/, $skudata); my $infscanfile; $infscanfile = "$outdir\Einfscan.$arch.$lang.$sku.lst" if !defined $binaries; $infscanfile = "$ENV{TEMP}\\infscan.$arch$ENV{_BuildType}.$lang.$cgsku.lst" if defined $binaries; my $infdir; $infdir = "$slpdir\\$sku\\$arch"; if ( $infscan or !-f $infscanfile ) { logmsg( "Scanning infs for $sku..." ); my $lg; $lg = "$outdir\Escan.$arch.$lang.$sku.log" if !defined $binaries; $lg = "$ENV{TEMP}\\infscan.$arch$ENV{_BuildType}.$lang.$cgsku.log" if defined $binaries; my $filt; $filt = "$outdir\Efilter.$arch.$lang.$sku.inf" if !defined $binaries; $filt = "$ENV{TEMP}\\filter.$arch$ENV{_BuildType}.$lang.$cgsku.inf" if defined $binaries; if ( !$binaries ) { system "rmdir /s /q $tmp >nul 2>&1"; system "mkdir $tmp >nul 2>&1"; system "copy $infdir\\*.inf $tmp > $lg 2>nul"; system "copy $infdir\\*.in_ $tmp >> $lg 2>nul"; system "copy $infdir\\lang\\*.inf $tmp >> $lg 2>nul"; system "copy $infdir\\lang\\*.in_ $tmp >> $lg 2>nul"; system "expand -r $tmp\\*.in_ >nul 2>nul"; system "del /q $tmp\\*.in_ >nul 2>nul"; } else { my $build = $buildorder{$sku}; my $temp = $build; $build =~ s/^\Q$curbuild \E//i; $curbuild = $temp; my @build = split(" ", $build); foreach my $loc ( @build ) { logmsg "Copying from $loc."; if ( $loc eq "\\" ) { system "rmdir /s /q $tmp >nul 2>&1"; system "mkdir $tmp >nul 2>&1"; system "copy $bindir\\lang\\*.inf $tmp >> $lg 2>nul"; } my $infdir = "$bindir$loc"; system "copy $infdir*.inf $tmp > $lg 2>nul"; } } if ( $cpage ) { foreach my $file ( `dir /b /s $tmp\\*.inf` ) { chomp $file; system "unitext -u -$cpage -z $file $file\.2 >nul 2>nul"; system "move /y $file\.2 $file >nul 2>nul"; } } logmsg( "Generating filter.inf..." ); system "infscan /R /G /V ntx86 /D /C $filt $tmp >> $lg 2>&1"; logmsg( "Running infscan..." ); system "infscan /G /F $filt /Q $infscanfile $tmp >> $lg 2>&1"; } } system "rmdir /s /q $tmp >nul 2>nul"; # Do infscan and layout.inf parse for each sku. foreach my $skudata ( @skus ) { my ($bit, $sku, $cgsku) = split(/\s*,\s*/, $skudata); logmsg( "Processing sku $sku:" ); $skumask |= $bit; $skucount++; # Read in the infscan information. logmsg( "Reading infscan information..." ); my $infscanfile; $infscanfile = "$outdir\Einfscan.$arch.$lang.$sku.lst" if !defined $binaries; $infscanfile = "$ENV{TEMP}\\infscan.$arch$ENV{_BuildType}.$lang.$cgsku.lst" if defined $binaries; if ( !open INFSCAN, $infscanfile ) { errmsg( "Couldn't find $infscanfile" ); die; } while ( ) { if ( /^\"([^\"]*)\",(\d*),(\"([^\"]*)\")?,\"([^\"]*)\",(\d),\"([^\"]*)\"(,\"([^\"]*)\")?\s*$/ ) { # " my ($source, $dirid, $subdir, $target, $flag, $inf, $sect) = ($1,$2,$4,$5,$6,$7,$9); if ( $dirid eq "" ) { $unknowns{"$source\t$target"} = ""; next; } $source = "$2\\$1" if $source =~ /^(.*\\)([^\\]*)$/; $subdir = addPrefix($dirid, lc $subdir, %CanonDir::scanids); ($dirid, $subdir) = removePrefix($subdir, %CanonDir::revids); addEntry($source, $dirid, $subdir, $target, $bit); } else { chomp; print LOG "Line omitted: $_\n" } } close INFSCAN; # Read in the layout.inf information. logmsg( "Reading layout.inf information..." ); my $infdir; $infdir = "$bindir" if $sku eq "pro"; $infdir = "$bindir\\$cgsku\Einf" if $sku ne "pro"; my %layf; if ( open INF, "$infdir\\layout.inf" ) { $_ = parseSect(sub { return; }); while ( /^\[/ ) { if ( /^\s*\[SourceDisksFiles(\.\w*)?\]\s*$/i and ($1 eq "" or lc$1 eq ".$barch") ) { # Process a list of files and data. $_ = parseSect(sub { my ($layf, $data) = split(/\s*=\s*/,$_[0]); my @fields = split(/\s*,\s*/, $data); $layf{lc $layf} = [ @fields ]; } ); } elsif ( /^\s*\[WinntDirectories\]\s*$/i ) { # Process a list of directories and their numbers. $_ = parseSect(sub { my ($num, $dest) = split(/\s*=\s*/); $dest = $1 if $dest=~/^\"\\?([^\"]*)\"$/; # " $layids{$num} = lc $dest; } ); } elsif ( /^\s*\[Strings\]\s*$/i ) { # Process a list of localization strings. $_ = parseSect( \&parseStr ); } else { $_ = parseSect(sub { return; }); } } close INF; # Insert strings into directory names. foreach my $num (keys %layids) { $layids{$num} = strSub($layids{$num}); } # Figure out where the files go. foreach my $file (keys %layf) { my @data = @{ $layf{$file} }; my $target = strSub($data[10]); $target = $file if $target eq ""; if ( $file =~ /\.cat$/i ) { addEntry($file, 65619, "", $target, $bit); addEntry($file, 65535, ".cat", $target, $bit); next; } my $ofile = $file; $file = "$2\\$1" if $file =~ /^(.*\\)([^\\]*)$/; my $dirid = $data[7]; if ( $dirid ne "" ) { my $path = addPrefix($dirid, "", %layids); ($dirid, $path) = removePrefix($path, %CanonDir::revids); addEntry($file, $dirid, $path, $target, $bit); } else { $unknowns{"$ofile\t$target"} = ""; } } undef %InfParse::strings; undef %layids; } # Determine what entries go in Cache.files. logmsg( "Reading cache file information..." ); my $cachedir = "$bindir\\congeal_scripts\\autogen\\"; if ( open CACHE, "$cachedir$barch\_$cgsku.h" ) { while ( ) { next if !/^\s*{\s*(NULL|L\"([^\"]*)\"\s*),\s*L\".*\\\\([^\\\"]*)\"\s*,.*},?\s*$/; # " my $src = $3; $src = $2 if defined $2 and $2 ne ""; $src =~ /^(.*\\)?([^\\]*)$/; my $dest = $2; $src = "$2\\$1" if defined $1; addEntry($src, 65619, "", $dest, $bit); } close CACHE; } } # Print various warnings. foreach my $key (keys %misses) { my $count = $misses{$key} / $skucount; print LOG "Unknown dirID ($count+ files affected): $key\n"; } undef %misses; foreach my $file (sort keys %unknowns) { $file = lc $file; my ($source, $target) = split(/\t/, $file); if ( !defined $files{$source} ) { print LOG "Directory not known: $source\n"; next; } next if $target eq $source; my $test = 0; foreach my $entry (getEntries($source)) { $test = 1 if lc $entry->{NAME} eq $target; } print LOG "Lost target name for $source: $target\n" if !$test; } undef %unknowns; # Handle WOW files. if ( $arch eq "ia64" ) { # Get a list of wow files to generate. logmsg( "Examining WOW files..." ); if ( !-d $wowdir ) { errmsg( "Unable to find wow directory." ); die; } foreach my $wow (`dir /b /s /a-d $wowdir\\w*`) { $wow =~ s/^\Q$wowdir\E\\//; if ( $wow =~ /^\s*(.*\\)?w([^\\]*[^\\\s])\s*$/ ) { my $file = $2; my $wfile = "w$2"; my $dir = $altpath ? "wow\\$1":"wowbins_uncomp\\$1"; my $key = $wfile; $key = "$wfile\\$dir" if exists $files{"$wfile\\$dir"}; if ( exists $files{$key} ) { foreach my $entry ( @{ $files{$key} } ) { $entry->setSrcDir($dir); } my $entries = $files{$key}; delete $files{$key}; $files{"$wfile\\$dir"} = $entries; } } } } # Put appropriate stuff in the lang directory. my %temp; if ( !$binaries ) { my $prodir = "$slpdir\\pro\\$arch"; foreach my $file ( `dir /a-d /b $prodir` ) { $file =~ s/^\Q$prodir\E\\//i; $file =~ s/\s*$//; $temp{lc $file} = ""; } } my $langdir = "$bindir\\lang"; foreach my $file ( `dir /a-d /b $langdir` ) { $file =~ s/^\Q$langdir\\//i; $file =~ s/\s*$//; $file = lc $file; next if $file =~ /\\/; next if !exists $files{$file}; $file =~ /^([^\.]*(\..?.?)).?$/; next if exists $temp{$file} or exists $temp{"$1_"}; my $list = $files{$file}; delete $files{$file}; foreach my $entry ( @$list ) { $entry->setSrcDir("lang"); } $files{"$file\\lang\\"} = $list; } undef %temp; if ( $altpath ) { foreach my $val ( keys %relpath ) { delete $relpath{$val} if $val == $skumask; } } # Parse a command file, if specified. logmsg( "Processing change files..." ); @changes = () if !defined @changes; foreach my $changefile ( @changes ) { if ( !open CHANGE, $changefile ) { errmsg( "Unable to open $changefile." ); die; } while ( ) { next if /^\s*(\#.*)?$/; if ( /^\s*([^\=]*\S)\s*=\s*(.*\S)\s*$/ ) { my $filename = lc $1; my $file = $filename; my $cmd = lc $2; $file = "$2\\$1\\" if $file =~ /^(.*)\\([^\\]*)$/; if ( defined $files{$file} ) { if ( $cmd eq "dontwait" ) { # This file should be copied before reboot. foreach my $entry ( @{ $files{$file} } ) { $entry->{WHEN} = "dontdelayuntilreboot"; } } elsif ( $cmd =~ /^type\s*:\s*(.*\S)\s*$/ ) { # This file belongs to a non-obvious inf section group. my $spec = lc $1; foreach my $entry ( @{ $files{$file} } ) { $entry->{SPEC} = $spec; } } elsif ( $cmd =~ /^dir\s*:\s*(.*)$/ ) { # This file goes in an unusual source directory. my $newdir = lc $1; $newdir = "" if !defined $newdir; my $entries = $files{$file}; foreach my $entry ( @$entries ) { $entry->setSrcDir($newdir); } delete $files{$file}; $file =~ /^([^\\]*)(\\.*)?$/; $file = $1; $files{"$file\\$newdir\\"} = $entries; } elsif ( $cmd =~ /^flag\s*:\s*(.*)$/ ) { my $flag = lc $1; foreach my $entry ( @{ $files{$file} } ) { next if $entry->{DIRID} == 65619; $entry->{FLAG} = $flag; } } elsif ( $cmd =~ /^temp\s*:\s*(.*)$/ ) { my $temp = lc $1; foreach my $entry ( @{ $files{$file} } ) { $entry->{TEMP} = $temp; } } else { wrnmsg "Unknown change file command: $cmd"; } } else { wrnmsg "Unknown entry in change file: $filename"; next; } } elsif ( /^\s*ADD\s*/ ) { while ( ) { last if /^\s*END\s*$/; next if /^\s*(\#.*)?$/; chomp; my $entry = InfData->new(); if ( !$entry->readLine($_,\@skuletters) ) { wrnmsg "Line omitted:\n$_"; next; } my $file = $entry->getSrcDir(); $file = $entry->getSrc() . "\\$file" if defined $file; $file = $entry->getSrc() if !defined $file; my $test = 0; foreach my $ent ( @{ $files{$file} } ) { $test = 1 if $ent->addBit($entry); } next if $test; $files{$file} = [ () ] if !exists $files{$file}; push @{ $files{$file} }, $entry; } } elsif ( /^\s*DELETE\s*/ ) { while ( ) { last if /^\s*END\s*$/; next if /^\s*(\#.*)?$/; chomp; my $entry = InfData->new(); if ( !$entry->readLine($_,\@skuletters) ) { wrnmsg "Line omitted:\n$_"; next; } my $file = $entry->getSrcDir(); if ($file ne "") { $file = $entry->getSrc() . "\\$file"; } else { $file = $entry->getSrc(); } next if !exists $files{$file}; my @newlist = (); foreach my $ent ( @{ $files{$file} } ) { my $newent = $ent->delBit($entry); push @newlist, $newent if defined $newent; } if ($#newlist >= 0) { $files{$file} = [ @newlist ]; } else { delete $files{$file}; } } } } close CHANGE; } # If need relative paths, need to find all of the affected files. logmsg( "Finding different file versions..." ); foreach my $sku ( @pathorder ) { next if ($infpath{$sku} & $skumask) == 0; foreach my $file ( `dir /a-d /b /s $bindir\\$sku` ) { $file =~ s/^\Q$bindir\E\\//i; chomp $file; $file =~ /\\([^\\]*)$/; next if !exists $files{lc $1} and !exists $files{lc $file}; $relfiles{lc $file} = ""; } } # Generate the table. logmsg( "Generating the table..." ); if ( !open OUT, ">$outfile" ) { errmsg( "Unable to open $outfile." ); die; } # Step through the files. foreach my $file (sort keys %files) { # Figure out what versions of the file exist. my @vers = (); foreach my $entry ( getEntries($file) ) { my @list = (); my $mask = $entry->{MASK}; foreach my $ver (@vers) { $ver = $mask if ($ver|$mask) == $mask; $mask = 0 if ($ver|$mask) == $ver; push @list, $ver; } push @list, $mask if $mask != 0; @vers = @list; } # Process each entry for each file. foreach my $entry ( getEntries($file) ) { my $fullmask; my $mask = $entry->{MASK}; foreach my $ver (@vers) { $fullmask = $ver if ($ver|$mask) == $ver; } my $fullpath = $entry->getFullPath(); my $myfile = $entry->getSrc(); my $mypath = $entry->getSrcDir(); # Break up by directory if more than one version exists. foreach my $skudir ( @pathorder ) { my $pathmask = 0; my $subdir; if ( exists $infpath{$skudir} ) { $pathmask = $infpath{$skudir}; } else { $pathmask = $fullmask; } $subdir = $relpath{$pathmask}; $subdir = "" if !$subdir or (!$altpath and $skudir eq "") or $mypath ne ""; $subdir = "$mypath$subdir" if $mypath ne "\\"; my $filename = "$subdir$myfile"; $pathmask &= $skumask; $pathmask &= $fullmask; next if ($pathmask & $mask) == 0; next if $skudir ne "" and !exists $relfiles{lc "$skudir\\$myfile"}; $fullmask &= ~$pathmask; # Add the entry needed for this version. $entry->{MASK} = $pathmask & $mask; $entry->{NAME} = $filename; print OUT $entry->getLine(\@skuletters); print OUT "\n"; } } } close OUT;