Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

688 lines
18 KiB

  1. # Filename: sendbuildstats.pl
  2. #
  3. # Have any changes to this file reviewed by DavePr, BryanT, or WadeLa
  4. # before checking in.
  5. # Any changes need to verified in all standard build/rebuild scenarios.
  6. #
  7. require $ENV{'sdxroot'} . '\TOOLS\sendmsg.pl';
  8. #
  9. # Globals
  10. #
  11. $BuildMachinesRelPathname = "TOOLS\\BuildMachines.txt";
  12. $BuildMachinesFile = $ENV{ "RazzleToolPath" } . "\\BuildMachines.txt";
  13. $SdDotMapPathname = "sd.map";
  14. $MaxBuildErrSize = 32 * 1024;
  15. #
  16. # Usage variables
  17. #
  18. $PGM='SendBuildStats:';
  19. $Usage = "\n" . 'Usage: SendBuildStats [-v] [-o | -t] -s | -w | -fb[:build.err] | -fpb[:pberr]' . " [-m msg]\n".
  20. "\n".
  21. " -? | -help help\n".
  22. " -v | -verbose verbose\n".
  23. " -o | -only send only to suspect list\n".
  24. " -t | -too put suspect list on To: line, DL on CC:\n".
  25. " -s | -success build success\n".
  26. " -w | -warn send out warning\n".
  27. " -fb[:fname] | -buildfailure build failed, fname is the build.err file\n".
  28. " -fpb[:fname] | -postbuildfailure post build failed\, fname is the postbuild.err file\n".
  29. " -f fname name of error file (i.e. build.err, postbuild.err)\n".
  30. " -m msg rest of line is a message to include in the mail\n";
  31. #
  32. # debug routine for printing out variables
  33. #
  34. sub pvar {
  35. for (@_) {
  36. print "\$$_ = $$_\n";
  37. }
  38. }
  39. #
  40. # signal catcher (at least this would work on unix)
  41. #
  42. sub catch_ctrlc {
  43. printf $LogHandle "$PGM Aborted.\n" if $LogHandle;
  44. die "$PGM Aborted.\n";
  45. }
  46. $SIG{INT} = \&catch_ctrlc;
  47. #
  48. # Get the current directory
  49. #
  50. open CWD, 'cd 2>&1|';
  51. $CurrDir = <CWD>;
  52. close CWD;
  53. chomp $CurrDir;
  54. $CurrDrive = substr($CurrDir, 0, 2);
  55. #
  56. # Check variables expected to be set in the environment.
  57. #
  58. $sdxroot = $ENV{'SDXROOT'} or die $PGM, "SDXROOT not set in environment\n";
  59. $MyComputername = $ENV{'COMPUTERNAME'} or die $PGM, "COMPUTERNAME not set in environment\n";
  60. $MyBuildArch = $ENV{'_BuildArch'} or die $PGM, "_BuildArch not set in environment\n";
  61. $MyBuildType = $ENV{'_BuildType'} or die $PGM, "_BuildType not set in environment\n";
  62. $MyBranch = $ENV{'_BuildBranch'} or die $PGM, "_BuildBranch not set in environment\n";
  63. $BuildDotChanges = $ENV{'sdxroot'} . '\build.changes';
  64. $BuildDotChangedFiles = $ENV{'sdxroot'} . '\build.changedfiles';
  65. $BuildDateFile = $ENV{'sdxroot'} . '\__blddate__';
  66. $BuildMailMsg = $ENV{'BuildMailMsg'};
  67. $BuildNumberFile = $ENV{'sdxroot'} . '\__bldnum__';
  68. #
  69. # Initialize variables
  70. #
  71. $Success = 0; # only one of these gets set
  72. $Warn = 0;
  73. $BuildFailed = 0;
  74. $PostBuildFailed = 0;
  75. $Fail = 0; # set if either BuildFailed or PostBuildFailed
  76. $ErrorFile = 0; # will hold the name of the build.err file
  77. $Fake = 0; # fake output flag
  78. $Verbose = 0; # verbose output flag
  79. $SuspectsOnly = 0; # flag to send mail only to the suspect list.
  80. $SuspectsToo = 0; # flag to send mail to the suspect list and CC everyone else.
  81. $BuildDate = ""; # set on first call to ReadBuildDate()
  82. $BuildNumber = ""; # set on first call to ReadBuildNumber()
  83. #
  84. # Determine if this machine is an Official Build machine or not
  85. #
  86. $OfficialBuildMachine = $ENV{'OFFICIAL_BUILD_MACHINE'};
  87. $CoverageBuild = $ENV{'_COVERAGE_BUILD'};
  88. if (!$BuildMailMsg) {
  89. if ($OfficialBuildMachine) {
  90. $BuildMailMsg = "Official Build";
  91. } else {
  92. $BuildMailMsg = "Private Build";
  93. }
  94. }
  95. #
  96. # BUGBUG - I don't understand why there is an OfficeBuildMachine flag and a PrivateBuild flag
  97. # they appear not to be mutually excusive
  98. #
  99. $BuildCategory = "Unknown";
  100. $BuildCategory = "Private" if $BuildMailMsg =~ /priv/i;
  101. $BuildCategory = "OFFICIAL" if $BuildMailMsg =~ /official/i;
  102. $BuildCategory = "MiniLoop" if $BuildMailMsg =~ /mini/i;
  103. $BuildCategory = "Coverage" if $BuildMailMsg =~ /coverage/i;
  104. $SpecialMsg = 0;
  105. #
  106. # Get Complete Build Number if possible
  107. #
  108. $CompleteBuildNumber = ReadBuildNumber() . "\.$MyBuildArch$MyBuildType\.$MyBranch\." . ReadBuildDate();
  109. #
  110. # process arguments
  111. #
  112. for (@ARGV) {
  113. if ($SpecialMsg) {
  114. $SpecialMsg .= " $_";
  115. next;
  116. }
  117. if (/^-m$/i or /^-msg$/i or /^-message$/i) {
  118. $SpecialMsg = "***";
  119. next;
  120. }
  121. if ($GetFname) {
  122. $fname = $_;
  123. $GetFname = 0;
  124. next;
  125. }
  126. if (/^-fake$/i) {
  127. $Fake++;
  128. next;
  129. }
  130. if (/^-v$/i or /^-verbose$/i) {
  131. $Verbose++;
  132. next;
  133. }
  134. if (/^-t$/i or /^-too$/) {
  135. $SuspectsToo++;
  136. next;
  137. }
  138. if (/^-o$/i or /^-only$/) {
  139. $SuspectsOnly++;
  140. next;
  141. }
  142. if (/^-s$/i or /^-success$/i or /^-successful$/i) {
  143. $Success++;
  144. next;
  145. }
  146. if (/^-w$/i or /^-warn$/i) {
  147. $Warn++;
  148. next;
  149. }
  150. if (/^-f$/i) {
  151. $GetFname = 1;
  152. next;
  153. }
  154. if (/^-fb(:.*)?$/i or /^-buildfailure(:.*)?$/i) {
  155. $BuildFailed++;
  156. $Fail++;
  157. if ($1) {
  158. $ErrorFile = $1;
  159. $ErrorFile =~ s/://;
  160. $SetErrFile++;
  161. } else {
  162. $ErrorFile = $ENV{'sdxroot'} . '\build.err';
  163. }
  164. next;
  165. }
  166. if (/^-fpb(:.*)?$/i or /^-postbuildfailure(:.*)?$/i) {
  167. $PostBuildFailed++;
  168. $Fail++;
  169. if ($1) {
  170. $ErrorFile = $1;
  171. $ErrorFile =~ s/://;
  172. $SetErrFile++;
  173. } else {
  174. if ( -e $ENV{'_NTTREE'} . '\build_logs\postbuild.err') {
  175. $ErrorFile = $ENV{'_NTTREE'} . '\build_logs\postbuild.err';
  176. } else {
  177. $ErrorFile = '\\\\' . $MyComputername . '\latest\build_logs\postbuild.err';
  178. }
  179. }
  180. next;
  181. }
  182. if (/^-?$/i or /^-help$/) {
  183. print $Usage;
  184. exit 0;
  185. }
  186. die $Usage;
  187. }
  188. $SpecialMsg .= " ***\n" if $SpecialMsg;
  189. #
  190. # Sanity Check arguments
  191. #
  192. pvar Success, Warn, BuildFailed, ErrorFile, PostBuildFailed, fname, SetErrFile, SuspectsOnly if $Verbose;
  193. die $Usage unless $Success + $Warn + $BuildFailed + $PostBuildFailed == 1;
  194. die $Usage unless $SuspectsOnly + $SuspectsToo <= 1;
  195. die $Usage if $fname and $SetErrFile;
  196. $ErrorFile = $fname if $fname;
  197. #
  198. # Compute MyDl, BuildChanges, Changers, and Suspects
  199. # Will use to decide the recipients to send Message To
  200. #
  201. SetMyDl();
  202. GetChangersAndSuspects();
  203. #
  204. # Generate the appropriate build message.
  205. #
  206. $BuildMail = FormatBuildMailStart();
  207. $PrivateBuild = ($BuildCategory =~ /private/i);
  208. # PriveteBuild gets messed up if it is a coverage build
  209. # so set PrivateBuild to true if it is a non-offical coverage build
  210. if(!$PrivateBuild && $CoverageBuild && !$OfficialBuildMachine)
  211. {
  212. $PrivateBuild = 1;
  213. }
  214. if ($Success) {
  215. #
  216. # Build was successful, so format a successful build message, and
  217. # send success build mail
  218. #
  219. #$BuildMailSubject = "Build Succeeded: $MyBuildArch$MyBuildType $CompleteBuildNumber";
  220. $BuildMailSubject = "BUILD $MyComputername/$MyBranch: $BuildCategory $BuildDate $MyBuildArch$MyBuildType SUCCEEDED";
  221. if ($PrivateBuild) {
  222. $BuildMail .= "Build is available on $ENV{'_NTTREE'}\n";
  223. } else {
  224. if($CoverageBuild){
  225. $ReleaseShare = "latest_cov";
  226. }else{
  227. $ReleaseShare = "latest";
  228. }
  229. $BuildMail .= "Build is available on \\\\$MyComputername\\$ReleaseShare\n" . "\nChanges for this build include\n\n\n" . $BuildChanges;
  230. }
  231. } elsif ($Warn) {
  232. #
  233. # We are sending a warning message.
  234. #
  235. $BuildMailSubject = "BUILD $MyComputername/$MyBranch: $BuildCategory $BuildDate $MyBuildArch$MyBuildType warning";
  236. } elsif ($BuildFailed) {
  237. #
  238. # Build failed, so format either a build.exe failure email and log data,
  239. #
  240. #$BuildMailSubject = "Build Failed: $MyBuildArch$MyBuildType $CompleteBuildNumber";
  241. $BuildMailSubject = "BUILD $MyComputername/$MyBranch: $BuildCategory $BuildDate $MyBuildArch$MyBuildType FAILED";
  242. $BuildMail .= "These failures occurred:\n\n\n" . $BuildErrs . "\n";
  243. $BuildMail .= "\nChanges for this build include\n\n\n" . $BuildChanges unless $PrivateBuild;
  244. } else { # $PostBuildFailed
  245. #
  246. # or a postbuild failure email and log data
  247. #
  248. #$BuildMailSubject = "Build Failed (PostBuild): $MyBuildArch$MyBuildType $CompleteBuildNumber";
  249. $BuildMailSubject = "BUILD $MyComputername/$MyBranch: $BuildCategory $BuildDate $MyBuildArch$MyBuildType FAILED - POSTBUILD";
  250. $BuildMail .= "PostBuild Failure:\n\n\n" . ReadErrorFile();
  251. $BuildMail .= $ErrorFileContents if $ErrorFileContents;
  252. $BuildMail .= "\nChanges for this build include\n\n\n" . $BuildChanges unless $PrivateBuild;
  253. }
  254. pvar BuildMailSubject,MyDl,MyComputername,MyBuildArch,MyBuildType if $Verbose;
  255. print $BuildMail if $Verbose;
  256. if ($PrivateBuild) {
  257. @MailTargets = ($MyDl);
  258. } elsif ($SuspectsOnly and scalar @Suspects) {
  259. @MailTargets = @Suspects;
  260. } elsif ($SuspectsToo and scalar @Suspects) {
  261. @MailTargets = ("CC:$MyDl", @Suspects);
  262. } elsif (scalar @Changers) {
  263. @MailTargets = ("CC:$MyDl", @Changers);
  264. } else {
  265. @MailTargets = ("$MyDl");
  266. }
  267. if ($Fake) {
  268. print "sendmsg parameters:\n";
  269. for ('-v', $MyDl."DisableOof", $BuildMailSubject, $BuildMail, @MailTargets) {
  270. print "<<$_>>";
  271. }
  272. print "\n";
  273. } else {
  274. #
  275. # Really send the message
  276. #
  277. $rc = sendmsg ('-v', $MyDl."DisableOof", $BuildMailSubject, $BuildMail, @MailTargets);
  278. print "WARNING: sendmsg failed!\n" if $rc;
  279. }
  280. exit 0;
  281. ##
  282. ## Support Subroutine Section
  283. ##
  284. #
  285. # Set MyDl.
  286. # For official build machines, extract this from BuildMachines.txt,
  287. # otherwise use _NT_BUILD_DL, USERNAME, or the script maintainer -- in that order.
  288. #
  289. sub SetMyDl {
  290. if ($OfficialBuildMachine) {
  291. $fname = $BuildMachinesFile;
  292. open BMFILE, $fname or die "Could not open: $fname\n";
  293. for (<BMFILE>) {
  294. s/\s+//g;
  295. s/;.*$//;
  296. next if /^$/;
  297. my($vblmach, $vblprime, $vblbranch, $vblarch, $vbldbgtype, $vbldl, $disttype ) = split /,/;
  298. #
  299. # The BuildMachines.txt record is keyed by computername, architecture, type, and branch
  300. #
  301. if ( ($vblmach =~ /\Q$MyComputername\E/io) &&
  302. ($vblarch =~ /\Q$MyBuildArch\E/io) &&
  303. ($vbldbgtype =~ /\Q$MyBuildType\E/io) &&
  304. ($vblbranch =~ /\Q$MyBranch\E/io) ) {
  305. close BMFILE;
  306. $MyPrime = $vblprime;
  307. $MyDl = $vbldl;
  308. return;
  309. }
  310. }
  311. printf $PGM . "Problem Encounterd. $MyComputername was NOT found in buildmachines.txt. dl defaults to DavePr\n";
  312. $MyDl = "DavePr";
  313. close BMFILE;
  314. } else {
  315. $MyDl = $ENV{'_NT_BUILD_DL'};
  316. if (!$MyDl) {
  317. $MyDl = $ENV{'USERNAME'} or $MyDl = "DavePr";
  318. }
  319. }
  320. }
  321. #
  322. # Construct the base message used in the various cases.
  323. #
  324. sub FormatBuildMailStart {
  325. my($msg);
  326. my($BuildDate);
  327. my($MacroName);
  328. if ($Success) {
  329. $msg = "Build Was Successful\n\n";
  330. } elsif ($Warn) {
  331. $msg = "Build early-warning message\n\n";
  332. } elsif ($BuildFailed) {
  333. $msg = "Build errors were found\n\n";
  334. } else { # $PostBuildFailed
  335. $msg = "Postbuild errors were found\n\n";
  336. }
  337. if (scalar @Suspects) {
  338. $msg .= "SUSPECTS:";
  339. for (@Suspects) {
  340. $msg .= " $_";
  341. }
  342. $msg .= "\n\n";
  343. }
  344. $msg .= $BuildMailMsg . "\n" if $BuildMailMsg;
  345. $msg .= $SpecialMsg . "\n" if $SpecialMsg;
  346. $msg .= "\nBuild Name : $CompleteBuildNumber\n";
  347. $msg .= "\nBuild Date : " . ReadBuildDate() . "\n";
  348. $msg .= "Build Machine: $MyComputername\n";
  349. $msg .= "Architecture : $MyBuildArch\n";
  350. $msg .= "DbgType : $MyBuildType\n";
  351. $msg .= "Branch : $MyBranch\n";
  352. $msg .= "SdxRoot : $sdxroot\n";
  353. $msg .= "DL Notified : $MyDl\n";
  354. $msg .= "\n\n";
  355. return $msg;
  356. }
  357. #
  358. # Canonicalize the prefix of a build path so we can make guesses about who
  359. # might have caused a build error based on who made changes to what.
  360. #
  361. sub CanonicalizeBuildPath {
  362. $_ = @_[0];
  363. s/\\[^\\]+$//; # remove filename
  364. s/\\obj[^\\]*\\.*//i; # ignore OBJ directories
  365. s/\\daytona\\.*//i; # ignore common sub-directories (
  366. s/\\i386\\.*//i;
  367. s/\\amd64\\.*//i;
  368. s/\\ia64\\.*//i;
  369. s/\\daytona\\.*//i;
  370. s/\\i386\\.*//i;
  371. s/\\amd64\\.*//i;
  372. s/\\ia64\\.*//i;
  373. s/^[a-z]:\\[^\\]+\\//i; # remove sdxroot
  374. # remove last directory component -- if we have at least three
  375. s/\\[^\\]+$// if 3 <= split /\\/;
  376. return $_;
  377. }
  378. #
  379. # As build error file is read, we are called to record the canonicalized paths found.
  380. #
  381. sub CaptureBuildFailure {
  382. $_ = @_[0];
  383. chomp;
  384. $capture = "";
  385. if (/NMAKE/) {
  386. if (/U1073/) {
  387. s/'$//;
  388. s/.*//;
  389. $capture = $_;
  390. }
  391. } else {
  392. s/[ (].*//;
  393. s/^[0-9]*>//;
  394. $capture = $_;
  395. }
  396. if ($capture) {
  397. $capture = CanonicalizeBuildPath $capture;
  398. $BuildFailure{$capture}++;
  399. }
  400. }
  401. #
  402. # Set $BuildChanges, $BuildErrs, @Changers, and @Suspects -- as appropriate.
  403. #
  404. sub GetChangersAndSuspects {
  405. $BuildChanges = "";
  406. $BuildErrs = "Build Errors from $ErrorFile\n";
  407. @Changers = ();
  408. @Suspects = ();
  409. $TruncatedBuildErrLines = 0;
  410. #
  411. # If this was a build failure, process ErrorFile
  412. #
  413. if ($BuildFailed) {
  414. my($rc) = open FD, $ErrorFile or warn $ErrorFile, ": ", $!, "\n";
  415. if ($rc) {
  416. for (<FD>) {
  417. if (length $BuildErrs < $MaxBuildErrSize) {
  418. $BuildErrs .= $_;
  419. } else {
  420. $TruncatedBuildErrLines++;
  421. }
  422. CaptureBuildFailure($_);
  423. }
  424. close FD;
  425. $BuildErrs .= "\n< Truncated last $TruncatedBuildErrLines lines of $ErrorFile >\n" if $TruncatedBuildErrLines;
  426. } else {
  427. $BuildErrs = "Sorry, unable to locate $ErrorFile\n";
  428. }
  429. }
  430. #
  431. # Get the Changers and record the BuildChanges for use in the BuildMail.
  432. #
  433. my($rcc) = open FD, $BuildDotChanges or warn $BuildDotChanges, ": ", $!, "\n";
  434. if ($rcc) {
  435. %Checklist=();
  436. for (<FD>) {
  437. $BuildChanges .= $_;
  438. next unless /^Change /;
  439. chop;
  440. s/'.*$//;
  441. s/.* by //;
  442. s/@.*//;
  443. s/.*\\//;
  444. tr/A-Z/a-z/;
  445. $Checklist{$_}++;
  446. }
  447. close FD;
  448. @Changers = sort keys %Checklist;
  449. } else {
  450. $BuildChanges = "Sorry, unable to locate $BuildDotChanges\n";
  451. }
  452. #
  453. # Get the Suspects
  454. #
  455. if ($BuildFailed) {
  456. my($project, $change, $dev, $date, $time, $sdpath, $type);
  457. $rcc = open FD, $BuildDotChangedFiles or warn $BuildDotChangedFiles, ": ", $!, "\n";
  458. if ($rcc) {
  459. %Checklist=();
  460. for (<FD>) {
  461. my($project, $change, $dev, $date, $time, $sdpath, $type) = split;
  462. next unless $type;
  463. $_ = $sdpath;
  464. s|#.*||; # strip #change
  465. s|//depot/[^/]*/||i; # strip //depot/lab
  466. tr|/|\\|; # / -> \
  467. $canonpath = CanonicalizeBuildPath $_;
  468. next unless $BuildFailure{$canonpath};
  469. print "Suspect $dev because of change $change affecting $canonpath\n" if $Verbose and not $Pinged{$change};
  470. $Pinged{$change}++;
  471. $Checklist{$dev}++;
  472. }
  473. close FD;
  474. @Suspects = sort keys %Checklist;
  475. }
  476. }
  477. }
  478. #
  479. # Return the contents of the ErrorFile file.
  480. #
  481. sub ReadErrorFile {
  482. my($pbcontents) = "";
  483. my(@errfiles) = ();
  484. $ErrorFileContents = ""; # global
  485. $rc = open FD, $ErrorFile;
  486. return "Sorry, unable to locate $ErrorFile\n" unless $rc;
  487. for (<FD>) {
  488. $pbcontents .= $_;
  489. if (/\ssee:?\s+(\S+)/i) {
  490. $foo = $1;
  491. $foo =~ s/\.$//;
  492. push @errfiles, $foo;
  493. }
  494. }
  495. close FD;
  496. for (@errfiles) {
  497. $rc = open FD, $_;
  498. if (not $rc) {
  499. $ErrorFileContents .= "\nUnable to open $_: $!\n";
  500. next;
  501. }
  502. $ErrorFileContents .= "\nContents of $_\n";
  503. for (<FD>) {
  504. $ErrorFileContents .= $_;
  505. }
  506. close FD;
  507. $ErrorFileContents .= "\n";
  508. }
  509. return $pbcontents;
  510. }
  511. #
  512. # Set BuildDate from the contents of the BuildDate file and return it.
  513. #
  514. sub ReadBuildDate {
  515. my($rc, $mname, $bd);
  516. return $BuildDate if $BuildDate;
  517. $BuildDate = "UnknownBuildDate";
  518. $rc = open FD, $BuildDateFile or warn $BuildDateFile, ": ", $!, "\n";
  519. return $BuildDate unless $rc;
  520. for (<FD>) {
  521. chomp;
  522. ($mname, $bd) = split /=/;
  523. $BuildDate = $bd if $mname =~ /BUILDDATE/i;
  524. }
  525. close FD;
  526. return $BuildDate;
  527. }
  528. #
  529. # Set BuildDate from the contents of the BuildDate file and return it.
  530. #
  531. sub ReadBuildNumber {
  532. my($rc, $mname, $bn);
  533. return $BuildNumber if $BuildNumber;
  534. $BuildNumber = "UnknownBuildNumber";
  535. $rc = open FD, $BuildNumberFile or warn $BuildNumberFile, ": ", $!, "\n";
  536. return $BuildNumber unless $rc;
  537. for (<FD>) {
  538. chomp;
  539. ($mname, $bn) = split /=/;
  540. $BuildNumber = $bn if $mname =~ /BUILDNUMBER/i;
  541. }
  542. close FD;
  543. return $BuildNumber;
  544. }