#----------------------------------------------------------------// # Script: copylang.pl # # (c) 2000 Microsoft Corporation. All rights reserved. # # Purpose: This script copy the files between Source Tree and # Loc Tree listed in $ENV{RazzleToolPath}\intlsrc.txt. # # Version: <1.00> 06/30/2000 : Suemiao Rossognol #----------------------------------------------------------------// ###-----Set current script Name/Version.----------------// package copylang; $VERSION = '1.10'; $ENV{script_name} = 'copylang.pl'; ###-----Require section and extern modual.---------------// require 5.003; use strict; use lib $ENV{ "RazzleToolPath" }; use lib $ENV{ "RazzleToolPath" } . "\\PostBuildScripts"; no strict 'vars'; no strict 'subs'; no strict 'refs'; use File::Basename; use GetParams; use Logmsg; use cklang; use cktarg; use HashText; use comlib; use vars (qw($DEBUG)); %hashCodes=(); &HashText::Read_Text_Hash( 0, $ENV{RazzleToolPath}."\\Codes.txt", \%hashCodes ); # Delete everything that's not international delete $hashCodes{RO}; delete $hashCodes{CA}; delete $hashCodes{CHP}; delete $hashCodes{PSU}; delete $hashCodes{MIR}; delete $hashCodes{TST}; my ( $infsSrcTree, $unfsLocTree, $isNtsetup ); my ( $syncOnly, $syncTime ); #------------------------------------------------------------------// #Function: main #Parameter: (1) Language # (2) Root of the Source Tree # (3) Root of the Localized Tree # (4) Incremental Flag # (5) Powerless Flag #------------------------------------------------------------------// my $EXECUTE = { "USA" => \&CopyUSAToLocalized , "PSU" => \&GenNoOp , "MIR" => \&GenNoOp , "FE" => \&GenNoOp , "-" => \&CopyLocToBuild }; sub CopyUSAToLocalized{ } sub main { my ( $pLang, $pSrcRoot, $pLocRoot, $pIsIncr, $pIsPowerless ) = @_; if( $pLang eq "USA" ) { $theFromTree = "SourceTree"; $theFromRoot = $pSrcRoot; $theFromFile = "SourceFilename"; $theToTree = "LocTree"; $theToRoot = $pLocRoot; $theToFile = "LocTreeFilename"; }else { $theFromTree = "LocTree"; $theFromRoot = $pLocRoot; $theFromFile = "LocTreeFilename"; $theToTree = "SourceTree"; $theToRoot = $pSrcRoot; $theToFile = "SourceFilename"; } # my $OldLang = "-" if cklang::FieldByName($pLang, "OLD_LANG") and !defined( $EXECUTE->{$pLang}); # my $status = (defined( $EXECUTE->{$OldLang})) ? # $EXECUTE->{$OldLang}->() : -1; # pending ###---Set xcopy flag.--------------------------------// my $copyOpt = "/V /F /R /Y"; if( $pIsPowerless ) { $copyOpt .= " /L /D"; } else { if( $pIsIncr ){ $copyOpt .= " /D"; } } ###---Get Hash value from intlsrc.txt file.----------// my @srcHash=(); &HashText::Read_Text_Hash( 1, "$ENV{\"RazzleToolPath\"}\\intlsrc.txt", \@srcHash ); @theHashKey = ("Target", "SourceFilename", "SourceTree", "LocTree","LocTreeFilename" , "Lang", "Comments"); %tmp=(); my @filtered = (); for $line( @srcHash){ &logmsg("Skipping " . $line->{ "LocTree"} . "\\". $line->{ "LocTreeFilename" } . " for ". $pLang ) and next if $line->{ "Lang" } =~ /\~$pLang/i; push @filtered, $line; } $#srcHash= -1; push @srcHash, @filtered; # poor man fix of the language filter for $line( @srcHash) { for $curKey ( @theHashKey ) { if( $line->{ $curKey } =~ /^(.*)(\$\(LANG\))(.*)$/ ) { $line->{ $curKey } = $1 . lc($pLang) .$3; } if( $line->{ $curKey } =~ /^(.*)([c|h])(\$\(PRIMARY_LANG_ID\))(.*)$/ ) { if( $pLang eq "CHT" || $pLang eq "CHS" ) { $LCID = substr( $hashCodes{uc($pLang)}->{LCID}, 2, length($hashCodes{uc($pLang)}->{LCID})-2); $line->{ $curKey } = "prf" . $2 . $LCID .$4; } else { $priLangID = "0". substr( $hashCodes{uc($pLang)}->{PriLangID}, 2, length($hashCodes{uc($pLang)}->{PriLangID})-2); $line->{ $curKey } = $1 . $2. $priLangID .$4; } } } $to = $theToRoot."\\". $line->{ $theToTree }; if( !exists $tmp{$to} ){ $tmp{$to}=(); } } if( $pLang eq "USA" ) { for( keys %tmp ) { &CkCleanDir( $_, $pIsIncr, $pIsPowerless ); } } ###---Perform Copy now.------------------------------// &dbgmsg("Read targets from intlsrc.txt: \n"); for $line ( @srcHash ) { &dbgmsg($line->{ $theFromTree },"\n"); if( $pLang ne "USA" ) { next if( !&cktarg::CkTarg( $line->{'Target'}, uc($pLang) ) ); } next if(&IsTargInfsNtsetup( $line, $pLang,$pSrcRoot, $pLocRoot, $pIsIncr, $pIsPowerless, $copyOpt)); $from = $theFromRoot ."\\". $line->{ $theFromTree }."\\".$line->{ $theFromFile}; if( $pLang ne "USA" ) { if( $line->{ $theFromFile } eq ".") { $from = $theFromRoot ."\\". $line->{ $theFromTree }."\\".$line->{ $theToFile }; } } $to = $theToRoot."\\". $line->{ $theToTree }. "\\".$line->{ $theToFile }; &PerformCopy( $line->{Target}, $from, $to, $copyOpt); } exit( !&comlib::CheckError( $ENV{ERRFILE}, "Copy Successfully" ) ); } #------------------------------------------------------------------// #Function: IsTargInfsNtsetup #Parameter: (1) Line from intlsrc.txt # (2) Language # (3) Root of the Source Tree # (4) Root of the Localized Tree # (5) Incremental Flag # (6) Powerless Flag # (7) xcopy optional flags #------------------------------------------------------------------// sub IsTargInfsNtsetup { my( $pLine, $pLang, $pSrcRoot, $pLocRoot, $pIsIncr, $pIsPowerless,$pCopyOpt )= @_; my( $from, $to ); return 0 if( $pLine->{Target} ne "INFS_NTSETUP" && $pLine->{Target} ne "INFS_FIXPRNSV" && $pLine->{Target} ne "INFS_COMPDATA" && $pLine->{Target} ne "INFS_FAXSRV" ); if( $pLine->{Target} eq "INFS_NTSETUP" ) { $isNtsetup=1; } else { $isNtsetup=0; } $infsSrcTree = "$pSrcRoot\\$pLine->{SourceTree}"; $infsLocTree = "$pLocRoot\\$pLine->{LocTree}"; ### Drop the ending path of "\$(LANG)" from SourceTree $infsSrcTree =~ s/^(.+)\\$pLang$/$1/i; if( $pLang eq "USA") { ###(1)Copy $infsSrcTree\\*.inx => infs\setup $from = "$infsSrcTree\\*\.inx"; $to = "$infsLocTree\\."; &CkCleanDir( $to, $pIsIncr, $pIsPowerless ); &PerformCopy( $line->{Target}, $from, $to, $pCopyOpt); ###(2)Copy $infsSrcTree\\usa\* => $infsLocTree\usa_all $from = "$infsSrcTree\\usa\\*"; $to = "$infsLocTree\\usa_all"; &CkCleanDir( $to, $pIsIncr, $pIsPowerless ); &PerformCopy( $line->{Target}, $from, "$to\\.", $pCopyOpt); } ###(3)If copylang.pl -l:usa, precompile MergedComponents\SetupInfs\usa => to infs\setup\$lang ### for all $lang's in codes.txt. ### This step is necessary to make LocStudio load the unlocalized text files, ### as LS does not understand if statements. ### ###(1)Otherwise, copy loc\res\$lang\windows\sources\infs\setup => MergedComponents\SetupInfs\$lang ### for the localizable txt files, and ### copy MergedComponents\SetupInfs\usa => MergedComponents\SetupInfs\$lang ### for the unlocalizable txt files. ### In the end, MergedComponents\SetupInfs\$lang will have the same list of files as ### MergedComponents\SetupInfs\usa. &ClSrc( $pLine->{Target}, $pLang, $pSrcRoot, $pLocRoot, $pIsIncr,$pIsPowerless, $pCopyOpt ); return 1; } #------------------------------------------------------------------// #Function: ClSrc #Parameter: (1) Language # (2) Root of the Source Tree # (3) Root of the Localized Tree # (4) Incremental Flag # (5) Powerless Flag # (6) xcopy optional flags #------------------------------------------------------------------// sub ClSrc { my( $pTarget, $pLang, $pSrcRoot, $pLocRoot,$pIsIncr, $pIsPowerless, $pCopyOpt )=@_; ###---Get LCID from codes.txt.---------------------------------// my $srcDir = "$infsSrcTree\\usa"; my @srcFiles = `dir /on /b $srcDir`; chomp @srcFiles; if( $pLang eq "USA") { my @myLang = sort keys %hashCodes; for( my $i=0; $i < @myLang; $i++) { $destDir = "$infsLocTree\\$myLang[$i]"; &CkCleanDir( $destDir, $pIsIncr, $pIsPowerless ); &PerformCompile( $myLang[$i], \@srcFiles, $srcDir, $destDir, $pIsPowerless, $pCopyOpt); } return 1; } ###(1)Copy $infsLocTree\* $infsSrcTree\ $destDir = "$infsSrcTree\\$pLang"; &CkCleanDir( $destDir, $pIsIncr, $pIsPowerless ); $locDir = $infsLocTree; @locFiles = `dir /on /b $locDir`; chomp @locFiles; for( my $i=0; $i < @locFiles; $i++) { &PerformCopy( $pTarget, "$locDir\\$locFiles[$i]", "$destDir\\.", $pCopyOpt); } ###Sepcial case for chh if( lc($pLang) eq "cht" && $isNtsetup ) { $destDir =~ /(.*)cht$/ || $destDir =~ /(.*)CHT$/; { $tmpDestDir = "$1chh"; } &PerformCopy( $pTarget, "$locDir\\chh\\hivesft\.txt", "$tmpDestDir\\.", $pCopyOpt) ; } ###(2)Precompile the files in srcDir but not in locDir=>destDir %tmp=(); for ( @locFiles ){ $tmp{lc($_)}=1;} @srcFiles = map( { exists $tmp{lc($_)}?():lc($_)} @srcFiles); &PerformCompile( $pLang, \@srcFiles, $srcDir, $destDir, $pIsPowerless, $pCopyOpt); return 1; } #------------------------------------------------------------------// #Function: PerformCompile #Parameter: (1) Language # (2) Source File name # (3) Source File Path # (4) Target File Path # (5) Powerless Flag # (6) xcopy optional flags #------------------------------------------------------------------// sub PerformCompile { my( $pLang, $pSrcFiles, $pSrcDir, $pDestDir, $pIsPowerless, $pCopyOpt)=@_; my $PREFLAGS =""; for ( my $i=0; $i < @$pSrcFiles; $i++) { my $from = "$pSrcDir\\$pSrcFiles->[$i]"; my $to = "$pDestDir\\$pSrcFiles->[$i]"; if( lc($pSrcFiles->[$i]) eq "intl\.txt" ) { &PerformCopy( "INFS_NTSETUP", $from, $to, $pCopyOpt); next; } ###Special CHH case for hivesft.txt if( lc($pSrcFiles->[$i]) eq "hivesft\.txt" && lc($pLang) eq "chh" ) { $PREFLAGS = "/DLANGUAGE_ID=0xc04 /EP"; } else { $PREFLAGS = "/DLANGUAGE_ID=$hashCodes{uc($pLang)}->{LCID} /EP"; } $cmdLine="cl /nologo $PREFLAGS $from 1\>$to"; # if ( !$pIsPowerless and !$synconly) # BUG the compiler is executed even if "synconly" was requested. if ( !$pIsPowerless) { # Sync if( $syncOnly && lc $ENV{lang} ne "usa") { &SdCmds( $from, $to, "revert" ); &SdCmds( $from, $to, "sync" ); next; } # Edit &SdCmds( $from, $to, "edit" )if( lc $ENV{lang} ne "usa" ); # Copy files &comlib::ExecuteSystem( $cmdLine ); # Revert file if no different &SdCmds( $from, $to, "revert" )if( lc $ENV{lang} ne "usa" ); } else { if ( not $synconly){ print "$cmdLine\n"; } else { print "SUPPRESSED IN SYNC ONLY MODE:\n$cmdLine\n\n"; } } } } #------------------------------------------------------------------// #Function: CkCleanDir #Parameter: (1) Directory to be checked and cleaned # (2) Incremental flag # (3) Powerless flag #------------------------------------------------------------------// sub CkCleanDir { my ( $pDir, $pIsIncr, $pIsPowerless )=@_; if( !$pIsIncr && !$pIsPowerless ) { if( -e $pDir ){ #&comlib::ExecuteSystem( "rd /S /Q $pDir" ); } } if( !(-e $pDir) ){ &comlib::ExecuteSystem( "md $pDir"); } } #------------------------------------------------------------------// #Function: PerformCopy #Parameter: (1) Any String - information purpose, it could be NULL. # (2) Source of the xcopy # (3) Target of the xcopy # (4) xcopy optional flags #------------------------------------------------------------------// sub PerformCopy { my( $pTarg, $pFrom, $pTo, $pCopyOpt )=@_; # Sync only if( $syncOnly && lc $ENV{lang} ne "usa"){ &SdCmds( $pFrom, $pTo, "revert" ); return &SdCmds( $pFrom, $pTo, "sync" ); } # Do SD update from Loc depot to Source Tree # Open file for Edit &SdCmds( $pFrom, $pTo, "edit" ) if( lc $ENV{lang} ne "usa"); # Copy files &comlib::ExecuteSystem( "echo F|xcopy $pCopyOpt $pFrom $pTo", "$pTarg:"); # Revert file with no different &SdCmds( $pFrom, $pTo, "revert" ) if( lc $ENV{lang} ne "usa" ); return 1; } #------------------------------------------------------------------// #Function: SdCmds #Parameter: (1) Source of the xcopy # (2) Target of the xcopy #------------------------------------------------------------------// sub SdCmds { my( $pFrom, $pTo, $pCmd )=@_; my @sdEdit; $pTo =~ /^(.+)\\([^\\]+)$/; my $toDir = $1; my $toBase = $2; my $fromDir; if( -d $pFrom ) { $pFrom =~ /^(.+)\\([^\\]+)$/; $fromDir = $1; return if( "compdata" eq lc $2 || "chh" eq lc $2); my @tmp = `dir /s /b $pFrom`; chomp( @tmp ); for my $file ( @tmp ) { my $tailStr; while(1) { $file =~ /^(.+)\\([^\\]+)$/; if( $tailStr ){ $tailStr = "$2\\$tailStr";} else { $tailStr = $2; } last if ( lc $fromDir eq lc $1 ); $file = $1; } push( @sdEdit, "$toDir\\$tailStr" ); } $fromDir=$pFrom; } else { $pFrom =~ /^(.+)\\([^\\]+)$/; $fromDir = $1; if( $toBase eq "." ) { push( @sdEdit, basename($pFrom) ); } elsif( $toBase eq "*" ) { @sdEdit = `dir /b $toDir`; } else { push( @sdEdit, $toBase ); } } chomp( @sdEdit ); for my $theFile ( @sdEdit ) { # Sync if( $pCmd =~ /sync/i ) { if( !(-e "$toDir\\$theFile" ) ) { &comlib::ExecuteSystem(" copy $fromDir\\$theFile $toDir\\$theFile" ); } &comlib::ExecuteSystem( "cd /d $toDir& sd sync -f $theFile$syncTime" ); next; } # Revert if( $pCmd =~ /revert/i ) { if ($syncOnly){ # omit -a flag # print STDERR "this is the file name! ", $theFile if $theFile =~ /\\/i; &comlib::ExecuteSystem( "cd /d $toDir& sd revert $theFile" ); } else { &comlib::ExecuteSystem( "cd /d $toDir& sd revert -a $theFile" ); } next; } # Edit my $tmp = `cd /d $toDir& sd have $theFile`; if( !$tmp ) { &comlib::ExecuteSystem(" copy $fromDir\\$theFile $toDir\\$theFile" ); &comlib::ExecuteSystem( "cd /d $toDir& sd add $theFile" ); } else { &comlib::ExecuteSystem( "cd /d $toDir& sd edit $theFile" ); } } } #------------------------------------------------------------------// #Function: Usage #-----------------------------------------------------------------// sub Usage { print < [-x:] [-y:] [-i] [-p] [-s] -l Language. -x Root path of the SourceTree. Defaults to $ENV{_NTBINDIR}. -y Root path of the LocTree. Defaults to $ENV{_NTBINDIR}\\usasrc when language is usa. Defaults to $ENV{_NTBINDIR}\\loc\\res\\\\windows\\sources otherwise. -i Incremental. This flag is used for incremental copy. -t Timestamp for syncing loc files in the source tree. Default is latest. This only applied to non-usa languages. -u Update via sd resolve and submit operations. Default to update the loc files in the source tree as the following: (1) Enlist according to the project view intlview.txt (*) (2) Open the loc files in the source tree for edit according to intlsrc.txt (+) (3) Copy the loc files from the loc depot to the source tree according to intlsrc.txt (+) (4) Precompile the files using the timestamped public headers according to intlsrc.txt (+) (5) Revert the files with no changes (?) (6) Submit the files (+) -s Sync the loc files in the source tree only. The policy to update when the -s flag set is: (1) Enlist according to the project view intlview.txt (*) (2) Sync the collection of files and dirs defined in project view intlview.txt to the USA build timestamp (*) (3) Sync the collection of files and dirs defined in project view intlsrc.txt to current (+) * assumed to have happened in INTL razzle, driven by intlsdop.cmd and intlmap.pl + currently used - currently is missing or implicit -p Powerless. Only lists the files that would get used. /? Displays usage. Examples: $0 -l:usa -x:\\\\ntbld03\\sources -y:\\\\intlnt\\2500.x86.src Copies sources from the usa build machines for localization. $0 -l:usa -y:\\\\intlnt\\2500.x86.src Copies sources from local machines for localization. $0 -l:jpn -i -p Queries which localized sources would get copied from the localization tree to the source tree for jpn. USAGE exit(1); } #--------------------------------------------------------// #Cmd entry point for script. #--------------------------------------------------------// if (eval("\$0 =~ /" . __PACKAGE__ . "\\.pl\$/i")) { # &GetParams ('-n', 'l:','-o', 'x:y:t:ips', '-p', 'lang srcroot locroot synctime isincr ispowerless synconly', @ARGV); $syncOnly = $synconly; $syncTime = $synctime; if( $syncTime && ( $syncTime !~ /^([@#].+)$/ )){ $syncTime = "\@".$syncTime; } #Validate or Set default $lang = uc($lang); if( !&ValidateParams( $lang, \$srcroot, \$locroot ) ) {exit(1); } $ENV{lang}=$lang; exit( !©lang::main( $lang, $srcroot,$locroot, $isincr, $ispowerless ) ); } #--------------------------------------------------------// #Function: ValidateParams #--------------------------------------------------------// sub ValidateParams { my ( $pLang, $pSrcRoot, $pLocRoot ) = @_; if ( !&cklang::CkLang( uc($pLang) ) ) { $ENV{LOGFILE} = "copylang.log"; $ENV{ERRFILE} = "copylang.err"; errmsg("Invalid language $pLang."); return 0; } #Create log/err file if( !( -e "$ENV{_NTTREE}\\$lang\\build_logs") ) { &comlib::ExecuteSystem( "md \"$ENV{_NTTREE}\\$lang\\build_logs\""); } $ENV{LOGFILE} = "$ENV{_NTTREE}\\$lang\\build_logs\\copylang.log"; $ENV{ERRFILE} = "$ENV{_NTTREE}\\$lang\\build_logs\\copylang.err"; &comlib::ExecuteSystem( "del $ENV{LOGFILE}" ) if( -e $ENV{LOGFILE} ); &comlib::ExecuteSystem( "del $ENV{ERRFILE}" ) if( -e $ENV{ERRFILE} ); if( !${$pSrcRoot} ) { $$pSrcRoot = "$ENV{_NTBINDIR}"; } if( !${$pLocRoot} ) { if( $pLang eq "USA" ) { $$pLocRoot = "$ENV{_NTBINDIR}\\usasrc"; } else { $$pLocRoot = "$ENV{_NTBINDIR}\\loc\\res\\$pLang\\windows\\sources"; } } return 1; } #----------------------------------------------------------------// #Function: GetParams #----------------------------------------------------------------// sub GetParams { use GetParams; #Call pm getparams with specified arguments &GetParams::getparams(@_); #Call the usage if specified by /? if ($HELP){ &Usage();} } ###------------------------------------------------------// 1; __END__ =head1 NAME B - Perform Files copy =head1 SYNOPSIS =head1 DESCRIPTION =head1 INSTANCES =head2 =head1 METHODS =head2 =head1 SEE ALSO __END__ =head1 AUTHOR > =head1 CHANGES Rearranging the logic to separate sd update - sd sync - no sd. This rearrangement will involve intlbld.pl, copylang.pl, intlsdop.cmd Please read the usage for all these. Q: Is there the way to do it safe and incremental? A: Yes. Possibly. Step 1. add copylang.pl "sync only" functionality to intlsdop.cmd Step 2. add intlmap.pl "selective enlist" functionality to intlsdop.cmd and copylang/intlbld add intlsdop.cmd "sync" functionality to copylang/intlbld Step 3. do a cleanup of redundant code. Define logic in mapping files Q: Files involved? intlbld.txt intlsrc.txt intlview.txt codes.txt prodskus.txt copylang.pl intlbld.pl intlsdop.cmd intlbld.mak