6670 lines
196 KiB
Prolog
6670 lines
196 KiB
Prolog
## Method calling graph ( bit simplified for readability)
|
|
## -------------------------------------------------------------
|
|
##
|
|
## ParseArgs::parseargs -> UseOldParser
|
|
## GetSysDir
|
|
## SwitchWorkDir
|
|
## CleanLogFiles
|
|
## LoadEnv
|
|
## MakeRevEnvVars
|
|
## LoadHotFixFile
|
|
## LoadCodes -> define macros
|
|
## LoadReposit -> LoadKeys -> SetField
|
|
## -> AddTargetPath
|
|
## LoadCmd -> SetField
|
|
##
|
|
## MakeRevEnvVars
|
|
## ReadSysfile -> ReadLine -> LoadRecord -> LineToRecord
|
|
## -> cklang::CkLang
|
|
## -> AddRecord
|
|
## -> SetMacro
|
|
## -> LogRecord
|
|
## -> ReplaceVar
|
|
##
|
|
## -> LoadRuleBlock
|
|
## -> ReadBlock
|
|
## -> ReadRules
|
|
## -> SetMacro -> SetAttribOp
|
|
## -> SetIfFlag -> ReplaceVar
|
|
## -> StrToArray
|
|
## -> Message
|
|
## -> ReadSysfile
|
|
## VerifyCover -> SetMacro
|
|
## -> Create_Tree_Branch -> Get_Except -> GetMacro
|
|
## -> Find_Dot_Dot_Dot -> Match_Subjects
|
|
## -> Remove_Dummy_Create_Branch
|
|
## -> Remove_Root_List
|
|
## -> Find_UnMapping -> readdir -> Error
|
|
## PopulateReposit -> FillFilter
|
|
## -> AddFiles
|
|
## -> AddEntry -> AddFileInfo
|
|
## -> AddTargetPath
|
|
## -> AddTarget
|
|
##
|
|
## -> GetMacro
|
|
## -> IsEmptyHash
|
|
## -> IsHashKey
|
|
## -> IsFoundTarget
|
|
## -> FillTokens -> FillTokEntry
|
|
## -> FillCmd -> IsEmptyHash
|
|
## -> IsHashKey -> GetFieldVal
|
|
## -> RecordToCmd -> GetFieldVal
|
|
## -> IsRepositKey
|
|
## -> GenXcopy
|
|
## -> GenMUICmd
|
|
## -> GenLocCmd -> GenRsrcCmd
|
|
## -> GenLcxCmd (LOC STUDIO)
|
|
## -> GenBgnCmd -> SetSymPaths
|
|
## -> MakeXcopyCmd -> RevEnvVars
|
|
## -> GetLocOpType
|
|
## -> SetBgnSymSw
|
|
## -> GetBgnICodesSw
|
|
## -> GetBgnMultitok
|
|
## -> RevEnvVars
|
|
## -> PushFieldVal
|
|
##
|
|
## GenNmakeFiles -> FillSyscmd -> CmdCompare -> GetFieldVal
|
|
## -> GenMakeDir -> SetField
|
|
## -> PushFieldVal
|
|
## -> UpdTargetEntries -> DeleteKey
|
|
## -> SetField
|
|
## -> RevEnvVars
|
|
## -> ApplyRepositoryFixes -> GetMacro
|
|
## -> RevEnvVars
|
|
## -> FilterOpType
|
|
## -> RevEnvVars
|
|
## -> WriteCompdirTarget
|
|
## -> RevEnvVars
|
|
## -> WriteCompdir
|
|
## -> RevEnvVars
|
|
## -> WriteToMakefile
|
|
## -> WriteSysgenTarget
|
|
## -> WriteSettings
|
|
## -> WriteAllTarget
|
|
## -> WriteFileCmds
|
|
## -> WriteToSyscmd
|
|
## SumErrors
|
|
# //////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
use Data::Dumper;
|
|
|
|
use lib $ENV{"RAZZLETOOLPATH"} . "\\PostBuildScripts";
|
|
use lib $ENV{"RAZZLETOOLPATH"};
|
|
use cklang;
|
|
use ParseTable;
|
|
use strict;
|
|
no strict 'refs';
|
|
|
|
# The parsearg.pm needs strict.
|
|
use Logmsg;
|
|
use PbuildEnv;
|
|
use ParseArgs;
|
|
|
|
$ENV{script_name} = 'sysgen.pl';
|
|
|
|
|
|
use vars (qw(
|
|
$CLEAN $LANG $ACCELERATE $WORKDIR $SYNTAX_CHECK_ONLY $VERSION
|
|
@SYSFILES $SYSCMD @SYSCMD
|
|
$SECTIONS $ARGL
|
|
$EXCLUDE_DIRECTORIES $DEFAULT_SECTIONS $SECTIONNAME
|
|
$REPOSITORY $CMD_REPOSITORY
|
|
$SYSDIR $SYSFILE
|
|
$HOTFIX %HOTFIX
|
|
$LOGFILE $ERRFILE
|
|
$MAKEFILE %MAKEDIR
|
|
$TARGETS $MACROS %MACROS
|
|
$COMPDIR_FILE_TEMPLATE %COMPDIR_COMMAND %COMPDIR %MAP_COMPDIR_EXT %REATTR_COMMAND
|
|
%FILTER %default
|
|
$MAP_BINGEN_TOK_EXT %MAP_RSRC_TOK_EXT $CODESFNAME
|
|
@MAPPINGS %MAPPING_INDEX %MAP_ERR %CODES %LOCOPS @BGNSWS %BGNSWS
|
|
@IFSTACK $FILE $LINE $ERRORS
|
|
&Version
|
|
@FFILES
|
|
@FEXTENSIONS
|
|
@FBINDIRS
|
|
@FTOKENS
|
|
@FTOKDIRS
|
|
@TFILES
|
|
@TEXTENSIONS
|
|
@TBINDIRS
|
|
@TTOKENS
|
|
@TTOKDIRS
|
|
%VERIFYPATH
|
|
$VERIFY
|
|
@VERIFY
|
|
|
|
));
|
|
|
|
# MAP_ERR: Errors SYSGEN recognizes
|
|
# 1101 is used for ERROR instructions found in sysfiles
|
|
|
|
require 5.003;
|
|
|
|
|
|
my $SELF = "sysgen";
|
|
my $VERSION = "5.5";
|
|
$SELF = $SELF. " v " . $VERSION;
|
|
|
|
%MAP_ERR = (
|
|
|
|
1001, "sysfile not found",
|
|
1002, "file not found",
|
|
1003, "file not found: unable to run sysgen incrementally",
|
|
1004, "target file not found",
|
|
1005, "filter file not found",
|
|
1006, "unable to open file for reading",
|
|
1007, "unable to open file for writing",
|
|
1008, "/f option requires a filename",
|
|
1009, "target not found in filter file",
|
|
1010, "/w option requires a directory name",
|
|
1011, "/n option requires number of sections",
|
|
1101, "",
|
|
|
|
1110, "syntax error",
|
|
1111, "syntax error: ENDIF unexpected",
|
|
1112, "syntax error: IF unexpected",
|
|
1113, "syntax error: END_MAP unexpected",
|
|
1114, "syntax error: BEGIN_*_MAP unexpected",
|
|
1115, "syntax error: INCLUDE unexpected",
|
|
1116, "syntax error: unknown operator in expression",
|
|
1117, "syntax error: \")\" missing in macro invocation",
|
|
1118, "syntax error: incomplete description line",
|
|
1119, "syntax error: unknown mapping type",
|
|
1120, "syntax error: unmatched IF",
|
|
1121, "syntax error: unmatched BEGIN_*_MAP",
|
|
|
|
1210, "file format error: target not found",
|
|
1211, "file format error: target not listed in \"all\"",
|
|
1212, "file format error: no description blocks found for files",
|
|
1213, "file format error: \"sysgen\" target not found",
|
|
1214, "file format error: filename with special characters",
|
|
1215, "file format error: Similar target found",
|
|
|
|
1910, "unknown language",
|
|
1911, "missing or duplicated entry for language",
|
|
1912, "incomplete entry for language",
|
|
1913, "unknown class for language",
|
|
|
|
2011, "no binary found for token",
|
|
2012, "duplicated tokens",
|
|
2013, "unknown resource type: ",
|
|
2014, "unexpected token for already localized binary",
|
|
2015, "input bingen token not found for multi",
|
|
2016, "no bingen token found for custom resource",
|
|
2017, "unknown bingen token extension in token filename",
|
|
2018, "both bingen and rsrc tokens found",
|
|
2019, "custom resource found for rsrc token",
|
|
2020, "custom resource with no token",
|
|
|
|
2051, "sysfile error: undefined source path to verify",
|
|
2052, "folder not covered in sysfiles",
|
|
2053, "not mapped",
|
|
2054, "not mapped",
|
|
|
|
2101, "binary not found",
|
|
2102, "token not found",
|
|
|
|
3011, "internal error: unknown operation type",
|
|
|
|
4001, "filename with whitespace badly handled",
|
|
|
|
5001, "_COMPDIR currently only supported in clean build",
|
|
5002, "Incremental run with COMPDIR",
|
|
|
|
6001, "dir not exist",
|
|
6002, "file not covered",
|
|
|
|
); # MAP_ERR
|
|
|
|
|
|
# Logical operations understood by the map file parser.
|
|
|
|
my $LOGICALOPS = { "||" => ["1", "1"] ,
|
|
"&&" => ["0", "0"]
|
|
}; # LOGICALOPS
|
|
|
|
my $DEBUG_PARSER = 0;
|
|
my $ANSW = {0=>"FALSE", 1 =>"TRUE"} ;
|
|
|
|
# MAP_BINGEN_TOK_EXT: Bingen token extensions SYSGEN recognizes
|
|
|
|
$MAP_BINGEN_TOK_EXT = {};
|
|
|
|
# For the extensions not defined in %$MAP_BINGEN_TOK_EXT, we can apply compdir as well.
|
|
|
|
%MAP_COMPDIR_EXT = (
|
|
); # MAP_COMPDIR_EXT
|
|
# empty
|
|
|
|
# MAP_RSRC_TOK_EXT: Rsrc token extensions SYSGEN recognizes
|
|
|
|
%MAP_RSRC_TOK_EXT =(
|
|
'.rsrc', '',
|
|
|
|
); # MAP_RSRC_TOK_EXT
|
|
|
|
# Exclude GLOB
|
|
my %EXCLUDE_EXTENSION = ();
|
|
# Exclude path
|
|
my $EXCLUDE_DIRECTORIES={};
|
|
|
|
# Debug flags
|
|
my $DEBUG=0;
|
|
|
|
|
|
# Global Constants
|
|
my $SETSYMPATHS = 1;
|
|
my $PARSESYMBOLCD = undef;
|
|
my $SYNTAX_CHECK_ONLY = undef;
|
|
my $FULLCHECK = undef;
|
|
my $DLINE = "TARGET: DEPEND";
|
|
|
|
|
|
my %RevEnvVars = ();
|
|
my @RevEnvVars = ();
|
|
|
|
|
|
# CODESFNAME - the name of the file containing the language codes for all languages
|
|
# SYSGEN looks for this file in %NT_BLDTOOLS%
|
|
|
|
$CODESFNAME = "codes.txt";
|
|
|
|
# MAPPINGS: Mapping types SYSGEN recognizes
|
|
|
|
@MAPPINGS = qw(FILES BINDIRS TOKENS TOKDIRS EXTENSIONS);
|
|
|
|
%MAPPING_INDEX = map { $MAPPINGS[$_] => $_+1 } 0..$#MAPPINGS;
|
|
|
|
|
|
# Sub mappings:
|
|
|
|
my $LOCOP_BASED = { "-" => \&GenXcopy ,
|
|
"1" => \&GenXcopy ,
|
|
"2" => \&GenLocCmd
|
|
};
|
|
|
|
my $EXT_BASED = {"\\\.rsrc\\b" => \&GenRsrcCmd,
|
|
"\\\.lcx\\b" => \&GenLcxCmd,
|
|
"\\\.\\w\\w_\\b" => \&GenBgnCmd
|
|
};
|
|
|
|
|
|
|
|
# FFILES, FBINDIRS, FTOKENS, FTOKDIRS: The corresponding list of fields
|
|
|
|
@FFILES = ( "DestPath", "DestFile", "DestDbg", "SrcPath", "SrcFile", "SrcDbg", "Xcopy", "Langs" );
|
|
@FBINDIRS = ( "DestPath", "DestDbg", "SrcPath", "SrcDbg", "Xcopy", "R", "Langs");
|
|
@FTOKENS = ( "DestPath", "DestFile", "TokPath", "TokFile", "ITokPath", "LocOp", "BgnSw", "Langs");
|
|
@FTOKDIRS = ( "DestPath", "TokPath", "ITokPath", "LocOp", "BgnSw", "Langs");
|
|
@FEXTENSIONS = ("TokExt", "BinExt", "Langs");
|
|
|
|
# Localization Tool Operation Types (append or replace)
|
|
|
|
%LOCOPS = (
|
|
a => '-a',
|
|
r => '-r',
|
|
i => '-i $opts',
|
|
ai => '-i $opts -a',
|
|
ri => '-i $opts -r'
|
|
);
|
|
|
|
# Bingen Switches
|
|
|
|
@BGNSWS = ( "-b", "-c" );
|
|
|
|
%BGNSWS = ( "-b" => "-b",
|
|
"-c" => "-c"
|
|
);
|
|
|
|
# allow for nested IF's;
|
|
|
|
@IFSTACK = ();
|
|
|
|
# Global Variables
|
|
|
|
# CODES - the contents of the CODESFNAME file, change to hash at 04/03/00
|
|
%CODES = ();
|
|
my %LANGCODES=();
|
|
|
|
# TFILES, TBINDIRS, TTOKENS, TTOKDIRS: Records for the corresponding mapping (one of @MAPPINGS),
|
|
# in the same order as they are found in the mapping files.
|
|
# One record is a hash with the keys stored by @F<mapping_type>
|
|
|
|
@TFILES = ();
|
|
@TEXTENSIONS = ();
|
|
@TBINDIRS = ();
|
|
@TTOKENS = ();
|
|
@TTOKDIRS = ();
|
|
|
|
# BUG BUG %EXCLUDE_DIRECTORIES and %EXCLUDE_SUBDIR
|
|
# seem to carry out same mission.
|
|
my %EXCLUDE_SUBDIR = ();
|
|
|
|
%VERIFYPATH = (
|
|
EXCLUDE_ROOT => 'DIR_PATHS',
|
|
EXCLUDE_SUBDIR => 'IGNORE_PATHS',
|
|
EXCLUDE_FILE_PATTERN => '(\.pdb)|(\.edb)|(\.dbg)|(slm\.ini)|(symbols)'
|
|
);
|
|
|
|
|
|
# REPOSITORY: Info necessary to generate the appropriate xcopy or bingen command,
|
|
# for every file to be aggregated.
|
|
|
|
$REPOSITORY = {}; # As a db table, the key is (DestPath, DestFile).
|
|
# As a Perl hash:
|
|
# key = $DestPath (lowercase)
|
|
# value = hash with
|
|
# key = $DestFile (lowercase)
|
|
# value = hash with
|
|
# keys = DestPath, DestFile, DestDbg,
|
|
# SrcPath, SrcFile, SrcDbg,
|
|
# TokPath, TokFile, ITokPath,
|
|
# OpType, LocOp, BgnSw,
|
|
# Cmd
|
|
#
|
|
# not necessarily all of them defined.
|
|
|
|
|
|
my @REPOSITORY_TEMPLATE = qw( SrcFile DestFile SrcDbg DestDbg SrcPath DestPath LocOp BgnSw);
|
|
|
|
# CMD_REPOSITORY: Repository as found in an existing SYSGEN-generated makefile.
|
|
# For every key, only {Cmd} field is filled in.
|
|
|
|
$CMD_REPOSITORY = {};
|
|
|
|
# SYSCMD: Stores the contents of the syscmd (SYSGEN-generated NMAKE command file)
|
|
|
|
@SYSCMD= ();
|
|
|
|
# MACROS: Hash storing the names and values of the macros.
|
|
# Types of macros sysgen recognizes:
|
|
# - command line macros (type 1)= macros defined in the command line.
|
|
# Their value can be overwritten only by another command line macro.
|
|
# - sysfile macros (type 0) = macros defined by the sysfiles,
|
|
# inside or outside the pair (BEGIN_*_MAP, END_MAP) (type 0).
|
|
# The sysfile macros do not overwrite the command line macros, but they overwrite the environment
|
|
# macros.
|
|
# - environment macros (type 0 too) = variables inherited from the cmd environment.
|
|
# Can be overwritten by the other types of macros.
|
|
# Internally, MACROS uppercases all the macro names, so the macronames are case insesitive.
|
|
|
|
$MACROS = {};
|
|
|
|
# FILTER
|
|
# Stores the list of files to be considered.
|
|
|
|
%FILTER = ();
|
|
|
|
# FILE: Filename of the current description file; LINE: the current line number
|
|
|
|
$FILE = "";
|
|
$LINE = 0;
|
|
|
|
# ERRORS: non fatal errors found during running SYSGEN
|
|
|
|
$ERRORS = {};
|
|
|
|
# MAKEFILE: name of the nmake makefile to generate
|
|
# By default, MAKEFILE is set to "makefile" if not overridden on the command line,
|
|
# /W option
|
|
|
|
$MAKEFILE = "sysgen.mak";
|
|
$SYSCMD = "syscmd";
|
|
|
|
# SYSFILE: names of the starting description files
|
|
# By default, SYSFILE is set to ".\\sysfile" if not specified otherwise in the command line,
|
|
# /F option
|
|
|
|
$SYSFILE = "sysfile";
|
|
|
|
# HOTFIX: the rules missed by sysgen due to its one-to-one mapping
|
|
$HOTFIX = "hotfix";
|
|
|
|
# Error and Log Filenames
|
|
$LOGFILE = "sysgen.log";
|
|
$ERRFILE = "sysgen.err";
|
|
|
|
# Temporary list file(s) for compdir execution are stored here.
|
|
# In the case more than one file to be generated, they will have the names compdir.NNN.mak
|
|
# where NNN will be the number
|
|
$ACCELERATE = undef;
|
|
$COMPDIR_FILE_TEMPLATE = "compdir.mak";
|
|
|
|
# %COMPDIR_COMMAND block will replace all _nttree => _ntpostbld 'plain' copy operations.
|
|
%COMPDIR = ();
|
|
%COMPDIR_COMMAND = ();
|
|
%REATTR_COMMAND = ();
|
|
# The attrib commands group must go after the compdir command group
|
|
#
|
|
|
|
my $SRCBASE = ($VERSION=~/^3\./) ? "" : $ENV{"_NTTREE"};
|
|
my $DSTBASE = ($VERSION=~/^3\./) ? "" : $ENV{"_NTPOSTBLD"};
|
|
|
|
|
|
# The directory where the $MAKEFILE to be written
|
|
$WORKDIR = undef;
|
|
# The directory where the $SYSFILE resides
|
|
$SYSDIR = undef;
|
|
|
|
# TARGETS: targets specified in the command line
|
|
|
|
$TARGETS = {}; # key = $DestFile (lowercase string))
|
|
# value = hash with
|
|
# keys = OldDestPath (array), NewDestPath (array)
|
|
|
|
%MAKEDIR=();
|
|
%HOTFIX = ();
|
|
|
|
# CLEAN : 1 if a clean SYSGEN is invoked ( by specifying /c in the command line )
|
|
|
|
$CLEAN = 0;
|
|
|
|
# SECTIONS
|
|
|
|
($SECTIONNAME, $DEFAULT_SECTIONS, $SECTIONS)=("PROCESS", 16, 16);
|
|
|
|
# main {
|
|
# Flush output buffer for remote.exe
|
|
select(STDOUT); $| = 1;
|
|
my $start_time = time();
|
|
|
|
# Parse the command line
|
|
|
|
my $SEPARATOR = int(rand time);
|
|
$SEPARATOR =~ s/\d{2}/chr($&)/eg;
|
|
$SEPARATOR =~ s/\W/_/g;
|
|
|
|
my $ARGL = join($SEPARATOR, @ARGV);
|
|
|
|
&ParseArgs::parseargs(
|
|
'y' => \$VERIFY,
|
|
'c' => \$CLEAN,
|
|
'a' => \$ACCELERATE,
|
|
'w:' => \$WORKDIR,
|
|
'f:' => \$SYSFILES[0],
|
|
'n:' => \$SECTIONS,
|
|
's' => \$SYNTAX_CHECK_ONLY,
|
|
'v' => \&printVersion,
|
|
'h' => \&PrintHelp,
|
|
'?' => \&PrintHelp,
|
|
'old' => \&UseOldParser,
|
|
);
|
|
|
|
|
|
$LANG = uc($ENV{"LANG"});
|
|
&SetMacro("LANGUAGE", $LANG, 0);
|
|
&SetMacro("VERIFY", $VERIFY, 0);
|
|
|
|
|
|
&SetMacro("DEBUG", $ENV{"DEBUG"}, 0);
|
|
$DEBUG= &GetMacro("DEBUG");
|
|
|
|
|
|
@SYSFILES = grep {/\S/} @SYSFILES;
|
|
if ($DEBUG) {
|
|
my $sf = scalar(@SYSFILES);
|
|
print STDERR "\n\n", join("\n", (
|
|
"\$LANG=$LANG",
|
|
"\$ACCELERATE=$ACCELERATE",
|
|
"\$CLEAN=$CLEAN",
|
|
"\$SYNTAX_CHECK_ONLY=$SYNTAX_CHECK_ONLY",
|
|
"\$WORKDIR=$WORKDIR",
|
|
"\$SYSFILES[0]=\"$SYSFILES[0]\"",
|
|
"\@SYSFILES=$sf"
|
|
)),
|
|
"\n\n";
|
|
}
|
|
|
|
&SysgenLogMsg($SELF, 1);
|
|
|
|
&FatalError( 5001, "Imcompatible command line flags" ) if $ACCELERATE and !$CLEAN;
|
|
|
|
$ACCELERATE and &SysgenLogMsg("Accelerated aggregation..." , 1);
|
|
|
|
# Verify $WORKDIR's existence or create it
|
|
-d $WORKDIR or qx("md $WORKDIR") unless $WORKDIR !~/\S/;
|
|
|
|
# Verify SYSFILE's existence
|
|
$SYSFILE = $SYSFILES[0] unless !scalar(@SYSFILES);
|
|
( -e $SYSFILE ) || &FatalError( 1001, $SYSFILE );
|
|
|
|
$FULLCHECK = &GetMacro("FULLCHECK") unless $FULLCHECK;
|
|
( -e $MAKEFILE ) || &FatalError( 1003, $MAKEFILE ) unless $CLEAN or $SYNTAX_CHECK_ONLY;
|
|
|
|
# $HOTFIX and $SYSFILE are in $SYSDIR
|
|
|
|
$SYSDIR = &GetSysDir($SYSFILE);
|
|
&SetMacro("SYSDIR" , uc($SYSDIR), 0);
|
|
|
|
$HOTFIX = &SwitchWorkDir($HOTFIX, $SYSDIR);
|
|
|
|
# $MAKEFILE, $LOGFILE, $ERRFILE, $SYSCMD - all created in $WORKDIR
|
|
&SetMacro( "WORKDIR", $WORKDIR, 0 );
|
|
|
|
|
|
$MAKEFILE = &SwitchWorkDir($MAKEFILE, $WORKDIR);
|
|
$LOGFILE = &SwitchWorkDir($LOGFILE, $WORKDIR);
|
|
$ERRFILE = &SwitchWorkDir($ERRFILE, $WORKDIR);
|
|
$SYSCMD = &SwitchWorkDir($SYSCMD, $WORKDIR);
|
|
|
|
|
|
|
|
# Clean log and err files.
|
|
&CleanLogFiles( $WORKDIR );
|
|
|
|
# Load the inherited environment variables
|
|
&LoadEnv();
|
|
|
|
&MakeRevEnvVars();
|
|
|
|
# sysgen assumption about the _NTPOSTBUILD
|
|
# this code needs to go to the sub LoadEnv body...
|
|
|
|
&LoadHotFixFile($HOTFIX);
|
|
|
|
# Load codes.txt file
|
|
|
|
&LoadCodes();
|
|
|
|
|
|
$DEBUG and
|
|
print STDERR join("\n", "EXCLUDE_SUBDIR:" ,
|
|
sort(split( /\s+/, &RevEnvVars(
|
|
&GetMacro(
|
|
$VERIFYPATH{"EXCLUDE_SUBDIR"}))))), "\n\n";
|
|
|
|
|
|
# For incremental, load Repository-associated
|
|
# commands from an existing makefile to CMD_REPOSITORY.
|
|
|
|
&LoadReposit( $MAKEFILE );
|
|
|
|
&MakeRevEnvVars();
|
|
|
|
# Read the description files
|
|
|
|
&ReadSysfile( $SYSFILE );
|
|
|
|
map {$MAP_BINGEN_TOK_EXT->{$_->{"TokExt"}} = $_->{"BinExt"}} @TEXTENSIONS;
|
|
@MAPPINGS = grep {!/EXTENSIONS/} @MAPPINGS;
|
|
|
|
$DEBUG and
|
|
print STDERR join("\n", map {"\"$_\""} keys %$MAP_BINGEN_TOK_EXT), "\n\n";
|
|
|
|
&MakeRevEnvVars();
|
|
|
|
# Here we can expand the variable defining the %MAP_COMPDIR_EXT,
|
|
# to update the file type list.
|
|
|
|
foreach my $CompdirExtension (split(/\s*;\s*/, &GetMacro("COMPDIR_EXTENSION"))){
|
|
$CompdirExtension=~ s+\*\.+\.+g;
|
|
$MAP_COMPDIR_EXT{lc($CompdirExtension)} = 1;
|
|
}
|
|
$DEBUG and
|
|
print STDERR "compdir extension\(s\):\n",
|
|
join( "\n", keys(%MAP_COMPDIR_EXT)),
|
|
"\n\n";
|
|
|
|
# Verify the whole tree covered.
|
|
&VerifyCover();
|
|
|
|
# Populate the repository (according to the description files)
|
|
&PopulateReposit();
|
|
|
|
$SYNTAX_CHECK_ONLY or &GenNmakeFiles( $MAKEFILE, $SYSCMD );
|
|
|
|
# Display errors
|
|
&SumErrors();
|
|
|
|
# } # main
|
|
|
|
# ReadSysfile
|
|
# Reads a given sysfile and all the included sysfiles;
|
|
# For each mapping type found, loads data into the corresponding array of hashes
|
|
# ( one of the "@T" variables )
|
|
# Input
|
|
# mapping filename
|
|
# Output
|
|
# none
|
|
# Usage
|
|
# &ReadSysfile( "sysgen.def" );
|
|
|
|
sub ReadSysfile {
|
|
|
|
my $mapfname = shift;
|
|
|
|
# File contents
|
|
my @mapfile = ();
|
|
my $RULES = [];
|
|
my $ifstack = scalar (@IFSTACK) ;
|
|
# Mapping type ( one of @MAPPINGS )
|
|
my $maptype;
|
|
|
|
# Included filename
|
|
my $incfile;
|
|
|
|
# Flags
|
|
my ( $if_flag, $inside_if, $if_line ) = (1, 0, undef);
|
|
|
|
# Indexes
|
|
my $i;
|
|
|
|
# Error/Message text
|
|
my $error;
|
|
my $message;
|
|
|
|
# Open the mapping file
|
|
|
|
( -e $mapfname ) || &FatalError( 1002, $mapfname );
|
|
|
|
&SysgenLogMsg("Loading $mapfname..." , 1);
|
|
|
|
if ($PARSESYMBOLCD && !(%default) && &GetMacro("SYMBOLCD")){
|
|
|
|
%default = &parseSymbolCD(&GetMacro("SYMBOLCD"));
|
|
$DEBUG and map {print STDERR $_ , "\n"} values(%default);
|
|
}
|
|
|
|
|
|
# Load file contents
|
|
open( FILE, $mapfname ) || &FatalError( 1006, $mapfname );
|
|
@mapfile = <FILE>;
|
|
close( FILE );
|
|
|
|
# Parse the mapping file and load the mappings.
|
|
for ( $i=0, $LINE=0, $FILE=$mapfname; $i < @mapfile; $i++, $LINE++ ) {
|
|
|
|
for ( $mapfile[$i] ) {
|
|
|
|
SWITCH: {
|
|
|
|
# white lines
|
|
if ( ! /\S/ ) { last SWITCH; }
|
|
|
|
# commented lines
|
|
if ( /^\s*#/ ) { last SWITCH; }
|
|
|
|
# ENDIF
|
|
if ( /\bENDIF\b/i ) {
|
|
|
|
$DEBUG and print STDERR scalar(@IFSTACK), "\n";
|
|
$ifstack < scalar(@IFSTACK) or &FatalError( 1111, $FILE, $LINE );
|
|
pop @IFSTACK;
|
|
$inside_if = scalar(@IFSTACK) - $ifstack;
|
|
$if_flag = ($inside_if) ? $IFSTACK[-1] : 1;
|
|
last SWITCH;
|
|
} # case
|
|
|
|
|
|
# IF
|
|
if ( /\bIF\b/i) {
|
|
push @IFSTACK, &SetIfFlag( $_);
|
|
$if_flag and
|
|
$if_flag = $IFSTACK[-1];
|
|
$inside_if = 1;
|
|
$if_line = $i;
|
|
last SWITCH;
|
|
} # case
|
|
|
|
if ( ! $if_flag ) { last SWITCH; }
|
|
|
|
# INCLUDE
|
|
|
|
if ( /\bINCLUDE\b\s+(\S+)/i ) {
|
|
# Evaluate the macros of the include statement
|
|
$incfile = &ReplaceVar( $1, 0 );
|
|
# Read the included file
|
|
&ReadSysfile(&SwitchWorkDir($incfile, $SYSDIR));
|
|
# Continue with the current file; set FILE and LINE back
|
|
$FILE = $mapfname;
|
|
$LINE = $i;
|
|
last SWITCH;
|
|
} # case
|
|
|
|
|
|
# SET
|
|
if ( /\bSET\b/i ) {
|
|
my $fields = [&SetAttribOp($_)];
|
|
$LANG = $fields->[1] if $fields->[0] =~ /LANGUAGE/i;
|
|
&SetMacro( &SetAttribOp( $_ ), 0 );
|
|
last SWITCH;
|
|
} # case
|
|
|
|
# MESSAGE
|
|
if ( /\bMESSAGE\b\s+(\S.*)$/i) {
|
|
$message = &ReplaceVar( $1, 0 );
|
|
&Error( 1101, $message );
|
|
last SWITCH;
|
|
} # case
|
|
|
|
# ERROR
|
|
if ( /\bERROR\b\s+(\S.*)$/i) {
|
|
$error = &ReplaceVar( $1, 0 );
|
|
&FatalError( 1101, $error );
|
|
last SWITCH;
|
|
} # case
|
|
|
|
# END RULES
|
|
|
|
if ( /END_RULES/i ) {
|
|
|
|
&LoadRuleBlock($RULES);
|
|
$#$RULES = -1;
|
|
|
|
last SWITCH;
|
|
} # case
|
|
|
|
# BEGIN RULES
|
|
|
|
if ( /\bBEGIN_RULES/i ) {
|
|
|
|
# Read the found map.
|
|
&SysgenLogMsg("Loading RULES from $mapfname...", 1);
|
|
|
|
# Read the RULES from the next line and until END_RULES
|
|
|
|
$i = &ReadRules( \@mapfile, $i+1, $RULES);
|
|
last SWITCH;
|
|
} # case
|
|
|
|
|
|
|
|
# END_MAP
|
|
if ( /END_MAP/i ) {
|
|
|
|
&FatalError( 1113, $FILE, $LINE );
|
|
last SWITCH;
|
|
} # case
|
|
|
|
# BEGIN_MAP
|
|
if ( /\bBEGIN_(\S+)_MAP/i ) {
|
|
|
|
$maptype = $MAPPING_INDEX{$1};
|
|
|
|
( $maptype ) || &FatalError( 1119, $FILE, $LINE );
|
|
|
|
# Read the found map.
|
|
&SysgenLogMsg("Loading $MAPPINGS[$maptype-1] map from $mapfname...", 1);
|
|
|
|
# Read the map and return the last line index
|
|
|
|
$i = &ReadBlock( \@mapfile, $i+1, $maptype-1 );
|
|
last SWITCH;
|
|
} # case
|
|
|
|
# default
|
|
&FatalError( 1110, $FILE, $LINE );
|
|
|
|
} #SWITCH
|
|
} #for
|
|
|
|
} # for
|
|
|
|
&FatalError( 1120, $FILE, $if_line ) if scalar(@IFSTACK) != $ifstack;
|
|
|
|
return;
|
|
|
|
} # ReadSysfile
|
|
|
|
# ReadRules
|
|
# Reads a RULES block away from the mapping file
|
|
# The lines array is returned to the caller via array ref argument
|
|
# Input
|
|
# sysfile contents (reference)
|
|
# index of the current line
|
|
# 'rules' array ref
|
|
# Output
|
|
# index of the END_RULES line
|
|
# Usage
|
|
|
|
sub ReadRules{
|
|
|
|
my($mapref, $index, $ruleref) = @_;
|
|
my $line;
|
|
|
|
for ( ; $index < @{$mapref}; $index++, $LINE++ ) {
|
|
|
|
my $line = $mapref->[$index];
|
|
|
|
if ( $line=~/END_RULES/i ) {
|
|
return $index-1;
|
|
} # case
|
|
|
|
push @$ruleref, $line;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
# ReadBlock
|
|
# Reads a map from the mapping file, according to its type.
|
|
# Loads data into the corresponding "table" variable ( one of the "@T" variables)
|
|
# Input
|
|
# sysfile contents (reference)
|
|
# index of the current line
|
|
# mapping type
|
|
# Output
|
|
# index of the END_MAP line
|
|
# Usage
|
|
# $index = &ReadBlock( \@mapfile, $index, $maptype );
|
|
|
|
sub ReadBlock {
|
|
|
|
|
|
my($mapref, $index, $maptype) = @_;
|
|
my $ifstack = scalar(@IFSTACK);
|
|
# Output - $index
|
|
|
|
# Other indexes
|
|
my $line;
|
|
|
|
# IF flags
|
|
my( $if_flag, $inside_if, $if_line ) = (1, 0, undef);
|
|
|
|
# Error/Message text
|
|
my $error;
|
|
my $message;
|
|
|
|
# Parse the current mapping type
|
|
for ( ; $index < @{$mapref}; $index++, $LINE++ ) {
|
|
|
|
for ( $mapref->[$index] ) {
|
|
SWITCH: {
|
|
|
|
# white lines
|
|
if ( ! /\S/ ) { last SWITCH; }
|
|
|
|
# commented lines
|
|
if ( /^\s*#/ ) { last SWITCH; }
|
|
|
|
# ENDIF
|
|
if ( /\bENDIF\b/i) {
|
|
|
|
scalar(@IFSTACK) or &FatalError( 1111, $FILE, $LINE );
|
|
pop @IFSTACK;
|
|
$inside_if = scalar(@IFSTACK);
|
|
$if_flag = ($inside_if) ? $IFSTACK[-1] : 1;
|
|
last SWITCH;
|
|
} # case
|
|
|
|
if ( /\bIF\b/i) {
|
|
push @IFSTACK, &SetIfFlag( $_);
|
|
$if_flag and
|
|
$if_flag = $IFSTACK[-1];
|
|
$inside_if = 1;
|
|
$if_line = $index;
|
|
last SWITCH;
|
|
} # case
|
|
|
|
if ( ! $if_flag ) { last SWITCH; }
|
|
|
|
# INCLUDE
|
|
if ( /\bINCLUDE\b\s+(\S+)/i ) {
|
|
|
|
&FatalError( 1115, $FILE, $LINE );
|
|
last SWITCH;
|
|
} # case
|
|
|
|
# SET
|
|
if ( /\bSET\b/i ) {
|
|
my $fields = [&SetAttribOp($_)];
|
|
$LANG = $fields->[1] if $fields->[0] =~ /LANGUAGE/i;
|
|
&SetMacro( &SetAttribOp( $_ ), 0 );
|
|
last SWITCH;
|
|
} # case
|
|
|
|
# MESSAGE
|
|
if ( /\bMESSAGE\b\s+(\S.*)$/i) {
|
|
$message = &ReplaceVar( $1, 0 );
|
|
&Error( 1101, $message );
|
|
last SWITCH;
|
|
} # case
|
|
|
|
# ERROR
|
|
if ( /\bERROR\b\s+(\S.*)$/i) {
|
|
$error = &ReplaceVar( $1, 0 );
|
|
&FatalError( 1101, $error );
|
|
last SWITCH;
|
|
} # case
|
|
|
|
# END_MAP
|
|
if ( /END_MAP/i ) {
|
|
( $ifstack!= scalar(@IFSTACK) ) and &FatalError( 1120, $FILE, $if_line );
|
|
return $index;
|
|
} # case
|
|
|
|
# BEGIN_MAP
|
|
if ( /BEGIN_\S+_MAP/i ) {
|
|
|
|
&FatalError( 1114, $FILE, $LINE );
|
|
last SWITCH;
|
|
} # case
|
|
|
|
# default
|
|
&ReadLine( $mapref, $index, $maptype );
|
|
|
|
} # SWITCH
|
|
} # for
|
|
|
|
} # for
|
|
|
|
print &FatalError( 1121, $FILE, $LINE );
|
|
return -1;
|
|
|
|
} # ReadBlock
|
|
|
|
# SetIfFlag
|
|
# Evaluates the given IF expression.
|
|
# Input
|
|
# IF statement
|
|
# Output
|
|
# 1 if the IF expression is satisfied, 0 otherwise
|
|
|
|
sub SetIfFlag {
|
|
|
|
|
|
my $initialToken = &ReplaceVar( shift, 0 );
|
|
|
|
# Output
|
|
|
|
my $LogicOp = "";
|
|
my $TokenResult = 0;
|
|
my @subTokenResultSet = ();
|
|
my @parsedTokenlist = ();
|
|
|
|
# Identify the operands and the operator, then evaluate the expression.
|
|
|
|
# 1 . Chop away the IF
|
|
|
|
|
|
|
|
$initialToken =~ s/^\s*\bIF\b\s+//gi;
|
|
|
|
# Process AND/OR stuff
|
|
# NOTE: one level, not too rigorous design! :-)
|
|
|
|
if ($initialToken =~ /\|\||\&\&/){
|
|
|
|
foreach my $knownLogicOp (keys(%$LOGICALOPS)){
|
|
|
|
if ($initialToken =~ /\Q$knownLogicOp\E/){
|
|
|
|
my $simpleTokenList = &StrToArray($initialToken, $knownLogicOp) ;
|
|
push @parsedTokenlist, @$simpleTokenList;
|
|
$LogicOp = $knownLogicOp;
|
|
|
|
}
|
|
}
|
|
}
|
|
else{
|
|
push @parsedTokenlist, $initialToken;
|
|
}
|
|
|
|
# print STDERR join ("\n", @parsedTokenlist), "\n";
|
|
|
|
foreach my $initialToken (@parsedTokenlist){
|
|
|
|
my $TokenResult = 0;
|
|
|
|
# print STDERR "\n\"",$initialToken,"\"\n";
|
|
|
|
# 3 . Load arguments and operands
|
|
|
|
my ($operand1, $operator, $operand2, @unexpected) = split(" ", $initialToken);
|
|
|
|
&FatalError( 1110, $FILE, $LINE ) if @unexpected;
|
|
|
|
SWITCH: {
|
|
if ( $operator eq "==" ) {
|
|
if ( $operand1 eq $operand2 ) {
|
|
$TokenResult = 1;
|
|
}
|
|
|
|
last SWITCH;
|
|
} # if
|
|
|
|
if ( $operator eq "!=" ) {
|
|
if ( $operand1 ne $operand2 ) {
|
|
$TokenResult = 1;
|
|
}
|
|
|
|
last SWITCH;
|
|
} # if
|
|
|
|
&FatalError( 1116, $FILE, $LINE );
|
|
|
|
} # SWITCH
|
|
|
|
# print STDERR "\n\"",$TokenResult,"\"\n";
|
|
|
|
push @subTokenResultSet, $TokenResult;
|
|
|
|
}
|
|
|
|
my $ExprResult = join ("", @subTokenResultSet);
|
|
$TokenResult = 0;
|
|
|
|
$TokenResult = 1 if ( (!$LogicOp && $ExprResult =~ /1/)
|
|
||
|
|
($LogicOp && (
|
|
($LOGICALOPS->{$LogicOp}->[1] &&
|
|
$ExprResult =~ /$LOGICALOPS->{$LogicOp}->[0]/i)
|
|
||
|
|
|
|
(!($LOGICALOPS->{$LogicOp}->[1]) &&
|
|
$ExprResult !~ /$LOGICALOPS->{$LogicOp}->[0]/i)
|
|
)
|
|
)
|
|
);
|
|
|
|
return $TokenResult;
|
|
|
|
} # SetIfFlag
|
|
|
|
|
|
# test logic parser is able to interpret.
|
|
# used for debug purposes, e.g.
|
|
#
|
|
# &testCompoundIF("IF CHT == CHT");
|
|
# &testCompoundIF("IF CHT == CHS || (CHT == CHT)");
|
|
# &testCompoundIF("IF CHT == CHS");
|
|
# &testCompoundIF("IF CHT == CHS && (CHT == CHS) ");
|
|
# &testCompoundIF("IF CHT == chs");
|
|
|
|
sub testCompoundIF{
|
|
|
|
$DEBUG_PARSER and print STDERR join("\t", $_[0],
|
|
$ANSW->{&SetIfFlag($_[0])}), "\n";
|
|
|
|
}
|
|
|
|
# Expands the combined "IF" condition joined by the 'OR' or 'AND'
|
|
# into elementary "IF" conditions.
|
|
# e.g. &StrToArray("CHT == CHT \&\& (CHT == CHS)", "\&\&");
|
|
# will return the array:
|
|
#
|
|
# CHT == CHS
|
|
# CHT == CHS
|
|
#
|
|
# which would evaluate to FALSE
|
|
|
|
sub StrToArray{
|
|
|
|
# $DEBUG_PARSER and print STDERR $_,"\n";
|
|
|
|
my ($line, $sep) = @_;
|
|
# $DEBUG_PARSER and print STDERR "\"$line\"\n";
|
|
|
|
my @parsedTokenlist = split(/\s*\Q$sep\E\s*/, $line);
|
|
|
|
map {s/^\s*\(\s*([^\(][ \"\'\@a-z0-9=!\(\)]+[^\)])\s*\)\s*/$1/ig} @parsedTokenlist;
|
|
|
|
push @parsedTokenlist, $line unless @parsedTokenlist;
|
|
|
|
# DEBUG_PARSER and print STDERR join("\n", @parsedTokenlist), "\n";
|
|
|
|
\@parsedTokenlist;
|
|
|
|
} # StrToArray
|
|
|
|
|
|
# SetAttribOp
|
|
# Evaluates a SET statement.
|
|
# Input
|
|
# a SET statement
|
|
# Output
|
|
# evaluated macroname and macrovalue
|
|
# Usage
|
|
# ($macroname, $macrovalue) = &SetAttribOp( $mapfile[$index] ) ;
|
|
|
|
|
|
sub SetAttribOp {
|
|
|
|
|
|
my $entry = &ReplaceVar( shift, 0 );
|
|
|
|
$entry =~ s/^\s*\bSET\b\s+//gi;
|
|
|
|
# Output
|
|
|
|
my ($varname, $value, @unexpected) = split(/\s*=\s*\;*/, $entry);
|
|
|
|
# Set variable's name and variable's value
|
|
|
|
@unexpected and &FatalError( 1110, $FILE, $LINE );
|
|
|
|
# Verify if a variable name is defined
|
|
|
|
if ( $varname eq "" ) {
|
|
return ("", "");
|
|
} # if
|
|
|
|
($varname, $value);
|
|
|
|
} # SetAttribOp
|
|
|
|
|
|
# ReadLine
|
|
# Description
|
|
# Reads one description line, evaluates it and store the records it generates
|
|
# Input
|
|
# sysfile contents ( reference )
|
|
# line number
|
|
# Output
|
|
# none
|
|
# Usage
|
|
# &ReadLine( $mapref, $index, $maptype );
|
|
# Implementation
|
|
|
|
sub ReadLine {
|
|
|
|
my( $mapref, $index, $maptype ) = @_;
|
|
|
|
# Replace variables and spawn new records
|
|
|
|
map {&LoadRecord($maptype, $_) } &ReplaceVar($mapref->[$index],1);
|
|
|
|
$index;
|
|
|
|
} # ReadLine
|
|
|
|
# LoadRecord
|
|
# Description
|
|
# Transforms an evaluated description line into a record,
|
|
# and adds it to the appropriate table.
|
|
# Input
|
|
# mapping type
|
|
# description line (string)
|
|
# Output
|
|
# <none>
|
|
# Usage
|
|
# &LoadRecord( $maptype, $line_contents );
|
|
|
|
sub LoadRecord {
|
|
|
|
my($maptype, $entry) = @_;
|
|
|
|
# The hash table storring the current line (record).
|
|
# The keys (field names) are given by the corresponding "@F$maptype" array.
|
|
my %record;
|
|
|
|
%record = &LineToRecord( $maptype, $entry );
|
|
|
|
# If the current language matches the record language,
|
|
# add the record to the corresponding "@T$maptype" array.
|
|
|
|
if (&cklang::CkLang($LANG, $record{Langs})) {
|
|
&AddRecord( $maptype, %record );
|
|
} elsif ((!defined $record{SrcFile}) && (defined $record{SrcPath})) {
|
|
&SysgenLogMsg("Ignore $record{SrcPath}",0);
|
|
&SetMacro(
|
|
$VERIFYPATH{'EXCLUDE_SUBDIR'},
|
|
&GetMacro($VERIFYPATH{'EXCLUDE_SUBDIR'}) . "; " .
|
|
$record{SrcPath},
|
|
0
|
|
);
|
|
}# if
|
|
|
|
return;
|
|
|
|
} # LoadRecord
|
|
|
|
# LineToRecord
|
|
# Transforms a given string into a record (hash table).
|
|
# Input
|
|
# mapping type
|
|
# description line contents
|
|
# Output
|
|
# record of the mapping type
|
|
# Usage
|
|
# %record = &LineToRecord( $maptype, $entry );
|
|
|
|
sub LineToRecord {
|
|
|
|
my($maptype, $entry) = @_;
|
|
|
|
# Output
|
|
my %record;
|
|
|
|
# Mapping name
|
|
my $mapname;
|
|
|
|
# Corresponding "@F$maptype" variable (reference)
|
|
my $ftable;
|
|
|
|
# Current line split in fields
|
|
my @fields;
|
|
|
|
# Storage for the current line
|
|
my $line = $entry;
|
|
|
|
# Indexes
|
|
my($i, $j);
|
|
|
|
# Get the mapping name.
|
|
$mapname = "F$MAPPINGS[$maptype]";
|
|
$ftable = \@$mapname;
|
|
|
|
# Split the record into fields
|
|
@fields = split " ", $entry;
|
|
|
|
# Fill in %record variable with the field names as keys and
|
|
# the field values as values.
|
|
for ( $i=0; $i< @$ftable; $i++ ) {
|
|
|
|
# All fields must be non-null
|
|
if ( !$fields[$i] ) {
|
|
&FatalError( 1118, $FILE, $LINE );
|
|
} # if
|
|
$record{$ftable->[$i]} = $fields[$i];
|
|
} # for
|
|
|
|
return %record;
|
|
|
|
} # LineToRecord
|
|
|
|
# AddRecord
|
|
# Adds the given record to the appropriate "@T" table.
|
|
# Input
|
|
# mapping type
|
|
# record
|
|
# Output
|
|
# <none>
|
|
|
|
sub AddRecord {
|
|
|
|
my($maptype, %record) = @_;
|
|
|
|
# Table name ( name of one of the "@T" variables )
|
|
my $tname;
|
|
|
|
# Table (reference)
|
|
my $table;
|
|
|
|
# Storage for the current record
|
|
my %buffer;
|
|
|
|
# Subdirectories ( if "R" field defined )
|
|
my @subdirs;
|
|
|
|
# Source field name (one of SrcPath, TokPath )
|
|
my $fname;
|
|
|
|
# Indexes
|
|
my $i;
|
|
|
|
# Set table
|
|
$tname = "T$MAPPINGS[$maptype]";
|
|
$table = \@$tname;
|
|
|
|
# Set the field name for the path ( TokPath or SrcPath)
|
|
# according to the mapping type
|
|
# Set the field name for the path ( TokPath or SrcPath)
|
|
# according to the mapping type
|
|
# $MAPPINGS[$maptype] => EXTENSIONS
|
|
# must be handled separately anyway.
|
|
#
|
|
$fname = undef;
|
|
SWITCH: {
|
|
|
|
if ( $MAPPINGS[$maptype] =~ /TOK/i ) {
|
|
$fname = "TokPath";
|
|
last SWITCH;
|
|
} # if
|
|
|
|
if ( $MAPPINGS[$maptype] =~ /BIN/i ) {
|
|
$fname = "SrcPath";
|
|
last SWITCH;
|
|
} # if
|
|
|
|
if ( $MAPPINGS[$maptype] =~ /FILES/i ) {
|
|
$fname = "SrcPath";
|
|
last SWITCH;
|
|
} # if
|
|
|
|
# default
|
|
# nothing to do
|
|
|
|
} # SWITCH
|
|
|
|
# Verify the existence of the source path
|
|
if ( defined ($fname) and ! -e $record{$fname} ) {
|
|
$VERIFY and
|
|
&Error( 6001, &RevEnvVars($record{$fname}));
|
|
return;
|
|
} # if
|
|
|
|
# Insert record into the table
|
|
push @$table, \%record;
|
|
|
|
# Write the record to the log file
|
|
&LogRecord($maptype, \%record);
|
|
|
|
!defined ($fname) and return;
|
|
|
|
# For Bindirs, look for the value of Subdirs
|
|
if ( $MAPPINGS[$maptype] ne "BINDIRS" ) {
|
|
return;
|
|
} # if
|
|
|
|
# Return if no subdirectories to be parsed (
|
|
# BUG: 457873 implied bejavior should not be 'recursive'
|
|
if ( $record{R} !~ /y/i ) {
|
|
return;
|
|
} # if
|
|
|
|
# Get the list of subdirectories
|
|
@subdirs = `dir /ad /b $record{SrcPath}`;
|
|
|
|
# Add one record for each subdirectory found.
|
|
for ( $i=0; $i < @subdirs; $i++ ) {
|
|
|
|
chomp $subdirs[$i];
|
|
|
|
# Found that in NT5, the result of dir /b starts with an empty line
|
|
if ( $subdirs[$i] eq "" ) {
|
|
next;
|
|
}
|
|
|
|
# Skip the "symbols" subdirectory
|
|
if ( $subdirs[$i] eq "symbols" ) {
|
|
next;
|
|
}
|
|
|
|
# Add one record for the current subdirectory
|
|
%buffer = %record;
|
|
$buffer{DestPath} .= "\\$subdirs[$i]";
|
|
$buffer{SrcPath} .= "\\$subdirs[$i]";
|
|
|
|
&AddRecord( $maptype, %buffer );
|
|
|
|
} # for
|
|
|
|
return;
|
|
|
|
} # AddRecord
|
|
|
|
# LogRecord
|
|
# Writes the record to the log file.
|
|
# Input
|
|
# mapping type (reference)
|
|
# record (reference)
|
|
# Output
|
|
# <none>
|
|
|
|
sub LogRecord {
|
|
|
|
my( $maptype, $recref ) = @_;
|
|
|
|
# String to be written to the logfile
|
|
my $logstr;
|
|
|
|
$logstr = "$MAPPINGS[$maptype]:";
|
|
|
|
for ( sort keys %{$recref} ) {
|
|
|
|
$logstr .= "$_=$recref->{$_} ";
|
|
|
|
} # for
|
|
|
|
$logstr .= "\n";
|
|
# print "$logstr";
|
|
|
|
} # LogRecord
|
|
|
|
# ReplaceVar
|
|
# Replaces all the macronames with their value in the given string.
|
|
# Input
|
|
# the string to be evaluated
|
|
# the replacement type:
|
|
# 0 = scalar
|
|
# 1 = array
|
|
# Output
|
|
# array if the replacement type is 1
|
|
# string if the replacement type is 0
|
|
# In both cases, all the macros are evaluated.
|
|
# Usage
|
|
# $entry = &ReplaceVar( $entry, 0 );
|
|
# @records = &ReplaceVar( $entry, 1 );
|
|
|
|
sub ReplaceVar {
|
|
|
|
my( $line, $type ) = @_;
|
|
|
|
$line =~ s/\s{2,}/ /g;
|
|
$line =~ s/^\s//;
|
|
$line =~ s/\s$//;
|
|
|
|
my @list = ($line);
|
|
|
|
foreach $line (@list){
|
|
my $epyt = 0;
|
|
|
|
while ($line =~ /\$\(([^\)]+)\)/){
|
|
my $macro = $1;
|
|
$line =~ s/\$\(\Q$macro\E\)/__MACRO__/g;
|
|
|
|
if ($macro =~ /(\w+)\[\s?\]/) {
|
|
$macro = $1;
|
|
$epyt = 1;
|
|
}
|
|
|
|
my $thevalue = &GetMacro($macro);
|
|
$thevalue =~ s/\s\s+//g;
|
|
# BUG BUG we can end up chopping all whitespace!
|
|
|
|
if (!$epyt){
|
|
$line =~ s/__MACRO__/$thevalue/g;
|
|
}
|
|
else{
|
|
pop @list; # take one, give many
|
|
foreach my $value (split (";" , $thevalue)){
|
|
next if $value !~ /\S/;
|
|
my $this = $line;
|
|
$this =~ s/__MACRO__/$value/g;
|
|
push @list, $this;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
($type) ? @list : pop @list;
|
|
} # ReplaceVar
|
|
|
|
|
|
# LoadCodes
|
|
# Loads $CODEFNAME's file contents in %CODES
|
|
# Input & Output <none>
|
|
# Usage
|
|
# &LoadCodes();
|
|
|
|
sub LoadCodes {
|
|
|
|
# codes.txt filename
|
|
|
|
my $codefile = sprintf "%s\\$CODESFNAME", &GetMacro( "RAZZLETOOLPATH" );
|
|
scalar (%LANGCODES) or &ParseTable::parse_table_file($codefile, \%LANGCODES );
|
|
map {&SetMacro("$_", $LANGCODES{$LANG}->{$_}, 0)} keys(%{$LANGCODES{$LANG}});
|
|
|
|
foreach my $pLangKey (keys %LANGCODES) {
|
|
# accomplish the task previously solved by &Undefine_Exclude_Lang_Subdir...
|
|
next if $pLangKey eq $LANG;
|
|
$CODES{$pLangKey} = undef;
|
|
$EXCLUDE_SUBDIR{$ENV{"_NTTREE"} . "\\" . $pLangKey . ";" } = undef;
|
|
$EXCLUDE_SUBDIR{$ENV{"_NTTREE"} . "\\" . $LANGCODES{$pLangKey}->{"Lang"} . ";"} = undef;
|
|
next if $LANGCODES{$pLangKey}->{"Class"} eq &GetMacro("CLASS");
|
|
$EXCLUDE_SUBDIR{$ENV{"_NTTREE"} . "\\" . substr($LANGCODES{$pLangKey}->{"Class"},1) . ";"} = undef;
|
|
}
|
|
|
|
|
|
|
|
&SetMacro("OLD_LANG", &GetMacro("LANG"));
|
|
|
|
my $Exclude_Subdir = join(" " , keys(%EXCLUDE_SUBDIR));
|
|
|
|
&SetMacro($VERIFYPATH{"EXCLUDE_SUBDIR"}, join("; " ,
|
|
&GetMacro($VERIFYPATH{"EXCLUDE_SUBDIR"}) ,
|
|
$Exclude_Subdir),0);
|
|
|
|
my $RSRC_LCID = &GetMacro("LCID");
|
|
$RSRC_LCID =~ s/^0x0?//g;
|
|
&SetMacro("RSRC_LCID", "-l ". $RSRC_LCID, 0);
|
|
|
|
return;
|
|
|
|
} # LoadCodes
|
|
|
|
|
|
sub VerifyCover {
|
|
|
|
my %tree=();
|
|
|
|
return if &GetMacro("DIR_PATHS") eq "";
|
|
|
|
# Create except tree from @T$Mapping array, then remove the dummy to find all branch into $tree_hashptr
|
|
&Create_Tree_Branch(\%tree);
|
|
# Remove Root and its parents from branch tree
|
|
&Remove_Root_List(\%tree, grep {/\S/} split(/\s*;\s*/, &GetMacro("DIR_PATHS")));
|
|
$VERIFY and VerifyLocDropMapping(\%tree);
|
|
# Find unmapping folders from branch tree, also remove the empty folders and the specified file-pattern files (such as slm.ini).
|
|
&Find_UnMapping(\%tree);
|
|
|
|
} # VerifyCover
|
|
|
|
sub VerifyLocDropMapping($){
|
|
|
|
my $tree = shift;
|
|
my %mappeddir = ();
|
|
my $mappeddir = undef;
|
|
my $dir = undef;
|
|
my @files = ();
|
|
|
|
&dbgmsg(
|
|
"DIR_PATHS:\n\n",
|
|
join ("\n", grep {/\S/} split(/\s*;\s*/,
|
|
&RevEnvVars(&GetMacro("DIR_PATHS")))));
|
|
|
|
&dbgmsg( "Covered:\n\n",
|
|
join ("\n", grep {/\S/} split(/\s*;\s*/,
|
|
&RevEnvVars(join( "; ", sort (keys(%$tree))))
|
|
)));
|
|
|
|
foreach my $maptype (0..$#MAPPINGS){
|
|
|
|
my $tname = "T$MAPPINGS[$maptype]";
|
|
my $table = \@$tname;
|
|
|
|
my $fname = undef;
|
|
SWITCH: {
|
|
|
|
if ( $MAPPINGS[$maptype] =~ /TOK/i ) {
|
|
$fname = "TokPath";
|
|
last SWITCH;
|
|
} # if
|
|
|
|
if ( $MAPPINGS[$maptype] =~ /BIN/i ) {
|
|
$fname = "SrcPath";
|
|
last SWITCH;
|
|
} # if
|
|
|
|
if ( $MAPPINGS[$maptype] =~ /FILES/i ) {
|
|
$fname = "SrcPath";
|
|
last SWITCH;
|
|
} # if
|
|
|
|
# default
|
|
# nothing to do
|
|
|
|
} # SWITCH
|
|
@mappeddir {map {$table->[$_]->{$fname}} (0..$#$tname)} = ();
|
|
|
|
}
|
|
|
|
dbgmsg( "Mapped:\n\n". join "\n", map {&RevEnvVars($_)} (sort(keys(%mappeddir))));
|
|
|
|
# now find not mapped directories:
|
|
# start from $DIR_PATHS -> list files -> exclude covered by @mappeddir
|
|
# -> display
|
|
&dbgmsg(
|
|
"IGNORE_PATHS:\n\n",
|
|
join ("\n", grep {/\S/} split(/\s*;\s*/,
|
|
&RevEnvVars(&GetMacro("IGNORE_PATHS")))));
|
|
|
|
my @excludedir = grep {/\S/} split(/\s*;\s*/,
|
|
&GetMacro("IGNORE_PATHS"));
|
|
@files = ();
|
|
|
|
foreach $dir (sort(grep {/\S/} split(/\s*;\s*/, &GetMacro("DIR_PATHS")))){
|
|
|
|
# the DIR_PATHS is not covering the MISC/HELP files!
|
|
&dbgmsg("current $dir");
|
|
push @files, qx("dir /b/s/a-d $dir");
|
|
&dbgmsg("files\n".join "\n", @files );
|
|
|
|
foreach $mappeddir (@excludedir){
|
|
# do a simple check here
|
|
# $mappeddir =~ s/^.+\\\.\.\.\\([\w\\]+)/$1/g;
|
|
|
|
@files = grep {!/\Q$mappeddir\E\\/oi} @files;
|
|
|
|
if ($mappeddir=~/\.\.\./){
|
|
my ($head, $tail) = ($mappeddir =~ /(.+)\\\.\.\.\\(.+)/);
|
|
@files = grep {!/\Q$head\E\\.+\\\Q$tail\E\\/oi} @files;
|
|
dbgmsg("\$head=$head, \$tail=$tail");
|
|
}
|
|
|
|
}
|
|
foreach $mappeddir (keys(%mappeddir)){
|
|
&dbgmsg("scan $mappeddir");
|
|
|
|
@files = grep {!/\Q$mappeddir\E\\[^\\]+$/oi} @files;
|
|
}
|
|
}
|
|
map {chop} @files;
|
|
map {&Error(6002, &RevEnvVars($_))} @files if ($#files);
|
|
|
|
|
|
exit;
|
|
|
|
} # VerifyLocDropMapping
|
|
|
|
|
|
# Create_Tree_Branch
|
|
# Create except tree from @T$Mapping array, then remove the dummy to find all branch into $tree_hashptr
|
|
# Input
|
|
# $tree_hashptr - a hash pointer for store the branch tree
|
|
#
|
|
# Output
|
|
# none - the branch tree stored in %$tree_hashptr
|
|
# Usage
|
|
# &Create_Tree_Branch(\%mytree);
|
|
|
|
sub Create_Tree_Branch {
|
|
my($tree_hashptr)=@_;
|
|
my(%except)=();
|
|
|
|
# Create Exception Tree from @T$Mapping array
|
|
&Get_Except(\%except);
|
|
|
|
# Remove the subdir if parent defined, the remains create into hash (parent dir) - hash (current dir)
|
|
&Remove_Dummy_Create_Branch(\%except,$tree_hashptr);
|
|
}
|
|
|
|
# Get_Except
|
|
# Create Exception Tree from @T$Mapping array
|
|
# Input
|
|
# $except_hashptr - a hash pointer for store the exception tree
|
|
#
|
|
# Output
|
|
# none - the exception tree stored in %$except_hashptr
|
|
#
|
|
# Usage
|
|
# &Get_Except(\%myexcept);
|
|
|
|
sub Get_Except {
|
|
my($except_hashptr)=@_;
|
|
|
|
my($tablename, $hashptr)=();
|
|
|
|
## Predefine except directories
|
|
for (split(/\s*;\s*/, &GetMacro($VERIFYPATH{'EXCLUDE_SUBDIR'}))) {
|
|
next if ($_ eq "");
|
|
if (/\.\.\./) {
|
|
map({$except_hashptr->{$_}=1} &Find_Dot_Dot_Dot(lc$_));
|
|
} else {
|
|
$except_hashptr->{lc $_}=1;
|
|
}
|
|
}
|
|
|
|
## According bindir or tokdir to define the sourcepath to %except
|
|
for $tablename (@MAPPINGS) {
|
|
for $hashptr (@{"T$tablename"}) {
|
|
if (exists $hashptr->{'R'}) {
|
|
if ($hashptr->{'R'} eq 'n') {
|
|
$except_hashptr->{ lc$hashptr->{SrcPath} . "\\."} = 1
|
|
} else {
|
|
$except_hashptr->{lc$hashptr->{SrcPath}} = 1
|
|
}
|
|
} elsif (exists $hashptr->{SrcPath}) {
|
|
if (defined $hashptr->{SrcFile}) {
|
|
$except_hashptr->{lc($hashptr->{SrcPath} . "\\" . $hashptr->{SrcFile})} = 1
|
|
} else {
|
|
$except_hashptr->{lc$hashptr->{SrcPath}} = 1
|
|
}
|
|
} elsif (exists $hashptr->{TokPath}) {
|
|
if (defined $hashptr->{TokFile}) {
|
|
$except_hashptr->{lc($hashptr->{TokPath} . "\\" . $hashptr->{TokFile})} = 1
|
|
} else {
|
|
$except_hashptr->{lc$hashptr->{TokPath}} = 1
|
|
}
|
|
} else {&Error( 2051, $hashptr);}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Find_Dot_Dot_Dot
|
|
# Collect the combination of the ... folders
|
|
# Input
|
|
# $path - A path contains "..."
|
|
#
|
|
# Output
|
|
# keys %regist - The array of all the combination found in the path
|
|
#
|
|
# Usage
|
|
# &Find_Dot_Dot_Dot("$ENV{_NTTREE}\\...\\symbols.pri");
|
|
|
|
sub Find_Dot_Dot_Dot {
|
|
my ($path) = @_;
|
|
my ($mymatch, $file, %regist);
|
|
my @piece=split(/\\\.{3}/, $path);
|
|
|
|
for $file ( `dir /s/b/l $piece[0]$piece[-1] 2>nul`) {
|
|
chomp $file;
|
|
$mymatch = &Match_Subjects($file, \@piece);
|
|
next if (!defined $mymatch);
|
|
$regist{$mymatch}=1;
|
|
}
|
|
return keys %regist;
|
|
}
|
|
|
|
# Match_Subjects
|
|
# The subroutine called by Find_Dot_Dot_Dot, which perform the match for
|
|
# all the pieces of the infomation of the path with the file. For example
|
|
#
|
|
# Input
|
|
# $file - The fullpath filename
|
|
# $pieceptr - The array ptr
|
|
#
|
|
# Output
|
|
# undef if not match
|
|
# $match - store the path it matched
|
|
#
|
|
# Usage
|
|
# &Match_Subjects(
|
|
# "D:\\ntb.binaries.x86fre\\dx8\\symbols\\retail\\dll\\migrate.pdb",
|
|
# \@{"D:\\ntb.binaries.x86fre", "symbols\\", "dll\\"} );
|
|
# Return => "D:\\ntb.binaries.x86fre\\dx8\\symbols\\retail\\dll"
|
|
#
|
|
|
|
sub Match_Subjects {
|
|
my ($file, $pieceptr)=@_;
|
|
my $match;
|
|
# my $DEBUG = 1;
|
|
|
|
for (@$pieceptr) {
|
|
return if ($file!~/\Q$_\E/);
|
|
$match .= $` . $&;
|
|
$file = $';
|
|
}
|
|
# $DEBUG and print STDERR "$match\n";
|
|
return $match;
|
|
}
|
|
|
|
# Remove_Dummy_Create_Branch
|
|
# Remove the subdir if parent defined, the remains create into hash (parent dir) - hash (current dir)
|
|
# Input
|
|
# $except_hashptr - stored the exception hash from %T$Mapping
|
|
# $tree_hashptr - will store the covered tree in hash - hash
|
|
#
|
|
# Output
|
|
# none - the covered tree stored in %$tree_hashptr
|
|
#
|
|
# Usage
|
|
# &Remove_Dummy_Create_Branch(\%myexcept, \%mybranch);
|
|
|
|
sub Remove_Dummy_Create_Branch {
|
|
my($except_hashptr,$tree_hashptr)=@_;
|
|
|
|
next1: for (keys %$except_hashptr) {
|
|
|
|
# loop all its parent
|
|
|
|
while(/\\+[^\\]+/g) {
|
|
# print STDERR join ("\t",($_, $`, $1)), "\n";
|
|
# Is this folder covered by its parent?
|
|
if (exists $except_hashptr->{$`}) {
|
|
# Remove current folder
|
|
delete $except_hashptr->{$_};
|
|
next next1;
|
|
} else {
|
|
# define the $tree{$parent}->{$child}=1
|
|
$tree_hashptr->{$`}->{$&}=1;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
# Remove_Root_List
|
|
# Remove Root and its parents from branch tree
|
|
# Input
|
|
# $tree_hashptr - a hash pointer, %$tree_hashptr is the branch tree
|
|
# @root_list_ptr - a array for the root list
|
|
#
|
|
# Output
|
|
# none - the root and its parent will be remove from branch tree (%$tree_hashptr)
|
|
#
|
|
# Usage
|
|
# &VerifyCover();
|
|
|
|
sub Remove_Root_List {
|
|
my($tree_hashptr, @root_list_ptr)=@_;
|
|
|
|
my $concern;
|
|
|
|
# split the $root_list by ';'
|
|
for (@root_list_ptr) {
|
|
|
|
# loop all its parents
|
|
while(/\\/g) {
|
|
# remove all the parent folder
|
|
delete $tree_hashptr->{$`} if ($` ne $_);
|
|
}
|
|
}
|
|
Next2: for (keys %$tree_hashptr) {
|
|
for $concern (@root_list_ptr) {
|
|
next Next2 if (/\Q$concern\E/i);
|
|
}
|
|
delete $tree_hashptr->{$_};
|
|
}
|
|
}
|
|
|
|
# Find_UnMapping
|
|
# Find unmapping folders from branch tree, also remove the empty folders and the specified file-pattern files (such as slm.ini).
|
|
# Input
|
|
# $tree_hashptr - a hash - hash pointer, %$tree_hashptr is the branch tree
|
|
#
|
|
# Output
|
|
# none
|
|
# Usage
|
|
# &VerifyCover();
|
|
|
|
|
|
sub Find_UnMapping {
|
|
|
|
my $tree = shift;
|
|
|
|
my ($parent, $mykey, @myfiles);
|
|
|
|
for $parent (keys %$tree) {
|
|
my @children = ();
|
|
|
|
if (opendir(PARENT, $parent)){
|
|
# @children = grep {-d "$parent\\$_"} readdir (PARENT);
|
|
while (my $child = readdir (PARENT)){
|
|
|
|
next if $child =~ /^\.+$/;
|
|
push @children, lc($child) if -d "$parent\\$child";
|
|
}
|
|
}
|
|
|
|
closedir (PARENT);
|
|
BADDIR: foreach my $child (@children) {
|
|
if (!exists $tree->{$parent}->{"\\$child"}) {
|
|
$mykey = "$parent\\$child";
|
|
|
|
# find its all children / subidr children,
|
|
# and remove specified file pattern files, then repro the error
|
|
foreach my $file (qx("dir /s/b/a-d/l $mykey 2>nul")) {
|
|
# chomp $file;
|
|
$file =~ s/\\[^\\]+$//sgi;
|
|
&Error($mykey =~ /\Q$ENV{"_NTTREE"}\E/i ? 2053 : 2054, &RevEnvVars($file))
|
|
and next BADDIR if ($file !~ /$VERIFYPATH{'EXCLUDE_FILE_PATTERN'}/i
|
|
&& $file =~ /^(.+)\\[^\\]+$/)
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
# PopulateReposit
|
|
# Populates REPOSITORY with data from the mapping tables ( @T variables).
|
|
# Input & Output <none>
|
|
# Usage
|
|
# &PopulateReposit();
|
|
|
|
sub PopulateReposit {
|
|
|
|
# Fill filter table
|
|
&FillFilter();
|
|
|
|
# Add file entries from TFILES and TBINDIRS
|
|
&AddFiles();
|
|
|
|
# If FILTER is defined, verify if all its entries (file names)
|
|
# were loaded in Repository.
|
|
for ( keys %FILTER ) {
|
|
|
|
if ( &IsEmptyHash( $TARGETS ) || &IsHashKey( $TARGETS, $_ ) ) {
|
|
|
|
if ( $FILTER{$_} <= 0 ) {
|
|
&Error( 1002, sprintf( "%s (%s)", $_, &GetMacro( "FILTER" ) ) );
|
|
next;
|
|
} # if
|
|
|
|
} # if
|
|
|
|
} # for
|
|
|
|
# If TARGETS is defined, verify that all its entries (target names)
|
|
# were loaded in Repository.
|
|
for ( keys %$TARGETS ) {
|
|
( &IsFoundTarget( $_ ) ) || &FatalError( 1004, $_ );
|
|
} # for
|
|
# Fill in info about tokens from TTOKENS and TTOKDIRS
|
|
&FillTokens();
|
|
|
|
# Fill in the {Cmd} field for relevant keys
|
|
&FillCmd();
|
|
|
|
return;
|
|
|
|
} # PopulateReposit
|
|
|
|
# FillCmd
|
|
# Fill in the {Cmd} REPOSITORY field
|
|
# Input & Output
|
|
# none
|
|
# Usage
|
|
# &FillCmd();
|
|
|
|
sub FillCmd {
|
|
|
|
|
|
|
|
# Situations when, for a given (DestPath, DestFile) REPOSITORY key,
|
|
# the associated NMAKE commands must be stored in REPOSITORY's {Cmd} field:
|
|
# - clean SYSGEN
|
|
# - incremental SYSGEN with no command-line targets specified (empty TARGETS)
|
|
my $key = {"DestPath" => undef,
|
|
"DestFile" => undef};
|
|
|
|
my $all = $CLEAN || &IsEmptyHash( $TARGETS );
|
|
|
|
# Fill in the {Cmd} field
|
|
&SysgenLogMsg("Translating data to makefile commands...", 0);
|
|
|
|
|
|
foreach my $destpath ( sort keys %$REPOSITORY ) {
|
|
|
|
$SYNTAX_CHECK_ONLY or print &RevEnvVars("\t$destpath") unless !$all;
|
|
|
|
foreach my $destfile ( keys %{$REPOSITORY->{$destpath}} ) {
|
|
|
|
# Repository key
|
|
$key->{"DestPath"} = $destpath;
|
|
$key->{"DestFile"} = $destfile;
|
|
|
|
|
|
|
|
#~ # Check special character to avoid nmake broken
|
|
#~ if ( $destfile =~ /\$$/ ) {
|
|
#~ delete $REPOSITORY->{$destpath}->{$destfile};
|
|
#~ delete $TARGETS->{$destfile};
|
|
#~
|
|
#~ &Error(1214, "$destpath\\$destfile");
|
|
#~ next;
|
|
#~ }
|
|
|
|
# If incremental with targets, print target's full path
|
|
if ( !$all && &IsHashKey( $TARGETS, $destfile ) ) {
|
|
$SYNTAX_CHECK_ONLY or
|
|
printf "\t%s\\%s\n",
|
|
&GetFieldVal( $REPOSITORY, $key, "DestPath" ),
|
|
&GetFieldVal( $REPOSITORY, $key, "DestFile" );
|
|
|
|
} # if
|
|
|
|
# Translate the current REPOSITORY entry into a set of nmake commands
|
|
if ( $all || &IsHashKey( $TARGETS, $destfile ) ) {
|
|
&RecordToCmd( $key );
|
|
} # if
|
|
|
|
} # for
|
|
if ($all){
|
|
$SYNTAX_CHECK_ONLY or
|
|
print "\n";
|
|
};
|
|
|
|
|
|
} # for
|
|
|
|
return;
|
|
|
|
} # FillCmd
|
|
|
|
# AddTarget
|
|
# Add a filename (given on the command line) to the TARGETS table
|
|
|
|
sub AddTarget {
|
|
|
|
$TARGETS->{lc$_[0]}->{Target}=1;
|
|
|
|
} # AddTarget
|
|
|
|
# IsHashKey
|
|
# Verifies if a given string is key in a given hash
|
|
# Input
|
|
# hash (reference)
|
|
# entry (without path)
|
|
# Output
|
|
# 1 if the key exists
|
|
# 0 otherwise
|
|
|
|
sub IsHashKey {
|
|
|
|
return (exists $_[0]->{lc $_[1]});
|
|
|
|
} # IsHashKey
|
|
|
|
|
|
# IsFoundTarget
|
|
# Verifies if a filename is marked as found in TARGETS
|
|
# All targets specified in the command line must be marked in TARGETS at the moment
|
|
# REPOSITORY is fully loaded.
|
|
# Input
|
|
# filename (no path)
|
|
# Output
|
|
# 1 if the target is loaded in Repository
|
|
# 0 otherwise
|
|
|
|
sub IsFoundTarget {
|
|
|
|
if ( @{$TARGETS->{lc$_[0]}->{NewDestPath}} > 0 ) { return 1; }
|
|
return 0;
|
|
|
|
} # IsFoundTarget
|
|
|
|
# AddTargetPath
|
|
# Add a DestPath value to one of the NewDestPath, OldDestPath fields of TARGETS
|
|
# Input
|
|
# target name
|
|
# field name (one of NewDestPath and OldDestPath)
|
|
# field value
|
|
# Output
|
|
# none
|
|
# Usage
|
|
# &AddTargetPath( "ntdll.dll", "OldDestPath", "f:\\nt\\relbins\\jpn");
|
|
|
|
sub AddTargetPath {
|
|
|
|
push @{$TARGETS->{lc$_[0]}->{$_[1]}}, lc$_[2];
|
|
|
|
} # AddTargetPath
|
|
|
|
# IsEmptyHash
|
|
# Verifies if a hash table is empty.
|
|
# Input
|
|
# hash table (reference)
|
|
# Output
|
|
# 1 if hash is empty
|
|
# 0 otherwise
|
|
|
|
sub IsEmptyHash {
|
|
|
|
!scalar(%{$_[0]});
|
|
|
|
} # IsEmptyHash
|
|
|
|
# UpdTargetEntries
|
|
# In case of an incremental build, update the entries corresponding to the TARGETS
|
|
# from CMD_REPOSITORY according to data from REPOSITORY.
|
|
# Input & Output
|
|
# none
|
|
|
|
sub UpdTargetEntries {
|
|
|
|
# Target name
|
|
my $tname;
|
|
|
|
# Indexes
|
|
my $i;
|
|
|
|
# CMD_REPOSITORY key
|
|
my %key;
|
|
|
|
# OldDestPath array (reference)
|
|
my $destref;
|
|
|
|
if ( $CLEAN || &IsEmptyHash($TARGETS) ) { return; }
|
|
|
|
for $tname ( keys %$TARGETS ) {
|
|
|
|
$key{DestFile} = $tname;
|
|
|
|
# Delete CMD_REPOSITORY entries with DestFile equals to target name
|
|
$destref = $TARGETS->{$tname}->{OldDestPath};
|
|
|
|
for ( $i=0; $i < @{$destref}; $i++ ) {
|
|
$key{DestPath} = $destref->[$i];
|
|
&DeleteKey( $CMD_REPOSITORY, \%key );
|
|
|
|
} # for
|
|
|
|
# Copy data for TARGETS from REPOSITORY to CMD_REPOSITORY
|
|
$destref = $TARGETS->{$tname}->{NewDestPath};
|
|
|
|
for ( $i=0; $i < @{$destref}; $i++ ) {
|
|
|
|
$key{DestPath} = $destref->[$i];
|
|
|
|
# Use the same Cmd for CMD_REPOSITORY as for REPOSITORY
|
|
&SetField( $CMD_REPOSITORY, \%key, "DestPath", &GetFieldVal( $REPOSITORY, \%key, "DestPath" ) );
|
|
&SetField( $CMD_REPOSITORY, \%key, "DestFile", &GetFieldVal( $REPOSITORY, \%key, "DestFile" ) );
|
|
&SetField( $CMD_REPOSITORY, \%key, "Cmd", &GetFieldVal( $REPOSITORY, \%key, "Cmd" ) );
|
|
|
|
} # for
|
|
|
|
} # for
|
|
|
|
return;
|
|
|
|
} # UpdTargetEntries
|
|
# AddEntry
|
|
# Adds a new entry to REPOSITORY, only if REPOSITORY does not already have an
|
|
# entry with the same (DestPath, DestFile) key.
|
|
# Input
|
|
# new record (reference)
|
|
# boolean argument, saying if the file existence should be checked
|
|
# Output
|
|
# none
|
|
# Usage
|
|
# &AddEntry( $recref);
|
|
|
|
sub AddEntry {
|
|
|
|
my($recref, $e_check) = @_;
|
|
|
|
# Db key
|
|
my %key;
|
|
|
|
# Add entry to Repository based on file existence and on
|
|
# the type of the call (clean, incremental, with or without targets)
|
|
|
|
$recref->{DestFile} !~/\s/ or
|
|
&Error( 4001, "\"$recref->{SrcPath}\\$recref->{SrcFile}\"") and return;
|
|
|
|
if ( ( &IsEmptyHash( \%FILTER ) || # no filters
|
|
&IsHashKey( \%FILTER, $recref->{DestFile} ) ) # filter file
|
|
&&
|
|
( $CLEAN || # clean sysgen
|
|
&IsEmptyHash( $TARGETS ) || # incremental with no targets specified
|
|
&IsHashKey( $TARGETS, $recref->{DestFile} ) ) # incremental with targets, and the current
|
|
# entry is one of the targets
|
|
) {
|
|
|
|
if ( $e_check ) {
|
|
if ( ! -e "$recref->{SrcPath}\\$recref->{SrcFile}" ) {
|
|
&Error( 2101, &RevEnvVars("$recref->{SrcPath}\\$recref->{SrcFile}") );
|
|
return;
|
|
} # if
|
|
} # if
|
|
|
|
# Set the key
|
|
$key{DestPath} = $recref->{DestPath};
|
|
$key{DestFile} = $recref->{DestFile};
|
|
|
|
&AddFileInfo( $recref, \%key );
|
|
|
|
} # if
|
|
|
|
return;
|
|
|
|
} # AddEntry
|
|
|
|
# AddFileInfo
|
|
# Input
|
|
# record to be added
|
|
# Output
|
|
# <none>
|
|
# Usage
|
|
# &AddFileInfo( $recref, \%key );
|
|
|
|
sub AddFileInfo {
|
|
|
|
my($recref, $keyref) = @_;
|
|
|
|
# Nothing to do if the DestFile already has an entry in the Repository
|
|
( ! &IsRepositKey( $REPOSITORY, $keyref ) ) || return;
|
|
|
|
if ( &IsHashKey( $TARGETS, $keyref->{DestFile} ) ) {
|
|
&AddTargetPath( $keyref->{DestFile}, "NewDestPath", $keyref->{DestPath} );
|
|
} # if
|
|
|
|
if ( &IsHashKey( \%FILTER, $recref->{DestFile} ) ) {
|
|
$FILTER{lc( $recref->{DestFile} )}++;
|
|
} # if
|
|
|
|
# Set the fields of the new entry
|
|
&SetField( $REPOSITORY, $keyref, "DestPath", $recref->{DestPath} );
|
|
&SetField( $REPOSITORY, $keyref, "DestFile", $recref->{DestFile} );
|
|
&SetField( $REPOSITORY, $keyref, "SrcPath", $recref->{SrcPath} );
|
|
&SetField( $REPOSITORY, $keyref, "SrcFile", $recref->{SrcFile} );
|
|
&SetField( $REPOSITORY, $keyref, "SrcDbg", $recref->{SrcDbg} );
|
|
&SetField( $REPOSITORY, $keyref, "DestDbg", $recref->{DestDbg} );
|
|
|
|
SWITCH: {
|
|
if ( $recref->{Xcopy} eq "y" ) {
|
|
|
|
&SetField( $REPOSITORY, $keyref, "OpType", "1" );
|
|
last SWITCH;
|
|
} # case
|
|
|
|
# default
|
|
## &SetField( $REPOSITORY, $keyref, "OpType", "0" );
|
|
} # SWITCH
|
|
|
|
return;
|
|
|
|
} # AddFileInfo
|
|
|
|
# IsRepositKey
|
|
# Looks in REPOSITORY for the given key.
|
|
# Used to decide if a record will be added or not to REPOSITORY.
|
|
# Input
|
|
# map (reference)
|
|
# the key (reference)
|
|
# Output
|
|
# 1 if the key is found and 0 otherwise
|
|
# Usage
|
|
# $is_key_found = &IsRepositKey( $REPOSITORY, $keyref );
|
|
|
|
sub IsRepositKey {
|
|
|
|
# The key referred by keyref is modified,
|
|
# but this saves a lot of execution time
|
|
# so we prefer not to work with a copy of the key
|
|
|
|
return ( exists $_[0]->{lc$_[1]->{DestPath}}->{lc$_[1]->{DestFile}} );
|
|
|
|
} # IsRepositKey
|
|
|
|
# DeleteKey
|
|
# Input
|
|
# map name (one of REPOSITORY, CMD_REPOSITORY) (reference)
|
|
# key (reference)
|
|
# Output
|
|
# none
|
|
|
|
sub DeleteKey {
|
|
|
|
# my( $mapref, $keyref ) = @_;
|
|
|
|
# The key referred by keyref is modified,
|
|
# but this saves a lot of execution time
|
|
# so we prefer not to work with a copy of the key
|
|
|
|
delete $_[0]->{lc$_[1]->{DestPath}}->{lc$_[1]->{DestFile}};
|
|
|
|
} # DeleteKey
|
|
|
|
# FillTokens
|
|
# Adds info regarding tokens in REPOSITORY (TTOKENS and TTOKDIRS).
|
|
# Input & Output <none>
|
|
# Usage
|
|
# &FillTokens();
|
|
|
|
sub FillTokens {
|
|
|
|
# Current entry from TTOKDIRS
|
|
my $recref;
|
|
|
|
# List of files found in the current directory
|
|
my @files;
|
|
|
|
# Indexes
|
|
my( $i, $j, $k );
|
|
|
|
# Custom resource file name
|
|
my $custfile;
|
|
|
|
# Custom reosurces array
|
|
my @custres;
|
|
|
|
# Custom reosurces hash for verification
|
|
my %custres_h;
|
|
|
|
# Temporary store the file hash for special sort @files
|
|
my %hashfiles;
|
|
|
|
&SysgenLogMsg("Filling token data...",0);
|
|
|
|
# Fill in TTOKENS
|
|
for ( $i=0; $i < @TTOKENS; $i++ ) {
|
|
|
|
$recref = $TTOKENS[$i];
|
|
|
|
my $TokFile = $recref->{"TokFile"};
|
|
|
|
# rsrc or lcx
|
|
if ( $TokFile =~ m/\.lcx\r?$/i or $TokFile =~ m/\.rsrc\r?$/i) {
|
|
|
|
&FillTokEntry( $recref, 1 );
|
|
next;
|
|
|
|
} # if
|
|
|
|
# bingen
|
|
if ( $TokFile =~ m/\.\w+_\r?$/i) {
|
|
|
|
# Identify all the custom resources under the following assumptions:
|
|
# * the token and the custom resources of a binary are in the same directory
|
|
# * for a "<binary_name>.<token_extension>" token, a custom resource has
|
|
# the name <binary_name>_<binary_extension>_<custom_resource_name>
|
|
# * a token is always followed in lexicographic order by its custom resources
|
|
# or by another token
|
|
|
|
# Parse the token file to find embeded files
|
|
# open(F, "$recref->{TokPath}\\$recref->{TokFile}");
|
|
# my @text=<F>;
|
|
# close(F);
|
|
# for $str (@text) {
|
|
# if (($str=~/\[[^\]]+\|1\|0\|4\|\"[\w\.]*\"]+\=(\w+\.\w+)\s*$/)&&(-e $1)) {
|
|
# $custres_h{$1}=0;
|
|
# }
|
|
# }
|
|
# undef @text;
|
|
|
|
# Store it to an array for later use
|
|
# @custres=keys %custres_h;
|
|
|
|
# Verify all its names' token files are included
|
|
|
|
$custfile = $recref->{DestFile};
|
|
$custfile =~ s/\./_/g;
|
|
|
|
@custres=qx("dir /a-d-h /b /on $recref->{TokPath}\\$custfile\* 2\>nul")
|
|
if -d "$recref->{TokPath}\\$custfile";
|
|
$recref->{CustRc} = "";
|
|
|
|
for ( $j=0; $j < @custres; $j++ ) {
|
|
chop $custres[$j];
|
|
|
|
# Found that in NT5, the result of dir /b starts with an empty line
|
|
if ( $custres[$j] eq "" ) {
|
|
next;
|
|
} # if
|
|
|
|
$recref->{CustRc} .= "\ \\\n\t\t\t$recref->{TokPath}\\$custres[$j]";
|
|
|
|
} # for
|
|
|
|
# Call FillTokEntry with 1, meaning that the existence of the
|
|
# token file is queried and a non fatal error fired if the file
|
|
# is missing.
|
|
# As the token is explicitly listed in TOKENS MAP, it is natural
|
|
# to suppose it should exist.
|
|
&FillTokEntry( $recref, 1 );
|
|
|
|
next;
|
|
|
|
} # if
|
|
|
|
# default
|
|
&Error( 2013, "$recref->{TokPath}\\$recref->{TokFile}" );
|
|
|
|
|
|
} #for
|
|
|
|
@TTOKENS = ();
|
|
|
|
# Fill in TTOKDIRS
|
|
for ( $i=0; $i < @TTOKDIRS; $i++ ) {
|
|
|
|
$recref = $TTOKDIRS[$i];
|
|
|
|
&SysgenLogMsg("\t$recref->{TokPath}",0);
|
|
|
|
# Load all files from the TokPath directory
|
|
@files = ();
|
|
|
|
# because in the same folder, so we could ignore \a\b.txt, \a\b\c.txt, \a\c.txt problem.
|
|
@files =
|
|
sort {&TokToFile($a , 1) cmp &TokToFile($b, 1)}
|
|
split ("\n", qx("dir /a-d-h /l /b $recref->{TokPath} 2\>nul"));
|
|
|
|
|
|
# Fill in the corresponding entry from REPOSITORY for each token found at TokPath
|
|
for ( $j=0; $j < @files; $j++ ) {
|
|
|
|
my $TokFile = $files[$j];
|
|
|
|
# Found that in NT5, the result of dir /b starts with an empty line (bug)
|
|
if ( $TokFile eq "" ) {
|
|
next;
|
|
|
|
}
|
|
|
|
if ( $TokFile =~ m/\.lcx\r?$/i) {
|
|
|
|
$recref->{TokFile} = $TokFile;
|
|
$recref->{DestFile} = &TokToFile( $TokFile );
|
|
|
|
&FillTokEntry( $recref, 0, \@custres );
|
|
|
|
# look for bogus custom resources
|
|
$custfile = &TokToFile($TokFile, 1);
|
|
|
|
for ( $k = $j+1; $k < @files; $k++ ) {
|
|
|
|
if ( $files[$k] =~ /$custfile/i ) {
|
|
&Error( 2019, "$recref->{TokPath}\\$files[$k] for $recref->{TokPath}\\$TokFile" );
|
|
} # if
|
|
else {
|
|
last;
|
|
} # else
|
|
|
|
} # for
|
|
$j = $k-1;
|
|
|
|
next;
|
|
}
|
|
# bingen
|
|
if ( $TokFile =~ m/\.\w+_\r?$/i ) {
|
|
|
|
$recref->{TokFile} = $TokFile;
|
|
$recref->{DestFile} = &TokToFile( $TokFile );
|
|
|
|
# check if the next entry in @files is an rsrc token for the same filename
|
|
if ( $j < @files-1 && $files[$j+1] =~ m/\.rsrc\r?$/i ) {
|
|
|
|
if ( $recref->{DestFile} eq &TokToFile( $files[$j+1] ) ) {
|
|
&Error( 2018, "$recref->{DestPath}\\$files[$j] and $recref->{DestPath}\\$files[$j+1]");
|
|
$j = $j+1;
|
|
} # if
|
|
} # if
|
|
|
|
# Identify file's custom resources
|
|
$custfile = &TokToFile( $TokFile , 1 );
|
|
|
|
$recref->{CustRc} = "";
|
|
for ( $k = $j+1; $k < @files; $k++ ) {
|
|
|
|
if ( $files[$k] =~ /$custfile/i ) {
|
|
$recref->{CustRc} .= "\ \\\n\t\t\t$recref->{TokPath}\\$files[$k]";
|
|
} # if
|
|
else {
|
|
last;
|
|
} # else
|
|
|
|
} # for
|
|
$j = $k-1;
|
|
|
|
# Call FillTokEntry with 0, to inhibit the file existence checking,
|
|
# as TokFile is a result of a dir command (see above).
|
|
|
|
&FillTokEntry( $recref, 0, \@custres );
|
|
|
|
next;
|
|
} # if
|
|
else {
|
|
# rsrc
|
|
if ( $TokFile =~ m/\.rsrc\r?$/i ) {
|
|
|
|
$recref->{TokFile} = $TokFile;
|
|
$recref->{DestFile} = &TokToFile($TokFile);
|
|
|
|
# look for bogus custom resources
|
|
$custfile = &TokToFile($TokFile, 1);
|
|
|
|
for ( $k = $j+1; $k < @files; $k++ ) {
|
|
|
|
if ( $files[$k] =~ /$custfile/i ) {
|
|
&Error( 2019, "$recref->{TokPath}\\$files[$k] for $recref->{TokPath}\\$files[$j]" );
|
|
} # if
|
|
else {
|
|
last;
|
|
} # else
|
|
|
|
} # for
|
|
$j = $k-1;
|
|
|
|
&FillTokEntry( $recref, 0, \@custres );
|
|
|
|
next;
|
|
} # if
|
|
|
|
else {
|
|
# default
|
|
&Error( 2013, "$recref->{TokPath}\\$TokFile" );
|
|
|
|
} # else
|
|
|
|
} #else
|
|
|
|
|
|
} # for
|
|
|
|
} # for
|
|
|
|
@TTOKDIRS = ();
|
|
|
|
} # FillTokens
|
|
|
|
|
|
# TokToFile
|
|
# Converts a given bingen token name to a binary name.
|
|
# Input
|
|
# token name
|
|
# Optional argument: replace "." with "_" to provde resource allocation
|
|
# like
|
|
# autodisc_dll_reginst.inf => autodisc.dll
|
|
#
|
|
# Output
|
|
# binary name
|
|
# Usage
|
|
# $fname = &TokToFile( $tokname, $replacedots );
|
|
#
|
|
|
|
sub TokToFile{
|
|
|
|
my ($tokname, $replacedots) = @_;
|
|
my $badname = 1;
|
|
|
|
foreach my $toktype (keys(%$MAP_BINGEN_TOK_EXT)) {
|
|
$badname = 0 if $tokname =~/$toktype$/i;
|
|
$toktype = $MAP_BINGEN_TOK_EXT->{$toktype};
|
|
$toktype =~ s/\./\_/g;
|
|
$badname = 0 if $tokname =~/$toktype\_/i;
|
|
} # if
|
|
|
|
$badname and &Error( 2013, $tokname);
|
|
|
|
map { $tokname =~ s/\Q$_\E$/$MAP_BINGEN_TOK_EXT->{$_}/g;} keys %$MAP_BINGEN_TOK_EXT;
|
|
$tokname =~ s/\./_/g if $replacedots;
|
|
|
|
$tokname;
|
|
} # TokToFile
|
|
|
|
|
|
# FillTokEntry
|
|
# Given a REPOSITORY key, adds info on tokens (@TTOKENS and @TTOKDIRS).
|
|
# Input
|
|
# a token record
|
|
# boolean argument, saying if the existence of the token needs to be verified
|
|
# Output
|
|
# <none>
|
|
|
|
sub FillTokEntry {
|
|
|
|
my($recref, $e_check) = @_;
|
|
|
|
# %key = is a hash table (as defined by the database key)
|
|
my %key;
|
|
|
|
# Token information is added to an existing Repository entry
|
|
# based on token file existance and the type of the sysgen call
|
|
# (clean, incremental with or without targets)
|
|
|
|
if ( ( &IsEmptyHash( \%FILTER ) || # no filter
|
|
&IsHashKey( \%FILTER, $recref->{DestFile} ) ) # filter file
|
|
&&
|
|
( $CLEAN || # clean sysgen
|
|
&IsEmptyHash( $TARGETS ) || # incremental without targets
|
|
&IsHashKey( $TARGETS, $recref->{DestFile} ) ) # incremental with targets and the current
|
|
# entry corresponds to a target
|
|
|
|
) {
|
|
|
|
if ( $e_check ) {
|
|
if ( ! -e "$recref->{TokPath}\\$recref->{TokFile}" ) {
|
|
&Error( 2102, "$recref->{TokPath}\\$recref->{TokFile}" );
|
|
return;
|
|
} # if
|
|
} # if
|
|
|
|
# Set REPOSITORY key
|
|
$key{DestPath} = $recref->{DestPath};
|
|
$key{DestFile} = $recref->{DestFile};
|
|
|
|
# Fill in the information needed in REPOSITORY
|
|
|
|
&FillTokInfo( $recref, \%key );
|
|
|
|
} # if
|
|
|
|
return;
|
|
|
|
} # FillTokEntry
|
|
|
|
# FillTokInfo
|
|
# Adds information for bingen switches to an existing REPOSITORY entry.
|
|
# Sets the following keys ( fields ): TokPath, TokFile, BgnSw, OpType, ITokPath.
|
|
# Input
|
|
# hash table with the @FTOKENS keys, representing one token record (reference)
|
|
# key to identify the REPOSITORY entry (reference)
|
|
# Output
|
|
# <none>
|
|
# Usage
|
|
# &FillTokInfo( $recref, \%key );
|
|
|
|
sub FillTokInfo {
|
|
|
|
my( $recref, $keyref ) = @_;
|
|
|
|
# Operation type
|
|
my $optype;
|
|
# A token record does not create new entries, just adds/updates fields of an existing entry.
|
|
|
|
|
|
if ( ! &ExistsBinForTok( $keyref, $recref ) ) {
|
|
my $notfound = 1;
|
|
my $key = lc($recref->{"TokPath"}."\\".$recref->{"TokFile"});
|
|
my $hotfix = \%HOTFIX;
|
|
foreach my $fix (keys(%HOTFIX)){
|
|
# $DEBUG and
|
|
# print STDERR "\%HOTFIX key:", $fix, "\n";
|
|
my $depend = $hotfix->{$fix}->{"depend"};
|
|
|
|
foreach my $dep (@$depend){
|
|
|
|
if ($key eq $dep) {
|
|
# $DEBUG and
|
|
# print STDERR "found hotfix for $key\n";
|
|
$notfound = 0;
|
|
}
|
|
}
|
|
}
|
|
if ($notfound){
|
|
&Error( 2011, &RevEnvVars("$recref->{TokPath}\\$recref->{TokFile}") );
|
|
return;
|
|
}
|
|
} # if
|
|
|
|
# If no key found in REPOSITORY for this entry, then return.
|
|
# ( the token does not follow the default rule given by the TOKDIRS map;
|
|
# it might be associated with another binary via the TOKENS map )
|
|
|
|
if ( ! &IsRepositKey( $REPOSITORY, $keyref ) ) { return; }
|
|
|
|
# Verify if the binary is locked (XCopy = y) for localization.
|
|
# If locked, the binary will not be localized via bingen or rsrc and an xcopy command
|
|
# will apply instead, even though a token exists in the token tree.
|
|
|
|
$optype = &GetFieldVal( $REPOSITORY, $keyref, "OpType" );
|
|
|
|
SWITCH: {
|
|
if ( $optype eq "1" ) {
|
|
|
|
&Error( 2014, sprintf( "%s\\%s for %s\\%s",
|
|
$recref->{TokPath},
|
|
$recref->{TokFile},
|
|
&GetFieldVal( $REPOSITORY, $keyref, "SrcPath" ),
|
|
&GetFieldVal( $REPOSITORY, $keyref, "SrcFile" ) ) );
|
|
return;
|
|
} # if
|
|
|
|
if ( $optype eq "2" ) {
|
|
my($previous, $current)=( &GetFieldVal( $REPOSITORY, $keyref, "TokPath" ) . "\\" .
|
|
&GetFieldVal( $REPOSITORY, $keyref, "TokFile" ),
|
|
$recref->{TokPath} . "\\" . $recref->{TokFile});
|
|
|
|
$previous ne $current and
|
|
&Error( 2012, &RevEnvVars(sprintf( "%s\\%s and %s\\%s \<\= %s\\%s",
|
|
&GetFieldVal( $REPOSITORY, $keyref, "TokPath" ),
|
|
&GetFieldVal( $REPOSITORY, $keyref, "TokFile" ),
|
|
$recref->{TokPath},
|
|
$recref->{TokFile},
|
|
&GetFieldVal( $REPOSITORY, $keyref, "SrcPath" ),
|
|
&GetFieldVal( $REPOSITORY, $keyref, "SrcFile" ))));
|
|
|
|
return;
|
|
} # if
|
|
|
|
if ( $optype eq "-" ) {
|
|
# Set the token-related fields for the binary given by the key
|
|
map {&SetField( $REPOSITORY, $keyref, $_, $recref->{$_} )}
|
|
qw(TokPath TokFile ITokPath BgnSw LocOp CustRc);
|
|
&SetField( $REPOSITORY, $keyref, "OpType", "2" );
|
|
last SWITCH;
|
|
} # if
|
|
|
|
# default
|
|
&FatalError( 3011, sprintf( "$recref->{DestPath}\\$recref->{DestFile} %s",
|
|
&GetFieldVal( $REPOSITORY, $keyref, "OpType") ) );
|
|
|
|
} # SWITCH
|
|
|
|
return;
|
|
|
|
} # FillTokInfo
|
|
|
|
# ExistsBinForTok
|
|
# Looks in Repository for an entry corresponding to a given token
|
|
# Input
|
|
# a token record ( reference )
|
|
# a Repository key ( reference )
|
|
# Output
|
|
# 1 - entry found ( token info might be already completed )
|
|
# 0 - otherwise
|
|
# Example
|
|
# if ( &ExistsBinForTok( $recref ) ) { ... }
|
|
|
|
sub ExistsBinForTok {
|
|
|
|
my( $keyref, $recref ) = @_;
|
|
|
|
# Copy key
|
|
|
|
my $tmpkey = {};
|
|
|
|
return 1 if &IsRepositKey( $REPOSITORY, $keyref );
|
|
|
|
$tmpkey->{DestFile} = $keyref->{DestFile};
|
|
|
|
foreach ( keys %$REPOSITORY ) {
|
|
|
|
$tmpkey->{DestPath} = $_;
|
|
|
|
next unless &IsRepositKey( $REPOSITORY, $tmpkey );
|
|
|
|
return 1 if ((&GetFieldVal( $REPOSITORY, $tmpkey, "TokFile" ) eq $recref->{TokFile}) and
|
|
(&GetFieldVal( $REPOSITORY, $tmpkey, "TokPath" ) eq $recref->{TokPath}));
|
|
|
|
} # for
|
|
|
|
return 0;
|
|
|
|
} # ExistsBinForTok
|
|
|
|
# SetField
|
|
# Updates a key (field) of a given REPOSITORY entry with a given value.
|
|
# Input
|
|
# map (reference)
|
|
# key value (reference)
|
|
# field name
|
|
# field value
|
|
# Output
|
|
# none
|
|
# Usage
|
|
# &SetField( \%key, "TokPath", $TokPath );
|
|
|
|
sub SetField {
|
|
|
|
$_[0]->{lc$_[1]->{"DestPath"}}->{lc$_[1]->{"DestFile"}}->{$_[2]} = $_[3] if ($_[3] ne '-');
|
|
|
|
} # SetField
|
|
|
|
# PushFieldVal
|
|
# Add one element to a field storing an array ({Cmd} field for REPOSITORY)
|
|
# Input
|
|
# map (reference)
|
|
# key value (reference)
|
|
# field name
|
|
# field value
|
|
# Output
|
|
# none
|
|
# Usage
|
|
# &PushFieldVal();
|
|
|
|
sub PushFieldVal {
|
|
|
|
# my( $mapref, $keyref, $fname, $fval ) = @_;
|
|
|
|
# The key referred by keyref is modified,
|
|
# but this saves a lot of execution time
|
|
# so we prefer not to work with a copy of the key
|
|
|
|
push @{$_[0]->{lc$_[1]->{DestPath}}->{lc$_[1]->{DestFile}}->{$_[2]}}, $_[3] if ($_[3] ne '-');
|
|
|
|
} # PushFieldVal
|
|
|
|
# GetFieldVal
|
|
# Return the value of a particular key of a given REPOSITORY entry.
|
|
# Input
|
|
# map (reference)
|
|
# key value (reference)
|
|
# name of the searched field
|
|
# Output
|
|
# the value of the searched field
|
|
# Usage
|
|
# $srcpath = &GetFieldVal( $REPOSITORY, \%key, "SrcPath" );
|
|
|
|
|
|
sub GetFieldVal {
|
|
defined($_[0]->{lc($_[1]->{"DestPath"})}->{lc($_[1]->{"DestFile"})}->{$_[2]})?
|
|
$_[0]->{lc($_[1]->{"DestPath"})}->{lc($_[1]->{"DestFile"})}->{$_[2]}
|
|
: "-";
|
|
} # GetFieldVal
|
|
|
|
# GenNmakeFiles
|
|
# For each entry found in the REPOSITORY, generate the NMAKE description blocks
|
|
# and write them to a makefile. Also generate a nmake command file with command-line
|
|
# parameters for nmake.
|
|
# Input
|
|
# makefile name, syscmd file name
|
|
# Output
|
|
# none
|
|
# Usage
|
|
# &GenNmakeFiles($MAKEFILE, $SYSCMD);
|
|
|
|
sub GenNmakeFiles {
|
|
|
|
my $MAKEFILE = shift;
|
|
my $SYSCMD = shift;
|
|
|
|
&SysgenLogMsg("Generating $MAKEFILE...", 1);
|
|
|
|
# Update CMD_REPOSITORY for TARGETS with updated data from REPOSITORY
|
|
&UpdTargetEntries();
|
|
|
|
|
|
# Identify all targets with description block changed
|
|
# and keep them in SYSCMD
|
|
&FillSyscmd();
|
|
|
|
&GenMakeDir();
|
|
|
|
# FIX The REPOSITORY changes described in hotfix
|
|
&FixRepository();
|
|
|
|
# Handle the copy operation via compdir
|
|
if ($ACCELERATE and $CLEAN){
|
|
&FilterOpType($REPOSITORY, ["1","2"]);
|
|
|
|
my $COMPDIR_SCRIPTS_BASE = &GetMacro("COMPDIR_SCRIPTS_BASE");
|
|
|
|
&SysgenLogMsg("Writing compdir listings in $COMPDIR_SCRIPTS_BASE",0);
|
|
-d $COMPDIR_SCRIPTS_BASE or qx("md $COMPDIR_SCRIPTS_BASE");
|
|
|
|
|
|
my $GENERATE_SCRIPTS_BASE = &GetMacro("GENERATE_SCRIPTS_BASE");
|
|
if ($GENERATE_SCRIPTS_BASE){
|
|
&SysgenLogMsg("Creating $GENERATE_SCRIPTS_BASE",0);
|
|
-d $GENERATE_SCRIPTS_BASE or qx("md $GENERATE_SCRIPTS_BASE");
|
|
}
|
|
|
|
&SysgenLogMsg("Cleaning $COMPDIR_SCRIPTS_BASE",0);
|
|
qx("del /q $COMPDIR_SCRIPTS_BASE\\*");
|
|
|
|
$COMPDIR_FILE_TEMPLATE = &SwitchWorkDir($COMPDIR_FILE_TEMPLATE, $COMPDIR_SCRIPTS_BASE);
|
|
# cannot do it too early.
|
|
|
|
&WriteCompdir($COMPDIR_FILE_TEMPLATE, \%COMPDIR);
|
|
}
|
|
|
|
# Write the makefile
|
|
&WriteToMakefile( $MAKEFILE, $SYSCMD );
|
|
|
|
# Write the syscmd file
|
|
&WriteToSyscmd($SYSCMD);
|
|
|
|
if ( -e $MAKEFILE ) {
|
|
&SysgenLogMsg("\n$MAKEFILE is ready for execution.",0);
|
|
} # if
|
|
else {
|
|
print "No MAKEFILE generated. Nothing to do.\n";
|
|
} # else
|
|
|
|
return;
|
|
|
|
} # GenNmakeFiles
|
|
|
|
# FillSyscmd
|
|
# Fills in @SYSCMD global variable with the target names having their description
|
|
# block changed. It is used for incremental calls only.
|
|
# Input & Output
|
|
# none
|
|
|
|
sub FillSyscmd {
|
|
|
|
# Repository key
|
|
my %key;
|
|
|
|
# Destinationa path, destination file
|
|
my( $destpath, $destfile );
|
|
|
|
# Nothing to do in case of:
|
|
# clean sysgen
|
|
# incremental sysgen with targets
|
|
|
|
if ( $CLEAN || !&IsEmptyHash( $TARGETS ) ) {
|
|
return;
|
|
} # if
|
|
# The call is incremental, without targets, and a previous makefile exists
|
|
|
|
# It is fine to use REPOSITORY in this case;
|
|
# the only case when we use CMD_REPOSITORY to write info to the makefile
|
|
# is an incremental sysgen with targets (when a makefile already exists).
|
|
|
|
# Compare all description blocks
|
|
for $destpath ( sort keys %$REPOSITORY ) {
|
|
for $destfile ( sort keys %{$REPOSITORY->{$destpath}} ) {
|
|
|
|
$key{DestPath} = $destpath;
|
|
$key{DestFile} = $destfile;
|
|
|
|
# Store in SYSCMD the keys with changed description blocks
|
|
if ( &CmdCompare( \%key ) ) {
|
|
push @SYSCMD, "$destpath\\$destfile";
|
|
} # if
|
|
|
|
} # for
|
|
} # for
|
|
|
|
return;
|
|
|
|
} # FillSyscmd
|
|
|
|
sub MakeRevEnvVars{
|
|
|
|
my @vars = qw(_NTPOSTBLD
|
|
_NTLOCDIR
|
|
_NTTREE
|
|
TEMP
|
|
RAZZLETOOLPATH
|
|
_NTBINDIR
|
|
);
|
|
|
|
&SetMacro("LANG", &GetMacro("LANGUAGE"));
|
|
# do not remove this line! the design of the aggregation
|
|
# allows for re-use of the LANG environment variable.
|
|
# The reaslization is not perfect though:
|
|
# the variables below are all dedicated to language/language transmutation:
|
|
# SRC_LANG LANG LANGUAGE ALT_LANG
|
|
my $SRC_LANG = uc(&GetMacro("LANGUAGE"));
|
|
my $LANG = uc(&GetMacro("ALT_LANG"));
|
|
my $_NTDRIVE = uc(&GetMacro("_NTDRIVE"));
|
|
|
|
&SetMacro("_NTLOCDIR", &GetMacro("_NTBINDIR")."\\LOC\\RES\\".&GetMacro("LANGUAGE"));
|
|
# Is is important here to allow _NTLOCDIR get overwritten!
|
|
# print STDERR "overwriting the \$\(_NTLOCDIR\):" , &GetMacro("_NTLOCDIR"), "\n";
|
|
|
|
%RevEnvVars = ();
|
|
@RevEnvVars = ();
|
|
|
|
%RevEnvVars = map {uc(&GetMacro($_)) => uc("\$\($_\)")} @vars;
|
|
push @RevEnvVars, map {uc(&GetMacro($_))} @vars;
|
|
|
|
if ($LANG ne "" and ($SRC_LANG ne $LANG) ){
|
|
|
|
$RevEnvVars{$LANG."\\"} = "\$\(LANG\)\\";
|
|
push @RevEnvVars, $LANG."\\";
|
|
|
|
$RevEnvVars{".$LANG"} = ".\$\(LANG\)";
|
|
push @RevEnvVars, ".$LANG";
|
|
|
|
$RevEnvVars{"\\$LANG"} = "\\\$\(LANG\)";
|
|
push @RevEnvVars, "\\$LANG";
|
|
|
|
$RevEnvVars{$SRC_LANG."\\"} = "\$\(SRC_LANG\)\\";
|
|
push @RevEnvVars, $SRC_LANG."\\";
|
|
|
|
$RevEnvVars{".$SRC_LANG"} = ".\$\(SRC_LANG\)";
|
|
push @RevEnvVars, ".$SRC_LANG";
|
|
|
|
$RevEnvVars{"\\$SRC_LANG"} = "\\\$\(SRC_LANG\)";
|
|
push @RevEnvVars, "\\$SRC_LANG";
|
|
|
|
} else {
|
|
|
|
$RevEnvVars{$SRC_LANG."\\"} = "\$\(LANG\)\\";
|
|
push @RevEnvVars, $SRC_LANG."\\";
|
|
|
|
$RevEnvVars{".$SRC_LANG"} = ".\$\(LANG\)";
|
|
push @RevEnvVars, ".$SRC_LANG";
|
|
|
|
$RevEnvVars{"\\$SRC_LANG"} = "\\\$\(LANG\)";
|
|
push @RevEnvVars, "\\$SRC_LANG";
|
|
|
|
|
|
}
|
|
|
|
|
|
$RevEnvVars{$_NTDRIVE."\\"} = "\$\(_NTDRIVE\)\\";
|
|
push @RevEnvVars, $_NTDRIVE."\\";
|
|
|
|
$RevEnvVars {uc(&GetMacro("DST"))} = "\$\(_NTPOSTBLD\)"
|
|
if &GetMacro("DST");
|
|
|
|
|
|
$DEBUG and
|
|
print STDERR
|
|
join("\n", "ENV:", map {$RevEnvVars{$_}." => ".$_} @RevEnvVars), "\n\n\n\n";
|
|
|
|
# exit;
|
|
|
|
}
|
|
sub RevEnvVars{
|
|
|
|
my $expr = shift;
|
|
if ($expr =~ /\S/){
|
|
map {$expr =~ s/\Q$_\E/$RevEnvVars{$_}/gim} @RevEnvVars;
|
|
# Here we enforce the order in replacing the revenvvars with their labels
|
|
# in order to get rid of the expressions like
|
|
# $(_NTBINRIR).binaries.x86fre
|
|
}
|
|
$expr;
|
|
}
|
|
|
|
|
|
|
|
sub WriteSettings{
|
|
|
|
|
|
my $LANGUAGE = uc(&GetMacro("LANGUAGE"));
|
|
my $ALT_LANG = uc(&GetMacro("ALT_LANG"));
|
|
|
|
my $LANG = "\$\(LANG\)";
|
|
|
|
if ($LANGUAGE ne $ALT_LANG){
|
|
$LANG = uc(&GetMacro("LANG"));
|
|
$LANGUAGE = $ALT_LANG;
|
|
|
|
}
|
|
|
|
my $language = $LANGUAGE;
|
|
|
|
$language =~ s-(\w+)-\U$1\E-;
|
|
$LANGUAGE =~ s-(\w+)-\L$1\E-;
|
|
|
|
my $COMPDIR = &GetMacro("COMPDIR");
|
|
my $COPY = &GetMacro("COPY");
|
|
my $BINGEN = &GetMacro("BINGEN");
|
|
my $REATTR = &GetMacro("REATTR");
|
|
my $_BUILDARCH = &GetMacro("_BUILDARCH");
|
|
my $LSBUILD = &GetMacro("LSBUILD");
|
|
|
|
print "# $SELF\n";
|
|
print <<EOE;
|
|
|
|
#
|
|
!IFNDEF LANG
|
|
! ERROR You must define macro LANG
|
|
!ENDIF
|
|
|
|
!IFNDEF _NTTREE
|
|
! ERROR You must run aggregation in the NT razzle
|
|
!ENDIF
|
|
|
|
!IF "\$(LANG)" != "$LANGUAGE" \&\& "\$(LANG)" != "$language"
|
|
! ERROR This file is for $LANGUAGE
|
|
!ENDIF
|
|
|
|
!IF "\$(_BUILDARCH)" != "$_BUILDARCH"
|
|
! ERROR This file is for $_BUILDARCH
|
|
!ENDIF
|
|
|
|
# Directory name aliases
|
|
_NTLOCDIR=\$\(\_NTBINDIR\)\\LOC\\RES\\${LANG}
|
|
|
|
# Binary file operation aliases
|
|
COMPDIR=$COMPDIR
|
|
BINGEN=$BINGEN
|
|
COPY=$COPY
|
|
REATTR=$REATTR
|
|
LSBUILD=$LSBUILD
|
|
EOE
|
|
|
|
|
|
my @LANGCODES = qw(Site
|
|
Read1st
|
|
Comments
|
|
Readme
|
|
ACP
|
|
LCID
|
|
Home
|
|
PriLangID
|
|
Flavor
|
|
PerfID
|
|
GUID
|
|
SubLangID
|
|
Class
|
|
LANG
|
|
LangISO
|
|
RSRC_LCID
|
|
);
|
|
map {print "$_=". &GetMacro($_), "\n";} @LANGCODES;
|
|
|
|
print "LANG=". &GetMacro("ALT_LANG"). "\n\n";
|
|
if (&GetMacro("LANGUAGE") ne &GetMacro("ALT_LANG")){
|
|
my $SRC_LANG = uc(&GetMacro("LANGUAGE"));
|
|
print "SRC_LANG=$SRC_LANG \n\n";
|
|
}
|
|
|
|
|
|
$DEBUG or
|
|
print <<SOE;
|
|
.SILENT:
|
|
|
|
SOE
|
|
|
|
}
|
|
|
|
|
|
# WriteToMakefile
|
|
# Generate the makefile
|
|
# Input
|
|
# makefile name, syscmd file name
|
|
# Output
|
|
# none
|
|
|
|
sub WriteToMakefile {
|
|
|
|
my( $makefname, $sysfname ) = @_;
|
|
|
|
# Open the makefile
|
|
( open MAKEFILE, ">"."$makefname" ) || &FatalError( 1007, $makefname );
|
|
select( MAKEFILE );
|
|
|
|
# Write "sysgen" target
|
|
&WriteSysgenTarget($sysfname );
|
|
|
|
&WriteSettings();
|
|
|
|
# Write "all" target
|
|
&WriteAllTarget();
|
|
|
|
$ACCELERATE and $CLEAN and &WriteCompdirTarget();
|
|
|
|
# Write file-by-file description blocks
|
|
&WriteFileCmds();
|
|
|
|
close(MAKEFILE);
|
|
select( STDOUT );
|
|
|
|
return;
|
|
} # WriteToMakefile
|
|
|
|
|
|
|
|
# WriteSysgenTarget
|
|
# Write "sysgen" target in the generated makefile
|
|
# It invokes nmake recursively.
|
|
# Input
|
|
# filename (including path) of the generated syscmd file
|
|
# Output
|
|
# none
|
|
# Usage
|
|
# &WriteSysgenTarget($SYSCMD);
|
|
|
|
sub WriteSysgenTarget {
|
|
|
|
my($cmdfile) = @_;
|
|
|
|
printf "sysgen:\n";
|
|
|
|
# Call nmake @syscmd in the following cases:
|
|
# sysgen with targets (clean or incremental)
|
|
# incremental sysgen without targets, but with changes in the description blocks
|
|
|
|
if ( !&IsEmptyHash( $TARGETS ) || ( !$CLEAN && ( @SYSCMD > 0 ) ) ) {
|
|
printf "\t\@nmake /K /NOLOGO /F $MAKEFILE \@$cmdfile \n";
|
|
} # if
|
|
|
|
# Call nmake all in the following cases:
|
|
# sysgen without targets (clean or incremental)
|
|
|
|
if ( &IsEmptyHash( $TARGETS ) ) {
|
|
if ( $CLEAN ) {
|
|
printf "\t\@nmake /A /K /F $MAKEFILE /NOLOGO all\n";
|
|
} # if
|
|
else {
|
|
printf "\t\@nmake /K /F $MAKEFILE /NOLOGO all\n";
|
|
} # else
|
|
} # if
|
|
|
|
printf "\n";
|
|
|
|
|
|
return;
|
|
|
|
} # WriteSysgenTarget
|
|
|
|
# WriteAllTarget
|
|
# Writes "all"'s target dependency line in the makefile.
|
|
# Input & Output
|
|
# none
|
|
# Usage
|
|
# &WriteAllTarget();
|
|
|
|
sub WriteAllTarget {
|
|
|
|
# Table (reference): REPOSITORY or CMD_REPOSITORY
|
|
my $mapref;
|
|
|
|
# Destination path, destination file
|
|
my( $destpath, $destfile );
|
|
|
|
my $chars;
|
|
|
|
my $steps;
|
|
|
|
my @alltargets;
|
|
|
|
my $i;
|
|
|
|
my $j;
|
|
|
|
my $k;
|
|
|
|
my $total=0;
|
|
|
|
# Use data from CMD_REPOSITORY or REPOSITORY,
|
|
# depending if SYSGEN is called or not incrementally,
|
|
# with or without targets.
|
|
|
|
$mapref = $CMD_REPOSITORY;
|
|
if ( $CLEAN || &IsEmptyHash( $TARGETS ) ) {
|
|
$mapref = $REPOSITORY;
|
|
} # if
|
|
|
|
# MAKEFILE file handler is the default.
|
|
print "all:\ \\\n\t" ;
|
|
|
|
$i = 0;
|
|
|
|
@alltargets = map({$total+=scalar(keys %{$mapref->{$_}});$_;} sort keys %{$mapref});
|
|
|
|
if ($alltargets[$i]=~/_DIRS/i) {
|
|
print "_DIRS ";
|
|
$ACCELERATE and print "_COMPDIR ";
|
|
$i++;
|
|
}
|
|
|
|
$SECTIONS = $DEFAULT_SECTIONS if ($SECTIONS!~/\d+/);
|
|
|
|
$SECTIONS = 1 if ($#alltargets < $SECTIONS);
|
|
|
|
$chars=length($SECTIONS);
|
|
$steps=$total / $SECTIONS;
|
|
|
|
for ($j = 1; $j <= $SECTIONS; $j++) {
|
|
printf("${SECTIONNAME}\%0${chars}d\%0${chars}d ", $SECTIONS, $j);
|
|
}
|
|
|
|
for ($j = 0, $k = 0;$i <= $#alltargets; $i++) {
|
|
|
|
for $destfile ( sort keys %{$mapref->{$alltargets[$i]}} ) {
|
|
|
|
if ($j <= $k++) {
|
|
|
|
$j += $steps;
|
|
printf("\n\n${SECTIONNAME}\%0${chars}d\%0${chars}d: ", $SECTIONS, $j / $steps);
|
|
|
|
}
|
|
|
|
my $quot_ = $destfile =~ /\s/ ? "\"": undef;
|
|
print "\ \\\n\t${quot_}".
|
|
&RevEnvVars($alltargets[$i]."\\".$destfile).
|
|
"${quot_}";
|
|
|
|
} # for
|
|
|
|
} # for
|
|
|
|
print " \n\n";
|
|
|
|
return;
|
|
|
|
} # WriteAllTarget
|
|
|
|
|
|
# write %COMPDIR_COMMAND
|
|
# lines
|
|
# usage: &WriteCompdirTarget()
|
|
|
|
sub WriteCompdirTarget{
|
|
|
|
my @TargetList = sort keys(%COMPDIR_COMMAND);
|
|
|
|
print &RevEnvVars(join("\ \\\n", "_COMPDIR:", map {"\t$_"} @TargetList)),
|
|
"\n",
|
|
join("\n", map {"\tlogerr \"".&RevEnvVars($COMPDIR_COMMAND{$_})."\""} @TargetList),
|
|
"\n",
|
|
&RevEnvVars(join("\n", map {"\t$REATTR_COMMAND{$_}"} keys(%REATTR_COMMAND))),
|
|
"\n\n";
|
|
|
|
}
|
|
|
|
|
|
sub WriteCompdir{
|
|
|
|
my $listFileTemplate = shift;
|
|
my $mapref = shift;
|
|
local $\ = "\n";
|
|
# my $DEBUG = 1;
|
|
my $cnt = 0;
|
|
my $listfile;
|
|
my $compdir_command;
|
|
my $srcfile;
|
|
my $key = {"DestPath" => undef,
|
|
"DestFile" => undef};
|
|
|
|
$listFileTemplate =~ s|\.(\w+)$|.XXX.$1| unless $listFileTemplate =~ /XXX\.\w+$/;
|
|
|
|
foreach my $srcpath ( sort keys %{$mapref} ) {
|
|
$cnt++;
|
|
# $DEBUG and print STDERR $cnt ."\=\>". $srcpath."\n";
|
|
$listfile = $listFileTemplate;
|
|
|
|
$listfile =~ s/XXX/sprintf("%03d", $cnt)/ie;
|
|
# add unique number
|
|
# before the extension.
|
|
#
|
|
open LISTFILE, ">".$listfile;
|
|
select LISTFILE;
|
|
foreach my $file ( sort keys %{$mapref->{$srcpath}} ) {
|
|
$srcfile = $file;
|
|
# $DEBUG and
|
|
# print STDERR $srcfile;
|
|
print $srcfile;
|
|
}
|
|
close LISTFILE;
|
|
|
|
$key->{"DestPath"} = $srcpath;
|
|
$key->{"DestFile"} = $srcfile;
|
|
|
|
my $cmdref= &GetFieldVal( $mapref, $key, "Cmd" );
|
|
$compdir_command = "\$\(COMPDIR\) /m:$listfile @$cmdref",
|
|
$COMPDIR_COMMAND{$listfile} = $compdir_command;
|
|
$REATTR_COMMAND{$listfile} = join(" ", "\$\(REATTR\)", $cmdref->[1], $listfile)
|
|
if &GetFieldVal( $mapref,$key, "Attribute");
|
|
########
|
|
## $COMPDIR_COMMAND{$listfile} .= "\"\n\t\$\(REATTR\) ". $cmdref->[1] . " \"$listfile"
|
|
## if &GetFieldVal( $mapref,$key, "Attribute");
|
|
#
|
|
# $DEBUG or next;
|
|
# select STDERR;
|
|
# print "$compdir_command\n";
|
|
}
|
|
|
|
1;
|
|
}
|
|
|
|
#
|
|
# FilterOpType ([1,2])
|
|
# arguments: currently unused.
|
|
#
|
|
sub FilterOpType{
|
|
|
|
my $mapref = shift;
|
|
my $knownOpType = shift;
|
|
my %knownOpType = map {$_=>$_} @$knownOpType;
|
|
$knownOpType = \%knownOpType;
|
|
my %key = ();
|
|
|
|
foreach my $destpath (keys %{$mapref} ) {
|
|
|
|
foreach my $destfile ( keys %{$mapref->{$destpath}}) {
|
|
my $docopy = 0;
|
|
my $cmdref = $mapref->{$destpath}->{$destfile}->{Cmd};
|
|
for ( my $i=1; $i < @{$cmdref}; $i++ ) {
|
|
$docopy = 1 unless $cmdref->[$i] !~ /\S/;
|
|
# $DEBUG and print STDERR "\"".$cmdref->[$i]."\"\n" if $docopy;
|
|
}
|
|
$key{DestPath} = $destpath;
|
|
$key{DestFile} = $destfile;
|
|
$docopy
|
|
or &DeleteKey( $mapref, \%key );
|
|
# $docopy
|
|
# of
|
|
# &SetField( $REPOSITORY, $keyref, "OpType", "-1" );
|
|
}
|
|
}
|
|
}
|
|
|
|
# WriteFileCmds
|
|
# For every file, write its dependency block in the makefile.
|
|
# Input & Output
|
|
# none
|
|
# Usage
|
|
# &WriteFileCmds();
|
|
|
|
sub WriteFileCmds {
|
|
|
|
# Table (reference): REPOSITORY or CMD_REPOSITORY
|
|
my $mapref;
|
|
|
|
# Counter
|
|
my $i;
|
|
|
|
|
|
# Reference to the Cmd field
|
|
my $cmdref;
|
|
|
|
# Destinationa path, destination file
|
|
my( $destpath, $destfile );
|
|
|
|
# Use data from CMD_REPOSITORY or REPOSITORY,
|
|
# depending if SYSGEN is called or not incrementally,
|
|
# with or without targets.
|
|
|
|
$mapref = $CMD_REPOSITORY;
|
|
if ( $CLEAN || &IsEmptyHash( $TARGETS ) ) {
|
|
$mapref = $REPOSITORY;
|
|
} # if
|
|
|
|
# Write file-by-file description blocks
|
|
|
|
for $destpath ( sort keys %{$mapref} ) {
|
|
for $destfile ( sort keys %{$mapref->{$destpath}} ) {
|
|
# Print {Cmd} field
|
|
print
|
|
grep {/\t?\S/} @{$mapref->{$destpath}->{$destfile}->{Cmd}};
|
|
print "\n";
|
|
} # for
|
|
} # for
|
|
|
|
return;
|
|
} # WriteFileCmds
|
|
|
|
# CmdCompare
|
|
# Given a key, compare the {Cmd} field from REPOSITORY to the {Cmd} CMD_REPOSITORY field.
|
|
# Input
|
|
# repository type key (reference)
|
|
# Output
|
|
# 0 if commands compare OK
|
|
# 1 if commands are different
|
|
# Usage
|
|
# $is_different = &CmdCompare( \%key );
|
|
|
|
sub CmdCompare {
|
|
|
|
my( $keyref ) = @_;
|
|
|
|
# Cmd fields
|
|
my( $repref, $cmdref );
|
|
|
|
$repref = &GetFieldVal( $REPOSITORY, $keyref, "Cmd" );
|
|
$cmdref = &GetFieldVal( $CMD_REPOSITORY, $keyref, "Cmd" );
|
|
lc("@$repref") cmp lc("@$cmdref");
|
|
|
|
|
|
} # CmdCompare
|
|
|
|
# RecordToCmd
|
|
# Converts one entry from REPOSITORY to a set of cmd instructions,
|
|
# stored in the REPOSITORY as well
|
|
# Input
|
|
# key identifying the REPOSITORY entry (reference)
|
|
# Output
|
|
# none
|
|
# Usage
|
|
# &RecordToCmd( $keyref );
|
|
|
|
sub RecordToCmd {
|
|
|
|
#Input
|
|
my $keyref = shift;
|
|
my $key = &GetFieldVal( $REPOSITORY, $keyref, "OpType" );
|
|
|
|
my $status = (defined( $LOCOP_BASED->{"$key"})) ?
|
|
$LOCOP_BASED->{"$key"}->($keyref) :
|
|
&FatalError(3011,
|
|
sprintf "$keyref->{DestPath}\\$keyref->{DestFile} %s",
|
|
&GetFieldVal( $REPOSITORY, $keyref, "OpType") );
|
|
|
|
# (for ex. because DeleteKey was called by GenBgnCmd)
|
|
if ( ! &IsRepositKey( $REPOSITORY, $keyref ) ) {
|
|
return;
|
|
} # if
|
|
|
|
if ( ! &GetMacro( "ntdebug" ) && # only retail builds
|
|
&GetMacro( "GENERATE_MUI_DLLS" ) # and only when requested
|
|
) {
|
|
&GenMUICmd( $keyref );
|
|
} # if
|
|
|
|
} # RecordToCmd
|
|
|
|
|
|
# GetDynGata
|
|
# get all REPOSITORY fiels in one call instead of calling for each one.
|
|
#
|
|
sub GetDynData {
|
|
|
|
my $repository = shift;
|
|
my $key = shift;
|
|
my $dyndata = $repository->{lc($key->{"DestPath"})}->{lc($key->{"DestFile"})};
|
|
map {$dyndata->{$_} = "-" unless defined($dyndata->{$_})}
|
|
@REPOSITORY_TEMPLATE;
|
|
# cannot use "keys (%$dyndata);"!
|
|
|
|
$dyndata;
|
|
}
|
|
|
|
# GenXcopy
|
|
# For the given entry from REPOSITORY, generates the xcopy commands.
|
|
# Input
|
|
# key identifying the REPOSITORY entry
|
|
# Output
|
|
# none
|
|
# Usage
|
|
# &GetXcopy( $keyref );
|
|
|
|
|
|
sub GenXcopy {
|
|
|
|
return if $SYNTAX_CHECK_ONLY;
|
|
|
|
my( $keyref ) = @_;
|
|
|
|
# dbgline, pdbline, file line, dependency line
|
|
my( $dbgline, $pdbline, $symline, $fline, $dline );
|
|
|
|
# Paths and filenames for the symbols
|
|
my $dyndata = &GetDynData($REPOSITORY, $keyref);
|
|
my ($srcext, $srcpdb, $srcdbg, $srcsym, $dstext, $dstpdb, $dstdbg, $dstsym) =
|
|
&NewImageToSymbol($dyndata->{"SrcFile"} ,
|
|
$dyndata->{"DestFile"},
|
|
$dyndata->{"SrcPath"} ,
|
|
$dyndata->{"SrcDbg" } );
|
|
|
|
my( $srcsymfull, $dstsymfull ) = &SetSymPaths(
|
|
$dyndata->{"SrcDbg"} ,
|
|
$srcext,
|
|
$srcpdb,
|
|
$srcdbg,
|
|
$srcsym,
|
|
$dyndata->{"DestDbg"},
|
|
$dstext );
|
|
|
|
# Dependency line
|
|
|
|
$dline = $DLINE;
|
|
my $quot_ = $dyndata->{"SrcFile"} =~ /\s/ ? "\"" : undef;
|
|
my $qquot_ = "\\\"" if $quot_;
|
|
|
|
$dline =~ s/TARGET\b/${quot_}$dyndata->{"DestPath"}\\$dyndata->{"DestFile"}${quot_}/;
|
|
$dline =~ s/DEPEND/${quot_}$dyndata->{"SrcPath"}\\$dyndata->{"SrcFile"}${quot_}/;
|
|
# $dline =~ s/$qquot_$quot_/$qquot_/g;
|
|
|
|
$dline = &RevEnvVars($dline);
|
|
|
|
# Generate the copy commands for the symbols files (dbg, pdb, and sym)
|
|
$pdbline = &MakeXcopyCmd( $dyndata->{"SrcFile"},
|
|
$srcsymfull,
|
|
$srcpdb,
|
|
$dstsymfull,
|
|
$dstpdb , undef, 1 );
|
|
|
|
$dbgline = &MakeXcopyCmd( $dyndata->{"SrcFile"},
|
|
$srcsymfull,
|
|
$srcdbg,
|
|
$dstsymfull,
|
|
$dstdbg , undef, 1 );
|
|
$symline = &MakeXcopyCmd( $dyndata->{"SrcFile"},
|
|
$srcsymfull,
|
|
$srcsym,
|
|
$dstsymfull,
|
|
$dstsym , undef, 1 );
|
|
|
|
# Generate binary's xcopy command
|
|
|
|
$fline = &MakeXcopyCmd( $dyndata->{"SrcFile" } ,
|
|
$dyndata->{"SrcPath" } ,
|
|
$dyndata->{"SrcFile" } ,
|
|
$dyndata->{"DestPath"} ,
|
|
$dyndata->{"DestFile"} ,
|
|
$qquot_ );
|
|
|
|
|
|
# Write the dependency line
|
|
$DEBUG and print STDERR $dline."\n" if $quot_;
|
|
&PushFieldVal( $REPOSITORY, $keyref, "Cmd", "$dline\n" );
|
|
|
|
$MAKEDIR{$dyndata->{"DestPath"}} = 1;
|
|
|
|
&PushFieldVal( $REPOSITORY, $keyref, "Cmd", "\t$fline\n" );
|
|
if ( $dbgline || $pdbline || $symline ) {
|
|
if ( $dstsymfull ne $dyndata->{"DestDbg"}) {
|
|
$MAKEDIR{$dstsymfull} = 1;
|
|
} # if
|
|
} # if
|
|
if ( $dbgline ) {
|
|
&PushFieldVal( $REPOSITORY, $keyref, "Cmd", "\t$dbgline\n" );
|
|
} # if
|
|
if ( $pdbline ) {
|
|
&PushFieldVal( $REPOSITORY, $keyref, "Cmd", "\t$pdbline\n" );
|
|
} # if
|
|
if ( $symline ) {
|
|
&PushFieldVal( $REPOSITORY, $keyref, "Cmd", "\t$symline\n" );
|
|
} # if
|
|
|
|
return;
|
|
|
|
} # GenXcopy
|
|
|
|
|
|
# SetSymPaths
|
|
# Set the source and destination paths for the symbols
|
|
# Can be the same as set in the sysfile (BBT case)
|
|
# or can be the subdir with the the filename externsion.
|
|
# The decision is made based on the pdb file existence.
|
|
# Input
|
|
# SrcDbg field value
|
|
# source file extension
|
|
# source pdb file
|
|
# source dbg file
|
|
# source sym file
|
|
# DestDbg field value
|
|
# destination file extension
|
|
# Output
|
|
# path to the source symbol file
|
|
# path to the destination symbols file
|
|
|
|
sub SetSymPaths {
|
|
|
|
my( $srcpath, $srcext, $srcpdb, $srcdbg, $srcsym, $dstpath, $dstext ) = @_;
|
|
return ( $srcpath, $dstpath ) unless $SETSYMPATHS;
|
|
|
|
# Output
|
|
my( $srcsymfull, $dstsymfull) = ( $srcpath, $dstpath );
|
|
|
|
# Verify if the file exists in extension subdir
|
|
if ( $srcpath ne "-" &&
|
|
$dstpath ne "-" &&
|
|
( -e "$srcpath\\$srcext\\$srcpdb" || -e "$srcpath\\$srcext\\$srcdbg" || -e "$srcpath\\$srcext\\$srcsym" ) ) {
|
|
$srcsymfull .= "\\$srcext";
|
|
$dstsymfull .= "\\$dstext";
|
|
}
|
|
|
|
return ( $srcsymfull, $dstsymfull );
|
|
|
|
} # SetSymPaths
|
|
|
|
# MakeXcopyCmd
|
|
# Generates an xcopy command for copying a given file.
|
|
# Input
|
|
# source path
|
|
# source file
|
|
# dest path
|
|
# dest file
|
|
# Output
|
|
# xcopy command
|
|
# Usage
|
|
# $xcopy = &MakeXcopyCmd( "f:\\nt\\usa\\\binaries", "advapi.dll",
|
|
# "f:\\nt\\jpn\\relbins", "advapi.dll" );
|
|
|
|
sub MakeXcopyCmd {
|
|
|
|
my( $binary, $srcpath, $srcfile, $dstpath, $dstfile, $qquot_, $forceDoPlainCopy ) = @_;
|
|
# last argument : optional
|
|
|
|
# print STDERR $binary , "\n" if $forceDoPlainCopy;
|
|
my $result;
|
|
# for $CLEAN build, use COMPDIR insdead of copy when possible
|
|
# if $dstfile identical to $srcfile,
|
|
# compare $dstpath with $srcpath, the relative ones.
|
|
# use
|
|
# %MAP_COMPDIR_EXT
|
|
# to apply compdir.exe without doing more checks.
|
|
|
|
|
|
|
|
my $APPLY_COMPDIR = 0;
|
|
my $IS_PLAIN_COPY = 0;
|
|
|
|
$forceDoPlainCopy = 1 if $srcfile =~ /\s/;
|
|
|
|
if ($ACCELERATE and
|
|
$CLEAN and !$forceDoPlainCopy){
|
|
|
|
my $chkext=$srcfile;
|
|
$chkext=~ s/.*\.(\w+)$/.\L$1\E/g;
|
|
|
|
# print STDERR $srcfile, "\n" and
|
|
$APPLY_COMPDIR = 1 if defined($MAP_COMPDIR_EXT{$chkext});
|
|
|
|
}
|
|
|
|
if ($ACCELERATE and
|
|
$CLEAN and
|
|
!$forceDoPlainCopy and
|
|
$dstfile =~ /[^\-]/ and
|
|
$dstpath =~ /[^\-]/ ){
|
|
|
|
my $chkpath=$srcpath;
|
|
$chkpath=~ s/^$SRCBASE/$DSTBASE/eg;
|
|
# fill the %COMPDIR here.
|
|
|
|
if (lc($dstpath) eq lc($chkpath) and -e "$srcpath\\$srcfile") {
|
|
$IS_PLAIN_COPY = 1;
|
|
}
|
|
|
|
if (($APPLY_COMPDIR or $IS_PLAIN_COPY) and ($dstfile eq $srcfile)){
|
|
|
|
# Note that the the field names "DestPath" and "DestFile"
|
|
# are hard-coded in the definition of the keyref.
|
|
# The fields are used here but the information stored
|
|
# is the SrcPath+DestPath/SrcFile rather then DestPath/DestFile
|
|
|
|
my $keyref = {"DestPath"=> "$srcpath/$dstpath", "DestFile"=> $srcfile};
|
|
|
|
|
|
&SetField( \%COMPDIR, $keyref, "DestPath", "$srcpath/$dstpath" );
|
|
&SetField( \%COMPDIR, $keyref, "DestFile", $srcfile );
|
|
&SetField( \%COMPDIR, $keyref, "Attribute", !$IS_PLAIN_COPY );
|
|
&PushFieldVal( \%COMPDIR, $keyref, "Cmd", "$srcpath" );
|
|
&PushFieldVal( \%COMPDIR, $keyref, "Cmd", "$dstpath" );
|
|
|
|
# Debugging messed paths!
|
|
# Comment out when not debug to shorten the execution time.
|
|
# my $DEBUG = 1;
|
|
# my $mapref= \%COMPDIR;
|
|
# $APPLY_COMPDIR and $DEBUG and
|
|
# print STDERR
|
|
# $srcpath, "\t", $srcfile, "\t\"",
|
|
# join (" ", @{$mapref->{lc($srcpath)}->{lc($srcfile)}->{Cmd}}), "\"\n"
|
|
# my $mapref = \%COMPDIR;
|
|
# my $cmdref= &GetFieldVal( $mapref, $keyref, "Cmd" );
|
|
# print STDERR "@$cmdref\n";
|
|
&SetField( \%COMPDIR, $keyref, "OpType" , "3" );
|
|
|
|
return undef;
|
|
# remove the 'old-fashioned' rule?
|
|
}
|
|
}
|
|
|
|
if (( $dstpath eq "-") || ($srcpath eq "-" ) || !(-e "$srcpath\\$srcfile")){
|
|
$result = "";
|
|
if ($PARSESYMBOLCD && defined($default{$binary})){
|
|
my %hints = %{$default{$binary}};
|
|
my $ext = $1 if ($dstfile=~/\.(\w+)$/);
|
|
if ($ext && $hints{$ext}){
|
|
my ($sympath, $symname ) =
|
|
($hints{$ext}=~/^(.*)\\([^\\]+)$/);
|
|
|
|
my $srchead= "$(_NTTREE)";
|
|
my $dsthead= &RevEnvVars(&GetMacro("DST"));
|
|
$sympath = &RevEnvVars($srcpath);
|
|
|
|
$result =
|
|
"logerr \"\$\(COPY\) ${qquot_}$srchead\\$sympath\\$symname${qquot_} ".
|
|
"${qquot_}$dsthead\\$sympath\\${qquot_}\"";
|
|
$DEBUG and
|
|
print STDERR " added default for $binary: \"", $ext ,"\"\n";
|
|
}
|
|
}
|
|
}
|
|
else{
|
|
my $dstname = ($dstfile ne $srcfile) ? $dstfile: "";
|
|
$srcpath = &RevEnvVars($srcpath);
|
|
$dstpath = &RevEnvVars($dstpath);
|
|
$result = ($forceDoPlainCopy) ?"logerr \"\$\(COPY\) ${qquot_}$srcpath\\$srcfile${qquot_} ${qquot_}$dstpath\\$dstname${qquot_}\"" :
|
|
"logerr \"\$\(COPY\) ${qquot_}\$\*\*${qquot_} ${qquot_}\$\(\@D\)${qquot_}\"";
|
|
|
|
$result = "logerr \"\$\(COPY\) ${qquot_}\$\*\*${qquot_} ${qquot_}\$\@${qquot_}\"" if $dstfile ne $srcfile;
|
|
|
|
}
|
|
|
|
$result;
|
|
} # MakeXcopyCmd
|
|
|
|
|
|
# GenLocCmd
|
|
# For the given entry from REPOSITORY, generates the bingen commands.
|
|
# Input
|
|
# key identifying the REPOSITORY entry (reference
|
|
# array of commands where the output is added (reference)
|
|
# Output
|
|
# none
|
|
# Usage
|
|
# &GenLocCmd( $keyref );
|
|
|
|
sub GenLocCmd {
|
|
|
|
return if $SYNTAX_CHECK_ONLY;
|
|
my $keyref = shift;
|
|
my $TokFile = &GetFieldVal( $REPOSITORY, $keyref, "TokFile" );
|
|
map {$TokFile=~/$_/i && $EXT_BASED->{$_}->($keyref)} keys(%$EXT_BASED);
|
|
|
|
} # GenLocCmd
|
|
|
|
# GenBgnCmd
|
|
#
|
|
#
|
|
sub GenBgnCmd {
|
|
|
|
my( $keyref ) = @_;
|
|
|
|
# pdb line, bingen line, dependency line
|
|
my( $pdbline, $bgnline, $symline, $dbgline, $dline ) = ("", "", "", "", "");
|
|
my $symcmd = "";
|
|
|
|
# Symbol paths and filenames
|
|
my $dyndata = &GetDynData($REPOSITORY, $keyref);
|
|
|
|
my ($srcext, $srcpdb, $srcdbg, $srcsym, $dstext, $dstpdb, $dstdbg, $dstsym) =
|
|
&NewImageToSymbol($dyndata->{"SrcFile" },
|
|
$dyndata->{"DestFile"},
|
|
$dyndata->{"SrcPath" },
|
|
$dyndata->{"SrcDbg" });
|
|
|
|
|
|
my( $symsrcfull, $symdstfull ) =
|
|
&SetSymPaths( $dyndata->{"SrcDbg" },
|
|
$srcext,
|
|
$srcpdb,
|
|
$srcdbg,
|
|
$srcsym,
|
|
$dyndata->{"DestDbg" },
|
|
$dstext );
|
|
|
|
$pdbline = &MakeXcopyCmd( $dyndata->{"SrcFile"}, $symsrcfull, $srcpdb, $symdstfull, $dstpdb, undef, 1 );
|
|
$symline = &MakeXcopyCmd( $dyndata->{"SrcFile"}, $symsrcfull, $srcsym, $symdstfull, $dstsym, undef, 1 );
|
|
$dbgline = &MakeXcopyCmd( $dyndata->{"SrcFile"}, $symsrcfull, $srcdbg, $symdstfull, $dstdbg, undef, 1 );
|
|
|
|
# -a | -r | -ai | -ir switch
|
|
# localization operation type ( append or replace )
|
|
my $bgntype = &GetLocOpType( $dyndata->{"LocOp" } );
|
|
|
|
# -m switch (specific to bingen; different for rsrc)
|
|
$symcmd = &SetBgnSymSw( $symsrcfull, $srcdbg, $symdstfull, $dstdbg );
|
|
|
|
# -i switch (specific for bingen)
|
|
my $icodes = &GetBgnICodesSw( $bgntype, &GetMacro( "ILANGUAGE" ) );
|
|
|
|
# multitoken (bingen)
|
|
my $multitok = ""; # &GetBgnMultitok( $keyref, $bgntype );
|
|
|
|
# the unlocalized version of the token must exist as well
|
|
# if ( substr($bgntype,-2) eq "-a" ) {
|
|
# if ( ! -e $multitok ) {
|
|
# &Error( 2015, sprintf( "\n%s\\%s",
|
|
# $dyndata->{"TokPath" },
|
|
# $dyndata->{"TokFile" } ) );
|
|
# &DeleteKey( $REPOSITORY, $keyref );
|
|
# return;
|
|
# } # if
|
|
# } # if
|
|
|
|
|
|
|
|
# Sets the bingen command
|
|
map {$dyndata->{$_}=
|
|
&RevEnvVars($dyndata->{$_})}
|
|
qw (SrcPath DestPAth TokPath DestPath CustRc);
|
|
# here we must avoid macros when additional dependency or multiple tokens.
|
|
my $LocOp = sprintf ("%s %s %s", $BGNSWS{$dyndata->{"BgnSw" }}, $icodes, $bgntype);
|
|
|
|
$bgnline =
|
|
($multitok =~ /\S/ || $dyndata->{"CustRc" })?
|
|
$bgnline = sprintf "logerr \"\$\(BINGEN\) %s -p \$\(ACP\) -o \$\(PriLangID\) \$\(SubLangID\) %s %s\\%s %s %s\\%s \$\@\"",
|
|
$symcmd,
|
|
$LocOp,
|
|
$dyndata->{"SrcPath" },
|
|
$dyndata->{"SrcFile" },
|
|
$multitok,
|
|
$dyndata->{"TokPath" },
|
|
$dyndata->{"TokFile" }
|
|
:
|
|
GetMacro("BINGEN_COMMAND");
|
|
|
|
$bgnline =~ s/\\\$\\\(LocOp\\\)/$LocOp/g;
|
|
$bgnline =~ s/\\\$\\\((\w+)\\\)/\$($1)/g;
|
|
# nmake special macro expansion
|
|
$bgnline =~ s/\\\$\\\(([\*\@\<\>]\w)\\\)/\$($1)/gmi;
|
|
|
|
|
|
# Dependency line
|
|
$dline = sprintf "%s\\%s: %s\\%s %s\\%s %s%s\n",
|
|
$dyndata->{"DestPath" },
|
|
$dyndata->{"DestFile" },
|
|
$dyndata->{"SrcPath" },
|
|
$dyndata->{"SrcFile" },
|
|
$dyndata->{"TokPath" },
|
|
$dyndata->{"TokFile" },
|
|
$dyndata->{"CustRc" };
|
|
$multitok;
|
|
|
|
$dline = &RevEnvVars($dline);
|
|
&PushFieldVal( $REPOSITORY, $keyref, "Cmd", "$dline" );
|
|
|
|
# Dependency line done
|
|
|
|
# Description block
|
|
|
|
$MAKEDIR{$dyndata->{"DestPath" }} = 1;
|
|
|
|
if ( $multitok || $pdbline || $symline ) {
|
|
if ( $symdstfull ne $dyndata->{"DestDbg" } ) {
|
|
$MAKEDIR{$symdstfull}=1;
|
|
} # if
|
|
} # if
|
|
|
|
&PushFieldVal( $REPOSITORY, $keyref, "Cmd", sprintf( "\t$bgnline\n" ) );
|
|
if ( $pdbline ) {
|
|
&PushFieldVal( $REPOSITORY, $keyref, "Cmd", sprintf( "\t$pdbline\n" ) );
|
|
} # if
|
|
if ( $symline ) {
|
|
&PushFieldVal( $REPOSITORY, $keyref, "Cmd", sprintf( "\t$symline\n" ) );
|
|
} # if
|
|
if ( $dbgline && $symcmd!~/\W/) {
|
|
&PushFieldVal( $REPOSITORY, $keyref, "Cmd", sprintf( "\t$dbgline\n" ) );
|
|
} # if
|
|
|
|
# Optional resource-only DLL generation
|
|
|
|
# Description block done
|
|
|
|
return;
|
|
|
|
} # GenBgnCmd
|
|
|
|
|
|
|
|
sub GenLcxCmd{
|
|
|
|
return if $SYNTAX_CHECK_ONLY;
|
|
|
|
my $keyref = shift;
|
|
|
|
my $dyndata = &GetDynData($REPOSITORY, $keyref);
|
|
map {$dyndata->{$_} = &RevEnvVars($dyndata->{$_})}
|
|
qw (SrcPath DestPAth TokPath CustRc DestPath);
|
|
|
|
|
|
my $lcxline= ""; # lcx command line
|
|
my $dline = "";
|
|
|
|
my ($srcext,
|
|
$srcpdb,
|
|
$srcdbg,
|
|
$srcsym,
|
|
$dstext,
|
|
$dstpdb,
|
|
$dstdbg,
|
|
$dstsym) =
|
|
&NewImageToSymbol($dyndata->{"SrcFile" },
|
|
$dyndata->{"DestFile"},
|
|
$dyndata->{"SrcPath" },
|
|
$dyndata->{"SrcDbg" });
|
|
|
|
|
|
# lcx command line
|
|
|
|
my $lcxop = "";
|
|
# -l switch
|
|
|
|
|
|
$lcxline = &GetMacro("GENERATE_COMMAND");
|
|
|
|
my $a = $dyndata->{"SrcPath" }."\\".$dyndata->{"SrcFile" };
|
|
my $b = $dyndata->{"TokPath" }."\\". $dyndata->{"TokFile" };
|
|
my $c = $dyndata->{"TokPath" };
|
|
my $d = $dyndata->{"SrcPath" };
|
|
my $e = $dyndata->{"TokFile" };
|
|
my $f = $dyndata->{"SrcFile" };
|
|
|
|
|
|
|
|
$lcxline =~ s|\s+\&\s*\_\s*|\n\t|;
|
|
|
|
# timing improvements due to the pre-parsed US LCX files:
|
|
# This is a fix. Possibly a solution needs to be
|
|
# more robust.
|
|
|
|
my $r = $b;
|
|
$r =~ s/\.\w+$//;
|
|
|
|
$lcxline =~ s|\\\$\\\(\*\*\[0\]R\\\)|$r|gi;
|
|
$lcxline =~ s|\\\$\\\(\*\*\[1\]R\\\)|$r|gi;
|
|
|
|
|
|
$lcxline =~ s|\\\$\\\(\*\*\[0\]D\\\)|$d|gi;
|
|
$lcxline =~ s|\\\$\\\(\*\*\[1\]D\\\)|$c|gi;
|
|
$lcxline =~ s|\\\$\\\(\*\*\[0\]F\\\)|$f|gi;
|
|
$lcxline =~ s|\\\$\\\(\*\*\[1\]F\\\)|$e|gi;
|
|
$lcxline =~ s|\$\*\*\[0\]|$a|g;
|
|
$lcxline =~ s|\$\*\*\[1\]|$b|g;
|
|
|
|
$lcxline =~ s/\\\$\\\(LCX_OP\\\)/$lcxop/gm;
|
|
$lcxline =~ s/\\\$\\\((\w+)\\\)/\$($1)/gmi;
|
|
# nmake special macro expansion
|
|
$lcxline =~ s/\\\$\\\(([\*\@\<\>]\w)\\\)/\$($1)/gmi;
|
|
|
|
|
|
# Dependency line
|
|
$dline = sprintf "%s\\%s: %s\\%s %s\\%s\n",
|
|
$dyndata->{"DestPath"},
|
|
$dyndata->{"DestFile"},
|
|
$dyndata->{"SrcPath" },
|
|
$dyndata->{"SrcFile" },
|
|
$dyndata->{"TokPath" },
|
|
$dyndata->{"TokFile" };
|
|
|
|
# Dependency line done
|
|
&PushFieldVal( $REPOSITORY, $keyref, "Cmd", &RevEnvVars($dline));
|
|
|
|
$MAKEDIR{$dyndata->{"DestPath"}} = 1;
|
|
|
|
# Description block
|
|
&PushFieldVal( $REPOSITORY, $keyref, "Cmd", sprintf( "\t$lcxline\n" ) );
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
sub GenRsrcCmd {
|
|
|
|
return if $SYNTAX_CHECK_ONLY;
|
|
|
|
my( $keyref ) = @_;
|
|
|
|
my $dyndata = &GetDynData($REPOSITORY, $keyref);
|
|
|
|
|
|
my $pdbline = ""; # copy pdb line
|
|
my $dbgline = ""; # copy dbg line
|
|
my $symline = ""; # copy sym line
|
|
my $binline = ""; # copy binary line
|
|
my $rsrcline= ""; # rsrc command line
|
|
my $dline = "";
|
|
|
|
my ($srcext,
|
|
$srcpdb,
|
|
$srcdbg,
|
|
$srcsym,
|
|
$dstext,
|
|
$dstpdb,
|
|
$dstdbg,
|
|
$dstsym) =
|
|
&NewImageToSymbol($dyndata->{"SrcFile" },
|
|
$dyndata->{"DestFile"},
|
|
$dyndata->{"SrcPath" },
|
|
$dyndata->{"SrcDbg" });
|
|
|
|
|
|
my( $symsrcfull, $symdstfull ) =
|
|
&SetSymPaths( $dyndata->{"SrcDbg" },
|
|
$srcext,
|
|
$srcpdb,
|
|
$srcdbg,
|
|
$srcsym,
|
|
$dyndata->{"DestDbg"},
|
|
$dstext );
|
|
|
|
$pdbline = &MakeXcopyCmd( $dyndata->{"SrcFile"}, $symsrcfull, $srcpdb, $symdstfull, $dstpdb, undef, 1 );
|
|
$dbgline = &MakeXcopyCmd( $dyndata->{"SrcFile"}, $symsrcfull, $srcdbg, $symdstfull, $srcdbg, undef, 1 );
|
|
$symline = &MakeXcopyCmd( $dyndata->{"SrcFile"}, $symsrcfull, $srcsym, $symdstfull, $srcsym, undef, 1 );
|
|
|
|
|
|
# in fact, it is necessary to fix the makexcopycmd
|
|
|
|
|
|
# $binline = &MakeXcopyCmd( $dyndata->{"SrcFile" },
|
|
# $dyndata->{"SrcPath" },
|
|
# $dyndata->{"SrcFile" },
|
|
# $dyndata->{"DestPath"},
|
|
# $dyndata->{"DestFile"},
|
|
# undef, 1);
|
|
|
|
map {$dyndata->{$_}=
|
|
&RevEnvVars($dyndata->{$_})}
|
|
qw (SrcPath DestPAth TokPath CustRc DestPath);
|
|
|
|
# -a or -r switch
|
|
# localization operation type ( append or replace )
|
|
my $rsrctype = &GetLocOpType( $dyndata->{"LocOp" } );
|
|
|
|
# -l switch
|
|
|
|
|
|
my $langsw = &GetMacro("LCID" );
|
|
|
|
$langsw =~ s/0x0//;
|
|
$langsw =~ s/0x//;
|
|
|
|
$langsw = sprintf "-l %s", $langsw;
|
|
|
|
# -s switch
|
|
my $symsw = &SetRsrcSymSw( $symsrcfull, $srcdbg, $symdstfull, $dstdbg );
|
|
|
|
# rsrc command line
|
|
|
|
$rsrcline = GetMacro("RSRC_COMMAND");
|
|
|
|
my $a = $dyndata->{"SrcPath" }."\\".$dyndata->{"SrcFile" };
|
|
my $b = $dyndata->{"TokPath" }."\\". $dyndata->{"TokFile" };
|
|
|
|
|
|
$rsrcline =~ s|\s+\&\s*\_\s*|\n\t|;
|
|
$rsrcline =~ s|\$\*\*\[0\]|$a|g;
|
|
$rsrcline =~ s|\$\*\*\[1\]|$b|g;
|
|
|
|
$rsrcline =~ s/\\\$\\\(LocOp\\\)/$rsrctype/gm;
|
|
$rsrcline =~ s/\\\$\\\((\w+)\\\)/\$($1)/gm;
|
|
# nmake special macro expansion
|
|
$rsrcline =~ s/\\\$\\\(([\*\@\<\>]\w)\\\)/\$($1)/gmi;
|
|
|
|
|
|
# $rsrcline = sprintf "logerr \"rsrc \$\@ %s %s\\%s %s %s \"",
|
|
# $rsrctype,
|
|
# $dyndata->{"TokPath" },
|
|
# $dyndata->{"TokFile" },
|
|
# $langsw,
|
|
# $symsw;
|
|
#
|
|
|
|
# Dependency line
|
|
$dline = sprintf "%s\\%s: %s\\%s %s\\%s\n",
|
|
$dyndata->{"DestPath"},
|
|
$dyndata->{"DestFile"},
|
|
$dyndata->{"SrcPath" },
|
|
$dyndata->{"SrcFile" },
|
|
$dyndata->{"TokPath" },
|
|
$dyndata->{"TokFile" };
|
|
|
|
$dline = &RevEnvVars($dline);
|
|
|
|
&PushFieldVal( $REPOSITORY, $keyref, "Cmd", "$dline" );
|
|
|
|
# Dependency line done
|
|
|
|
# Description block
|
|
|
|
$MAKEDIR{$dyndata->{"DestPath"}} = 1;
|
|
|
|
if ( $dbgline || $pdbline || $symline) {
|
|
if ( $symdstfull ne $dyndata->{"DestDbg" } ) {
|
|
$MAKEDIR{$symdstfull}=1;
|
|
} # if
|
|
} # if
|
|
|
|
if ( $pdbline ) {
|
|
&PushFieldVal( $REPOSITORY, $keyref, "Cmd", sprintf( "\t$pdbline\n" ) );
|
|
} # if
|
|
|
|
if ( $dbgline ) {
|
|
&PushFieldVal( $REPOSITORY, $keyref, "Cmd", sprintf( "\t$dbgline\n" ) );
|
|
} # if
|
|
|
|
if ( $symline ) {
|
|
&PushFieldVal( $REPOSITORY, $keyref, "Cmd", sprintf( "\t$symline\n" ) );
|
|
} # if
|
|
|
|
# &PushFieldVal( $REPOSITORY, $keyref, "Cmd", sprintf( "\t$binline\n" ) );
|
|
&PushFieldVal( $REPOSITORY, $keyref, "Cmd", sprintf( "\t$rsrcline\n" ) );
|
|
|
|
return;
|
|
|
|
} # GenRsrcCmd
|
|
|
|
|
|
sub GenMUICmd {
|
|
|
|
return if $SYNTAX_CHECK_ONLY;
|
|
|
|
# Optional resource-only DLL generation
|
|
|
|
my( $keyref ) = @_;
|
|
|
|
my $dyndata = &GetDynData($REPOSITORY, $keyref);
|
|
|
|
my $muiline = "";
|
|
|
|
my $_target = &GetMacro( "_target" ),
|
|
|
|
# Sets the muibld command
|
|
$muiline = GetMacro("MUI_COMMAND");
|
|
|
|
my $a = $dyndata->{"SrcPath" }."\\".$dyndata->{"SrcFile" };
|
|
my $b = $dyndata->{"TokPath" }."\\". $dyndata->{"TokFile" };
|
|
|
|
|
|
$muiline =~ s|\s+\&\s*\_\s*|\n\t|g;
|
|
$muiline =~ s|\$\*\*\[0\]|$a|g;
|
|
$muiline =~ s|\$\*\*\[1\]|$b|g;
|
|
$muiline =~ s/\\\$\\\((\w+)\\\)/\$($1)/gm;
|
|
# nmake special macro expansion
|
|
$muiline =~ s/\\\$\\\(([\*\@\<\>]\w)\\\)/\$($1)/gmi;
|
|
$DEBUG and
|
|
print STDERR "\"$muiline\"\n";
|
|
$MAKEDIR{sprintf("%s\\mui\\%s\\res",
|
|
&GetMacro("_NTBINDIR"),
|
|
&GetMacro("LANGUAGE"))} = 1;
|
|
|
|
$MAKEDIR{sprintf("%s\\mui\\%s\\$_target",
|
|
&GetMacro("_NTBINDIR"),
|
|
&GetMacro("LANGUAGE"))} = 1;
|
|
|
|
&PushFieldVal( $REPOSITORY, $keyref, "Cmd", sprintf( "\t$muiline\n" ) );
|
|
|
|
return;
|
|
|
|
} # GenMUICmd
|
|
|
|
|
|
|
|
# GetBgnCodePageSw
|
|
# Sets the code path bingen switch (-p)
|
|
# Input: code page value
|
|
# Output: bingen -p switch
|
|
|
|
sub GetBgnCodePageSw {
|
|
|
|
return "-p $_[0]" unless $_[0]=~/^\-$/;
|
|
|
|
} # GetBgnCodePageSw
|
|
|
|
|
|
# GetBgnICodesSw
|
|
# Sets the -i bingen switch, if needed
|
|
# ( primary language id and secondary language id for the input language )
|
|
# Input: bingen opetation type (-r or -a)
|
|
# the input language
|
|
# Output: -i <pri lang code> <sec lang code> in any of the following cases:
|
|
# * append token operation (bingen -a)
|
|
# * the input language is different from USA
|
|
# (another way of saying that ILANGUAGE is defined and is
|
|
# different from USA)
|
|
# Otherwise, return ""
|
|
|
|
sub GetBgnICodesSw {
|
|
|
|
my( $bgntype, $ilang ) = @_;
|
|
|
|
# Append operation => set -i
|
|
# Replace operation and the input language is not USA => -i
|
|
if ( $bgntype eq "-a" || ( $ilang && lc($ilang) ne "usa" ) ) {
|
|
if ( !$ilang ) {
|
|
$ilang = "USA";
|
|
} # if
|
|
return join(" ", "-i", map {$LANGCODES{$ilang}->{$_}} ("PriLangID", "SubLangID"));
|
|
} # if
|
|
|
|
return "";
|
|
|
|
} # GetBgnICodesSw
|
|
|
|
# SetBgnSymSw
|
|
# Generates the -m bingen switch
|
|
# Input: dbg source path, dbg file, dbg destination path, dbg file
|
|
# Output: string containing the -m bingen switch
|
|
|
|
sub SetBgnSymSw {
|
|
return " -m $_[0] $_[2]" if ($_[0] !~ /^\-$/ && $_[2] !~ /^\-$/ && -e "$_[0]\\$_[1]");
|
|
} # SetBgnSymSw
|
|
|
|
# SetRsrcSymSw
|
|
# Input
|
|
# dbg source path, dbg file, dbg destination path, dbg file
|
|
# Output
|
|
# the -s rsrc switch
|
|
|
|
sub SetRsrcSymSw {
|
|
return " -s $_[2]\\$_[3]" if ($_[0] !~/^\-$/ && $_[1] !~/^\-$/ && -e "$_[0]\\$_[1]");
|
|
} # SetRsrcSymSw
|
|
|
|
# GetLocOpType
|
|
# Sets the localization operation type ( replace or append )
|
|
# Input
|
|
# loc op type as found in the mapping file
|
|
# Output
|
|
# loc op type ( -a or -r )
|
|
|
|
sub GetLocOpType {
|
|
my $loctype = shift;
|
|
my ($locmatch,$locargs, $retstr);
|
|
$loctype |= &GetMacro( "LOC_OP_TYPE" );
|
|
if ($loctype){
|
|
($locmatch,$locargs)=($loctype=~/^-([A-z]+)([^A-r]*)/);
|
|
$locargs=~s/,/ /g;
|
|
if (exists $LOCOPS{$locmatch}) {
|
|
($retstr=$LOCOPS{$locmatch})=~s/\$opts/$locargs/e;
|
|
return $retstr;
|
|
}
|
|
}
|
|
"-r";
|
|
} # GetLocOpType
|
|
|
|
|
|
# GetBgnMultitok
|
|
# Sets the multitoken input parameter (bingen)
|
|
# Input: operation type and path to the input token files
|
|
|
|
sub GetBgnMultitok {
|
|
|
|
my( $keyref, $bgntype ) = @_;
|
|
|
|
# Language itokens
|
|
my $langpath;
|
|
|
|
# Tok path, tok file
|
|
my( $itokpath, $itokfile );
|
|
$itokpath = &GetFieldVal( $REPOSITORY, $keyref, "ITokPath" );
|
|
$itokfile = &GetFieldVal( $REPOSITORY, $keyref, "TokFile" );
|
|
|
|
if ( substr($bgntype,-2) ne "-a" ) { return ""; }
|
|
|
|
$langpath = sprintf "%s\\%s", $itokpath, &GetMacro( "LANGUAGE" );
|
|
if ( -e "$langpath\\$itokfile" ) {
|
|
return "$langpath\\$itokfile";
|
|
}
|
|
|
|
return "$itokpath\\$itokfile";
|
|
|
|
} # GetBgnMultitok
|
|
|
|
|
|
# GetBgnMultitok
|
|
# Returns the filenames for symbol files and the extension for directory
|
|
#
|
|
# Input: ($srcfile,$destfile,$srcpath)
|
|
# source file name,
|
|
# destination file name,
|
|
# source file path,
|
|
# source file symbol path.
|
|
#
|
|
# Output: ($srcext, $srcpdb, $srcdbg, $srcsym, $dstext, $dstpdb, $dstdbg, $dstsym)
|
|
# source file extension,
|
|
# source pdb file name,
|
|
# source dbg file name,
|
|
# source sym file name
|
|
# destination file extension,
|
|
# destination pdb file name,
|
|
# destination dbg file name,
|
|
# destination sym file name.
|
|
#
|
|
# Example: ($srcext, $srcpdb, $srcdbg, $srcsym, $dstext, $dstpdb, $dstdbg, $dstsym) =
|
|
# &NewImageToSymbol($SrcFile, $DestFile, $SrcPath, $srcDbg);
|
|
|
|
|
|
sub NewImageToSymbol {
|
|
|
|
my ($srcfile,$destfile,$srcpath, $srcdbg) = @_;
|
|
my @known=qw(exe dll sys ocx drv);
|
|
my $checkext = qq(pdb);
|
|
my $valid = 0;
|
|
my @ext = qw (pdb dbg sym);
|
|
|
|
map {$valid = 1 if ($srcfile =~/\.$_\b/i)} @known;
|
|
my @sym = ();
|
|
|
|
foreach my $name (($srcfile, $destfile)){
|
|
my $ext=$1 if ($name=~/\.(\w+)$/);
|
|
push @sym, $ext;
|
|
foreach my $newext (@ext){
|
|
my $sym = $name;
|
|
$sym =~ s/$ext$/$newext/;
|
|
if ($valid && $sym =~ /$checkext$/) {
|
|
# >link /dump /headers <binary> |(
|
|
# more? perl -ne "{print $1 if /\s(\S+\.pdb) *$/im}" )
|
|
# >blah-blah-blah.pdb
|
|
my $testname = join "\\",($srcdbg, $ext, $sym);
|
|
if (! -e $testname){
|
|
# we must get the correct directory to check -e!
|
|
#
|
|
if ($FULLCHECK and $srcdbg ne "-"){
|
|
print STDERR "LINK /DUMP ... $srcpath\\$srcfile => replace $sym " if $DEBUG;
|
|
|
|
my $result = qx ("LINK /DUMP /HEADERS $srcpath\\$srcfile");
|
|
$sym = $3 if $result =~/\s\b(([^\\]+\\)+)?(\S+.$checkext) *$/im;
|
|
print STDERR "with $sym\n" if $DEBUG;
|
|
# _namematch($srcpdb,$pdb); use it still?
|
|
}
|
|
}
|
|
}
|
|
push @sym, $sym;
|
|
}
|
|
}
|
|
print STDERR join("\n", @sym), "\n----\n" if $DEBUG;
|
|
@sym;
|
|
} # NewImageToSymbol
|
|
|
|
|
|
# GenMakeDir
|
|
# Write the whole tree to create the target structure.
|
|
# Input & Output
|
|
# none
|
|
# Usage
|
|
# &GenMakeDir();
|
|
|
|
|
|
sub GenMakeDir {
|
|
|
|
# Remove the parent folder
|
|
|
|
my $curdir;
|
|
my @directories;
|
|
my $EXPAND_DIRS_TARGET = &GetMacro("EXPAND_DIRS_TARGET");
|
|
|
|
for $curdir (keys %MAKEDIR) {
|
|
if (exists $MAKEDIR{$curdir}) {
|
|
while ($curdir=~/\\/g) {
|
|
if (($` ne $curdir) && (exists $MAKEDIR{$`})) {
|
|
delete $MAKEDIR{$`};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
my $key = {"DestPath" => "_DIRS", "DestFile" => "makedir"};
|
|
&SetField( $REPOSITORY, $key, "DestPath", "_DIRS" );
|
|
&SetField( $REPOSITORY, $key, "DestFile", "makedir" );
|
|
|
|
# BUG BUG the @Cmd size is expected to be at least 2.
|
|
|
|
&PushFieldVal( $REPOSITORY, $key, "Cmd", "\n\n.IGNORE:");
|
|
&PushFieldVal( $REPOSITORY, $key, "Cmd", "\n\n_DIRS:\ " );
|
|
|
|
if ($EXPAND_DIRS_TARGET){
|
|
|
|
my @tk = map {&RevEnvVars($_)} sort keys %MAKEDIR;
|
|
|
|
&PushFieldVal( $REPOSITORY, $key, "Cmd", join ("\ \\\n\t", @tk) .
|
|
"\n\n" .
|
|
join ("\ \\\n", @tk) .
|
|
"\ :\n\t\!md \$\@ 2>NUL\n" .
|
|
"\n!CMDSWITCHES\n\n" );
|
|
|
|
}
|
|
else{
|
|
my @tk = map {"\tmd ".&RevEnvVars($_)."\ 2\>NUL"} sort keys %MAKEDIR;
|
|
&PushFieldVal( $REPOSITORY, $key, "Cmd", "\n" .
|
|
join ("\n", @tk) .
|
|
"\n\n!CMDSWITCHES\n\n" );
|
|
}
|
|
#print STDERR "\n\n_DIRS:\ " .
|
|
# join ("\ \\\n\t", @tk) .
|
|
# "\n\n.IGNORE:\n\n" .
|
|
# join ("\ \\\n", @tk) .
|
|
# "\ :\n\t\!md \$\@ 2>NUL\n" .
|
|
# "\n!CMDSWITCHES\n\n";
|
|
# exit;
|
|
1;
|
|
|
|
} # GenMakeDir
|
|
|
|
# WriteToSyscmd
|
|
# Creates the SYSCMD nmake command file.
|
|
# Input
|
|
# syscmd file name
|
|
# Output
|
|
# none
|
|
|
|
sub WriteToSyscmd {
|
|
|
|
my( $SYSCMD ) = @_;
|
|
|
|
# Target name, as specified in the command line
|
|
my $tname;
|
|
|
|
# Indexes
|
|
my $i;
|
|
|
|
# Write to syscmd in the following cases:
|
|
# - sysgen with targets (clean or incremental)
|
|
# - sysgen incremental without targets, but with changes detected in the description blocks
|
|
|
|
if ( &IsEmptyHash( $TARGETS ) && ( $CLEAN || ( @SYSCMD == 0 ) ) ) {
|
|
return;
|
|
} # if
|
|
|
|
&SysgenLogMsg("Generating $SYSCMD...",1);
|
|
( open( SYSCMD, ">$SYSCMD" ) ) || &FatalError( 1007, $SYSCMD );
|
|
|
|
print SYSCMD "/A \n";
|
|
|
|
# Always run DIRS
|
|
|
|
print SYSCMD "_DIRS ";
|
|
print SYSCMD "_COMPDIR " if $ACCELERATE;
|
|
|
|
# Write targets to syscmd
|
|
for $tname ( sort keys %$TARGETS ) {
|
|
print SYSCMD
|
|
join("\n",
|
|
map {"\ \\\n $TARGETS->{$tname}->{NewDestPath}->[$_]\\$tname"}
|
|
@{$TARGETS->{$tname}->{NewDestPath}});
|
|
} # for
|
|
|
|
# For incremental without TARGETS, print targets stored in @SYSCMD
|
|
# ( tbd - add an assert here: SYSCMD can be non-empty only for incremental without targets)
|
|
for ( $i=0; $i < @SYSCMD; $i++ ) {
|
|
print SYSCMD "\ \\\n $SYSCMD[$i]";
|
|
} # for
|
|
|
|
close( SYSCMD );
|
|
|
|
return;
|
|
} # WriteToSyscmd
|
|
|
|
# LoadReposit
|
|
# Populate CMD_REPOSITORY according to an existing SYSGEN generated makefile.
|
|
# For each found key, fill in the {Cmd} field.
|
|
# Input
|
|
# makefile name
|
|
# Output
|
|
# none
|
|
# Usage
|
|
# &LoadReposit();
|
|
|
|
sub LoadReposit {
|
|
|
|
my( $makefname ) = @_;
|
|
|
|
# Contents of an existing makefile
|
|
my @makefile;
|
|
|
|
# Line number where the "all" target description line finishes
|
|
my $line;
|
|
|
|
# Nothing to do in case of a clean SYSGEN
|
|
( !$CLEAN ) || return;
|
|
|
|
# Nothing to do if the makefile does not exist
|
|
( -e $makefname ) || &FatalError( 1003, $makefname );
|
|
|
|
# Open the makefile
|
|
( open( MAKEFILE, $makefname ) ) || &FatalError( 1006, $makefname );
|
|
|
|
&SysgenLogMsg("Loading $makefname...", 1);
|
|
# Load makefile's contents
|
|
@makefile = <MAKEFILE>;
|
|
close( MAKEFILE );
|
|
|
|
# Fill in keys according to "all" target found in the makefile
|
|
|
|
$line = &LoadKeys( $makefname, \@makefile );
|
|
|
|
# Load makefile's description blocks into CMD_REPOSITORY's {Cmd} field.
|
|
&LoadCmd( $makefname, $line, \@makefile );
|
|
|
|
return;
|
|
|
|
} # LoadReposit
|
|
|
|
# LoadKeys
|
|
# Loads keys according to the "all" target from the given
|
|
# SYSGEN-generated makefile.
|
|
# Input
|
|
# makefile's name
|
|
# makefile's contents (reference)
|
|
# Output
|
|
# line number following "all" target or
|
|
# -1 if "all" target was not found
|
|
# Usage
|
|
# &LoadKeys( \@makefile );
|
|
|
|
sub LoadKeys {
|
|
# BUG : never exists, causing out of memovy
|
|
# perl death.
|
|
#
|
|
my( $makefname, $makefref ) = @_;
|
|
|
|
# Repository key
|
|
my $key = {};
|
|
|
|
# Indexes
|
|
my $i;
|
|
|
|
my %alltarget=();
|
|
|
|
my $cursection=0;
|
|
|
|
my $makeflines = $#$makefref;
|
|
|
|
my ($targetname, $errortarget);
|
|
|
|
|
|
# Skip white lines in the beginnig of makefile
|
|
|
|
for ( $i=0; $i < $makeflines; $i++ ) {
|
|
if ( $makefref->[$i] =~ /\S/ ) { last; }
|
|
} # for
|
|
|
|
# First non empty line from MAKEFILE must contain "sysgen" target
|
|
( ( $i < @{$makefref} && $makefref->[$i] =~ /sysgen\s*:/i ) ) || &FatalError( 1213, $makefname);
|
|
|
|
# ignore nmake line
|
|
$i++;
|
|
|
|
$alltarget{'all'} = 1;
|
|
|
|
$makeflines = $#$makefref;
|
|
|
|
while (scalar(keys(%alltarget))) { # dengerous never accomplished....
|
|
|
|
# Error if target was not solved.
|
|
( ++$i < $makeflines ) || &FatalError( 1210, "${makefname}(" . join(",", keys %alltarget) . ")" );
|
|
|
|
# Find the target, such as all
|
|
for ( ; $i < $makefref ; $i++) {
|
|
|
|
$errortarget=$targetname = '';
|
|
|
|
next unless $makefref->[$i]=~ /\w+\s*:\s+/i;
|
|
# Suppose only find one item matched
|
|
($targetname, $errortarget) = map({($makefref->[$i]=~m/^\Q$_\E\s*:\s+/i) ? $_ : ()}
|
|
keys %alltarget);
|
|
|
|
# Go to next line if not found
|
|
next if ($targetname eq '');
|
|
|
|
&SysgenLogMsg($targetname, 0);
|
|
|
|
# Two target found in same line
|
|
|
|
&FatalError( 1215, "${makefname}($targetname, $errortarget, ...)")
|
|
unless $errortarget eq '';
|
|
|
|
# Target was found, move to next line and exit for loop
|
|
$i++;
|
|
last;
|
|
|
|
} # for
|
|
|
|
# Look for its belongs
|
|
for ( ; $i < $makeflines ; $i++ ) {
|
|
|
|
last if ($makefref->[$i] !~ /\S/);
|
|
|
|
# lookfor item(s) in one line
|
|
for (split(/\s+/, $makefref->[$i])) {
|
|
|
|
next if ($_ eq '');
|
|
last if ($_ eq '\\');
|
|
|
|
# $_=$makefref->[$i];
|
|
|
|
# If it is a section name, push it into alltarget hash
|
|
if ( /\Q$SECTIONNAME\E\d+$/) {
|
|
|
|
$alltarget{$_} = 1;
|
|
|
|
# Match the last one of $SECTIONAME, with (\d+)\1 => such as 88 => 8, 1616 => 16, 6464 => 64
|
|
$SECTIONS = $1 if (($SECTIONS !~/^\d+$/) && (/\Q$SECTIONNAME\E(\d+)\1/));
|
|
|
|
# Create it into REPOSITORY
|
|
} elsif (/^\t(\S*)\\(\S*)/) {
|
|
$key = {};
|
|
($key->{DestPath}, $key->{DestFile})=($1, $2);
|
|
|
|
&SetField( $CMD_REPOSITORY, $key, "DestPath", $1 );
|
|
&SetField( $CMD_REPOSITORY, $key, "DestFile", $2 );
|
|
|
|
# If DestFile is part of TARGETS, store DestPath in TARGETS.
|
|
if ( &IsHashKey( $TARGETS, $key->{DestFile} ) ) {
|
|
&AddTargetPath( $key->{DestFile}, "OldDestPath", $key->{DestPath} );
|
|
} # if
|
|
|
|
} # if
|
|
|
|
} # for
|
|
|
|
} # for
|
|
|
|
delete $alltarget{$targetname};
|
|
|
|
} # while
|
|
|
|
return $i;
|
|
|
|
} # LoadKeys
|
|
|
|
# LoadCmd
|
|
# Load the body of the makefile.
|
|
# Fill in the {Cmd} field for each CMD_REPOSITORY key
|
|
# Input
|
|
# makefile name
|
|
# makefile line following the all target dependency lines
|
|
# make file contents (reference)
|
|
# Output
|
|
# none
|
|
# Usage
|
|
# &LoadCmd( 2543, \@makefile );
|
|
|
|
sub LoadCmd {
|
|
|
|
my( $makefname, $line, $makefref ) = @_;
|
|
|
|
my $key = {"DestPath" => undef,
|
|
"DestFile" => undef};
|
|
|
|
# Counters
|
|
my($i, $j);
|
|
|
|
# Repository key
|
|
|
|
|
|
# Description line (one or more file lines,
|
|
# depending on the existence of custom resources)
|
|
my $dline;
|
|
|
|
# Buffer for one line
|
|
my $buffer;
|
|
|
|
&FatalError( 1212, $makefname) if $line > scalar(@$makefref);
|
|
|
|
foreach $i ($line..$#$makefref) {
|
|
|
|
my $rule = $makefref->[$i];
|
|
# Skip white lines and resolve lines
|
|
next if $rule !~ /\S/ or $rule =~ /^\t/;
|
|
next unless $rule =~ /\s:\s/;
|
|
$rule =~/\bCOMPDIR\b/ and &Error(5002, "_COMPDIR target found " );
|
|
|
|
# Identify dependency line and key
|
|
$rule =~ /^\"?(.*)\\([^\\]+)\"?\s*:$/;
|
|
|
|
$key -> {"DestPath"} = $1,
|
|
$key -> {"DestFile"} = $2;
|
|
|
|
# The key must exist in CMD_REPOSITORY
|
|
&IsRepositKey( $CMD_REPOSITORY, $key ) ||
|
|
&FatalError( 1211, "$rule" );
|
|
|
|
# Load the description block into the {Cmd} field
|
|
$dline = "";
|
|
|
|
# Read first the dependency line.
|
|
# It might be spread over several lines in the makefile,
|
|
# but we will make a single line from it.
|
|
foreach $j ($i..$#$makefref){
|
|
$dline .= $makefref->[$j];
|
|
last if $makefref->[$j] !~ /\\$/;
|
|
# Dependency line ends when the last character
|
|
# is not a continuation line mark
|
|
} # for
|
|
|
|
&PushFieldVal( $CMD_REPOSITORY, $key, "Cmd", $dline );
|
|
$i=$j+1;
|
|
|
|
# Read then the command block.
|
|
foreach $j ($i..$#$makefref){
|
|
|
|
# Description block ends at the first white line encountered
|
|
last if $makefref->[$j] !~ /\S/ ;
|
|
|
|
# Load the current command line
|
|
&PushFieldVal( $CMD_REPOSITORY, $key, "Cmd", $makefref->[$j] );
|
|
|
|
} # for
|
|
|
|
$i = $j;
|
|
|
|
} # for
|
|
|
|
return;
|
|
|
|
} # LoadCmd
|
|
|
|
# LoadEnv
|
|
# Loads the environment variables into MACROS (hash).
|
|
# Uppercases all the macroname.
|
|
# Input & Output: none
|
|
# Usage
|
|
# &LoadEnv();
|
|
|
|
sub LoadEnv {
|
|
|
|
for ( keys %ENV ) {
|
|
&SetMacro( $_, $ENV{$_}, 0 );
|
|
} #for
|
|
|
|
# version
|
|
my $version = $VERSION;
|
|
$version =~ s/\.\d+$//g;
|
|
&SetMacro("VERSION", $version, 0);
|
|
$DEBUG and
|
|
print STDERR "\"$version\"\n";
|
|
# stuff fot the MakeXcopyCmd
|
|
|
|
$version ne "3" and &SetMacro( "DST", $ENV{"_NTPOSTBLD"}, 0 );
|
|
|
|
$SRCBASE =~ s/([\\\.])/\Q$1\E/g;
|
|
|
|
$DEBUG and
|
|
print STDERR "\"",$DSTBASE, "\"\t\"", $SRCBASE, "\"\n";
|
|
|
|
return;
|
|
|
|
} # LoadEnv
|
|
|
|
# GetMacro
|
|
# Returns the value of a macro.
|
|
# Input
|
|
# macroname
|
|
# Output
|
|
# macrovalue ( empty string if not defined )
|
|
# Usage
|
|
# $language = &GetMacro("Language");
|
|
|
|
sub GetMacro {
|
|
|
|
return $MACROS->{uc$_[0]}->{Value};
|
|
|
|
} # GetMacro
|
|
|
|
# SetMacro
|
|
# Sets the value of a macro.
|
|
# Input
|
|
# macroname
|
|
# macrovalue
|
|
# macrotype (see %MACROS in the beginning of the file
|
|
# for more details on the types of macros.
|
|
# Output
|
|
# none
|
|
# Usage
|
|
# &SetMacro( "_BuildArch", "nec_98", 0);
|
|
|
|
sub SetMacro {
|
|
|
|
my $varname=uc shift;
|
|
|
|
# Do not overwrite a macro defined in the command line unless
|
|
# the same macro is redefined in the command line
|
|
if ( (!exists $MACROS->{$varname}) || ($MACROS->{$varname}->{Type} == 0) || ($_[1] == 1)) {
|
|
($MACROS->{$varname}->{Value}, $MACROS->{$varname}->{Type})=@_;
|
|
}
|
|
|
|
return;
|
|
|
|
} # SetMacro
|
|
|
|
|
|
|
|
|
|
# FatalError
|
|
# Prints the error and exits.
|
|
# Input
|
|
# error code
|
|
# name of the sysfile where the error occured or any other text
|
|
# line number on the description file where the error occured or 0 if not the case
|
|
# Ouput
|
|
# none
|
|
# Usage
|
|
# &FatalError( 1111, "sysfile", 12 );
|
|
# &FatalError( 1002, $CODESFNAME, 0 );
|
|
|
|
sub FatalError {
|
|
|
|
&PrintError( "fatal error", @_);
|
|
print "Stop.\n";
|
|
exit;
|
|
|
|
} # FatalError
|
|
|
|
# Error
|
|
# Prints the error and returns.
|
|
# Input
|
|
# error code
|
|
# name of the sysfile where the error occured or any other text
|
|
# line number on the description file where the error occured or 0 if not the case
|
|
# Output
|
|
# none
|
|
# Usage
|
|
# &Error( 2011, $recref->{TokPath}\\$recref->{TokFile});
|
|
|
|
sub Error {
|
|
|
|
# Errors from IGNORE macro are not counted
|
|
if ($ERRORS->{$_[-1]} != $_[-2] ){
|
|
if ( &PrintError( "error", @_ ) ) {
|
|
$ERRORS->{$_[-1]} = $_[-2];
|
|
}
|
|
} # if
|
|
# $DEBUG and
|
|
# print STDERR join( "\t" , keys %$ERRORS) , "\n";
|
|
|
|
return;
|
|
|
|
} # Error
|
|
|
|
# PrintError
|
|
# Prints the encountered error with the format
|
|
# <description filename>(<line>): <fatal error | error> <error_code>: <error_text>
|
|
# Input
|
|
# error type ( fatal error or error )
|
|
# error code
|
|
# filename where the error was encountered or other text
|
|
# line number or 0
|
|
# Output
|
|
# 1 if the error is counted
|
|
# 0 otherwise
|
|
# Usage
|
|
# &PrintError( 1002, $CODESFNAME, 0);
|
|
|
|
sub PrintError {
|
|
|
|
my( $errtype, $errno, $file, $line ) = @_;
|
|
|
|
# Error text
|
|
my $errtxt;
|
|
|
|
# Ignore errors
|
|
my $ignore;
|
|
my @ivalues;
|
|
|
|
# Counter
|
|
my $i;
|
|
|
|
my $fileline;
|
|
|
|
# Do not print anything if errno is listed in IGNORE macro
|
|
$ignore = &GetMacro( "IGNORE" );
|
|
$ignore =~ s/\s*//g;
|
|
@ivalues = split ";", $ignore;
|
|
for ( $i=0; $i < @ivalues; $i++ ) {
|
|
if ( $errno == $ivalues[$i] ) {
|
|
return 0;
|
|
} # if
|
|
} # for
|
|
|
|
$errtxt = "SYSGEN:";
|
|
$fileline="";
|
|
if ( $file ) {
|
|
if ( $line ) { $fileline=" $file($line)"; }
|
|
else { $fileline=" $file"; }
|
|
} # if
|
|
|
|
if ( $MAP_ERR{$errno} ) { $fileline .= ": ".$MAP_ERR{$errno}; }
|
|
|
|
$errtxt .= " $errtype $errno:$fileline";
|
|
|
|
( open LOGFILE, ">>$LOGFILE" ) || &FatalError( 1007, $LOGFILE );
|
|
printf LOGFILE "$errtxt\n";
|
|
close LOGFILE;
|
|
|
|
( open ERRFILE, ">>$ERRFILE" ) || &FatalError( 1007, $ERRFILE );
|
|
printf ERRFILE "$errtxt\n";
|
|
close ERRFILE ;
|
|
|
|
select(STDERR); $| = 1;
|
|
printf "$errtxt\n";
|
|
select(STDOUT); $| = 1;
|
|
|
|
return 1;
|
|
|
|
} # PrintError
|
|
|
|
# SumErrors
|
|
# Displays the number of non-fatal errors found while running SYSGEN.
|
|
# Runs at the end of sysgen.
|
|
# Input & Output: none
|
|
# Usage
|
|
# &SumErrors();
|
|
|
|
sub SumErrors {
|
|
|
|
my $elapsed_time = time() - $start_time;
|
|
|
|
print STDERR "Finished in $elapsed_time seconds\n";
|
|
|
|
my $errors = scalar(keys(%$ERRORS));
|
|
|
|
if ( 0 == $errors ) {
|
|
print "\nSYSGEN: No errors found during execution.\n";
|
|
}
|
|
else {
|
|
print "\nSYSGEN: Total Errors: $errors\n";
|
|
}
|
|
|
|
1;
|
|
|
|
} # SumErrors
|
|
|
|
|
|
# CleanLogFiles
|
|
# For a clean SYSGEN, delete sysgen.log and sysgen.err files.
|
|
# For an incremental SYSGEN, delete only sysgen.err.
|
|
# Input
|
|
# sysfile's directory
|
|
# Output
|
|
# none
|
|
# Usage
|
|
# &CleanLogFiles();
|
|
|
|
sub CleanLogFiles {
|
|
|
|
my( $sysdir ) = @_;
|
|
|
|
# Delete $LOGFILE and $ERRFILE
|
|
&SysgenLogMsg("Cleaning log and error files...",1);
|
|
|
|
if ( -e $ERRFILE ) { unlink $ERRFILE; }
|
|
|
|
-e $ERRFILE and exit "failed to delete $ERRFILE\n";
|
|
|
|
open ERRFILE, ">>$ERRFILE" or exit "Cannot access $ERRFILE\n";
|
|
close ERRFILE;
|
|
|
|
if ( $CLEAN && -e $LOGFILE ) {
|
|
unlink $LOGFILE;
|
|
-e $LOGFILE and exit "failed to delete $LOGFILE\n";
|
|
}
|
|
|
|
# Delete existing $MAKEFILE and $SYSCMD
|
|
if ( $CLEAN && !$SYNTAX_CHECK_ONLY && -e $MAKEFILE ) { unlink $MAKEFILE; }
|
|
|
|
if ( !$SYNTAX_CHECK_ONLY && -e $SYSCMD ) { unlink $SYSCMD; }
|
|
|
|
return;
|
|
|
|
} # CleanLogFiles
|
|
|
|
# ////////////////////////////////////////////////////////////////////////////////////////
|
|
# PrintHelp
|
|
# Print usage
|
|
|
|
sub PrintHelp {
|
|
|
|
my $version = &Version;
|
|
my $scriptname = $0;
|
|
$scriptname =~s/^.*\\//;
|
|
|
|
print STDERR <<EOH;
|
|
$version
|
|
Usage
|
|
perl $scriptname [<options>] [<macros>] [<targets>]
|
|
where
|
|
Options:
|
|
|
|
/c generate the makefile from scratch, overwriting existing one.
|
|
By running nmake, all the targets will be generated.
|
|
/s limit sysgen to syntax check - makefile not (re)generated.
|
|
/f <name> takes <name> as the sysfile.
|
|
If this option is omitted, sysgen searches the current
|
|
directory for a file called sysfile and uses it as a
|
|
description (mapping) file.
|
|
/w <name> takes <name> as the PATH for reading/writing
|
|
makefile and err/log files. Note that the default
|
|
aggregation 'makefile' name is sysgen.mak
|
|
/v display version
|
|
/y verify LOC drop coverage. Use in conjunction with -c flag
|
|
/z generate LS 5.0 tokens from existing LOC drop (bingen/rsrc).
|
|
Use in conjunction with -c flag
|
|
/l:<LANG> specify language
|
|
/?
|
|
/h display this message
|
|
|
|
Macros:
|
|
list the command line macro definitions in the format
|
|
"<macroname>=<macrovalue>". Seldomly used
|
|
|
|
Targets:
|
|
specify files (without path) to localize/aggregate.
|
|
|
|
For a full documentation please run perldoc.exe $scriptname
|
|
|
|
EOH
|
|
|
|
exit 0;
|
|
|
|
} #PrintHelp
|
|
|
|
|
|
sub printVersion{
|
|
|
|
print STDERR &Version;
|
|
exit 0;
|
|
|
|
} #PrintVersion
|
|
|
|
|
|
# Version of the SYSGEN
|
|
# usage : &Version
|
|
|
|
|
|
sub Version{
|
|
|
|
<<EOT;
|
|
|
|
SYSGEN v.$VERSION: Whistler aggregation tool for international builds.
|
|
|
|
EOT
|
|
} #Version
|
|
|
|
|
|
# FillFilter
|
|
# Fills in the FILTER variable
|
|
# Input & Output
|
|
# none
|
|
|
|
sub FillFilter {
|
|
|
|
# List of filtered files
|
|
my @farray;
|
|
|
|
# Index
|
|
my $i;
|
|
|
|
my $file = &GetMacro( "FILTER" );
|
|
|
|
( $file ) || return;
|
|
( -e $file ) || &FatalError( 1005, $file );
|
|
|
|
# Open the mapping file
|
|
( open( FILE, $file ) ) || &FatalError( 1006, $file );
|
|
|
|
# Load file contents
|
|
@farray = <FILE>;
|
|
close( FILE );
|
|
|
|
&SysgenLogMsg("Loading filter $file...", 1);
|
|
for ( $i = 0; $i < @farray; $i++ ) {
|
|
|
|
chop $farray[$i];
|
|
$farray[$i] =~ s/^\s*//g;
|
|
$farray[$i] =~ s/\s*=\s*/=/g;
|
|
next if (($farray[$i]=~/^\;/)||($farray[$i]=~/^\s*$/));
|
|
$FILTER{lc( $farray[$i] )} = 0;
|
|
|
|
} # for
|
|
|
|
# In case targets were specified in the command line,
|
|
# verify FILTER contains them.
|
|
if ( ! &IsEmptyHash( $TARGETS ) ) {
|
|
for ( keys %$TARGETS ) {
|
|
if ( ! exists $FILTER{lc( $_ )} ) {
|
|
&FatalError( 1009, $_ );
|
|
} # if
|
|
} # for
|
|
} # if
|
|
|
|
return;
|
|
|
|
} # FillFilter
|
|
|
|
# GetSysDir
|
|
# Returns the directory name from a sysfile path.
|
|
# Sysfile's directory is the place where SYSGEN generates the following files:
|
|
# makefile (used by nmake to execute the aggregation)
|
|
# syscmd (is the nmake command-line file used for incremental builds)
|
|
# sysgen.log (the output of SYSGEN and NMAKE)
|
|
# sysgen.err (the errors found while running SYSGEN and NMAKE)
|
|
# Input
|
|
# full path to a sysfile
|
|
# Output
|
|
# directory name of the given sysfile
|
|
|
|
sub GetSysDir {
|
|
|
|
my $sysfile = shift;
|
|
$DEBUG and print STDERR "get \$sysfile \"$sysfile\"\n";
|
|
|
|
my $sysdir = $sysfile if $sysfile =~ /\\/;
|
|
$sysdir =~ s/\\[^\\]+$//;
|
|
$DEBUG and print STDERR "set \$sysdir \"$sysdir\"\n";
|
|
$sysdir;
|
|
|
|
} # GetSysDir
|
|
|
|
# The wrapper for the old sysgen
|
|
# &ParseCmdLine while transition to
|
|
# &parseargs
|
|
# Usege &UseOldParser
|
|
|
|
sub UseOldParser{
|
|
|
|
$ARGL =~ s/(\w):([^\\])/$1$SEPARATOR$2/g;
|
|
$ARGL =~ s/\-old//g;
|
|
|
|
my @argv = grep {/\S/} split($SEPARATOR, $ARGL);
|
|
$DEBUG and
|
|
print STDERR "\n", join("\n", @argv), "\n\n";
|
|
&ParseCmdLine(@argv);
|
|
|
|
@argv;
|
|
}
|
|
|
|
|
|
# ParseCmdLine
|
|
# Parses the command line.
|
|
# SYSGEN's command-line syntax is:
|
|
# SYSGEN [<options>] [<macros>] [<targets>]
|
|
# Input
|
|
# command-line array
|
|
# Output
|
|
# none
|
|
# Usage
|
|
# &ParseCmdline(@ARGV);
|
|
|
|
sub ParseCmdLine {
|
|
|
|
my @cmdline = @_;
|
|
|
|
# Indexes
|
|
|
|
my( $i , $optname , @text );
|
|
|
|
for ( $i=0; $i < @cmdline; $i++ ) {
|
|
|
|
$_ = $cmdline[$i];
|
|
|
|
($optname)=m/[-\/]([\?hVcnfswaxl])/i;
|
|
$optname =~s/(\w)/\L$1\E/;
|
|
|
|
# Check Option
|
|
if ( $optname ) {
|
|
|
|
$SYNTAX_CHECK_ONLY = 1 and next if $optname eq 's'; # -s for syntax check only
|
|
|
|
if ($optname eq '?' or $optname eq 'h') { # -? for help
|
|
|
|
&PrintHelp;
|
|
|
|
} elsif ($optname eq 'v') { # -V for version number
|
|
|
|
&printVersion;
|
|
|
|
} elsif ($optname eq 'c') { # -c for CLEAN
|
|
|
|
$CLEAN = 1;
|
|
|
|
next;
|
|
|
|
} elsif ($optname eq 'n') { # -n for Section Number
|
|
|
|
# Set SECTIONS value
|
|
$i++;
|
|
( $i < @cmdline ) || &FatalError( 1011 );
|
|
|
|
$SECTIONS = $cmdline[$i];
|
|
$SECTIONS = $DEFAULT_SECTIONS if ($SECTIONS !~/^\d+$/);
|
|
|
|
next;
|
|
|
|
} elsif ($optname eq 'f') { # -f for specified SYSFILE
|
|
|
|
# add SYSFILES
|
|
$i++;
|
|
( $i < @cmdline ) || &FatalError( 1008 );
|
|
push @SYSFILES, $cmdline[$i];
|
|
|
|
next;
|
|
|
|
} elsif ($optname eq 'l') { # -l LANG
|
|
|
|
# add SYSFILES
|
|
$i++;
|
|
( $i < @cmdline ) || &FatalError( 1008 );
|
|
$LANG = $cmdline[$i];
|
|
&SetMacro("LANGUAGE", uc($cmdline[$i]), 0);
|
|
$DEBUG and print STDERR "LANG\t", &GetMacro("LANGUAGE"), "\n";
|
|
|
|
next;
|
|
|
|
} elsif ($optname eq 'x') { # -x for exclude path from mapping
|
|
|
|
# add SYSFILES
|
|
$i++;
|
|
( $i < @cmdline ) || &FatalError( 1008 );
|
|
$EXCLUDE_DIRECTORIES->{$cmdline[$i]} = 1;
|
|
|
|
next;
|
|
|
|
|
|
} elsif ($optname eq 'w') { # -w for specified WORKDIR
|
|
|
|
# Set WORKDIR value
|
|
$i++;
|
|
( $i < @cmdline ) || &FatalError( 1010 );
|
|
|
|
$WORKDIR = $cmdline[$i];
|
|
|
|
|
|
next;
|
|
} elsif ($optname eq 'a') { # -w for ACCELERATE
|
|
|
|
# Set ACCEL
|
|
$ACCELERATE = 1;
|
|
|
|
next;
|
|
}
|
|
|
|
|
|
} # if
|
|
|
|
# macro definition
|
|
if ( /\s*(\S*)\s*\=\s*(\S*)\s*/ ) {
|
|
&SetMacro( $1, $2, 1 );
|
|
last SWITCH;
|
|
} # if
|
|
|
|
# default (target name)
|
|
&AddTarget($_);
|
|
|
|
} # for
|
|
|
|
return;
|
|
|
|
} # ParseCmdLine
|
|
|
|
|
|
# parseSymbolCD
|
|
# create the mapping for some binaries listed in symbolcd.txt
|
|
# Input
|
|
# filename [path to symbolcd.txt]
|
|
# Output
|
|
# REF TO ARRAY OF HASH REF [{BIN => SYMBOL}, ... ]
|
|
# Sample usage:
|
|
# print join "\n", @{&parseSymbolCD("symbolcd.txt")};
|
|
#
|
|
|
|
sub parseSymbolCD{
|
|
|
|
my $fname = shift;
|
|
open (F, "<". $fname);
|
|
my %o;
|
|
my $o = \%o;
|
|
while(<F>){
|
|
chomp;
|
|
my @s = split ",", $_;
|
|
# keep the filename of the binary from symbolcd.txt
|
|
$s[0] =~ s/^.*\\//g;
|
|
next if ($s[0] eq $s[1]);
|
|
$s[1] =~ s/^.*\.//g;
|
|
# keep the extension of the symbol file from symbolcd.txt
|
|
$o->{$s[0]} = {} unless defined ($o->{$s[0]});
|
|
$o->{$s[0]}->{$s[1]} = $s[2];
|
|
# there are more lines
|
|
}
|
|
close(F);
|
|
&SysgenLogMsg("Loading $fname... ". scalar(keys(%o)). " symbols", 1);
|
|
if ($DEBUG){
|
|
foreach my $lib (keys(%o)){
|
|
my %hint = %{$o{$lib}};
|
|
print STDERR join("\t", keys(%hint)), "\n";
|
|
}
|
|
}
|
|
|
|
%o;
|
|
}
|
|
|
|
|
|
# LoadHotFix
|
|
# Reads and loads the makefile style hotfix, using the two parts
|
|
# of the dependancy rule for:
|
|
#
|
|
# * check for token->binary depenancy during repository generation
|
|
# * repository modification
|
|
#
|
|
# Input
|
|
# HOTFIX filename
|
|
# Output
|
|
# <none>
|
|
# LoadHotFix can be called any time,
|
|
# since it expands symbols without relying on
|
|
# that vars are defined.
|
|
# Input
|
|
# filename [path to hotfix file]
|
|
# Output
|
|
# <unused>
|
|
|
|
|
|
sub LoadHotFixFile{
|
|
|
|
my $filename = shift;
|
|
|
|
return unless -e $filename;
|
|
open (HOTFIX, $filename);
|
|
# makefile style hot fix.
|
|
my ($target, $build, $depend, $message);
|
|
my $hotfix = \%HOTFIX;
|
|
|
|
while(<HOTFIX>){
|
|
chomp;
|
|
next if /^\#/; # comment
|
|
if (/^\S*SET/i){
|
|
&SetMacro( &SetAttribOp( $_ ), 0 );
|
|
next;
|
|
}
|
|
if ( /\bMESSAGE\b\s+(\S.*)$/i) { # line
|
|
$message = &ReplaceVar( $1, 0 ); # MESSAGE something impo
|
|
$message =~ s/\"//g; # becomes:
|
|
$message = "$target: $message"; # SYSGEN: error 1101: <target>: something important
|
|
$message =~ s/^.*\\([^ ]+)/$1/g; # logerr "echo <target> something important"
|
|
&Error( 1101, $message );
|
|
|
|
$build = $hotfix->{$target}->{"build"}
|
|
if defined($hotfix->{$target}->{"build"});
|
|
|
|
push @$build, "\t".&ReplaceVar("logerr \"echo $message\"")."\n";
|
|
next;
|
|
} # case
|
|
|
|
if ($_=~/^(.*) *\:[^\\](.*)$/){
|
|
$target = $1; #<target>: <source list>
|
|
my @depend = ();
|
|
$depend = $2;
|
|
map {push @depend, lc(&ReplaceVar($_))} split( /\s+/, $depend);
|
|
$target =~s/ +$//g;
|
|
$target = lc(&ReplaceVar($target));
|
|
$HOTFIX{$target} = {"build" => [],
|
|
"depend" => [@depend]};
|
|
print STDERR join("\n", map {"'$_'"} @depend), "\n---\n" if $DEBUG;
|
|
$build = $hotfix->{$target}->{"build"};
|
|
}
|
|
push @$build, "\t".&ReplaceVar($_)."\n" if (/\S/ && /^\t/ );# instructions
|
|
}
|
|
|
|
&SysgenLogMsg("Loading $filename ... ". scalar (keys(%HOTFIX)). " hotfix rules", 1);
|
|
print STDERR join("\n", "keys \%HOTFIX:", map {"'$_'"} keys(%HOTFIX)), "\n" if $DEBUG;
|
|
close(HOTFIX);
|
|
|
|
map {print STDERR $_, "\n",join("\n",
|
|
@{$hotfix->{$_}->{"build"}}), "\n---\n"}
|
|
keys(%$hotfix) if $DEBUG;
|
|
|
|
1;
|
|
}
|
|
|
|
|
|
|
|
# LoadRuleBlock
|
|
# Reads and loads the makefile style hotfix, using the two parts
|
|
# of the dependancy rule for:
|
|
#
|
|
# * check for token->binary depenancy during repository generation
|
|
# * repository modification
|
|
#
|
|
# Input
|
|
# HOTFIX filename
|
|
# Output
|
|
# <none>
|
|
# LoadHotFix can be called any time,
|
|
# since it expands symbols without relying on
|
|
# that vars are defined.
|
|
# Input
|
|
# filename [path to hotfix file]
|
|
# Output
|
|
# <unused>
|
|
|
|
sub LoadRuleBlock{
|
|
|
|
my $ruleref = shift;
|
|
my $hotfix = \%HOTFIX;
|
|
my ($target, $depend, $build, $filename);
|
|
foreach (@$ruleref){
|
|
chomp;
|
|
next if /^\#/; # comment
|
|
|
|
if ( /\bMESSAGE\b\s+(\S.*)$/i) { # line
|
|
my $message;
|
|
$message = &ReplaceVar( $1, 0 ); # MESSAGE something impo
|
|
$message =~ s/\"//g; # becomes:
|
|
$message = "$target: $message"; # SYSGEN: error 1101: <target>: something important
|
|
$message =~ s/^.*\\([^ ]+)/$1/g; # logerr "echo <target> something important"
|
|
&Error( 1101, $message );
|
|
$build = $hotfix->{$target}->{"build"} if
|
|
defined($hotfix->{$target}->{"build"});
|
|
push @$build, "\t".&ReplaceVar("logerr \"echo $message\"")."\n";
|
|
next;
|
|
} # MESSAGE
|
|
|
|
|
|
if (/^\S*SET/i){
|
|
&SetMacro( &SetAttribOp( $_ ), 0 );
|
|
next;
|
|
}
|
|
if ($_=~/^(.*) *\:[^\\](.*)$/){
|
|
$target = $1;#target: source list
|
|
my @depend = ();
|
|
$depend = $2;
|
|
map {push @depend, lc(&ReplaceVar($_))} split( /\s+/, $depend);
|
|
$target =~s/ +$//g;
|
|
$target = lc(&ReplaceVar($target));
|
|
$HOTFIX{$target} = {"build" => [],
|
|
"depend" => [@depend]};
|
|
print STDERR "Depend:\n+------\n|",join("\n|", map {"'$_'"} @depend), "\n+---\n" if $DEBUG;
|
|
$build = $hotfix->{$target}->{"build"};
|
|
}
|
|
push @$build, "\t".&RevEnvVars(&ReplaceVar($_))."\n" if (/\S/ && /^\t/ );# instructions
|
|
}
|
|
# my $DEBUG = 1;
|
|
&SysgenLogMsg("Loading $filename ... ". scalar (keys(%HOTFIX)). " hotfix rules", 1);
|
|
|
|
map {print STDERR $_, ":\n",join("",
|
|
@{$hotfix->{$_}->{"build"}}), "\n---\n"}
|
|
keys(%$hotfix)
|
|
if $DEBUG;
|
|
|
|
1;
|
|
}
|
|
|
|
|
|
# FixRepository
|
|
# Merges contents of Repository with the commands from the HOTFIX file
|
|
# on the same target without introducing new targets.
|
|
# Must be called as late as possible but before the
|
|
# writing the nmake Makefile
|
|
# Input
|
|
# <none>
|
|
# Output
|
|
# <unused>
|
|
sub FixRepository{
|
|
|
|
my $mapref = $REPOSITORY;
|
|
return unless scalar(keys(%HOTFIX));
|
|
|
|
foreach my $destpath ( sort keys %{$mapref} ) {
|
|
|
|
foreach my $destfile ( sort keys %{$mapref->{$destpath}} ) {
|
|
|
|
my $fullname=lc(join("\\",$destpath, $destfile));
|
|
if ($HOTFIX{lc($fullname)}){
|
|
print STDERR "Applying HOTFIX rule for $fullname\n" if $DEBUG;
|
|
my $cmdref = $mapref->{$destpath}->{$destfile}->{"Cmd"};
|
|
my @cmd = map {$_} @{$cmdref};
|
|
my $hotfix = \%HOTFIX;
|
|
my $depend = $hotfix->{$fullname}->{"depend"};
|
|
my $dep = &RevEnvVars(join(" ", "", @$depend));
|
|
chomp $dep;
|
|
$cmd[0] =~ s/$/$dep/;# append the dep list
|
|
$#cmd=0 if &GetMacro("OVERWRITE_DEFAULT_RULE");
|
|
my $newcmd = $hotfix->{$fullname}->{"build"};
|
|
foreach (@$newcmd) {$_ = &RevEnvVars($_);};
|
|
if (&GetMacro("APPEND")){
|
|
# append:
|
|
push @cmd, @{$newcmd};
|
|
}
|
|
else{
|
|
# prepend:
|
|
my $line0 = shift @cmd;
|
|
unshift @cmd, @{$newcmd};
|
|
unshift @cmd, $line0;
|
|
}
|
|
$mapref->{$destpath}->{$destfile}->{"Cmd"} = \@cmd;
|
|
map {print STDERR "$_\n"} @cmd if $DEBUG;
|
|
}
|
|
}
|
|
}
|
|
|
|
1;
|
|
}
|
|
|
|
# SwitchWorkDir
|
|
# Prepends the path to the filename
|
|
#
|
|
# Usage: &SwitchWorkDir(<SYSGENFILE>, <SYSGENDIR>);
|
|
# Input
|
|
# <filename>, <dir>
|
|
# Output
|
|
# <filename>
|
|
|
|
sub SwitchWorkDir{
|
|
|
|
my $logfile = shift;
|
|
my $workdir = shift;
|
|
$logfile = $workdir . "\\" . $logfile if $workdir;
|
|
$logfile;
|
|
}
|
|
|
|
# SysgenLogMsg
|
|
# Formats and prints messages like "Loading blah blah blah..."
|
|
#
|
|
# Input
|
|
# <message>
|
|
# Output
|
|
# <none>
|
|
|
|
sub SysgenLogMsg{
|
|
|
|
my $msg = shift;
|
|
my $chomp = shift;
|
|
|
|
$msg =~ s/\s\\([^\\]+)\b/ $1/g if $chomp;
|
|
$msg =~ s/\s\b\S+\\([^\\]+)\b/ $1/g if $chomp;
|
|
$msg =~ s/\.+\\//g if $chomp;
|
|
$msg = &RevEnvVars($msg);
|
|
|
|
$SYNTAX_CHECK_ONLY or print "$msg\n";
|
|
|
|
}
|
|
|
|
|
|
# homemade
|
|
# ...
|
|
# pattern
|
|
# matcher
|
|
#
|
|
#
|
|
# &match3dot("D:\\ntt\\private\\...\\wmi", "D:\\ntt\\private\\sergueik_dev\\tools\\wmi\\perl");
|
|
# &match3dot("D:\\ntt\\private", "D:\\ntt\\private\\sergueik_dev\\tools\\wmi\\perl");
|
|
# !&match3dot("D:\\ntt\\private\\sergueik_dev2", "D:\\ntt\\private\\sergueik_dev\\tools\\wmi\\perl");
|
|
|
|
#
|
|
# JeremyD suggests (not yet implemented)
|
|
#
|
|
#
|
|
# $regex = quotemeta($a);
|
|
# $regex =~ s/\\\.\\\.\\\./\(\.\*\)/g;
|
|
#
|
|
# @list = $b =~ /$regex/;
|
|
#
|
|
#
|
|
#
|
|
|
|
sub match3dot{
|
|
|
|
my ($vector, $path) = @_;
|
|
my $left;
|
|
my $right;
|
|
my $res = 0;
|
|
foreach my $pattern (grep {/\S/} keys(%$vector)){
|
|
if ($pattern =~ /(.+)\.\.\.(.+)/ ){
|
|
$left = $1;
|
|
$right = $2;
|
|
if ($path =~ /^\Q$left\E(.+)?\Q$right\E\b\\?/) {$res = 1;}
|
|
}
|
|
else{
|
|
if ($path =~ /^\Q$pattern\E\b\\?/) {$res = 1;}
|
|
}
|
|
}
|
|
|
|
$res and &SysgenLogMsg("Ignore ". &RevEnvVars($path),0);
|
|
$res;
|
|
}
|
|
|
|
|
|
# AddFiles
|
|
# Parses TFILES and TBINDIRS and adds entries to REPOSITORY.
|
|
# Input & Output <none>
|
|
# Usage
|
|
# &AddFiles();
|
|
|
|
sub AddFiles {
|
|
|
|
|
|
# Current element of TBINDIRS ( reference )
|
|
my $recref;
|
|
|
|
# EXCLUDE_DIRECTORIES
|
|
#
|
|
foreach my $ExcludeDir (split(/\s*;\s*/, &GetMacro("EXCLUDE_DIRECTORIES"))){
|
|
$ExcludeDir=~ s+\*\.+\.+g;
|
|
$EXCLUDE_DIRECTORIES->{lc($ExcludeDir)} = 1;
|
|
}
|
|
# my $DEBUG = 1;
|
|
# $DEBUG and
|
|
# print STDERR "exclude dir\(s\):\n",
|
|
# join( "\n", keys(%$EXCLUDE_DIRECTORIES)),
|
|
# "\n\n";
|
|
|
|
# EXCLUDE_EXTENSIONS
|
|
#
|
|
|
|
foreach my $ExcludeExtension (split(/\s*;\s*/, &GetMacro("EXCLUDE_EXTENSION"))){
|
|
$ExcludeExtension=~ s+\*\.+\.+g;
|
|
$EXCLUDE_EXTENSION{lc($ExcludeExtension)} = 1;
|
|
}
|
|
# $DEBUG and
|
|
# print STDERR "exclude extension\(s\):\n",
|
|
# join( "\n", keys(%EXCLUDE_EXTENSION)),
|
|
# "\n\n";
|
|
|
|
# Current directory contents
|
|
my @files;
|
|
|
|
# Indexes
|
|
my($i, $j, $dir);
|
|
|
|
&SysgenLogMsg("Adding file data...",0);
|
|
|
|
# Add the files listed in TFILES to REPOSITORY
|
|
map {&AddEntry( $_, 1 )} @TFILES;
|
|
|
|
my %TFILE_DIRS = ();
|
|
my $TFILE_DIRS = \%TFILE_DIRS;
|
|
|
|
map {$TFILE_DIRS->{lc($_->{"SrcPath"})} = lc($_->{"SrcFile"})} @TFILES;
|
|
my $TFILERE = join "|", map {lc($_->{"SrcFile"})} @TFILES ; # files that may cause trouble
|
|
|
|
# BUG only one file per directory is possible here!
|
|
# TODO : put the $TFILERE stuff into the value of %$TFILE_DIRS
|
|
|
|
map {delete($EXCLUDE_DIRECTORIES->{$_})} grep {/^\s*$/}keys(%$EXCLUDE_DIRECTORIES);
|
|
# BUG BUG
|
|
# $EXCLUDE_DIRECTORIES=xxx; <space> is wrongly handled by the parser.
|
|
# <space> becomes the key in %$EXCLUDE_DIRECTORIES!
|
|
|
|
# Add the files found in the directories stored by TBINDIRS
|
|
for ( $i=0; $i < @TBINDIRS; $i++ ) {
|
|
|
|
$recref = $TBINDIRS[$i];
|
|
|
|
next if &match3dot($EXCLUDE_DIRECTORIES, lc($recref->{SrcPath}));
|
|
|
|
# Load all files from the SrcPath directory
|
|
|
|
&SysgenLogMsg("\t$recref->{SrcPath}",0);
|
|
|
|
# Add one entry in REPOSITORY for each file found at SrcPath
|
|
|
|
my $aparent = lc($recref->{"SrcPath"});
|
|
my @afiles = ();
|
|
|
|
if (opendir(APARENT, $aparent)){
|
|
@afiles = grep { /[^\.]/ && -f "$aparent/$_" } readdir (APARENT);
|
|
closedir(APARENT);
|
|
}
|
|
|
|
foreach my $afile (@afiles) {
|
|
|
|
$recref->{SrcFile} = $afile;
|
|
$recref->{DestFile} = $afile;
|
|
|
|
# avoid doubly mapped files across FILES and BINDIRS sections.
|
|
if ($afile =~ /$TFILERE/io ){
|
|
# &dbgmsg("OVERLAP :$aparent\\$afile ($TFILE_DIRS->{$aparent})");
|
|
next if $TFILE_DIRS->{$aparent} eq lc($afile);
|
|
# use Jarod code for the proper pattern here ?
|
|
}
|
|
# next if $TFILE_DIRS->{$aparent} eq lc($afile);
|
|
|
|
|
|
|
|
# Call AddEntry with 0, to inhibit the file existence checking,
|
|
# as SrcFile is a result of a dir command (see above).
|
|
|
|
$afile =~ s/.*\.([\$\w]+)\s*$/.\L$1\E/;
|
|
|
|
# convert source file extension to lower case.
|
|
$EXCLUDE_EXTENSION{$afile} or &AddEntry( $recref, 0);
|
|
|
|
} # for
|
|
|
|
} # for
|
|
|
|
@TBINDIRS = ();
|
|
|
|
return;
|
|
|
|
} # AddFiles
|
|
|
|
|
|
|
|
# usage :
|
|
# &testCklang();
|
|
#
|
|
#
|
|
sub testCklang{
|
|
my @a = (scalar(@ARGV)) ? (map {$_} @ARGV) : ("\@EU;GER", "GER");
|
|
@a = (scalar(@ARGV)) ? (map {$_} @ARGV) : ("ALL;~GER", "GER");
|
|
print STDERR join("\t",
|
|
$a[0],
|
|
$a[1],
|
|
$ANSW->{&cklang::CkLang($a[1], $a[0] )});
|
|
|
|
} # testCklang
|
|
|
|
|
|
#
|
|
# filter_regex
|
|
# Builds a regular expression that will match the relative path of
|
|
# service pack files. Uses spfiles.txt for the file specifications.
|
|
# Codes.txt has the languages that may be used as ALT_PROJECT_TARGET.
|
|
# And there's a hard-coded pattern for sku, wow, coverage, etc
|
|
# directories.
|
|
#
|
|
# Takes no paramaters, return value is a regular expression suitable
|
|
# for use with qr//, throws a fatal error if spfiles.txt is not
|
|
# available. Many things that probably should be fatal are treated
|
|
# as warnings right now.
|
|
# [jtolman]
|
|
#
|
|
|
|
sub filter_regex {
|
|
|
|
print STDERR "Building filtering regular expression\n";
|
|
|
|
my $start_re = "(?:\Q$ENV{_NTPOSTBLD}\E\\\\)";
|
|
my $variations_re = '(?:(?:covinf\\\\)?...inf|lang|wow6432|pre(?:-bbt|rebase))';
|
|
|
|
|
|
my @file_patterns;
|
|
my $sp_file = "$ENV{_NTPOSTBLD}\\..\\build_logs\\files.txt";
|
|
print STDERR "$sp_file\n";
|
|
open SP, $sp_file or die "sp file list open failed: $!";
|
|
while (<SP>) {
|
|
chomp;
|
|
s/;.*$//; # first strip comments
|
|
next if /^\s*$/; # then skip blank lines
|
|
|
|
my ($tag, $file) = /^(\S*)\s*(\S+)$/;
|
|
if (!$file) {
|
|
print STDERR "WARNING: Failed to parse line: $_ ($tag - $file)\n";
|
|
next;
|
|
}
|
|
|
|
if ($tag =~ /d/) {
|
|
next;
|
|
}
|
|
|
|
if ($file =~ /^(.+)\Q\...\E$/) {
|
|
my $dir = $1;
|
|
push @file_patterns, "\Q$dir\E\\\\.+";
|
|
next;
|
|
}
|
|
elsif ($file =~ /^(.+)\Q\*\E$/) {
|
|
my $dir = $1;
|
|
push @file_patterns, "\Q$dir\E\\\\[^\\\\]+";
|
|
next;
|
|
}
|
|
else {
|
|
push @file_patterns, "\Q$file\E";
|
|
next;
|
|
}
|
|
}
|
|
close SP;
|
|
|
|
my $files_re = '(?:' . join('|', @file_patterns) . ')';
|
|
|
|
my $filter_re =
|
|
qr/$start_re(?:$variations_re\\)?$files_re/io;
|
|
|
|
return $filter_re;
|
|
}
|
|
|
|
|
|
__END__
|
|
|
|
=head1 NAME
|
|
|
|
SYSGEN - Aggregation driver
|
|
|
|
Aggregation (also termed sysgen) is the important postbuild step for the international
|
|
Whistler builds. In fact, it is the very first step executed by the postbuild and
|
|
is specific to the international Whistler builds. International build is recognized by
|
|
the %LANG% environment being defined and different from the "USA" (the default).
|
|
|
|
|
|
Sysgen consists of creating the file structure in the %_NTPOSTBLD% environment which
|
|
is to be in some way identical to the %_NTTREE% one but which content represents the
|
|
blend of US and localized files. The identity between the US and %LANG% file
|
|
structure guarantees the subsequent build steps do not differ between the languages.
|
|
|
|
This equivalence is achieved by applying appropriate file operations to certain files.
|
|
From the sysgen point of view, there is few choices to take when producing the
|
|
desired bits:
|
|
|
|
* use the (copy of) US and %LANG% pre-build ones
|
|
* create an appropriate merge between US files and %LANG% resources
|
|
|
|
|
|
In most typical run, which is the full rebuild, sysgen starts with the empty
|
|
%_NTPOSTBLD% and ends with the makefile appropriate to build the full file tree.
|
|
The originals of the US tree are never destroyed for the sake of reusability. In
|
|
the incremental run, the task solved by sysgen is rather complicated: none but
|
|
certain (changed) files are touched,to preserve the build's timestamps. Also, for a
|
|
reasonably small number of files changed the incremental run was thought to consume
|
|
far less time, than the full run, and for some time it was, indeed, true.
|
|
|
|
Lately, the investigation for advanced aggregation strategy has been taken. This
|
|
was mainly due to poor aggregation timings, but also due to possible future transition
|
|
to the build scenario, in which some or even all the assumptions the aggregation
|
|
relies upon, may change.
|
|
|
|
Let's take the bird eye view on the task aggregation solves.
|
|
|
|
Sure, most of the files in the %LANG% build would eventually be identical to the US
|
|
ones. Some, noticeably the text files will be totally different from the US but
|
|
just identical to the ones found in localizer's drop.
|
|
|
|
Some (noticeably executable) bits are specific: most resources are language
|
|
specific and thus, not interchangeable. These files never existed before the
|
|
aggregation took place.
|
|
|
|
So the main and only task of aggregation is to decide, how to build the file tree.
|
|
|
|
It is also becoming important, the procedure must be as efficient and easily
|
|
configurable as possible. The least important, even seldomly stated clearly,
|
|
goal is to make the internals of the sysgen script simple to maintain and modify,
|
|
template compliant etc. etc.
|
|
|
|
This document describes certain features the sysgen is capable, along with certain
|
|
file specifications, focusing on the recent features. For the introduction to sysgen
|
|
and aggregation principles, see http://ntbld/whistler/intl/sysgen.htm and other
|
|
documents on NT Whistler International build web site http://ntbld/intl
|
|
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
perl sysgen.pl [<options>] [<macros>] [<targets>]
|
|
|
|
where
|
|
|
|
Options:
|
|
|
|
/c generate the makefile from scratch, overwriting existing one.
|
|
By running nmake, all the targets will be generated.
|
|
/s limit sysgen to syntax check - makefile not (re)generated.
|
|
/f <name> takes <name> as the sysfile.
|
|
If this option is omitted, sysgen searches the current
|
|
directory for a file called sysfile and uses it as a
|
|
description (mapping) file.
|
|
/w <name> takes <name> as the PATH for reading/writing
|
|
makefile and err/log files. Note that the default
|
|
aggregation 'makefile' name is sysgen.mak
|
|
/v display version
|
|
/l:<LANG> specify language
|
|
/?
|
|
/h display this message
|
|
|
|
Macros:
|
|
list the command line macro definitions in the format
|
|
"<macroname>=<macrovalue>". Seldomly used
|
|
|
|
Targets:
|
|
specify files (without path) to localize/aggregate.
|
|
|
|
You may use the TEMPLATE COMPLIANT
|
|
|
|
-<flag>:<value>
|
|
|
|
syntax or OLD sysgen
|
|
|
|
-<flag> <value>
|
|
|
|
syntax or both. There is little need to specify any option but the language
|
|
on command line. The only one case you may need it is when you verify the mappings.
|
|
In this case you will have to type
|
|
|
|
perl sysgen.pl -l:<LANG> -c [-a] [-s]
|
|
|
|
The working directory of sysgen.pl is now arbitrary, and one is able to specify
|
|
|
|
* mappings file
|
|
* output folder
|
|
* language
|
|
|
|
via command line switches. In the past, it was crucial to change to the directory
|
|
|
|
%RAZZLETOOLPATH%\POSTBUILDSCRIPTS\SYSGEN\RELBINS\%LANG%
|
|
|
|
in order to have sysgen running. Not anymore! Once again, this means one can
|
|
execute multiple language and architecture aggregations on the same box at once.
|
|
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
Historically, there are four different ways the sysgen.pl can execute.
|
|
|
|
One of these is totally obsolete now, and one is not of practical use by the time of
|
|
this writing. So only two modes you are to learn although all four are still available.
|
|
|
|
Below is the list.
|
|
|
|
* acceletared aggregation
|
|
* full aggregation
|
|
* powerless syntax check (no aggregation)
|
|
* old incremental aggregation
|
|
|
|
|
|
The command line flags specify the desired mode, the default is (still!) being full
|
|
aggregation, without acceletarion. That corresponds to the incremental postbuild.
|
|
|
|
The accelerated aggregation is the fastest mode and corresponds to full postbuild.
|
|
It may seem strange but it is not the default mode yet.
|
|
|
|
The syntax check sysgen provides the quickest way to test the validity of the mappings
|
|
and produces no output, garbages no screen, but detects all errors that would hurt
|
|
the real aggregation, but cannot be used for anything else.
|
|
You may never encounter any need in powerless syntax check. The sysgen
|
|
is never executed in this way by the real postbuild.
|
|
|
|
Last, the old incremental aggregation is totally obsolete by now, though still
|
|
accessible. As a matter of fact, it does not give any time savings, neither is it more
|
|
reliable that the rest of the modes.
|
|
|
|
Alas, have you dropped the command line arguments completely, the incremental
|
|
aggregation would be fired. It will end up almost immediately, though, since it
|
|
relies on the presence of the makefile (filename is sysgen.mak) in the current working
|
|
directory, which hardly will be the case.
|
|
|
|
Admittedly, accelerated aggregation is approximately three times quicker than the
|
|
non-accel one. It may become the default by the time of your reading this.
|
|
The accelerated aggregation is a part of a full postbuild.
|
|
|
|
|
|
=head1 MAKEFILE
|
|
|
|
Generation of the files in the %_NTPOSTBLD% is accomplished through executing the
|
|
nmake.exe with the Makefile, described below. The default location of the Makefile is
|
|
%TEMP%\%LANG% and the default name is sysgen.mak. After the successful aggregation the
|
|
makefile is copied to the %_NTPOSTBLD%\%LANG%\BUILD_LOGS folder.
|
|
|
|
|
|
Sure the sysgen.mak represents a syntaxically correct makefile.
|
|
However, the aggregation makefiles are seldom used more than once by the current
|
|
design. It is more important to have is a useful for a post-break post-mortem
|
|
study.
|
|
|
|
The Makefile shown below corresponds to version 3.0x and later of SYSGEN.
|
|
[change #52 in //depot/private/intlred/tools/PostBuildScripts/sysgen.pl]
|
|
It is not syntaxically different from the older versions, though certain effort
|
|
has been applied to make it a little bit slick and e.g. easy to browse.
|
|
|
|
For example, the total size of the makefile (adding all the COMPDIR listing files,
|
|
to be honest) is around 1 Megabyte by now. Compare it with the typical makefile of
|
|
the early versions of the sysgen (i.e. before the COMPDIR was first used, let's use
|
|
the term version 3.0x).
|
|
|
|
The makefile for the same build weighted up to and over 10 Megabytes.
|
|
|
|
|
|
It was virtually impossible to diff the makefiles between the consequent builds, or
|
|
languages. This made the typical investigation for the origin of certain files more
|
|
difficult.
|
|
|
|
|
|
|
|
The sysgen.mak file:
|
|
|
|
-------- top of the file -----------------------------------
|
|
sysgen:
|
|
@nmake /A /K /F d:\lp.temp.x86fre\GER\sysgen.mak /NOLOGO all
|
|
|
|
------- Definition of the target to build ------------------
|
|
|
|
!IFNDEF LANG
|
|
! ERROR You must define macro LANG
|
|
!ENDIF
|
|
|
|
!IF "$(LANG)" != "GER" && "$(LANG)" != "ger"
|
|
! ERROR This file is for GER
|
|
!ENDIF
|
|
|
|
!IF "$(_BUILDARCH)" != "x86"
|
|
! ERROR This file is for x86
|
|
!ENDIF
|
|
|
|
------ header: lagguage and arch definitions ---------------
|
|
|
|
# Directory name aliases
|
|
_NTLOCDIR=$(_NTBINDIR)\loc\res\$(LANG)
|
|
|
|
# Binary file operation aliases
|
|
COMPDIR=compdir.exe /ukerlntsd
|
|
BINGEN=bingen -n -w -v -f
|
|
# Note: one must omit the extension here
|
|
# to enable the logerr 'magic'
|
|
COPY=copy
|
|
|
|
------- varialbes definitions ------------------------------
|
|
.SILENT:
|
|
|
|
all: \
|
|
_DIRS _COMPDIR PROCESS1601 PROCESS1602 PROCESS1603 .... PROCESS1616
|
|
|
|
------- pseudotarget expansion definitions -----------------
|
|
|
|
PROCESS1601: \
|
|
$(_NTPOSTBLD)\192.dns \
|
|
$(_NTPOSTBLD)\31x5hc01.cnt \
|
|
$(_NTPOSTBLD)\31x5hs01.cnt \
|
|
|
|
------- target make rules ----------------------------------
|
|
|
|
_DIRS:
|
|
md $(_NTPOSTBLD)\dtcinf 2>NUL
|
|
...
|
|
_COMPDIR: \
|
|
$(TEMP)\COMPDIR\compdir.001.mak \
|
|
...
|
|
$(TEMP)\COMPDIR\compdir.735.mak
|
|
logerr "$(COMPDIR) /m:$(TEMP)\COMPDIR\compdir.001.mak $(_NTTREE) $(_NTPOSTBLD)"
|
|
...
|
|
|
|
$(_NTPOSTBLD)\192.dns: $(_NTLOCDIR)\windows\misc\192.dns
|
|
logerr "$(COPY) $(_NTLOCDIR)\windows\misc\192.dns $(_NTPOSTBLD)\"
|
|
...
|
|
$(_NTPOSTBLD)\wow6432\wowreg32.exe: $(_NTTREE)\wow6432\wowreg32.exe \
|
|
$(_NTLOCDIR)\windows\tokens\wow6432\wowreg32.ex_
|
|
logerr "$(BINGEN) -p 1252 -o 0x07 0x01 -r $(_NTTREE)\wow6432\wowreg32.exe..."
|
|
|
|
|
|
|
|
------- end of Makefile -----------------------------------
|
|
|
|
=head1 MAKING LINKS
|
|
|
|
The distinctive feature of version 3.0x of aggregation is using the compdir.exe
|
|
to arrange hard links for the aggregation rather then perform file-by-file copy.
|
|
|
|
It has not been investigated too rigorously, whether the speedup was achieved
|
|
because of
|
|
|
|
* reducing the number of steps executed due to
|
|
grouping the files by directory (~10000 file ops versus ~100 lists ops)
|
|
* generation of links instead of copying the files
|
|
|
|
Basically, it is possible to investigate: compdir's rich set of flags allows one.
|
|
|
|
In fact, no explicit command name is used by current makefile, but only macros.
|
|
The COPY, COMPDIR, BINGEN macros definitiond are found in sysfile.inc:
|
|
|
|
SET COMPDIR=compdir.exe /ukerlntsd
|
|
SET COPY=copy
|
|
SET BINGEN=bingen -n -w -v -f
|
|
|
|
Now, getting actual command changed is a matter of macro definition. No need to edit
|
|
a letter in the aggregation script.
|
|
|
|
Now, the COPY and COMPDIR macros operations are both present. Why not removing
|
|
the COPY completely? The next section explains, why.
|
|
|
|
|
|
=head1 OPTIMIZATION
|
|
|
|
Where are the time savings coming from? The statistics shows that there are as many as
|
|
|
|
35350 files in
|
|
750 directories.
|
|
|
|
handled now by compdir.exe and this saves up to 60% of former execution time.
|
|
|
|
At present there are ~250 'explicit' copy operations left for GER aggregation,
|
|
and almost ~1500 copy operations for ARA aggregation.
|
|
|
|
The reason for such a difference is that rsrc compiler was discovered to be sensitive
|
|
to whether the resource it operates is a link or separate copy. The generic
|
|
rsrc build instruction involves both %_NTTREE% and %_NTPOSTBLD% libraries.
|
|
Too bad if they are links to each other: rsrc will simply fail to do the job.
|
|
|
|
The problem is, sysgen does not really seem to trace the inter-file relations
|
|
well enough. This may be re-designed in the future versions. To put is short,
|
|
there is no easy way to identify the rsrc files from the rest when
|
|
looking 'from inside' of the sysgen.
|
|
|
|
The approach chosen was based on a minimal tradeoff between the execution acceleration
|
|
and complexities introduced.
|
|
|
|
The default policy of sysgen is always to take COPY, not COMPDIR approach.
|
|
By the way, the COMPDIR approach means here not just applying the compdir.exe with
|
|
certain flags (as we have seen this task is trivial and may be achieved by re-defining
|
|
the COPY macro in the sysfile.inc) but rather complex:
|
|
generating some file hash info for subsequent grouping the files by source/destination
|
|
directories etc. etc.
|
|
|
|
First, sysgen does a clever guess about what is the file copying serve for. This means,
|
|
it is able to factor out files going alone %_NTTREE%->%_NTPOSTBLD% trails,
|
|
and lets compdir.exe do that. This gives the major contribution:
|
|
|
|
28002 files
|
|
650 directories.
|
|
|
|
which is almost 80% counted in file numers, not times.
|
|
|
|
|
|
However, files which propagate between %_NTLOCDIR% and %_NTPOSTBLD% do not provide the
|
|
possibility for file name, path based clever guess. This corresponds to the whole
|
|
localizer tree and leaves the problem open.
|
|
|
|
To teach sysgen to select files, that are safe passing to COMPDIR, is a complex task.
|
|
The solution is rely upon the simplest test to enforce COMPDIR handling for
|
|
some files and allow COPY the rest. The more bulk of files get handled the better.
|
|
Thus file operation may be determined by the file EXTENSION.
|
|
|
|
Sysgen simply reads extension hints in the mapping files to know the file is
|
|
safe to pass to COMPDIR. No hints - no risky optimization. Old behavior restored.
|
|
|
|
|
|
In future design, the logic of the sysgen may be refined. However, at
|
|
present state of the optimization, there is already 10 to 1 reduction of the
|
|
file ops and 3 to 1 reduction in time. It is clearly hard to beleive the next factor
|
|
3 acceleration possible.
|
|
|
|
=head1 MAPPING FILES
|
|
|
|
Sysgen reads mapping files for variable and build tree directory structure
|
|
definitions. Here we concentrate on mapping file paths.
|
|
|
|
For the versions of sysgen prior and including 3.0x, the directory
|
|
occupued by mapping file 'sysfile' was in fact unique for each
|
|
%LANG% and fixed. This introduced redundancy between the mapping files and
|
|
leaded to maintenance complexity.
|
|
|
|
For compatibility reasons, this structure is still present in the SD, the
|
|
parses able to work with it, etc.
|
|
|
|
Starting with version 4.0x of sysgen, the layout of mapping files has
|
|
been simplified to make the aggregation more flexible and robust and to enable
|
|
more changes without the need to fix the Perl code.
|
|
|
|
Of course, necessary functionality has been implemented to help factoring out
|
|
apparently redundant definitions.
|
|
|
|
In particular, mapping file paths and names are no longer hardcoded, e.g.
|
|
sysfile location may be specified to the sysgen via a command
|
|
line 'f' argument, while makefile location via the 'w' argument. No arguments
|
|
are mandatory and default to current directory. Also, this leads to ability of
|
|
running multiple language and architecture aggregations on the same box at once.
|
|
|
|
The include tree of the mapping files is schematically displayed below.
|
|
|
|
sysgen.pl
|
|
|
|
* sysfile.inc
|
|
|
|
* hotfix.inc
|
|
* scp.inc
|
|
* mapfix.inc
|
|
* iis.inc
|
|
* con.inc
|
|
* msmq.inc
|
|
* smtpnntp.inc
|
|
* netmeeting.inc
|
|
* indexsrv.inc
|
|
* fax.inc
|
|
* exch_se.inc
|
|
* bldbins.inc
|
|
* ntloc.inc
|
|
|
|
The number of mapping files exists is determined by the the number of projects.
|
|
See the
|
|
%_NTDRIVE%\LOC\RES\%LANG%
|
|
directory structure.
|
|
|
|
The bldbins.inc and ntloc.inc map
|
|
%_NTTREE%
|
|
and
|
|
%_NTDRIVE%\LOC\RES\%LANG%\WINDOWS
|
|
directories and their subdirectories, respectively.
|
|
|
|
The hotfix.inc file contains rules that are to be applied 'as is' and
|
|
otherwise confuse the sysgen [see HOTFIX FILE SYNTAX below].
|
|
|
|
The mapfix.inc file is reserved to the unapproved mapping fixes required e.g.
|
|
to fix bugs but less essential for the build to succeed. It may disappear in the
|
|
future versions of the sysgen.
|
|
|
|
Also, external drop mappings are planned to move in one mapping file.
|
|
|
|
In a more distant future, it is planned to have some convenient tool to wirk with
|
|
mapping files.
|
|
|
|
=head1 MAPPING FILE SYNTAX
|
|
|
|
The syntax of the mapping files did not change too much, to provide
|
|
backward compatibility with versions 3.0x abd below.
|
|
The descriprion may be found in http://ntbld/whistler/intl/Sysgen.htm
|
|
In fact, much effort has been applied to make the parser features stable.
|
|
|
|
=head1 HOTFIX FILE SYNTAX
|
|
|
|
The hotfix.inc file contains rules that are to be applied 'as is' and would
|
|
otherwise confuse the sysgen.
|
|
|
|
Sure, the syntax of the hotfix.inc is specific.
|
|
The reason for introducing the hotfix.inc file comes from the fact that the
|
|
assumption the aggregation is based upon:
|
|
the existence of one-to-one correspondence
|
|
between the token and its binary file, can sometimes be not true.
|
|
|
|
Rules written in the hotfix file apply exactly to such situations and
|
|
are to be considered patches to makefile,
|
|
but applied before passing the makefile to nmake.
|
|
|
|
|
|
|
|
Below you see the part of the hotfix.inc file
|
|
|
|
|
|
IF "$(LANG)" == "KOR"
|
|
|
|
BEGIN_RULES
|
|
|
|
SET MULTITOKDIR=$(_ntbindir)\loc\res\$(lang)\windows\tokens\multi
|
|
SET HOTFIXDIR=$(_ntbindir)\loc\res\tools\twisttok
|
|
SET DST=$(_ntpostbld)\$(lang)
|
|
# version 3.x
|
|
SET DST=$(_ntpostbld)
|
|
# version 4.x
|
|
SET SRC=$(_nttree)
|
|
SET ACP0=1252
|
|
SET ACP1=949
|
|
SET PriLangID0=0x09
|
|
set SubLangID0=0X01
|
|
SET PriLangID1=0x12
|
|
set SubLangID1=0X01
|
|
SET OVERWRITE_DEFAULT_RULE=1
|
|
|
|
$(dst)\netmsg.dll : $(src)\netmsg.dll $(multitokdir)\netmsg.dl_
|
|
logerr "echo bug 423602"
|
|
logerr "bingen -n -w -v -f -p $(ACP0) -o $(PriLangID0) ..."
|
|
logerr "bingen -n -w -v -f -p $(ACP1) -o $(PriLangID1) ..."
|
|
|
|
END_RULES
|
|
|
|
ENDIF
|
|
|
|
It is evident that the dependency block has a typical Makefile,
|
|
rather then sysgen mapping syntax.
|
|
|
|
Notice also the new BEGIN_RULES/END_RULES tags. These separate the hotfix section
|
|
from the rest of the file.
|
|
|
|
In fact, the contents of the mapping enclosed by these targs are not parsed at all.
|
|
|
|
|
|
=head1 OUTLOOK OF VERSION 4.0x VALUABLE FEATURES
|
|
|
|
1. Produce well-formed good-looking Makefile.
|
|
E.g. use of dotted and preprocessing nmake directives.
|
|
|
|
2. Fix the mapping file structure to ensure that keeping the mapping files
|
|
in the folders %RAZZLETOOLPATH%\postbuildscripts\sysgen\relbins\%LANG%
|
|
is no longer required to execute the aggregation.
|
|
E.g. provide language setting on command line
|
|
|
|
3. Extend the parser e.g. to recognize constructs:
|
|
|
|
IF "$(LANGUAGE)" == "INTL" || "$(LANGUAGE)" == "intl"
|
|
|
|
This is to mimic the makefile syntax (nmake):
|
|
|
|
!IF "$(LANGUAGE)" == "INTL" || "$(LANGUAGE)" == "intl"
|
|
|
|
|
|
Thus one can verify that given $LANGUAGE = CHT
|
|
|
|
&testCompoundIF("IF $(LANG) == CHT"); TRUE
|
|
&testCompoundIF("IF $(LANG) == CHS || ($(LANG) == CHT)"); TRUE
|
|
&testCompoundIF("IF $(LANG) == CHS"); FALSE
|
|
&testCompoundIF("IF ($(LANG) == CHT) && ($(LANG) == CHS) "); FALSE
|
|
&testCompoundIF("IF $(LANG) == chs"); FALSE
|
|
&testCompoundIF("IF \"\" == \"\" "); TRUE
|
|
|
|
|
|
This ability could be useful to factor out the repeated blocks in the mapping
|
|
files (currently underway)
|
|
|
|
4. Work on the aggregation code timings.
|
|
|
|
|
|
Profiling the script(full accelerated aggregation).
|
|
|
|
Total Elapsed Time = -10.7283 Seconds
|
|
User+System Time = 146.7325 Seconds
|
|
Exclusive Times
|
|
%Time ExclSec CumulS #Calls sec/call Csec/c Name
|
|
31.6 46.49 57.020 148164 0.0003 0.0004 main::MakeXcopyCmd
|
|
13.1 19.22 19.114 37423 0.0005 0.0005 main::NewImageToSymbol
|
|
12.5 18.43 32.083 1 18.435 32.083 main::AddFiles
|
|
9.52 13.97 12.928 348653 0.0000 0.0000 main::SetField
|
|
8.91 13.07 12.923 51459 0.0003 0.0003 main::RevEnvVars
|
|
5.89 8.642 95.413 35868 0.0002 0.0027 main::GenXcopy
|
|
4.47 6.554 6.120 145184 0.0000 0.0000 main::PushFieldVal
|
|
4.40 6.463 5.000 488408 0.0000 0.0000 main::GetMacro
|
|
4.21 6.174 19.067 1688 0.0037 0.0113 main::AddRecord
|
|
3.07 4.506 4.506 1 4.5060 4.5060 main::Find_UnMapping
|
|
2.40 3.516 10.984 47581 0.0001 0.0002 main::AddFileInfo
|
|
2.03 2.985 2.874 37423 0.0001 0.0001 main::GetDynData
|
|
1.64 2.412 13.357 47581 0.0001 0.0003 main::AddEntry
|
|
1.17 1.720 106.19 37423 0.0000 0.0028 main::RecordToCmd
|
|
1.08 1.581 1.317 88136 0.0000 0.0000 main::IsRepositKey
|
|
|
|
|
|
|
|
Profiling the script(syntax check).
|
|
|
|
Total Elapsed Time = -5.11500 Seconds
|
|
User+System Time = 42.05962 Seconds
|
|
Exclusive Times
|
|
%Time ExclSec CumulS #Calls sec/call Csec/c Name
|
|
44.5 18.73 31.126 1 18.730 31.126 main::AddFiles
|
|
19.5 8.220 7.372 243189 0.0000 0.0000 main::SetField
|
|
15.0 6.345 19.905 1688 0.0038 0.0118 main::AddRecord
|
|
11.1 4.706 4.706 1 4.7060 4.7060 main::Find_UnMapping
|
|
7.93 3.335 10.111 47581 0.0001 0.0002 main::AddFileInfo
|
|
5.14 2.161 12.138 47581 0.0000 0.0003 main::AddEntry
|
|
3.21 1.351 1.042 88136 0.0000 0.0000 main::IsRepositKey
|
|
2.49 1.048 1.914 37423 0.0000 0.0001 main::RecordToCmd
|
|
2.31 0.971 0.834 39011 0.0000 0.0000 main::GetFieldVal
|
|
1.57 0.660 0.398 74846 0.0000 0.0000 main::IsHashKey
|
|
1.31 0.550 0.378 49147 0.0000 0.0000 main::IsEmptyHash
|
|
1.14 0.480 2.394 1 0.4800 2.3940 main::FillCmd
|
|
1.11 0.468 1.410 1 0.4678 1.4103 main::FillTokens
|
|
0.78 0.330 0.193 39274 0.0000 0.0000 main::GetMacro
|
|
0.65 0.272 0.146 35868 0.0000 0.0000 main::GenXcopy
|
|
|
|
|
|
Clearly, the time consuming operations involve various file tests
|
|
which cannot be skipped.
|
|
|
|
=head1 LIMITATIONS
|
|
|
|
* the currently unused sysgen run modes are not being tested
|
|
too thoroughfully and may happen to fail or produce weird results
|
|
support for the obsolete modes of might terminate
|
|
|
|
* the syntax of the mapping files is archaic.
|
|
the parser cannot dynamically bias the expected to the actual
|
|
mapping file syntax (not all fields are used)
|
|
|
|
* aggregation toolset (sysgen.pl/locag.pl/mtnmake.cmd/sysfile etc.)
|
|
backward compatibility is slim
|
|
|
|
=head1 AUTHOR
|
|
|
|
Contact Serguei Kouzmine sergueik@microsoft.com
|
|
|
|
=head2 Notes
|
|
|
|
cmd.exe can live with forward slashed instead of bachward slashes
|
|
However, it needs to quote the filenames to have this working
|
|
|
|
dir /ad "c:/program files"
|
|
Volume in drive C has no label.
|
|
Volume Serial Number is 58F7-C69D
|
|
|
|
Directory of c:\program files
|
|
|
|
04/16/2002 11:10 AM <DIR> .
|
|
04/16/2002 11:10 AM <DIR> ..
|
|
...
|
|
|
|
1. hotfix:
|
|
make use of the "env" variables inherited from codes.txt
|
|
|
|
2. get meaningful names!
|
|
LANGUAGE => SRC_LANG
|
|
ALT_LANG => LANG
|
|
|
|
!IFNDEF LANG
|
|
! ERROR You must define macro LANG
|
|
!ENDIF
|
|
DEST=$(_NTPOSTBLD)\$(LANG)
|
|
!ERROR $(DEST)
|
|
|
|
D:\test\tools\PostBuildScripts\sysgen.pl : D:\test\tools\PostBuildScripts\sysgen.dep D:\test\tools\PostBuildScripts\sysgen.dep2
|
|
echo $@ " " $?
|
|
|
|
|
|
|
|
BEGIN_MESSAGES_MAP
|
|
|
|
#Code Description LANG
|
|
#---------------------------------------------------------------
|
|
1001 sysfile not found @EU;@CS;@FE
|
|
1002 file not found @EU;@CS;@FE
|
|
1003 file not found: unable to run sysgen incrementally @EU;@CS;@FE
|
|
1004 target file not found @EU;@CS;@FE
|
|
1005 filter file not found @EU;@CS;@FE
|
|
1006 unable to open file for reading @EU;@CS;@FE
|
|
1007 unable to open file for writing @EU;@CS;@FE
|
|
1008 /f option requires a filename @EU;@CS;@FE
|
|
1009 target not found in filter file @EU;@CS;@FE
|
|
1010 /w option requires a directory name @EU;@CS;@FE
|
|
1011 /n option requires number of sections@EU;@CS;@FE
|
|
1101 @EU;@CS;@FE
|
|
|
|
1110 syntax error @EU;@CS;@FE
|
|
1111 syntax error: ENDIF unexpected @EU;@CS;@FE
|
|
1112 syntax error: IF unexpected @EU;@CS;@FE
|
|
1113 syntax error: END_MAP unexpected @EU;@CS;@FE
|
|
1114 syntax error: BEGIN_*_MAP unexpected @EU;@CS;@FE
|
|
1115 syntax error: INCLUDE unexpected @EU;@CS;@FE
|
|
1116 syntax error: unknown operator in expression @EU;@CS;@FE
|
|
1117 syntax error: \")\" missing in macro invocation @EU;@CS;@FE
|
|
1118 syntax error: incomplete description line @EU;@CS;@FE
|
|
1119 syntax error: unknown mapping type @EU;@CS;@FE
|
|
1120 syntax error: unmatched IF @EU;@CS;@FE
|
|
1121 syntax error: unmatched BEGIN_*_MAP @EU;@CS;@FE
|
|
|
|
1210 file format error: target not found @EU;@CS;@FE
|
|
1211 file format error: target not listed in \"all\" @EU;@CS;@FE
|
|
1212 file format error: no description blocks for files @EU;@CS;@FE
|
|
1213 file format error: \"sysgen\" target not found @EU;@CS;@FE
|
|
1214 file format error: filename with special characters @EU;@CS;@FE
|
|
1215 file format error: Similar target found @EU;@CS;@FE
|
|
|
|
1910 unknown language @EU;@CS;@FE
|
|
1911 missing or duplicated entry for language @EU;@CS;@FE
|
|
1912 incomplete entry for language @EU;@CS;@FE
|
|
1913 unknown class for language @EU;@CS;@FE
|
|
|
|
2011 no binary found for token @EU;@CS;@FE
|
|
2012 duplicated tokens @EU;@CS;@FE
|
|
2013 unknown token type (bingen or rsrc) @EU;@CS;@FE
|
|
2014 unexpected token for already localized binary @EU;@CS;@FE
|
|
2015 input bingen token not found for multi @EU;@CS;@FE
|
|
2016 no bingen token found for custom resource @EU;@CS;@FE
|
|
2017 unknown bingen token extension in token filename @EU;@CS;@FE
|
|
2018 both bingen and rsrc tokens found @EU;@CS;@FE
|
|
2019 custom resource found for rsrc token @EU;@CS;@FE
|
|
2020 custom resource with no token @EU;@CS;@FE
|
|
|
|
2051 sysfile error: undefined source path to verify @EU;@CS;@FE
|
|
2052 folder not covered in sysfiles @EU;@CS;@FE
|
|
2053 not mapped @EU;@CS;@FE
|
|
2054 not mapped @EU;@CS;@FE
|
|
|
|
2101 binary not found @EU;@CS;@FE
|
|
2102 token not found @EU;@CS;@FE
|
|
|
|
3011 internal error: unknown operation type @EU;@CS;@FE
|
|
|
|
4001 filename with whitespace badly handled @EU;@CS;@FE
|
|
|
|
5001 _COMPDIR currently only supported in clean build @EU;@CS;@FE
|
|
5002 Incremental run with COMPDIR @EU;@CS;@FE
|
|
|
|
END_MAP
|
|
|
|
|
|
|
|
my $d = " 5002 Incremental run with COMPDIR \@EU;\@CS;\@FE";
|
|
($a, $b, $c) = $d =~ m/\s*([^ ]+)\s{4,}([^ ].+[^ ])\s{4,}([^ ]+)/;
|
|
print STDERR $a, "\n", $b, "\n", $c, "\n\n";
|
|
|
|
getting rid of
|
|
|
|
;[Lang] [ACP] [LCID] [PriLangID] [SubLangID] [Class] [Flavor] [Site] [LangISO] [PerfID] [Readme] [GUID] [Read1st] [Home] [Comments]
|
|
;-------------------------------------------------------------------------------------------------------------------------------------------
|
|
;
|
|
|
|
ARA 1256 0x0401 0x01 0x01 @CS WKS REDMOND AR 001 readme AF202818-350E-11d2-B167-0060B03C1CA5 read1st home Arabic
|
|
|
|
|
|
|
|
|
|
=head1 LOCSTUDIO BUILD HANDLING
|
|
|
|
|
|
=head2 OBSOLETE INSTRUCTIONS
|
|
|
|
Different ~classes~ of bingen/rsrc command must be appropriately mapped to LSBUILD.EXE
|
|
Sample INTL build commands (represent different execution flag sets for BINGEN and RSRC.):
|
|
|
|
1. BINGEN
|
|
1.1 replace resources (85%). Pseudorule:
|
|
|
|
$(BINGEN) -p $(ACP) -o $(PriLangID) $(SubLangID) -r $** $@
|
|
|
|
expands into:
|
|
|
|
bingen -n -w -v -f -p 932 -o 0x11 0x01 -r $(_NTTREE)\user32.dll $(_NTLOCDIR)\windows\tokens\user32.dl_ $(_NTPOSTBLD)\user32.dll
|
|
|
|
|
|
1.2. add the LANG resources and leave the US ones alone so that the target binary has both (~10%). Pseudorule:
|
|
|
|
|
|
$(BINGEN) -p $(ACP) -o $(PriLangID) $(SubLangID) -i $(PriLangUSA) $(SubLangUSA) -a $(_NTTREE)\aciniupd.exe $(_NTLOCDIR)\windows\tokens\multi\aciniupd.ex_ $@
|
|
|
|
expands into:
|
|
|
|
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
|
|
|
|
|
|
1.3. Multiple resources for a single binary, e.g. embedded resources such as INF files and XML files (1%)
|
|
|
|
|
|
$(_NTPOSTBLD)\photowiz.dll: $(_NTTREE)\photowiz.dll $(_NTLOCDIR)\windows\tokens\photowiz.dl_ \
|
|
$(_NTLOCDIR)\windows\tokens\photowiz_dll_reginst.inf \
|
|
$(_NTLOCDIR)\windows\tokens\photowiz_dll_tmpldata.xml
|
|
$(BINGEN) -p $(ACP) -o $(PriLangID) $(SubLangID) -r $(_NTTREE)\photowiz.dll $(_NTLOCDIR)\windows\tokens\photowiz.dl_ $@
|
|
|
|
|
|
|
|
2. BIDI specific resource manipulation command (RSRC)
|
|
|
|
2.1 Replace
|
|
|
|
rsrc $(_NTPOSTBLD)\w3isapi.dll -r $(_NTLOCDIR)\windows\tokens\w3isapi.dll.rsrc -l 411 (~4% - ALL LANGS)
|
|
|
|
2.2 Append
|
|
|
|
rsrc $(_NTPOSTBLD)\winnt32\winnt32.exe -a $(_NTLOCDIR)\windows\tokens\winnt32\multi\winnt32.exe.rsrc -l 40D (@CS specific)
|
|
|
|
|
|
2.3 Merge several LANG
|
|
|
|
$(_NTPOSTBLD)\comdlg32.dll: $(_NTTREE)\comdlg32.dll $(_NTLOCDIR)\windows\tokens\multi\comdlg32.dll.40d.rsrc $(_NTLOCDIR)\windows\tokens\multi\comdlg32.dll.80d.rsrc
|
|
rsrc $(_NTPOSTBLD)\comdlg32.dll -a $(_NTLOCDIR)\windows\tokens\multi\comdlg32.dll.40d.rsrc -l 0x040d (@CS specific exception )
|
|
|
|
|
|
=head1 MODIFICATIONS to aggregation logic and MAPPING files.
|
|
|
|
Add coverage to make sysgen aware of the .lcx files
|
|
|
|
=head1 Building the LSBUILD.EXE rules
|
|
|
|
=head2 1. When the lcx file is supplied, the .dl_ or .ex_ file must be deleted.
|
|
If it is not the aggregation is greately confused.
|
|
If both .dl_ token and .lcx tonken are found, the .dl_ one wins.
|
|
This is because of possible multiple resources for the binary case, discussed above.
|
|
|
|
=head2 2. Errors generated from missing .dl_ files:
|
|
|
|
SYSGEN: error 2102: d:\ls\loc\res\JPN\windows\tokens\dpcdll.dl_: token not found
|
|
|
|
This fix is delete the explicitt mapping line (scp.inc)
|
|
|
|
$(dst) dpcdll.dll $(tok) dpcdll.dl_ - -r - @EU;@FE
|
|
|
|
=head2 1. Errors from discovered .lcx files:
|
|
|
|
SYSGEN: error 2013: d:\ls\loc\res\JPN\netmeeting\tokens\nmevtmsg.dll.lcx: unknown token type (bingen or rsrc)
|
|
|
|
Verify the extensions are mapped in sysfile.inc:
|
|
|
|
Provide the dummy rule for .dll <= .dll.lcx
|
|
.exe <= .exe.lcx
|
|
|
|
BEGIN_EXTENSIONS_MAP
|
|
|
|
# TokExt BinExt Langs
|
|
#----------------------
|
|
.a_ .ax @EU;@FE;@CS
|
|
.ac_ .acm @EU;@FE;@CS
|
|
.ax_ .ax @EU;@FE;@CS
|
|
.bi_ .bin @EU;@FE;@CS
|
|
.cn_ .cnv @EU;@FE;@CS
|
|
.co_ .com @EU;@FE;@CS
|
|
.cp_ .cpl @EU;@FE;@CS
|
|
.dl_ .dll @EU;@FE;@CS
|
|
.dr_ .drv @EU;@FE;@CS
|
|
.ds_ .ds @EU;@FE;@CS
|
|
.ef_ .efi @EU;@FE;@CS
|
|
.ex_ .exe @EU;@FE;@CS
|
|
.fl_ .flt @EU;@FE;@CS
|
|
.im_ .ime @EU;@FE;@CS
|
|
.mo_ .mod @EU;@FE;@CS
|
|
.ms_ .mst @EU;@FE;@CS
|
|
.oc_ .ocx @EU;@FE;@CS
|
|
.rs_ .rsc @EU;@FE;@CS
|
|
.sc_ .scr @EU;@FE;@CS
|
|
.sy_ .sys @EU;@FE;@CS
|
|
.tl_ .tlb @EU;@FE;@CS
|
|
.ts_ .tsp @EU;@FE;@CS
|
|
.wp_ .wpc @EU;@FE;@CS
|
|
|
|
|
|
.sys.rsrc .sys @EU;@CS;@FE
|
|
.dll.rsrc .dll @EU;@CS;@FE
|
|
.exe.rsrc .exe @EU;@CS;@FE
|
|
.drv.rsrc .drv @EU;@CS;@FE
|
|
.acm.rsrc .acm @EU;@CS;@FE
|
|
.cpl.rsrc .cpl @EU;@CS;@FE
|
|
.scr.rsrc .scr @EU;@CS;@FE
|
|
|
|
.dll.lcx .dll @EU;@FE;@CS
|
|
.exe.lcx .exe @EU;@FE;@CS
|
|
|
|
END_MAP
|
|
|
|
|
|
===
|
|
print "reducing reduntant LCX mapping patterns:\n";
|
|
$a="(\\.\\w+)\\.lcx";
|
|
$b="\$1";
|
|
$p = "file.foo.lcx";
|
|
print $p,"\n";
|
|
print "$a $b\n";
|
|
print qq("$a $b"), "\n";
|
|
$b =~ s|\$(\d)|$1|g;
|
|
|
|
|
|
$p =~ s|$a|${$b}|egi;
|
|
print $p,"\n";
|
|
$p = "file.BAR.lcx";
|
|
print $p,"\n";
|
|
$p =~ s|(\.\w+)\.lcx$|$1|eg;
|
|
print $p,"\n";
|
|
|
|
|