Source code of Windows XP (NT5)
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.

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