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.

563 lines
18 KiB

  1. ###############################################################################
  2. #+---------------------------------------------------------------------------+#
  3. #| WriteStreams.pl - Demonstrates how to add source depot informaion into a |#
  4. #| PDB to use when source debugging. |#
  5. #| |#
  6. #| Outline: |#
  7. #| This script performs 3 general steps- |#
  8. #| (1) Collect the information to map a local filesystem path to a remote |#
  9. #| Source Depot path. |#
  10. #| (2) Collect the information required to map a Source Depot path to a |#
  11. #| specific server. |#
  12. #| (3) Translate each source file for a PDB to a Source Depot path and |#
  13. #| server. |#
  14. #| The resulting information is then placed into the PDB for use by the |#
  15. #| debugger. |#
  16. #| |#
  17. #| By default, the script works entirely in the tree rooted at the current |#
  18. #| directory. To override this behavior, the environment variables |#
  19. #| %SOURCE_ROOT% and %SYMBOLS_ROOT% can be defined. |#
  20. #| |#
  21. #| - %SOURCE_ROOT% - this is the directory under which source depot data is |#
  22. #| collected for. |#
  23. #| - %SYMBOLS_ROOT%- this is the directory which will be recursed when |#
  24. #| processing PDB files. |#
  25. #| |#
  26. #+---------------------------------------------------------------------------+#
  27. ###############################################################################
  28. use strict;
  29. use File::Spec;
  30. use File::Temp qw(tempfile);
  31. sub TRUE {return(1);} # BOOLEAN TRUE
  32. sub FALSE {return(0);} # BOOLEAN FALSE
  33. #
  34. # Temp vars
  35. #
  36. my @data;
  37. #
  38. # Init default globals
  39. #
  40. my $SDP_CMD = "sdp";
  41. my $SOURCE_ROOT = `cd`;
  42. chomp $SOURCE_ROOT;
  43. my $SYMBOLS_ROOT = $SOURCE_ROOT;
  44. my $SDP_HAVE = $SOURCE_ROOT . "\\sdp.have";
  45. my $SDP_SRV = $SOURCE_ROOT . "\\sdp.srv";
  46. my $SRCSRV_INI = $SOURCE_ROOT . "\\srcsrv.ini";
  47. my $DEBUG = FALSE;
  48. my $HELP = FALSE;
  49. #
  50. # Allow override of Source Depot tool from environment
  51. #
  52. if (defined $ENV{SDP_CMD}) {
  53. $SDP_CMD = $ENV{SDP_CMD};
  54. }
  55. #
  56. # Allow override of source root from environment
  57. #
  58. if (defined $ENV{SOURCE_ROOT}) {
  59. $SOURCE_ROOT = $ENV{SOURCE_ROOT};
  60. }
  61. #
  62. # Allow override of symbols root from environment
  63. #
  64. if (defined $ENV{SYMBOLS_ROOT}) {
  65. $SYMBOLS_ROOT = $ENV{SYMBOLS_ROOT};
  66. }
  67. #
  68. # Allow override of sdp.have from environment
  69. #
  70. if (defined $ENV{SDP_HAVE}) {
  71. $SDP_HAVE = $ENV{SDP_HAVE};
  72. }
  73. #
  74. # Allow override of sdp.srv from environment
  75. #
  76. if (defined $ENV{SDP_SRV}) {
  77. $SDP_SRV = $ENV{SDP_SRV};
  78. }
  79. #
  80. # Allow override of srcsrv.ini from environment
  81. #
  82. if (defined $ENV{SRCSRV_INI}) {
  83. $SRCSRV_INI = $ENV{SRCSRV_INI};
  84. }
  85. #
  86. # Process the command line
  87. #
  88. my @opt;
  89. foreach (@ARGV) {
  90. # handle command options
  91. if (substr($_, 0, 1) =~ /^[\/-]$/) {
  92. # options that set values
  93. if ( (@opt = split(/=/, $_))==2 ) {
  94. block: {
  95. $SOURCE_ROOT = $opt[1], last if ( uc substr($opt[0], 1) eq "SOURCE" );
  96. $SYMBOLS_ROOT = $opt[1], last if ( uc substr($opt[0], 1) eq "SYMBOLS" );
  97. $SDP_HAVE = $opt[1], last if ( uc substr($opt[0], 1) eq "HAVE" );
  98. $SDP_SRV = $opt[1], last if ( uc substr($opt[0], 1) eq "SRV" );
  99. $SRCSRV_INI = $opt[1], last if ( uc substr($opt[0], 1) eq "INI" );
  100. printf("WARN $0: Unknown option \"$_\". Ignored.\n");
  101. 1;
  102. }
  103. # options that are just flags
  104. } else {
  105. block: {
  106. $DEBUG = TRUE, last if ( uc substr($_, 1) eq "DEBUG");
  107. $HELP = TRUE, last if ( uc substr($_, 1) eq "?");
  108. printf("WARN $0: Unknown option \"$_\". Ignored.\n");
  109. 1;
  110. }
  111. }
  112. # non-option parameters
  113. } else {
  114. printf("WARN $0: Unknown option \"$_\". Ignored.\n");
  115. }
  116. }
  117. if ($HELP) {
  118. Usage();
  119. exit(1);
  120. }
  121. if ($DEBUG) {
  122. printf("INFO $0: ROOT == $SOURCE_ROOT\n");
  123. printf("INFO $0: SYMBOLS == $SYMBOLS_ROOT\n");
  124. printf("INFO $0: HAVE == $SDP_HAVE\n");
  125. printf("INFO $0: SRV == $SDP_SRV\n");
  126. printf("INFO $0: INI == $SRCSRV_INI\n");
  127. printf("INFO $0: SDP_CMD == $SDP_CMD\n");
  128. }
  129. my $CURDIR = `cd`; # remember where we are...
  130. chdir("$SOURCE_ROOT");
  131. #------------------------------------------------------------------------------
  132. #
  133. # Create the srcsv ini file
  134. # If $SRCSRV_INI already exists, skip this step
  135. #
  136. #------------------------------------------------------------------------------
  137. if (! -e $SRCSRV_INI) {
  138. my $SaveDir = `cd`;
  139. chomp $SaveDir;
  140. while (! -e "sd.ini") {
  141. chdir("..");
  142. }
  143. open(hProc, "$SDP_CMD info ...|");
  144. my $LastDir = "";
  145. my $DepotName = "";
  146. my $Server = "";
  147. while (<hProc>) {
  148. chomp;
  149. if (m/^-/) {
  150. m/([^\s]+)\s+-*$/i;
  151. $LastDir = uc $1;
  152. $DepotName = substr($LastDir, rindex($LastDir, "\\")+1);
  153. } elsif (m/^Server\saddress:\s*(.*)\s*$/i) {
  154. $Server = lc $1;
  155. open(hFile, ">>$SRCSRV_INI");
  156. printf hFile "$DepotName=$Server\n";
  157. close(hFile);
  158. }
  159. }
  160. close(hProc);
  161. chdir("$SaveDir");
  162. }
  163. #------------------------------------------------------------------------------
  164. #
  165. # Create a file listing using %SDP_CMD%
  166. # If $SDP_HAVE already exists, skip this step
  167. #
  168. #------------------------------------------------------------------------------
  169. if (! -e $SDP_HAVE) {
  170. printf("INFO $0: Generating $SDP_HAVE...\n") if ($DEBUG);
  171. open(hFile, ">$SDP_HAVE") || die("ERROR $0: Can't open $SDP_HAVE ($!). Terminating.\n");
  172. open(hPROC, "$SDP_CMD have |") || die("ERROR $0: Can't run '$SDP_CMD HAVE' ($!). Terminating.\n");
  173. while (<hPROC>) {
  174. next if (m/^--/);
  175. next if (m/^\s*$/);
  176. next unless (m/#/);
  177. chomp;
  178. @data = split(/\s+-\s+/, $_);
  179. printf(hFile "$data[1]=$data[0]\n");
  180. }
  181. close(hPROC);
  182. close(hFile);
  183. } else {
  184. printf("INFO $0: $SDP_HAVE exists. Using it...\n") if ($DEBUG);
  185. }
  186. #------------------------------------------------------------------------------
  187. #
  188. # Create the server file listing
  189. # If $SDP_SRV already exists, skip this step
  190. #------------------------------------------------------------------------------
  191. if (! -e $SDP_SRV) {
  192. printf("INFO $0: Generating $SDP_SRV...\n") if ($DEBUG);
  193. open(hPROC, "$SDP_CMD info|") || die("ERROR $0: Can't get Source Depot info ($!). Terminating.\n");
  194. my ($cli_root, %Servers);
  195. while (<hPROC>) {
  196. chomp;
  197. if (m/^Client root:\s*(.*)/) {
  198. $cli_root = uc $1;
  199. } elsif (m/^Server address:\s*(.*)/) {
  200. # only get the first depot pointing to a location
  201. if (! defined $Servers{$cli_root} ) {
  202. $Servers{$cli_root} = lc $1;
  203. }
  204. }
  205. }
  206. close(hPROC);
  207. open(hFile, ">$SDP_SRV") || die("ERROR $0: Can't open $SDP_SRV ($!). Terminating.\n");
  208. foreach (sort keys %Servers) {
  209. printf(hFile "$_=$Servers{$_}\n");
  210. }
  211. close(hFile);
  212. } else {
  213. printf("INFO $0: $SDP_SRV exists. Using it...\n") if ($DEBUG);
  214. }
  215. chdir("$CURDIR"); # return to where we were....
  216. #------------------------------------------------------------------------------
  217. #
  218. # Make any changes to the files here!
  219. #
  220. #------------------------------------------------------------------------------
  221. #
  222. # Hashes used to lookup source file -> sd file information
  223. #
  224. my %FILE_LISTING;
  225. my %SERVER_TO_VAR;
  226. my %VAR_TO_SERVER;
  227. my %PATH_TO_SERVER;
  228. #------------------------------------------------------------------------------
  229. #
  230. # Create a file lookup from SDP_HAVE
  231. #
  232. # When this code finishes, the global hash %FILE_LISTING should contain
  233. # a map of local source (key)==server source (value). For example:
  234. # $FILE_LISTING{"Z:\NT\DRIVERS\APM\APMBATT\APMBATT.RC"} =
  235. # "//depot/main/drivers/apm/apmbatt/apmbatt.rc#1"
  236. #
  237. # The local source should be in upper case. The case of the remote source is
  238. # undefined.
  239. #
  240. # If $SDP_HAVE already exists, skip this step
  241. #
  242. #------------------------------------------------------------------------------
  243. if (-e $SDP_HAVE) {
  244. printf("INFO $0: Reading $SDP_HAVE...\n") if ($DEBUG);
  245. open(hFile, "$SDP_HAVE") || die("ERROR $0: Can't open $SDP_HAVE ($!). Terminating.\n");
  246. while (<hFile>) {
  247. next if (m/^;/); # skip comments
  248. next if (m/^\s*$/);# skip blank lines
  249. chomp;
  250. if ( (@data = split(/=/, $_)) == 2) {
  251. $data[0] =~ s/^\s*//;
  252. $data[0] =~ s/\s*$//;
  253. $data[1] =~ s/^\s*//;
  254. $data[1] =~ s/\s*$//;
  255. $FILE_LISTING{uc $data[0]} = $data[1];
  256. } else {
  257. printf("WARN $0: Skipping misformed line ${SDP_HAVE}:${.}\n") if ($DEBUG);
  258. }
  259. }
  260. close(hFile);
  261. } else {
  262. die("ERROR $0: $SDP_HAVE doesn't exist! Terminating!\n");
  263. }
  264. #------------------------------------------------------------------------------
  265. #
  266. # Create the server lookup
  267. #
  268. # This code initializes the global hashes %PATH_TO_SERVER
  269. #
  270. # %PATH_TO_SERVER maps a local root path to a particular SD server. For
  271. # example: $PATH_TO_SERVER{Z:\NT\DRIVERS}= depot.somecompany.com:2005
  272. # The values are expected to be in lower case.
  273. #
  274. #------------------------------------------------------------------------------
  275. if (-e $SDP_SRV) {
  276. printf("INFO $0: Reading $SDP_SRV...\n") if ($DEBUG);
  277. open(hFile, "$SDP_SRV") || die("ERROR $0: Can't open $SDP_SRV ($!). Terminating.\n");
  278. my ($var, $cli_root, %Servers);
  279. while (<hFile>) {
  280. next if (m/^;/); # skip comments
  281. next if (m/^\s*$/);# skip blank lines
  282. chomp;
  283. if ( (@data = split(/=/, $_)) == 2) {
  284. $data[0] =~ s/^\s*//;
  285. $data[0] =~ s/\s*$//;
  286. $data[1] =~ s/^\s*//;
  287. $data[1] =~ s/\s*$//;
  288. if (! defined $PATH_TO_SERVER{uc $data[0]} ) {
  289. $PATH_TO_SERVER{uc $data[0]} = lc $data[1];
  290. }
  291. } else {
  292. printf("WARN $0: Skipping misformed line ${SDP_SRV}:${.}\n") if ($DEBUG);
  293. }
  294. }
  295. close(hFile);
  296. } else {
  297. die("ERROR $0: $SDP_SRV doesn't exist! Terminating!\n");
  298. }
  299. #------------------------------------------------------------------------------
  300. #
  301. # Generate a variable substitution hash from SRCSRV_INI
  302. #
  303. # %SERVER_TO_VAR maps a local given server to a variable name. For example:
  304. # $SERVER_TO_VAR{depot.somecompany.com:2005}=SRV00
  305. # The keys are expected to be in lower case.
  306. #
  307. # %VAR_TO_SERVER is a reverse map of %SERVER_TO_VAR.
  308. #
  309. # NOTE: $SERVER_TO_VAR{$PATH_TO_SERVER{$path}} is expected to return the
  310. # value of the depot variable that is used to get the file described by
  311. # $path.
  312. #------------------------------------------------------------------------------
  313. if (-e $SRCSRV_INI) {
  314. printf("INFO $0: Reading $SRCSRV_INI...\n") if ($DEBUG);
  315. open(hFile, "$SRCSRV_INI") || die("ERROR $0: Can't open $SRCSRV_INI ($!). Terminating.\n");
  316. while (<hFile>) {
  317. next if (m/^;/); # skip comments
  318. next if (m/^\s*$/);# skip blank lines
  319. chomp;
  320. if ( (@data = split(/=/, $_)) == 2) {
  321. $data[0] =~ s/^\s*//;
  322. $data[0] =~ s/\s*$//;
  323. $data[1] =~ s/^\s*//;
  324. $data[1] =~ s/\s*$//;
  325. $SERVER_TO_VAR{lc $data[1]} = uc $data[0];
  326. } else {
  327. printf("WARN $0: Skipping misformed line ${SRCSRV_INI}:${.}\n") if ($DEBUG);
  328. }
  329. }
  330. close(hFile);
  331. } else {
  332. die("ERROR $0: $SRCSRV_INI doesn't exist! Terminating!\n");
  333. }
  334. %VAR_TO_SERVER = reverse %SERVER_TO_VAR;
  335. #------------------------------------------------------------------------------
  336. #
  337. # Initialization is done. Now, recurse the tree that contains the symbols and
  338. # call StuffSrcIntoPdb() for every directory and file found. StuffSrcIntoPDB()
  339. # ensures that is only operates on files ending in .PDB
  340. #
  341. #------------------------------------------------------------------------------
  342. RecurseDirectoryTree("$SYMBOLS_ROOT", \&StuffSrcIntoPdb);
  343. # --------------------------------------------------------------------------
  344. # Recurses a directory tree and invokes the callback routine for
  345. # every file and directory found (except '.' and '..')
  346. # --------------------------------------------------------------------------
  347. sub RecurseDirectoryTree {
  348. # setup
  349. my $TreeRoot = shift;
  350. $TreeRoot =~ s/\\$//; # cut possible trailing '\'
  351. my $Function = shift;
  352. my $file;
  353. my @files;
  354. local *hDIR;
  355. if (!opendir(hDIR, "$TreeRoot") ) {
  356. printf("WARN $0: Cannot open $TreeRoot\n");
  357. return(0);
  358. }
  359. #
  360. # Loop through all entries
  361. #
  362. foreach $file ( readdir(hDIR) ) {
  363. next if ($file eq "."); # skip '.'
  364. next if ($file eq ".."); # skip '..'
  365. $file = "$TreeRoot\\$file"; # add parent path
  366. &$Function("$file"); # invoke callback
  367. if (-d "$file" ){ # recurse directories
  368. RecurseDirectoryTree("$file", \&$Function);
  369. }
  370. }
  371. # cleanup
  372. closedir(hDIR);
  373. }
  374. # --------------------------------------------------------------------------
  375. # Given a file with full path, return either an array of [$file, $sd_path]
  376. # or an empty array.
  377. # --------------------------------------------------------------------------
  378. sub ResolveFileToSD {
  379. my @SD_SPEC;
  380. my $file = shift;
  381. if ( defined $FILE_LISTING{uc $file}) {
  382. @SD_SPEC = ($file, $FILE_LISTING{uc $file});
  383. printf("INFO $0: \t ... found $file == $FILE_LISTING{uc $file}\n") if ($DEBUG);
  384. } else {
  385. @SD_SPEC = ();
  386. }
  387. return(@SD_SPEC);
  388. }
  389. # --------------------------------------------------------------------------
  390. # Processes a single PDB
  391. # --------------------------------------------------------------------------
  392. sub StuffSrcIntoPdb {
  393. my $line;
  394. my $file = shift;
  395. #
  396. # Only operate on existing files that end in ".pdb"
  397. #
  398. return if ($file !~ /\.pdb$/i);
  399. return if (! -e "$file");
  400. return if ( -d "$file");
  401. printf("INFO $0: Processing $file...\n") if ($DEBUG);
  402. #
  403. # Get a temp file to work with
  404. #
  405. my $TempFile;
  406. (undef, $TempFile) = tempfile("PdbXXXXXX", SUFFIX=>".stream", OPEN => 0, DIR => "$ENV{TEMP}");
  407. if (! open(hTEMP, ">$TempFile") ) {
  408. printf("WARN $0: Can't open tempfile for $file ($!) - skipping\n");
  409. return();
  410. }
  411. #
  412. # List each entry in %VAR_TO_SERVER into the PDB so substitution can be
  413. # done on the client side if need be.
  414. #
  415. printf(hTEMP "SRCSRV: variables ------------------------------------------\n");
  416. foreach (sort keys %VAR_TO_SERVER) {
  417. printf(hTEMP "$_=$VAR_TO_SERVER{$_}\n");
  418. }
  419. #
  420. # Process all of the files used to build the binary this PDB belongs to.
  421. #
  422. printf(hTEMP "SRCSRV: source files ---------------------------------------\n");
  423. if (! open(hCV, "cvdump -sf $file|") ) {
  424. printf("WARN $0: Can't call cvdump! ($!)\n");
  425. return();
  426. }
  427. LOOP: while ( $line = <hCV> ) {
  428. last LOOP if ($line =~ /^\*\*\* SOURCE FILES/);
  429. }
  430. while ($line = <hCV>) {
  431. my @file_spec = ();
  432. chomp $line;
  433. next if ($line =~ m/^\s*$/);
  434. #
  435. # Translate the local file path to a source depot path and revision
  436. #
  437. @file_spec = ResolveFileToSD($line);
  438. #
  439. # Make sure an empty array wasn't returned
  440. #
  441. if (defined @file_spec && $#file_spec == 1) {
  442. #
  443. # Loop through the SD servers in reverse order until we find one
  444. # who's local root path matches the root path of the current file.
  445. #
  446. foreach (reverse sort keys %PATH_TO_SERVER) {
  447. if ($file_spec[0] =~ /^\Q$_\E/i) {
  448. #
  449. # Write the source line into the format:
  450. # <LocalPath>*<ServerVariable>*<SourceDepotPath>
  451. #
  452. my $sdvar = "\${".$SERVER_TO_VAR{lc $PATH_TO_SERVER{$_}}."}";
  453. printf(hTEMP "SD: $file_spec[0]*$sdvar*$file_spec[1]\n");
  454. @file_spec = ();
  455. }
  456. }
  457. }
  458. }
  459. close(hCV);
  460. printf(hTEMP "SRCSRV: end ------------------------------------------------\n");
  461. close(hTEMP);
  462. #
  463. # Push the information into the "srcsrv" stream of the PDB
  464. #
  465. system("pdbstr -w -i:$TempFile -p:$file -s:srcsrv");
  466. #
  467. # Clean up our temp file
  468. #
  469. unlink("$TempFile");
  470. }
  471. sub Usage {
  472. print <<USAGE;
  473. $0 : [/Source=<path>] [/Symbols=<path>] [/Ini=<file>] [/Have=<file>] [/Srv=<file>] [/debug]
  474. NAME SWITCH ENV. VAR Default
  475. ------------------------------------------------------------------------------------------
  476. 1) Source root Source SOURCE_ROOT Current directory
  477. 2) Symbols root Symbols SYMBOLS_ROOT Current directory
  478. 3) sdp.have Have SDP_HAVE \%SOURCE_ROOT\%\\sdp.have
  479. 4) sdp.srv Srv SDP_SRV \%SOURCE_ROOT\%\\sdp.srv
  480. 5) srcsrv.ini Ini SRCSRV_INI \%SOURCE_ROOT\%\\srcsrv.ini
  481. 6) sdp command <n/a> SDP_CMD sdp
  482. Precedence is: Default, environment, cmdline switch. (ie. env overrides default, switch
  483. overrides env).
  484. Using '/debug' will turn on verbose output.
  485. Verbose output is classed as either informational, warning, or error. Errors are always
  486. shown and result in termination of the script. Informational messages are just that-
  487. verbose output that may be of interest. Warnings indicate the detection of a recoverable
  488. error that may affect validity of the results.
  489. USAGE
  490. }