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.

552 lines
15 KiB

  1. @REM -----------------------------------------------------------------
  2. @REM
  3. @REM buildinx - jtolman
  4. @REM Build several inf section tables and combine them into
  5. @REM an inx file that will compile into the appropriate table.
  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} . "\\sp";
  16. use lib $ENV{RAZZLETOOLPATH};
  17. use Logmsg;
  18. use ParseArgs;
  19. use InfData;
  20. sub Usage { print<<USAGE; exit(1) }
  21. Usage: buildinx [options] <root_dir> <build_num>
  22. /xs Ignore server skus.
  23. /xc Ignore client skus.
  24. /m Remerge old and new tables.
  25. /r Rebuild all tables.
  26. /i Remake infscan.lst using infscan before reading it.
  27. /o Use original directory structure.
  28. /s Use a service pack build instead of a full build.
  29. USAGE
  30. my ($newbld, $rebuild, $merge, $dir, $build, $daytona, $svc);
  31. my ($_xs, $_xc, $_i, $_o, $_s);
  32. parseargs('?' => \&Usage,
  33. 'r' => \$rebuild,
  34. 'm' => \$merge,
  35. 'xs'=> \$_xs,
  36. 'xc'=> \$_xc,
  37. 'i' => \$_i,
  38. 'o' => \$_o,
  39. 's' => \$svc,
  40. \$dir,
  41. \$build
  42. );
  43. if ( !$dir or !$build ) {
  44. errmsg( "Incomplete command line." );
  45. Usage();
  46. }
  47. $_xs = $_xs ? "/xs":"";
  48. $_xc = $_xc ? "/xc":"";
  49. $_i = $_i ? "/i" :"";
  50. $_o = $_o ? "/o" :"";
  51. $_s = $svc ? "/s" :"";
  52. my $exe = "$ENV{RAZZLETOOLPATH}\\sp";
  53. # Fixed data.
  54. my @skuletters = ();
  55. if ( $_xc eq "" ) {
  56. push @skuletters, ("p", "c");
  57. }
  58. if ( $_xs eq "" ) {
  59. push @skuletters, ("s", "a", "d");
  60. # push @skuletters, ("l", "b");
  61. }
  62. # Format: ANSI_Codepage LCID bit
  63. my %langinfo = (
  64. "ara" => "1256 401",
  65. "br" => "1252 416",
  66. "chh" => "0950 C04",
  67. "chs" => "0936 804",
  68. "cht" => "0950 404",
  69. "cs" => "1250 405",
  70. "da" => "1252 406",
  71. "el" => "1253 408",
  72. "es" => "1252 C0A",
  73. "fi" => "1252 40B",
  74. "fr" => "1252 40C",
  75. "ger" => "1252 407",
  76. "heb" => "1255 40D",
  77. "hu" => "1250 40E",
  78. "it" => "1252 410",
  79. "jpn" => "0932 411",
  80. "kor" => "0949 412",
  81. "nl" => "1252 413",
  82. "no" => "1252 414",
  83. "pl" => "1250 415",
  84. "psu" => "1253 408",
  85. "pt" => "1252 816",
  86. "ru" => "1251 419",
  87. "sv" => "1252 41D",
  88. "tr" => "1254 41F",
  89. "usa" => "1252 409"
  90. );
  91. my %archinfo = (
  92. "x86" => "\@i:",
  93. "ia64"=> "\@m:",
  94. "" => "\@\@:"
  95. );
  96. my %revarchs = (
  97. "\@i:"=> "x86",
  98. "\@m:"=> "ia64"
  99. );
  100. my %archbits = (
  101. "x86" => 0,
  102. "ia64"=> 0
  103. );
  104. # Output file notation information.
  105. my %sets;
  106. my %setnames;
  107. my %archlangs;
  108. my $nextbit = 1;
  109. my $maxCount = 4;
  110. my $setval = 1;
  111. my $subval = 1;
  112. # Build the tables, as needed
  113. if ( !open BUILDS, "$dir\\builds.txt" ) {
  114. errmsg( "Unable to open builds.txt." );
  115. die;
  116. }
  117. my @builds = ();
  118. while ( <BUILDS> ) {
  119. /^\s*(\S*)\s*(\S*)\s*$/;
  120. my $arch = lc $1;
  121. my $lang = lc $2;
  122. push @builds, "$arch $lang";
  123. my $_w = ($arch eq "ia64") ? "/w":"";
  124. if ( !exists $langinfo{$lang} ) {
  125. wrnmsg( "Skipped unknown language: $lang" );
  126. next;
  127. }
  128. if ( !exists $archinfo{$arch} ) {
  129. wrnmsg( "Skipped unknown architecture: $arch" );
  130. next;
  131. }
  132. # Generate a table if needed.
  133. my $start = 0;
  134. my $bdir = $svc ? "xpsp1\\$build\\$lang":"main\\$lang\\$build";
  135. my $ext = $svc ? "tmp":"tbl";
  136. if ( $rebuild or !-f "$dir\\$arch$lang.$ext" ) {
  137. my $_c = "/c $dir\\all.txt" if -f "$dir\\all.txt";
  138. if ( opendir CHANGE, $dir ) {
  139. my @files = readdir CHANGE;
  140. foreach (@files) {
  141. my $cfile = "";
  142. $cfile = "$dir\\$_" if /^(inx\.)?($ENV{_BUILDARCH}\.)?($lang\.)?txt$/i;
  143. next if $cfile eq "";
  144. $_c .= " /c:$cfile";
  145. }
  146. }
  147. logmsg( "Generating table for $build $arch $lang..." );
  148. my $cmd = "$exe\\findinfdata /f $dir\\$arch$lang.$ext /l $lang " .
  149. "\\\\ntdev\\release\\$bdir\\$arch\Efre " .
  150. "$_c $_w $_o $_i $_s $_xs $_xc";
  151. #print "$cmd\n";
  152. system $cmd;
  153. $start = 1 if $svc;
  154. }
  155. # Merge with an old table if needed.
  156. if ( $merge or $start or !-f "$dir\\$arch$lang.tbl" or !-f "$dir\\$arch$lang\Ediff.tbl" ) {
  157. my $oldt = "\\\\ntdev\\release\\$bdir\\$arch\Efre\\bin\\idw\\srvpack\\infsect.tbl";
  158. my $newt = "$dir\\$arch$lang.tmp";
  159. my $oldd = "\\\\ntdev\\release\\$bdir\\$arch\Efre\\bin\\idw\\srvpack\\infdiff.tbl";
  160. if ( !-f $oldd ) {
  161. $oldd = "$dir\\temp.tbl";
  162. system "touch /c $oldd";
  163. }
  164. my $outt = "$dir\\$arch$lang.tbl";
  165. my $outd = "$dir\\$arch$lang\Ediff.tbl";
  166. logmsg( "Merging old and new tables for $build $arch $lang..." );
  167. #print "$exe\\mergetables $oldt $newt $oldd $outt $outd\n";
  168. system "$exe\\mergetables $oldt $newt $oldd $outt $outd";
  169. }
  170. }
  171. # Setup for merging the tables.
  172. logmsg( "Merging the tables..." );
  173. system "md $dir\\final" if !-d "$dir\\final";
  174. undef $/;
  175. my %tables;
  176. my %lines;
  177. foreach my $build ( @builds ) {
  178. my $file = InfTbl->new($build);
  179. my $arch = $file->{ARCH};
  180. my $lang = $file->{LANG};
  181. $tables{"$arch $lang"} = $file;
  182. my $setname = "$archinfo{$arch}\%$lang\%";
  183. $sets{$nextbit} = $setname;
  184. $setnames{$setname} = $nextbit;
  185. $archbits{$arch} |= $nextbit;
  186. if ( $arch eq "ia64" and exists $tables{"x86 $lang"} ) {
  187. my $oldname = $archinfo{"x86"} . "\%$lang\%";
  188. my $newname = $archinfo{""} . "\%$lang\%";
  189. my $bits = $setnames{$oldname} | $setnames{$setname};
  190. $sets{$bits} = $newname;
  191. $setnames{$newname} = $bits;
  192. }
  193. $nextbit = $nextbit << 1;
  194. if ( exists $langinfo{$file->{LANG}} ) {
  195. my ($cpage, $lcid) = split(/\s+/, $langinfo{$file->{LANG}});
  196. $file->addValue($file->{LANG},"");
  197. $file->addValue("cpage",$cpage);
  198. $file->addValue("lcid",$lcid);
  199. }
  200. }
  201. my @archs = keys %archbits;
  202. $archbits{""} = 0;
  203. foreach my $arch ( @archs ) {
  204. $archbits{""} |= $archbits{$arch};
  205. }
  206. foreach my $arch ( keys %archinfo ) {
  207. my $setname = "$archinfo{$arch}";
  208. if ( exists $sets{$archbits{$arch}} ) {
  209. $archlangs{$setname} = $sets{$archbits{$arch}};
  210. }
  211. $sets{$archbits{$arch}} = $setname;
  212. $setnames{$setname} = $archbits{$arch};
  213. }
  214. # Merge multiple files together.
  215. sub mergeTables {
  216. my ($suffix, $outfile) = @_;
  217. # Read in the necessary files.
  218. my %queue;
  219. my %final;
  220. foreach my $build ( @builds ) {
  221. $build =~ /^(\S*) (\S*)$/;
  222. my $arch = lc $1;
  223. my $lang = lc $2;
  224. my $file = $tables{"$arch $lang"};
  225. $file->loadLines("$dir\\$arch$lang$suffix.tbl");
  226. $file->getNext();
  227. }
  228. if ( !open OUT, ">$outfile" ) {
  229. errmsg( "Unable to open output file $outfile." );
  230. die;
  231. }
  232. # Go through all of the files.
  233. for (;;) {
  234. # Find the next file to work on.
  235. my $file = "\xff";
  236. foreach my $entry ( keys %lines ) {
  237. $file = $entry if ($entry cmp $file) < 0;
  238. }
  239. last if $file eq "\xff";
  240. # Read out all of the lines for that file, and merge the lines as much as possible.
  241. my $list = $lines{$file};
  242. while ( $#$list >= 0 ) {
  243. my $line = shift @$list;
  244. $tables{$line->{SRC}}->getNext();
  245. my $temp = $line->{DATA}->{DEST};
  246. $line->{DATA}->{DEST} = "";
  247. my $sect = lc $line->getLine();
  248. $line->{DATA}->{DEST} = $temp;
  249. $queue{$sect} = [ () ] if !exists $queue{$sect};
  250. push @{ $queue{$sect} }, $line;
  251. }
  252. delete $lines{$file};
  253. # Generate entries for each section.
  254. foreach my $sect ( sort keys %queue ) {
  255. # See if there are different names for different languages.
  256. my $entries = $queue{$sect};
  257. foreach my $entry ( @$entries ) {
  258. my $line = $entry->getLine();
  259. $final{$line} = [ () ] if !exists $final{$line};
  260. push @{ $final{$line} }, $entry->{SRC};
  261. }
  262. my $count = keys %final;
  263. my $subst = $count >= $maxCount;
  264. my @lines;
  265. my @srcs = ();
  266. if ( $subst ) {
  267. foreach my $entry ( @$entries ) {
  268. $tables{$entry->{SRC}}->addValue("sub$subval",$entry->getDest());
  269. push @srcs, $entry->{SRC};
  270. }
  271. my $line = $entries->[0];
  272. $line->setDest("\%sub$subval\%");
  273. @lines = ($line->getLine());
  274. } else {
  275. @lines = keys %final;
  276. }
  277. # Step through the different lines for each section.
  278. foreach my $line ( @lines ) {
  279. @srcs = @{ $final{$line} } if !$subst;
  280. # Figure out what langs and archs use this line.
  281. my $bits = 0;
  282. foreach my $src ( @srcs ) {
  283. my ($arch, $lang) = split(/ /, $src, 2);
  284. $bits |= $setnames{"$archinfo{$arch}\%$lang\%"};
  285. }
  286. # Create a new set if needed.
  287. my $prefix = "";
  288. if ( !$subst and !exists $sets{$bits} ) {
  289. my $bit = 1;
  290. my $temp = $bits;
  291. while ( $temp ) {
  292. if ( $bits & $bit ) {
  293. $sets{$bit} =~ /^([^\%]*)(?:\%([^\%]*)\%)?$/;
  294. $archlangs{$1} =~ /^([^\%]*)\%([^\%]*)\%$/ if !defined $2;
  295. my $tbl = $tables{"$revarchs{$1} $2"};
  296. $tbl->addValue("set$setval","");
  297. }
  298. $bit = $bit << 1;
  299. $temp = $temp >> 1;
  300. }
  301. my $test = 0;
  302. foreach my $arch ( @archs ) {
  303. my $mybits = $bits & $archbits{$arch};
  304. my $myname = $archinfo{$arch} . "\%set$setval\%";
  305. next if $mybits == 0;
  306. $test = 1 if $mybits == $bits;
  307. next if exists $sets{$mybits};
  308. $sets{$mybits} = $myname;
  309. $setnames{$myname} = $mybits;
  310. }
  311. if ( !$test ) {
  312. my $myname = $archinfo{""} . "\%set$setval\%";
  313. $sets{$bits} = $myname;
  314. $setnames{$myname} = $bits;
  315. }
  316. $setval++;
  317. }
  318. if ( $subst ) {
  319. foreach my $arch ( @archs ) {
  320. next if ($archbits{$arch} & $bits) != $bits;
  321. $prefix = $archinfo{$arch};
  322. }
  323. $prefix = $archinfo{""} if $prefix eq "";
  324. } else {
  325. $prefix = $sets{$bits};
  326. }
  327. # Generate a line.
  328. print OUT "$prefix$line\n";
  329. $subval++ if $subst;
  330. }
  331. undef %final;
  332. }
  333. undef %queue;
  334. }
  335. close OUT;
  336. }
  337. # Do the merging.
  338. mergeTables("", "$dir\\final\\infsect.inx");
  339. mergeTables("diff", "$dir\\final\\infdiff.inx") if $svc;
  340. # Combine the architecture files.
  341. foreach my $lang ( keys %langinfo ) {
  342. if ( exists $tables{"x86 $lang"} and exists $tables{"ia64 $lang"} ) {
  343. my $filei = $tables{"x86 $lang"}->{DEFS};
  344. my $filem = $tables{"ia64 $lang"}->{DEFS};
  345. my @file = ();
  346. my %lines;
  347. foreach my $line ( @$filem ) {
  348. $lines{$line} = "";
  349. }
  350. foreach my $line ( @$filei ) {
  351. if ( exists $lines{$line} ) {
  352. delete $lines{$line};
  353. push @file, "\@\@:$line";
  354. } else {
  355. push @file, "\@i:$line";
  356. }
  357. }
  358. foreach my $line ( keys %lines ) {
  359. push @file, "\@m:$line";
  360. }
  361. $tables{"x86 $lang"}->{DEFS} = [ @file ];
  362. delete $tables{"ia64 $lang"};
  363. }
  364. }
  365. # Save the language specific files.
  366. foreach my $tbl ( keys %tables ) {
  367. $tables{$tbl}->makeFile();
  368. }
  369. ####################################################################
  370. # Class for representing a table.
  371. package InfTbl;
  372. use strict;
  373. use lib $ENV{RAZZLETOOLPATH} . "\\PostBuildScripts";
  374. use lib $ENV{RAZZLETOOLPATH};
  375. use Logmsg;
  376. # Create new inf section with just a name.
  377. sub new {
  378. my $class = shift;
  379. my ($build) = @_;
  380. # Collect all needed data.
  381. $build =~ /^(\S*) (\S*)$/;
  382. my $arch = lc $1;
  383. my $lang = lc $2;
  384. my @defs = ();
  385. if ( !open TABLE, "$dir\\$arch$lang.tbl" ) {
  386. errmsg( "Unable to find table $dir\\$arch$lang.tbl" );
  387. die;
  388. }
  389. my @file = split(/\s*\n/, <TABLE>);
  390. close TABLE;
  391. # Construct the object.
  392. my $self = {};
  393. $self->{ARCH} = $arch; # Architecture the table is for.
  394. $self->{LANG} = $lang; # Language the table is for.
  395. $self->{DEFS} = \@defs; # Contents of the definition file.
  396. return bless $self;
  397. }
  398. # Get more lines from a file.
  399. sub loadLines {
  400. my $self = shift;
  401. my ($file) = @_;
  402. if ( !open TABLE, "$file" ) {
  403. errmsg( "Unable to find table $file" );
  404. die;
  405. }
  406. my @file = split(/\s*\n/, <TABLE>);
  407. close TABLE;
  408. $self->{FILE} = \@file;
  409. }
  410. # Put the next line in the first hash table.
  411. sub getNext {
  412. my $self = shift;
  413. my $line = shift @{ $self->{FILE} };
  414. return if !defined $line;
  415. my $entry = InfLine->new($line, "$self->{ARCH} $self->{LANG}");
  416. my $file = $entry->{DATA}->getSrc();
  417. $lines{$file} = [ () ] if !exists $lines{$file};
  418. push @{ $lines{$file} }, $entry;
  419. }
  420. # Add a key value pair to the definitions file.
  421. sub addValue {
  422. my $self = shift;
  423. my ($key, $value, $archbits) = @_;
  424. my $defs = $self->{DEFS};
  425. push @$defs, "$key\t=\t$value";
  426. }
  427. # Save the definitions file to disk.
  428. sub makeFile {
  429. my $self = shift;
  430. my $fname = $self->{LANG};
  431. if ( !open DEF, ">$dir\\final\\$fname\.txt" ) {
  432. errmsg( "Unable to open definition file $dir\\final\\$fname.txt" );
  433. die;
  434. }
  435. print DEF "\@*: This file is generated automatically.\n";
  436. print DEF "\@*: Do not edit; any changes will be lost in the next revision.\n";
  437. print DEF "\@*:\n";
  438. print DEF join("\n", @{ $self->{DEFS} });
  439. close DEF;
  440. }
  441. 1;
  442. ####################################################################
  443. # Class for representing a line in the table.
  444. package InfLine;
  445. use strict;
  446. use lib $ENV{RAZZLETOOLPATH} . "\\PostBuildScripts";
  447. use lib $ENV{RAZZLETOOLPATH};
  448. use Logmsg;
  449. # Extract needed data from a line.
  450. sub new {
  451. my $class = shift;
  452. my ($line, $src) = @_;
  453. # Collect all needed data.
  454. chomp $line;
  455. my $data = InfData->new();
  456. $data->readLine($line, \@skuletters);
  457. # Construct the object.
  458. my $self = {};
  459. $self->{SRC} = $src;
  460. $self->{DATA} = $data;
  461. return bless $self;
  462. }
  463. # Recombine the data to create the line.
  464. sub getLine {
  465. my $self = shift;
  466. return $self->{DATA}->getLine(\@skuletters);
  467. }
  468. # Change the destination filename.
  469. sub setDest {
  470. my $self = shift;
  471. my ($dest) = @_;
  472. $self->{DATA}->setDest($dest);
  473. }
  474. # Change the source filename.
  475. sub setSrc {
  476. my $self = shift;
  477. my ($src) = @_;
  478. my $fields = $self->{DATA};
  479. return if $fields->{NAME} eq $src;
  480. $fields->{DEST} = $fields->{NAME} if $fields->{DEST} eq "";
  481. $fields->{NAME} = $src;
  482. }
  483. # Change the source directory for the file.
  484. sub setSrcDir {
  485. my $self = shift;
  486. my ($dir) = @_;
  487. $self->{DATA}->setSrcDir($dir);
  488. }
  489. # Find the current source directory.
  490. sub getSrcDir {
  491. my $self = shift;
  492. return $self->{DATA}->getSrcDir();
  493. }
  494. # Figure out the destination filename.
  495. sub getDest {
  496. my $self = shift;
  497. return $self->{DATA}->getDest();
  498. }
  499. 1;