# # # TODO NOTES: # # batch and perform MD5 checksum checks # output file of what we decided was golden # be sure we return a status that gets warning message sent if we are unhappy # check that we are setting the dates correctly # see if we can work with files under lang\ (or other sub-directories) # be sure Wade is happy # # FileName: PopFilter.pl # # Script for building Windows .Net Server builds and XP Client SP1 together. # Have any changes to this file reviewed by DavePr, BryanT, or BPerkins before checking in. # # Usage = PopFilter.pl [-fake] # Usage = PopFilter.pl [-fake] [-vbl=vblreleasedir] [-xpsp1=xpsp1dir] [-nttree=nttreedir] [-nocompare] # # Function: Populate missing files in nttreedir from vblreleaseddir so # 1) Find the XPSP1 directory, searching under # xpsp1dir # SDXROOT\.. # VBL_RELEASE # \\winbuilds\release\main\usa # And read in the Version information and the GoldTimestamps # 2) Read in SDXROOT\dbdir\LockedDB and parse into several piles: # CheckFiles -- non-PE files that should be found to be completely unchanged -- measured by # an external date/size/checksum. .inf files are checked for size/date after # removing the DriverVer= line This check probably will just say UNICODE INFs miscompare. # CheckPEFiles -- PE files that should be found to be completely unchanged -- measured by # an internal date/checksum. # PEReplaceFiles -- PE files that are expected to have been re-built, but should be the same as measured # by xpsp1compdir /i /b /y [someday just /y]. # 3) Check CheckFiles unless -nocompare. # 4) Check CheckPEFiles unless -nocompare. # 5) Check PEReplaceFiles against XPSP1\Reference\* -- unless -nocompare or CHK or ClientSDXRoot in version != SDXROOT # 6) Pick a source directory for PEReplaceFiles according to the following algorithm # XPSP1\Checked -- if this is a checked build, and this directory exists # XPSP1\Gold -- if this directory exists # XPSP1\Reference -- otherwise # 7) Replace files (unless check said not to). # # [-verbose] -- chatter while working # [-fake] -- don't actually make any changes # [-force] -- force copies even if errors on comparisons -- but miscompares never copied # [-filter] -- copy over the files from the client if no mismatches or -force # [-nofilter] -- do not copy over the client files (kind of an official -fake). The default is $NoFilter -- for now. # [-test] -- hook for special testing # # [-nodates] -- turn off date comparisons in non-PE files and rely on size and checksum # ####################################### ### TEMPORARY -- REMOVE THESE LINES ### ####################################### # [-ignoreinf] -- temporary flag to ignore INF files if sizes match, since the DriverVer issues wasn't fixed in the last test run # [-testfilter=flags] -- restrict testing to files with the specified flags (BN, BF, PF, ...) # [-testlimits=flags] -- limit testing to that specified by the flags {CF, PECF, RF) # # # VBLpath will be computed from BuildMachines.txt if not supplied either # on the command line, or in the VBL_RELEASE environment variable. # # WARNING: # WARNING: make sure pathname comparisons are case insensitive. # WARNING: Either convert the case (e.g. with 'lc') or do the comparisons like this: # WARNING: if ($foo =~ /^\Q$bar\E$/i) {} # WARNING: or if ($foo !~ /^\Q$bar\E$/i) {} # WARNING: $begintime = time(); $VBLPathVariableName = 'VBL_RELEASE'; $BuildMachinesFile = $ENV{ "RazzleToolPath" } . "\\BuildMachines.txt"; $PopFilterDir = $ENV{ "RazzleToolPath" } . "\\PopFilter"; $LogFile = "build.popfilter"; $TestFile = "build.popfilter-test"; $GoldFiles = "build.GoldFiles"; $xpsp1dir = ""; $Winbuilds = "\\\\winbuilds\\release\\main\\usa"; $SERVERDIRNAME = "REPLACED_SERVER_FILES"; $CLIENTDIRNAME = "INCOMING_CLIENT_FILES"; $dbdir = "MergedComponents\\PopFilter"; $LockedDatabase = "LockedDB"; $SymbolFiles = "SymbolFiles"; #$comptool = "tools\\PopFilter\\popcompdir.exe /y /i /b"; $comptool = "tools\\PopFilter\\pecomp.exe /OnlyPE /Silent "; $PECOMPERROR = 999999999; # # Usage variables # $PGM='PopFilter: '; $Usage = $PGM . "Usage: PopFilter.pl [-fake] [-vbl=vblreleasedir] [-xpsp1=xpsp1dir] [-nttree=nttreedir] [-nocompare]\n"; # # Get the current directory # open CWD, 'cd 2>&1|'; $CurrDir = ; close CWD; chomp $CurrDir; $CurrDrive = substr($CurrDir, 0, 2); # # Check variables expected to be set in the environment. # $sdxroot = $ENV{'SDXROOT'} or die $PGM, "Error: SDXROOT not set in environment\n"; $buildarch = $ENV{'_BuildArch'} or die $PGM, "Error: _BuildArch not set in environment\n"; $computername = $ENV{'COMPUTERNAME'} or die $PGM, "Error: COMPUTERNAME not set in environment\n"; $branchname = $ENV{'_BuildBranch'} or die $PGM, "Error: _BuildBranch not set in environment\n"; $foo = $ENV{'NTDEBUG'} or die $PGM, "Error: NTDEBUG not set in environment\n"; $dbgtype = 'chk'; $dbgtype = 'fre' if $foo =~ /nodbg$/i; $chkbuild = ($dbgtype eq 'chk'); $official = $ENV{'OFFICIAL_BUILD_MACHINE'}; # # initialize argument variables # $Test = $ENV{'POPFILTER_TEST'}; $Force = $ENV{'POPFILTER_FORCE'}; $Fake = $ENV{'POPFILTER_FAKE'}; $Verbose = $ENV{'POPFILTER_VERBOSE'}; # # Reverse the order of the lines below to change default of $NoFilter # $NoFilter = !$ENV{'POPFILTER_FILTER'}; # default value for $NoFilter is TRUE. $NoFilter = $ENV{'POPFILTER_NOFILTER'}; # default value for $NoFilter is FALSE. $NoCompare = $ENV{'POPFILTER_NOCOMPARE'}; $NoPECompare= $ENV{'POPFILTER_NOPECOMPARE'}; $xpsp1dirarg= $ENV{'POPFILTER_XPSP1'}; $NoDates = $ENV{'POPFILTER_NODATES'}; $IgnoreINFs = $ENV{'POPFILTER_IGNOREINFS'}; $TestLimits = $ENV{'POPFILTER_TESTLIMITS'}; $TestFilters= $ENV{'POPFILTER_TESTFILTERS'}; # # Debug routines for printing out variables # sub gvar { for (@_) { print "\$$_ = $$_\n"; } } # # print on the various files # sub printall { print LOGFILE @_; print $PGM unless @_ == 1 and @_[0] eq "\n"; print @_; } sub printfall { printf LOGFILE @_; print $PGM unless @_ == 1 and @_[0] eq "\n"; printf @_; } # # Sub hms # Takes Argument time in seconds and returns as list of (hrs, mins, secs) # sub hms { $s = shift @_; $h = int ($s / 3600); $s -= 3600*$h; $m = int ($s / 60); $s -= 60*$m; return ($h, $m, $s); } # # signal catcher (at least this would work on unix) # sub catch_ctrlc { printall "Aborted.\n"; die $PGM, "Error: Aborted.\n"; } $SIG{INT} = \&catch_ctrlc; # # routine to fully qualify a pathname # sub fullyqualify { die $PGM . "Error: Internal error in fullpathname().\n" unless @_ == 1; $_ = @_[0]; if (/\s/) { die $PGM, "Error: Spaces in pathnames not allowed: '", $_, "'\n"; } return $_ unless $_; # empty strings are a noop s/([^:])\\$/$1/; # get rid of trailing \ while (s/\\\.\\/\\/) {} # get rid of \.\ while (s/\\[^\\]+\\\.\.\\/\\/) {} # get rid of \foo\..\ s/\\[^\\]+\\\.\.$/\\/; # get rid of \foo\.. s/:[^\\]+\\\.\.$/:/; # get rid of x:foo\.. s/([^:])\\\.$/$1/; # get rid of foo\. s/:\\\.$/:\\/; # get rid of x:\. s/:[^\\]+\\\.\.$/:/; # get rid of x:foo\.. s/^$CurrDrive[^\\]/$CurrDir\\/i; # convert drive-relative on current drive if (/^[a-z]:\\/i) { return $_; } # full if (/^\\[^\\].*/) { return "$CurrDrive$_"; } # rooted if (/^\\\\[^\\]/) { # print $PGM, 'Warning: Use of UNC name bypasses safety checks: ', $_, "\n"; return $_; # UNC } if (/^\.$/) { return "$CurrDir"; } # dot if (/^$CurrDrive\.$/i) { return "$CurrDir"; } # dot on current drive if (/^[^\\][^:].*/i) { return "$CurrDir\\$_"; } # relative if (/^([a-z]:)([^\\].*)/i) { $drp = $CurrDir; # this case handled above if ($1 ne $CurrDir) { # $drp = $ENV{"=$1"}; # doesn't work! die $PGM, "Error: Can't translate drive-relative pathnames: ", $_, "\n"; } return "$drp\\$2"; # drive:relative } die $PGM, "Error: Unrecognized pathname format: $_\n"; } # # Routine to copy a file -- avoiding win32::CopyFile # # Not currently used # use Fcntl; sub populatecopy { my $writesize = 64*4096; my($src, $dst) = @_; my($infile, $outfile, $buf, $n, $r, $o); if (not sysopen INFILE, $src, O_RDONLY() | O_BINARY()) { return 0; } if (not sysopen OUTFILE, $dst, O_WRONLY() | O_CREAT() | O_TRUNC() | O_BINARY(), 0777) { close INFILE; return 0; } $r = 0; # need this to be defined in case INFILE is empty ERR: while ($n = sysread INFILE, $buf, $writesize) { last ERR unless defined $n; $o = 0; while ($n) { $r = syswrite OUTFILE, $buf, $n, $o; last ERR unless defined $r; $n -= $r; $o += $r; } } close INFILE; close OUTFILE; return 0 if not defined $n or not defined $r or $n != 0; return 1; } # Given a path to a file, strip off the # file and check for the existence of # the directory. If it does not exist, # create it. sub verifydestdir($) { my $dest = shift; my $dest_dir, @dest_dir; # Check for existence of destination dir if ( $dest =~ /(.*)\\.*$/ && ! -d $1 ) { $dest_dir = fullyqualify( $1 ); if ( $dest_dir =~ /^([a-zA-Z]\:\\[^\\]+)\\/ || $dest_dir =~ /^(\\\\[^\\]\\[^\\])\\/ ) { my $initial = $1; my $final = $'; @dest_dir = ( $initial, split /\\/, $final ); } else { @dest_dir = split /\\/, $dest_dir; } # Create destination directory my $new_dir = ""; foreach (@dest_dir) { $new_dir .= "$_\\"; return if ( ! -d $new_dir && !mkdir $new_dir, 0777 ); } } return 1; } use File::Copy; use File::Compare; use Win32::File qw(SetAttributes); sub copyex($$) { my ($src, $dest) = @_; return (verifydestdir($dest) && copy($src, $dest)); } sub renameex($$) { my ($src, $dest) = @_; return (verifydestdir($dest) && rename($src,$dest)); } # # Process and validate arguments # for (@ARGV) { if (/^[\/\-]test$/i) { $Test++; next; } if (/^[\/\-]fake$/i) { $Fake++; next; } if (/^[\/\-]verbose$/i) { $Verbose++; next; } if (/^[\/\-]force$/i) { $Force++; next; } if (/^[\/\-]filter$/i) { $NoFilter = 0; next; } if (/^[\/\-]nofilter$/i) { $NoFilter = 1; next; } if (/^[\/\-]nocompare$/i) { $NoCompare++; next; } if (/^[\/\-]nopecompare$/i) { $NoPECompare++; next; } if (/^[\/\-]nodates$/i) { $NoDates++; next; } if (/^[\/\-]ignoreinfs$/i) { $IgnoreINFs++; next; } if (/^[\/\-]vbl=(.+)$/i) { $VBL = $1; next; } if (/^[\/\-]nttree=(.+)$/i) { $nttree = $1; next; } if (/^[\/\-]xpsp1=(.+)$/i) { $xpsp1dirarg = $1; next; } if (/^[\/\-]testfilters=(.*)$/i) { $TestFilters = $1; next; } if (/^[\/\-]testlimits=(.*)$/i) { $TestLimits = $1; next; } if (/^[\/\-]?$/i) { die $Usage; } if (/^[\/\-]help$/i) { die $Usage; } die $Usage; } # # If we didn't get the NTTree directory from the command line, # get it from the _NTTREE environment variable. # $nttree = $ENV{'_NTTREE'} unless $nttree; $t = $NoFilter? 'NOFILTER' : 'FILTER'; $t .= ' NOCOMPARE' if $NoCompare; $t .= ' NOPECOMPARE' if $NoPECompare; $t .= ' NODATES' if $NoDates; $t .= ' VERBOSE' if $Verbose; $t .= ' FORCE' if $Force; $t .= ' FAKE' if $Fake; $t .= ' TEST' if $Test; $t .= ' IGNOREINFs' if $IgnoreINFs; $t .= " XPSP1=$xpsp1dirarg" if $xpsp1dirarg; $t .= " LIMITS=$TestLimits" if $TestLimits; $t .= " LIMITS=$TestLimits" if $TestFilters; printall "OPTIONS: $t\n"; if ($TestLimits) { $what = ''; for (split /,/, $TestLimits) { $testlimitedto{$_}++; $what .= " $_"; } printall "Limiting test to:$what\n"; } if ($TestFilters) { $what = ''; for (split /,/, $TestFilters) { $testfilteredto{$_}++; $what .= " $_"; } printall "Filtering files by:$what\n"; } # # Can only popfilter with the current directory the same as sdxroot. # die $PGM, "Error: Can only popfilter if CD <$CurrDir> is SDXROOT <$sdxroot>\n" unless $sdxroot =~ /^\Q$CurrDir\E$/io; # # If we didn't get the local target directory from the command line, # get it from the environment. If that fails, we parse BuildMachines.txt. # $VBL = $ENV{$VBLPathVariableName} unless $VBL; if ((not $VBL) || ($VBL =~ /^[\d\w_]+$/)) { $tbranchname = $branchname; $tbranchname = $VBL if $VBL =~ /^[\d\w_]+$/; $fname = $BuildMachinesFile; open BMFILE, $fname or die $PGM, "Error: Could not open: $fname\n"; for () { s/\s+//g; s/;.*$//; next if /^$/; ($vblmach, $vblprime, $vblbranch, $vblarch, $vbldbgtype, $vbldl, $disttype, $alt_release ) = split /,/; if ($vblarch =~ /\Q$buildarch\E/io and $vbldbgtype =~ /\Q$dbgtype\E/io and $vblbranch =~ /\Q$tbranchname\E/io and $disttype !~ /distbuild/i) { if ( defined $alt_release) { $dname = $alt_release; last; } else { $dname = "\\\\$vblmach\\release"; } opendir BDIR, "$dname\\" or die $PGM, "Error: Could not open directory: $dname\n"; @reldirs = readdir BDIR; close BDIR; $rname = 0; $date = 0; for (@reldirs) { next unless /[0-9]+\.$vblarch$vbldbgtype\.$vblbranch\.(.+)$/io; ($date = $1, $rname = $_) unless $date gt $1 or substr($date, 0, 2) eq '00' and substr($1, 0, 2) eq '99'; # Y2K trade-off } if (not $rname) { print $PGM, "Warning: No valid release shares found on $dname.\n"; } else { $VBL = "$dname\\$rname"; } last; } } close BMFILE; } die $PGM, "Error: Not a directory: ", $VBL, "\n" if $VBL and ! -d $VBL; die $Usage unless $nttree; die $PGM, "Error: Not a directory: ", $nttree, "\n" unless -d $nttree; die $PGM, "Error: Not writable: ", $nttree, "\n" unless -w $nttree; if (-d "$nttree\\$SERVERDIRNAME") { warn $PGM, "Skipping populate filtering: $SERVERDIRNAME directory already exists\n"; exit 0; } die $PGM, "Error: $SERVERDIRNAME directory already exists\n" if -e "$nttree\\$SERVERDIRNAME"; die $PGM, "Error: $CLIENTDIRNAME directory already exists\n" if -e "$nttree\\$CLIENTDIRNAME"; # # Fully qualify the pathnames # $VBL = fullyqualify($VBL) if $VBL; $nttree = fullyqualify($nttree); # # Open the logfile, and maybe the testfile # Open the build.GoldFiles file. # $foo = "$nttree\\build_logs\\$LogFile"; open LOGFILE, ">>$foo" or die $PGM, "Error: Could not create logfile: ", $foo, ": $!\n"; open TSTFILE, ">$TestFile" or die $PGM, "Error: Could not create testfile: ", $TestFile, ": $!\n" if $Test; open GOLDFILESFILE, ">$GoldFiles" or die $PGM, "Error: Could not create testfile: ", $GoldFiles, ": $!\n"; # # Find the XPSP1 directory # $t = $xpsp1dirarg; if (not $xpsp1dir and -d $t and -s "$t\\Version" and -d "$t\\Reference") { printall "WARNING: XPSP1DIR: $xpsp1dir invalid (Version or Reference\\ missing)\n"; $xpsp1dir = ""; } $xpsp1dir = ""; $tries = 0; @slist = (); push @slist, ( "$xpsp1dirarg", "$xpsp1dirarg\\XPSP1" ) if $xpsp1dirarg; push @slist, ( "$sdxroot\\XPSP1", "$VBL\\XPSP1", "$Winbuilds\\XPSP1" ); for ( @slist ) { $tries++; $xpsp1dir = $_ if -d $_ and -s "$_\\Version" and -d "$_\\Reference"; last if $xpsp1dir; } die "Could not find XPSP1 (@slist)\n" unless $xpsp1dir; printall "WARNING: XPSP1DIR: $xpsp1dirarg invalid (Version or Reference\\ missing)\n" if $xpsp1dirarg and $tries > 2; printall "Comparing against: $xpsp1dir\n"; # # Read in the Version information # $tname = "$xpsp1dir\\Version"; open VERSION, "<$tname" or die "Could not open $tname\n"; for () { chop; s/\s*\#.*$//; next if /^\s*$/; s/^\s*//; s/\s*=\s*/=/; ($tkey, $tvalue) = split '=', $_, 2; $verinfo{lc $tkey} = $tvalue; } close VERSION; # # Read in the GoldTimestamps # $tname = "$xpsp1dir\\GoldTimestamps"; open GOLDSTAMPS, "<$tname" or die "Could not open $tname\n"; for () { chop; s/\s*\#.*$//; next if /^\s*$/; s/^\s*//; s/\s*=\s*/=/; ($tfile, $tstamp, $tlen, $tmd5) = split ' ', $_, 4; $tfile = lc $tfile; $GoldStamp{$tfile} = $tstamp; $GoldMD5{$tfile} = $tmd5; $GoldLength{$tfile} = $tlen; } close VERSION; printall "ClientSDXRoot defaulting to D:\\NT because undefined in Version\n" unless $verinfo{lc ClientSDXRoot}; @list = ( 'ClientFileVersion', 'ClientSDXRoot' ); for ( @list ) { printall "Version Info: $_=$verinfo{lc $_}\n"; } if ($Test) { print "Version info:\n"; for (sort keys %verinfo) { print "$_ = $verinfo{$_}\n"; } } # # Parse LockedDatabase into CheckFiles, CheckPEFiles and PEReplaceFiles. # $t = "$sdxroot\\$dbdir\\$LockedDatabase"; open LOCKEDDB, "<$t" or die "Could not open: $t\n"; $lineno = 0; for () { $lineno++; chop; s/\s*[#;].*//; next if /^$/; s/\s+/ /g; ($fname, $rest) = split " ", $_, 2; $fname = lc $fname; $lockeddb{$fname} = $rest; # # act_plcy.htm # no @htm 3b634b0a 58548 - 106c - Sat_Jul_28_16:30:18_2001 F # fname # 0 drivercab (+no, +yes) # 1 filetype (EXE, DLL, @ext) # 2 timestamp # 3 PE checksum # 4 MD5 checksum # 5 -- # 6 magic (size) # 7 -- # 8 datestring # 9 category type ( BD BF BFI D F FI PD PF ) # @fields = split " ", $rest; $category = $fields[9]; next if $TestFilters and not $testfilteredto{$category}; if ($category =~ /^P/) { push @PEReplaceFiles, $fname; } elsif ($fields[1] =~ /^\@/) { push @CheckFiles, $fname; } else { die "$LockedDatabase corrupt at line $lineno: (", $fname, @fields, ")\n" unless $category =~ /^B/; push @CheckPEFiles, $fname; } } close LOCKEDDB; printfall "%-5d files to just Check, %-5d files to maybe replace from client RTM.\n", @CheckFiles + @CheckPEFiles, scalar @PEReplaceFiles; $PEMismatches = 0; $OtherMismatches = 0; $OtherPEMismatches = 0; # # Check CheckFiles unless -nocompare. # unless ($NoCompare) { unless ($TestLimits and not $testlimitedto{'CF'}) { for (sort @CheckFiles) { $fname = "$nttree\\$_"; $openstring = /\.inf$/i ? "findstr /B /V DriverVer $fname |" : "<$fname"; $s = open CHKFD, "$openstring" or printall "Could not open for checking $openstring\n"; next unless $s; @client = split " ", $lockeddb{$_}; @s = stat $fname; $magic = $s[7]; # size $timestamp = $s[9]; { local $/; $checksum = unpack ("%32C*", ); } close CHKFD; $client[2] = hex $client[2]; $client[3] = hex $client[3]; $client[6] = hex $client[6]; $timestamp = $client[2] = 0 if $NoDates; $checksum = $client[3] = 0 if $IgnoreINFs and $magic == $client[6] and /\.inf$/i; if ($timestamp != $client[2] or $checksum != $client[3] or $magic != $client[6]) { printfall "LockedDB %-13s (%8s, %8s, %8s) -> (%8s, %8s, %8s)\n", '=> Built', 'timstamp', 'chksum', 'size(.)', 'timstamp', 'chksum', 'size(.)' unless $HeaderOutputCF++; $OtherMismatches++; printfall "MISMATCH: %-13s (%8x, %8x, %8d) -> <%8x, %8x, %8d>\n", $_, $client[2], $client[3], $client[6], $timestamp, $checksum, $magic; } else { push @FilesToStamp, $_; printall "$_ NC NPE\n" if $Test; } } } unless ($TestLimits and not $testlimitedto{'PECF'}) { for (sort @CheckPEFiles) { $fname = "$nttree\\$_"; $s = open CHKFD, "<$fname" or printall "Could not open for checking $fname\n"; next unless $s; $NoncopyCount++; @client = split " ", $lockeddb{$_}; open FD, "link -dump -headers $fname|" or ($OtherPEErrors++, printall "Could not run link command on $fname\n"); $type = $pefile = $timestamp = $checksum = $entrypoint = $magic = $machine = $datestring = "?"; for () { $pefile += m/PE signature found/; $timestamp = $1, $datestring = $2 if m/\s*([\dA-Z]+)\s+time date stamp\s+(.+)/; $checksum = $1 if m/\s([\dA-Z]+)\s+checksum/; $entrypoint = $1 if m/entry point\s+\(([\dA-Z]+)\)/; } close FD; unless ($pefile) { printall "Not a PE file: $fname\n"; $OtherPEMismatches++; next; } $checksum = hex $checksum; $timestamp = hex $timestamp; $client[2] = hex $client[2]; $client[3] = hex $client[3]; if ($timestamp != $client[2] or $checksum != $client[3]) { printfall "LockedDB %-13s (%8s, %8s) -> (%8s, %8s)\n", '=> Built', 'timstamp', 'chksum', 'timstamp', 'chksum' unless $HeaderOutputPECF++; $OtherPEMismatches++; printfall "MISMATCH: %-13s (%8x, %8x) -> <%8x, %8x>\n", $_, $client[2], $client[3], $timestamp, $checksum; } else { push @FilesToStamp, $_; printall "$_ NC PE\n" if $Test; } } } } # # Check PEReplaceFiles against XPSP1\Reference\* -- unless -nocompare or CHK or ClientSDXRoot in version != SDXROOT # $reason = ""; $reason = "checked build.\n" if $chkbuild; $t = $verinfo{'clientsdxroot'}; # # run even if there is an sdxroot mismatch, pecomp may still provide some benefit # #$reason = "sdxroot mismatch with comparison build ($t).\n" if $t ne $sdxroot; $reason = "POPFILTER_NOPECOMPARE set in the environment.\n" if $nopecompare; $reason = "Test limited to non PE files.\n" if not $reason and $TestLimits and not $testlimitedto{'RF'}; printall "PE Comparison not done because $reason\n" if $reason; $PopFilterBytesSaved = 0; if ($reason or $NoCompare) { @filestocopy = @PEReplaceFiles; printall @filestocopy . " files from LockedFiles/LockedDrivers may be replaced -- without comparison\n"; } else { @filestocopy = (); for (sort @PEReplaceFiles) { $t = "$sdxroot\\$comptool $xpsp1dir\\Reference\\$_ $nttree\\$_"; #open COMP, "$t|" or die "Could not run $t\n"; # for compdir /y # $realdifference = ""; # for () { # $realdifference = $_ if /DIFFER/; # } # close COMP; # for PECOMP $realdifference = system($t); if ($realdifference == 0) { printall "SAME: $_\n" if $Test; $PopFilterBytesSaved += hex $GoldLength{$_}; } elsif ($realdifference == $PECOMPERROR) { printall "ERROR: unexpected error: $t\n"; $ToolProblems++; } else { printfall "DIFFER<%5d): $_\n", $realdifference if $Verbose; $PEMismatches++; } if ($realdifference) { } else { push @filestocopy, $_; push @FilesToStamp, $_; } } printall @filestocopy . " files from LockedFiles/LockedDrivers may be replaced -- based on comparison\n"; } # Read in list of known symbols # on an official build machine if ( $official ) { $t = "$sdxroot\\$dbdir\\$SymbolFiles"; open SYMBOLDB, "<$t" or die "Could not open: $t\n"; $lineno = 0; for () { $lineno++; chop; s/\s*[#;].*//; next if /^$/; my ($x, $y, $z) = split; die "$SymbolFiles Corrupt at line $lineno: ($_)\n" unless ( defined $x && defined $y && defined $z ); $symbols{lc$x} = [$y, $z]; } close SYMBOLDB; # Associate symbols and files marked to be copied @symbolstocopy = (); foreach ( @filestocopy ) { next unless ( exists $symbols{lc$_} ); my $relpath = $symbols{lc$_}->[0]; my $copypub = $symbols{lc$_}->[1]; if ( $copypub ) { push @symbolstocopy, "symbols\\$relpath"; $GoldStamp{"symbols\\". lc$relpath} = $GoldStamp{$_}; push @FilesToStamp, "symbols\\". lc$relpath if @FilesToStamp; } push @symbolstocopy, "symbols.pri\\$relpath"; $GoldStamp{"symbols.pri\\". lc$relpath} = $GoldStamp{$_}; push @FilesToStamp, "symbols.pri\\". lc$relpath if @FilesToStamp; } } $TotalCompareErrors = $OtherMismatches + $DroppedPEMismatches + $PEMismatches; $checktime = time(); # # Replace files on the list -- unless check says not to, or we had compare errors and we are not forcing. # printall "WARNING: Faking!!! Not really populating _NTTREE with client files\n" if $Fake and @filestocopy > 0; printall @filestocopy + @symbolstocopy. " Files to copy\n"; $fatal = 0; unless ($NoFilter or $Fake or not $Force and $TotalCompareErrors > 0) { # # Make directories. # $serverdir = "$nttree\\$SERVERDIRNAME"; $clientdir = "$nttree\\$CLIENTDIRNAME"; mkdir "$serverdir", 0777 or die "Could not mkdir $serverdir\n"; mkdir "$clientdir", 0777 or die "Could not mkdir $clientdir\n"; printall "Created $serverdir to save replaced files.\n"; printall "Created $clientdir to stage replacement files.\n"; # # Pick a source directory for PEReplaceFiles according to the following algorithm # XPSP1\Checked -- if this is a checked build, and this directory exists # XPSP1\Gold -- if this directory exists # XPSP1\Reference -- otherwise # And check that all the files we need to replace exist there. And copy them down. # Check existence using a file from $PEReplaceFiles # $testfile = $PEReplaceFiles[0]; @list = (); push @list, 'Checked' if $chkbuild; push @list, 'Gold'; push @list, 'Reference'; $sourcedir = ""; for ( @list ) { $t = "$xpsp1dir\\$_"; $sourcedir = "$t" if -s "$t\\$testfile"; last if $sourcedir; } die "Could not find source directory for binaries under $xpsp1dir (using $testfile)\n" unless $sourcedir; printall "Source directory for client files: $sourcedir\n"; # # Copy down all the client files # for (@filestocopy) { $succ = copy ("$sourcedir\\$_", "$clientdir\\$_"); printall ("Error copying $_ from $sourcedir to $clientdir\n"), $fatal++ unless $succ; $CopyCount++; $CopyBytes += -s "$clientdir\\$_"; } for $sym (@symbolstocopy) { $succ = copyex( $sourcedir."Symbols\\$sym", "$clientdir\\$sym"); printall ("Error copying $sym from $sourcedir". "Symbols to $clientdir\n"), $fatal++ unless $succ; # Treat symbols same as other files now for rename push @filestocopy, $sym; $SymCopyCount++; $CopyBytes += -s "$clientdir\\$sym"; } die $PGM, "Failure to copy down all the expected client files is fatal.\n" if $fatal; # # Move the server files out of the way. # for $f (@filestocopy) { if (-s "$nttree\\$f") { $succ = renameex ("$nttree\\$f", "$serverdir\\$f"); printall ("Error moving $f from $nttree to $serverdir\n"), $fatal++ unless $succ; } else { $missingserverfiles++; printall "WARNING: $f does not exist in $nttree\n"; } } printall "WARNING: $missingserverfiles files found missing from server\n" if $missingserverfiles; die $PGM, "Fatal error moving aside client files to $serverdir - Move them back manually.\n" if $fatal; # # Move in the client files. # for $f (@filestocopy) { $succ = renameex ("$clientdir\\$f", "$nttree\\$f"); printall ("Error moving $f from $clientdir to $nttree\n"), $fatal++ unless $succ; } die $PGM, "Fatal error moving in client files from $clientdir." . " May need to put back server files ($serverdir) manually.\n" if $fatal; } else { printall "WARNING: Not populating _NTTREE with client files:\n"; printall " NoFilter=$NoFilter Fake=$Fake Force=$Force TotalCompareErrors=$TotalCompareErrors\n"; } # # Set the gold timestamp on the files which matches or were successfully compared file if there was no mismatch # unless (scalar @FilesToStamp) { push @FilesToStamp, @CheckFiles; push @FilesToStamp, @CheckPEFiles; push @FilesToStamp, @PEReplaceFiles; if ( $official ) { foreach ( @PEReplaceFiles ) { next unless ( exists $symbols{lc$_} ); my $relpath = $symbols{lc$_}->[0]; my $copypub = $symbols{lc$_}->[1]; push @FilesToStamp, "symbols\\". lc$relpath if $copypub; push @FilesToStamp, "symbols.pri\\". lc$relpath; } } } # # set the timestamps on files # and write the gold files list # $BytesSaved = 0; unless ($Fake) { for (@FilesToStamp) { $stamp = lc $GoldStamp{$_}; if ( !SetAttributes("$nttree\\$_", 0) ) { printall "ERROR: Resetting file attributes on $_ failed\n"; } if ( !utime($begintime, hex $stamp, "$nttree\\$_") ) { printall "ERROR: Updating time-stamp for $nttree\\$_ failed\n"; } printall "Could not find stamp for: $_\n" unless $stamp; printf GOLDFILESFILE "%s\n", $_; $BytesSaved += hex $GoldLength{$_}; } } close GOLDFILESFILE; #======================================================================================================================= #======================================================================================================================= # # At this point we remember $begintime, $checktime, and have $CopyBytes, $CopyCount, # $PEMismatches, $OtherMismatches and $OtherPEMismatches. # $nttree and $sourcedir are also available. # LOGFILE and TSTFILE are open # $Fake and $Test may be set. # $t0 = $checktime - $begintime; $t1 = time() - $checktime; ($h0, $m0, $s0) = hms $t0; ($h1, $m1, $s1) = hms $t1; ($h2, $m2, $s2) = hms ($t0 + $t1); $KB = $CopyBytes/1024; $MB = $KB/1024; $kbrate = $KB/$t1 unless not $t1; printfall "Populated $nttree with $CopyCount CLIENT RTM files ".(@symbolstocopy?"and $SymCopyCount CLIENT RTM symbols ":"")."(%4.0f MB)" . " from $sourcedir".(@symbolstocopy?"[Symbols] ":"")." [%7.2f KB/S]\n" , $MB, $kbrate; printall "Verified $NoncopyCount CLIENT RTM files.\n"; if ($PEMismatches or $OtherMismatches or $OtherPEMismatches) { printall "ERROR: $PEMismatches mismatches in built PE files,\n"; printall "ERROR: $OtherMismatches mismatches in other files, and\n"; printall "ERROR: $OtherPEMismatches other PE files had unexpected mismatches!\n"; } if ($ToolProblems) { printall "ERROR: $ToolProblems problems with the tools.\n"; } printfall "Reused %5d XP/RTM files totalling %4d MB\n", scalar @FilesToStamp, $BytesSaved/1000000; printfall "Only %5d XP/RTM files totalling %4d MB due to popfilter\n", scalar $CopyCount, $PopFilterBytesSaved/1000000; printfall "Checking time %5d secs (%d:%02d:%02d)\n", $t0, $h0, $m0, $s0; printfall "CopyFile time %5d secs (%d:%02d:%02d)\n", $t1, $h1, $m1, $s1; printfall "TotalTime time %5d secs (%d:%02d:%02d)\n", $t0+$t1, $h2, $m2, $s2; # # Return an error if we were faking so timebuild doesn't proceed. # close LOGFILE; close TSTFILE if $Test; exit $Fake;