1040 lines
29 KiB
Perl
1040 lines
29 KiB
Perl
|
# FileName: PopulateFromVBL.pl
|
||
|
#
|
||
|
# Have any changes to this file reviewed by DavePr, BryanT, or WadeLa
|
||
|
# before checking in.
|
||
|
# Any changes need to verified in all standard build/rebuild scenarios.
|
||
|
#
|
||
|
# Usage = PopulateFromVBL.pl [-force] [-vbl=vblreleasedir] [-nttree=nttreedir] [-symbols]
|
||
|
#
|
||
|
# Function: Populate missing files in nttreedir from vblreleaseddir so
|
||
|
# 0) Verify that binplacedir and VBL are (compatible?) release directories
|
||
|
# 1) Find the binplace.log output for both paths
|
||
|
# 2) Figure out what projects were built in the nttree
|
||
|
# 3) Generate a list of files that were built on VBL for the projectlist
|
||
|
# 4) Output a list of files we should have built locally, but didn't
|
||
|
# 5) If (4) is empty, or -force, populate missing files in nttreedir
|
||
|
# from vblreleaseedir forall projects
|
||
|
#
|
||
|
# No files in nttreedir are overwritten from vblreleasedir
|
||
|
# The checks for what should be there are not exact, because we rely only on
|
||
|
# binplace.log entries -- and the VBL build may not exactly match the nttree build.
|
||
|
#
|
||
|
# [-force] -- do copying even if the nttree doesn't contain project files built in VBL
|
||
|
# [-verbose] -- chatter while working
|
||
|
# [-fake] -- don't do the actual copies
|
||
|
# [-checkbinplace] -- note VBL files that are in binplace.log but not build.binlist
|
||
|
# [-fulltargetok] -- run even if the target machine has built in all projects
|
||
|
#
|
||
|
#
|
||
|
# VBLpath will be computed from BuildMachines.txt if not supplied either
|
||
|
# on the command line, or in the VBL_RELEASE environment variable.
|
||
|
#
|
||
|
# If we are a build lab, we succeed without doing much.
|
||
|
#
|
||
|
|
||
|
# WARNING:
|
||
|
# WARNING: make sure pathname comparisons are case insensitive. Either convert the case or do the
|
||
|
# WARNING: comparisons like this:
|
||
|
# WARNING: if ($foo =~ /^\Q$bar\E$/i) {}
|
||
|
# WARNING: or if ($foo !~ /^\Q$bar\E$/i) {}
|
||
|
# WARNING:
|
||
|
|
||
|
#
|
||
|
# BUGBUG: Still need to copy down the compressed directory, per Wade's request...
|
||
|
# ... but I'm really hoping that this will translate into an opportunity not
|
||
|
# ... to copy down the uncompressed version from the VBL... Or, as MarkL suggested,
|
||
|
# ... I should uncompress the compressed version rather than copy it. I'd need to
|
||
|
# ... validate this, maybe in postbuild on the VBL?
|
||
|
|
||
|
|
||
|
$begintime = time();
|
||
|
|
||
|
$VBLPathVariableName = 'VBL_RELEASE';
|
||
|
$BuildMachinesFile = $ENV{ "RazzleToolPath" } . "\\BuildMachines.txt";
|
||
|
$SdDotMapPathname = "sd.map";
|
||
|
$LogFile = "build.populate";
|
||
|
$BinListFile = "build.binlist";
|
||
|
$TestFileName = "build.testpopulate";
|
||
|
$CDDATAFileName = "cddata.txt";
|
||
|
|
||
|
#
|
||
|
# Build the complete list of non-root projects
|
||
|
#
|
||
|
@Projects = (public, mergedcomponents,
|
||
|
admin, base, com, drivers, ds, enduser, inetcore, inetsrv,
|
||
|
multimedia, net, printscan, sdktools, shell, termsrv, windows);
|
||
|
|
||
|
for (@Projects) {
|
||
|
$Project{$_} = 1;
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# Usage variables
|
||
|
#
|
||
|
$PGM='PopulateFromVBL: ';
|
||
|
|
||
|
$Usage = $PGM . "Usage: PopulateFromVBL.pl [-force] [-vbl=vblreleasedir] [-nttree=nttreedir] [-symbols]\n";
|
||
|
|
||
|
#
|
||
|
# Get the current directory
|
||
|
#
|
||
|
open CWD, 'cd 2>&1|';
|
||
|
$CurrDir = <CWD>;
|
||
|
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;
|
||
|
|
||
|
#
|
||
|
# initialize argument variables
|
||
|
#
|
||
|
$Fake = $ENV{'POPULATEFROMVBL_FAKE'};
|
||
|
$Verbose = $ENV{'POPULATEFROMVBL_VERBOSE'};
|
||
|
$Compare = $ENV{'POPULATEFROMVBL_COMPARE'};
|
||
|
$Progress = $ENV{'POPULATEFROMVBL_PROGRESS'};
|
||
|
$Test = $ENV{'POPULATEFROMVBL_TEST'};
|
||
|
$Symbols = $ENV{'POPULATEFROMVBL_SYMBOLS'};
|
||
|
$SkipPats = $ENV{'POPULATEFROMVBL_SKIP'};
|
||
|
$CDDataOnly = $ENV{'POPULATEFROMVBL_CDDATAONLY'};
|
||
|
|
||
|
$Force = 0;
|
||
|
$FullTargetOk = 0;
|
||
|
|
||
|
$CheckBinplace = 0;
|
||
|
|
||
|
#
|
||
|
# Debug routines for printing out variables
|
||
|
#
|
||
|
sub gvar {
|
||
|
for (@_) {
|
||
|
print "\$$_ = $$_\n";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# print on the various files
|
||
|
#
|
||
|
sub printall {
|
||
|
print TSTFILE @_ if $Test;
|
||
|
print LOGFILE @_;
|
||
|
print $PGM unless @_ == 1 and @_[0] eq "\n";
|
||
|
print @_;
|
||
|
}
|
||
|
|
||
|
sub printfall {
|
||
|
printf TSTFILE @_ if $Test;
|
||
|
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 for exploding directory names into a list of components (for mkdir)
|
||
|
#
|
||
|
sub explodedir {
|
||
|
my(@explodelist) = ();
|
||
|
my(@components);
|
||
|
my($path);
|
||
|
|
||
|
for (@_) {
|
||
|
$_ = shift;
|
||
|
@components = split /\\/;
|
||
|
push @components, "";
|
||
|
$path = shift @components;
|
||
|
for (@components) {
|
||
|
push @explodelist, $path;
|
||
|
$path = $path . "\\" . $_;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return @explodelist;
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# Routine to copy a file -- avoiding win32::CopyFile
|
||
|
#
|
||
|
# BUGBUG: This doesn't work. sysread() seems broken.
|
||
|
#
|
||
|
#
|
||
|
|
||
|
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(), 0666) {
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
use File::Copy;
|
||
|
use File::Compare;
|
||
|
|
||
|
|
||
|
#
|
||
|
# Process and validate arguments
|
||
|
#
|
||
|
for (@ARGV) {
|
||
|
if (/^[\/\-]test$/i) { $Test++; next; }
|
||
|
if (/^[\/\-]verbose$/i) { $Verbose++; next; }
|
||
|
if (/^[\/\-]cddataonly$/i) { $CDDataOnly++; next; }
|
||
|
if (/^[\/\-]compare$/i) { $Compare++; next; }
|
||
|
if (/^[\/\-]symbols$/i) { $Symbols++; next; }
|
||
|
if (/^[\/\-]force$/i) { $Force++; next; }
|
||
|
if (/^[\/\-]fake$/i) { $Fake++; next; }
|
||
|
if (/^[\/\-]fulltargetok$/i) { $FullTargetOk++; next; }
|
||
|
if (/^[\/\-]vbl=(.+)$/i) { $VBL = $1; next; }
|
||
|
if (/^[\/\-]nttree=(.+)$/i) { $NTTree = $1; next; }
|
||
|
|
||
|
if (/^[\/\-]skip=(.+)$/i) { $SkipPats .= "$1;"; next; }
|
||
|
|
||
|
if (/^[\/\-]?$/i) { die $Usage; }
|
||
|
if (/^[\/\-]help$/i) { die $Usage; }
|
||
|
|
||
|
if (/^[\/\-]checkbinplace$/i) { $CheckBinplace++; next; }
|
||
|
|
||
|
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;
|
||
|
|
||
|
#
|
||
|
# Can only populate with the current directory the same as sdxroot.
|
||
|
#
|
||
|
die $PGM, "Error: Can only populate if CD <$CurrDir> is SDXROOT <$sdxroot>\n" unless $sdxroot =~ /^\Q$CurrDir\E$/io;
|
||
|
|
||
|
$rc = system 'perl %sdxroot%\Tools\CombineDistributedBinplaceLogs.pl', "-nttree=$NTTree";
|
||
|
die $PGM, "Error: CombineDistributedBinplaceLogs.pl failed.\n" if $rc;
|
||
|
#
|
||
|
# We always need to build a current binlist file -- unless it already exists.
|
||
|
#
|
||
|
$foo = "Creating binlist file with dir command.\n";
|
||
|
print $PGM, $foo;
|
||
|
|
||
|
$NTTreeBinListFile = "$NTTree\\build_logs\\$BinListFile";
|
||
|
if (! -s $NTTreeBinListFile) {
|
||
|
$rc = system "dir /b/s /a-d %_NTTREE% > $NTTreeBinListFile";
|
||
|
die $PGM, "Error: Error building $NTTreeBinListFile: $!\n" if $rc;
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# 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 (<BMFILE>) {
|
||
|
s/\s+//g;
|
||
|
s/;.*$//;
|
||
|
next if /^$/;
|
||
|
($vblmach, $vblprime, $vblbranch, $vblarch, $vbldbgtype, $vbldl, $disttype, $alt_release ) = split /,/;
|
||
|
|
||
|
#
|
||
|
#BUGBUG:
|
||
|
# Should this really come through the environment
|
||
|
# variable that declares this to be a VBL?
|
||
|
#
|
||
|
if ($vblmach =~ /\Q$computername\E/io) {
|
||
|
print $PGM, "Skipping populate because this is a VBL machine.\n";
|
||
|
exit 0;
|
||
|
}
|
||
|
|
||
|
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) {
|
||
|
$VBL = $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;
|
||
|
|
||
|
$SkipPats =~ tr/@/^/;
|
||
|
$SkipPats =~ s/;;+/;/g;
|
||
|
$SkipPats =~ s/\\/\\\\/g;
|
||
|
$SkipPats =~ s/\\\\\./\\./g;
|
||
|
$SkipPats =~ s/^;//;
|
||
|
$SkipPats =~ s/;$//;
|
||
|
@SkipPatterns = split /;/, $SkipPats if $SkipPats;
|
||
|
|
||
|
#
|
||
|
# Fully qualify the pathnames
|
||
|
#
|
||
|
$VBL = fullyqualify($VBL) if $VBL;
|
||
|
$NTTree = fullyqualify($NTTree);
|
||
|
|
||
|
#
|
||
|
# Open the logfile, and maybe the testfile
|
||
|
#
|
||
|
$foo = "$NTTree\\build_logs\\$LogFile";
|
||
|
open LOGFILE, ">>$foo" or die $PGM, "Error: Could not create logfile: ", $foo, ": $!\n";
|
||
|
|
||
|
open TSTFILE, ">$TestFileName" or die $PGM, "Error: Could not create testfile: ", $TestFileName, ": $!\n" if $Test;
|
||
|
|
||
|
#
|
||
|
# Verify that VBL and NTTree are compatible release directories
|
||
|
# BUGBUG:
|
||
|
# For now, this just means ensure they both have build_logs directories.
|
||
|
# It might be nice to check that the builds are from the same branch, and the same main branch build, but ...
|
||
|
#
|
||
|
|
||
|
die $PGM . "Error: The nttree build_logs not found.\n" unless -d "$NTTree\\build_logs\\.";
|
||
|
|
||
|
if ($VBL) {
|
||
|
die $PGM . "Error: The VBL build_logs not found.\n" unless -d "$VBL\\build_logs\\.";
|
||
|
printall "Populating $NTTree from VBL $VBL\n";
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# Process the CDDATA file to build a real copylist.
|
||
|
#
|
||
|
# BUGBUG: I put the code in to do this (if the flag is set), but
|
||
|
# I don't understand how Wade and Mike thought I could use
|
||
|
# this data to automatically trim what gets copied from the VBL.
|
||
|
#
|
||
|
if ($VBL) {
|
||
|
$CDDATAFileName = "$VBL\\build_logs\\$CDDATAFileName";
|
||
|
printall $PGM . "Warning: Could not open $CDDATAFileName: $!\n" unless -r $CDDATAFileName;
|
||
|
|
||
|
@CDData = ();
|
||
|
|
||
|
if ($CDDataOnly) {
|
||
|
open CDDATA, $CDDATAFileName or die $PGM, "Error: Could not open: ", $CDDATAFileName, ": $!\n";
|
||
|
for (<CDDATA>) {
|
||
|
chomp;
|
||
|
s/\s+//g;
|
||
|
s/;.*//;
|
||
|
next if /^$/;
|
||
|
($name, $signed, $prodlist, $iscompressed, $isdriver, $isprinter, $dosnet)
|
||
|
= /(.*)=([tf]):([a-z]+):([tf]):([tf]):([tf]):([tf])/;
|
||
|
|
||
|
printall $PGM . "WARNING: failed to parse cddata line: $_\n" unless $name;
|
||
|
next unless $name;
|
||
|
|
||
|
$CDData{$name}++;
|
||
|
}
|
||
|
close CDDATA;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# Alert that we are skipping certain classes of files
|
||
|
#
|
||
|
printall "Skipping various symbols directories.\n" unless $Symbols;
|
||
|
printall "Skipping delayload directory.\n";
|
||
|
printall "Skip Patterns:\n";
|
||
|
for (@SkipPatterns) {
|
||
|
$pat = $_;
|
||
|
$pat =~ s/\\\\/\\/g;
|
||
|
printall "Skip /$pat/\n";
|
||
|
}
|
||
|
|
||
|
|
||
|
#
|
||
|
# BUGBUG:
|
||
|
# At some point, there will be a file in build_logs which we tell use
|
||
|
# interesting details about a build. We will want to dump out the contents
|
||
|
# of this file for both VBL and NTTree, so the user can see what they
|
||
|
# are getting themselves into.
|
||
|
#
|
||
|
|
||
|
|
||
|
#
|
||
|
# Read in the VBL and NTTree binplace logs and process them
|
||
|
#
|
||
|
open BINPLACE, "$NTTree\\build_logs\\binplace.log"
|
||
|
#or open BINPLACE, "$NTTree\\binplace.log"
|
||
|
or die $PGM, "Error: Could not open: ", "$NTTree\\build_logs\\binplace.log", "\n";
|
||
|
|
||
|
$nignored = 0;
|
||
|
|
||
|
for (<BINPLACE>) {
|
||
|
$whichline++;
|
||
|
tr/A-Z/a-z/;
|
||
|
|
||
|
$skipline = 0;
|
||
|
|
||
|
# First test skips case where NTTree is under SDXROOT and there are binplace records (thanks to SCP)
|
||
|
if (/^\Q$NTTree\E\\/io) {
|
||
|
$skipline = 1;
|
||
|
} elsif (/^\Q$sdxroot\E\\([^\\]+)\\([^\s]+)\\([^\\\s]*)\s+/io) {
|
||
|
$project=$1; $relpath=$2; $filename=$3;
|
||
|
} else {
|
||
|
$skipline = 1;
|
||
|
}
|
||
|
|
||
|
if ($skipline) {
|
||
|
print TSTFILE "Ignored TARG binplace record at line $whichline: ", $_ if $Test;
|
||
|
|
||
|
$nignored++;
|
||
|
if ($Verbose && $nignored <= 10) {
|
||
|
print LOGFILE $PGM . "Ignored TARG binplace record at line $whichline: ", $_;
|
||
|
print LOGFILE $PGM . "...\n" if $nignored == 10;
|
||
|
}
|
||
|
next;
|
||
|
}
|
||
|
|
||
|
$project =~ tr/A-Z/a-z/;
|
||
|
$relpath =~ tr/A-Z/a-z/;
|
||
|
$filename =~ tr/A-Z/a-z/;
|
||
|
|
||
|
if (not $Project{$project}) {
|
||
|
$msg = $PGM . "Error: NTTREE: unknown project '$project' at line $whichline: $_\n";
|
||
|
if ($Fake) { warn $msg; } else { die $msg; }
|
||
|
next;
|
||
|
}
|
||
|
|
||
|
$TargCounts{$project}++;
|
||
|
push @{"T_" . $project . "_binplaced"}, "$relpath\\$filename";
|
||
|
}
|
||
|
close BINPLACE;
|
||
|
|
||
|
if ($Verbose) {
|
||
|
$total = 0;
|
||
|
printall "\n";
|
||
|
printall "NTTree project counts\n";
|
||
|
for (@Projects) {
|
||
|
printfall " %5d %s\n", $TargCounts{$_}, $_;
|
||
|
$total += $TargCounts{$_};
|
||
|
}
|
||
|
printall "-----------------\n";
|
||
|
printfall " %5d TOTAL\n", $total;
|
||
|
printfall " %5d records ignored\n\n", $nignored if $nignored;
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# If files have been binplaced in all the projects, we assume all projects are built locally, and
|
||
|
# don't try to populate -- unless explictly told to do so by the -fulltargetbuildok
|
||
|
#
|
||
|
if (not $FullTargetOk) {
|
||
|
|
||
|
$TargetIsFullBuild = 1;
|
||
|
for (@Projects) {
|
||
|
next if /public/;
|
||
|
next if $TargCounts{$_};
|
||
|
|
||
|
$TargetIsFullBuild = 0;
|
||
|
}
|
||
|
|
||
|
if ($TargetIsFullBuild) {
|
||
|
printall "Not run because $NTTree should be a full build of all projects.\n";
|
||
|
|
||
|
close LOGFILE;
|
||
|
close TSTFILE if $Test;
|
||
|
exit 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
die $PGM, "Error: There was trouble finding a VBL.\n" unless $VBL;
|
||
|
|
||
|
|
||
|
open BINPLACE, "$VBL\\build_logs\\binplace.log"
|
||
|
#or open BINPLACE, "$VBL\\binplace.log"
|
||
|
or die $PGM, "Error: Could not open: ", "$VBL\\build_logs\\binplace.log", "\n";
|
||
|
|
||
|
$nignored = 0;
|
||
|
$whichline = 0;
|
||
|
|
||
|
for (<BINPLACE>) {
|
||
|
$whichline++;
|
||
|
tr/A-Z/a-z/;
|
||
|
|
||
|
#
|
||
|
# BUGBUG: assumes all VBLs build under an sdxroot something like x:\foo
|
||
|
#
|
||
|
if (/^[a-z]:\\[^\\]+\\([^\\]+)\\([^\s]+)\\([^\\\s]*)\s+/io) { $project=$1; $relpath=$2; $filename=$3; }
|
||
|
else {
|
||
|
print TSTFILE "Ignored VBL binplace record at line $whichline: ", $_ if $Test;
|
||
|
|
||
|
$nignored++;
|
||
|
if ($Verbose && $nignored <= 10) {
|
||
|
print LOGFILE $PGM, "Ignored VBL binplace record at line $whichline: ", $_;
|
||
|
print LOGFILE $PGM, "...\n" if $nignored == 10;
|
||
|
}
|
||
|
next;
|
||
|
}
|
||
|
|
||
|
$project =~ tr/A-Z/a-z/;
|
||
|
$relpath =~ tr/A-Z/a-z/;
|
||
|
$filename =~ tr/A-Z/a-z/;
|
||
|
|
||
|
die $PGM . "Error: VBL: unknown project at line $whichline: " . $_ . "\n" unless $Project{$project};
|
||
|
|
||
|
$VBLCounts{$project}++;
|
||
|
push @{"V_" . $project . "_binplaced"}, "$relpath\\$filename";
|
||
|
}
|
||
|
close BINPLACE;
|
||
|
|
||
|
#
|
||
|
# Check that VBL built stuff everywhere, except maybe 'public'.
|
||
|
#
|
||
|
for (@Projects) {
|
||
|
next if /public/;
|
||
|
|
||
|
if (not $VBLCounts{$project}) {
|
||
|
printall "VBL did not build anything in ", $_, "\n";
|
||
|
$fatal++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($Verbose or $fatal) {
|
||
|
$total = 0;
|
||
|
printall "\n";
|
||
|
printall "VBL project counts\n";
|
||
|
for (@Projects) {
|
||
|
printfall " %5d %s\n", $VBLCounts{$_}, $_;
|
||
|
$total += $VBLCounts{$_};
|
||
|
}
|
||
|
printall "-----------------\n";
|
||
|
printfall " %5d TOTAL\n", $total;
|
||
|
printfall " %5d records ignored\n\n", $nignored if $nignored;
|
||
|
}
|
||
|
|
||
|
die $PGM, "Error: VBL release seems bad.\n" if $fatal;
|
||
|
|
||
|
#
|
||
|
# Analyze what got built on the VBL versus the local tree
|
||
|
#
|
||
|
# For each project that we built locally, see if there are any files
|
||
|
# in the VBL tree that we are missing.
|
||
|
#
|
||
|
|
||
|
$NotLocallyPlaced = 0;
|
||
|
%VBLhash = ();
|
||
|
%Targhash = ();
|
||
|
|
||
|
for (@Projects) {
|
||
|
next if /public/ or not $TargCounts{$_};
|
||
|
|
||
|
$project = $_;
|
||
|
|
||
|
#
|
||
|
# Build a hash table for the VBL files, and check target files.
|
||
|
# and vice-versa...
|
||
|
#
|
||
|
for (@{"V_" . $project . "_binplaced"}) {
|
||
|
$VBLhash{$_} = 1;
|
||
|
}
|
||
|
|
||
|
for (@{"T_" . $project . "_binplaced"}) {
|
||
|
printall 'Warning: non-VBL file binplaced on target: ', $_, "\n" unless $VBLhash{$_};
|
||
|
$Targhash{$_} = 1;
|
||
|
}
|
||
|
|
||
|
for (@{"V_" . $project . "_binplaced"}) {
|
||
|
next if $Targhash{$_};
|
||
|
|
||
|
printall 'WARNING: VBL file not binplaced on target: ', $_, "\n";
|
||
|
$NotLocallyPlaced++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($NotLocallyPlaced and not $Force) {
|
||
|
die $PGM, "Error: ", $NotLocallyPlaced, " binplaced VBL files were not binplaced into ", $NTTree, "\n";
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# Thats the checks. Now we just have to do the actual populate.
|
||
|
#
|
||
|
|
||
|
#
|
||
|
# Do a directory listing
|
||
|
# Build build.binlist for NTTREE
|
||
|
# Read in the build.binlist files for NTTree.
|
||
|
# Read in the build.binlist files for the VBL.
|
||
|
#
|
||
|
open BINLIST, "$NTTreeBinListFile"
|
||
|
or die $PGM, "Error: Could not open: ", "$NTTreeBinListFile", "\n";
|
||
|
|
||
|
$whichline = 0;
|
||
|
for (<BINLIST>) {
|
||
|
#
|
||
|
#
|
||
|
$whichline++;
|
||
|
tr/A-Z/a-z/;
|
||
|
chomp;
|
||
|
|
||
|
if (/^\Q$NTTree\E\\([^\s]*)$/io) {
|
||
|
|
||
|
$relpath = $1;
|
||
|
|
||
|
#
|
||
|
# ignore symbol and other directories
|
||
|
#
|
||
|
if (not $Symbols) {
|
||
|
next if /\\symbolcd\\/i;
|
||
|
next if /\\symbols\.pri\\/i;
|
||
|
next if /\\symbols\\/i;
|
||
|
next if /\\scp_wpa\\/i;
|
||
|
|
||
|
# instead we use $SkipPatterns
|
||
|
# next if $relpath =~ /^mstools\\/i;
|
||
|
# next if $relpath =~ /^idw\\/i;
|
||
|
# next if $relpath =~ /^dump\\/i;
|
||
|
# next if $relpath =~ /^clients\\/i;
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# ignore delayload directory
|
||
|
#
|
||
|
next if /\\delayload\\/i;
|
||
|
|
||
|
#
|
||
|
# ignore HelpAndSupportServices directory
|
||
|
#
|
||
|
next if /\\HelpAndSupportServices\\/i;
|
||
|
|
||
|
#
|
||
|
# ignore paths that match skip patterns
|
||
|
#
|
||
|
$skiphit = 0;
|
||
|
for (@SkipPatterns) {
|
||
|
$skiphit = $relpath =~ /$_/i;
|
||
|
$spat = $_;
|
||
|
last if $skiphit;
|
||
|
}
|
||
|
print TSTFILE "TARG: skipping $relpath\n" if $Test and $skiphit;
|
||
|
next if $skiphit;
|
||
|
|
||
|
$TargFileList{$relpath} = 1;
|
||
|
|
||
|
} else {
|
||
|
$fatal++;
|
||
|
printall "Could not parse target build.binplace at line ", $whichline, ": ", $_, "\n";
|
||
|
}
|
||
|
}
|
||
|
close BINLIST;
|
||
|
|
||
|
#
|
||
|
# BUGBUG... in a few releases these will all be in build_logs
|
||
|
#
|
||
|
$foo = "$VBL\\build_logs\\$BinListFile";
|
||
|
open BINLIST, $foo
|
||
|
or open BINLIST, "$VBL\\$BinListFile"
|
||
|
or die $PGM, "Error: Could not open: ", $foo, "\n";
|
||
|
|
||
|
$whichline = 0;
|
||
|
for (<BINLIST>) {
|
||
|
$whichline++;
|
||
|
tr/A-Z/a-z/;
|
||
|
chomp;
|
||
|
|
||
|
if (/^[a-z]:\\[^\\]+\\([^\s]*)$/io) {
|
||
|
|
||
|
$relpath = $1;
|
||
|
|
||
|
#
|
||
|
# skip log files found in VBL.
|
||
|
#
|
||
|
next if /\\build\.[^\\]+$/i;
|
||
|
next if /\\build_logs\\/i;
|
||
|
|
||
|
#
|
||
|
# ignore symbol directories
|
||
|
#
|
||
|
if (not $Symbols) {
|
||
|
next if /\\symbolcd\\/i;
|
||
|
next if /\\symbols\.pri\\/i;
|
||
|
next if /\\symbols\\/i;
|
||
|
|
||
|
# instead we use $SkipPatterns
|
||
|
# next if $relpath =~ /^mstools\\/i;
|
||
|
# next if $relpath =~ /^idw\\/i;
|
||
|
# next if $relpath =~ /^dump\\/i;
|
||
|
# next if $relpath =~ /^clients\\/i;
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# ignore delayload directory
|
||
|
#
|
||
|
next if /\\delayload\\/i;
|
||
|
|
||
|
#
|
||
|
# ignore HelpAndSupportServices directory
|
||
|
#
|
||
|
next if /\\HelpAndSupportServices\\/i;
|
||
|
|
||
|
#
|
||
|
# ignore paths that match skip patterns
|
||
|
#
|
||
|
$skiphit = 0;
|
||
|
for (@SkipPatterns) {
|
||
|
$skiphit = $relpath =~ /$_/i;
|
||
|
$spat = $_;
|
||
|
last if $skiphit;
|
||
|
}
|
||
|
print TSTFILE "VBL: skipping $relpath\n" if $Test and $skiphit;
|
||
|
next if $skiphit;
|
||
|
|
||
|
$VBLFileList{$relpath} = 1;
|
||
|
|
||
|
} else {
|
||
|
$fatal++;
|
||
|
printall "Could not parse VBL build.binplace at line ", $whichline, ": ", $_, "\n";
|
||
|
}
|
||
|
}
|
||
|
close BINLIST;
|
||
|
|
||
|
die $PGM, "Error: Fatal error parsing build.binplace.\n" if $fatal;
|
||
|
|
||
|
|
||
|
#
|
||
|
# Optionally note VBL files that were not binplaced.
|
||
|
#
|
||
|
if ($CheckBinplace) {
|
||
|
printall "Checking non-binplaced VBL files\n";
|
||
|
for (@VBLFileList) {
|
||
|
next unless $VBLhash{$_};
|
||
|
printall "Info: Non-binplaced VBL file: ", $_, "\n";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($Test) {
|
||
|
print TSTFILE "#VBLhash=", scalar keys %VBLhash, " #Targhash=", scalar keys %Targhash, "\n";
|
||
|
print TSTFILE "#VBLFileList=", scalar keys %VBLFileList, " #TargFileList=", scalar keys %TargFileList, "\n";
|
||
|
}
|
||
|
|
||
|
|
||
|
#
|
||
|
# Generate list of files to copy (i.e. every file in VBLFileList not in TargFileList).
|
||
|
#
|
||
|
printall "FAKING -- NO COPYING ACTUALLY BEING DONE\n" if $Fake;
|
||
|
$preptime = time();
|
||
|
|
||
|
$TotalCount = scalar keys %VBLFileList;
|
||
|
$ToCopy = $TotalCount - keys %TargFileList;
|
||
|
|
||
|
if ($TotalCount < 1000 or $ToCopy < 0) {
|
||
|
printall "ERROR: Something wrong with VBL build.binlist -- only $TotalCount files.\n";
|
||
|
exit 1;
|
||
|
}
|
||
|
|
||
|
$CopyCount = 0;
|
||
|
$NonCopyCount = 0;
|
||
|
$CopyBytes = 0;
|
||
|
|
||
|
# 12/28/2000 - added by jonwis
|
||
|
#
|
||
|
# Special code for SxS goop:
|
||
|
# - Copies down the vbl's binplace logs to $NTTree\\build_logs\\$(binplace file name root)-vbl.log-sxs
|
||
|
# This ensures that the sxs wfp updating code will actually pick up the vbl's binplaced assemblies
|
||
|
# as well as assemblies that the user has created.
|
||
|
$vblsxslogs = "$VBL\\build_logs\\binplace*.log-sxs";
|
||
|
for (glob($vblsxslogs)) {
|
||
|
$orig = $_;
|
||
|
s/.*\\(.*)(\.log-sxs)/$1-vbl$2/;
|
||
|
copy ($orig, "$NTTree\\build_logs\\$_") or die "Can't copy down vbl's WinFuse sxs list [$orig]?";
|
||
|
$atleastonesxslogexisted = true;
|
||
|
}
|
||
|
die "No WinFuse build logs exist on build server, can't continue" unless $atleastonesxslogexisted;
|
||
|
|
||
|
|
||
|
|
||
|
printall "Copying $ToCopy files from VBL\n";
|
||
|
|
||
|
for (keys %VBLFileList) {
|
||
|
if ($TargFileList{$_}) {
|
||
|
$NonCopyCount++;
|
||
|
next;
|
||
|
}
|
||
|
|
||
|
$VBfile = "$VBL\\$_";
|
||
|
$NTfile = "$NTTree\\$_";
|
||
|
|
||
|
#
|
||
|
# We try to create each directory the first time we see it, just in case.
|
||
|
#
|
||
|
$dir = $_;
|
||
|
$r = $dir =~ s/\\[^\\]+$//;
|
||
|
if ($r) {
|
||
|
@dirs = explodedir $dir;
|
||
|
for (@dirs) {
|
||
|
$mdname = "$NTTree\\$_";
|
||
|
next if $seencount{$_}++ or -d $mdname;
|
||
|
$r = mkdir $mdname, 0777;
|
||
|
if (not $r) {
|
||
|
printall $PGM . "ERROR: mkdir $mdname FAILED: $!\n";
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$CopyCount++;
|
||
|
if ($Fake) {
|
||
|
print LOGFILE "Faking: copy $VBfile $NTfile\n";
|
||
|
} else {
|
||
|
|
||
|
#
|
||
|
# Do copy.
|
||
|
#
|
||
|
# populatecopy seems to be faster than copy, but what we should
|
||
|
# really get is a parallel copy.
|
||
|
|
||
|
#
|
||
|
# copy has been used more than populatecopy because the latter wasn't
|
||
|
# using O_BINARY when opening the files. populatecopy seems to work fine now,
|
||
|
# but it is only 9% faster -- so we'll stick with copy.
|
||
|
#
|
||
|
|
||
|
# $r = populatecopy ($VBfile, $NTfile);
|
||
|
$r = copy ($VBfile, $NTfile);
|
||
|
|
||
|
print TSTFILE "Copy<$r>: $VBfile -> $NTfile\n" if $Test;
|
||
|
|
||
|
if (not $r) {
|
||
|
printall "FAILED: copy $VBfile $NTfile: $!\n";
|
||
|
} else {
|
||
|
$t = -s $NTfile;
|
||
|
|
||
|
$v = -s $VBfile;
|
||
|
if ($v != $t) {
|
||
|
printall "SIZE ERROR $_: NTTree=$t VBL=$v\n";
|
||
|
}
|
||
|
|
||
|
$CopyBytes += $t;
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# Do comparison, if requested.
|
||
|
#
|
||
|
if ($Compare) {
|
||
|
$r = compare ($VBfile, $NTfile);
|
||
|
if ($r) {
|
||
|
printall "COMPARSION ERROR <$r>: $VBfile $NTfile: $!\n";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# Mark progress (if requested)
|
||
|
# Estimated completion is pretty bogus
|
||
|
# The adaptive timing of updates sort of works. At least
|
||
|
# we aren't checking the time a lot.
|
||
|
#
|
||
|
$datarate = 1024*1024;
|
||
|
if (not $Fake and $Progress) {
|
||
|
if ($CopyBytes > $lastcopybytes + 5*$datarate # every 5 secs
|
||
|
or $CopyCount > $lastcopycount + 100) { # or every 100 files
|
||
|
|
||
|
$lasttime = $preptime unless $lasttime;
|
||
|
|
||
|
$newtime = time();
|
||
|
|
||
|
$datarate = ($CopyBytes-$lastcopybytes)/($newtime - $lasttime);
|
||
|
$esttotalbytes = $CopyBytes * ($ToCopy / $CopyCount);
|
||
|
|
||
|
$eta = ($esttotalbytes - $CopyBytes) / $datarate;
|
||
|
|
||
|
($h0, $m0, $s0) = hms $eta;
|
||
|
|
||
|
$foo = sprintf "Status: %5dMB (%5d of %5d files) copied (%%%5.2f)"
|
||
|
. " %7.2f KB/S estimated complete in %d:%02d:%02d \r",
|
||
|
$CopyBytes/1024/1024,
|
||
|
$CopyCount, $ToCopy,
|
||
|
100 * $CopyCount / $ToCopy,
|
||
|
$datarate/1024, $h0, $m0, $s0;
|
||
|
|
||
|
print $foo;
|
||
|
|
||
|
if ($Test) {
|
||
|
$foo =~ s/\r/\n/;
|
||
|
print TSTFILE $foo;
|
||
|
}
|
||
|
|
||
|
$lastcopybytes = $CopyBytes;
|
||
|
$lastcopycount = $CopyCount;
|
||
|
$lasttime = $newtime;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
printf "\n";
|
||
|
|
||
|
|
||
|
$t0 = $preptime - $begintime;
|
||
|
$t1 = time() - $preptime;
|
||
|
($h0, $m0, $s0) = hms $t0;
|
||
|
($h1, $m1, $s1) = hms $t1;
|
||
|
($h2, $m2, $s2) = hms ($t0 + $t1);
|
||
|
|
||
|
if (not $Fake) {
|
||
|
$KB = $CopyBytes/1024;
|
||
|
$MB = $KB/1024;
|
||
|
|
||
|
$kbrate = $KB/$t1 unless not $t1;
|
||
|
|
||
|
printfall "Populated $NTTree with $CopyCount files (%4.0f MB)"
|
||
|
. " from $VBL [%7.2f KB/S]\n", $MB, $kbrate;
|
||
|
}
|
||
|
|
||
|
|
||
|
printall "NTTree had $NonCopyCount non-replaced files. VBL total files were $TotalCount.\n";
|
||
|
|
||
|
printfall "Preparation 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;
|
||
|
|