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.

6669 lines
196 KiB

  1. ## Method calling graph ( bit simplified for readability)
  2. ## -------------------------------------------------------------
  3. ##
  4. ## ParseArgs::parseargs -> UseOldParser
  5. ## GetSysDir
  6. ## SwitchWorkDir
  7. ## CleanLogFiles
  8. ## LoadEnv
  9. ## MakeRevEnvVars
  10. ## LoadHotFixFile
  11. ## LoadCodes -> define macros
  12. ## LoadReposit -> LoadKeys -> SetField
  13. ## -> AddTargetPath
  14. ## LoadCmd -> SetField
  15. ##
  16. ## MakeRevEnvVars
  17. ## ReadSysfile -> ReadLine -> LoadRecord -> LineToRecord
  18. ## -> cklang::CkLang
  19. ## -> AddRecord
  20. ## -> SetMacro
  21. ## -> LogRecord
  22. ## -> ReplaceVar
  23. ##
  24. ## -> LoadRuleBlock
  25. ## -> ReadBlock
  26. ## -> ReadRules
  27. ## -> SetMacro -> SetAttribOp
  28. ## -> SetIfFlag -> ReplaceVar
  29. ## -> StrToArray
  30. ## -> Message
  31. ## -> ReadSysfile
  32. ## VerifyCover -> SetMacro
  33. ## -> Create_Tree_Branch -> Get_Except -> GetMacro
  34. ## -> Find_Dot_Dot_Dot -> Match_Subjects
  35. ## -> Remove_Dummy_Create_Branch
  36. ## -> Remove_Root_List
  37. ## -> Find_UnMapping -> readdir -> Error
  38. ## PopulateReposit -> FillFilter
  39. ## -> AddFiles
  40. ## -> AddEntry -> AddFileInfo
  41. ## -> AddTargetPath
  42. ## -> AddTarget
  43. ##
  44. ## -> GetMacro
  45. ## -> IsEmptyHash
  46. ## -> IsHashKey
  47. ## -> IsFoundTarget
  48. ## -> FillTokens -> FillTokEntry
  49. ## -> FillCmd -> IsEmptyHash
  50. ## -> IsHashKey -> GetFieldVal
  51. ## -> RecordToCmd -> GetFieldVal
  52. ## -> IsRepositKey
  53. ## -> GenXcopy
  54. ## -> GenMUICmd
  55. ## -> GenLocCmd -> GenRsrcCmd
  56. ## -> GenLcxCmd (LOC STUDIO)
  57. ## -> GenBgnCmd -> SetSymPaths
  58. ## -> MakeXcopyCmd -> RevEnvVars
  59. ## -> GetLocOpType
  60. ## -> SetBgnSymSw
  61. ## -> GetBgnICodesSw
  62. ## -> GetBgnMultitok
  63. ## -> RevEnvVars
  64. ## -> PushFieldVal
  65. ##
  66. ## GenNmakeFiles -> FillSyscmd -> CmdCompare -> GetFieldVal
  67. ## -> GenMakeDir -> SetField
  68. ## -> PushFieldVal
  69. ## -> UpdTargetEntries -> DeleteKey
  70. ## -> SetField
  71. ## -> RevEnvVars
  72. ## -> ApplyRepositoryFixes -> GetMacro
  73. ## -> RevEnvVars
  74. ## -> FilterOpType
  75. ## -> RevEnvVars
  76. ## -> WriteCompdirTarget
  77. ## -> RevEnvVars
  78. ## -> WriteCompdir
  79. ## -> RevEnvVars
  80. ## -> WriteToMakefile
  81. ## -> WriteSysgenTarget
  82. ## -> WriteSettings
  83. ## -> WriteAllTarget
  84. ## -> WriteFileCmds
  85. ## -> WriteToSyscmd
  86. ## SumErrors
  87. # //////////////////////////////////////////////////////////////////////////////////////////////
  88. use Data::Dumper;
  89. use lib $ENV{"RAZZLETOOLPATH"} . "\\PostBuildScripts";
  90. use lib $ENV{"RAZZLETOOLPATH"};
  91. use cklang;
  92. use ParseTable;
  93. use strict;
  94. no strict 'refs';
  95. # The parsearg.pm needs strict.
  96. use Logmsg;
  97. use PbuildEnv;
  98. use ParseArgs;
  99. $ENV{script_name} = 'sysgen.pl';
  100. use vars (qw(
  101. $CLEAN $LANG $ACCELERATE $WORKDIR $SYNTAX_CHECK_ONLY $VERSION
  102. @SYSFILES $SYSCMD @SYSCMD
  103. $SECTIONS $ARGL
  104. $EXCLUDE_DIRECTORIES $DEFAULT_SECTIONS $SECTIONNAME
  105. $REPOSITORY $CMD_REPOSITORY
  106. $SYSDIR $SYSFILE
  107. $HOTFIX %HOTFIX
  108. $LOGFILE $ERRFILE
  109. $MAKEFILE %MAKEDIR
  110. $TARGETS $MACROS %MACROS
  111. $COMPDIR_FILE_TEMPLATE %COMPDIR_COMMAND %COMPDIR %MAP_COMPDIR_EXT %REATTR_COMMAND
  112. %FILTER %default
  113. $MAP_BINGEN_TOK_EXT %MAP_RSRC_TOK_EXT $CODESFNAME
  114. @MAPPINGS %MAPPING_INDEX %MAP_ERR %CODES %LOCOPS @BGNSWS %BGNSWS
  115. @IFSTACK $FILE $LINE $ERRORS
  116. &Version
  117. @FFILES
  118. @FEXTENSIONS
  119. @FBINDIRS
  120. @FTOKENS
  121. @FTOKDIRS
  122. @TFILES
  123. @TEXTENSIONS
  124. @TBINDIRS
  125. @TTOKENS
  126. @TTOKDIRS
  127. %VERIFYPATH
  128. $VERIFY
  129. @VERIFY
  130. ));
  131. # MAP_ERR: Errors SYSGEN recognizes
  132. # 1101 is used for ERROR instructions found in sysfiles
  133. require 5.003;
  134. my $SELF = "sysgen";
  135. my $VERSION = "5.5";
  136. $SELF = $SELF. " v " . $VERSION;
  137. %MAP_ERR = (
  138. 1001, "sysfile not found",
  139. 1002, "file not found",
  140. 1003, "file not found: unable to run sysgen incrementally",
  141. 1004, "target file not found",
  142. 1005, "filter file not found",
  143. 1006, "unable to open file for reading",
  144. 1007, "unable to open file for writing",
  145. 1008, "/f option requires a filename",
  146. 1009, "target not found in filter file",
  147. 1010, "/w option requires a directory name",
  148. 1011, "/n option requires number of sections",
  149. 1101, "",
  150. 1110, "syntax error",
  151. 1111, "syntax error: ENDIF unexpected",
  152. 1112, "syntax error: IF unexpected",
  153. 1113, "syntax error: END_MAP unexpected",
  154. 1114, "syntax error: BEGIN_*_MAP unexpected",
  155. 1115, "syntax error: INCLUDE unexpected",
  156. 1116, "syntax error: unknown operator in expression",
  157. 1117, "syntax error: \")\" missing in macro invocation",
  158. 1118, "syntax error: incomplete description line",
  159. 1119, "syntax error: unknown mapping type",
  160. 1120, "syntax error: unmatched IF",
  161. 1121, "syntax error: unmatched BEGIN_*_MAP",
  162. 1210, "file format error: target not found",
  163. 1211, "file format error: target not listed in \"all\"",
  164. 1212, "file format error: no description blocks found for files",
  165. 1213, "file format error: \"sysgen\" target not found",
  166. 1214, "file format error: filename with special characters",
  167. 1215, "file format error: Similar target found",
  168. 1910, "unknown language",
  169. 1911, "missing or duplicated entry for language",
  170. 1912, "incomplete entry for language",
  171. 1913, "unknown class for language",
  172. 2011, "no binary found for token",
  173. 2012, "duplicated tokens",
  174. 2013, "unknown resource type: ",
  175. 2014, "unexpected token for already localized binary",
  176. 2015, "input bingen token not found for multi",
  177. 2016, "no bingen token found for custom resource",
  178. 2017, "unknown bingen token extension in token filename",
  179. 2018, "both bingen and rsrc tokens found",
  180. 2019, "custom resource found for rsrc token",
  181. 2020, "custom resource with no token",
  182. 2051, "sysfile error: undefined source path to verify",
  183. 2052, "folder not covered in sysfiles",
  184. 2053, "not mapped",
  185. 2054, "not mapped",
  186. 2101, "binary not found",
  187. 2102, "token not found",
  188. 3011, "internal error: unknown operation type",
  189. 4001, "filename with whitespace badly handled",
  190. 5001, "_COMPDIR currently only supported in clean build",
  191. 5002, "Incremental run with COMPDIR",
  192. 6001, "dir not exist",
  193. 6002, "file not covered",
  194. ); # MAP_ERR
  195. # Logical operations understood by the map file parser.
  196. my $LOGICALOPS = { "||" => ["1", "1"] ,
  197. "&&" => ["0", "0"]
  198. }; # LOGICALOPS
  199. my $DEBUG_PARSER = 0;
  200. my $ANSW = {0=>"FALSE", 1 =>"TRUE"} ;
  201. # MAP_BINGEN_TOK_EXT: Bingen token extensions SYSGEN recognizes
  202. $MAP_BINGEN_TOK_EXT = {};
  203. # For the extensions not defined in %$MAP_BINGEN_TOK_EXT, we can apply compdir as well.
  204. %MAP_COMPDIR_EXT = (
  205. ); # MAP_COMPDIR_EXT
  206. # empty
  207. # MAP_RSRC_TOK_EXT: Rsrc token extensions SYSGEN recognizes
  208. %MAP_RSRC_TOK_EXT =(
  209. '.rsrc', '',
  210. ); # MAP_RSRC_TOK_EXT
  211. # Exclude GLOB
  212. my %EXCLUDE_EXTENSION = ();
  213. # Exclude path
  214. my $EXCLUDE_DIRECTORIES={};
  215. # Debug flags
  216. my $DEBUG=0;
  217. # Global Constants
  218. my $SETSYMPATHS = 1;
  219. my $PARSESYMBOLCD = undef;
  220. my $SYNTAX_CHECK_ONLY = undef;
  221. my $FULLCHECK = undef;
  222. my $DLINE = "TARGET: DEPEND";
  223. my %RevEnvVars = ();
  224. my @RevEnvVars = ();
  225. # CODESFNAME - the name of the file containing the language codes for all languages
  226. # SYSGEN looks for this file in %NT_BLDTOOLS%
  227. $CODESFNAME = "codes.txt";
  228. # MAPPINGS: Mapping types SYSGEN recognizes
  229. @MAPPINGS = qw(FILES BINDIRS TOKENS TOKDIRS EXTENSIONS);
  230. %MAPPING_INDEX = map { $MAPPINGS[$_] => $_+1 } 0..$#MAPPINGS;
  231. # Sub mappings:
  232. my $LOCOP_BASED = { "-" => \&GenXcopy ,
  233. "1" => \&GenXcopy ,
  234. "2" => \&GenLocCmd
  235. };
  236. my $EXT_BASED = {"\\\.rsrc\\b" => \&GenRsrcCmd,
  237. "\\\.lcx\\b" => \&GenLcxCmd,
  238. "\\\.\\w\\w_\\b" => \&GenBgnCmd
  239. };
  240. # FFILES, FBINDIRS, FTOKENS, FTOKDIRS: The corresponding list of fields
  241. @FFILES = ( "DestPath", "DestFile", "DestDbg", "SrcPath", "SrcFile", "SrcDbg", "Xcopy", "Langs" );
  242. @FBINDIRS = ( "DestPath", "DestDbg", "SrcPath", "SrcDbg", "Xcopy", "R", "Langs");
  243. @FTOKENS = ( "DestPath", "DestFile", "TokPath", "TokFile", "ITokPath", "LocOp", "BgnSw", "Langs");
  244. @FTOKDIRS = ( "DestPath", "TokPath", "ITokPath", "LocOp", "BgnSw", "Langs");
  245. @FEXTENSIONS = ("TokExt", "BinExt", "Langs");
  246. # Localization Tool Operation Types (append or replace)
  247. %LOCOPS = (
  248. a => '-a',
  249. r => '-r',
  250. i => '-i $opts',
  251. ai => '-i $opts -a',
  252. ri => '-i $opts -r'
  253. );
  254. # Bingen Switches
  255. @BGNSWS = ( "-b", "-c" );
  256. %BGNSWS = ( "-b" => "-b",
  257. "-c" => "-c"
  258. );
  259. # allow for nested IF's;
  260. @IFSTACK = ();
  261. # Global Variables
  262. # CODES - the contents of the CODESFNAME file, change to hash at 04/03/00
  263. %CODES = ();
  264. my %LANGCODES=();
  265. # TFILES, TBINDIRS, TTOKENS, TTOKDIRS: Records for the corresponding mapping (one of @MAPPINGS),
  266. # in the same order as they are found in the mapping files.
  267. # One record is a hash with the keys stored by @F<mapping_type>
  268. @TFILES = ();
  269. @TEXTENSIONS = ();
  270. @TBINDIRS = ();
  271. @TTOKENS = ();
  272. @TTOKDIRS = ();
  273. # BUG BUG %EXCLUDE_DIRECTORIES and %EXCLUDE_SUBDIR
  274. # seem to carry out same mission.
  275. my %EXCLUDE_SUBDIR = ();
  276. %VERIFYPATH = (
  277. EXCLUDE_ROOT => 'DIR_PATHS',
  278. EXCLUDE_SUBDIR => 'IGNORE_PATHS',
  279. EXCLUDE_FILE_PATTERN => '(\.pdb)|(\.edb)|(\.dbg)|(slm\.ini)|(symbols)'
  280. );
  281. # REPOSITORY: Info necessary to generate the appropriate xcopy or bingen command,
  282. # for every file to be aggregated.
  283. $REPOSITORY = {}; # As a db table, the key is (DestPath, DestFile).
  284. # As a Perl hash:
  285. # key = $DestPath (lowercase)
  286. # value = hash with
  287. # key = $DestFile (lowercase)
  288. # value = hash with
  289. # keys = DestPath, DestFile, DestDbg,
  290. # SrcPath, SrcFile, SrcDbg,
  291. # TokPath, TokFile, ITokPath,
  292. # OpType, LocOp, BgnSw,
  293. # Cmd
  294. #
  295. # not necessarily all of them defined.
  296. my @REPOSITORY_TEMPLATE = qw( SrcFile DestFile SrcDbg DestDbg SrcPath DestPath LocOp BgnSw);
  297. # CMD_REPOSITORY: Repository as found in an existing SYSGEN-generated makefile.
  298. # For every key, only {Cmd} field is filled in.
  299. $CMD_REPOSITORY = {};
  300. # SYSCMD: Stores the contents of the syscmd (SYSGEN-generated NMAKE command file)
  301. @SYSCMD= ();
  302. # MACROS: Hash storing the names and values of the macros.
  303. # Types of macros sysgen recognizes:
  304. # - command line macros (type 1)= macros defined in the command line.
  305. # Their value can be overwritten only by another command line macro.
  306. # - sysfile macros (type 0) = macros defined by the sysfiles,
  307. # inside or outside the pair (BEGIN_*_MAP, END_MAP) (type 0).
  308. # The sysfile macros do not overwrite the command line macros, but they overwrite the environment
  309. # macros.
  310. # - environment macros (type 0 too) = variables inherited from the cmd environment.
  311. # Can be overwritten by the other types of macros.
  312. # Internally, MACROS uppercases all the macro names, so the macronames are case insesitive.
  313. $MACROS = {};
  314. # FILTER
  315. # Stores the list of files to be considered.
  316. %FILTER = ();
  317. # FILE: Filename of the current description file; LINE: the current line number
  318. $FILE = "";
  319. $LINE = 0;
  320. # ERRORS: non fatal errors found during running SYSGEN
  321. $ERRORS = {};
  322. # MAKEFILE: name of the nmake makefile to generate
  323. # By default, MAKEFILE is set to "makefile" if not overridden on the command line,
  324. # /W option
  325. $MAKEFILE = "sysgen.mak";
  326. $SYSCMD = "syscmd";
  327. # SYSFILE: names of the starting description files
  328. # By default, SYSFILE is set to ".\\sysfile" if not specified otherwise in the command line,
  329. # /F option
  330. $SYSFILE = "sysfile";
  331. # HOTFIX: the rules missed by sysgen due to its one-to-one mapping
  332. $HOTFIX = "hotfix";
  333. # Error and Log Filenames
  334. $LOGFILE = "sysgen.log";
  335. $ERRFILE = "sysgen.err";
  336. # Temporary list file(s) for compdir execution are stored here.
  337. # In the case more than one file to be generated, they will have the names compdir.NNN.mak
  338. # where NNN will be the number
  339. $ACCELERATE = undef;
  340. $COMPDIR_FILE_TEMPLATE = "compdir.mak";
  341. # %COMPDIR_COMMAND block will replace all _nttree => _ntpostbld 'plain' copy operations.
  342. %COMPDIR = ();
  343. %COMPDIR_COMMAND = ();
  344. %REATTR_COMMAND = ();
  345. # The attrib commands group must go after the compdir command group
  346. #
  347. my $SRCBASE = ($VERSION=~/^3\./) ? "" : $ENV{"_NTTREE"};
  348. my $DSTBASE = ($VERSION=~/^3\./) ? "" : $ENV{"_NTPOSTBLD"};
  349. # The directory where the $MAKEFILE to be written
  350. $WORKDIR = undef;
  351. # The directory where the $SYSFILE resides
  352. $SYSDIR = undef;
  353. # TARGETS: targets specified in the command line
  354. $TARGETS = {}; # key = $DestFile (lowercase string))
  355. # value = hash with
  356. # keys = OldDestPath (array), NewDestPath (array)
  357. %MAKEDIR=();
  358. %HOTFIX = ();
  359. # CLEAN : 1 if a clean SYSGEN is invoked ( by specifying /c in the command line )
  360. $CLEAN = 0;
  361. # SECTIONS
  362. ($SECTIONNAME, $DEFAULT_SECTIONS, $SECTIONS)=("PROCESS", 16, 16);
  363. # main {
  364. # Flush output buffer for remote.exe
  365. select(STDOUT); $| = 1;
  366. my $start_time = time();
  367. # Parse the command line
  368. my $SEPARATOR = int(rand time);
  369. $SEPARATOR =~ s/\d{2}/chr($&)/eg;
  370. $SEPARATOR =~ s/\W/_/g;
  371. my $ARGL = join($SEPARATOR, @ARGV);
  372. &ParseArgs::parseargs(
  373. 'y' => \$VERIFY,
  374. 'c' => \$CLEAN,
  375. 'a' => \$ACCELERATE,
  376. 'w:' => \$WORKDIR,
  377. 'f:' => \$SYSFILES[0],
  378. 'n:' => \$SECTIONS,
  379. 's' => \$SYNTAX_CHECK_ONLY,
  380. 'v' => \&printVersion,
  381. 'h' => \&PrintHelp,
  382. '?' => \&PrintHelp,
  383. 'old' => \&UseOldParser,
  384. );
  385. $LANG = uc($ENV{"LANG"});
  386. &SetMacro("LANGUAGE", $LANG, 0);
  387. &SetMacro("VERIFY", $VERIFY, 0);
  388. &SetMacro("DEBUG", $ENV{"DEBUG"}, 0);
  389. $DEBUG= &GetMacro("DEBUG");
  390. @SYSFILES = grep {/\S/} @SYSFILES;
  391. if ($DEBUG) {
  392. my $sf = scalar(@SYSFILES);
  393. print STDERR "\n\n", join("\n", (
  394. "\$LANG=$LANG",
  395. "\$ACCELERATE=$ACCELERATE",
  396. "\$CLEAN=$CLEAN",
  397. "\$SYNTAX_CHECK_ONLY=$SYNTAX_CHECK_ONLY",
  398. "\$WORKDIR=$WORKDIR",
  399. "\$SYSFILES[0]=\"$SYSFILES[0]\"",
  400. "\@SYSFILES=$sf"
  401. )),
  402. "\n\n";
  403. }
  404. &SysgenLogMsg($SELF, 1);
  405. &FatalError( 5001, "Imcompatible command line flags" ) if $ACCELERATE and !$CLEAN;
  406. $ACCELERATE and &SysgenLogMsg("Accelerated aggregation..." , 1);
  407. # Verify $WORKDIR's existence or create it
  408. -d $WORKDIR or qx("md $WORKDIR") unless $WORKDIR !~/\S/;
  409. # Verify SYSFILE's existence
  410. $SYSFILE = $SYSFILES[0] unless !scalar(@SYSFILES);
  411. ( -e $SYSFILE ) || &FatalError( 1001, $SYSFILE );
  412. $FULLCHECK = &GetMacro("FULLCHECK") unless $FULLCHECK;
  413. ( -e $MAKEFILE ) || &FatalError( 1003, $MAKEFILE ) unless $CLEAN or $SYNTAX_CHECK_ONLY;
  414. # $HOTFIX and $SYSFILE are in $SYSDIR
  415. $SYSDIR = &GetSysDir($SYSFILE);
  416. &SetMacro("SYSDIR" , uc($SYSDIR), 0);
  417. $HOTFIX = &SwitchWorkDir($HOTFIX, $SYSDIR);
  418. # $MAKEFILE, $LOGFILE, $ERRFILE, $SYSCMD - all created in $WORKDIR
  419. &SetMacro( "WORKDIR", $WORKDIR, 0 );
  420. $MAKEFILE = &SwitchWorkDir($MAKEFILE, $WORKDIR);
  421. $LOGFILE = &SwitchWorkDir($LOGFILE, $WORKDIR);
  422. $ERRFILE = &SwitchWorkDir($ERRFILE, $WORKDIR);
  423. $SYSCMD = &SwitchWorkDir($SYSCMD, $WORKDIR);
  424. # Clean log and err files.
  425. &CleanLogFiles( $WORKDIR );
  426. # Load the inherited environment variables
  427. &LoadEnv();
  428. &MakeRevEnvVars();
  429. # sysgen assumption about the _NTPOSTBUILD
  430. # this code needs to go to the sub LoadEnv body...
  431. &LoadHotFixFile($HOTFIX);
  432. # Load codes.txt file
  433. &LoadCodes();
  434. $DEBUG and
  435. print STDERR join("\n", "EXCLUDE_SUBDIR:" ,
  436. sort(split( /\s+/, &RevEnvVars(
  437. &GetMacro(
  438. $VERIFYPATH{"EXCLUDE_SUBDIR"}))))), "\n\n";
  439. # For incremental, load Repository-associated
  440. # commands from an existing makefile to CMD_REPOSITORY.
  441. &LoadReposit( $MAKEFILE );
  442. &MakeRevEnvVars();
  443. # Read the description files
  444. &ReadSysfile( $SYSFILE );
  445. map {$MAP_BINGEN_TOK_EXT->{$_->{"TokExt"}} = $_->{"BinExt"}} @TEXTENSIONS;
  446. @MAPPINGS = grep {!/EXTENSIONS/} @MAPPINGS;
  447. $DEBUG and
  448. print STDERR join("\n", map {"\"$_\""} keys %$MAP_BINGEN_TOK_EXT), "\n\n";
  449. &MakeRevEnvVars();
  450. # Here we can expand the variable defining the %MAP_COMPDIR_EXT,
  451. # to update the file type list.
  452. foreach my $CompdirExtension (split(/\s*;\s*/, &GetMacro("COMPDIR_EXTENSION"))){
  453. $CompdirExtension=~ s+\*\.+\.+g;
  454. $MAP_COMPDIR_EXT{lc($CompdirExtension)} = 1;
  455. }
  456. $DEBUG and
  457. print STDERR "compdir extension\(s\):\n",
  458. join( "\n", keys(%MAP_COMPDIR_EXT)),
  459. "\n\n";
  460. # Verify the whole tree covered.
  461. &VerifyCover();
  462. # Populate the repository (according to the description files)
  463. &PopulateReposit();
  464. $SYNTAX_CHECK_ONLY or &GenNmakeFiles( $MAKEFILE, $SYSCMD );
  465. # Display errors
  466. &SumErrors();
  467. # } # main
  468. # ReadSysfile
  469. # Reads a given sysfile and all the included sysfiles;
  470. # For each mapping type found, loads data into the corresponding array of hashes
  471. # ( one of the "@T" variables )
  472. # Input
  473. # mapping filename
  474. # Output
  475. # none
  476. # Usage
  477. # &ReadSysfile( "sysgen.def" );
  478. sub ReadSysfile {
  479. my $mapfname = shift;
  480. # File contents
  481. my @mapfile = ();
  482. my $RULES = [];
  483. my $ifstack = scalar (@IFSTACK) ;
  484. # Mapping type ( one of @MAPPINGS )
  485. my $maptype;
  486. # Included filename
  487. my $incfile;
  488. # Flags
  489. my ( $if_flag, $inside_if, $if_line ) = (1, 0, undef);
  490. # Indexes
  491. my $i;
  492. # Error/Message text
  493. my $error;
  494. my $message;
  495. # Open the mapping file
  496. ( -e $mapfname ) || &FatalError( 1002, $mapfname );
  497. &SysgenLogMsg("Loading $mapfname..." , 1);
  498. if ($PARSESYMBOLCD && !(%default) && &GetMacro("SYMBOLCD")){
  499. %default = &parseSymbolCD(&GetMacro("SYMBOLCD"));
  500. $DEBUG and map {print STDERR $_ , "\n"} values(%default);
  501. }
  502. # Load file contents
  503. open( FILE, $mapfname ) || &FatalError( 1006, $mapfname );
  504. @mapfile = <FILE>;
  505. close( FILE );
  506. # Parse the mapping file and load the mappings.
  507. for ( $i=0, $LINE=0, $FILE=$mapfname; $i < @mapfile; $i++, $LINE++ ) {
  508. for ( $mapfile[$i] ) {
  509. SWITCH: {
  510. # white lines
  511. if ( ! /\S/ ) { last SWITCH; }
  512. # commented lines
  513. if ( /^\s*#/ ) { last SWITCH; }
  514. # ENDIF
  515. if ( /\bENDIF\b/i ) {
  516. $DEBUG and print STDERR scalar(@IFSTACK), "\n";
  517. $ifstack < scalar(@IFSTACK) or &FatalError( 1111, $FILE, $LINE );
  518. pop @IFSTACK;
  519. $inside_if = scalar(@IFSTACK) - $ifstack;
  520. $if_flag = ($inside_if) ? $IFSTACK[-1] : 1;
  521. last SWITCH;
  522. } # case
  523. # IF
  524. if ( /\bIF\b/i) {
  525. push @IFSTACK, &SetIfFlag( $_);
  526. $if_flag and
  527. $if_flag = $IFSTACK[-1];
  528. $inside_if = 1;
  529. $if_line = $i;
  530. last SWITCH;
  531. } # case
  532. if ( ! $if_flag ) { last SWITCH; }
  533. # INCLUDE
  534. if ( /\bINCLUDE\b\s+(\S+)/i ) {
  535. # Evaluate the macros of the include statement
  536. $incfile = &ReplaceVar( $1, 0 );
  537. # Read the included file
  538. &ReadSysfile(&SwitchWorkDir($incfile, $SYSDIR));
  539. # Continue with the current file; set FILE and LINE back
  540. $FILE = $mapfname;
  541. $LINE = $i;
  542. last SWITCH;
  543. } # case
  544. # SET
  545. if ( /\bSET\b/i ) {
  546. my $fields = [&SetAttribOp($_)];
  547. $LANG = $fields->[1] if $fields->[0] =~ /LANGUAGE/i;
  548. &SetMacro( &SetAttribOp( $_ ), 0 );
  549. last SWITCH;
  550. } # case
  551. # MESSAGE
  552. if ( /\bMESSAGE\b\s+(\S.*)$/i) {
  553. $message = &ReplaceVar( $1, 0 );
  554. &Error( 1101, $message );
  555. last SWITCH;
  556. } # case
  557. # ERROR
  558. if ( /\bERROR\b\s+(\S.*)$/i) {
  559. $error = &ReplaceVar( $1, 0 );
  560. &FatalError( 1101, $error );
  561. last SWITCH;
  562. } # case
  563. # END RULES
  564. if ( /END_RULES/i ) {
  565. &LoadRuleBlock($RULES);
  566. $#$RULES = -1;
  567. last SWITCH;
  568. } # case
  569. # BEGIN RULES
  570. if ( /\bBEGIN_RULES/i ) {
  571. # Read the found map.
  572. &SysgenLogMsg("Loading RULES from $mapfname...", 1);
  573. # Read the RULES from the next line and until END_RULES
  574. $i = &ReadRules( \@mapfile, $i+1, $RULES);
  575. last SWITCH;
  576. } # case
  577. # END_MAP
  578. if ( /END_MAP/i ) {
  579. &FatalError( 1113, $FILE, $LINE );
  580. last SWITCH;
  581. } # case
  582. # BEGIN_MAP
  583. if ( /\bBEGIN_(\S+)_MAP/i ) {
  584. $maptype = $MAPPING_INDEX{$1};
  585. ( $maptype ) || &FatalError( 1119, $FILE, $LINE );
  586. # Read the found map.
  587. &SysgenLogMsg("Loading $MAPPINGS[$maptype-1] map from $mapfname...", 1);
  588. # Read the map and return the last line index
  589. $i = &ReadBlock( \@mapfile, $i+1, $maptype-1 );
  590. last SWITCH;
  591. } # case
  592. # default
  593. &FatalError( 1110, $FILE, $LINE );
  594. } #SWITCH
  595. } #for
  596. } # for
  597. &FatalError( 1120, $FILE, $if_line ) if scalar(@IFSTACK) != $ifstack;
  598. return;
  599. } # ReadSysfile
  600. # ReadRules
  601. # Reads a RULES block away from the mapping file
  602. # The lines array is returned to the caller via array ref argument
  603. # Input
  604. # sysfile contents (reference)
  605. # index of the current line
  606. # 'rules' array ref
  607. # Output
  608. # index of the END_RULES line
  609. # Usage
  610. sub ReadRules{
  611. my($mapref, $index, $ruleref) = @_;
  612. my $line;
  613. for ( ; $index < @{$mapref}; $index++, $LINE++ ) {
  614. my $line = $mapref->[$index];
  615. if ( $line=~/END_RULES/i ) {
  616. return $index-1;
  617. } # case
  618. push @$ruleref, $line;
  619. }
  620. }
  621. # ReadBlock
  622. # Reads a map from the mapping file, according to its type.
  623. # Loads data into the corresponding "table" variable ( one of the "@T" variables)
  624. # Input
  625. # sysfile contents (reference)
  626. # index of the current line
  627. # mapping type
  628. # Output
  629. # index of the END_MAP line
  630. # Usage
  631. # $index = &ReadBlock( \@mapfile, $index, $maptype );
  632. sub ReadBlock {
  633. my($mapref, $index, $maptype) = @_;
  634. my $ifstack = scalar(@IFSTACK);
  635. # Output - $index
  636. # Other indexes
  637. my $line;
  638. # IF flags
  639. my( $if_flag, $inside_if, $if_line ) = (1, 0, undef);
  640. # Error/Message text
  641. my $error;
  642. my $message;
  643. # Parse the current mapping type
  644. for ( ; $index < @{$mapref}; $index++, $LINE++ ) {
  645. for ( $mapref->[$index] ) {
  646. SWITCH: {
  647. # white lines
  648. if ( ! /\S/ ) { last SWITCH; }
  649. # commented lines
  650. if ( /^\s*#/ ) { last SWITCH; }
  651. # ENDIF
  652. if ( /\bENDIF\b/i) {
  653. scalar(@IFSTACK) or &FatalError( 1111, $FILE, $LINE );
  654. pop @IFSTACK;
  655. $inside_if = scalar(@IFSTACK);
  656. $if_flag = ($inside_if) ? $IFSTACK[-1] : 1;
  657. last SWITCH;
  658. } # case
  659. if ( /\bIF\b/i) {
  660. push @IFSTACK, &SetIfFlag( $_);
  661. $if_flag and
  662. $if_flag = $IFSTACK[-1];
  663. $inside_if = 1;
  664. $if_line = $index;
  665. last SWITCH;
  666. } # case
  667. if ( ! $if_flag ) { last SWITCH; }
  668. # INCLUDE
  669. if ( /\bINCLUDE\b\s+(\S+)/i ) {
  670. &FatalError( 1115, $FILE, $LINE );
  671. last SWITCH;
  672. } # case
  673. # SET
  674. if ( /\bSET\b/i ) {
  675. my $fields = [&SetAttribOp($_)];
  676. $LANG = $fields->[1] if $fields->[0] =~ /LANGUAGE/i;
  677. &SetMacro( &SetAttribOp( $_ ), 0 );
  678. last SWITCH;
  679. } # case
  680. # MESSAGE
  681. if ( /\bMESSAGE\b\s+(\S.*)$/i) {
  682. $message = &ReplaceVar( $1, 0 );
  683. &Error( 1101, $message );
  684. last SWITCH;
  685. } # case
  686. # ERROR
  687. if ( /\bERROR\b\s+(\S.*)$/i) {
  688. $error = &ReplaceVar( $1, 0 );
  689. &FatalError( 1101, $error );
  690. last SWITCH;
  691. } # case
  692. # END_MAP
  693. if ( /END_MAP/i ) {
  694. ( $ifstack!= scalar(@IFSTACK) ) and &FatalError( 1120, $FILE, $if_line );
  695. return $index;
  696. } # case
  697. # BEGIN_MAP
  698. if ( /BEGIN_\S+_MAP/i ) {
  699. &FatalError( 1114, $FILE, $LINE );
  700. last SWITCH;
  701. } # case
  702. # default
  703. &ReadLine( $mapref, $index, $maptype );
  704. } # SWITCH
  705. } # for
  706. } # for
  707. print &FatalError( 1121, $FILE, $LINE );
  708. return -1;
  709. } # ReadBlock
  710. # SetIfFlag
  711. # Evaluates the given IF expression.
  712. # Input
  713. # IF statement
  714. # Output
  715. # 1 if the IF expression is satisfied, 0 otherwise
  716. sub SetIfFlag {
  717. my $initialToken = &ReplaceVar( shift, 0 );
  718. # Output
  719. my $LogicOp = "";
  720. my $TokenResult = 0;
  721. my @subTokenResultSet = ();
  722. my @parsedTokenlist = ();
  723. # Identify the operands and the operator, then evaluate the expression.
  724. # 1 . Chop away the IF
  725. $initialToken =~ s/^\s*\bIF\b\s+//gi;
  726. # Process AND/OR stuff
  727. # NOTE: one level, not too rigorous design! :-)
  728. if ($initialToken =~ /\|\||\&\&/){
  729. foreach my $knownLogicOp (keys(%$LOGICALOPS)){
  730. if ($initialToken =~ /\Q$knownLogicOp\E/){
  731. my $simpleTokenList = &StrToArray($initialToken, $knownLogicOp) ;
  732. push @parsedTokenlist, @$simpleTokenList;
  733. $LogicOp = $knownLogicOp;
  734. }
  735. }
  736. }
  737. else{
  738. push @parsedTokenlist, $initialToken;
  739. }
  740. # print STDERR join ("\n", @parsedTokenlist), "\n";
  741. foreach my $initialToken (@parsedTokenlist){
  742. my $TokenResult = 0;
  743. # print STDERR "\n\"",$initialToken,"\"\n";
  744. # 3 . Load arguments and operands
  745. my ($operand1, $operator, $operand2, @unexpected) = split(" ", $initialToken);
  746. &FatalError( 1110, $FILE, $LINE ) if @unexpected;
  747. SWITCH: {
  748. if ( $operator eq "==" ) {
  749. if ( $operand1 eq $operand2 ) {
  750. $TokenResult = 1;
  751. }
  752. last SWITCH;
  753. } # if
  754. if ( $operator eq "!=" ) {
  755. if ( $operand1 ne $operand2 ) {
  756. $TokenResult = 1;
  757. }
  758. last SWITCH;
  759. } # if
  760. &FatalError( 1116, $FILE, $LINE );
  761. } # SWITCH
  762. # print STDERR "\n\"",$TokenResult,"\"\n";
  763. push @subTokenResultSet, $TokenResult;
  764. }
  765. my $ExprResult = join ("", @subTokenResultSet);
  766. $TokenResult = 0;
  767. $TokenResult = 1 if ( (!$LogicOp && $ExprResult =~ /1/)
  768. ||
  769. ($LogicOp && (
  770. ($LOGICALOPS->{$LogicOp}->[1] &&
  771. $ExprResult =~ /$LOGICALOPS->{$LogicOp}->[0]/i)
  772. ||
  773. (!($LOGICALOPS->{$LogicOp}->[1]) &&
  774. $ExprResult !~ /$LOGICALOPS->{$LogicOp}->[0]/i)
  775. )
  776. )
  777. );
  778. return $TokenResult;
  779. } # SetIfFlag
  780. # test logic parser is able to interpret.
  781. # used for debug purposes, e.g.
  782. #
  783. # &testCompoundIF("IF CHT == CHT");
  784. # &testCompoundIF("IF CHT == CHS || (CHT == CHT)");
  785. # &testCompoundIF("IF CHT == CHS");
  786. # &testCompoundIF("IF CHT == CHS && (CHT == CHS) ");
  787. # &testCompoundIF("IF CHT == chs");
  788. sub testCompoundIF{
  789. $DEBUG_PARSER and print STDERR join("\t", $_[0],
  790. $ANSW->{&SetIfFlag($_[0])}), "\n";
  791. }
  792. # Expands the combined "IF" condition joined by the 'OR' or 'AND'
  793. # into elementary "IF" conditions.
  794. # e.g. &StrToArray("CHT == CHT \&\& (CHT == CHS)", "\&\&");
  795. # will return the array:
  796. #
  797. # CHT == CHS
  798. # CHT == CHS
  799. #
  800. # which would evaluate to FALSE
  801. sub StrToArray{
  802. # $DEBUG_PARSER and print STDERR $_,"\n";
  803. my ($line, $sep) = @_;
  804. # $DEBUG_PARSER and print STDERR "\"$line\"\n";
  805. my @parsedTokenlist = split(/\s*\Q$sep\E\s*/, $line);
  806. map {s/^\s*\(\s*([^\(][ \"\'\@a-z0-9=!\(\)]+[^\)])\s*\)\s*/$1/ig} @parsedTokenlist;
  807. push @parsedTokenlist, $line unless @parsedTokenlist;
  808. # DEBUG_PARSER and print STDERR join("\n", @parsedTokenlist), "\n";
  809. \@parsedTokenlist;
  810. } # StrToArray
  811. # SetAttribOp
  812. # Evaluates a SET statement.
  813. # Input
  814. # a SET statement
  815. # Output
  816. # evaluated macroname and macrovalue
  817. # Usage
  818. # ($macroname, $macrovalue) = &SetAttribOp( $mapfile[$index] ) ;
  819. sub SetAttribOp {
  820. my $entry = &ReplaceVar( shift, 0 );
  821. $entry =~ s/^\s*\bSET\b\s+//gi;
  822. # Output
  823. my ($varname, $value, @unexpected) = split(/\s*=\s*\;*/, $entry);
  824. # Set variable's name and variable's value
  825. @unexpected and &FatalError( 1110, $FILE, $LINE );
  826. # Verify if a variable name is defined
  827. if ( $varname eq "" ) {
  828. return ("", "");
  829. } # if
  830. ($varname, $value);
  831. } # SetAttribOp
  832. # ReadLine
  833. # Description
  834. # Reads one description line, evaluates it and store the records it generates
  835. # Input
  836. # sysfile contents ( reference )
  837. # line number
  838. # Output
  839. # none
  840. # Usage
  841. # &ReadLine( $mapref, $index, $maptype );
  842. # Implementation
  843. sub ReadLine {
  844. my( $mapref, $index, $maptype ) = @_;
  845. # Replace variables and spawn new records
  846. map {&LoadRecord($maptype, $_) } &ReplaceVar($mapref->[$index],1);
  847. $index;
  848. } # ReadLine
  849. # LoadRecord
  850. # Description
  851. # Transforms an evaluated description line into a record,
  852. # and adds it to the appropriate table.
  853. # Input
  854. # mapping type
  855. # description line (string)
  856. # Output
  857. # <none>
  858. # Usage
  859. # &LoadRecord( $maptype, $line_contents );
  860. sub LoadRecord {
  861. my($maptype, $entry) = @_;
  862. # The hash table storring the current line (record).
  863. # The keys (field names) are given by the corresponding "@F$maptype" array.
  864. my %record;
  865. %record = &LineToRecord( $maptype, $entry );
  866. # If the current language matches the record language,
  867. # add the record to the corresponding "@T$maptype" array.
  868. if (&cklang::CkLang($LANG, $record{Langs})) {
  869. &AddRecord( $maptype, %record );
  870. } elsif ((!defined $record{SrcFile}) && (defined $record{SrcPath})) {
  871. &SysgenLogMsg("Ignore $record{SrcPath}",0);
  872. &SetMacro(
  873. $VERIFYPATH{'EXCLUDE_SUBDIR'},
  874. &GetMacro($VERIFYPATH{'EXCLUDE_SUBDIR'}) . "; " .
  875. $record{SrcPath},
  876. 0
  877. );
  878. }# if
  879. return;
  880. } # LoadRecord
  881. # LineToRecord
  882. # Transforms a given string into a record (hash table).
  883. # Input
  884. # mapping type
  885. # description line contents
  886. # Output
  887. # record of the mapping type
  888. # Usage
  889. # %record = &LineToRecord( $maptype, $entry );
  890. sub LineToRecord {
  891. my($maptype, $entry) = @_;
  892. # Output
  893. my %record;
  894. # Mapping name
  895. my $mapname;
  896. # Corresponding "@F$maptype" variable (reference)
  897. my $ftable;
  898. # Current line split in fields
  899. my @fields;
  900. # Storage for the current line
  901. my $line = $entry;
  902. # Indexes
  903. my($i, $j);
  904. # Get the mapping name.
  905. $mapname = "F$MAPPINGS[$maptype]";
  906. $ftable = \@$mapname;
  907. # Split the record into fields
  908. @fields = split " ", $entry;
  909. # Fill in %record variable with the field names as keys and
  910. # the field values as values.
  911. for ( $i=0; $i< @$ftable; $i++ ) {
  912. # All fields must be non-null
  913. if ( !$fields[$i] ) {
  914. &FatalError( 1118, $FILE, $LINE );
  915. } # if
  916. $record{$ftable->[$i]} = $fields[$i];
  917. } # for
  918. return %record;
  919. } # LineToRecord
  920. # AddRecord
  921. # Adds the given record to the appropriate "@T" table.
  922. # Input
  923. # mapping type
  924. # record
  925. # Output
  926. # <none>
  927. sub AddRecord {
  928. my($maptype, %record) = @_;
  929. # Table name ( name of one of the "@T" variables )
  930. my $tname;
  931. # Table (reference)
  932. my $table;
  933. # Storage for the current record
  934. my %buffer;
  935. # Subdirectories ( if "R" field defined )
  936. my @subdirs;
  937. # Source field name (one of SrcPath, TokPath )
  938. my $fname;
  939. # Indexes
  940. my $i;
  941. # Set table
  942. $tname = "T$MAPPINGS[$maptype]";
  943. $table = \@$tname;
  944. # Set the field name for the path ( TokPath or SrcPath)
  945. # according to the mapping type
  946. # Set the field name for the path ( TokPath or SrcPath)
  947. # according to the mapping type
  948. # $MAPPINGS[$maptype] => EXTENSIONS
  949. # must be handled separately anyway.
  950. #
  951. $fname = undef;
  952. SWITCH: {
  953. if ( $MAPPINGS[$maptype] =~ /TOK/i ) {
  954. $fname = "TokPath";
  955. last SWITCH;
  956. } # if
  957. if ( $MAPPINGS[$maptype] =~ /BIN/i ) {
  958. $fname = "SrcPath";
  959. last SWITCH;
  960. } # if
  961. if ( $MAPPINGS[$maptype] =~ /FILES/i ) {
  962. $fname = "SrcPath";
  963. last SWITCH;
  964. } # if
  965. # default
  966. # nothing to do
  967. } # SWITCH
  968. # Verify the existence of the source path
  969. if ( defined ($fname) and ! -e $record{$fname} ) {
  970. $VERIFY and
  971. &Error( 6001, &RevEnvVars($record{$fname}));
  972. return;
  973. } # if
  974. # Insert record into the table
  975. push @$table, \%record;
  976. # Write the record to the log file
  977. &LogRecord($maptype, \%record);
  978. !defined ($fname) and return;
  979. # For Bindirs, look for the value of Subdirs
  980. if ( $MAPPINGS[$maptype] ne "BINDIRS" ) {
  981. return;
  982. } # if
  983. # Return if no subdirectories to be parsed (
  984. # BUG: 457873 implied bejavior should not be 'recursive'
  985. if ( $record{R} !~ /y/i ) {
  986. return;
  987. } # if
  988. # Get the list of subdirectories
  989. @subdirs = `dir /ad /b $record{SrcPath}`;
  990. # Add one record for each subdirectory found.
  991. for ( $i=0; $i < @subdirs; $i++ ) {
  992. chomp $subdirs[$i];
  993. # Found that in NT5, the result of dir /b starts with an empty line
  994. if ( $subdirs[$i] eq "" ) {
  995. next;
  996. }
  997. # Skip the "symbols" subdirectory
  998. if ( $subdirs[$i] eq "symbols" ) {
  999. next;
  1000. }
  1001. # Add one record for the current subdirectory
  1002. %buffer = %record;
  1003. $buffer{DestPath} .= "\\$subdirs[$i]";
  1004. $buffer{SrcPath} .= "\\$subdirs[$i]";
  1005. &AddRecord( $maptype, %buffer );
  1006. } # for
  1007. return;
  1008. } # AddRecord
  1009. # LogRecord
  1010. # Writes the record to the log file.
  1011. # Input
  1012. # mapping type (reference)
  1013. # record (reference)
  1014. # Output
  1015. # <none>
  1016. sub LogRecord {
  1017. my( $maptype, $recref ) = @_;
  1018. # String to be written to the logfile
  1019. my $logstr;
  1020. $logstr = "$MAPPINGS[$maptype]:";
  1021. for ( sort keys %{$recref} ) {
  1022. $logstr .= "$_=$recref->{$_} ";
  1023. } # for
  1024. $logstr .= "\n";
  1025. # print "$logstr";
  1026. } # LogRecord
  1027. # ReplaceVar
  1028. # Replaces all the macronames with their value in the given string.
  1029. # Input
  1030. # the string to be evaluated
  1031. # the replacement type:
  1032. # 0 = scalar
  1033. # 1 = array
  1034. # Output
  1035. # array if the replacement type is 1
  1036. # string if the replacement type is 0
  1037. # In both cases, all the macros are evaluated.
  1038. # Usage
  1039. # $entry = &ReplaceVar( $entry, 0 );
  1040. # @records = &ReplaceVar( $entry, 1 );
  1041. sub ReplaceVar {
  1042. my( $line, $type ) = @_;
  1043. $line =~ s/\s{2,}/ /g;
  1044. $line =~ s/^\s//;
  1045. $line =~ s/\s$//;
  1046. my @list = ($line);
  1047. foreach $line (@list){
  1048. my $epyt = 0;
  1049. while ($line =~ /\$\(([^\)]+)\)/){
  1050. my $macro = $1;
  1051. $line =~ s/\$\(\Q$macro\E\)/__MACRO__/g;
  1052. if ($macro =~ /(\w+)\[\s?\]/) {
  1053. $macro = $1;
  1054. $epyt = 1;
  1055. }
  1056. my $thevalue = &GetMacro($macro);
  1057. $thevalue =~ s/\s\s+//g;
  1058. # BUG BUG we can end up chopping all whitespace!
  1059. if (!$epyt){
  1060. $line =~ s/__MACRO__/$thevalue/g;
  1061. }
  1062. else{
  1063. pop @list; # take one, give many
  1064. foreach my $value (split (";" , $thevalue)){
  1065. next if $value !~ /\S/;
  1066. my $this = $line;
  1067. $this =~ s/__MACRO__/$value/g;
  1068. push @list, $this;
  1069. }
  1070. }
  1071. }
  1072. }
  1073. ($type) ? @list : pop @list;
  1074. } # ReplaceVar
  1075. # LoadCodes
  1076. # Loads $CODEFNAME's file contents in %CODES
  1077. # Input & Output <none>
  1078. # Usage
  1079. # &LoadCodes();
  1080. sub LoadCodes {
  1081. # codes.txt filename
  1082. my $codefile = sprintf "%s\\$CODESFNAME", &GetMacro( "RAZZLETOOLPATH" );
  1083. scalar (%LANGCODES) or &ParseTable::parse_table_file($codefile, \%LANGCODES );
  1084. map {&SetMacro("$_", $LANGCODES{$LANG}->{$_}, 0)} keys(%{$LANGCODES{$LANG}});
  1085. foreach my $pLangKey (keys %LANGCODES) {
  1086. # accomplish the task previously solved by &Undefine_Exclude_Lang_Subdir...
  1087. next if $pLangKey eq $LANG;
  1088. $CODES{$pLangKey} = undef;
  1089. $EXCLUDE_SUBDIR{$ENV{"_NTTREE"} . "\\" . $pLangKey . ";" } = undef;
  1090. $EXCLUDE_SUBDIR{$ENV{"_NTTREE"} . "\\" . $LANGCODES{$pLangKey}->{"Lang"} . ";"} = undef;
  1091. next if $LANGCODES{$pLangKey}->{"Class"} eq &GetMacro("CLASS");
  1092. $EXCLUDE_SUBDIR{$ENV{"_NTTREE"} . "\\" . substr($LANGCODES{$pLangKey}->{"Class"},1) . ";"} = undef;
  1093. }
  1094. &SetMacro("OLD_LANG", &GetMacro("LANG"));
  1095. my $Exclude_Subdir = join(" " , keys(%EXCLUDE_SUBDIR));
  1096. &SetMacro($VERIFYPATH{"EXCLUDE_SUBDIR"}, join("; " ,
  1097. &GetMacro($VERIFYPATH{"EXCLUDE_SUBDIR"}) ,
  1098. $Exclude_Subdir),0);
  1099. my $RSRC_LCID = &GetMacro("LCID");
  1100. $RSRC_LCID =~ s/^0x0?//g;
  1101. &SetMacro("RSRC_LCID", "-l ". $RSRC_LCID, 0);
  1102. return;
  1103. } # LoadCodes
  1104. sub VerifyCover {
  1105. my %tree=();
  1106. return if &GetMacro("DIR_PATHS") eq "";
  1107. # Create except tree from @T$Mapping array, then remove the dummy to find all branch into $tree_hashptr
  1108. &Create_Tree_Branch(\%tree);
  1109. # Remove Root and its parents from branch tree
  1110. &Remove_Root_List(\%tree, grep {/\S/} split(/\s*;\s*/, &GetMacro("DIR_PATHS")));
  1111. $VERIFY and VerifyLocDropMapping(\%tree);
  1112. # Find unmapping folders from branch tree, also remove the empty folders and the specified file-pattern files (such as slm.ini).
  1113. &Find_UnMapping(\%tree);
  1114. } # VerifyCover
  1115. sub VerifyLocDropMapping($){
  1116. my $tree = shift;
  1117. my %mappeddir = ();
  1118. my $mappeddir = undef;
  1119. my $dir = undef;
  1120. my @files = ();
  1121. &dbgmsg(
  1122. "DIR_PATHS:\n\n",
  1123. join ("\n", grep {/\S/} split(/\s*;\s*/,
  1124. &RevEnvVars(&GetMacro("DIR_PATHS")))));
  1125. &dbgmsg( "Covered:\n\n",
  1126. join ("\n", grep {/\S/} split(/\s*;\s*/,
  1127. &RevEnvVars(join( "; ", sort (keys(%$tree))))
  1128. )));
  1129. foreach my $maptype (0..$#MAPPINGS){
  1130. my $tname = "T$MAPPINGS[$maptype]";
  1131. my $table = \@$tname;
  1132. my $fname = undef;
  1133. SWITCH: {
  1134. if ( $MAPPINGS[$maptype] =~ /TOK/i ) {
  1135. $fname = "TokPath";
  1136. last SWITCH;
  1137. } # if
  1138. if ( $MAPPINGS[$maptype] =~ /BIN/i ) {
  1139. $fname = "SrcPath";
  1140. last SWITCH;
  1141. } # if
  1142. if ( $MAPPINGS[$maptype] =~ /FILES/i ) {
  1143. $fname = "SrcPath";
  1144. last SWITCH;
  1145. } # if
  1146. # default
  1147. # nothing to do
  1148. } # SWITCH
  1149. @mappeddir {map {$table->[$_]->{$fname}} (0..$#$tname)} = ();
  1150. }
  1151. dbgmsg( "Mapped:\n\n". join "\n", map {&RevEnvVars($_)} (sort(keys(%mappeddir))));
  1152. # now find not mapped directories:
  1153. # start from $DIR_PATHS -> list files -> exclude covered by @mappeddir
  1154. # -> display
  1155. &dbgmsg(
  1156. "IGNORE_PATHS:\n\n",
  1157. join ("\n", grep {/\S/} split(/\s*;\s*/,
  1158. &RevEnvVars(&GetMacro("IGNORE_PATHS")))));
  1159. my @excludedir = grep {/\S/} split(/\s*;\s*/,
  1160. &GetMacro("IGNORE_PATHS"));
  1161. @files = ();
  1162. foreach $dir (sort(grep {/\S/} split(/\s*;\s*/, &GetMacro("DIR_PATHS")))){
  1163. # the DIR_PATHS is not covering the MISC/HELP files!
  1164. &dbgmsg("current $dir");
  1165. push @files, qx("dir /b/s/a-d $dir");
  1166. &dbgmsg("files\n".join "\n", @files );
  1167. foreach $mappeddir (@excludedir){
  1168. # do a simple check here
  1169. # $mappeddir =~ s/^.+\\\.\.\.\\([\w\\]+)/$1/g;
  1170. @files = grep {!/\Q$mappeddir\E\\/oi} @files;
  1171. if ($mappeddir=~/\.\.\./){
  1172. my ($head, $tail) = ($mappeddir =~ /(.+)\\\.\.\.\\(.+)/);
  1173. @files = grep {!/\Q$head\E\\.+\\\Q$tail\E\\/oi} @files;
  1174. dbgmsg("\$head=$head, \$tail=$tail");
  1175. }
  1176. }
  1177. foreach $mappeddir (keys(%mappeddir)){
  1178. &dbgmsg("scan $mappeddir");
  1179. @files = grep {!/\Q$mappeddir\E\\[^\\]+$/oi} @files;
  1180. }
  1181. }
  1182. map {chop} @files;
  1183. map {&Error(6002, &RevEnvVars($_))} @files if ($#files);
  1184. exit;
  1185. } # VerifyLocDropMapping
  1186. # Create_Tree_Branch
  1187. # Create except tree from @T$Mapping array, then remove the dummy to find all branch into $tree_hashptr
  1188. # Input
  1189. # $tree_hashptr - a hash pointer for store the branch tree
  1190. #
  1191. # Output
  1192. # none - the branch tree stored in %$tree_hashptr
  1193. # Usage
  1194. # &Create_Tree_Branch(\%mytree);
  1195. sub Create_Tree_Branch {
  1196. my($tree_hashptr)=@_;
  1197. my(%except)=();
  1198. # Create Exception Tree from @T$Mapping array
  1199. &Get_Except(\%except);
  1200. # Remove the subdir if parent defined, the remains create into hash (parent dir) - hash (current dir)
  1201. &Remove_Dummy_Create_Branch(\%except,$tree_hashptr);
  1202. }
  1203. # Get_Except
  1204. # Create Exception Tree from @T$Mapping array
  1205. # Input
  1206. # $except_hashptr - a hash pointer for store the exception tree
  1207. #
  1208. # Output
  1209. # none - the exception tree stored in %$except_hashptr
  1210. #
  1211. # Usage
  1212. # &Get_Except(\%myexcept);
  1213. sub Get_Except {
  1214. my($except_hashptr)=@_;
  1215. my($tablename, $hashptr)=();
  1216. ## Predefine except directories
  1217. for (split(/\s*;\s*/, &GetMacro($VERIFYPATH{'EXCLUDE_SUBDIR'}))) {
  1218. next if ($_ eq "");
  1219. if (/\.\.\./) {
  1220. map({$except_hashptr->{$_}=1} &Find_Dot_Dot_Dot(lc$_));
  1221. } else {
  1222. $except_hashptr->{lc $_}=1;
  1223. }
  1224. }
  1225. ## According bindir or tokdir to define the sourcepath to %except
  1226. for $tablename (@MAPPINGS) {
  1227. for $hashptr (@{"T$tablename"}) {
  1228. if (exists $hashptr->{'R'}) {
  1229. if ($hashptr->{'R'} eq 'n') {
  1230. $except_hashptr->{ lc$hashptr->{SrcPath} . "\\."} = 1
  1231. } else {
  1232. $except_hashptr->{lc$hashptr->{SrcPath}} = 1
  1233. }
  1234. } elsif (exists $hashptr->{SrcPath}) {
  1235. if (defined $hashptr->{SrcFile}) {
  1236. $except_hashptr->{lc($hashptr->{SrcPath} . "\\" . $hashptr->{SrcFile})} = 1
  1237. } else {
  1238. $except_hashptr->{lc$hashptr->{SrcPath}} = 1
  1239. }
  1240. } elsif (exists $hashptr->{TokPath}) {
  1241. if (defined $hashptr->{TokFile}) {
  1242. $except_hashptr->{lc($hashptr->{TokPath} . "\\" . $hashptr->{TokFile})} = 1
  1243. } else {
  1244. $except_hashptr->{lc$hashptr->{TokPath}} = 1
  1245. }
  1246. } else {&Error( 2051, $hashptr);}
  1247. }
  1248. }
  1249. }
  1250. # Find_Dot_Dot_Dot
  1251. # Collect the combination of the ... folders
  1252. # Input
  1253. # $path - A path contains "..."
  1254. #
  1255. # Output
  1256. # keys %regist - The array of all the combination found in the path
  1257. #
  1258. # Usage
  1259. # &Find_Dot_Dot_Dot("$ENV{_NTTREE}\\...\\symbols.pri");
  1260. sub Find_Dot_Dot_Dot {
  1261. my ($path) = @_;
  1262. my ($mymatch, $file, %regist);
  1263. my @piece=split(/\\\.{3}/, $path);
  1264. for $file ( `dir /s/b/l $piece[0]$piece[-1] 2>nul`) {
  1265. chomp $file;
  1266. $mymatch = &Match_Subjects($file, \@piece);
  1267. next if (!defined $mymatch);
  1268. $regist{$mymatch}=1;
  1269. }
  1270. return keys %regist;
  1271. }
  1272. # Match_Subjects
  1273. # The subroutine called by Find_Dot_Dot_Dot, which perform the match for
  1274. # all the pieces of the infomation of the path with the file. For example
  1275. #
  1276. # Input
  1277. # $file - The fullpath filename
  1278. # $pieceptr - The array ptr
  1279. #
  1280. # Output
  1281. # undef if not match
  1282. # $match - store the path it matched
  1283. #
  1284. # Usage
  1285. # &Match_Subjects(
  1286. # "D:\\ntb.binaries.x86fre\\dx8\\symbols\\retail\\dll\\migrate.pdb",
  1287. # \@{"D:\\ntb.binaries.x86fre", "symbols\\", "dll\\"} );
  1288. # Return => "D:\\ntb.binaries.x86fre\\dx8\\symbols\\retail\\dll"
  1289. #
  1290. sub Match_Subjects {
  1291. my ($file, $pieceptr)=@_;
  1292. my $match;
  1293. # my $DEBUG = 1;
  1294. for (@$pieceptr) {
  1295. return if ($file!~/\Q$_\E/);
  1296. $match .= $` . $&;
  1297. $file = $';
  1298. }
  1299. # $DEBUG and print STDERR "$match\n";
  1300. return $match;
  1301. }
  1302. # Remove_Dummy_Create_Branch
  1303. # Remove the subdir if parent defined, the remains create into hash (parent dir) - hash (current dir)
  1304. # Input
  1305. # $except_hashptr - stored the exception hash from %T$Mapping
  1306. # $tree_hashptr - will store the covered tree in hash - hash
  1307. #
  1308. # Output
  1309. # none - the covered tree stored in %$tree_hashptr
  1310. #
  1311. # Usage
  1312. # &Remove_Dummy_Create_Branch(\%myexcept, \%mybranch);
  1313. sub Remove_Dummy_Create_Branch {
  1314. my($except_hashptr,$tree_hashptr)=@_;
  1315. next1: for (keys %$except_hashptr) {
  1316. # loop all its parent
  1317. while(/\\+[^\\]+/g) {
  1318. # print STDERR join ("\t",($_, $`, $1)), "\n";
  1319. # Is this folder covered by its parent?
  1320. if (exists $except_hashptr->{$`}) {
  1321. # Remove current folder
  1322. delete $except_hashptr->{$_};
  1323. next next1;
  1324. } else {
  1325. # define the $tree{$parent}->{$child}=1
  1326. $tree_hashptr->{$`}->{$&}=1;
  1327. }
  1328. }
  1329. }
  1330. }
  1331. # Remove_Root_List
  1332. # Remove Root and its parents from branch tree
  1333. # Input
  1334. # $tree_hashptr - a hash pointer, %$tree_hashptr is the branch tree
  1335. # @root_list_ptr - a array for the root list
  1336. #
  1337. # Output
  1338. # none - the root and its parent will be remove from branch tree (%$tree_hashptr)
  1339. #
  1340. # Usage
  1341. # &VerifyCover();
  1342. sub Remove_Root_List {
  1343. my($tree_hashptr, @root_list_ptr)=@_;
  1344. my $concern;
  1345. # split the $root_list by ';'
  1346. for (@root_list_ptr) {
  1347. # loop all its parents
  1348. while(/\\/g) {
  1349. # remove all the parent folder
  1350. delete $tree_hashptr->{$`} if ($` ne $_);
  1351. }
  1352. }
  1353. Next2: for (keys %$tree_hashptr) {
  1354. for $concern (@root_list_ptr) {
  1355. next Next2 if (/\Q$concern\E/i);
  1356. }
  1357. delete $tree_hashptr->{$_};
  1358. }
  1359. }
  1360. # Find_UnMapping
  1361. # Find unmapping folders from branch tree, also remove the empty folders and the specified file-pattern files (such as slm.ini).
  1362. # Input
  1363. # $tree_hashptr - a hash - hash pointer, %$tree_hashptr is the branch tree
  1364. #
  1365. # Output
  1366. # none
  1367. # Usage
  1368. # &VerifyCover();
  1369. sub Find_UnMapping {
  1370. my $tree = shift;
  1371. my ($parent, $mykey, @myfiles);
  1372. for $parent (keys %$tree) {
  1373. my @children = ();
  1374. if (opendir(PARENT, $parent)){
  1375. # @children = grep {-d "$parent\\$_"} readdir (PARENT);
  1376. while (my $child = readdir (PARENT)){
  1377. next if $child =~ /^\.+$/;
  1378. push @children, lc($child) if -d "$parent\\$child";
  1379. }
  1380. }
  1381. closedir (PARENT);
  1382. BADDIR: foreach my $child (@children) {
  1383. if (!exists $tree->{$parent}->{"\\$child"}) {
  1384. $mykey = "$parent\\$child";
  1385. # find its all children / subidr children,
  1386. # and remove specified file pattern files, then repro the error
  1387. foreach my $file (qx("dir /s/b/a-d/l $mykey 2>nul")) {
  1388. # chomp $file;
  1389. $file =~ s/\\[^\\]+$//sgi;
  1390. &Error($mykey =~ /\Q$ENV{"_NTTREE"}\E/i ? 2053 : 2054, &RevEnvVars($file))
  1391. and next BADDIR if ($file !~ /$VERIFYPATH{'EXCLUDE_FILE_PATTERN'}/i
  1392. && $file =~ /^(.+)\\[^\\]+$/)
  1393. }
  1394. }
  1395. }
  1396. }
  1397. }
  1398. # PopulateReposit
  1399. # Populates REPOSITORY with data from the mapping tables ( @T variables).
  1400. # Input & Output <none>
  1401. # Usage
  1402. # &PopulateReposit();
  1403. sub PopulateReposit {
  1404. # Fill filter table
  1405. &FillFilter();
  1406. # Add file entries from TFILES and TBINDIRS
  1407. &AddFiles();
  1408. # If FILTER is defined, verify if all its entries (file names)
  1409. # were loaded in Repository.
  1410. for ( keys %FILTER ) {
  1411. if ( &IsEmptyHash( $TARGETS ) || &IsHashKey( $TARGETS, $_ ) ) {
  1412. if ( $FILTER{$_} <= 0 ) {
  1413. &Error( 1002, sprintf( "%s (%s)", $_, &GetMacro( "FILTER" ) ) );
  1414. next;
  1415. } # if
  1416. } # if
  1417. } # for
  1418. # If TARGETS is defined, verify that all its entries (target names)
  1419. # were loaded in Repository.
  1420. for ( keys %$TARGETS ) {
  1421. ( &IsFoundTarget( $_ ) ) || &FatalError( 1004, $_ );
  1422. } # for
  1423. # Fill in info about tokens from TTOKENS and TTOKDIRS
  1424. &FillTokens();
  1425. # Fill in the {Cmd} field for relevant keys
  1426. &FillCmd();
  1427. return;
  1428. } # PopulateReposit
  1429. # FillCmd
  1430. # Fill in the {Cmd} REPOSITORY field
  1431. # Input & Output
  1432. # none
  1433. # Usage
  1434. # &FillCmd();
  1435. sub FillCmd {
  1436. # Situations when, for a given (DestPath, DestFile) REPOSITORY key,
  1437. # the associated NMAKE commands must be stored in REPOSITORY's {Cmd} field:
  1438. # - clean SYSGEN
  1439. # - incremental SYSGEN with no command-line targets specified (empty TARGETS)
  1440. my $key = {"DestPath" => undef,
  1441. "DestFile" => undef};
  1442. my $all = $CLEAN || &IsEmptyHash( $TARGETS );
  1443. # Fill in the {Cmd} field
  1444. &SysgenLogMsg("Translating data to makefile commands...", 0);
  1445. foreach my $destpath ( sort keys %$REPOSITORY ) {
  1446. $SYNTAX_CHECK_ONLY or print &RevEnvVars("\t$destpath") unless !$all;
  1447. foreach my $destfile ( keys %{$REPOSITORY->{$destpath}} ) {
  1448. # Repository key
  1449. $key->{"DestPath"} = $destpath;
  1450. $key->{"DestFile"} = $destfile;
  1451. #~ # Check special character to avoid nmake broken
  1452. #~ if ( $destfile =~ /\$$/ ) {
  1453. #~ delete $REPOSITORY->{$destpath}->{$destfile};
  1454. #~ delete $TARGETS->{$destfile};
  1455. #~
  1456. #~ &Error(1214, "$destpath\\$destfile");
  1457. #~ next;
  1458. #~ }
  1459. # If incremental with targets, print target's full path
  1460. if ( !$all && &IsHashKey( $TARGETS, $destfile ) ) {
  1461. $SYNTAX_CHECK_ONLY or
  1462. printf "\t%s\\%s\n",
  1463. &GetFieldVal( $REPOSITORY, $key, "DestPath" ),
  1464. &GetFieldVal( $REPOSITORY, $key, "DestFile" );
  1465. } # if
  1466. # Translate the current REPOSITORY entry into a set of nmake commands
  1467. if ( $all || &IsHashKey( $TARGETS, $destfile ) ) {
  1468. &RecordToCmd( $key );
  1469. } # if
  1470. } # for
  1471. if ($all){
  1472. $SYNTAX_CHECK_ONLY or
  1473. print "\n";
  1474. };
  1475. } # for
  1476. return;
  1477. } # FillCmd
  1478. # AddTarget
  1479. # Add a filename (given on the command line) to the TARGETS table
  1480. sub AddTarget {
  1481. $TARGETS->{lc$_[0]}->{Target}=1;
  1482. } # AddTarget
  1483. # IsHashKey
  1484. # Verifies if a given string is key in a given hash
  1485. # Input
  1486. # hash (reference)
  1487. # entry (without path)
  1488. # Output
  1489. # 1 if the key exists
  1490. # 0 otherwise
  1491. sub IsHashKey {
  1492. return (exists $_[0]->{lc $_[1]});
  1493. } # IsHashKey
  1494. # IsFoundTarget
  1495. # Verifies if a filename is marked as found in TARGETS
  1496. # All targets specified in the command line must be marked in TARGETS at the moment
  1497. # REPOSITORY is fully loaded.
  1498. # Input
  1499. # filename (no path)
  1500. # Output
  1501. # 1 if the target is loaded in Repository
  1502. # 0 otherwise
  1503. sub IsFoundTarget {
  1504. if ( @{$TARGETS->{lc$_[0]}->{NewDestPath}} > 0 ) { return 1; }
  1505. return 0;
  1506. } # IsFoundTarget
  1507. # AddTargetPath
  1508. # Add a DestPath value to one of the NewDestPath, OldDestPath fields of TARGETS
  1509. # Input
  1510. # target name
  1511. # field name (one of NewDestPath and OldDestPath)
  1512. # field value
  1513. # Output
  1514. # none
  1515. # Usage
  1516. # &AddTargetPath( "ntdll.dll", "OldDestPath", "f:\\nt\\relbins\\jpn");
  1517. sub AddTargetPath {
  1518. push @{$TARGETS->{lc$_[0]}->{$_[1]}}, lc$_[2];
  1519. } # AddTargetPath
  1520. # IsEmptyHash
  1521. # Verifies if a hash table is empty.
  1522. # Input
  1523. # hash table (reference)
  1524. # Output
  1525. # 1 if hash is empty
  1526. # 0 otherwise
  1527. sub IsEmptyHash {
  1528. !scalar(%{$_[0]});
  1529. } # IsEmptyHash
  1530. # UpdTargetEntries
  1531. # In case of an incremental build, update the entries corresponding to the TARGETS
  1532. # from CMD_REPOSITORY according to data from REPOSITORY.
  1533. # Input & Output
  1534. # none
  1535. sub UpdTargetEntries {
  1536. # Target name
  1537. my $tname;
  1538. # Indexes
  1539. my $i;
  1540. # CMD_REPOSITORY key
  1541. my %key;
  1542. # OldDestPath array (reference)
  1543. my $destref;
  1544. if ( $CLEAN || &IsEmptyHash($TARGETS) ) { return; }
  1545. for $tname ( keys %$TARGETS ) {
  1546. $key{DestFile} = $tname;
  1547. # Delete CMD_REPOSITORY entries with DestFile equals to target name
  1548. $destref = $TARGETS->{$tname}->{OldDestPath};
  1549. for ( $i=0; $i < @{$destref}; $i++ ) {
  1550. $key{DestPath} = $destref->[$i];
  1551. &DeleteKey( $CMD_REPOSITORY, \%key );
  1552. } # for
  1553. # Copy data for TARGETS from REPOSITORY to CMD_REPOSITORY
  1554. $destref = $TARGETS->{$tname}->{NewDestPath};
  1555. for ( $i=0; $i < @{$destref}; $i++ ) {
  1556. $key{DestPath} = $destref->[$i];
  1557. # Use the same Cmd for CMD_REPOSITORY as for REPOSITORY
  1558. &SetField( $CMD_REPOSITORY, \%key, "DestPath", &GetFieldVal( $REPOSITORY, \%key, "DestPath" ) );
  1559. &SetField( $CMD_REPOSITORY, \%key, "DestFile", &GetFieldVal( $REPOSITORY, \%key, "DestFile" ) );
  1560. &SetField( $CMD_REPOSITORY, \%key, "Cmd", &GetFieldVal( $REPOSITORY, \%key, "Cmd" ) );
  1561. } # for
  1562. } # for
  1563. return;
  1564. } # UpdTargetEntries
  1565. # AddEntry
  1566. # Adds a new entry to REPOSITORY, only if REPOSITORY does not already have an
  1567. # entry with the same (DestPath, DestFile) key.
  1568. # Input
  1569. # new record (reference)
  1570. # boolean argument, saying if the file existence should be checked
  1571. # Output
  1572. # none
  1573. # Usage
  1574. # &AddEntry( $recref);
  1575. sub AddEntry {
  1576. my($recref, $e_check) = @_;
  1577. # Db key
  1578. my %key;
  1579. # Add entry to Repository based on file existence and on
  1580. # the type of the call (clean, incremental, with or without targets)
  1581. $recref->{DestFile} !~/\s/ or
  1582. &Error( 4001, "\"$recref->{SrcPath}\\$recref->{SrcFile}\"") and return;
  1583. if ( ( &IsEmptyHash( \%FILTER ) || # no filters
  1584. &IsHashKey( \%FILTER, $recref->{DestFile} ) ) # filter file
  1585. &&
  1586. ( $CLEAN || # clean sysgen
  1587. &IsEmptyHash( $TARGETS ) || # incremental with no targets specified
  1588. &IsHashKey( $TARGETS, $recref->{DestFile} ) ) # incremental with targets, and the current
  1589. # entry is one of the targets
  1590. ) {
  1591. if ( $e_check ) {
  1592. if ( ! -e "$recref->{SrcPath}\\$recref->{SrcFile}" ) {
  1593. &Error( 2101, &RevEnvVars("$recref->{SrcPath}\\$recref->{SrcFile}") );
  1594. return;
  1595. } # if
  1596. } # if
  1597. # Set the key
  1598. $key{DestPath} = $recref->{DestPath};
  1599. $key{DestFile} = $recref->{DestFile};
  1600. &AddFileInfo( $recref, \%key );
  1601. } # if
  1602. return;
  1603. } # AddEntry
  1604. # AddFileInfo
  1605. # Input
  1606. # record to be added
  1607. # Output
  1608. # <none>
  1609. # Usage
  1610. # &AddFileInfo( $recref, \%key );
  1611. sub AddFileInfo {
  1612. my($recref, $keyref) = @_;
  1613. # Nothing to do if the DestFile already has an entry in the Repository
  1614. ( ! &IsRepositKey( $REPOSITORY, $keyref ) ) || return;
  1615. if ( &IsHashKey( $TARGETS, $keyref->{DestFile} ) ) {
  1616. &AddTargetPath( $keyref->{DestFile}, "NewDestPath", $keyref->{DestPath} );
  1617. } # if
  1618. if ( &IsHashKey( \%FILTER, $recref->{DestFile} ) ) {
  1619. $FILTER{lc( $recref->{DestFile} )}++;
  1620. } # if
  1621. # Set the fields of the new entry
  1622. &SetField( $REPOSITORY, $keyref, "DestPath", $recref->{DestPath} );
  1623. &SetField( $REPOSITORY, $keyref, "DestFile", $recref->{DestFile} );
  1624. &SetField( $REPOSITORY, $keyref, "SrcPath", $recref->{SrcPath} );
  1625. &SetField( $REPOSITORY, $keyref, "SrcFile", $recref->{SrcFile} );
  1626. &SetField( $REPOSITORY, $keyref, "SrcDbg", $recref->{SrcDbg} );
  1627. &SetField( $REPOSITORY, $keyref, "DestDbg", $recref->{DestDbg} );
  1628. SWITCH: {
  1629. if ( $recref->{Xcopy} eq "y" ) {
  1630. &SetField( $REPOSITORY, $keyref, "OpType", "1" );
  1631. last SWITCH;
  1632. } # case
  1633. # default
  1634. ## &SetField( $REPOSITORY, $keyref, "OpType", "0" );
  1635. } # SWITCH
  1636. return;
  1637. } # AddFileInfo
  1638. # IsRepositKey
  1639. # Looks in REPOSITORY for the given key.
  1640. # Used to decide if a record will be added or not to REPOSITORY.
  1641. # Input
  1642. # map (reference)
  1643. # the key (reference)
  1644. # Output
  1645. # 1 if the key is found and 0 otherwise
  1646. # Usage
  1647. # $is_key_found = &IsRepositKey( $REPOSITORY, $keyref );
  1648. sub IsRepositKey {
  1649. # The key referred by keyref is modified,
  1650. # but this saves a lot of execution time
  1651. # so we prefer not to work with a copy of the key
  1652. return ( exists $_[0]->{lc$_[1]->{DestPath}}->{lc$_[1]->{DestFile}} );
  1653. } # IsRepositKey
  1654. # DeleteKey
  1655. # Input
  1656. # map name (one of REPOSITORY, CMD_REPOSITORY) (reference)
  1657. # key (reference)
  1658. # Output
  1659. # none
  1660. sub DeleteKey {
  1661. # my( $mapref, $keyref ) = @_;
  1662. # The key referred by keyref is modified,
  1663. # but this saves a lot of execution time
  1664. # so we prefer not to work with a copy of the key
  1665. delete $_[0]->{lc$_[1]->{DestPath}}->{lc$_[1]->{DestFile}};
  1666. } # DeleteKey
  1667. # FillTokens
  1668. # Adds info regarding tokens in REPOSITORY (TTOKENS and TTOKDIRS).
  1669. # Input & Output <none>
  1670. # Usage
  1671. # &FillTokens();
  1672. sub FillTokens {
  1673. # Current entry from TTOKDIRS
  1674. my $recref;
  1675. # List of files found in the current directory
  1676. my @files;
  1677. # Indexes
  1678. my( $i, $j, $k );
  1679. # Custom resource file name
  1680. my $custfile;
  1681. # Custom reosurces array
  1682. my @custres;
  1683. # Custom reosurces hash for verification
  1684. my %custres_h;
  1685. # Temporary store the file hash for special sort @files
  1686. my %hashfiles;
  1687. &SysgenLogMsg("Filling token data...",0);
  1688. # Fill in TTOKENS
  1689. for ( $i=0; $i < @TTOKENS; $i++ ) {
  1690. $recref = $TTOKENS[$i];
  1691. my $TokFile = $recref->{"TokFile"};
  1692. # rsrc or lcx
  1693. if ( $TokFile =~ m/\.lcx\r?$/i or $TokFile =~ m/\.rsrc\r?$/i) {
  1694. &FillTokEntry( $recref, 1 );
  1695. next;
  1696. } # if
  1697. # bingen
  1698. if ( $TokFile =~ m/\.\w+_\r?$/i) {
  1699. # Identify all the custom resources under the following assumptions:
  1700. # * the token and the custom resources of a binary are in the same directory
  1701. # * for a "<binary_name>.<token_extension>" token, a custom resource has
  1702. # the name <binary_name>_<binary_extension>_<custom_resource_name>
  1703. # * a token is always followed in lexicographic order by its custom resources
  1704. # or by another token
  1705. # Parse the token file to find embeded files
  1706. # open(F, "$recref->{TokPath}\\$recref->{TokFile}");
  1707. # my @text=<F>;
  1708. # close(F);
  1709. # for $str (@text) {
  1710. # if (($str=~/\[[^\]]+\|1\|0\|4\|\"[\w\.]*\"]+\=(\w+\.\w+)\s*$/)&&(-e $1)) {
  1711. # $custres_h{$1}=0;
  1712. # }
  1713. # }
  1714. # undef @text;
  1715. # Store it to an array for later use
  1716. # @custres=keys %custres_h;
  1717. # Verify all its names' token files are included
  1718. $custfile = $recref->{DestFile};
  1719. $custfile =~ s/\./_/g;
  1720. @custres=qx("dir /a-d-h /b /on $recref->{TokPath}\\$custfile\* 2\>nul")
  1721. if -d "$recref->{TokPath}\\$custfile";
  1722. $recref->{CustRc} = "";
  1723. for ( $j=0; $j < @custres; $j++ ) {
  1724. chop $custres[$j];
  1725. # Found that in NT5, the result of dir /b starts with an empty line
  1726. if ( $custres[$j] eq "" ) {
  1727. next;
  1728. } # if
  1729. $recref->{CustRc} .= "\ \\\n\t\t\t$recref->{TokPath}\\$custres[$j]";
  1730. } # for
  1731. # Call FillTokEntry with 1, meaning that the existence of the
  1732. # token file is queried and a non fatal error fired if the file
  1733. # is missing.
  1734. # As the token is explicitly listed in TOKENS MAP, it is natural
  1735. # to suppose it should exist.
  1736. &FillTokEntry( $recref, 1 );
  1737. next;
  1738. } # if
  1739. # default
  1740. &Error( 2013, "$recref->{TokPath}\\$recref->{TokFile}" );
  1741. } #for
  1742. @TTOKENS = ();
  1743. # Fill in TTOKDIRS
  1744. for ( $i=0; $i < @TTOKDIRS; $i++ ) {
  1745. $recref = $TTOKDIRS[$i];
  1746. &SysgenLogMsg("\t$recref->{TokPath}",0);
  1747. # Load all files from the TokPath directory
  1748. @files = ();
  1749. # because in the same folder, so we could ignore \a\b.txt, \a\b\c.txt, \a\c.txt problem.
  1750. @files =
  1751. sort {&TokToFile($a , 1) cmp &TokToFile($b, 1)}
  1752. split ("\n", qx("dir /a-d-h /l /b $recref->{TokPath} 2\>nul"));
  1753. # Fill in the corresponding entry from REPOSITORY for each token found at TokPath
  1754. for ( $j=0; $j < @files; $j++ ) {
  1755. my $TokFile = $files[$j];
  1756. # Found that in NT5, the result of dir /b starts with an empty line (bug)
  1757. if ( $TokFile eq "" ) {
  1758. next;
  1759. }
  1760. if ( $TokFile =~ m/\.lcx\r?$/i) {
  1761. $recref->{TokFile} = $TokFile;
  1762. $recref->{DestFile} = &TokToFile( $TokFile );
  1763. &FillTokEntry( $recref, 0, \@custres );
  1764. # look for bogus custom resources
  1765. $custfile = &TokToFile($TokFile, 1);
  1766. for ( $k = $j+1; $k < @files; $k++ ) {
  1767. if ( $files[$k] =~ /$custfile/i ) {
  1768. &Error( 2019, "$recref->{TokPath}\\$files[$k] for $recref->{TokPath}\\$TokFile" );
  1769. } # if
  1770. else {
  1771. last;
  1772. } # else
  1773. } # for
  1774. $j = $k-1;
  1775. next;
  1776. }
  1777. # bingen
  1778. if ( $TokFile =~ m/\.\w+_\r?$/i ) {
  1779. $recref->{TokFile} = $TokFile;
  1780. $recref->{DestFile} = &TokToFile( $TokFile );
  1781. # check if the next entry in @files is an rsrc token for the same filename
  1782. if ( $j < @files-1 && $files[$j+1] =~ m/\.rsrc\r?$/i ) {
  1783. if ( $recref->{DestFile} eq &TokToFile( $files[$j+1] ) ) {
  1784. &Error( 2018, "$recref->{DestPath}\\$files[$j] and $recref->{DestPath}\\$files[$j+1]");
  1785. $j = $j+1;
  1786. } # if
  1787. } # if
  1788. # Identify file's custom resources
  1789. $custfile = &TokToFile( $TokFile , 1 );
  1790. $recref->{CustRc} = "";
  1791. for ( $k = $j+1; $k < @files; $k++ ) {
  1792. if ( $files[$k] =~ /$custfile/i ) {
  1793. $recref->{CustRc} .= "\ \\\n\t\t\t$recref->{TokPath}\\$files[$k]";
  1794. } # if
  1795. else {
  1796. last;
  1797. } # else
  1798. } # for
  1799. $j = $k-1;
  1800. # Call FillTokEntry with 0, to inhibit the file existence checking,
  1801. # as TokFile is a result of a dir command (see above).
  1802. &FillTokEntry( $recref, 0, \@custres );
  1803. next;
  1804. } # if
  1805. else {
  1806. # rsrc
  1807. if ( $TokFile =~ m/\.rsrc\r?$/i ) {
  1808. $recref->{TokFile} = $TokFile;
  1809. $recref->{DestFile} = &TokToFile($TokFile);
  1810. # look for bogus custom resources
  1811. $custfile = &TokToFile($TokFile, 1);
  1812. for ( $k = $j+1; $k < @files; $k++ ) {
  1813. if ( $files[$k] =~ /$custfile/i ) {
  1814. &Error( 2019, "$recref->{TokPath}\\$files[$k] for $recref->{TokPath}\\$files[$j]" );
  1815. } # if
  1816. else {
  1817. last;
  1818. } # else
  1819. } # for
  1820. $j = $k-1;
  1821. &FillTokEntry( $recref, 0, \@custres );
  1822. next;
  1823. } # if
  1824. else {
  1825. # default
  1826. &Error( 2013, "$recref->{TokPath}\\$TokFile" );
  1827. } # else
  1828. } #else
  1829. } # for
  1830. } # for
  1831. @TTOKDIRS = ();
  1832. } # FillTokens
  1833. # TokToFile
  1834. # Converts a given bingen token name to a binary name.
  1835. # Input
  1836. # token name
  1837. # Optional argument: replace "." with "_" to provde resource allocation
  1838. # like
  1839. # autodisc_dll_reginst.inf => autodisc.dll
  1840. #
  1841. # Output
  1842. # binary name
  1843. # Usage
  1844. # $fname = &TokToFile( $tokname, $replacedots );
  1845. #
  1846. sub TokToFile{
  1847. my ($tokname, $replacedots) = @_;
  1848. my $badname = 1;
  1849. foreach my $toktype (keys(%$MAP_BINGEN_TOK_EXT)) {
  1850. $badname = 0 if $tokname =~/$toktype$/i;
  1851. $toktype = $MAP_BINGEN_TOK_EXT->{$toktype};
  1852. $toktype =~ s/\./\_/g;
  1853. $badname = 0 if $tokname =~/$toktype\_/i;
  1854. } # if
  1855. $badname and &Error( 2013, $tokname);
  1856. map { $tokname =~ s/\Q$_\E$/$MAP_BINGEN_TOK_EXT->{$_}/g;} keys %$MAP_BINGEN_TOK_EXT;
  1857. $tokname =~ s/\./_/g if $replacedots;
  1858. $tokname;
  1859. } # TokToFile
  1860. # FillTokEntry
  1861. # Given a REPOSITORY key, adds info on tokens (@TTOKENS and @TTOKDIRS).
  1862. # Input
  1863. # a token record
  1864. # boolean argument, saying if the existence of the token needs to be verified
  1865. # Output
  1866. # <none>
  1867. sub FillTokEntry {
  1868. my($recref, $e_check) = @_;
  1869. # %key = is a hash table (as defined by the database key)
  1870. my %key;
  1871. # Token information is added to an existing Repository entry
  1872. # based on token file existance and the type of the sysgen call
  1873. # (clean, incremental with or without targets)
  1874. if ( ( &IsEmptyHash( \%FILTER ) || # no filter
  1875. &IsHashKey( \%FILTER, $recref->{DestFile} ) ) # filter file
  1876. &&
  1877. ( $CLEAN || # clean sysgen
  1878. &IsEmptyHash( $TARGETS ) || # incremental without targets
  1879. &IsHashKey( $TARGETS, $recref->{DestFile} ) ) # incremental with targets and the current
  1880. # entry corresponds to a target
  1881. ) {
  1882. if ( $e_check ) {
  1883. if ( ! -e "$recref->{TokPath}\\$recref->{TokFile}" ) {
  1884. &Error( 2102, "$recref->{TokPath}\\$recref->{TokFile}" );
  1885. return;
  1886. } # if
  1887. } # if
  1888. # Set REPOSITORY key
  1889. $key{DestPath} = $recref->{DestPath};
  1890. $key{DestFile} = $recref->{DestFile};
  1891. # Fill in the information needed in REPOSITORY
  1892. &FillTokInfo( $recref, \%key );
  1893. } # if
  1894. return;
  1895. } # FillTokEntry
  1896. # FillTokInfo
  1897. # Adds information for bingen switches to an existing REPOSITORY entry.
  1898. # Sets the following keys ( fields ): TokPath, TokFile, BgnSw, OpType, ITokPath.
  1899. # Input
  1900. # hash table with the @FTOKENS keys, representing one token record (reference)
  1901. # key to identify the REPOSITORY entry (reference)
  1902. # Output
  1903. # <none>
  1904. # Usage
  1905. # &FillTokInfo( $recref, \%key );
  1906. sub FillTokInfo {
  1907. my( $recref, $keyref ) = @_;
  1908. # Operation type
  1909. my $optype;
  1910. # A token record does not create new entries, just adds/updates fields of an existing entry.
  1911. if ( ! &ExistsBinForTok( $keyref, $recref ) ) {
  1912. my $notfound = 1;
  1913. my $key = lc($recref->{"TokPath"}."\\".$recref->{"TokFile"});
  1914. my $hotfix = \%HOTFIX;
  1915. foreach my $fix (keys(%HOTFIX)){
  1916. # $DEBUG and
  1917. # print STDERR "\%HOTFIX key:", $fix, "\n";
  1918. my $depend = $hotfix->{$fix}->{"depend"};
  1919. foreach my $dep (@$depend){
  1920. if ($key eq $dep) {
  1921. # $DEBUG and
  1922. # print STDERR "found hotfix for $key\n";
  1923. $notfound = 0;
  1924. }
  1925. }
  1926. }
  1927. if ($notfound){
  1928. &Error( 2011, &RevEnvVars("$recref->{TokPath}\\$recref->{TokFile}") );
  1929. return;
  1930. }
  1931. } # if
  1932. # If no key found in REPOSITORY for this entry, then return.
  1933. # ( the token does not follow the default rule given by the TOKDIRS map;
  1934. # it might be associated with another binary via the TOKENS map )
  1935. if ( ! &IsRepositKey( $REPOSITORY, $keyref ) ) { return; }
  1936. # Verify if the binary is locked (XCopy = y) for localization.
  1937. # If locked, the binary will not be localized via bingen or rsrc and an xcopy command
  1938. # will apply instead, even though a token exists in the token tree.
  1939. $optype = &GetFieldVal( $REPOSITORY, $keyref, "OpType" );
  1940. SWITCH: {
  1941. if ( $optype eq "1" ) {
  1942. &Error( 2014, sprintf( "%s\\%s for %s\\%s",
  1943. $recref->{TokPath},
  1944. $recref->{TokFile},
  1945. &GetFieldVal( $REPOSITORY, $keyref, "SrcPath" ),
  1946. &GetFieldVal( $REPOSITORY, $keyref, "SrcFile" ) ) );
  1947. return;
  1948. } # if
  1949. if ( $optype eq "2" ) {
  1950. my($previous, $current)=( &GetFieldVal( $REPOSITORY, $keyref, "TokPath" ) . "\\" .
  1951. &GetFieldVal( $REPOSITORY, $keyref, "TokFile" ),
  1952. $recref->{TokPath} . "\\" . $recref->{TokFile});
  1953. $previous ne $current and
  1954. &Error( 2012, &RevEnvVars(sprintf( "%s\\%s and %s\\%s \<\= %s\\%s",
  1955. &GetFieldVal( $REPOSITORY, $keyref, "TokPath" ),
  1956. &GetFieldVal( $REPOSITORY, $keyref, "TokFile" ),
  1957. $recref->{TokPath},
  1958. $recref->{TokFile},
  1959. &GetFieldVal( $REPOSITORY, $keyref, "SrcPath" ),
  1960. &GetFieldVal( $REPOSITORY, $keyref, "SrcFile" ))));
  1961. return;
  1962. } # if
  1963. if ( $optype eq "-" ) {
  1964. # Set the token-related fields for the binary given by the key
  1965. map {&SetField( $REPOSITORY, $keyref, $_, $recref->{$_} )}
  1966. qw(TokPath TokFile ITokPath BgnSw LocOp CustRc);
  1967. &SetField( $REPOSITORY, $keyref, "OpType", "2" );
  1968. last SWITCH;
  1969. } # if
  1970. # default
  1971. &FatalError( 3011, sprintf( "$recref->{DestPath}\\$recref->{DestFile} %s",
  1972. &GetFieldVal( $REPOSITORY, $keyref, "OpType") ) );
  1973. } # SWITCH
  1974. return;
  1975. } # FillTokInfo
  1976. # ExistsBinForTok
  1977. # Looks in Repository for an entry corresponding to a given token
  1978. # Input
  1979. # a token record ( reference )
  1980. # a Repository key ( reference )
  1981. # Output
  1982. # 1 - entry found ( token info might be already completed )
  1983. # 0 - otherwise
  1984. # Example
  1985. # if ( &ExistsBinForTok( $recref ) ) { ... }
  1986. sub ExistsBinForTok {
  1987. my( $keyref, $recref ) = @_;
  1988. # Copy key
  1989. my $tmpkey = {};
  1990. return 1 if &IsRepositKey( $REPOSITORY, $keyref );
  1991. $tmpkey->{DestFile} = $keyref->{DestFile};
  1992. foreach ( keys %$REPOSITORY ) {
  1993. $tmpkey->{DestPath} = $_;
  1994. next unless &IsRepositKey( $REPOSITORY, $tmpkey );
  1995. return 1 if ((&GetFieldVal( $REPOSITORY, $tmpkey, "TokFile" ) eq $recref->{TokFile}) and
  1996. (&GetFieldVal( $REPOSITORY, $tmpkey, "TokPath" ) eq $recref->{TokPath}));
  1997. } # for
  1998. return 0;
  1999. } # ExistsBinForTok
  2000. # SetField
  2001. # Updates a key (field) of a given REPOSITORY entry with a given value.
  2002. # Input
  2003. # map (reference)
  2004. # key value (reference)
  2005. # field name
  2006. # field value
  2007. # Output
  2008. # none
  2009. # Usage
  2010. # &SetField( \%key, "TokPath", $TokPath );
  2011. sub SetField {
  2012. $_[0]->{lc$_[1]->{"DestPath"}}->{lc$_[1]->{"DestFile"}}->{$_[2]} = $_[3] if ($_[3] ne '-');
  2013. } # SetField
  2014. # PushFieldVal
  2015. # Add one element to a field storing an array ({Cmd} field for REPOSITORY)
  2016. # Input
  2017. # map (reference)
  2018. # key value (reference)
  2019. # field name
  2020. # field value
  2021. # Output
  2022. # none
  2023. # Usage
  2024. # &PushFieldVal();
  2025. sub PushFieldVal {
  2026. # my( $mapref, $keyref, $fname, $fval ) = @_;
  2027. # The key referred by keyref is modified,
  2028. # but this saves a lot of execution time
  2029. # so we prefer not to work with a copy of the key
  2030. push @{$_[0]->{lc$_[1]->{DestPath}}->{lc$_[1]->{DestFile}}->{$_[2]}}, $_[3] if ($_[3] ne '-');
  2031. } # PushFieldVal
  2032. # GetFieldVal
  2033. # Return the value of a particular key of a given REPOSITORY entry.
  2034. # Input
  2035. # map (reference)
  2036. # key value (reference)
  2037. # name of the searched field
  2038. # Output
  2039. # the value of the searched field
  2040. # Usage
  2041. # $srcpath = &GetFieldVal( $REPOSITORY, \%key, "SrcPath" );
  2042. sub GetFieldVal {
  2043. defined($_[0]->{lc($_[1]->{"DestPath"})}->{lc($_[1]->{"DestFile"})}->{$_[2]})?
  2044. $_[0]->{lc($_[1]->{"DestPath"})}->{lc($_[1]->{"DestFile"})}->{$_[2]}
  2045. : "-";
  2046. } # GetFieldVal
  2047. # GenNmakeFiles
  2048. # For each entry found in the REPOSITORY, generate the NMAKE description blocks
  2049. # and write them to a makefile. Also generate a nmake command file with command-line
  2050. # parameters for nmake.
  2051. # Input
  2052. # makefile name, syscmd file name
  2053. # Output
  2054. # none
  2055. # Usage
  2056. # &GenNmakeFiles($MAKEFILE, $SYSCMD);
  2057. sub GenNmakeFiles {
  2058. my $MAKEFILE = shift;
  2059. my $SYSCMD = shift;
  2060. &SysgenLogMsg("Generating $MAKEFILE...", 1);
  2061. # Update CMD_REPOSITORY for TARGETS with updated data from REPOSITORY
  2062. &UpdTargetEntries();
  2063. # Identify all targets with description block changed
  2064. # and keep them in SYSCMD
  2065. &FillSyscmd();
  2066. &GenMakeDir();
  2067. # FIX The REPOSITORY changes described in hotfix
  2068. &FixRepository();
  2069. # Handle the copy operation via compdir
  2070. if ($ACCELERATE and $CLEAN){
  2071. &FilterOpType($REPOSITORY, ["1","2"]);
  2072. my $COMPDIR_SCRIPTS_BASE = &GetMacro("COMPDIR_SCRIPTS_BASE");
  2073. &SysgenLogMsg("Writing compdir listings in $COMPDIR_SCRIPTS_BASE",0);
  2074. -d $COMPDIR_SCRIPTS_BASE or qx("md $COMPDIR_SCRIPTS_BASE");
  2075. my $GENERATE_SCRIPTS_BASE = &GetMacro("GENERATE_SCRIPTS_BASE");
  2076. if ($GENERATE_SCRIPTS_BASE){
  2077. &SysgenLogMsg("Creating $GENERATE_SCRIPTS_BASE",0);
  2078. -d $GENERATE_SCRIPTS_BASE or qx("md $GENERATE_SCRIPTS_BASE");
  2079. }
  2080. &SysgenLogMsg("Cleaning $COMPDIR_SCRIPTS_BASE",0);
  2081. qx("del /q $COMPDIR_SCRIPTS_BASE\\*");
  2082. $COMPDIR_FILE_TEMPLATE = &SwitchWorkDir($COMPDIR_FILE_TEMPLATE, $COMPDIR_SCRIPTS_BASE);
  2083. # cannot do it too early.
  2084. &WriteCompdir($COMPDIR_FILE_TEMPLATE, \%COMPDIR);
  2085. }
  2086. # Write the makefile
  2087. &WriteToMakefile( $MAKEFILE, $SYSCMD );
  2088. # Write the syscmd file
  2089. &WriteToSyscmd($SYSCMD);
  2090. if ( -e $MAKEFILE ) {
  2091. &SysgenLogMsg("\n$MAKEFILE is ready for execution.",0);
  2092. } # if
  2093. else {
  2094. print "No MAKEFILE generated. Nothing to do.\n";
  2095. } # else
  2096. return;
  2097. } # GenNmakeFiles
  2098. # FillSyscmd
  2099. # Fills in @SYSCMD global variable with the target names having their description
  2100. # block changed. It is used for incremental calls only.
  2101. # Input & Output
  2102. # none
  2103. sub FillSyscmd {
  2104. # Repository key
  2105. my %key;
  2106. # Destinationa path, destination file
  2107. my( $destpath, $destfile );
  2108. # Nothing to do in case of:
  2109. # clean sysgen
  2110. # incremental sysgen with targets
  2111. if ( $CLEAN || !&IsEmptyHash( $TARGETS ) ) {
  2112. return;
  2113. } # if
  2114. # The call is incremental, without targets, and a previous makefile exists
  2115. # It is fine to use REPOSITORY in this case;
  2116. # the only case when we use CMD_REPOSITORY to write info to the makefile
  2117. # is an incremental sysgen with targets (when a makefile already exists).
  2118. # Compare all description blocks
  2119. for $destpath ( sort keys %$REPOSITORY ) {
  2120. for $destfile ( sort keys %{$REPOSITORY->{$destpath}} ) {
  2121. $key{DestPath} = $destpath;
  2122. $key{DestFile} = $destfile;
  2123. # Store in SYSCMD the keys with changed description blocks
  2124. if ( &CmdCompare( \%key ) ) {
  2125. push @SYSCMD, "$destpath\\$destfile";
  2126. } # if
  2127. } # for
  2128. } # for
  2129. return;
  2130. } # FillSyscmd
  2131. sub MakeRevEnvVars{
  2132. my @vars = qw(_NTPOSTBLD
  2133. _NTLOCDIR
  2134. _NTTREE
  2135. TEMP
  2136. RAZZLETOOLPATH
  2137. _NTBINDIR
  2138. );
  2139. &SetMacro("LANG", &GetMacro("LANGUAGE"));
  2140. # do not remove this line! the design of the aggregation
  2141. # allows for re-use of the LANG environment variable.
  2142. # The reaslization is not perfect though:
  2143. # the variables below are all dedicated to language/language transmutation:
  2144. # SRC_LANG LANG LANGUAGE ALT_LANG
  2145. my $SRC_LANG = uc(&GetMacro("LANGUAGE"));
  2146. my $LANG = uc(&GetMacro("ALT_LANG"));
  2147. my $_NTDRIVE = uc(&GetMacro("_NTDRIVE"));
  2148. &SetMacro("_NTLOCDIR", &GetMacro("_NTBINDIR")."\\LOC\\RES\\".&GetMacro("LANGUAGE"));
  2149. # Is is important here to allow _NTLOCDIR get overwritten!
  2150. # print STDERR "overwriting the \$\(_NTLOCDIR\):" , &GetMacro("_NTLOCDIR"), "\n";
  2151. %RevEnvVars = ();
  2152. @RevEnvVars = ();
  2153. %RevEnvVars = map {uc(&GetMacro($_)) => uc("\$\($_\)")} @vars;
  2154. push @RevEnvVars, map {uc(&GetMacro($_))} @vars;
  2155. if ($LANG ne "" and ($SRC_LANG ne $LANG) ){
  2156. $RevEnvVars{$LANG."\\"} = "\$\(LANG\)\\";
  2157. push @RevEnvVars, $LANG."\\";
  2158. $RevEnvVars{".$LANG"} = ".\$\(LANG\)";
  2159. push @RevEnvVars, ".$LANG";
  2160. $RevEnvVars{"\\$LANG"} = "\\\$\(LANG\)";
  2161. push @RevEnvVars, "\\$LANG";
  2162. $RevEnvVars{$SRC_LANG."\\"} = "\$\(SRC_LANG\)\\";
  2163. push @RevEnvVars, $SRC_LANG."\\";
  2164. $RevEnvVars{".$SRC_LANG"} = ".\$\(SRC_LANG\)";
  2165. push @RevEnvVars, ".$SRC_LANG";
  2166. $RevEnvVars{"\\$SRC_LANG"} = "\\\$\(SRC_LANG\)";
  2167. push @RevEnvVars, "\\$SRC_LANG";
  2168. } else {
  2169. $RevEnvVars{$SRC_LANG."\\"} = "\$\(LANG\)\\";
  2170. push @RevEnvVars, $SRC_LANG."\\";
  2171. $RevEnvVars{".$SRC_LANG"} = ".\$\(LANG\)";
  2172. push @RevEnvVars, ".$SRC_LANG";
  2173. $RevEnvVars{"\\$SRC_LANG"} = "\\\$\(LANG\)";
  2174. push @RevEnvVars, "\\$SRC_LANG";
  2175. }
  2176. $RevEnvVars{$_NTDRIVE."\\"} = "\$\(_NTDRIVE\)\\";
  2177. push @RevEnvVars, $_NTDRIVE."\\";
  2178. $RevEnvVars {uc(&GetMacro("DST"))} = "\$\(_NTPOSTBLD\)"
  2179. if &GetMacro("DST");
  2180. $DEBUG and
  2181. print STDERR
  2182. join("\n", "ENV:", map {$RevEnvVars{$_}." => ".$_} @RevEnvVars), "\n\n\n\n";
  2183. # exit;
  2184. }
  2185. sub RevEnvVars{
  2186. my $expr = shift;
  2187. if ($expr =~ /\S/){
  2188. map {$expr =~ s/\Q$_\E/$RevEnvVars{$_}/gim} @RevEnvVars;
  2189. # Here we enforce the order in replacing the revenvvars with their labels
  2190. # in order to get rid of the expressions like
  2191. # $(_NTBINRIR).binaries.x86fre
  2192. }
  2193. $expr;
  2194. }
  2195. sub WriteSettings{
  2196. my $LANGUAGE = uc(&GetMacro("LANGUAGE"));
  2197. my $ALT_LANG = uc(&GetMacro("ALT_LANG"));
  2198. my $LANG = "\$\(LANG\)";
  2199. if ($LANGUAGE ne $ALT_LANG){
  2200. $LANG = uc(&GetMacro("LANG"));
  2201. $LANGUAGE = $ALT_LANG;
  2202. }
  2203. my $language = $LANGUAGE;
  2204. $language =~ s-(\w+)-\U$1\E-;
  2205. $LANGUAGE =~ s-(\w+)-\L$1\E-;
  2206. my $COMPDIR = &GetMacro("COMPDIR");
  2207. my $COPY = &GetMacro("COPY");
  2208. my $BINGEN = &GetMacro("BINGEN");
  2209. my $REATTR = &GetMacro("REATTR");
  2210. my $_BUILDARCH = &GetMacro("_BUILDARCH");
  2211. my $LSBUILD = &GetMacro("LSBUILD");
  2212. print "# $SELF\n";
  2213. print <<EOE;
  2214. #
  2215. !IFNDEF LANG
  2216. ! ERROR You must define macro LANG
  2217. !ENDIF
  2218. !IFNDEF _NTTREE
  2219. ! ERROR You must run aggregation in the NT razzle
  2220. !ENDIF
  2221. !IF "\$(LANG)" != "$LANGUAGE" \&\& "\$(LANG)" != "$language"
  2222. ! ERROR This file is for $LANGUAGE
  2223. !ENDIF
  2224. !IF "\$(_BUILDARCH)" != "$_BUILDARCH"
  2225. ! ERROR This file is for $_BUILDARCH
  2226. !ENDIF
  2227. # Directory name aliases
  2228. _NTLOCDIR=\$\(\_NTBINDIR\)\\LOC\\RES\\${LANG}
  2229. # Binary file operation aliases
  2230. COMPDIR=$COMPDIR
  2231. BINGEN=$BINGEN
  2232. COPY=$COPY
  2233. REATTR=$REATTR
  2234. LSBUILD=$LSBUILD
  2235. EOE
  2236. my @LANGCODES = qw(Site
  2237. Read1st
  2238. Comments
  2239. Readme
  2240. ACP
  2241. LCID
  2242. Home
  2243. PriLangID
  2244. Flavor
  2245. PerfID
  2246. GUID
  2247. SubLangID
  2248. Class
  2249. LANG
  2250. LangISO
  2251. RSRC_LCID
  2252. );
  2253. map {print "$_=". &GetMacro($_), "\n";} @LANGCODES;
  2254. print "LANG=". &GetMacro("ALT_LANG"). "\n\n";
  2255. if (&GetMacro("LANGUAGE") ne &GetMacro("ALT_LANG")){
  2256. my $SRC_LANG = uc(&GetMacro("LANGUAGE"));
  2257. print "SRC_LANG=$SRC_LANG \n\n";
  2258. }
  2259. $DEBUG or
  2260. print <<SOE;
  2261. .SILENT:
  2262. SOE
  2263. }
  2264. # WriteToMakefile
  2265. # Generate the makefile
  2266. # Input
  2267. # makefile name, syscmd file name
  2268. # Output
  2269. # none
  2270. sub WriteToMakefile {
  2271. my( $makefname, $sysfname ) = @_;
  2272. # Open the makefile
  2273. ( open MAKEFILE, ">"."$makefname" ) || &FatalError( 1007, $makefname );
  2274. select( MAKEFILE );
  2275. # Write "sysgen" target
  2276. &WriteSysgenTarget($sysfname );
  2277. &WriteSettings();
  2278. # Write "all" target
  2279. &WriteAllTarget();
  2280. $ACCELERATE and $CLEAN and &WriteCompdirTarget();
  2281. # Write file-by-file description blocks
  2282. &WriteFileCmds();
  2283. close(MAKEFILE);
  2284. select( STDOUT );
  2285. return;
  2286. } # WriteToMakefile
  2287. # WriteSysgenTarget
  2288. # Write "sysgen" target in the generated makefile
  2289. # It invokes nmake recursively.
  2290. # Input
  2291. # filename (including path) of the generated syscmd file
  2292. # Output
  2293. # none
  2294. # Usage
  2295. # &WriteSysgenTarget($SYSCMD);
  2296. sub WriteSysgenTarget {
  2297. my($cmdfile) = @_;
  2298. printf "sysgen:\n";
  2299. # Call nmake @syscmd in the following cases:
  2300. # sysgen with targets (clean or incremental)
  2301. # incremental sysgen without targets, but with changes in the description blocks
  2302. if ( !&IsEmptyHash( $TARGETS ) || ( !$CLEAN && ( @SYSCMD > 0 ) ) ) {
  2303. printf "\t\@nmake /K /NOLOGO /F $MAKEFILE \@$cmdfile \n";
  2304. } # if
  2305. # Call nmake all in the following cases:
  2306. # sysgen without targets (clean or incremental)
  2307. if ( &IsEmptyHash( $TARGETS ) ) {
  2308. if ( $CLEAN ) {
  2309. printf "\t\@nmake /A /K /F $MAKEFILE /NOLOGO all\n";
  2310. } # if
  2311. else {
  2312. printf "\t\@nmake /K /F $MAKEFILE /NOLOGO all\n";
  2313. } # else
  2314. } # if
  2315. printf "\n";
  2316. return;
  2317. } # WriteSysgenTarget
  2318. # WriteAllTarget
  2319. # Writes "all"'s target dependency line in the makefile.
  2320. # Input & Output
  2321. # none
  2322. # Usage
  2323. # &WriteAllTarget();
  2324. sub WriteAllTarget {
  2325. # Table (reference): REPOSITORY or CMD_REPOSITORY
  2326. my $mapref;
  2327. # Destination path, destination file
  2328. my( $destpath, $destfile );
  2329. my $chars;
  2330. my $steps;
  2331. my @alltargets;
  2332. my $i;
  2333. my $j;
  2334. my $k;
  2335. my $total=0;
  2336. # Use data from CMD_REPOSITORY or REPOSITORY,
  2337. # depending if SYSGEN is called or not incrementally,
  2338. # with or without targets.
  2339. $mapref = $CMD_REPOSITORY;
  2340. if ( $CLEAN || &IsEmptyHash( $TARGETS ) ) {
  2341. $mapref = $REPOSITORY;
  2342. } # if
  2343. # MAKEFILE file handler is the default.
  2344. print "all:\ \\\n\t" ;
  2345. $i = 0;
  2346. @alltargets = map({$total+=scalar(keys %{$mapref->{$_}});$_;} sort keys %{$mapref});
  2347. if ($alltargets[$i]=~/_DIRS/i) {
  2348. print "_DIRS ";
  2349. $ACCELERATE and print "_COMPDIR ";
  2350. $i++;
  2351. }
  2352. $SECTIONS = $DEFAULT_SECTIONS if ($SECTIONS!~/\d+/);
  2353. $SECTIONS = 1 if ($#alltargets < $SECTIONS);
  2354. $chars=length($SECTIONS);
  2355. $steps=$total / $SECTIONS;
  2356. for ($j = 1; $j <= $SECTIONS; $j++) {
  2357. printf("${SECTIONNAME}\%0${chars}d\%0${chars}d ", $SECTIONS, $j);
  2358. }
  2359. for ($j = 0, $k = 0;$i <= $#alltargets; $i++) {
  2360. for $destfile ( sort keys %{$mapref->{$alltargets[$i]}} ) {
  2361. if ($j <= $k++) {
  2362. $j += $steps;
  2363. printf("\n\n${SECTIONNAME}\%0${chars}d\%0${chars}d: ", $SECTIONS, $j / $steps);
  2364. }
  2365. my $quot_ = $destfile =~ /\s/ ? "\"": undef;
  2366. print "\ \\\n\t${quot_}".
  2367. &RevEnvVars($alltargets[$i]."\\".$destfile).
  2368. "${quot_}";
  2369. } # for
  2370. } # for
  2371. print " \n\n";
  2372. return;
  2373. } # WriteAllTarget
  2374. # write %COMPDIR_COMMAND
  2375. # lines
  2376. # usage: &WriteCompdirTarget()
  2377. sub WriteCompdirTarget{
  2378. my @TargetList = sort keys(%COMPDIR_COMMAND);
  2379. print &RevEnvVars(join("\ \\\n", "_COMPDIR:", map {"\t$_"} @TargetList)),
  2380. "\n",
  2381. join("\n", map {"\tlogerr \"".&RevEnvVars($COMPDIR_COMMAND{$_})."\""} @TargetList),
  2382. "\n",
  2383. &RevEnvVars(join("\n", map {"\t$REATTR_COMMAND{$_}"} keys(%REATTR_COMMAND))),
  2384. "\n\n";
  2385. }
  2386. sub WriteCompdir{
  2387. my $listFileTemplate = shift;
  2388. my $mapref = shift;
  2389. local $\ = "\n";
  2390. # my $DEBUG = 1;
  2391. my $cnt = 0;
  2392. my $listfile;
  2393. my $compdir_command;
  2394. my $srcfile;
  2395. my $key = {"DestPath" => undef,
  2396. "DestFile" => undef};
  2397. $listFileTemplate =~ s|\.(\w+)$|.XXX.$1| unless $listFileTemplate =~ /XXX\.\w+$/;
  2398. foreach my $srcpath ( sort keys %{$mapref} ) {
  2399. $cnt++;
  2400. # $DEBUG and print STDERR $cnt ."\=\>". $srcpath."\n";
  2401. $listfile = $listFileTemplate;
  2402. $listfile =~ s/XXX/sprintf("%03d", $cnt)/ie;
  2403. # add unique number
  2404. # before the extension.
  2405. #
  2406. open LISTFILE, ">".$listfile;
  2407. select LISTFILE;
  2408. foreach my $file ( sort keys %{$mapref->{$srcpath}} ) {
  2409. $srcfile = $file;
  2410. # $DEBUG and
  2411. # print STDERR $srcfile;
  2412. print $srcfile;
  2413. }
  2414. close LISTFILE;
  2415. $key->{"DestPath"} = $srcpath;
  2416. $key->{"DestFile"} = $srcfile;
  2417. my $cmdref= &GetFieldVal( $mapref, $key, "Cmd" );
  2418. $compdir_command = "\$\(COMPDIR\) /m:$listfile @$cmdref",
  2419. $COMPDIR_COMMAND{$listfile} = $compdir_command;
  2420. $REATTR_COMMAND{$listfile} = join(" ", "\$\(REATTR\)", $cmdref->[1], $listfile)
  2421. if &GetFieldVal( $mapref,$key, "Attribute");
  2422. ########
  2423. ## $COMPDIR_COMMAND{$listfile} .= "\"\n\t\$\(REATTR\) ". $cmdref->[1] . " \"$listfile"
  2424. ## if &GetFieldVal( $mapref,$key, "Attribute");
  2425. #
  2426. # $DEBUG or next;
  2427. # select STDERR;
  2428. # print "$compdir_command\n";
  2429. }
  2430. 1;
  2431. }
  2432. #
  2433. # FilterOpType ([1,2])
  2434. # arguments: currently unused.
  2435. #
  2436. sub FilterOpType{
  2437. my $mapref = shift;
  2438. my $knownOpType = shift;
  2439. my %knownOpType = map {$_=>$_} @$knownOpType;
  2440. $knownOpType = \%knownOpType;
  2441. my %key = ();
  2442. foreach my $destpath (keys %{$mapref} ) {
  2443. foreach my $destfile ( keys %{$mapref->{$destpath}}) {
  2444. my $docopy = 0;
  2445. my $cmdref = $mapref->{$destpath}->{$destfile}->{Cmd};
  2446. for ( my $i=1; $i < @{$cmdref}; $i++ ) {
  2447. $docopy = 1 unless $cmdref->[$i] !~ /\S/;
  2448. # $DEBUG and print STDERR "\"".$cmdref->[$i]."\"\n" if $docopy;
  2449. }
  2450. $key{DestPath} = $destpath;
  2451. $key{DestFile} = $destfile;
  2452. $docopy
  2453. or &DeleteKey( $mapref, \%key );
  2454. # $docopy
  2455. # of
  2456. # &SetField( $REPOSITORY, $keyref, "OpType", "-1" );
  2457. }
  2458. }
  2459. }
  2460. # WriteFileCmds
  2461. # For every file, write its dependency block in the makefile.
  2462. # Input & Output
  2463. # none
  2464. # Usage
  2465. # &WriteFileCmds();
  2466. sub WriteFileCmds {
  2467. # Table (reference): REPOSITORY or CMD_REPOSITORY
  2468. my $mapref;
  2469. # Counter
  2470. my $i;
  2471. # Reference to the Cmd field
  2472. my $cmdref;
  2473. # Destinationa path, destination file
  2474. my( $destpath, $destfile );
  2475. # Use data from CMD_REPOSITORY or REPOSITORY,
  2476. # depending if SYSGEN is called or not incrementally,
  2477. # with or without targets.
  2478. $mapref = $CMD_REPOSITORY;
  2479. if ( $CLEAN || &IsEmptyHash( $TARGETS ) ) {
  2480. $mapref = $REPOSITORY;
  2481. } # if
  2482. # Write file-by-file description blocks
  2483. for $destpath ( sort keys %{$mapref} ) {
  2484. for $destfile ( sort keys %{$mapref->{$destpath}} ) {
  2485. # Print {Cmd} field
  2486. print
  2487. grep {/\t?\S/} @{$mapref->{$destpath}->{$destfile}->{Cmd}};
  2488. print "\n";
  2489. } # for
  2490. } # for
  2491. return;
  2492. } # WriteFileCmds
  2493. # CmdCompare
  2494. # Given a key, compare the {Cmd} field from REPOSITORY to the {Cmd} CMD_REPOSITORY field.
  2495. # Input
  2496. # repository type key (reference)
  2497. # Output
  2498. # 0 if commands compare OK
  2499. # 1 if commands are different
  2500. # Usage
  2501. # $is_different = &CmdCompare( \%key );
  2502. sub CmdCompare {
  2503. my( $keyref ) = @_;
  2504. # Cmd fields
  2505. my( $repref, $cmdref );
  2506. $repref = &GetFieldVal( $REPOSITORY, $keyref, "Cmd" );
  2507. $cmdref = &GetFieldVal( $CMD_REPOSITORY, $keyref, "Cmd" );
  2508. lc("@$repref") cmp lc("@$cmdref");
  2509. } # CmdCompare
  2510. # RecordToCmd
  2511. # Converts one entry from REPOSITORY to a set of cmd instructions,
  2512. # stored in the REPOSITORY as well
  2513. # Input
  2514. # key identifying the REPOSITORY entry (reference)
  2515. # Output
  2516. # none
  2517. # Usage
  2518. # &RecordToCmd( $keyref );
  2519. sub RecordToCmd {
  2520. #Input
  2521. my $keyref = shift;
  2522. my $key = &GetFieldVal( $REPOSITORY, $keyref, "OpType" );
  2523. my $status = (defined( $LOCOP_BASED->{"$key"})) ?
  2524. $LOCOP_BASED->{"$key"}->($keyref) :
  2525. &FatalError(3011,
  2526. sprintf "$keyref->{DestPath}\\$keyref->{DestFile} %s",
  2527. &GetFieldVal( $REPOSITORY, $keyref, "OpType") );
  2528. # (for ex. because DeleteKey was called by GenBgnCmd)
  2529. if ( ! &IsRepositKey( $REPOSITORY, $keyref ) ) {
  2530. return;
  2531. } # if
  2532. if ( ! &GetMacro( "ntdebug" ) && # only retail builds
  2533. &GetMacro( "GENERATE_MUI_DLLS" ) # and only when requested
  2534. ) {
  2535. &GenMUICmd( $keyref );
  2536. } # if
  2537. } # RecordToCmd
  2538. # GetDynGata
  2539. # get all REPOSITORY fiels in one call instead of calling for each one.
  2540. #
  2541. sub GetDynData {
  2542. my $repository = shift;
  2543. my $key = shift;
  2544. my $dyndata = $repository->{lc($key->{"DestPath"})}->{lc($key->{"DestFile"})};
  2545. map {$dyndata->{$_} = "-" unless defined($dyndata->{$_})}
  2546. @REPOSITORY_TEMPLATE;
  2547. # cannot use "keys (%$dyndata);"!
  2548. $dyndata;
  2549. }
  2550. # GenXcopy
  2551. # For the given entry from REPOSITORY, generates the xcopy commands.
  2552. # Input
  2553. # key identifying the REPOSITORY entry
  2554. # Output
  2555. # none
  2556. # Usage
  2557. # &GetXcopy( $keyref );
  2558. sub GenXcopy {
  2559. return if $SYNTAX_CHECK_ONLY;
  2560. my( $keyref ) = @_;
  2561. # dbgline, pdbline, file line, dependency line
  2562. my( $dbgline, $pdbline, $symline, $fline, $dline );
  2563. # Paths and filenames for the symbols
  2564. my $dyndata = &GetDynData($REPOSITORY, $keyref);
  2565. my ($srcext, $srcpdb, $srcdbg, $srcsym, $dstext, $dstpdb, $dstdbg, $dstsym) =
  2566. &NewImageToSymbol($dyndata->{"SrcFile"} ,
  2567. $dyndata->{"DestFile"},
  2568. $dyndata->{"SrcPath"} ,
  2569. $dyndata->{"SrcDbg" } );
  2570. my( $srcsymfull, $dstsymfull ) = &SetSymPaths(
  2571. $dyndata->{"SrcDbg"} ,
  2572. $srcext,
  2573. $srcpdb,
  2574. $srcdbg,
  2575. $srcsym,
  2576. $dyndata->{"DestDbg"},
  2577. $dstext );
  2578. # Dependency line
  2579. $dline = $DLINE;
  2580. my $quot_ = $dyndata->{"SrcFile"} =~ /\s/ ? "\"" : undef;
  2581. my $qquot_ = "\\\"" if $quot_;
  2582. $dline =~ s/TARGET\b/${quot_}$dyndata->{"DestPath"}\\$dyndata->{"DestFile"}${quot_}/;
  2583. $dline =~ s/DEPEND/${quot_}$dyndata->{"SrcPath"}\\$dyndata->{"SrcFile"}${quot_}/;
  2584. # $dline =~ s/$qquot_$quot_/$qquot_/g;
  2585. $dline = &RevEnvVars($dline);
  2586. # Generate the copy commands for the symbols files (dbg, pdb, and sym)
  2587. $pdbline = &MakeXcopyCmd( $dyndata->{"SrcFile"},
  2588. $srcsymfull,
  2589. $srcpdb,
  2590. $dstsymfull,
  2591. $dstpdb , undef, 1 );
  2592. $dbgline = &MakeXcopyCmd( $dyndata->{"SrcFile"},
  2593. $srcsymfull,
  2594. $srcdbg,
  2595. $dstsymfull,
  2596. $dstdbg , undef, 1 );
  2597. $symline = &MakeXcopyCmd( $dyndata->{"SrcFile"},
  2598. $srcsymfull,
  2599. $srcsym,
  2600. $dstsymfull,
  2601. $dstsym , undef, 1 );
  2602. # Generate binary's xcopy command
  2603. $fline = &MakeXcopyCmd( $dyndata->{"SrcFile" } ,
  2604. $dyndata->{"SrcPath" } ,
  2605. $dyndata->{"SrcFile" } ,
  2606. $dyndata->{"DestPath"} ,
  2607. $dyndata->{"DestFile"} ,
  2608. $qquot_ );
  2609. # Write the dependency line
  2610. $DEBUG and print STDERR $dline."\n" if $quot_;
  2611. &PushFieldVal( $REPOSITORY, $keyref, "Cmd", "$dline\n" );
  2612. $MAKEDIR{$dyndata->{"DestPath"}} = 1;
  2613. &PushFieldVal( $REPOSITORY, $keyref, "Cmd", "\t$fline\n" );
  2614. if ( $dbgline || $pdbline || $symline ) {
  2615. if ( $dstsymfull ne $dyndata->{"DestDbg"}) {
  2616. $MAKEDIR{$dstsymfull} = 1;
  2617. } # if
  2618. } # if
  2619. if ( $dbgline ) {
  2620. &PushFieldVal( $REPOSITORY, $keyref, "Cmd", "\t$dbgline\n" );
  2621. } # if
  2622. if ( $pdbline ) {
  2623. &PushFieldVal( $REPOSITORY, $keyref, "Cmd", "\t$pdbline\n" );
  2624. } # if
  2625. if ( $symline ) {
  2626. &PushFieldVal( $REPOSITORY, $keyref, "Cmd", "\t$symline\n" );
  2627. } # if
  2628. return;
  2629. } # GenXcopy
  2630. # SetSymPaths
  2631. # Set the source and destination paths for the symbols
  2632. # Can be the same as set in the sysfile (BBT case)
  2633. # or can be the subdir with the the filename externsion.
  2634. # The decision is made based on the pdb file existence.
  2635. # Input
  2636. # SrcDbg field value
  2637. # source file extension
  2638. # source pdb file
  2639. # source dbg file
  2640. # source sym file
  2641. # DestDbg field value
  2642. # destination file extension
  2643. # Output
  2644. # path to the source symbol file
  2645. # path to the destination symbols file
  2646. sub SetSymPaths {
  2647. my( $srcpath, $srcext, $srcpdb, $srcdbg, $srcsym, $dstpath, $dstext ) = @_;
  2648. return ( $srcpath, $dstpath ) unless $SETSYMPATHS;
  2649. # Output
  2650. my( $srcsymfull, $dstsymfull) = ( $srcpath, $dstpath );
  2651. # Verify if the file exists in extension subdir
  2652. if ( $srcpath ne "-" &&
  2653. $dstpath ne "-" &&
  2654. ( -e "$srcpath\\$srcext\\$srcpdb" || -e "$srcpath\\$srcext\\$srcdbg" || -e "$srcpath\\$srcext\\$srcsym" ) ) {
  2655. $srcsymfull .= "\\$srcext";
  2656. $dstsymfull .= "\\$dstext";
  2657. }
  2658. return ( $srcsymfull, $dstsymfull );
  2659. } # SetSymPaths
  2660. # MakeXcopyCmd
  2661. # Generates an xcopy command for copying a given file.
  2662. # Input
  2663. # source path
  2664. # source file
  2665. # dest path
  2666. # dest file
  2667. # Output
  2668. # xcopy command
  2669. # Usage
  2670. # $xcopy = &MakeXcopyCmd( "f:\\nt\\usa\\\binaries", "advapi.dll",
  2671. # "f:\\nt\\jpn\\relbins", "advapi.dll" );
  2672. sub MakeXcopyCmd {
  2673. my( $binary, $srcpath, $srcfile, $dstpath, $dstfile, $qquot_, $forceDoPlainCopy ) = @_;
  2674. # last argument : optional
  2675. # print STDERR $binary , "\n" if $forceDoPlainCopy;
  2676. my $result;
  2677. # for $CLEAN build, use COMPDIR insdead of copy when possible
  2678. # if $dstfile identical to $srcfile,
  2679. # compare $dstpath with $srcpath, the relative ones.
  2680. # use
  2681. # %MAP_COMPDIR_EXT
  2682. # to apply compdir.exe without doing more checks.
  2683. my $APPLY_COMPDIR = 0;
  2684. my $IS_PLAIN_COPY = 0;
  2685. $forceDoPlainCopy = 1 if $srcfile =~ /\s/;
  2686. if ($ACCELERATE and
  2687. $CLEAN and !$forceDoPlainCopy){
  2688. my $chkext=$srcfile;
  2689. $chkext=~ s/.*\.(\w+)$/.\L$1\E/g;
  2690. # print STDERR $srcfile, "\n" and
  2691. $APPLY_COMPDIR = 1 if defined($MAP_COMPDIR_EXT{$chkext});
  2692. }
  2693. if ($ACCELERATE and
  2694. $CLEAN and
  2695. !$forceDoPlainCopy and
  2696. $dstfile =~ /[^\-]/ and
  2697. $dstpath =~ /[^\-]/ ){
  2698. my $chkpath=$srcpath;
  2699. $chkpath=~ s/^$SRCBASE/$DSTBASE/eg;
  2700. # fill the %COMPDIR here.
  2701. if (lc($dstpath) eq lc($chkpath) and -e "$srcpath\\$srcfile") {
  2702. $IS_PLAIN_COPY = 1;
  2703. }
  2704. if (($APPLY_COMPDIR or $IS_PLAIN_COPY) and ($dstfile eq $srcfile)){
  2705. # Note that the the field names "DestPath" and "DestFile"
  2706. # are hard-coded in the definition of the keyref.
  2707. # The fields are used here but the information stored
  2708. # is the SrcPath+DestPath/SrcFile rather then DestPath/DestFile
  2709. my $keyref = {"DestPath"=> "$srcpath/$dstpath", "DestFile"=> $srcfile};
  2710. &SetField( \%COMPDIR, $keyref, "DestPath", "$srcpath/$dstpath" );
  2711. &SetField( \%COMPDIR, $keyref, "DestFile", $srcfile );
  2712. &SetField( \%COMPDIR, $keyref, "Attribute", !$IS_PLAIN_COPY );
  2713. &PushFieldVal( \%COMPDIR, $keyref, "Cmd", "$srcpath" );
  2714. &PushFieldVal( \%COMPDIR, $keyref, "Cmd", "$dstpath" );
  2715. # Debugging messed paths!
  2716. # Comment out when not debug to shorten the execution time.
  2717. # my $DEBUG = 1;
  2718. # my $mapref= \%COMPDIR;
  2719. # $APPLY_COMPDIR and $DEBUG and
  2720. # print STDERR
  2721. # $srcpath, "\t", $srcfile, "\t\"",
  2722. # join (" ", @{$mapref->{lc($srcpath)}->{lc($srcfile)}->{Cmd}}), "\"\n"
  2723. # my $mapref = \%COMPDIR;
  2724. # my $cmdref= &GetFieldVal( $mapref, $keyref, "Cmd" );
  2725. # print STDERR "@$cmdref\n";
  2726. &SetField( \%COMPDIR, $keyref, "OpType" , "3" );
  2727. return undef;
  2728. # remove the 'old-fashioned' rule?
  2729. }
  2730. }
  2731. if (( $dstpath eq "-") || ($srcpath eq "-" ) || !(-e "$srcpath\\$srcfile")){
  2732. $result = "";
  2733. if ($PARSESYMBOLCD && defined($default{$binary})){
  2734. my %hints = %{$default{$binary}};
  2735. my $ext = $1 if ($dstfile=~/\.(\w+)$/);
  2736. if ($ext && $hints{$ext}){
  2737. my ($sympath, $symname ) =
  2738. ($hints{$ext}=~/^(.*)\\([^\\]+)$/);
  2739. my $srchead= "$(_NTTREE)";
  2740. my $dsthead= &RevEnvVars(&GetMacro("DST"));
  2741. $sympath = &RevEnvVars($srcpath);
  2742. $result =
  2743. "logerr \"\$\(COPY\) ${qquot_}$srchead\\$sympath\\$symname${qquot_} ".
  2744. "${qquot_}$dsthead\\$sympath\\${qquot_}\"";
  2745. $DEBUG and
  2746. print STDERR " added default for $binary: \"", $ext ,"\"\n";
  2747. }
  2748. }
  2749. }
  2750. else{
  2751. my $dstname = ($dstfile ne $srcfile) ? $dstfile: "";
  2752. $srcpath = &RevEnvVars($srcpath);
  2753. $dstpath = &RevEnvVars($dstpath);
  2754. $result = ($forceDoPlainCopy) ?"logerr \"\$\(COPY\) ${qquot_}$srcpath\\$srcfile${qquot_} ${qquot_}$dstpath\\$dstname${qquot_}\"" :
  2755. "logerr \"\$\(COPY\) ${qquot_}\$\*\*${qquot_} ${qquot_}\$\(\@D\)${qquot_}\"";
  2756. $result = "logerr \"\$\(COPY\) ${qquot_}\$\*\*${qquot_} ${qquot_}\$\@${qquot_}\"" if $dstfile ne $srcfile;
  2757. }
  2758. $result;
  2759. } # MakeXcopyCmd
  2760. # GenLocCmd
  2761. # For the given entry from REPOSITORY, generates the bingen commands.
  2762. # Input
  2763. # key identifying the REPOSITORY entry (reference
  2764. # array of commands where the output is added (reference)
  2765. # Output
  2766. # none
  2767. # Usage
  2768. # &GenLocCmd( $keyref );
  2769. sub GenLocCmd {
  2770. return if $SYNTAX_CHECK_ONLY;
  2771. my $keyref = shift;
  2772. my $TokFile = &GetFieldVal( $REPOSITORY, $keyref, "TokFile" );
  2773. map {$TokFile=~/$_/i && $EXT_BASED->{$_}->($keyref)} keys(%$EXT_BASED);
  2774. } # GenLocCmd
  2775. # GenBgnCmd
  2776. #
  2777. #
  2778. sub GenBgnCmd {
  2779. my( $keyref ) = @_;
  2780. # pdb line, bingen line, dependency line
  2781. my( $pdbline, $bgnline, $symline, $dbgline, $dline ) = ("", "", "", "", "");
  2782. my $symcmd = "";
  2783. # Symbol paths and filenames
  2784. my $dyndata = &GetDynData($REPOSITORY, $keyref);
  2785. my ($srcext, $srcpdb, $srcdbg, $srcsym, $dstext, $dstpdb, $dstdbg, $dstsym) =
  2786. &NewImageToSymbol($dyndata->{"SrcFile" },
  2787. $dyndata->{"DestFile"},
  2788. $dyndata->{"SrcPath" },
  2789. $dyndata->{"SrcDbg" });
  2790. my( $symsrcfull, $symdstfull ) =
  2791. &SetSymPaths( $dyndata->{"SrcDbg" },
  2792. $srcext,
  2793. $srcpdb,
  2794. $srcdbg,
  2795. $srcsym,
  2796. $dyndata->{"DestDbg" },
  2797. $dstext );
  2798. $pdbline = &MakeXcopyCmd( $dyndata->{"SrcFile"}, $symsrcfull, $srcpdb, $symdstfull, $dstpdb, undef, 1 );
  2799. $symline = &MakeXcopyCmd( $dyndata->{"SrcFile"}, $symsrcfull, $srcsym, $symdstfull, $dstsym, undef, 1 );
  2800. $dbgline = &MakeXcopyCmd( $dyndata->{"SrcFile"}, $symsrcfull, $srcdbg, $symdstfull, $dstdbg, undef, 1 );
  2801. # -a | -r | -ai | -ir switch
  2802. # localization operation type ( append or replace )
  2803. my $bgntype = &GetLocOpType( $dyndata->{"LocOp" } );
  2804. # -m switch (specific to bingen; different for rsrc)
  2805. $symcmd = &SetBgnSymSw( $symsrcfull, $srcdbg, $symdstfull, $dstdbg );
  2806. # -i switch (specific for bingen)
  2807. my $icodes = &GetBgnICodesSw( $bgntype, &GetMacro( "ILANGUAGE" ) );
  2808. # multitoken (bingen)
  2809. my $multitok = ""; # &GetBgnMultitok( $keyref, $bgntype );
  2810. # the unlocalized version of the token must exist as well
  2811. # if ( substr($bgntype,-2) eq "-a" ) {
  2812. # if ( ! -e $multitok ) {
  2813. # &Error( 2015, sprintf( "\n%s\\%s",
  2814. # $dyndata->{"TokPath" },
  2815. # $dyndata->{"TokFile" } ) );
  2816. # &DeleteKey( $REPOSITORY, $keyref );
  2817. # return;
  2818. # } # if
  2819. # } # if
  2820. # Sets the bingen command
  2821. map {$dyndata->{$_}=
  2822. &RevEnvVars($dyndata->{$_})}
  2823. qw (SrcPath DestPAth TokPath DestPath CustRc);
  2824. # here we must avoid macros when additional dependency or multiple tokens.
  2825. my $LocOp = sprintf ("%s %s %s", $BGNSWS{$dyndata->{"BgnSw" }}, $icodes, $bgntype);
  2826. $bgnline =
  2827. ($multitok =~ /\S/ || $dyndata->{"CustRc" })?
  2828. $bgnline = sprintf "logerr \"\$\(BINGEN\) %s -p \$\(ACP\) -o \$\(PriLangID\) \$\(SubLangID\) %s %s\\%s %s %s\\%s \$\@\"",
  2829. $symcmd,
  2830. $LocOp,
  2831. $dyndata->{"SrcPath" },
  2832. $dyndata->{"SrcFile" },
  2833. $multitok,
  2834. $dyndata->{"TokPath" },
  2835. $dyndata->{"TokFile" }
  2836. :
  2837. GetMacro("BINGEN_COMMAND");
  2838. $bgnline =~ s/\\\$\\\(LocOp\\\)/$LocOp/g;
  2839. $bgnline =~ s/\\\$\\\((\w+)\\\)/\$($1)/g;
  2840. # nmake special macro expansion
  2841. $bgnline =~ s/\\\$\\\(([\*\@\<\>]\w)\\\)/\$($1)/gmi;
  2842. # Dependency line
  2843. $dline = sprintf "%s\\%s: %s\\%s %s\\%s %s%s\n",
  2844. $dyndata->{"DestPath" },
  2845. $dyndata->{"DestFile" },
  2846. $dyndata->{"SrcPath" },
  2847. $dyndata->{"SrcFile" },
  2848. $dyndata->{"TokPath" },
  2849. $dyndata->{"TokFile" },
  2850. $dyndata->{"CustRc" };
  2851. $multitok;
  2852. $dline = &RevEnvVars($dline);
  2853. &PushFieldVal( $REPOSITORY, $keyref, "Cmd", "$dline" );
  2854. # Dependency line done
  2855. # Description block
  2856. $MAKEDIR{$dyndata->{"DestPath" }} = 1;
  2857. if ( $multitok || $pdbline || $symline ) {
  2858. if ( $symdstfull ne $dyndata->{"DestDbg" } ) {
  2859. $MAKEDIR{$symdstfull}=1;
  2860. } # if
  2861. } # if
  2862. &PushFieldVal( $REPOSITORY, $keyref, "Cmd", sprintf( "\t$bgnline\n" ) );
  2863. if ( $pdbline ) {
  2864. &PushFieldVal( $REPOSITORY, $keyref, "Cmd", sprintf( "\t$pdbline\n" ) );
  2865. } # if
  2866. if ( $symline ) {
  2867. &PushFieldVal( $REPOSITORY, $keyref, "Cmd", sprintf( "\t$symline\n" ) );
  2868. } # if
  2869. if ( $dbgline && $symcmd!~/\W/) {
  2870. &PushFieldVal( $REPOSITORY, $keyref, "Cmd", sprintf( "\t$dbgline\n" ) );
  2871. } # if
  2872. # Optional resource-only DLL generation
  2873. # Description block done
  2874. return;
  2875. } # GenBgnCmd
  2876. sub GenLcxCmd{
  2877. return if $SYNTAX_CHECK_ONLY;
  2878. my $keyref = shift;
  2879. my $dyndata = &GetDynData($REPOSITORY, $keyref);
  2880. map {$dyndata->{$_} = &RevEnvVars($dyndata->{$_})}
  2881. qw (SrcPath DestPAth TokPath CustRc DestPath);
  2882. my $lcxline= ""; # lcx command line
  2883. my $dline = "";
  2884. my ($srcext,
  2885. $srcpdb,
  2886. $srcdbg,
  2887. $srcsym,
  2888. $dstext,
  2889. $dstpdb,
  2890. $dstdbg,
  2891. $dstsym) =
  2892. &NewImageToSymbol($dyndata->{"SrcFile" },
  2893. $dyndata->{"DestFile"},
  2894. $dyndata->{"SrcPath" },
  2895. $dyndata->{"SrcDbg" });
  2896. # lcx command line
  2897. my $lcxop = "";
  2898. # -l switch
  2899. $lcxline = &GetMacro("GENERATE_COMMAND");
  2900. my $a = $dyndata->{"SrcPath" }."\\".$dyndata->{"SrcFile" };
  2901. my $b = $dyndata->{"TokPath" }."\\". $dyndata->{"TokFile" };
  2902. my $c = $dyndata->{"TokPath" };
  2903. my $d = $dyndata->{"SrcPath" };
  2904. my $e = $dyndata->{"TokFile" };
  2905. my $f = $dyndata->{"SrcFile" };
  2906. $lcxline =~ s|\s+\&\s*\_\s*|\n\t|;
  2907. # timing improvements due to the pre-parsed US LCX files:
  2908. # This is a fix. Possibly a solution needs to be
  2909. # more robust.
  2910. my $r = $b;
  2911. $r =~ s/\.\w+$//;
  2912. $lcxline =~ s|\\\$\\\(\*\*\[0\]R\\\)|$r|gi;
  2913. $lcxline =~ s|\\\$\\\(\*\*\[1\]R\\\)|$r|gi;
  2914. $lcxline =~ s|\\\$\\\(\*\*\[0\]D\\\)|$d|gi;
  2915. $lcxline =~ s|\\\$\\\(\*\*\[1\]D\\\)|$c|gi;
  2916. $lcxline =~ s|\\\$\\\(\*\*\[0\]F\\\)|$f|gi;
  2917. $lcxline =~ s|\\\$\\\(\*\*\[1\]F\\\)|$e|gi;
  2918. $lcxline =~ s|\$\*\*\[0\]|$a|g;
  2919. $lcxline =~ s|\$\*\*\[1\]|$b|g;
  2920. $lcxline =~ s/\\\$\\\(LCX_OP\\\)/$lcxop/gm;
  2921. $lcxline =~ s/\\\$\\\((\w+)\\\)/\$($1)/gmi;
  2922. # nmake special macro expansion
  2923. $lcxline =~ s/\\\$\\\(([\*\@\<\>]\w)\\\)/\$($1)/gmi;
  2924. # Dependency line
  2925. $dline = sprintf "%s\\%s: %s\\%s %s\\%s\n",
  2926. $dyndata->{"DestPath"},
  2927. $dyndata->{"DestFile"},
  2928. $dyndata->{"SrcPath" },
  2929. $dyndata->{"SrcFile" },
  2930. $dyndata->{"TokPath" },
  2931. $dyndata->{"TokFile" };
  2932. # Dependency line done
  2933. &PushFieldVal( $REPOSITORY, $keyref, "Cmd", &RevEnvVars($dline));
  2934. $MAKEDIR{$dyndata->{"DestPath"}} = 1;
  2935. # Description block
  2936. &PushFieldVal( $REPOSITORY, $keyref, "Cmd", sprintf( "\t$lcxline\n" ) );
  2937. return;
  2938. }
  2939. sub GenRsrcCmd {
  2940. return if $SYNTAX_CHECK_ONLY;
  2941. my( $keyref ) = @_;
  2942. my $dyndata = &GetDynData($REPOSITORY, $keyref);
  2943. my $pdbline = ""; # copy pdb line
  2944. my $dbgline = ""; # copy dbg line
  2945. my $symline = ""; # copy sym line
  2946. my $binline = ""; # copy binary line
  2947. my $rsrcline= ""; # rsrc command line
  2948. my $dline = "";
  2949. my ($srcext,
  2950. $srcpdb,
  2951. $srcdbg,
  2952. $srcsym,
  2953. $dstext,
  2954. $dstpdb,
  2955. $dstdbg,
  2956. $dstsym) =
  2957. &NewImageToSymbol($dyndata->{"SrcFile" },
  2958. $dyndata->{"DestFile"},
  2959. $dyndata->{"SrcPath" },
  2960. $dyndata->{"SrcDbg" });
  2961. my( $symsrcfull, $symdstfull ) =
  2962. &SetSymPaths( $dyndata->{"SrcDbg" },
  2963. $srcext,
  2964. $srcpdb,
  2965. $srcdbg,
  2966. $srcsym,
  2967. $dyndata->{"DestDbg"},
  2968. $dstext );
  2969. $pdbline = &MakeXcopyCmd( $dyndata->{"SrcFile"}, $symsrcfull, $srcpdb, $symdstfull, $dstpdb, undef, 1 );
  2970. $dbgline = &MakeXcopyCmd( $dyndata->{"SrcFile"}, $symsrcfull, $srcdbg, $symdstfull, $srcdbg, undef, 1 );
  2971. $symline = &MakeXcopyCmd( $dyndata->{"SrcFile"}, $symsrcfull, $srcsym, $symdstfull, $srcsym, undef, 1 );
  2972. # in fact, it is necessary to fix the makexcopycmd
  2973. # $binline = &MakeXcopyCmd( $dyndata->{"SrcFile" },
  2974. # $dyndata->{"SrcPath" },
  2975. # $dyndata->{"SrcFile" },
  2976. # $dyndata->{"DestPath"},
  2977. # $dyndata->{"DestFile"},
  2978. # undef, 1);
  2979. map {$dyndata->{$_}=
  2980. &RevEnvVars($dyndata->{$_})}
  2981. qw (SrcPath DestPAth TokPath CustRc DestPath);
  2982. # -a or -r switch
  2983. # localization operation type ( append or replace )
  2984. my $rsrctype = &GetLocOpType( $dyndata->{"LocOp" } );
  2985. # -l switch
  2986. my $langsw = &GetMacro("LCID" );
  2987. $langsw =~ s/0x0//;
  2988. $langsw =~ s/0x//;
  2989. $langsw = sprintf "-l %s", $langsw;
  2990. # -s switch
  2991. my $symsw = &SetRsrcSymSw( $symsrcfull, $srcdbg, $symdstfull, $dstdbg );
  2992. # rsrc command line
  2993. $rsrcline = GetMacro("RSRC_COMMAND");
  2994. my $a = $dyndata->{"SrcPath" }."\\".$dyndata->{"SrcFile" };
  2995. my $b = $dyndata->{"TokPath" }."\\". $dyndata->{"TokFile" };
  2996. $rsrcline =~ s|\s+\&\s*\_\s*|\n\t|;
  2997. $rsrcline =~ s|\$\*\*\[0\]|$a|g;
  2998. $rsrcline =~ s|\$\*\*\[1\]|$b|g;
  2999. $rsrcline =~ s/\\\$\\\(LocOp\\\)/$rsrctype/gm;
  3000. $rsrcline =~ s/\\\$\\\((\w+)\\\)/\$($1)/gm;
  3001. # nmake special macro expansion
  3002. $rsrcline =~ s/\\\$\\\(([\*\@\<\>]\w)\\\)/\$($1)/gmi;
  3003. # $rsrcline = sprintf "logerr \"rsrc \$\@ %s %s\\%s %s %s \"",
  3004. # $rsrctype,
  3005. # $dyndata->{"TokPath" },
  3006. # $dyndata->{"TokFile" },
  3007. # $langsw,
  3008. # $symsw;
  3009. #
  3010. # Dependency line
  3011. $dline = sprintf "%s\\%s: %s\\%s %s\\%s\n",
  3012. $dyndata->{"DestPath"},
  3013. $dyndata->{"DestFile"},
  3014. $dyndata->{"SrcPath" },
  3015. $dyndata->{"SrcFile" },
  3016. $dyndata->{"TokPath" },
  3017. $dyndata->{"TokFile" };
  3018. $dline = &RevEnvVars($dline);
  3019. &PushFieldVal( $REPOSITORY, $keyref, "Cmd", "$dline" );
  3020. # Dependency line done
  3021. # Description block
  3022. $MAKEDIR{$dyndata->{"DestPath"}} = 1;
  3023. if ( $dbgline || $pdbline || $symline) {
  3024. if ( $symdstfull ne $dyndata->{"DestDbg" } ) {
  3025. $MAKEDIR{$symdstfull}=1;
  3026. } # if
  3027. } # if
  3028. if ( $pdbline ) {
  3029. &PushFieldVal( $REPOSITORY, $keyref, "Cmd", sprintf( "\t$pdbline\n" ) );
  3030. } # if
  3031. if ( $dbgline ) {
  3032. &PushFieldVal( $REPOSITORY, $keyref, "Cmd", sprintf( "\t$dbgline\n" ) );
  3033. } # if
  3034. if ( $symline ) {
  3035. &PushFieldVal( $REPOSITORY, $keyref, "Cmd", sprintf( "\t$symline\n" ) );
  3036. } # if
  3037. # &PushFieldVal( $REPOSITORY, $keyref, "Cmd", sprintf( "\t$binline\n" ) );
  3038. &PushFieldVal( $REPOSITORY, $keyref, "Cmd", sprintf( "\t$rsrcline\n" ) );
  3039. return;
  3040. } # GenRsrcCmd
  3041. sub GenMUICmd {
  3042. return if $SYNTAX_CHECK_ONLY;
  3043. # Optional resource-only DLL generation
  3044. my( $keyref ) = @_;
  3045. my $dyndata = &GetDynData($REPOSITORY, $keyref);
  3046. my $muiline = "";
  3047. my $_target = &GetMacro( "_target" ),
  3048. # Sets the muibld command
  3049. $muiline = GetMacro("MUI_COMMAND");
  3050. my $a = $dyndata->{"SrcPath" }."\\".$dyndata->{"SrcFile" };
  3051. my $b = $dyndata->{"TokPath" }."\\". $dyndata->{"TokFile" };
  3052. $muiline =~ s|\s+\&\s*\_\s*|\n\t|g;
  3053. $muiline =~ s|\$\*\*\[0\]|$a|g;
  3054. $muiline =~ s|\$\*\*\[1\]|$b|g;
  3055. $muiline =~ s/\\\$\\\((\w+)\\\)/\$($1)/gm;
  3056. # nmake special macro expansion
  3057. $muiline =~ s/\\\$\\\(([\*\@\<\>]\w)\\\)/\$($1)/gmi;
  3058. $DEBUG and
  3059. print STDERR "\"$muiline\"\n";
  3060. $MAKEDIR{sprintf("%s\\mui\\%s\\res",
  3061. &GetMacro("_NTBINDIR"),
  3062. &GetMacro("LANGUAGE"))} = 1;
  3063. $MAKEDIR{sprintf("%s\\mui\\%s\\$_target",
  3064. &GetMacro("_NTBINDIR"),
  3065. &GetMacro("LANGUAGE"))} = 1;
  3066. &PushFieldVal( $REPOSITORY, $keyref, "Cmd", sprintf( "\t$muiline\n" ) );
  3067. return;
  3068. } # GenMUICmd
  3069. # GetBgnCodePageSw
  3070. # Sets the code path bingen switch (-p)
  3071. # Input: code page value
  3072. # Output: bingen -p switch
  3073. sub GetBgnCodePageSw {
  3074. return "-p $_[0]" unless $_[0]=~/^\-$/;
  3075. } # GetBgnCodePageSw
  3076. # GetBgnICodesSw
  3077. # Sets the -i bingen switch, if needed
  3078. # ( primary language id and secondary language id for the input language )
  3079. # Input: bingen opetation type (-r or -a)
  3080. # the input language
  3081. # Output: -i <pri lang code> <sec lang code> in any of the following cases:
  3082. # * append token operation (bingen -a)
  3083. # * the input language is different from USA
  3084. # (another way of saying that ILANGUAGE is defined and is
  3085. # different from USA)
  3086. # Otherwise, return ""
  3087. sub GetBgnICodesSw {
  3088. my( $bgntype, $ilang ) = @_;
  3089. # Append operation => set -i
  3090. # Replace operation and the input language is not USA => -i
  3091. if ( $bgntype eq "-a" || ( $ilang && lc($ilang) ne "usa" ) ) {
  3092. if ( !$ilang ) {
  3093. $ilang = "USA";
  3094. } # if
  3095. return join(" ", "-i", map {$LANGCODES{$ilang}->{$_}} ("PriLangID", "SubLangID"));
  3096. } # if
  3097. return "";
  3098. } # GetBgnICodesSw
  3099. # SetBgnSymSw
  3100. # Generates the -m bingen switch
  3101. # Input: dbg source path, dbg file, dbg destination path, dbg file
  3102. # Output: string containing the -m bingen switch
  3103. sub SetBgnSymSw {
  3104. return " -m $_[0] $_[2]" if ($_[0] !~ /^\-$/ && $_[2] !~ /^\-$/ && -e "$_[0]\\$_[1]");
  3105. } # SetBgnSymSw
  3106. # SetRsrcSymSw
  3107. # Input
  3108. # dbg source path, dbg file, dbg destination path, dbg file
  3109. # Output
  3110. # the -s rsrc switch
  3111. sub SetRsrcSymSw {
  3112. return " -s $_[2]\\$_[3]" if ($_[0] !~/^\-$/ && $_[1] !~/^\-$/ && -e "$_[0]\\$_[1]");
  3113. } # SetRsrcSymSw
  3114. # GetLocOpType
  3115. # Sets the localization operation type ( replace or append )
  3116. # Input
  3117. # loc op type as found in the mapping file
  3118. # Output
  3119. # loc op type ( -a or -r )
  3120. sub GetLocOpType {
  3121. my $loctype = shift;
  3122. my ($locmatch,$locargs, $retstr);
  3123. $loctype |= &GetMacro( "LOC_OP_TYPE" );
  3124. if ($loctype){
  3125. ($locmatch,$locargs)=($loctype=~/^-([A-z]+)([^A-r]*)/);
  3126. $locargs=~s/,/ /g;
  3127. if (exists $LOCOPS{$locmatch}) {
  3128. ($retstr=$LOCOPS{$locmatch})=~s/\$opts/$locargs/e;
  3129. return $retstr;
  3130. }
  3131. }
  3132. "-r";
  3133. } # GetLocOpType
  3134. # GetBgnMultitok
  3135. # Sets the multitoken input parameter (bingen)
  3136. # Input: operation type and path to the input token files
  3137. sub GetBgnMultitok {
  3138. my( $keyref, $bgntype ) = @_;
  3139. # Language itokens
  3140. my $langpath;
  3141. # Tok path, tok file
  3142. my( $itokpath, $itokfile );
  3143. $itokpath = &GetFieldVal( $REPOSITORY, $keyref, "ITokPath" );
  3144. $itokfile = &GetFieldVal( $REPOSITORY, $keyref, "TokFile" );
  3145. if ( substr($bgntype,-2) ne "-a" ) { return ""; }
  3146. $langpath = sprintf "%s\\%s", $itokpath, &GetMacro( "LANGUAGE" );
  3147. if ( -e "$langpath\\$itokfile" ) {
  3148. return "$langpath\\$itokfile";
  3149. }
  3150. return "$itokpath\\$itokfile";
  3151. } # GetBgnMultitok
  3152. # GetBgnMultitok
  3153. # Returns the filenames for symbol files and the extension for directory
  3154. #
  3155. # Input: ($srcfile,$destfile,$srcpath)
  3156. # source file name,
  3157. # destination file name,
  3158. # source file path,
  3159. # source file symbol path.
  3160. #
  3161. # Output: ($srcext, $srcpdb, $srcdbg, $srcsym, $dstext, $dstpdb, $dstdbg, $dstsym)
  3162. # source file extension,
  3163. # source pdb file name,
  3164. # source dbg file name,
  3165. # source sym file name
  3166. # destination file extension,
  3167. # destination pdb file name,
  3168. # destination dbg file name,
  3169. # destination sym file name.
  3170. #
  3171. # Example: ($srcext, $srcpdb, $srcdbg, $srcsym, $dstext, $dstpdb, $dstdbg, $dstsym) =
  3172. # &NewImageToSymbol($SrcFile, $DestFile, $SrcPath, $srcDbg);
  3173. sub NewImageToSymbol {
  3174. my ($srcfile,$destfile,$srcpath, $srcdbg) = @_;
  3175. my @known=qw(exe dll sys ocx drv);
  3176. my $checkext = qq(pdb);
  3177. my $valid = 0;
  3178. my @ext = qw (pdb dbg sym);
  3179. map {$valid = 1 if ($srcfile =~/\.$_\b/i)} @known;
  3180. my @sym = ();
  3181. foreach my $name (($srcfile, $destfile)){
  3182. my $ext=$1 if ($name=~/\.(\w+)$/);
  3183. push @sym, $ext;
  3184. foreach my $newext (@ext){
  3185. my $sym = $name;
  3186. $sym =~ s/$ext$/$newext/;
  3187. if ($valid && $sym =~ /$checkext$/) {
  3188. # >link /dump /headers <binary> |(
  3189. # more? perl -ne "{print $1 if /\s(\S+\.pdb) *$/im}" )
  3190. # >blah-blah-blah.pdb
  3191. my $testname = join "\\",($srcdbg, $ext, $sym);
  3192. if (! -e $testname){
  3193. # we must get the correct directory to check -e!
  3194. #
  3195. if ($FULLCHECK and $srcdbg ne "-"){
  3196. print STDERR "LINK /DUMP ... $srcpath\\$srcfile => replace $sym " if $DEBUG;
  3197. my $result = qx ("LINK /DUMP /HEADERS $srcpath\\$srcfile");
  3198. $sym = $3 if $result =~/\s\b(([^\\]+\\)+)?(\S+.$checkext) *$/im;
  3199. print STDERR "with $sym\n" if $DEBUG;
  3200. # _namematch($srcpdb,$pdb); use it still?
  3201. }
  3202. }
  3203. }
  3204. push @sym, $sym;
  3205. }
  3206. }
  3207. print STDERR join("\n", @sym), "\n----\n" if $DEBUG;
  3208. @sym;
  3209. } # NewImageToSymbol
  3210. # GenMakeDir
  3211. # Write the whole tree to create the target structure.
  3212. # Input & Output
  3213. # none
  3214. # Usage
  3215. # &GenMakeDir();
  3216. sub GenMakeDir {
  3217. # Remove the parent folder
  3218. my $curdir;
  3219. my @directories;
  3220. my $EXPAND_DIRS_TARGET = &GetMacro("EXPAND_DIRS_TARGET");
  3221. for $curdir (keys %MAKEDIR) {
  3222. if (exists $MAKEDIR{$curdir}) {
  3223. while ($curdir=~/\\/g) {
  3224. if (($` ne $curdir) && (exists $MAKEDIR{$`})) {
  3225. delete $MAKEDIR{$`};
  3226. }
  3227. }
  3228. }
  3229. }
  3230. my $key = {"DestPath" => "_DIRS", "DestFile" => "makedir"};
  3231. &SetField( $REPOSITORY, $key, "DestPath", "_DIRS" );
  3232. &SetField( $REPOSITORY, $key, "DestFile", "makedir" );
  3233. # BUG BUG the @Cmd size is expected to be at least 2.
  3234. &PushFieldVal( $REPOSITORY, $key, "Cmd", "\n\n.IGNORE:");
  3235. &PushFieldVal( $REPOSITORY, $key, "Cmd", "\n\n_DIRS:\ " );
  3236. if ($EXPAND_DIRS_TARGET){
  3237. my @tk = map {&RevEnvVars($_)} sort keys %MAKEDIR;
  3238. &PushFieldVal( $REPOSITORY, $key, "Cmd", join ("\ \\\n\t", @tk) .
  3239. "\n\n" .
  3240. join ("\ \\\n", @tk) .
  3241. "\ :\n\t\!md \$\@ 2>NUL\n" .
  3242. "\n!CMDSWITCHES\n\n" );
  3243. }
  3244. else{
  3245. my @tk = map {"\tmd ".&RevEnvVars($_)."\ 2\>NUL"} sort keys %MAKEDIR;
  3246. &PushFieldVal( $REPOSITORY, $key, "Cmd", "\n" .
  3247. join ("\n", @tk) .
  3248. "\n\n!CMDSWITCHES\n\n" );
  3249. }
  3250. #print STDERR "\n\n_DIRS:\ " .
  3251. # join ("\ \\\n\t", @tk) .
  3252. # "\n\n.IGNORE:\n\n" .
  3253. # join ("\ \\\n", @tk) .
  3254. # "\ :\n\t\!md \$\@ 2>NUL\n" .
  3255. # "\n!CMDSWITCHES\n\n";
  3256. # exit;
  3257. 1;
  3258. } # GenMakeDir
  3259. # WriteToSyscmd
  3260. # Creates the SYSCMD nmake command file.
  3261. # Input
  3262. # syscmd file name
  3263. # Output
  3264. # none
  3265. sub WriteToSyscmd {
  3266. my( $SYSCMD ) = @_;
  3267. # Target name, as specified in the command line
  3268. my $tname;
  3269. # Indexes
  3270. my $i;
  3271. # Write to syscmd in the following cases:
  3272. # - sysgen with targets (clean or incremental)
  3273. # - sysgen incremental without targets, but with changes detected in the description blocks
  3274. if ( &IsEmptyHash( $TARGETS ) && ( $CLEAN || ( @SYSCMD == 0 ) ) ) {
  3275. return;
  3276. } # if
  3277. &SysgenLogMsg("Generating $SYSCMD...",1);
  3278. ( open( SYSCMD, ">$SYSCMD" ) ) || &FatalError( 1007, $SYSCMD );
  3279. print SYSCMD "/A \n";
  3280. # Always run DIRS
  3281. print SYSCMD "_DIRS ";
  3282. print SYSCMD "_COMPDIR " if $ACCELERATE;
  3283. # Write targets to syscmd
  3284. for $tname ( sort keys %$TARGETS ) {
  3285. print SYSCMD
  3286. join("\n",
  3287. map {"\ \\\n $TARGETS->{$tname}->{NewDestPath}->[$_]\\$tname"}
  3288. @{$TARGETS->{$tname}->{NewDestPath}});
  3289. } # for
  3290. # For incremental without TARGETS, print targets stored in @SYSCMD
  3291. # ( tbd - add an assert here: SYSCMD can be non-empty only for incremental without targets)
  3292. for ( $i=0; $i < @SYSCMD; $i++ ) {
  3293. print SYSCMD "\ \\\n $SYSCMD[$i]";
  3294. } # for
  3295. close( SYSCMD );
  3296. return;
  3297. } # WriteToSyscmd
  3298. # LoadReposit
  3299. # Populate CMD_REPOSITORY according to an existing SYSGEN generated makefile.
  3300. # For each found key, fill in the {Cmd} field.
  3301. # Input
  3302. # makefile name
  3303. # Output
  3304. # none
  3305. # Usage
  3306. # &LoadReposit();
  3307. sub LoadReposit {
  3308. my( $makefname ) = @_;
  3309. # Contents of an existing makefile
  3310. my @makefile;
  3311. # Line number where the "all" target description line finishes
  3312. my $line;
  3313. # Nothing to do in case of a clean SYSGEN
  3314. ( !$CLEAN ) || return;
  3315. # Nothing to do if the makefile does not exist
  3316. ( -e $makefname ) || &FatalError( 1003, $makefname );
  3317. # Open the makefile
  3318. ( open( MAKEFILE, $makefname ) ) || &FatalError( 1006, $makefname );
  3319. &SysgenLogMsg("Loading $makefname...", 1);
  3320. # Load makefile's contents
  3321. @makefile = <MAKEFILE>;
  3322. close( MAKEFILE );
  3323. # Fill in keys according to "all" target found in the makefile
  3324. $line = &LoadKeys( $makefname, \@makefile );
  3325. # Load makefile's description blocks into CMD_REPOSITORY's {Cmd} field.
  3326. &LoadCmd( $makefname, $line, \@makefile );
  3327. return;
  3328. } # LoadReposit
  3329. # LoadKeys
  3330. # Loads keys according to the "all" target from the given
  3331. # SYSGEN-generated makefile.
  3332. # Input
  3333. # makefile's name
  3334. # makefile's contents (reference)
  3335. # Output
  3336. # line number following "all" target or
  3337. # -1 if "all" target was not found
  3338. # Usage
  3339. # &LoadKeys( \@makefile );
  3340. sub LoadKeys {
  3341. # BUG : never exists, causing out of memovy
  3342. # perl death.
  3343. #
  3344. my( $makefname, $makefref ) = @_;
  3345. # Repository key
  3346. my $key = {};
  3347. # Indexes
  3348. my $i;
  3349. my %alltarget=();
  3350. my $cursection=0;
  3351. my $makeflines = $#$makefref;
  3352. my ($targetname, $errortarget);
  3353. # Skip white lines in the beginnig of makefile
  3354. for ( $i=0; $i < $makeflines; $i++ ) {
  3355. if ( $makefref->[$i] =~ /\S/ ) { last; }
  3356. } # for
  3357. # First non empty line from MAKEFILE must contain "sysgen" target
  3358. ( ( $i < @{$makefref} && $makefref->[$i] =~ /sysgen\s*:/i ) ) || &FatalError( 1213, $makefname);
  3359. # ignore nmake line
  3360. $i++;
  3361. $alltarget{'all'} = 1;
  3362. $makeflines = $#$makefref;
  3363. while (scalar(keys(%alltarget))) { # dengerous never accomplished....
  3364. # Error if target was not solved.
  3365. ( ++$i < $makeflines ) || &FatalError( 1210, "${makefname}(" . join(",", keys %alltarget) . ")" );
  3366. # Find the target, such as all
  3367. for ( ; $i < $makefref ; $i++) {
  3368. $errortarget=$targetname = '';
  3369. next unless $makefref->[$i]=~ /\w+\s*:\s+/i;
  3370. # Suppose only find one item matched
  3371. ($targetname, $errortarget) = map({($makefref->[$i]=~m/^\Q$_\E\s*:\s+/i) ? $_ : ()}
  3372. keys %alltarget);
  3373. # Go to next line if not found
  3374. next if ($targetname eq '');
  3375. &SysgenLogMsg($targetname, 0);
  3376. # Two target found in same line
  3377. &FatalError( 1215, "${makefname}($targetname, $errortarget, ...)")
  3378. unless $errortarget eq '';
  3379. # Target was found, move to next line and exit for loop
  3380. $i++;
  3381. last;
  3382. } # for
  3383. # Look for its belongs
  3384. for ( ; $i < $makeflines ; $i++ ) {
  3385. last if ($makefref->[$i] !~ /\S/);
  3386. # lookfor item(s) in one line
  3387. for (split(/\s+/, $makefref->[$i])) {
  3388. next if ($_ eq '');
  3389. last if ($_ eq '\\');
  3390. # $_=$makefref->[$i];
  3391. # If it is a section name, push it into alltarget hash
  3392. if ( /\Q$SECTIONNAME\E\d+$/) {
  3393. $alltarget{$_} = 1;
  3394. # Match the last one of $SECTIONAME, with (\d+)\1 => such as 88 => 8, 1616 => 16, 6464 => 64
  3395. $SECTIONS = $1 if (($SECTIONS !~/^\d+$/) && (/\Q$SECTIONNAME\E(\d+)\1/));
  3396. # Create it into REPOSITORY
  3397. } elsif (/^\t(\S*)\\(\S*)/) {
  3398. $key = {};
  3399. ($key->{DestPath}, $key->{DestFile})=($1, $2);
  3400. &SetField( $CMD_REPOSITORY, $key, "DestPath", $1 );
  3401. &SetField( $CMD_REPOSITORY, $key, "DestFile", $2 );
  3402. # If DestFile is part of TARGETS, store DestPath in TARGETS.
  3403. if ( &IsHashKey( $TARGETS, $key->{DestFile} ) ) {
  3404. &AddTargetPath( $key->{DestFile}, "OldDestPath", $key->{DestPath} );
  3405. } # if
  3406. } # if
  3407. } # for
  3408. } # for
  3409. delete $alltarget{$targetname};
  3410. } # while
  3411. return $i;
  3412. } # LoadKeys
  3413. # LoadCmd
  3414. # Load the body of the makefile.
  3415. # Fill in the {Cmd} field for each CMD_REPOSITORY key
  3416. # Input
  3417. # makefile name
  3418. # makefile line following the all target dependency lines
  3419. # make file contents (reference)
  3420. # Output
  3421. # none
  3422. # Usage
  3423. # &LoadCmd( 2543, \@makefile );
  3424. sub LoadCmd {
  3425. my( $makefname, $line, $makefref ) = @_;
  3426. my $key = {"DestPath" => undef,
  3427. "DestFile" => undef};
  3428. # Counters
  3429. my($i, $j);
  3430. # Repository key
  3431. # Description line (one or more file lines,
  3432. # depending on the existence of custom resources)
  3433. my $dline;
  3434. # Buffer for one line
  3435. my $buffer;
  3436. &FatalError( 1212, $makefname) if $line > scalar(@$makefref);
  3437. foreach $i ($line..$#$makefref) {
  3438. my $rule = $makefref->[$i];
  3439. # Skip white lines and resolve lines
  3440. next if $rule !~ /\S/ or $rule =~ /^\t/;
  3441. next unless $rule =~ /\s:\s/;
  3442. $rule =~/\bCOMPDIR\b/ and &Error(5002, "_COMPDIR target found " );
  3443. # Identify dependency line and key
  3444. $rule =~ /^\"?(.*)\\([^\\]+)\"?\s*:$/;
  3445. $key -> {"DestPath"} = $1,
  3446. $key -> {"DestFile"} = $2;
  3447. # The key must exist in CMD_REPOSITORY
  3448. &IsRepositKey( $CMD_REPOSITORY, $key ) ||
  3449. &FatalError( 1211, "$rule" );
  3450. # Load the description block into the {Cmd} field
  3451. $dline = "";
  3452. # Read first the dependency line.
  3453. # It might be spread over several lines in the makefile,
  3454. # but we will make a single line from it.
  3455. foreach $j ($i..$#$makefref){
  3456. $dline .= $makefref->[$j];
  3457. last if $makefref->[$j] !~ /\\$/;
  3458. # Dependency line ends when the last character
  3459. # is not a continuation line mark
  3460. } # for
  3461. &PushFieldVal( $CMD_REPOSITORY, $key, "Cmd", $dline );
  3462. $i=$j+1;
  3463. # Read then the command block.
  3464. foreach $j ($i..$#$makefref){
  3465. # Description block ends at the first white line encountered
  3466. last if $makefref->[$j] !~ /\S/ ;
  3467. # Load the current command line
  3468. &PushFieldVal( $CMD_REPOSITORY, $key, "Cmd", $makefref->[$j] );
  3469. } # for
  3470. $i = $j;
  3471. } # for
  3472. return;
  3473. } # LoadCmd
  3474. # LoadEnv
  3475. # Loads the environment variables into MACROS (hash).
  3476. # Uppercases all the macroname.
  3477. # Input & Output: none
  3478. # Usage
  3479. # &LoadEnv();
  3480. sub LoadEnv {
  3481. for ( keys %ENV ) {
  3482. &SetMacro( $_, $ENV{$_}, 0 );
  3483. } #for
  3484. # version
  3485. my $version = $VERSION;
  3486. $version =~ s/\.\d+$//g;
  3487. &SetMacro("VERSION", $version, 0);
  3488. $DEBUG and
  3489. print STDERR "\"$version\"\n";
  3490. # stuff fot the MakeXcopyCmd
  3491. $version ne "3" and &SetMacro( "DST", $ENV{"_NTPOSTBLD"}, 0 );
  3492. $SRCBASE =~ s/([\\\.])/\Q$1\E/g;
  3493. $DEBUG and
  3494. print STDERR "\"",$DSTBASE, "\"\t\"", $SRCBASE, "\"\n";
  3495. return;
  3496. } # LoadEnv
  3497. # GetMacro
  3498. # Returns the value of a macro.
  3499. # Input
  3500. # macroname
  3501. # Output
  3502. # macrovalue ( empty string if not defined )
  3503. # Usage
  3504. # $language = &GetMacro("Language");
  3505. sub GetMacro {
  3506. return $MACROS->{uc$_[0]}->{Value};
  3507. } # GetMacro
  3508. # SetMacro
  3509. # Sets the value of a macro.
  3510. # Input
  3511. # macroname
  3512. # macrovalue
  3513. # macrotype (see %MACROS in the beginning of the file
  3514. # for more details on the types of macros.
  3515. # Output
  3516. # none
  3517. # Usage
  3518. # &SetMacro( "_BuildArch", "nec_98", 0);
  3519. sub SetMacro {
  3520. my $varname=uc shift;
  3521. # Do not overwrite a macro defined in the command line unless
  3522. # the same macro is redefined in the command line
  3523. if ( (!exists $MACROS->{$varname}) || ($MACROS->{$varname}->{Type} == 0) || ($_[1] == 1)) {
  3524. ($MACROS->{$varname}->{Value}, $MACROS->{$varname}->{Type})=@_;
  3525. }
  3526. return;
  3527. } # SetMacro
  3528. # FatalError
  3529. # Prints the error and exits.
  3530. # Input
  3531. # error code
  3532. # name of the sysfile where the error occured or any other text
  3533. # line number on the description file where the error occured or 0 if not the case
  3534. # Ouput
  3535. # none
  3536. # Usage
  3537. # &FatalError( 1111, "sysfile", 12 );
  3538. # &FatalError( 1002, $CODESFNAME, 0 );
  3539. sub FatalError {
  3540. &PrintError( "fatal error", @_);
  3541. print "Stop.\n";
  3542. exit;
  3543. } # FatalError
  3544. # Error
  3545. # Prints the error and returns.
  3546. # Input
  3547. # error code
  3548. # name of the sysfile where the error occured or any other text
  3549. # line number on the description file where the error occured or 0 if not the case
  3550. # Output
  3551. # none
  3552. # Usage
  3553. # &Error( 2011, $recref->{TokPath}\\$recref->{TokFile});
  3554. sub Error {
  3555. # Errors from IGNORE macro are not counted
  3556. if ($ERRORS->{$_[-1]} != $_[-2] ){
  3557. if ( &PrintError( "error", @_ ) ) {
  3558. $ERRORS->{$_[-1]} = $_[-2];
  3559. }
  3560. } # if
  3561. # $DEBUG and
  3562. # print STDERR join( "\t" , keys %$ERRORS) , "\n";
  3563. return;
  3564. } # Error
  3565. # PrintError
  3566. # Prints the encountered error with the format
  3567. # <description filename>(<line>): <fatal error | error> <error_code>: <error_text>
  3568. # Input
  3569. # error type ( fatal error or error )
  3570. # error code
  3571. # filename where the error was encountered or other text
  3572. # line number or 0
  3573. # Output
  3574. # 1 if the error is counted
  3575. # 0 otherwise
  3576. # Usage
  3577. # &PrintError( 1002, $CODESFNAME, 0);
  3578. sub PrintError {
  3579. my( $errtype, $errno, $file, $line ) = @_;
  3580. # Error text
  3581. my $errtxt;
  3582. # Ignore errors
  3583. my $ignore;
  3584. my @ivalues;
  3585. # Counter
  3586. my $i;
  3587. my $fileline;
  3588. # Do not print anything if errno is listed in IGNORE macro
  3589. $ignore = &GetMacro( "IGNORE" );
  3590. $ignore =~ s/\s*//g;
  3591. @ivalues = split ";", $ignore;
  3592. for ( $i=0; $i < @ivalues; $i++ ) {
  3593. if ( $errno == $ivalues[$i] ) {
  3594. return 0;
  3595. } # if
  3596. } # for
  3597. $errtxt = "SYSGEN:";
  3598. $fileline="";
  3599. if ( $file ) {
  3600. if ( $line ) { $fileline=" $file($line)"; }
  3601. else { $fileline=" $file"; }
  3602. } # if
  3603. if ( $MAP_ERR{$errno} ) { $fileline .= ": ".$MAP_ERR{$errno}; }
  3604. $errtxt .= " $errtype $errno:$fileline";
  3605. ( open LOGFILE, ">>$LOGFILE" ) || &FatalError( 1007, $LOGFILE );
  3606. printf LOGFILE "$errtxt\n";
  3607. close LOGFILE;
  3608. ( open ERRFILE, ">>$ERRFILE" ) || &FatalError( 1007, $ERRFILE );
  3609. printf ERRFILE "$errtxt\n";
  3610. close ERRFILE ;
  3611. select(STDERR); $| = 1;
  3612. printf "$errtxt\n";
  3613. select(STDOUT); $| = 1;
  3614. return 1;
  3615. } # PrintError
  3616. # SumErrors
  3617. # Displays the number of non-fatal errors found while running SYSGEN.
  3618. # Runs at the end of sysgen.
  3619. # Input & Output: none
  3620. # Usage
  3621. # &SumErrors();
  3622. sub SumErrors {
  3623. my $elapsed_time = time() - $start_time;
  3624. print STDERR "Finished in $elapsed_time seconds\n";
  3625. my $errors = scalar(keys(%$ERRORS));
  3626. if ( 0 == $errors ) {
  3627. print "\nSYSGEN: No errors found during execution.\n";
  3628. }
  3629. else {
  3630. print "\nSYSGEN: Total Errors: $errors\n";
  3631. }
  3632. 1;
  3633. } # SumErrors
  3634. # CleanLogFiles
  3635. # For a clean SYSGEN, delete sysgen.log and sysgen.err files.
  3636. # For an incremental SYSGEN, delete only sysgen.err.
  3637. # Input
  3638. # sysfile's directory
  3639. # Output
  3640. # none
  3641. # Usage
  3642. # &CleanLogFiles();
  3643. sub CleanLogFiles {
  3644. my( $sysdir ) = @_;
  3645. # Delete $LOGFILE and $ERRFILE
  3646. &SysgenLogMsg("Cleaning log and error files...",1);
  3647. if ( -e $ERRFILE ) { unlink $ERRFILE; }
  3648. -e $ERRFILE and exit "failed to delete $ERRFILE\n";
  3649. open ERRFILE, ">>$ERRFILE" or exit "Cannot access $ERRFILE\n";
  3650. close ERRFILE;
  3651. if ( $CLEAN && -e $LOGFILE ) {
  3652. unlink $LOGFILE;
  3653. -e $LOGFILE and exit "failed to delete $LOGFILE\n";
  3654. }
  3655. # Delete existing $MAKEFILE and $SYSCMD
  3656. if ( $CLEAN && !$SYNTAX_CHECK_ONLY && -e $MAKEFILE ) { unlink $MAKEFILE; }
  3657. if ( !$SYNTAX_CHECK_ONLY && -e $SYSCMD ) { unlink $SYSCMD; }
  3658. return;
  3659. } # CleanLogFiles
  3660. # ////////////////////////////////////////////////////////////////////////////////////////
  3661. # PrintHelp
  3662. # Print usage
  3663. sub PrintHelp {
  3664. my $version = &Version;
  3665. my $scriptname = $0;
  3666. $scriptname =~s/^.*\\//;
  3667. print STDERR <<EOH;
  3668. $version
  3669. Usage
  3670. perl $scriptname [<options>] [<macros>] [<targets>]
  3671. where
  3672. Options:
  3673. /c generate the makefile from scratch, overwriting existing one.
  3674. By running nmake, all the targets will be generated.
  3675. /s limit sysgen to syntax check - makefile not (re)generated.
  3676. /f <name> takes <name> as the sysfile.
  3677. If this option is omitted, sysgen searches the current
  3678. directory for a file called sysfile and uses it as a
  3679. description (mapping) file.
  3680. /w <name> takes <name> as the PATH for reading/writing
  3681. makefile and err/log files. Note that the default
  3682. aggregation 'makefile' name is sysgen.mak
  3683. /v display version
  3684. /y verify LOC drop coverage. Use in conjunction with -c flag
  3685. /z generate LS 5.0 tokens from existing LOC drop (bingen/rsrc).
  3686. Use in conjunction with -c flag
  3687. /l:<LANG> specify language
  3688. /?
  3689. /h display this message
  3690. Macros:
  3691. list the command line macro definitions in the format
  3692. "<macroname>=<macrovalue>". Seldomly used
  3693. Targets:
  3694. specify files (without path) to localize/aggregate.
  3695. For a full documentation please run perldoc.exe $scriptname
  3696. EOH
  3697. exit 0;
  3698. } #PrintHelp
  3699. sub printVersion{
  3700. print STDERR &Version;
  3701. exit 0;
  3702. } #PrintVersion
  3703. # Version of the SYSGEN
  3704. # usage : &Version
  3705. sub Version{
  3706. <<EOT;
  3707. SYSGEN v.$VERSION: Whistler aggregation tool for international builds.
  3708. EOT
  3709. } #Version
  3710. # FillFilter
  3711. # Fills in the FILTER variable
  3712. # Input & Output
  3713. # none
  3714. sub FillFilter {
  3715. # List of filtered files
  3716. my @farray;
  3717. # Index
  3718. my $i;
  3719. my $file = &GetMacro( "FILTER" );
  3720. ( $file ) || return;
  3721. ( -e $file ) || &FatalError( 1005, $file );
  3722. # Open the mapping file
  3723. ( open( FILE, $file ) ) || &FatalError( 1006, $file );
  3724. # Load file contents
  3725. @farray = <FILE>;
  3726. close( FILE );
  3727. &SysgenLogMsg("Loading filter $file...", 1);
  3728. for ( $i = 0; $i < @farray; $i++ ) {
  3729. chop $farray[$i];
  3730. $farray[$i] =~ s/^\s*//g;
  3731. $farray[$i] =~ s/\s*=\s*/=/g;
  3732. next if (($farray[$i]=~/^\;/)||($farray[$i]=~/^\s*$/));
  3733. $FILTER{lc( $farray[$i] )} = 0;
  3734. } # for
  3735. # In case targets were specified in the command line,
  3736. # verify FILTER contains them.
  3737. if ( ! &IsEmptyHash( $TARGETS ) ) {
  3738. for ( keys %$TARGETS ) {
  3739. if ( ! exists $FILTER{lc( $_ )} ) {
  3740. &FatalError( 1009, $_ );
  3741. } # if
  3742. } # for
  3743. } # if
  3744. return;
  3745. } # FillFilter
  3746. # GetSysDir
  3747. # Returns the directory name from a sysfile path.
  3748. # Sysfile's directory is the place where SYSGEN generates the following files:
  3749. # makefile (used by nmake to execute the aggregation)
  3750. # syscmd (is the nmake command-line file used for incremental builds)
  3751. # sysgen.log (the output of SYSGEN and NMAKE)
  3752. # sysgen.err (the errors found while running SYSGEN and NMAKE)
  3753. # Input
  3754. # full path to a sysfile
  3755. # Output
  3756. # directory name of the given sysfile
  3757. sub GetSysDir {
  3758. my $sysfile = shift;
  3759. $DEBUG and print STDERR "get \$sysfile \"$sysfile\"\n";
  3760. my $sysdir = $sysfile if $sysfile =~ /\\/;
  3761. $sysdir =~ s/\\[^\\]+$//;
  3762. $DEBUG and print STDERR "set \$sysdir \"$sysdir\"\n";
  3763. $sysdir;
  3764. } # GetSysDir
  3765. # The wrapper for the old sysgen
  3766. # &ParseCmdLine while transition to
  3767. # &parseargs
  3768. # Usege &UseOldParser
  3769. sub UseOldParser{
  3770. $ARGL =~ s/(\w):([^\\])/$1$SEPARATOR$2/g;
  3771. $ARGL =~ s/\-old//g;
  3772. my @argv = grep {/\S/} split($SEPARATOR, $ARGL);
  3773. $DEBUG and
  3774. print STDERR "\n", join("\n", @argv), "\n\n";
  3775. &ParseCmdLine(@argv);
  3776. @argv;
  3777. }
  3778. # ParseCmdLine
  3779. # Parses the command line.
  3780. # SYSGEN's command-line syntax is:
  3781. # SYSGEN [<options>] [<macros>] [<targets>]
  3782. # Input
  3783. # command-line array
  3784. # Output
  3785. # none
  3786. # Usage
  3787. # &ParseCmdline(@ARGV);
  3788. sub ParseCmdLine {
  3789. my @cmdline = @_;
  3790. # Indexes
  3791. my( $i , $optname , @text );
  3792. for ( $i=0; $i < @cmdline; $i++ ) {
  3793. $_ = $cmdline[$i];
  3794. ($optname)=m/[-\/]([\?hVcnfswaxl])/i;
  3795. $optname =~s/(\w)/\L$1\E/;
  3796. # Check Option
  3797. if ( $optname ) {
  3798. $SYNTAX_CHECK_ONLY = 1 and next if $optname eq 's'; # -s for syntax check only
  3799. if ($optname eq '?' or $optname eq 'h') { # -? for help
  3800. &PrintHelp;
  3801. } elsif ($optname eq 'v') { # -V for version number
  3802. &printVersion;
  3803. } elsif ($optname eq 'c') { # -c for CLEAN
  3804. $CLEAN = 1;
  3805. next;
  3806. } elsif ($optname eq 'n') { # -n for Section Number
  3807. # Set SECTIONS value
  3808. $i++;
  3809. ( $i < @cmdline ) || &FatalError( 1011 );
  3810. $SECTIONS = $cmdline[$i];
  3811. $SECTIONS = $DEFAULT_SECTIONS if ($SECTIONS !~/^\d+$/);
  3812. next;
  3813. } elsif ($optname eq 'f') { # -f for specified SYSFILE
  3814. # add SYSFILES
  3815. $i++;
  3816. ( $i < @cmdline ) || &FatalError( 1008 );
  3817. push @SYSFILES, $cmdline[$i];
  3818. next;
  3819. } elsif ($optname eq 'l') { # -l LANG
  3820. # add SYSFILES
  3821. $i++;
  3822. ( $i < @cmdline ) || &FatalError( 1008 );
  3823. $LANG = $cmdline[$i];
  3824. &SetMacro("LANGUAGE", uc($cmdline[$i]), 0);
  3825. $DEBUG and print STDERR "LANG\t", &GetMacro("LANGUAGE"), "\n";
  3826. next;
  3827. } elsif ($optname eq 'x') { # -x for exclude path from mapping
  3828. # add SYSFILES
  3829. $i++;
  3830. ( $i < @cmdline ) || &FatalError( 1008 );
  3831. $EXCLUDE_DIRECTORIES->{$cmdline[$i]} = 1;
  3832. next;
  3833. } elsif ($optname eq 'w') { # -w for specified WORKDIR
  3834. # Set WORKDIR value
  3835. $i++;
  3836. ( $i < @cmdline ) || &FatalError( 1010 );
  3837. $WORKDIR = $cmdline[$i];
  3838. next;
  3839. } elsif ($optname eq 'a') { # -w for ACCELERATE
  3840. # Set ACCEL
  3841. $ACCELERATE = 1;
  3842. next;
  3843. }
  3844. } # if
  3845. # macro definition
  3846. if ( /\s*(\S*)\s*\=\s*(\S*)\s*/ ) {
  3847. &SetMacro( $1, $2, 1 );
  3848. last SWITCH;
  3849. } # if
  3850. # default (target name)
  3851. &AddTarget($_);
  3852. } # for
  3853. return;
  3854. } # ParseCmdLine
  3855. # parseSymbolCD
  3856. # create the mapping for some binaries listed in symbolcd.txt
  3857. # Input
  3858. # filename [path to symbolcd.txt]
  3859. # Output
  3860. # REF TO ARRAY OF HASH REF [{BIN => SYMBOL}, ... ]
  3861. # Sample usage:
  3862. # print join "\n", @{&parseSymbolCD("symbolcd.txt")};
  3863. #
  3864. sub parseSymbolCD{
  3865. my $fname = shift;
  3866. open (F, "<". $fname);
  3867. my %o;
  3868. my $o = \%o;
  3869. while(<F>){
  3870. chomp;
  3871. my @s = split ",", $_;
  3872. # keep the filename of the binary from symbolcd.txt
  3873. $s[0] =~ s/^.*\\//g;
  3874. next if ($s[0] eq $s[1]);
  3875. $s[1] =~ s/^.*\.//g;
  3876. # keep the extension of the symbol file from symbolcd.txt
  3877. $o->{$s[0]} = {} unless defined ($o->{$s[0]});
  3878. $o->{$s[0]}->{$s[1]} = $s[2];
  3879. # there are more lines
  3880. }
  3881. close(F);
  3882. &SysgenLogMsg("Loading $fname... ". scalar(keys(%o)). " symbols", 1);
  3883. if ($DEBUG){
  3884. foreach my $lib (keys(%o)){
  3885. my %hint = %{$o{$lib}};
  3886. print STDERR join("\t", keys(%hint)), "\n";
  3887. }
  3888. }
  3889. %o;
  3890. }
  3891. # LoadHotFix
  3892. # Reads and loads the makefile style hotfix, using the two parts
  3893. # of the dependancy rule for:
  3894. #
  3895. # * check for token->binary depenancy during repository generation
  3896. # * repository modification
  3897. #
  3898. # Input
  3899. # HOTFIX filename
  3900. # Output
  3901. # <none>
  3902. # LoadHotFix can be called any time,
  3903. # since it expands symbols without relying on
  3904. # that vars are defined.
  3905. # Input
  3906. # filename [path to hotfix file]
  3907. # Output
  3908. # <unused>
  3909. sub LoadHotFixFile{
  3910. my $filename = shift;
  3911. return unless -e $filename;
  3912. open (HOTFIX, $filename);
  3913. # makefile style hot fix.
  3914. my ($target, $build, $depend, $message);
  3915. my $hotfix = \%HOTFIX;
  3916. while(<HOTFIX>){
  3917. chomp;
  3918. next if /^\#/; # comment
  3919. if (/^\S*SET/i){
  3920. &SetMacro( &SetAttribOp( $_ ), 0 );
  3921. next;
  3922. }
  3923. if ( /\bMESSAGE\b\s+(\S.*)$/i) { # line
  3924. $message = &ReplaceVar( $1, 0 ); # MESSAGE something impo
  3925. $message =~ s/\"//g; # becomes:
  3926. $message = "$target: $message"; # SYSGEN: error 1101: <target>: something important
  3927. $message =~ s/^.*\\([^ ]+)/$1/g; # logerr "echo <target> something important"
  3928. &Error( 1101, $message );
  3929. $build = $hotfix->{$target}->{"build"}
  3930. if defined($hotfix->{$target}->{"build"});
  3931. push @$build, "\t".&ReplaceVar("logerr \"echo $message\"")."\n";
  3932. next;
  3933. } # case
  3934. if ($_=~/^(.*) *\:[^\\](.*)$/){
  3935. $target = $1; #<target>: <source list>
  3936. my @depend = ();
  3937. $depend = $2;
  3938. map {push @depend, lc(&ReplaceVar($_))} split( /\s+/, $depend);
  3939. $target =~s/ +$//g;
  3940. $target = lc(&ReplaceVar($target));
  3941. $HOTFIX{$target} = {"build" => [],
  3942. "depend" => [@depend]};
  3943. print STDERR join("\n", map {"'$_'"} @depend), "\n---\n" if $DEBUG;
  3944. $build = $hotfix->{$target}->{"build"};
  3945. }
  3946. push @$build, "\t".&ReplaceVar($_)."\n" if (/\S/ && /^\t/ );# instructions
  3947. }
  3948. &SysgenLogMsg("Loading $filename ... ". scalar (keys(%HOTFIX)). " hotfix rules", 1);
  3949. print STDERR join("\n", "keys \%HOTFIX:", map {"'$_'"} keys(%HOTFIX)), "\n" if $DEBUG;
  3950. close(HOTFIX);
  3951. map {print STDERR $_, "\n",join("\n",
  3952. @{$hotfix->{$_}->{"build"}}), "\n---\n"}
  3953. keys(%$hotfix) if $DEBUG;
  3954. 1;
  3955. }
  3956. # LoadRuleBlock
  3957. # Reads and loads the makefile style hotfix, using the two parts
  3958. # of the dependancy rule for:
  3959. #
  3960. # * check for token->binary depenancy during repository generation
  3961. # * repository modification
  3962. #
  3963. # Input
  3964. # HOTFIX filename
  3965. # Output
  3966. # <none>
  3967. # LoadHotFix can be called any time,
  3968. # since it expands symbols without relying on
  3969. # that vars are defined.
  3970. # Input
  3971. # filename [path to hotfix file]
  3972. # Output
  3973. # <unused>
  3974. sub LoadRuleBlock{
  3975. my $ruleref = shift;
  3976. my $hotfix = \%HOTFIX;
  3977. my ($target, $depend, $build, $filename);
  3978. foreach (@$ruleref){
  3979. chomp;
  3980. next if /^\#/; # comment
  3981. if ( /\bMESSAGE\b\s+(\S.*)$/i) { # line
  3982. my $message;
  3983. $message = &ReplaceVar( $1, 0 ); # MESSAGE something impo
  3984. $message =~ s/\"//g; # becomes:
  3985. $message = "$target: $message"; # SYSGEN: error 1101: <target>: something important
  3986. $message =~ s/^.*\\([^ ]+)/$1/g; # logerr "echo <target> something important"
  3987. &Error( 1101, $message );
  3988. $build = $hotfix->{$target}->{"build"} if
  3989. defined($hotfix->{$target}->{"build"});
  3990. push @$build, "\t".&ReplaceVar("logerr \"echo $message\"")."\n";
  3991. next;
  3992. } # MESSAGE
  3993. if (/^\S*SET/i){
  3994. &SetMacro( &SetAttribOp( $_ ), 0 );
  3995. next;
  3996. }
  3997. if ($_=~/^(.*) *\:[^\\](.*)$/){
  3998. $target = $1;#target: source list
  3999. my @depend = ();
  4000. $depend = $2;
  4001. map {push @depend, lc(&ReplaceVar($_))} split( /\s+/, $depend);
  4002. $target =~s/ +$//g;
  4003. $target = lc(&ReplaceVar($target));
  4004. $HOTFIX{$target} = {"build" => [],
  4005. "depend" => [@depend]};
  4006. print STDERR "Depend:\n+------\n|",join("\n|", map {"'$_'"} @depend), "\n+---\n" if $DEBUG;
  4007. $build = $hotfix->{$target}->{"build"};
  4008. }
  4009. push @$build, "\t".&RevEnvVars(&ReplaceVar($_))."\n" if (/\S/ && /^\t/ );# instructions
  4010. }
  4011. # my $DEBUG = 1;
  4012. &SysgenLogMsg("Loading $filename ... ". scalar (keys(%HOTFIX)). " hotfix rules", 1);
  4013. map {print STDERR $_, ":\n",join("",
  4014. @{$hotfix->{$_}->{"build"}}), "\n---\n"}
  4015. keys(%$hotfix)
  4016. if $DEBUG;
  4017. 1;
  4018. }
  4019. # FixRepository
  4020. # Merges contents of Repository with the commands from the HOTFIX file
  4021. # on the same target without introducing new targets.
  4022. # Must be called as late as possible but before the
  4023. # writing the nmake Makefile
  4024. # Input
  4025. # <none>
  4026. # Output
  4027. # <unused>
  4028. sub FixRepository{
  4029. my $mapref = $REPOSITORY;
  4030. return unless scalar(keys(%HOTFIX));
  4031. foreach my $destpath ( sort keys %{$mapref} ) {
  4032. foreach my $destfile ( sort keys %{$mapref->{$destpath}} ) {
  4033. my $fullname=lc(join("\\",$destpath, $destfile));
  4034. if ($HOTFIX{lc($fullname)}){
  4035. print STDERR "Applying HOTFIX rule for $fullname\n" if $DEBUG;
  4036. my $cmdref = $mapref->{$destpath}->{$destfile}->{"Cmd"};
  4037. my @cmd = map {$_} @{$cmdref};
  4038. my $hotfix = \%HOTFIX;
  4039. my $depend = $hotfix->{$fullname}->{"depend"};
  4040. my $dep = &RevEnvVars(join(" ", "", @$depend));
  4041. chomp $dep;
  4042. $cmd[0] =~ s/$/$dep/;# append the dep list
  4043. $#cmd=0 if &GetMacro("OVERWRITE_DEFAULT_RULE");
  4044. my $newcmd = $hotfix->{$fullname}->{"build"};
  4045. foreach (@$newcmd) {$_ = &RevEnvVars($_);};
  4046. if (&GetMacro("APPEND")){
  4047. # append:
  4048. push @cmd, @{$newcmd};
  4049. }
  4050. else{
  4051. # prepend:
  4052. my $line0 = shift @cmd;
  4053. unshift @cmd, @{$newcmd};
  4054. unshift @cmd, $line0;
  4055. }
  4056. $mapref->{$destpath}->{$destfile}->{"Cmd"} = \@cmd;
  4057. map {print STDERR "$_\n"} @cmd if $DEBUG;
  4058. }
  4059. }
  4060. }
  4061. 1;
  4062. }
  4063. # SwitchWorkDir
  4064. # Prepends the path to the filename
  4065. #
  4066. # Usage: &SwitchWorkDir(<SYSGENFILE>, <SYSGENDIR>);
  4067. # Input
  4068. # <filename>, <dir>
  4069. # Output
  4070. # <filename>
  4071. sub SwitchWorkDir{
  4072. my $logfile = shift;
  4073. my $workdir = shift;
  4074. $logfile = $workdir . "\\" . $logfile if $workdir;
  4075. $logfile;
  4076. }
  4077. # SysgenLogMsg
  4078. # Formats and prints messages like "Loading blah blah blah..."
  4079. #
  4080. # Input
  4081. # <message>
  4082. # Output
  4083. # <none>
  4084. sub SysgenLogMsg{
  4085. my $msg = shift;
  4086. my $chomp = shift;
  4087. $msg =~ s/\s\\([^\\]+)\b/ $1/g if $chomp;
  4088. $msg =~ s/\s\b\S+\\([^\\]+)\b/ $1/g if $chomp;
  4089. $msg =~ s/\.+\\//g if $chomp;
  4090. $msg = &RevEnvVars($msg);
  4091. $SYNTAX_CHECK_ONLY or print "$msg\n";
  4092. }
  4093. # homemade
  4094. # ...
  4095. # pattern
  4096. # matcher
  4097. #
  4098. #
  4099. # &match3dot("D:\\ntt\\private\\...\\wmi", "D:\\ntt\\private\\sergueik_dev\\tools\\wmi\\perl");
  4100. # &match3dot("D:\\ntt\\private", "D:\\ntt\\private\\sergueik_dev\\tools\\wmi\\perl");
  4101. # !&match3dot("D:\\ntt\\private\\sergueik_dev2", "D:\\ntt\\private\\sergueik_dev\\tools\\wmi\\perl");
  4102. #
  4103. # JeremyD suggests (not yet implemented)
  4104. #
  4105. #
  4106. # $regex = quotemeta($a);
  4107. # $regex =~ s/\\\.\\\.\\\./\(\.\*\)/g;
  4108. #
  4109. # @list = $b =~ /$regex/;
  4110. #
  4111. #
  4112. #
  4113. sub match3dot{
  4114. my ($vector, $path) = @_;
  4115. my $left;
  4116. my $right;
  4117. my $res = 0;
  4118. foreach my $pattern (grep {/\S/} keys(%$vector)){
  4119. if ($pattern =~ /(.+)\.\.\.(.+)/ ){
  4120. $left = $1;
  4121. $right = $2;
  4122. if ($path =~ /^\Q$left\E(.+)?\Q$right\E\b\\?/) {$res = 1;}
  4123. }
  4124. else{
  4125. if ($path =~ /^\Q$pattern\E\b\\?/) {$res = 1;}
  4126. }
  4127. }
  4128. $res and &SysgenLogMsg("Ignore ". &RevEnvVars($path),0);
  4129. $res;
  4130. }
  4131. # AddFiles
  4132. # Parses TFILES and TBINDIRS and adds entries to REPOSITORY.
  4133. # Input & Output <none>
  4134. # Usage
  4135. # &AddFiles();
  4136. sub AddFiles {
  4137. # Current element of TBINDIRS ( reference )
  4138. my $recref;
  4139. # EXCLUDE_DIRECTORIES
  4140. #
  4141. foreach my $ExcludeDir (split(/\s*;\s*/, &GetMacro("EXCLUDE_DIRECTORIES"))){
  4142. $ExcludeDir=~ s+\*\.+\.+g;
  4143. $EXCLUDE_DIRECTORIES->{lc($ExcludeDir)} = 1;
  4144. }
  4145. # my $DEBUG = 1;
  4146. # $DEBUG and
  4147. # print STDERR "exclude dir\(s\):\n",
  4148. # join( "\n", keys(%$EXCLUDE_DIRECTORIES)),
  4149. # "\n\n";
  4150. # EXCLUDE_EXTENSIONS
  4151. #
  4152. foreach my $ExcludeExtension (split(/\s*;\s*/, &GetMacro("EXCLUDE_EXTENSION"))){
  4153. $ExcludeExtension=~ s+\*\.+\.+g;
  4154. $EXCLUDE_EXTENSION{lc($ExcludeExtension)} = 1;
  4155. }
  4156. # $DEBUG and
  4157. # print STDERR "exclude extension\(s\):\n",
  4158. # join( "\n", keys(%EXCLUDE_EXTENSION)),
  4159. # "\n\n";
  4160. # Current directory contents
  4161. my @files;
  4162. # Indexes
  4163. my($i, $j, $dir);
  4164. &SysgenLogMsg("Adding file data...",0);
  4165. # Add the files listed in TFILES to REPOSITORY
  4166. map {&AddEntry( $_, 1 )} @TFILES;
  4167. my %TFILE_DIRS = ();
  4168. my $TFILE_DIRS = \%TFILE_DIRS;
  4169. map {$TFILE_DIRS->{lc($_->{"SrcPath"})} = lc($_->{"SrcFile"})} @TFILES;
  4170. my $TFILERE = join "|", map {lc($_->{"SrcFile"})} @TFILES ; # files that may cause trouble
  4171. # BUG only one file per directory is possible here!
  4172. # TODO : put the $TFILERE stuff into the value of %$TFILE_DIRS
  4173. map {delete($EXCLUDE_DIRECTORIES->{$_})} grep {/^\s*$/}keys(%$EXCLUDE_DIRECTORIES);
  4174. # BUG BUG
  4175. # $EXCLUDE_DIRECTORIES=xxx; <space> is wrongly handled by the parser.
  4176. # <space> becomes the key in %$EXCLUDE_DIRECTORIES!
  4177. # Add the files found in the directories stored by TBINDIRS
  4178. for ( $i=0; $i < @TBINDIRS; $i++ ) {
  4179. $recref = $TBINDIRS[$i];
  4180. next if &match3dot($EXCLUDE_DIRECTORIES, lc($recref->{SrcPath}));
  4181. # Load all files from the SrcPath directory
  4182. &SysgenLogMsg("\t$recref->{SrcPath}",0);
  4183. # Add one entry in REPOSITORY for each file found at SrcPath
  4184. my $aparent = lc($recref->{"SrcPath"});
  4185. my @afiles = ();
  4186. if (opendir(APARENT, $aparent)){
  4187. @afiles = grep { /[^\.]/ && -f "$aparent/$_" } readdir (APARENT);
  4188. closedir(APARENT);
  4189. }
  4190. foreach my $afile (@afiles) {
  4191. $recref->{SrcFile} = $afile;
  4192. $recref->{DestFile} = $afile;
  4193. # avoid doubly mapped files across FILES and BINDIRS sections.
  4194. if ($afile =~ /$TFILERE/io ){
  4195. # &dbgmsg("OVERLAP :$aparent\\$afile ($TFILE_DIRS->{$aparent})");
  4196. next if $TFILE_DIRS->{$aparent} eq lc($afile);
  4197. # use Jarod code for the proper pattern here ?
  4198. }
  4199. # next if $TFILE_DIRS->{$aparent} eq lc($afile);
  4200. # Call AddEntry with 0, to inhibit the file existence checking,
  4201. # as SrcFile is a result of a dir command (see above).
  4202. $afile =~ s/.*\.([\$\w]+)\s*$/.\L$1\E/;
  4203. # convert source file extension to lower case.
  4204. $EXCLUDE_EXTENSION{$afile} or &AddEntry( $recref, 0);
  4205. } # for
  4206. } # for
  4207. @TBINDIRS = ();
  4208. return;
  4209. } # AddFiles
  4210. # usage :
  4211. # &testCklang();
  4212. #
  4213. #
  4214. sub testCklang{
  4215. my @a = (scalar(@ARGV)) ? (map {$_} @ARGV) : ("\@EU;GER", "GER");
  4216. @a = (scalar(@ARGV)) ? (map {$_} @ARGV) : ("ALL;~GER", "GER");
  4217. print STDERR join("\t",
  4218. $a[0],
  4219. $a[1],
  4220. $ANSW->{&cklang::CkLang($a[1], $a[0] )});
  4221. } # testCklang
  4222. #
  4223. # filter_regex
  4224. # Builds a regular expression that will match the relative path of
  4225. # service pack files. Uses spfiles.txt for the file specifications.
  4226. # Codes.txt has the languages that may be used as ALT_PROJECT_TARGET.
  4227. # And there's a hard-coded pattern for sku, wow, coverage, etc
  4228. # directories.
  4229. #
  4230. # Takes no paramaters, return value is a regular expression suitable
  4231. # for use with qr//, throws a fatal error if spfiles.txt is not
  4232. # available. Many things that probably should be fatal are treated
  4233. # as warnings right now.
  4234. # [jtolman]
  4235. #
  4236. sub filter_regex {
  4237. print STDERR "Building filtering regular expression\n";
  4238. my $start_re = "(?:\Q$ENV{_NTPOSTBLD}\E\\\\)";
  4239. my $variations_re = '(?:(?:covinf\\\\)?...inf|lang|wow6432|pre(?:-bbt|rebase))';
  4240. my @file_patterns;
  4241. my $sp_file = "$ENV{_NTPOSTBLD}\\..\\build_logs\\files.txt";
  4242. print STDERR "$sp_file\n";
  4243. open SP, $sp_file or die "sp file list open failed: $!";
  4244. while (<SP>) {
  4245. chomp;
  4246. s/;.*$//; # first strip comments
  4247. next if /^\s*$/; # then skip blank lines
  4248. my ($tag, $file) = /^(\S*)\s*(\S+)$/;
  4249. if (!$file) {
  4250. print STDERR "WARNING: Failed to parse line: $_ ($tag - $file)\n";
  4251. next;
  4252. }
  4253. if ($tag =~ /d/) {
  4254. next;
  4255. }
  4256. if ($file =~ /^(.+)\Q\...\E$/) {
  4257. my $dir = $1;
  4258. push @file_patterns, "\Q$dir\E\\\\.+";
  4259. next;
  4260. }
  4261. elsif ($file =~ /^(.+)\Q\*\E$/) {
  4262. my $dir = $1;
  4263. push @file_patterns, "\Q$dir\E\\\\[^\\\\]+";
  4264. next;
  4265. }
  4266. else {
  4267. push @file_patterns, "\Q$file\E";
  4268. next;
  4269. }
  4270. }
  4271. close SP;
  4272. my $files_re = '(?:' . join('|', @file_patterns) . ')';
  4273. my $filter_re =
  4274. qr/$start_re(?:$variations_re\\)?$files_re/io;
  4275. return $filter_re;
  4276. }
  4277. __END__
  4278. =head1 NAME
  4279. SYSGEN - Aggregation driver
  4280. Aggregation (also termed sysgen) is the important postbuild step for the international
  4281. Whistler builds. In fact, it is the very first step executed by the postbuild and
  4282. is specific to the international Whistler builds. International build is recognized by
  4283. the %LANG% environment being defined and different from the "USA" (the default).
  4284. Sysgen consists of creating the file structure in the %_NTPOSTBLD% environment which
  4285. is to be in some way identical to the %_NTTREE% one but which content represents the
  4286. blend of US and localized files. The identity between the US and %LANG% file
  4287. structure guarantees the subsequent build steps do not differ between the languages.
  4288. This equivalence is achieved by applying appropriate file operations to certain files.
  4289. From the sysgen point of view, there is few choices to take when producing the
  4290. desired bits:
  4291. * use the (copy of) US and %LANG% pre-build ones
  4292. * create an appropriate merge between US files and %LANG% resources
  4293. In most typical run, which is the full rebuild, sysgen starts with the empty
  4294. %_NTPOSTBLD% and ends with the makefile appropriate to build the full file tree.
  4295. The originals of the US tree are never destroyed for the sake of reusability. In
  4296. the incremental run, the task solved by sysgen is rather complicated: none but
  4297. certain (changed) files are touched,to preserve the build's timestamps. Also, for a
  4298. reasonably small number of files changed the incremental run was thought to consume
  4299. far less time, than the full run, and for some time it was, indeed, true.
  4300. Lately, the investigation for advanced aggregation strategy has been taken. This
  4301. was mainly due to poor aggregation timings, but also due to possible future transition
  4302. to the build scenario, in which some or even all the assumptions the aggregation
  4303. relies upon, may change.
  4304. Let's take the bird eye view on the task aggregation solves.
  4305. Sure, most of the files in the %LANG% build would eventually be identical to the US
  4306. ones. Some, noticeably the text files will be totally different from the US but
  4307. just identical to the ones found in localizer's drop.
  4308. Some (noticeably executable) bits are specific: most resources are language
  4309. specific and thus, not interchangeable. These files never existed before the
  4310. aggregation took place.
  4311. So the main and only task of aggregation is to decide, how to build the file tree.
  4312. It is also becoming important, the procedure must be as efficient and easily
  4313. configurable as possible. The least important, even seldomly stated clearly,
  4314. goal is to make the internals of the sysgen script simple to maintain and modify,
  4315. template compliant etc. etc.
  4316. This document describes certain features the sysgen is capable, along with certain
  4317. file specifications, focusing on the recent features. For the introduction to sysgen
  4318. and aggregation principles, see http://ntbld/whistler/intl/sysgen.htm and other
  4319. documents on NT Whistler International build web site http://ntbld/intl
  4320. =head1 SYNOPSIS
  4321. perl sysgen.pl [<options>] [<macros>] [<targets>]
  4322. where
  4323. Options:
  4324. /c generate the makefile from scratch, overwriting existing one.
  4325. By running nmake, all the targets will be generated.
  4326. /s limit sysgen to syntax check - makefile not (re)generated.
  4327. /f <name> takes <name> as the sysfile.
  4328. If this option is omitted, sysgen searches the current
  4329. directory for a file called sysfile and uses it as a
  4330. description (mapping) file.
  4331. /w <name> takes <name> as the PATH for reading/writing
  4332. makefile and err/log files. Note that the default
  4333. aggregation 'makefile' name is sysgen.mak
  4334. /v display version
  4335. /l:<LANG> specify language
  4336. /?
  4337. /h display this message
  4338. Macros:
  4339. list the command line macro definitions in the format
  4340. "<macroname>=<macrovalue>". Seldomly used
  4341. Targets:
  4342. specify files (without path) to localize/aggregate.
  4343. You may use the TEMPLATE COMPLIANT
  4344. -<flag>:<value>
  4345. syntax or OLD sysgen
  4346. -<flag> <value>
  4347. syntax or both. There is little need to specify any option but the language
  4348. on command line. The only one case you may need it is when you verify the mappings.
  4349. In this case you will have to type
  4350. perl sysgen.pl -l:<LANG> -c [-a] [-s]
  4351. The working directory of sysgen.pl is now arbitrary, and one is able to specify
  4352. * mappings file
  4353. * output folder
  4354. * language
  4355. via command line switches. In the past, it was crucial to change to the directory
  4356. %RAZZLETOOLPATH%\POSTBUILDSCRIPTS\SYSGEN\RELBINS\%LANG%
  4357. in order to have sysgen running. Not anymore! Once again, this means one can
  4358. execute multiple language and architecture aggregations on the same box at once.
  4359. =head1 DESCRIPTION
  4360. Historically, there are four different ways the sysgen.pl can execute.
  4361. One of these is totally obsolete now, and one is not of practical use by the time of
  4362. this writing. So only two modes you are to learn although all four are still available.
  4363. Below is the list.
  4364. * acceletared aggregation
  4365. * full aggregation
  4366. * powerless syntax check (no aggregation)
  4367. * old incremental aggregation
  4368. The command line flags specify the desired mode, the default is (still!) being full
  4369. aggregation, without acceletarion. That corresponds to the incremental postbuild.
  4370. The accelerated aggregation is the fastest mode and corresponds to full postbuild.
  4371. It may seem strange but it is not the default mode yet.
  4372. The syntax check sysgen provides the quickest way to test the validity of the mappings
  4373. and produces no output, garbages no screen, but detects all errors that would hurt
  4374. the real aggregation, but cannot be used for anything else.
  4375. You may never encounter any need in powerless syntax check. The sysgen
  4376. is never executed in this way by the real postbuild.
  4377. Last, the old incremental aggregation is totally obsolete by now, though still
  4378. accessible. As a matter of fact, it does not give any time savings, neither is it more
  4379. reliable that the rest of the modes.
  4380. Alas, have you dropped the command line arguments completely, the incremental
  4381. aggregation would be fired. It will end up almost immediately, though, since it
  4382. relies on the presence of the makefile (filename is sysgen.mak) in the current working
  4383. directory, which hardly will be the case.
  4384. Admittedly, accelerated aggregation is approximately three times quicker than the
  4385. non-accel one. It may become the default by the time of your reading this.
  4386. The accelerated aggregation is a part of a full postbuild.
  4387. =head1 MAKEFILE
  4388. Generation of the files in the %_NTPOSTBLD% is accomplished through executing the
  4389. nmake.exe with the Makefile, described below. The default location of the Makefile is
  4390. %TEMP%\%LANG% and the default name is sysgen.mak. After the successful aggregation the
  4391. makefile is copied to the %_NTPOSTBLD%\%LANG%\BUILD_LOGS folder.
  4392. Sure the sysgen.mak represents a syntaxically correct makefile.
  4393. However, the aggregation makefiles are seldom used more than once by the current
  4394. design. It is more important to have is a useful for a post-break post-mortem
  4395. study.
  4396. The Makefile shown below corresponds to version 3.0x and later of SYSGEN.
  4397. [change #52 in //depot/private/intlred/tools/PostBuildScripts/sysgen.pl]
  4398. It is not syntaxically different from the older versions, though certain effort
  4399. has been applied to make it a little bit slick and e.g. easy to browse.
  4400. For example, the total size of the makefile (adding all the COMPDIR listing files,
  4401. to be honest) is around 1 Megabyte by now. Compare it with the typical makefile of
  4402. the early versions of the sysgen (i.e. before the COMPDIR was first used, let's use
  4403. the term version 3.0x).
  4404. The makefile for the same build weighted up to and over 10 Megabytes.
  4405. It was virtually impossible to diff the makefiles between the consequent builds, or
  4406. languages. This made the typical investigation for the origin of certain files more
  4407. difficult.
  4408. The sysgen.mak file:
  4409. -------- top of the file -----------------------------------
  4410. sysgen:
  4411. @nmake /A /K /F d:\lp.temp.x86fre\GER\sysgen.mak /NOLOGO all
  4412. ------- Definition of the target to build ------------------
  4413. !IFNDEF LANG
  4414. ! ERROR You must define macro LANG
  4415. !ENDIF
  4416. !IF "$(LANG)" != "GER" && "$(LANG)" != "ger"
  4417. ! ERROR This file is for GER
  4418. !ENDIF
  4419. !IF "$(_BUILDARCH)" != "x86"
  4420. ! ERROR This file is for x86
  4421. !ENDIF
  4422. ------ header: lagguage and arch definitions ---------------
  4423. # Directory name aliases
  4424. _NTLOCDIR=$(_NTBINDIR)\loc\res\$(LANG)
  4425. # Binary file operation aliases
  4426. COMPDIR=compdir.exe /ukerlntsd
  4427. BINGEN=bingen -n -w -v -f
  4428. # Note: one must omit the extension here
  4429. # to enable the logerr 'magic'
  4430. COPY=copy
  4431. ------- varialbes definitions ------------------------------
  4432. .SILENT:
  4433. all: \
  4434. _DIRS _COMPDIR PROCESS1601 PROCESS1602 PROCESS1603 .... PROCESS1616
  4435. ------- pseudotarget expansion definitions -----------------
  4436. PROCESS1601: \
  4437. $(_NTPOSTBLD)\192.dns \
  4438. $(_NTPOSTBLD)\31x5hc01.cnt \
  4439. $(_NTPOSTBLD)\31x5hs01.cnt \
  4440. ------- target make rules ----------------------------------
  4441. _DIRS:
  4442. md $(_NTPOSTBLD)\dtcinf 2>NUL
  4443. ...
  4444. _COMPDIR: \
  4445. $(TEMP)\COMPDIR\compdir.001.mak \
  4446. ...
  4447. $(TEMP)\COMPDIR\compdir.735.mak
  4448. logerr "$(COMPDIR) /m:$(TEMP)\COMPDIR\compdir.001.mak $(_NTTREE) $(_NTPOSTBLD)"
  4449. ...
  4450. $(_NTPOSTBLD)\192.dns: $(_NTLOCDIR)\windows\misc\192.dns
  4451. logerr "$(COPY) $(_NTLOCDIR)\windows\misc\192.dns $(_NTPOSTBLD)\"
  4452. ...
  4453. $(_NTPOSTBLD)\wow6432\wowreg32.exe: $(_NTTREE)\wow6432\wowreg32.exe \
  4454. $(_NTLOCDIR)\windows\tokens\wow6432\wowreg32.ex_
  4455. logerr "$(BINGEN) -p 1252 -o 0x07 0x01 -r $(_NTTREE)\wow6432\wowreg32.exe..."
  4456. ------- end of Makefile -----------------------------------
  4457. =head1 MAKING LINKS
  4458. The distinctive feature of version 3.0x of aggregation is using the compdir.exe
  4459. to arrange hard links for the aggregation rather then perform file-by-file copy.
  4460. It has not been investigated too rigorously, whether the speedup was achieved
  4461. because of
  4462. * reducing the number of steps executed due to
  4463. grouping the files by directory (~10000 file ops versus ~100 lists ops)
  4464. * generation of links instead of copying the files
  4465. Basically, it is possible to investigate: compdir's rich set of flags allows one.
  4466. In fact, no explicit command name is used by current makefile, but only macros.
  4467. The COPY, COMPDIR, BINGEN macros definitiond are found in sysfile.inc:
  4468. SET COMPDIR=compdir.exe /ukerlntsd
  4469. SET COPY=copy
  4470. SET BINGEN=bingen -n -w -v -f
  4471. Now, getting actual command changed is a matter of macro definition. No need to edit
  4472. a letter in the aggregation script.
  4473. Now, the COPY and COMPDIR macros operations are both present. Why not removing
  4474. the COPY completely? The next section explains, why.
  4475. =head1 OPTIMIZATION
  4476. Where are the time savings coming from? The statistics shows that there are as many as
  4477. 35350 files in
  4478. 750 directories.
  4479. handled now by compdir.exe and this saves up to 60% of former execution time.
  4480. At present there are ~250 'explicit' copy operations left for GER aggregation,
  4481. and almost ~1500 copy operations for ARA aggregation.
  4482. The reason for such a difference is that rsrc compiler was discovered to be sensitive
  4483. to whether the resource it operates is a link or separate copy. The generic
  4484. rsrc build instruction involves both %_NTTREE% and %_NTPOSTBLD% libraries.
  4485. Too bad if they are links to each other: rsrc will simply fail to do the job.
  4486. The problem is, sysgen does not really seem to trace the inter-file relations
  4487. well enough. This may be re-designed in the future versions. To put is short,
  4488. there is no easy way to identify the rsrc files from the rest when
  4489. looking 'from inside' of the sysgen.
  4490. The approach chosen was based on a minimal tradeoff between the execution acceleration
  4491. and complexities introduced.
  4492. The default policy of sysgen is always to take COPY, not COMPDIR approach.
  4493. By the way, the COMPDIR approach means here not just applying the compdir.exe with
  4494. certain flags (as we have seen this task is trivial and may be achieved by re-defining
  4495. the COPY macro in the sysfile.inc) but rather complex:
  4496. generating some file hash info for subsequent grouping the files by source/destination
  4497. directories etc. etc.
  4498. First, sysgen does a clever guess about what is the file copying serve for. This means,
  4499. it is able to factor out files going alone %_NTTREE%->%_NTPOSTBLD% trails,
  4500. and lets compdir.exe do that. This gives the major contribution:
  4501. 28002 files
  4502. 650 directories.
  4503. which is almost 80% counted in file numers, not times.
  4504. However, files which propagate between %_NTLOCDIR% and %_NTPOSTBLD% do not provide the
  4505. possibility for file name, path based clever guess. This corresponds to the whole
  4506. localizer tree and leaves the problem open.
  4507. To teach sysgen to select files, that are safe passing to COMPDIR, is a complex task.
  4508. The solution is rely upon the simplest test to enforce COMPDIR handling for
  4509. some files and allow COPY the rest. The more bulk of files get handled the better.
  4510. Thus file operation may be determined by the file EXTENSION.
  4511. Sysgen simply reads extension hints in the mapping files to know the file is
  4512. safe to pass to COMPDIR. No hints - no risky optimization. Old behavior restored.
  4513. In future design, the logic of the sysgen may be refined. However, at
  4514. present state of the optimization, there is already 10 to 1 reduction of the
  4515. file ops and 3 to 1 reduction in time. It is clearly hard to beleive the next factor
  4516. 3 acceleration possible.
  4517. =head1 MAPPING FILES
  4518. Sysgen reads mapping files for variable and build tree directory structure
  4519. definitions. Here we concentrate on mapping file paths.
  4520. For the versions of sysgen prior and including 3.0x, the directory
  4521. occupued by mapping file 'sysfile' was in fact unique for each
  4522. %LANG% and fixed. This introduced redundancy between the mapping files and
  4523. leaded to maintenance complexity.
  4524. For compatibility reasons, this structure is still present in the SD, the
  4525. parses able to work with it, etc.
  4526. Starting with version 4.0x of sysgen, the layout of mapping files has
  4527. been simplified to make the aggregation more flexible and robust and to enable
  4528. more changes without the need to fix the Perl code.
  4529. Of course, necessary functionality has been implemented to help factoring out
  4530. apparently redundant definitions.
  4531. In particular, mapping file paths and names are no longer hardcoded, e.g.
  4532. sysfile location may be specified to the sysgen via a command
  4533. line 'f' argument, while makefile location via the 'w' argument. No arguments
  4534. are mandatory and default to current directory. Also, this leads to ability of
  4535. running multiple language and architecture aggregations on the same box at once.
  4536. The include tree of the mapping files is schematically displayed below.
  4537. sysgen.pl
  4538. * sysfile.inc
  4539. * hotfix.inc
  4540. * scp.inc
  4541. * mapfix.inc
  4542. * iis.inc
  4543. * con.inc
  4544. * msmq.inc
  4545. * smtpnntp.inc
  4546. * netmeeting.inc
  4547. * indexsrv.inc
  4548. * fax.inc
  4549. * exch_se.inc
  4550. * bldbins.inc
  4551. * ntloc.inc
  4552. The number of mapping files exists is determined by the the number of projects.
  4553. See the
  4554. %_NTDRIVE%\LOC\RES\%LANG%
  4555. directory structure.
  4556. The bldbins.inc and ntloc.inc map
  4557. %_NTTREE%
  4558. and
  4559. %_NTDRIVE%\LOC\RES\%LANG%\WINDOWS
  4560. directories and their subdirectories, respectively.
  4561. The hotfix.inc file contains rules that are to be applied 'as is' and
  4562. otherwise confuse the sysgen [see HOTFIX FILE SYNTAX below].
  4563. The mapfix.inc file is reserved to the unapproved mapping fixes required e.g.
  4564. to fix bugs but less essential for the build to succeed. It may disappear in the
  4565. future versions of the sysgen.
  4566. Also, external drop mappings are planned to move in one mapping file.
  4567. In a more distant future, it is planned to have some convenient tool to wirk with
  4568. mapping files.
  4569. =head1 MAPPING FILE SYNTAX
  4570. The syntax of the mapping files did not change too much, to provide
  4571. backward compatibility with versions 3.0x abd below.
  4572. The descriprion may be found in http://ntbld/whistler/intl/Sysgen.htm
  4573. In fact, much effort has been applied to make the parser features stable.
  4574. =head1 HOTFIX FILE SYNTAX
  4575. The hotfix.inc file contains rules that are to be applied 'as is' and would
  4576. otherwise confuse the sysgen.
  4577. Sure, the syntax of the hotfix.inc is specific.
  4578. The reason for introducing the hotfix.inc file comes from the fact that the
  4579. assumption the aggregation is based upon:
  4580. the existence of one-to-one correspondence
  4581. between the token and its binary file, can sometimes be not true.
  4582. Rules written in the hotfix file apply exactly to such situations and
  4583. are to be considered patches to makefile,
  4584. but applied before passing the makefile to nmake.
  4585. Below you see the part of the hotfix.inc file
  4586. IF "$(LANG)" == "KOR"
  4587. BEGIN_RULES
  4588. SET MULTITOKDIR=$(_ntbindir)\loc\res\$(lang)\windows\tokens\multi
  4589. SET HOTFIXDIR=$(_ntbindir)\loc\res\tools\twisttok
  4590. SET DST=$(_ntpostbld)\$(lang)
  4591. # version 3.x
  4592. SET DST=$(_ntpostbld)
  4593. # version 4.x
  4594. SET SRC=$(_nttree)
  4595. SET ACP0=1252
  4596. SET ACP1=949
  4597. SET PriLangID0=0x09
  4598. set SubLangID0=0X01
  4599. SET PriLangID1=0x12
  4600. set SubLangID1=0X01
  4601. SET OVERWRITE_DEFAULT_RULE=1
  4602. $(dst)\netmsg.dll : $(src)\netmsg.dll $(multitokdir)\netmsg.dl_
  4603. logerr "echo bug 423602"
  4604. logerr "bingen -n -w -v -f -p $(ACP0) -o $(PriLangID0) ..."
  4605. logerr "bingen -n -w -v -f -p $(ACP1) -o $(PriLangID1) ..."
  4606. END_RULES
  4607. ENDIF
  4608. It is evident that the dependency block has a typical Makefile,
  4609. rather then sysgen mapping syntax.
  4610. Notice also the new BEGIN_RULES/END_RULES tags. These separate the hotfix section
  4611. from the rest of the file.
  4612. In fact, the contents of the mapping enclosed by these targs are not parsed at all.
  4613. =head1 OUTLOOK OF VERSION 4.0x VALUABLE FEATURES
  4614. 1. Produce well-formed good-looking Makefile.
  4615. E.g. use of dotted and preprocessing nmake directives.
  4616. 2. Fix the mapping file structure to ensure that keeping the mapping files
  4617. in the folders %RAZZLETOOLPATH%\postbuildscripts\sysgen\relbins\%LANG%
  4618. is no longer required to execute the aggregation.
  4619. E.g. provide language setting on command line
  4620. 3. Extend the parser e.g. to recognize constructs:
  4621. IF "$(LANGUAGE)" == "INTL" || "$(LANGUAGE)" == "intl"
  4622. This is to mimic the makefile syntax (nmake):
  4623. !IF "$(LANGUAGE)" == "INTL" || "$(LANGUAGE)" == "intl"
  4624. Thus one can verify that given $LANGUAGE = CHT
  4625. &testCompoundIF("IF $(LANG) == CHT"); TRUE
  4626. &testCompoundIF("IF $(LANG) == CHS || ($(LANG) == CHT)"); TRUE
  4627. &testCompoundIF("IF $(LANG) == CHS"); FALSE
  4628. &testCompoundIF("IF ($(LANG) == CHT) && ($(LANG) == CHS) "); FALSE
  4629. &testCompoundIF("IF $(LANG) == chs"); FALSE
  4630. &testCompoundIF("IF \"\" == \"\" "); TRUE
  4631. This ability could be useful to factor out the repeated blocks in the mapping
  4632. files (currently underway)
  4633. 4. Work on the aggregation code timings.
  4634. Profiling the script(full accelerated aggregation).
  4635. Total Elapsed Time = -10.7283 Seconds
  4636. User+System Time = 146.7325 Seconds
  4637. Exclusive Times
  4638. %Time ExclSec CumulS #Calls sec/call Csec/c Name
  4639. 31.6 46.49 57.020 148164 0.0003 0.0004 main::MakeXcopyCmd
  4640. 13.1 19.22 19.114 37423 0.0005 0.0005 main::NewImageToSymbol
  4641. 12.5 18.43 32.083 1 18.435 32.083 main::AddFiles
  4642. 9.52 13.97 12.928 348653 0.0000 0.0000 main::SetField
  4643. 8.91 13.07 12.923 51459 0.0003 0.0003 main::RevEnvVars
  4644. 5.89 8.642 95.413 35868 0.0002 0.0027 main::GenXcopy
  4645. 4.47 6.554 6.120 145184 0.0000 0.0000 main::PushFieldVal
  4646. 4.40 6.463 5.000 488408 0.0000 0.0000 main::GetMacro
  4647. 4.21 6.174 19.067 1688 0.0037 0.0113 main::AddRecord
  4648. 3.07 4.506 4.506 1 4.5060 4.5060 main::Find_UnMapping
  4649. 2.40 3.516 10.984 47581 0.0001 0.0002 main::AddFileInfo
  4650. 2.03 2.985 2.874 37423 0.0001 0.0001 main::GetDynData
  4651. 1.64 2.412 13.357 47581 0.0001 0.0003 main::AddEntry
  4652. 1.17 1.720 106.19 37423 0.0000 0.0028 main::RecordToCmd
  4653. 1.08 1.581 1.317 88136 0.0000 0.0000 main::IsRepositKey
  4654. Profiling the script(syntax check).
  4655. Total Elapsed Time = -5.11500 Seconds
  4656. User+System Time = 42.05962 Seconds
  4657. Exclusive Times
  4658. %Time ExclSec CumulS #Calls sec/call Csec/c Name
  4659. 44.5 18.73 31.126 1 18.730 31.126 main::AddFiles
  4660. 19.5 8.220 7.372 243189 0.0000 0.0000 main::SetField
  4661. 15.0 6.345 19.905 1688 0.0038 0.0118 main::AddRecord
  4662. 11.1 4.706 4.706 1 4.7060 4.7060 main::Find_UnMapping
  4663. 7.93 3.335 10.111 47581 0.0001 0.0002 main::AddFileInfo
  4664. 5.14 2.161 12.138 47581 0.0000 0.0003 main::AddEntry
  4665. 3.21 1.351 1.042 88136 0.0000 0.0000 main::IsRepositKey
  4666. 2.49 1.048 1.914 37423 0.0000 0.0001 main::RecordToCmd
  4667. 2.31 0.971 0.834 39011 0.0000 0.0000 main::GetFieldVal
  4668. 1.57 0.660 0.398 74846 0.0000 0.0000 main::IsHashKey
  4669. 1.31 0.550 0.378 49147 0.0000 0.0000 main::IsEmptyHash
  4670. 1.14 0.480 2.394 1 0.4800 2.3940 main::FillCmd
  4671. 1.11 0.468 1.410 1 0.4678 1.4103 main::FillTokens
  4672. 0.78 0.330 0.193 39274 0.0000 0.0000 main::GetMacro
  4673. 0.65 0.272 0.146 35868 0.0000 0.0000 main::GenXcopy
  4674. Clearly, the time consuming operations involve various file tests
  4675. which cannot be skipped.
  4676. =head1 LIMITATIONS
  4677. * the currently unused sysgen run modes are not being tested
  4678. too thoroughfully and may happen to fail or produce weird results
  4679. support for the obsolete modes of might terminate
  4680. * the syntax of the mapping files is archaic.
  4681. the parser cannot dynamically bias the expected to the actual
  4682. mapping file syntax (not all fields are used)
  4683. * aggregation toolset (sysgen.pl/locag.pl/mtnmake.cmd/sysfile etc.)
  4684. backward compatibility is slim
  4685. =head1 AUTHOR
  4686. Contact Serguei Kouzmine sergueik@microsoft.com
  4687. =head2 Notes
  4688. cmd.exe can live with forward slashed instead of bachward slashes
  4689. However, it needs to quote the filenames to have this working
  4690. dir /ad "c:/program files"
  4691. Volume in drive C has no label.
  4692. Volume Serial Number is 58F7-C69D
  4693. Directory of c:\program files
  4694. 04/16/2002 11:10 AM <DIR> .
  4695. 04/16/2002 11:10 AM <DIR> ..
  4696. ...
  4697. 1. hotfix:
  4698. make use of the "env" variables inherited from codes.txt
  4699. 2. get meaningful names!
  4700. LANGUAGE => SRC_LANG
  4701. ALT_LANG => LANG
  4702. !IFNDEF LANG
  4703. ! ERROR You must define macro LANG
  4704. !ENDIF
  4705. DEST=$(_NTPOSTBLD)\$(LANG)
  4706. !ERROR $(DEST)
  4707. D:\test\tools\PostBuildScripts\sysgen.pl : D:\test\tools\PostBuildScripts\sysgen.dep D:\test\tools\PostBuildScripts\sysgen.dep2
  4708. echo $@ " " $?
  4709. BEGIN_MESSAGES_MAP
  4710. #Code Description LANG
  4711. #---------------------------------------------------------------
  4712. 1001 sysfile not found @EU;@CS;@FE
  4713. 1002 file not found @EU;@CS;@FE
  4714. 1003 file not found: unable to run sysgen incrementally @EU;@CS;@FE
  4715. 1004 target file not found @EU;@CS;@FE
  4716. 1005 filter file not found @EU;@CS;@FE
  4717. 1006 unable to open file for reading @EU;@CS;@FE
  4718. 1007 unable to open file for writing @EU;@CS;@FE
  4719. 1008 /f option requires a filename @EU;@CS;@FE
  4720. 1009 target not found in filter file @EU;@CS;@FE
  4721. 1010 /w option requires a directory name @EU;@CS;@FE
  4722. 1011 /n option requires number of sections@EU;@CS;@FE
  4723. 1101 @EU;@CS;@FE
  4724. 1110 syntax error @EU;@CS;@FE
  4725. 1111 syntax error: ENDIF unexpected @EU;@CS;@FE
  4726. 1112 syntax error: IF unexpected @EU;@CS;@FE
  4727. 1113 syntax error: END_MAP unexpected @EU;@CS;@FE
  4728. 1114 syntax error: BEGIN_*_MAP unexpected @EU;@CS;@FE
  4729. 1115 syntax error: INCLUDE unexpected @EU;@CS;@FE
  4730. 1116 syntax error: unknown operator in expression @EU;@CS;@FE
  4731. 1117 syntax error: \")\" missing in macro invocation @EU;@CS;@FE
  4732. 1118 syntax error: incomplete description line @EU;@CS;@FE
  4733. 1119 syntax error: unknown mapping type @EU;@CS;@FE
  4734. 1120 syntax error: unmatched IF @EU;@CS;@FE
  4735. 1121 syntax error: unmatched BEGIN_*_MAP @EU;@CS;@FE
  4736. 1210 file format error: target not found @EU;@CS;@FE
  4737. 1211 file format error: target not listed in \"all\" @EU;@CS;@FE
  4738. 1212 file format error: no description blocks for files @EU;@CS;@FE
  4739. 1213 file format error: \"sysgen\" target not found @EU;@CS;@FE
  4740. 1214 file format error: filename with special characters @EU;@CS;@FE
  4741. 1215 file format error: Similar target found @EU;@CS;@FE
  4742. 1910 unknown language @EU;@CS;@FE
  4743. 1911 missing or duplicated entry for language @EU;@CS;@FE
  4744. 1912 incomplete entry for language @EU;@CS;@FE
  4745. 1913 unknown class for language @EU;@CS;@FE
  4746. 2011 no binary found for token @EU;@CS;@FE
  4747. 2012 duplicated tokens @EU;@CS;@FE
  4748. 2013 unknown token type (bingen or rsrc) @EU;@CS;@FE
  4749. 2014 unexpected token for already localized binary @EU;@CS;@FE
  4750. 2015 input bingen token not found for multi @EU;@CS;@FE
  4751. 2016 no bingen token found for custom resource @EU;@CS;@FE
  4752. 2017 unknown bingen token extension in token filename @EU;@CS;@FE
  4753. 2018 both bingen and rsrc tokens found @EU;@CS;@FE
  4754. 2019 custom resource found for rsrc token @EU;@CS;@FE
  4755. 2020 custom resource with no token @EU;@CS;@FE
  4756. 2051 sysfile error: undefined source path to verify @EU;@CS;@FE
  4757. 2052 folder not covered in sysfiles @EU;@CS;@FE
  4758. 2053 not mapped @EU;@CS;@FE
  4759. 2054 not mapped @EU;@CS;@FE
  4760. 2101 binary not found @EU;@CS;@FE
  4761. 2102 token not found @EU;@CS;@FE
  4762. 3011 internal error: unknown operation type @EU;@CS;@FE
  4763. 4001 filename with whitespace badly handled @EU;@CS;@FE
  4764. 5001 _COMPDIR currently only supported in clean build @EU;@CS;@FE
  4765. 5002 Incremental run with COMPDIR @EU;@CS;@FE
  4766. END_MAP
  4767. my $d = " 5002 Incremental run with COMPDIR \@EU;\@CS;\@FE";
  4768. ($a, $b, $c) = $d =~ m/\s*([^ ]+)\s{4,}([^ ].+[^ ])\s{4,}([^ ]+)/;
  4769. print STDERR $a, "\n", $b, "\n", $c, "\n\n";
  4770. getting rid of
  4771. ;[Lang] [ACP] [LCID] [PriLangID] [SubLangID] [Class] [Flavor] [Site] [LangISO] [PerfID] [Readme] [GUID] [Read1st] [Home] [Comments]
  4772. ;-------------------------------------------------------------------------------------------------------------------------------------------
  4773. ;
  4774. ARA 1256 0x0401 0x01 0x01 @CS WKS REDMOND AR 001 readme AF202818-350E-11d2-B167-0060B03C1CA5 read1st home Arabic
  4775. =head1 LOCSTUDIO BUILD HANDLING
  4776. =head2 OBSOLETE INSTRUCTIONS
  4777. Different ~classes~ of bingen/rsrc command must be appropriately mapped to LSBUILD.EXE
  4778. Sample INTL build commands (represent different execution flag sets for BINGEN and RSRC.):
  4779. 1. BINGEN
  4780. 1.1 replace resources (85%). Pseudorule:
  4781. $(BINGEN) -p $(ACP) -o $(PriLangID) $(SubLangID) -r $** $@
  4782. expands into:
  4783. bingen -n -w -v -f -p 932 -o 0x11 0x01 -r $(_NTTREE)\user32.dll $(_NTLOCDIR)\windows\tokens\user32.dl_ $(_NTPOSTBLD)\user32.dll
  4784. 1.2. add the LANG resources and leave the US ones alone so that the target binary has both (~10%). Pseudorule:
  4785. $(BINGEN) -p $(ACP) -o $(PriLangID) $(SubLangID) -i $(PriLangUSA) $(SubLangUSA) -a $(_NTTREE)\aciniupd.exe $(_NTLOCDIR)\windows\tokens\multi\aciniupd.ex_ $@
  4786. expands into:
  4787. bingen -n -w -v -f -p 932 -o 0x11 0x01 -i 0x09 0x01 -a $(_NTTREE)\aciniupd.exe $(_NTLOCDIR)\windows\tokens\multi\aciniupd.ex_ $(_NTPOSTBLD)\aciniupd.exe
  4788. 1.3. Multiple resources for a single binary, e.g. embedded resources such as INF files and XML files (1%)
  4789. $(_NTPOSTBLD)\photowiz.dll: $(_NTTREE)\photowiz.dll $(_NTLOCDIR)\windows\tokens\photowiz.dl_ \
  4790. $(_NTLOCDIR)\windows\tokens\photowiz_dll_reginst.inf \
  4791. $(_NTLOCDIR)\windows\tokens\photowiz_dll_tmpldata.xml
  4792. $(BINGEN) -p $(ACP) -o $(PriLangID) $(SubLangID) -r $(_NTTREE)\photowiz.dll $(_NTLOCDIR)\windows\tokens\photowiz.dl_ $@
  4793. 2. BIDI specific resource manipulation command (RSRC)
  4794. 2.1 Replace
  4795. rsrc $(_NTPOSTBLD)\w3isapi.dll -r $(_NTLOCDIR)\windows\tokens\w3isapi.dll.rsrc -l 411 (~4% - ALL LANGS)
  4796. 2.2 Append
  4797. rsrc $(_NTPOSTBLD)\winnt32\winnt32.exe -a $(_NTLOCDIR)\windows\tokens\winnt32\multi\winnt32.exe.rsrc -l 40D (@CS specific)
  4798. 2.3 Merge several LANG
  4799. $(_NTPOSTBLD)\comdlg32.dll: $(_NTTREE)\comdlg32.dll $(_NTLOCDIR)\windows\tokens\multi\comdlg32.dll.40d.rsrc $(_NTLOCDIR)\windows\tokens\multi\comdlg32.dll.80d.rsrc
  4800. rsrc $(_NTPOSTBLD)\comdlg32.dll -a $(_NTLOCDIR)\windows\tokens\multi\comdlg32.dll.40d.rsrc -l 0x040d (@CS specific exception )
  4801. =head1 MODIFICATIONS to aggregation logic and MAPPING files.
  4802. Add coverage to make sysgen aware of the .lcx files
  4803. =head1 Building the LSBUILD.EXE rules
  4804. =head2 1. When the lcx file is supplied, the .dl_ or .ex_ file must be deleted.
  4805. If it is not the aggregation is greately confused.
  4806. If both .dl_ token and .lcx tonken are found, the .dl_ one wins.
  4807. This is because of possible multiple resources for the binary case, discussed above.
  4808. =head2 2. Errors generated from missing .dl_ files:
  4809. SYSGEN: error 2102: d:\ls\loc\res\JPN\windows\tokens\dpcdll.dl_: token not found
  4810. This fix is delete the explicitt mapping line (scp.inc)
  4811. $(dst) dpcdll.dll $(tok) dpcdll.dl_ - -r - @EU;@FE
  4812. =head2 1. Errors from discovered .lcx files:
  4813. SYSGEN: error 2013: d:\ls\loc\res\JPN\netmeeting\tokens\nmevtmsg.dll.lcx: unknown token type (bingen or rsrc)
  4814. Verify the extensions are mapped in sysfile.inc:
  4815. Provide the dummy rule for .dll <= .dll.lcx
  4816. .exe <= .exe.lcx
  4817. BEGIN_EXTENSIONS_MAP
  4818. # TokExt BinExt Langs
  4819. #----------------------
  4820. .a_ .ax @EU;@FE;@CS
  4821. .ac_ .acm @EU;@FE;@CS
  4822. .ax_ .ax @EU;@FE;@CS
  4823. .bi_ .bin @EU;@FE;@CS
  4824. .cn_ .cnv @EU;@FE;@CS
  4825. .co_ .com @EU;@FE;@CS
  4826. .cp_ .cpl @EU;@FE;@CS
  4827. .dl_ .dll @EU;@FE;@CS
  4828. .dr_ .drv @EU;@FE;@CS
  4829. .ds_ .ds @EU;@FE;@CS
  4830. .ef_ .efi @EU;@FE;@CS
  4831. .ex_ .exe @EU;@FE;@CS
  4832. .fl_ .flt @EU;@FE;@CS
  4833. .im_ .ime @EU;@FE;@CS
  4834. .mo_ .mod @EU;@FE;@CS
  4835. .ms_ .mst @EU;@FE;@CS
  4836. .oc_ .ocx @EU;@FE;@CS
  4837. .rs_ .rsc @EU;@FE;@CS
  4838. .sc_ .scr @EU;@FE;@CS
  4839. .sy_ .sys @EU;@FE;@CS
  4840. .tl_ .tlb @EU;@FE;@CS
  4841. .ts_ .tsp @EU;@FE;@CS
  4842. .wp_ .wpc @EU;@FE;@CS
  4843. .sys.rsrc .sys @EU;@CS;@FE
  4844. .dll.rsrc .dll @EU;@CS;@FE
  4845. .exe.rsrc .exe @EU;@CS;@FE
  4846. .drv.rsrc .drv @EU;@CS;@FE
  4847. .acm.rsrc .acm @EU;@CS;@FE
  4848. .cpl.rsrc .cpl @EU;@CS;@FE
  4849. .scr.rsrc .scr @EU;@CS;@FE
  4850. .dll.lcx .dll @EU;@FE;@CS
  4851. .exe.lcx .exe @EU;@FE;@CS
  4852. END_MAP
  4853. ===
  4854. print "reducing reduntant LCX mapping patterns:\n";
  4855. $a="(\\.\\w+)\\.lcx";
  4856. $b="\$1";
  4857. $p = "file.foo.lcx";
  4858. print $p,"\n";
  4859. print "$a $b\n";
  4860. print qq("$a $b"), "\n";
  4861. $b =~ s|\$(\d)|$1|g;
  4862. $p =~ s|$a|${$b}|egi;
  4863. print $p,"\n";
  4864. $p = "file.BAR.lcx";
  4865. print $p,"\n";
  4866. $p =~ s|(\.\w+)\.lcx$|$1|eg;
  4867. print $p,"\n";