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.

933 lines
27 KiB

  1. @echo off
  2. REM ------------------------------------------------------------------
  3. REM
  4. REM pbuild.cmd
  5. REM Drives postbuild using information in pbuild.dat
  6. REM
  7. REM Copyright (c) Microsoft Corporation. All rights reserved.
  8. REM
  9. REM ------------------------------------------------------------------
  10. perl -x "%~f0" %*
  11. goto :EOF
  12. #!perl
  13. use strict;
  14. use lib $ENV{RAZZLETOOLPATH} . "\\PostBuildScripts";
  15. use lib $ENV{RAZZLETOOLPATH};
  16. use PbuildEnv;
  17. use ParseArgs;
  18. use Logmsg;
  19. sub Usage { print<<USAGE; exit(1) }
  20. Pbuild [-d <datafile>] [-l <language>]
  21. -d datafile Use <datafile> instead of pbuild.dat
  22. This program processes a pbuild.dat file and issues the appropriate commands
  23. in a multithreaded manner to run all or some of the postbuild process.
  24. USAGE
  25. my $PrivateDataFile;
  26. parseargs('?' => \&Usage,
  27. 'd:'=> \$PrivateDataFile);
  28. # Global variable section
  29. my( $PBS ) = $ENV{ "RazzleToolPath" } . "\\PostBuildScripts";
  30. my( $AmOfficial ) = $ENV{ "OFFICIAL_BUILD_MACHINE" };
  31. my( $IsCoverageBuild ) = $ENV{ "_COVERAGE_BUILD" };
  32. my( $MaxThreads ) = $ENV{ "NUMBER_OF_PROCESSORS" };
  33. my( $Language ) = $ENV{ "lang" };
  34. my( $HorsePower ) = $ENV{ "HORSE_POWER" };
  35. my( $Comp ) = $ENV{ "COMP" };
  36. my( $NumProcs ) = $ENV{ "NUMBER_OF_PROCESSORS" };
  37. my( $MainBuildLab ) = $ENV{ "MAIN_BUILD_LAB_MACHINE" };
  38. my( $IntelMachine ) = $ENV{ "x86" };
  39. my( $Bit32 ) ;
  40. my( $Bit64 );
  41. if ( $ENV{ "_BuildArch" } =~ /x86/i ) { $Bit32 = "TRUE"; }
  42. if ( $ENV{ "_BuildArch" } =~ /amd64|ia64/i ) { $Bit64 = "TRUE"; }
  43. my( $NReqType ) = 0;
  44. my( $NNeedToDo ) = 1;
  45. my( $NInstance ) = 2;
  46. my( $NCommand ) = 3;
  47. my( $NOptions ) = 4;
  48. # declare globals
  49. my( $ExitCode, $Return, $AmIncremental );
  50. my( @Requests, @EndList, $TimingFile, %HelpMessages );
  51. # zeroth: init vars
  52. &InitializeVariables();
  53. # first: parse the data file
  54. my( $DataFileName );
  55. if ( defined( $PrivateDataFile ) ) {
  56. $DataFileName = $PrivateDataFile;
  57. } else {
  58. $DataFileName = $PBS . "\\pbuild.dat";
  59. }
  60. $Return = &ParseDataFile( $DataFileName );
  61. if ( $Return eq "FATAL" ) { goto QuitNow; }
  62. # debug: show the request list
  63. if ( $Logmsg::DEBUG ) { &DebugOnly(); }
  64. # second: kick off all the stuff we need to do
  65. $Return = &RunCommands();
  66. if ( $Return eq "FATAL" ) { goto QuitNow; }
  67. # last: display some interesting statistics
  68. print( "\n" );
  69. if ( -e $TimingFile ) { system( "type $TimingFile" ); }
  70. # exit gracefully, if using a goto is your idea of graceful
  71. QuitNow:
  72. exit;
  73. #
  74. # DebugOnly
  75. #
  76. # Arguments: variable
  77. #
  78. # Purpose: a general routine for holding lots of debug code that can easily
  79. # be turned off
  80. #
  81. sub DebugOnly
  82. {
  83. # get passed args
  84. # my( $FooBar ) = @_;
  85. # declare locals
  86. my( $Request );
  87. # dbgmsg( "In subroutine DebugOnly ..." );
  88. # dbgmsg( "Request list is:" );
  89. foreach $Request ( @Requests ) {
  90. # dbgmsg( "$Request" );
  91. }
  92. # dbgmsg( "End of request list." );
  93. }
  94. #
  95. # InitializeVariables
  96. #
  97. # Arguments: none
  98. #
  99. # Purpose: just set up the initial vars in the way we need them.
  100. # nothing fancy.
  101. #
  102. # Returns: nothing
  103. #
  104. sub InitializeVariables
  105. {
  106. # declare locals
  107. my( $HelpFile, @HelpLines, $Command, $HelpText, $Line );
  108. my( $IncFile );
  109. # dbgmsg( "In subroutine InitializeVariables ..." );
  110. # check for incremental builds
  111. undef( $AmIncremental );
  112. $IncFile = $ENV{ "_NTPostBld" } . "\\congeal_scripts\\firstpass.txt";
  113. if ( ! ( -e $IncFile ) ) {
  114. $AmIncremental = "TRUE";
  115. }
  116. # init setup stuff
  117. #$Logmsg::DEBUG = 1; # set to 1 to activate logging of dbgmsg's
  118. $TimingFile = $ENV{logfile} . ".timing.log";
  119. if ( -e $TimingFile ) { system( "del /f $TimingFile" ); }
  120. $ExitCode = 0;
  121. # Set maxthreads equal to numprocs * horse_power
  122. if ( $HorsePower ) {
  123. $MaxThreads = $NumProcs * $HorsePower;
  124. } else {
  125. $MaxThreads = $NumProcs * 4;
  126. }
  127. # read in the help message file
  128. $HelpFile = $ENV{ "RazzleToolPath" } . "\\PostBuildScripts\\pbuild.hlp";
  129. unless ( open( INFILE, $HelpFile ) ) {
  130. wrnmsg( "Failed to open help file." );
  131. goto PastHelpFile;
  132. }
  133. @HelpLines = <INFILE>;
  134. close( INFILE );
  135. foreach $Line ( @HelpLines ) {
  136. chomp( $Line );
  137. ( $Command, $HelpText ) = split( /\s+\:\s+/, $Line );
  138. if ( $Command =~ /.*\\([^\\]+?)$/ ) { $Command = $1; }
  139. $Command = "\L$Command";
  140. $HelpMessages{ $Command } = $HelpText;
  141. # dbgmsg( "Help Message for '$Command' : $HelpMessages{ $Command }" );
  142. }
  143. PastHelpFile:
  144. return;
  145. }
  146. #
  147. # ParseDataFile
  148. #
  149. # Arguments: $DataFileName
  150. #
  151. # Purpose: read in the given data file, and figure out what requests we will
  152. # need to run on this machine, storing the resultant requests in
  153. # @Requests.
  154. #
  155. # Returns: nothing
  156. #
  157. sub ParseDataFile
  158. {
  159. # get passed args
  160. my( $DataFileName ) = @_;
  161. # declare locals
  162. my( @DataLines, $Line );
  163. my( $ThisReqType, $ThisCommand, $ThisOptions, $ShortName );
  164. my( $NewRequest, $Request, $FoundBegin, $ThisShortName );
  165. # dbgmsg( "In subroutine ParseDataFile ..." );
  166. unless ( open( INFILE, $DataFileName ) ) {
  167. errmsg( "ParseDataFile:" );
  168. errmsg( "Failed to open $DataFileName for reading." );
  169. $ExitCode++;
  170. return( "FATAL" );
  171. }
  172. @DataLines = <INFILE>;
  173. close( INFILE );
  174. foreach $Line ( @DataLines ) {
  175. chomp( $Line );
  176. # see if this is a real line
  177. if ( ( length( $Line ) == 0 ) || ( $Line =~ /^\;/ ) ) { next; }
  178. # get the interesting info out of the file line
  179. ( $ThisReqType, $ThisCommand, $ThisOptions ) = split( /\s/, $Line, 3 );
  180. # first things first, see if we need to do this one
  181. if ( ( $ThisReqType =~ /OFFICIAL/i ) &&
  182. ( ! defined( $AmOfficial ) ) ) {
  183. # dbgmsg( "Not official, skipping $Line ..." );
  184. next;
  185. }
  186. if ( ( $ThisReqType =~ /INCREMENTAL/i ) &&
  187. ( $AmIncremental ne "TRUE" ) ) {
  188. dbgmsg( "Not incremental pass, skipping $Line ..." );
  189. next;
  190. }
  191. # check our full pass file and first pass file
  192. my( $FullPassFile ) = $ENV{ "_NTPOSTBLD" } . "\\build_logs\\FullPass.txt";
  193. my( $FirstPassFile ) = $ENV{ "_NTPOSTBLD" } . "\\congeal_scripts\\FirstPass.txt";
  194. if ( ( $ThisReqType =~ /FULL/i ) &&
  195. ( ! -e $FullPassFile ) &&
  196. ( ! -e $FirstPassFile ) ) {
  197. dbgmsg( "Not full pass, skipping $Line ..." );
  198. next;
  199. }
  200. if ( ( $ThisReqType =~ /COVERAGE/i ) &&
  201. ( ! defined ( $IsCoverageBuild ) ) ) {
  202. dbgmsg( "Not coverage build, skipping $Line ..." );
  203. next;
  204. }
  205. if ( ( $ThisReqType =~ /MAIN/i ) &&
  206. ( ! defined( $MainBuildLab ) ) ) {
  207. dbgmsg( "Not main build lab, skipping $Line ..." );
  208. next;
  209. }
  210. if ( ( $ThisReqType =~ /COMP/i ) &&
  211. ( $Comp !~ /yes/i ) ) {
  212. # dbgmsg( "COMP not set, skipping $Line ..." );
  213. next;
  214. }
  215. if ( ( $ThisReqType =~ /INTEL/i ) &&
  216. ( ! defined( $IntelMachine ) ) ) {
  217. dbgmsg( "Not intel machine, skipping $Line ..." );
  218. next;
  219. }
  220. if ( ( $ThisReqType =~ /32/i ) &&
  221. ( ! defined( $Bit32 ) ) ) {
  222. dbgmsg( "Not 32bit machine, skipping $Line ..." );
  223. next;
  224. }
  225. if ( ( $ThisReqType =~ /64/i ) &&
  226. ( ! defined( $Bit64 ) ) ) {
  227. dbgmsg( "Not 64bit machine, skipping $Line ..." );
  228. next;
  229. }
  230. # now, if we passed all parameters, our req type will be begin
  231. if ( ( $ThisReqType !~ /checkfatal/i ) &&
  232. ( $ThisReqType !~ /END/i ) ) {
  233. $ThisReqType = "BEGIN";
  234. }
  235. # create the short name of the command for instance counting
  236. if ( $ThisCommand =~ /.*\\([^\\]+?)$/ ) { $ShortName = $1; }
  237. else { $ShortName = $ThisCommand; }
  238. # begin populating the new request
  239. # start with the request type and the NeedToDo
  240. $NewRequest = "\U$ThisReqType\:t\:";
  241. # tack on the unique identifier
  242. $NewRequest .= &GetNextInstanceNumber( $ShortName, $ThisReqType ) .
  243. ":";
  244. # tack on the command and the options
  245. $NewRequest .= $ThisCommand . ":" . $ThisOptions;
  246. # now, if this is an /END/ request, make sure the associated /BEGIN/
  247. # is in our requests
  248. if ( $ThisReqType =~ /END/i ) {
  249. undef( $FoundBegin );
  250. foreach $Request ( @Requests ) {
  251. $ThisShortName = &GetField( $Request, $NCommand );
  252. if ( $ThisShortName =~ /.*\\([^\\]+?)$/ ) {
  253. $ThisShortName = $1;
  254. }
  255. if ( ( "\L$ShortName" eq "\L$ThisShortName" ) &&
  256. ( &GetField( $Request, $NReqType ) =~ /BEGIN/i ) ) {
  257. $FoundBegin = "TRUE";
  258. }
  259. }
  260. if ( ! defined( $FoundBegin ) ) {
  261. next;
  262. }
  263. }
  264. # finally, add the request to the list
  265. push( @Requests, $NewRequest );
  266. }
  267. }
  268. #
  269. # GetNextInstanceNumber
  270. #
  271. # Arguments: $CommandName, $ReqType
  272. #
  273. # Purpose: this routine will return a unique number for this instance of the
  274. # called command. e.g. if the data file calls crypto.cmd 4 times, and
  275. # we're processing the third one, this should return 2, as the first
  276. # returned 0 and the second returned 1.
  277. #
  278. sub GetNextInstanceNumber
  279. {
  280. # get passed args
  281. my( $CommandName, $ReqType ) = @_;
  282. # declare locals
  283. my( $Request, $Count, $LongName, $ShortName );
  284. # dbgmsg( "In subroutine GetNextInstanceNumber ..." );
  285. # get the command name in the right syntax
  286. if ( $CommandName =~ /.*\\([^\\]+?)$/ ) { $CommandName = $1; }
  287. # make the actual count of commands with this name
  288. $Count = 0;
  289. foreach $Request ( @Requests ) {
  290. if ( &GetField( $Request, $NReqType ) !~ /$ReqType/i ) { next; }
  291. $LongName = &GetField( $Request, $NCommand );
  292. if ( $LongName =~ /.*\\([^\\]+?)$/ ) { $ShortName = $1; }
  293. else { $ShortName = $LongName; }
  294. if ( "\L$ShortName" eq "\L$CommandName" ) {
  295. $Count++;
  296. }
  297. }
  298. # return the unique identifier
  299. return( $Count );
  300. }
  301. #
  302. # GetField
  303. #
  304. # Arguments: $Request, $FieldNumber
  305. #
  306. # Purpose: a simple sub to return the requested field from the given request.
  307. # this is just to abstract the actual data structure from the data.
  308. #
  309. sub GetField
  310. {
  311. # get passed args
  312. my( $Request, $FieldNumber ) = @_;
  313. # declare locals
  314. my( $ReqType, $NeedToDo, $Instance, $Command, $Options );
  315. # take out the dbgmsg here, just too much spew
  316. # dbgmsg( "In subroutine GetField ..." );
  317. ( $ReqType, $NeedToDo, $Instance, $Command, $Options ) =
  318. split( /\:/, $Request, 5 );
  319. if ( $FieldNumber == $NReqType ) { return $ReqType; }
  320. if ( $FieldNumber == $NNeedToDo ) { return $NeedToDo; }
  321. if ( $FieldNumber == $NInstance ) { return $Instance; }
  322. if ( $FieldNumber == $NCommand ) { return $Command; }
  323. if ( $FieldNumber == $NOptions ) { return $Options; }
  324. # if none of the above apply, return undef
  325. return( undef );
  326. }
  327. #
  328. # MakeNewRequest
  329. #
  330. # Arguments: $Request, $FieldNumber, $Value
  331. #
  332. # Purpose: a simple sub to return a new request with the requested field for
  333. # the given request set to the specified value. this is just to
  334. # abstract the actual data structure from the data.
  335. #
  336. # Returns: the new request
  337. #
  338. sub MakeNewRequest
  339. {
  340. # get passed args
  341. my( $Request, $FieldNumber, $Value ) = @_;
  342. # declare locals
  343. my( $ReqType, $NeedToDo, $Instance, $Command, $Options, $NewRequest );
  344. # dbgmsg( "In subroutine MakeNewRequest ..." );
  345. ( $ReqType, $NeedToDo, $Instance, $Command, $Options ) =
  346. split( /\:/, $Request, 5 );
  347. if ( $FieldNumber == $NReqType ) { $ReqType = $Value; }
  348. if ( $FieldNumber == $NNeedToDo ) { $NeedToDo = $Value; }
  349. if ( $FieldNumber == $NInstance ) { $Instance = $Value; }
  350. if ( $FieldNumber == $NCommand ) { $Command = $Value; }
  351. if ( $FieldNumber == $NOptions ) { $Options = $Value; }
  352. $NewRequest = "$ReqType:$NeedToDo:$Instance:$Command:$Options";
  353. return( $NewRequest )
  354. }
  355. #
  356. # RunCommands
  357. #
  358. # Arguments: none
  359. #
  360. # Purpose: this routine cycles over the requests from the data file and makes
  361. # the judgement calls for when to kick things off or not. this logic
  362. # is determined by the HORSE_POWER environment variable and the
  363. # number of processors available.
  364. #
  365. # Returns: nothing
  366. #
  367. sub RunCommands
  368. {
  369. # declare locals
  370. my( $Request, $ThreadCount, $NumThreads, $NumKill );
  371. my( $LongName, $ShortName, $Options, $Req );
  372. # dbgmsg( "In subroutine RunCommands ..." );
  373. # init vars
  374. $ThreadCount = 0;
  375. undef( @EndList );
  376. # debug only
  377. # foreach $Request ( @Requests ) {
  378. # $LongName = &GetField( $Request, $NCommand );
  379. # if ( $LongName =~ /.*\\([^\\]+?)$/ ) { $ShortName = $1; }
  380. # else { $ShortName = $LongName };
  381. # dbgmsg( "Script $ShortName needs " .
  382. # &GetNumThreads( &GetField( $Request, $NCommand ) ) .
  383. # " threads ..." );
  384. # }
  385. # start the loop
  386. foreach $Request ( @Requests ) {
  387. # see if we need to do this one
  388. if ( &GetField( $Request, $NNeedToDo ) =~ /f/i ) { next; }
  389. # see if this is a checkfatal statement
  390. if ( &GetField( $Request, $NReqType ) =~ /checkfatal/i ) {
  391. if ( &CheckForStop() eq "TRUE" ) {
  392. errmsg( "Errors encountered at this time, exiting." );
  393. # clear up any unfinished processes
  394. undef( @EndList );
  395. foreach $Req ( @Requests ) {
  396. if ( &IsRunning( $Req ) ) {
  397. push( @EndList, $Req );
  398. }
  399. if ( @EndList ) { &WaitForEnds(); }
  400. }
  401. return( undef );
  402. }
  403. $Request = &MakeNewRequest( $Request, $NNeedToDo, "f" );
  404. next;
  405. }
  406. # see if this is an end statement
  407. if ( &GetField( $Request, $NReqType ) =~ /END/i ) {
  408. push( @EndList, $Request );
  409. next;
  410. }
  411. # this is not an end, so kill off any pending ends
  412. if ( @EndList ) {
  413. $ThreadCount -= &WaitForEnds();
  414. }
  415. # see how many threads we'll need
  416. $NumThreads = &GetNumThreads( &GetField( $Request, $NCommand ) );
  417. # make sure we have the horse power to kick off the given request
  418. while ( ( $ThreadCount + $NumThreads > $MaxThreads ) &&
  419. ( $ThreadCount > 0 ) ) {
  420. # now we want to kill off enough requests so that we can kick
  421. # off the next script
  422. $LongName = &GetField( $Request, $NCommand );
  423. if ( $LongName =~ /.*\\([^\\]+?)$/ ) { $ShortName = $1; }
  424. else { $ShortName = $LongName };
  425. $NumKill = $ThreadCount - $MaxThreads + $NumThreads;
  426. #dbgmsg( "Waiting prematurely:" );
  427. #dbgmsg( "Script $ShortName needs $NumKill killed" );
  428. #dbgmsg( "as there are $ThreadCount running, we need " .
  429. #$NumThreads . ", and $MaxThreads is max ..." );
  430. $ThreadCount -= &KillRunningRequest( $NumKill );
  431. }
  432. # we are now guaranteed we have the horse power to run the request
  433. # kick it off and increment the thread count
  434. $LongName = &GetField( $Request, $NCommand );
  435. if ( $LongName =~ /.*\\([^\\]+?)$/ ) { $ShortName = $1; }
  436. else { $ShortName = $LongName; }
  437. $Options = &GetField( $Request, $NOptions );
  438. system( "start \"PB\_$ShortName\" /min cmd /c " .
  439. "\%RazzleToolPath\%\\PostBuildScripts\\Wrapper.cmd " .
  440. &GetField( $Request, $NInstance ) . " $LongName $Options" . " -l:$Language" );
  441. logmsg( "Beginning $ShortName with $NumThreads thread(s)" );
  442. $ThreadCount += $NumThreads;
  443. $Request = &MakeNewRequest( $Request, $NNeedToDo, "f" );
  444. }
  445. # cleanup - make sure @EndList is empty
  446. if ( @EndList ) { $ThreadCount -= &WaitForEnds(); }
  447. dbgmsg( "Current threads running: $ThreadCount" );
  448. }
  449. #
  450. # GetNumThreads
  451. #
  452. # Arguments: $Command
  453. #
  454. # Purpose: this routine will return the number of threads kicked off by the
  455. # passed command. DriverCab.cmd, for instance, kicks off twice the
  456. # number of processors.
  457. #
  458. # Returns: the number of threads a given process kicks off
  459. #
  460. sub GetNumThreads
  461. {
  462. # get passed args
  463. my( $Command ) = @_;
  464. # declare locals
  465. my( $ShortName );
  466. # dbgmsg( "In subroutine GetNumThreads ..." );
  467. # generate the short name (i.e. command name without the path)
  468. if ( $Command =~ /.*\\([^\\]+?)$/ ) { $ShortName = $1; }
  469. else { $ShortName = $Command };
  470. if ( $ShortName =~ /drivercab\.cmd/i ) { return( $NumProcs * 2 ); }
  471. if ( $ShortName =~ /startcompress\.cmd/i ) { return( $NumProcs ); }
  472. if ( $ShortName =~ /ddkcabs\.bat/i ) { return( $NumProcs ); }
  473. if ( $ShortName =~ /startcompress\_post\.cmd/i ) { return( $NumProcs ); }
  474. # if we weren't in the list, assume only one thread
  475. return( 1 );
  476. }
  477. #
  478. # IsRunning
  479. #
  480. # Arguments: a $Request
  481. #
  482. # Purpose: this routine will determine if the passed request is still running.
  483. # this means the /BEGIN/ half of the request must have $NeedToDo set
  484. # to "f" and the /END/ must be set to "t"
  485. #
  486. # Returns: "t" if the command is still running, undef otherwise.
  487. #
  488. sub IsRunning
  489. {
  490. # get passed args
  491. my( $Request ) = @_;
  492. # declare locals
  493. my( $Command, $Instance, $ReqType, $ShortName, $Req, $NeedToDo );
  494. # remove dbgmsg because of profuse spewing
  495. # dbgmsg( "In subroutine IsRunning ..." );
  496. # check for a finished END request
  497. if ( ( &GetField( $Request, $NReqType ) =~ /END/i ) &&
  498. ( &GetField( $Request, $NNeedToDo ) =~ /f/i ) ) {
  499. return( undef );
  500. }
  501. # now if we haven't started this one, we're not running now
  502. if ( ( &GetField( $Request, $NReqType ) =~ /BEGIN/i ) &&
  503. ( &GetField( $Request, $NNeedToDo ) =~ /t/i ) ) {
  504. return( undef );
  505. }
  506. # get the short command name (i.e. no path)
  507. $Command = &GetField( $Request, $NCommand );
  508. if ( $Command =~ /.*\\([^\\]+?)$/ ) { $Command = $1; }
  509. # get other interesting info
  510. $ReqType = &GetField( $Request, $NReqType );
  511. $Instance = &GetField( $Request, $NInstance );
  512. # loop over other requests
  513. foreach $Req ( @Requests ) {
  514. if ( &GetField( $Req, $NReqType ) =~ /$ReqType/i ) { next; }
  515. if ( &GetField( $Req, $NInstance ) !~ /$Instance/i ) { next; }
  516. $ShortName = &GetField( $Req, $NCommand );
  517. if ( $ShortName =~ /.*\\([^\\]+?)$/ ) { $ShortName = $1; }
  518. if ( "\L$ShortName" ne "\L$Command" ) { next; }
  519. # if we're here, we found our culprit
  520. $NeedToDo = &GetField( $Req, $NNeedToDo );
  521. if ( ( ( &GetField( $Req, $NReqType ) =~ /END/i ) &&
  522. ( $NeedToDo =~ /t/i ) ) ||
  523. ( ( &GetField( $Req, $NReqType ) =~ /BEGIN/i ) &&
  524. ( $NeedToDo =~ /f/i ) ) ) { return( "t" ); }
  525. return( undef );
  526. }
  527. # default: say we're not running
  528. return( undef );
  529. }
  530. #
  531. # WaitForEnds
  532. #
  533. # Arguments: none
  534. #
  535. # Purpose: wait on the threads in the @EndList.
  536. #
  537. # Returns: the number of threads that died from this wait cycle
  538. #
  539. sub WaitForEnds
  540. {
  541. # declare locals
  542. my( $EventName, $EventNames, $Req, $NumThreads, $Return, $Request );
  543. my( $Instance, $Command );
  544. # dbgmsg( "In subroutine WaitForEnds ..." );
  545. # make sure we have events to wait on
  546. unless ( @EndList ) {
  547. errmsg( "Null EndList, not performing wait ..." );
  548. $ExitCode++;
  549. return( 0 );
  550. }
  551. &DisplayHelp( @EndList );
  552. # loop over the end list and make our event names
  553. $NumThreads = 0;
  554. foreach $Req ( @EndList ) {
  555. $EventName = &GetField( $Req, $NCommand );
  556. if ( $EventName =~ /.*\\([^\\]+?)$/ ) { $EventName = $1; }
  557. $Instance = &GetField( $Req, $NInstance );
  558. # modify the request list to say this is done
  559. foreach $Request ( @Requests ) {
  560. $Command = &GetField( $Request, $NCommand );
  561. if ( $Command =~ /.*\\([^\\]+?)$/ ) { $Command = $1; }
  562. if ( ( "\L$Command" eq "\L$EventName" ) &&
  563. ( &GetField( $Request, $NInstance ) =~ /$Instance/i ) ) {
  564. # dbgmsg( "CHANGING REQUEST $Request ..." );
  565. $Request = &MakeNewRequest( $Request, $NNeedToDo, "f" );
  566. }
  567. }
  568. $NumThreads += &GetNumThreads( &GetField( $Req, $NCommand ) );
  569. $EventName .= "." . &GetField( $Req, $NInstance );
  570. $EventNames .= " $EventName";
  571. }
  572. # now wait on all the events
  573. $Return = system( "perl \%RazzleToolPath\%\\PostBuildScripts\\cmdevt.pl " .
  574. "-ivw " . $EventNames );
  575. if ( $Return != 0 ) {
  576. errmsg( "Waitloop failed waiting on the following events:" );
  577. errmsg( "$EventNames" );
  578. $ExitCode++;
  579. }
  580. undef( @EndList );
  581. # put in a blank line for aesthetic reasons
  582. print( "\n" );
  583. # return the thread count killed
  584. return( $NumThreads );
  585. }
  586. #
  587. # KillRunningRequest
  588. #
  589. # Arguments: number of threads to kill
  590. #
  591. # Purpose: this routine will check all requests. it will kill off the first n
  592. # requests it finds in the requests array that are not finished
  593. # running, such that we meet the number of threads to kill as passed.
  594. # as a footnote, we don't have to worry if this event name
  595. # is already in @EndList, because the point at which this routine is
  596. # called, we are guaranteed that @EndList is empty.
  597. #
  598. # Returns: the number of threads that were killed in this manner
  599. #
  600. sub KillRunningRequest
  601. {
  602. # get passed args
  603. my( $NumKill ) = @_;
  604. # declare locals
  605. my( $Req, $NThreads, $NEndedThreads );
  606. my( $EventName, $LongName, $Return );
  607. # dbgmsg( "In subroutine KillRunningRequest ..." );
  608. # before we go into the loop of this that are running that we should wait
  609. # on, let's see if anybody's done, and get rid of them first.
  610. $NEndedThreads = 0;
  611. $NThreads = 0;
  612. foreach $Req ( @Requests ) {
  613. if ( ( &IsRunning( $Req ) ) &&
  614. ( &GetField( $Req, $NNeedToDo ) !~ /f/i ) &&
  615. ( &GetField( $Req, $NReqType ) =~ /END/i ) ) {
  616. # build up the event name for this request
  617. $LongName = &GetField( $Req, $NCommand );
  618. if ( $LongName =~ /.*\\([^\\]+?)$/ ) { $EventName = $1; }
  619. else { $EventName = $LongName; }
  620. $EventName .= "." . &GetField( $Req, $NInstance );
  621. $Return = system( "perl \%RazzleToolPath\%\\PostBuildScripts\\" .
  622. "cmdevt.pl -qi " . $EventName . " \>nul" );
  623. if ( $Return == 0 ) {
  624. # this means we have already finished this task and we can
  625. # safely mark this one as finished
  626. push( @EndList, $Req );
  627. $Req = &MakeNewRequest( $Req, $NNeedToDo, "f" );
  628. $NThreads += &GetNumThreads( $Req, $NCommand );
  629. }
  630. }
  631. # if ( $NThreads >= $NumKill ) {
  632. # $NEndedThreads += &WaitForEnds();
  633. # last;
  634. # }
  635. }
  636. if ( @EndList ) {
  637. $NEndedThreads += &WaitForEnds();
  638. }
  639. if ( $NEndedThreads >= $NumKill ) {
  640. return( $NEndedThreads );
  641. }
  642. foreach $Req ( @Requests ) {
  643. if ( ( &IsRunning( $Req ) ) &&
  644. ( &GetField( $Req, $NNeedToDo ) !~ /f/i ) &&
  645. ( &GetField( $Req, $NReqType ) =~ /END/i ) ) {
  646. # do the work here so when we modify $Req to say it's finished,
  647. # we actually modify the one in @Requests
  648. push( @EndList, $Req );
  649. $Req = &MakeNewRequest( $Req, $NNeedToDo, "f" );
  650. $NThreads += &GetNumThreads( &GetField( $Req, $NCommand ) );
  651. }
  652. if ( $NThreads >= $NumKill ) {
  653. $NEndedThreads += &WaitForEnds();
  654. last;
  655. }
  656. }
  657. if ( @EndList ) {
  658. $NEndedThreads += &WaitForEnds();
  659. }
  660. return( $NEndedThreads );
  661. }
  662. #
  663. # DisplayHelp
  664. #
  665. # Arguments: @EndList
  666. #
  667. # Purpose: this routine goes through the given end list and prints out the
  668. # help for each thing being waited on.
  669. #
  670. # Returns: nothing
  671. #
  672. sub DisplayHelp
  673. {
  674. # get passed args
  675. my( @EndList ) = @_;
  676. # declare locals
  677. my( $Request, $BeginRequest, $Command );
  678. # go through each request, find the associated begin (so we can get the
  679. # options right) and print out the help
  680. print( "\n" );
  681. foreach $Request ( @EndList ) {
  682. $BeginRequest = &GetBegin( $Request );
  683. if ( $BeginRequest ) {
  684. $Command = &GetField( $BeginRequest, $NCommand );
  685. if ( $Command =~ /.*\\([^\\]+?)$/ ) { $Command = $1; }
  686. if ( &GetField( $BeginRequest, $NOptions ) ) {
  687. $Command .= " " . &GetField( $BeginRequest, $NOptions );
  688. }
  689. $Command = "\L$Command";
  690. if ( $HelpMessages{ $Command } ) {
  691. print( "INFO: $Command : $HelpMessages{ $Command }\n" );
  692. }
  693. }
  694. }
  695. #print( "\n" );
  696. }
  697. #
  698. # GetBegin
  699. #
  700. # Arguments: $EndRequest
  701. #
  702. # Purpose: this routine finds the begin request associated with the given end
  703. # request.
  704. #
  705. # Returns: the associated begin request
  706. #
  707. sub GetBegin
  708. {
  709. # get passed args
  710. my( $EndRequest ) = @_;
  711. # declare locals
  712. my( $Request, $EndInstance, $EndCommand, $Command );
  713. # if this isn't an /END/ request, return undef
  714. if ( &GetField( $EndRequest, $NReqType ) !~ /END/i ) { return( undef ); }
  715. $EndCommand = &GetField( $EndRequest, $NCommand );
  716. if ( $EndCommand =~ /.*\\([^\\]+?)$/ ) { $EndCommand = $1; }
  717. $EndCommand = "\L$EndCommand";
  718. $EndInstance = &GetField( $EndRequest, $NInstance );
  719. foreach $Request ( @Requests ) {
  720. $Command = &GetField( $Request, $NCommand );
  721. if ( $Command =~ /.*\\([^\\]+?)$/ ) { $Command = $1; }
  722. $Command = "\L$Command";
  723. if ( ( &GetField( $Request, $NInstance ) =~ /$EndInstance/i ) &&
  724. ( &GetField( $Request, $NReqType ) =~ /BEGIN/i ) &&
  725. ( $Command eq $EndCommand ) ) {
  726. return( $Request );
  727. }
  728. }
  729. # if we didn't find a match, return undef
  730. dbgmsg( "Failed to find a matching begin, returning undef ..." );
  731. return( undef );
  732. }
  733. #
  734. # CheckForStop
  735. #
  736. # Arguments: none
  737. #
  738. # Purpose: this function will check the current logfile for errors. if there
  739. # are any errors, it will signal by returning "TRUE".
  740. #
  741. # Returns: "TRUE" if errors were encountered, undef otherwise
  742. sub CheckForStop
  743. {
  744. # declare locals
  745. my( $LogFileName, @LogLines, $Line );
  746. # open the current logfile
  747. unless ( $LogFileName ) {
  748. # postbuild.cmd defines INTERLEAVE_LOG so Logmsg.pm will log all calls
  749. # to logmsg and errmsg in this file making it a good place to check
  750. # for errors
  751. $LogFileName = $ENV{ "INTERLEAVE_LOG" };
  752. }
  753. unless ( $LogFileName ) {
  754. errmsg( "No logfile found, assuming NO errors ..." );
  755. return( undef );
  756. }
  757. unless ( open( INFILE, $LogFileName ) ) {
  758. errmsg( "Failed to open logfile, assuming errors ..." );
  759. return( "TRUE" );
  760. }
  761. @LogLines = <INFILE>;
  762. close( INFILE );
  763. foreach $Line ( @LogLines ) {
  764. if ( $Line =~ /error\:/i ) {
  765. logmsg( "Errors found ..." );
  766. return( "TRUE" );
  767. }
  768. }
  769. # if we're here, no errors were found.
  770. logmsg( "No errors encountered at this time ..." );
  771. return( undef );
  772. }