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.

852 lines
24 KiB

  1. # FileName: scorch.pl
  2. #
  3. #
  4. # Usage = scorch.pl [-fake] [-arch=<archname> [-alt=<altdir>] [-save[=<savedirpath>]] -scorch=<newntdir>
  5. #
  6. # Function: Starting from the specified directory (ignoring <savedirpath>)
  7. # 0) Verify that the current directory is the same as <newntdir>.
  8. # 1) Use SD to build a list of opened files.
  9. # 2) Build a list of unopened files not marked readonly.
  10. # 3) Optionally copy notreadonly files to <savedirpath> preserving hierarchy
  11. # (ignoring files in $(O) directories).
  12. # 4) Abort on any error copying the files.
  13. # 5) Delete all notreadonly files.
  14. #
  15. # Example:
  16. # cd /D %SDXROOT%
  17. # scorch.pl -scorch=%SDXROOT%
  18. #
  19. # WARNING:
  20. # WARNING: make sure pathname comparisons are case insensitive. Either convert the case or do the
  21. # WARNING: comparisons like this:
  22. # WARNING: if ($foo =~ /^\Q$bar\E$/i) {}
  23. # WARNING: or if ($foo !~ /^\Q$bar\E$/i) {}
  24. # WARNING:
  25. if ( $ENV{BUILD_OFFLINE} eq '1' ) { exit 0 }
  26. $begintime = time();
  27. $PGM='SCORCH: ';
  28. $Usage = $PGM . "Usage: scorch.pl [-fake] [-arch=<archname>] [-save[=<savedirpath>]] -scorch=<newntdir>\n";
  29. #
  30. # Get the current directory
  31. #
  32. open CWD, 'cd 2>&1|';
  33. $ScorchDir = <CWD>;
  34. close CWD;
  35. chomp $ScorchDir;
  36. $ScorchDrive = substr($ScorchDir, 0, 2);
  37. #
  38. # initialize argument variables
  39. #
  40. $Verbose = 0;
  41. $VeryVerbose = 0;
  42. $Fake = 0;
  43. $Debug = 0;
  44. $Scorch = 0;
  45. $Save = 0;
  46. $Arch = "";
  47. $AltDir = "";
  48. $BackupDir = "$ScorchDir\\BACKUP";
  49. $BackupLogName = "NEWNT_SCORCHED.LOG";
  50. $BackupLogFile = "NoScorchLogFile";
  51. @ValidArchitectures = ( "i386", "ia64", "amd64" );
  52. #
  53. # These are the extensions that should be safe to delete anywhere they are found
  54. # in the tree -- even without first saving them.
  55. #
  56. # recent changes: -bmp
  57. @SafeDelExtensions = ( "pdb", "dbg", "cod", "pp", "pps", "ppx", "bsc", "tlb", "exe", "sys", "lib", "exp",
  58. "dll", "res", "sym", "map", "obj", "bin", "vbs", "bmf", "tab", "rsp", "dls", "dlx" );
  59. #
  60. # Build AllArchPattern
  61. #
  62. $AllArchPattern = "(";
  63. for (@ValidArchitectures) {
  64. $AllArchPattern .= $_ . '|';
  65. }
  66. chop $AllArchPattern; # get rid of trailing '|'
  67. $AllArchPattern .= ')';
  68. #
  69. # Build SafeDelPattern
  70. #
  71. $SafeDelPattern = "(";
  72. for (@SafeDelExtensions) {
  73. $SafeDelPattern .= $_ . '|';
  74. }
  75. chop $SafeDelPattern; # get rid of trailing '|'
  76. $SafeDelPattern .= ')';
  77. #
  78. # print on the various files
  79. #
  80. sub printall {
  81. print SCORCHLOGFILE @_;
  82. print $PGM unless @_ == 1 and @_[0] eq "\n";
  83. print @_;
  84. }
  85. sub printfall {
  86. printf SCORCHLOGFILE @_;
  87. print $PGM unless @_ == 1 and @_[0] eq "\n";
  88. printf @_;
  89. }
  90. #
  91. # Initialization
  92. #
  93. $ScorchLogFileName = "build.scorch";
  94. $ScorchLogFileSpec = ">" . $ScorchLogFileName;
  95. open SCORCHLOGFILE, $ScorchLogFileSpec or die $PGM, "Could not open: ", $ScorchLogFileName, "\n";
  96. ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime;
  97. $foo = sprintf "Scorch started at %04d/%02d/%02d-%02d:%02d:%02d.\n",
  98. 1900+$year, 1+$mon, $mday, $hour, $min, $sec;
  99. printall "\n";
  100. printall $foo;
  101. #
  102. # Debug routines for printing out variables
  103. #
  104. sub gvar {
  105. for (@_) {
  106. printall "\$$_ = $$_\n";
  107. }
  108. }
  109. #
  110. # signal catcher (at least this would work on unix)
  111. #
  112. sub catch_ctrlc {
  113. printall "Aborted.\n";
  114. print $BackupLogHandle "Aborted.\n" if $BackupLogHandle;
  115. die "$PGM Aborted.\n";
  116. }
  117. $SIG{INT} = \&catch_ctrlc;
  118. #
  119. # routine to fully qualify a pathname
  120. #
  121. sub fullyqualify {
  122. die "$PGM Internal error in fullpathname().\n" unless @_ == 1;
  123. $_ = @_[0];
  124. if (/\s/) { die "$PGM Spaces in pathnames not allowed: '", $_, "'\n"; }
  125. return $_ unless $_; # empty strings are a noop
  126. s/([^:])\\$/$1/; # get rid of trailing \
  127. while (s/\\\.\\/\\/) {} # get rid of \.\
  128. while (s/\\[^\\]+\\\.\.\\/\\/) {} # get rid of \foo\..\
  129. s/\\[^\\]+\\\.\.$/\\/; # get rid of \foo\..
  130. s/:[^\\]+\\\.\.$/:/; # get rid of x:foo\..
  131. s/([^:])\\\.$/$1/; # get rid of foo\.
  132. s/:\\\.$/:\\/; # get rid of x:\.
  133. s/:[^\\]+\\\.\.$/:/; # get rid of x:foo\..
  134. s/^$ScorchDrive[^\\]/$ScorchDir\\/i; # convert drive-relative on current drive
  135. if (/^[a-z]:\\/i) { return $_; } # full
  136. if (/^\\[^\\].*/) { return "$ScorchDrive$_"; } # rooted
  137. if (/^\\\\[^\\]/) {
  138. printall "Warning: Use of UNC name bypasses safety checks: $_\n";
  139. return $_; # UNC
  140. }
  141. if (/^\.$/) { return "$ScorchDir"; } # dot
  142. if (/^$ScorchDrive\.$/i) { return "$ScorchDir"; } # dot on current drive
  143. if (/^[^\\][^:].*/i) { return "$ScorchDir\\$_"; } # relative
  144. if (/^([a-z]:)([^\\].*)/i) { $drp = $ScorchDir; # this case handled above
  145. if ($1 ne $ScorchDir) {
  146. # $drp = $ENV{"=$1"}; # doesn't work!
  147. die $PGM, "Can't translate drive-relative pathnames: ", $_, "\n";
  148. }
  149. return "$drp\\$2"; # drive:relative
  150. }
  151. die "$PGM Unrecognized pathname format: $_\n";
  152. }
  153. #
  154. # process arguments
  155. #
  156. for (@ARGV) {
  157. if (/^-verbose$/i) { $Verbose++; next; }
  158. if (/^-veryverbose$/i) { $Verbose++; $VeryVerbose++; next; }
  159. if (/^-debug$/i) { $Debug++; next; }
  160. if (/^-save$/i) { $Save++; next; }
  161. if (/^-save=(.*)$/i) { $Save++; $BackupDir = $1; next; }
  162. if (/^-scorch=(.*)$/i) { $Check++; $CheckDir = $1; next; }
  163. if (/^-fake$/i) { $Fake++; next; }
  164. if (/^-arch=([^\\]*)$/i) { $ArchCheck++; $Arch = "$1"; next; }
  165. if (/^-alt=([^\\]*)$/i) { $AltCheck++; $AltDir = "$1"; next; }
  166. if (/^-?$/i) { die $Usage; }
  167. if (/^-help$/i) { die $Usage; }
  168. die $Usage;
  169. }
  170. #
  171. # Fully qualify the pathnames
  172. #
  173. $BackupDir = fullyqualify($BackupDir);
  174. $CheckDir = fullyqualify($CheckDir);
  175. #
  176. # validate arguments, consult environment
  177. #
  178. if ($ArchCheck > 1
  179. or $AltCheck > 1
  180. or $ArchCheck == 0 and $AltCheck
  181. or $Save > 1)
  182. {
  183. die $Usage;
  184. }
  185. if ($Arch) {
  186. $ok = 0;
  187. for (@ValidArchitectures) {
  188. if (/^\Q$Arch\E$/i) {
  189. $ok++;
  190. break;
  191. }
  192. }
  193. die "-arch $Arch is invalid architecture. Try $AllArchPattern.\n" unless $ok;
  194. }
  195. $a = $ENV{'BUILD_ALT_DIR'};
  196. die $PGM, "BUILD_ALT_DIR=$a mismatch with -alt=$AltDir\n", $Usage if ($a and $a !~ /^\Q$AltDir\E$/i);
  197. #
  198. # Act a little paranoid to keep caller from accidentally scorching something.
  199. #
  200. if ($Check != 1) {
  201. printall "Must explicitly specify -scorch=<newntdir>\n",
  202. "where <newntdir> is the root of the tree to be scorched\n\n";
  203. printall "<newntdir> is required to be the current directory ($ScorchDir)\n";
  204. die $Usage;
  205. }
  206. if ($ScorchDir !~ /^\Q$CheckDir\E$/i) {
  207. printall "$CheckDir is required to be the current directory ($ScorchDir)\n";
  208. die $Usage;
  209. }
  210. #
  211. # Figure out whether we are at the root of NewNT, under the root,
  212. # or somewhere else entirely.
  213. #
  214. $sdxroot = $ENV{'SDXROOT'} or die $PGM, "SDXROOT not set in environment\n";
  215. $Rooted = 0;
  216. if ($ScorchDir =~ /^\Q$sdxroot\E$/i) {
  217. $SDcmd = 'sdx';
  218. $SDopt = '-v';
  219. $Rooted = 1;
  220. } elsif ($ScorchDir =~ /^\Q$sdxroot\E\\/i) {
  221. $SDcmd = 'sd';
  222. $SDopt = '';
  223. } else {
  224. die $PGM, 'Must scorch at or under SDXROOT [', $sdxroot, "]\n";
  225. }
  226. #
  227. # Build the DollarOPattern's used to distinguish $(O) directories
  228. #
  229. $a = $ENV{'BUILD_ALT_DIR'};
  230. $MatchAllDollarOPattern = "obj[^\\\\]*\\\\$AllArchPattern\\\\";
  231. if ($Arch) {
  232. $DelDollarOPattern = "obj$AltDir\\\\$Arch\\\\";
  233. $OtherDollarOPattern = $MatchAllDollarOPattern;
  234. } else {
  235. $DelDollarOPattern = $MatchAllDollarOPattern;
  236. $OtherDollarOPattern = "(?!)";
  237. }
  238. #gvar Arch, AllArchPattern, AltDir, MatchAllDollarOPattern, DelDollarOPattern, OtherDollarOPattern; # DEBUG
  239. #
  240. # Warning!
  241. #
  242. printall "WARNING: NOT FAKING! WILL SCORCH $ScorchDir\n" unless $Fake;
  243. #
  244. # Validate the backup directory
  245. # We require that if the Backup directory path is in the Scorch hierarchy, it must
  246. # either be the default, non-existent, empty, or contain our BACKUPLOGFILE.
  247. #
  248. VALIDATE_BACKUP: {
  249. last VALIDATE_BACKUP unless $Save;
  250. $BackupLogFile = "$BackupDir\\$BackupLogName";
  251. #
  252. # Check whether BackupDir is a prefix of ScorchDir
  253. #
  254. if ($BackupDir !~ /^\Q$ScorchDir\E\\/i) {
  255. last VALIDATE_BACKUP;
  256. }
  257. stat $BackupDir;
  258. #
  259. # If it doesn't exist, create it. Otherwise check that it is empty or contains a logfile.
  260. #
  261. if (not -e _) {
  262. mkdir $BackupDir, 0777 or die $PGM . "Could not create backup directory: $BackupDir\n";
  263. } else {
  264. #
  265. # Check that it is a directory
  266. #
  267. -d _ or die $PGM . "Not a directory: $BackupDir\n";
  268. #
  269. # Read out the contents of the directory
  270. #
  271. opendir BDIR, $BackupDir or die $PGM . "Could not open backup directory: $BackupDir\n";
  272. @allfiles = readdir BDIR;
  273. close BDIR;
  274. #
  275. # If it's not empty, we insist that it have a logfile
  276. #
  277. shift @allfiles; # .
  278. shift @allfiles; # ..
  279. if (@allfiles > 0) {
  280. stat $BackupLogFile;
  281. -f _ or die $PGM . "Backup directory $BackupDir not empty and no logfile present: $BackupLogFile\n";
  282. -w _ or die $PGM . "Logfile not writable: $BackupLogFile\n";
  283. }
  284. }
  285. last VALIDATE_BACKUP;
  286. }
  287. #
  288. # If we are saving, start appending to the logfile
  289. #
  290. if ($Fake) {
  291. $BackupLogHandle = STDOUT;
  292. } elsif ($Save) {
  293. open BACKUPLOGFILE, ">>$BackupLogFile" or die $PGM, 'Could not create logfile: ', $BackupLogFile, "\n";
  294. $BackupLogHandle = BACKUPLOGFILE;
  295. }
  296. if ($Fake or $Save) {
  297. ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime;
  298. $fmt = "NewNT BuildTree Scorcher: Run on $ScorchDir at %04d/%02d/%02d-%02d:%02d:%02d.\n";
  299. printf $BackupLogHandle $fmt, 1900+$year, 1+$mon, $mday, $hour, $min, $sec;
  300. printfall $fmt, 1900+$year, 1+$mon, $mday, $hour, $min, $sec;
  301. }
  302. #
  303. # Capture 'SD opened -l' in the SdOpenedList.
  304. #
  305. # If we find the string /error/ in the output, we will retry.
  306. #
  307. $MaxRetries = 30;
  308. $RetryWait = 120;
  309. $NumberOfRetries = 0;
  310. $Retry = 1;
  311. while ($Retry and $NumberOfRetries < $MaxRetries) {
  312. if ($NumberOfRetries) {
  313. printall "Retry attempt $NumberOfRetries. Sleeping $RetryWait seconds...\n";
  314. sleep $RetryWait;
  315. printall "Continuing retry attempt...\n";
  316. }
  317. $timestart = time();
  318. $Fatal = 0;
  319. $Retry = 0;
  320. $CmdErr = 0;
  321. $SDCommand = "$SDcmd opened $SDopt -l";
  322. $SDOpenSpec = "$SDCommand 2>&1 |";
  323. printall "Running the $SDcmd opened command...\n";
  324. open SDOPENED, $SDOpenSpec or die $PGM, "Command failed: '$SDCommand'\n";
  325. for (<SDOPENED>) {
  326. #
  327. # Watch for errors returned from the command so we can return them.
  328. #
  329. if (/error:/i) {
  330. $CmdErr = 1;
  331. $Retry = 1;
  332. printall "WARNING: error in ($SDCommand$).\n";
  333. $NumberOfRetries++;
  334. }
  335. if ($CmdErr) {
  336. printall $_;
  337. next;
  338. }
  339. chomp; # discard final ("\n") char
  340. next if /\sdelete\s/; # skip files that are opened, but deleted
  341. last if /^=+\s*Summary\s/i; # skip everything after Summary
  342. next if /^\s*$/; # skip blank lines
  343. next if /^---* /; # skip sdx lines announcing DEPOT
  344. next if /^===*/; # skip sdx noise
  345. next if /^\s*Total /; # skip more sdx noise (opened, revert)
  346. next if /^\s*Updated:/; # skip more sdx noise (sync)
  347. next if /^\s*Added:/; # skip more sdx noise (sync)
  348. next if /^\s*Deleted:/; # skip more sdx noise (sync)
  349. next if /^\s*Total:/; # skip more sdx noise (sync)
  350. next if /^File.*not opened on this client/i; # skip sdx 'not opened' lines
  351. #
  352. # Get the pathname and check
  353. # #xxx: xxx can be a number or 'none'.
  354. #
  355. $pathname = "";
  356. if (/^(.*)#[0-9noe]+\s+\-\s+(\w+)\s+/i) { $pathname = $1; $openedtype = $2};
  357. if (not $pathname) {
  358. printall "Could not parse output of '$SDCommand': $_\n";
  359. $Fatal++;
  360. next;
  361. }
  362. #
  363. # Check if opened file is in the area to be scorched
  364. #
  365. if ($pathname !~ /^\Q$ScorchDir\E\\(.*)$/io) {
  366. printall "$pathname not in subtree being scorched\n" if ($VeryVerbose);
  367. next;
  368. }
  369. $relpath = $1;
  370. $relpath =~ tr/A-Z/a-z/; # remember filename as lower case
  371. if ($openedtype =~ /edit/i) {
  372. stat $relpath;
  373. if (not -f _) {
  374. printall "Warning: Edited file doesn't exist: '$pathname' [$_]\n";
  375. } elsif (! -r _) {
  376. printall "Warning: Unreadable opened file '$pathname' [$_]\n";
  377. } elsif (! -w _) {
  378. printall "Warning: Unwritable opened file '$pathname' [$_]\n";
  379. }
  380. }
  381. #
  382. # Remember the relative path.
  383. #
  384. $SdOpenedList{$relpath} = 1;
  385. $ExpectedSdOpenedCount++;
  386. }
  387. close SDOPENED;
  388. $nowtime = time();
  389. printfall "SD opened command completed in %d seconds\n", ($nowtime-$timestart);
  390. $timestart = $nowtime;
  391. }
  392. die "Aborting. Retried ($SDCommand) $NumberOfRetries times without success.\n" if $Retry;
  393. die "Aborting. Errors parsing output of 'sd opened -l'\n" if $Fatal;
  394. if ($Verbose) {
  395. printall "Currently opened files under $ScorchDir\n";
  396. for (keys %SdOpenedList) {
  397. printall $_, "\n";
  398. }
  399. }
  400. printfall "%d opened files will be skipped.\n", $ExpectedSdOpenedCount;
  401. #
  402. # If we are faking, we print to standard output instead of the logfile.
  403. # We won't actually do anything, so we set $Save to record what files we would have saved.
  404. #
  405. if ($Fake) {
  406. printall "Pretending to save...\n";
  407. $Save = 1;
  408. }
  409. #
  410. # Enumerate all files in the directory hierarchy.
  411. # We use 'dir' because it will (hopefully) use findfirst/next and avoid opening anything except the directories,
  412. # which should be an order of magnitude faster.
  413. #
  414. printall "Running recursive DIR command...\n";
  415. $DirCommand="dir /b/s /a-r-d|";
  416. open DIRS, $DirCommand or die "$PGM Command failed: '$DirCommand' executed in $ScorchDir\n";
  417. #
  418. # Filter out opened files and build two lists:
  419. # ObjDel -- files under an OBJ directory (there is quite a list of these).
  420. # JustDel -- non OBJ files that are obviously generated (see below).
  421. # SaveAndDel -- all other files we find,
  422. #
  423. # We exclude files in Tools, Developer, build.*, and $BackupDir
  424. #
  425. @ObjDel = ();
  426. @JustDel = ();
  427. @SaveAndDel = ();
  428. $lastsaved = "";
  429. $idlroot = "";
  430. $nskip_Tool = 0;
  431. $nskip_Developer = 0;
  432. $nskip_Root = 0;
  433. $nskip_Editor = 0;
  434. $nskip_Build = 0;
  435. $nskip_Opened = 0;
  436. for (<DIRS>) {
  437. chomp;
  438. #
  439. # skip files in backup directory
  440. #
  441. $skip = /^\Q$BackupDir\E\\/i;
  442. $Debug and $skip and printall "Skipping backup file: $_\n";
  443. next if $skip;
  444. #
  445. # skip files and subhierarchies under the root directory
  446. #
  447. if ($Rooted) {
  448. $skip = /^\Q$ScorchDir\E\\Tools\\/io;
  449. if ($skip) {
  450. printall "Skip Tool: $_\n";
  451. $nskip_Tool++;
  452. next;
  453. }
  454. $skip = /^\Q$ScorchDir\E\\Developer\\/io;
  455. if ($skip) {
  456. printall "Skip Developer: $_\n";
  457. $nskip_Developer++;
  458. next;
  459. }
  460. $skip = /^\Q$ScorchDir\E\\[^\\]+$/io;
  461. if ($skip) {
  462. printall "Skip Root: $_\n";
  463. $nskip_Root++;
  464. next;
  465. }
  466. }
  467. #
  468. # There are two different checks on build.* files.
  469. # This one excuses all build.* files in the root directory of the scorch.
  470. # The one down in the elsif excuses standard build logging files anywhere.
  471. #
  472. $skip = /^\Q$ScorchDir\E\\build\.[^\\]*$/io;
  473. if ($skip) {
  474. $nskip_Build++;
  475. next;
  476. }
  477. #
  478. # Compute relative names
  479. #
  480. /^\Q$ScorchDir\E\\(.*)$/io;
  481. $_ = $1;
  482. tr/A-Z/a-z/; # use filename as lower case
  483. #
  484. # skip opened files
  485. #
  486. $skip = $SdOpenedList{$_};
  487. if ($skip) {
  488. printall "Skip Opened: $_\n";
  489. $nskip_Opened++;
  490. next;
  491. }
  492. #
  493. # Figure out which list to put this file on.
  494. # Ignore $(O) directories not mattching our arch pattern.
  495. #
  496. # We used to ignore _objects.mac files because scorch was run
  497. # as part of build, and the files that just got created would
  498. # be deleted. But now scorch runs separately from build in the
  499. # timebuild.pl script, so it is safe to let them get deleted.
  500. #
  501. $objdir = 0;
  502. $genfile = 0;
  503. if ($Verbose and /(\A|\\)($MatchAllDollarOPattern)/io) {
  504. $ODirCounts{$2}++;
  505. }
  506. if (/(\A|\\)$DelDollarOPattern/io) { $objdir = 1; }
  507. elsif (/(\A|\\)$OtherDollarOPattern/io) { $objignored++; }
  508. # elsif (/(\A|\\)obj[^\\]*\\_objects\.mac$/io) { $nskip_Build++; }
  509. # elsif (/(\A|\\)obj[^\\]*\\_objects\.mac$/io) { $genfile = 1; }
  510. elsif (/(\A|\\)obj[^\\]*\\_objects\.mac$/io) { $Arch ? $objignored++ : $objdir++; }
  511. # elsif (/(\A|\\)build.(log|wrn|err)$/io) { $nskip_Build++; }
  512. elsif (/(\A|\\).*\.(vpj|vtg|vpw)$/i) { $nskip_Editor++; } # VSlick
  513. elsif (/([^\\]+)\_[awscip]\.c$/i) { $genfile = 1; $idlroot = $1; }
  514. elsif (/(\A|\\)dlldata\.c$/i) { $genfile = 1; }
  515. elsif (/\.$SafeDelPattern$/io) { $genfile = 1; }
  516. elsif (/\\msg\.(h|[rm]c)$/i) { $genfile = 1; }
  517. else { push @SaveAndDel, ($_); $lastsaved = $_; }
  518. #
  519. # We want to remember each obj directory once, since we will scorch with a single del command.
  520. # If -arch=foo was specified, we only scorch files under the sub-directory foo.
  521. #
  522. if ($objdir) {
  523. push @ObjDel, $_;
  524. } elsif ($genfile) {
  525. #
  526. # Look for a generated .h IDL file and move to the JustDel category.
  527. # Assumes the dir/s collates it right before the generated .c files.
  528. #
  529. if ($idlroot && $lastsaved =~ /\Q$idlroot\E\.h$/i) {
  530. push @JustDel, pop @SaveAndDel;
  531. }
  532. push @JustDel, $_;
  533. $idlroot = "";
  534. $lastsaved = "";
  535. }
  536. }
  537. close DIRS;
  538. $nowtime = time();
  539. $dirtime = ($nowtime - $timestart);
  540. $timestart = $nowtime;
  541. #
  542. # Prepare to do the saves/deletes
  543. #
  544. $CopyCommand = "xcopy /FHKX";
  545. #
  546. # If we are faking, we render these commands harmless
  547. #
  548. if ($Fake) {
  549. $CopyCommand = "${CopyCommand}L";
  550. printall "SCORCH IS BEING FAKED\n";
  551. }
  552. #
  553. # If we are not Save, we transfer SaveAndDel to JustDel
  554. #
  555. if (not $Save) {
  556. push @JustDel, @SaveAndDel;
  557. @SaveAndDel = ();
  558. } else {
  559. printfall "Backing up %d files.\n", scalar @SaveAndDel;
  560. }
  561. #
  562. # Make backup copies of the save files
  563. #
  564. for (@SaveAndDel) {
  565. $root = $_;
  566. $root =~ s/[^\\]*$//;
  567. $cmd = "$CopyCommand \"$ScorchDir\\$_\" \"$BackupDir\\$root\"";
  568. $rc = system($cmd);
  569. if ($Fake) {
  570. printall $cmd, "\n" if $Debug;
  571. printall "COPY ($_) FAILED <returned $rc>.\n\n" if $rc;
  572. } else {
  573. die "COPY ($_) FAILED <returned $rc>.\n" if $rc;
  574. printall "Saved: $ScorchDir\\$_\n";
  575. printf $BackupLogHandle "Saved: $ScorchDir\\$_\n" if $BackupLogHandle;
  576. }
  577. }
  578. if ($Verbose) {
  579. for (@JustDel) {
  580. printall "Unlink: ", $_, "\n";
  581. }
  582. }
  583. $backuptime = 0;
  584. if (@SaveAndDel) {
  585. $nowtime = time();
  586. $backuptime = $nowtime - $timestart;
  587. $timestart = $nowtime;
  588. }
  589. #
  590. # Do the deletions
  591. #
  592. sub printocounts () {
  593. return unless $Verbose;
  594. $cnt = 0;
  595. for (sort keys %ODirCounts) {
  596. printall '$(O) Counts', "\n" unless $cnt++;
  597. printfall " %5d %s\n", $ODirCounts{$_}, $_;
  598. }
  599. }
  600. @ManuallyCheck = ();
  601. if ($Fake) {
  602. printfall "%d \$(O) files would have been just deleted.\n", scalar @ObjDel;
  603. printfall "%d \$(O) files would have been ignored (other archs or not obj$AltDir).\n", $objignored if $objignored;
  604. printocounts() if $objignored;
  605. printfall "%d other files would have been just deleted.\n", scalar @JustDel;
  606. printfall "%d files would have been deleted after being saved.\n", scalar @SaveAndDel;
  607. } else {
  608. $odcount = unlink @ObjDel;
  609. push @ManuallyCheck, @ObjDel if $odcount < scalar @ObjDel;
  610. printfall "%d of %d \$(O) files were just deleted.\n", $odcount, scalar @ObjDel;
  611. printfall "%d \$(O) files were ignored (other archs).\n", $objignored if $objignored;
  612. printocounts() if $objignored;
  613. $jdcount = unlink @JustDel;
  614. push @ManuallyCheck, @JustDel if $jdcount < scalar @JustDel;
  615. printfall "%d of %d other files were just deleted.\n", $jdcount, scalar @JustDel;
  616. $sdcount = unlink @SaveAndDel;
  617. push @ManuallyCheck, @SaveAndDel if $sdcount < scalar @SaveAndDel;
  618. printfall "%d of %d files were deleted after being saved.\n", $sdcount, scalar @SaveAndDel;
  619. }
  620. if (scalar @ManuallyCheck) {
  621. $odn = scalar @ObjDel - $odcount;
  622. $jdn = scalar @JustDel - $jdcount;
  623. $sdn = scalar @SaveAndDel - $sdcount;
  624. printall "\n";
  625. printall "\n****************************\n";
  626. printall "WARNING: Not all files that were supposed to be deleted were deleted.\n\n";
  627. printfall "\t<%d undeleted \$(O) files>\n", $odn if $odn;
  628. printfall "\t<%d undeleted just delete files>\n", $jdn if $jdn;
  629. printfall "\t<%d undeleted save&delete files>\n\n", $sdn if $sdn;
  630. printall "\n";
  631. printall "CHECKING FOR UNDELETED FILES. THIS WILL TAKE A WHILE.\n";
  632. $notdeleted = 0;
  633. for (@ManuallyCheck) {
  634. next unless -e $_;
  635. printall "UNDELETED FILE: $_\n";
  636. printall " ... was now able to delete $_\n" if unlink $_;
  637. $notdeleted++;
  638. }
  639. if ($notdeleted != $odn + $jdn + $sdn) {
  640. printfall "\nWARNING: Found only %d undeleted files\n", $notdeleted;
  641. }
  642. printall "****************************\n";
  643. printall "\n";
  644. }
  645. #
  646. # Get rid of empty directories
  647. #
  648. if ($Fake) {
  649. #open (MTDirs, "mtdir /d $sdxroot |");
  650. #@MTDirsList = <MTDirs>;
  651. #close (MTDirs);
  652. #printall "The following empty directories would have been deleted.\n @MTDirsList", ;
  653. } else {
  654. #open (MTDirs, "mtdir /d /e $sdxroot |");
  655. #@MTDirsList = <MTDirs>;
  656. #close (MTDirs);
  657. #printall "The following empty directories were deleted.\n @MTDirsList";
  658. }
  659. #
  660. # Done!
  661. #
  662. $nowtime = time();
  663. if ($ExpectedSdOpenedCount != $nskip_Opened) {
  664. printfall "Expected to skip %d opened files but skipped %d\n",
  665. $ExpectedSdOpenedCount, $nskip_Opened;
  666. }
  667. printfall "Skipped files: Tool %d Developer %d Root %d Build %d Opened %d Editor %d\n",
  668. $nskip_Tool, $nskip_Developer, $nskip_Root, $nskip_Build, $nskip_Opened, $nskip_Editor;
  669. printfall "DIR processing took %d seconds\n", $dirtime;
  670. printfall "File backup took %d seconds\n", $backuptime if $backuptime;
  671. printfall "File deletion took %d seconds\n", $nowtime-$timestart;
  672. printfall "Total time: %d seconds\n", ($nowtime-$begintime);
  673. printfall "SCORCH WAS FAKED\n" if $Fake;
  674. exit 0;