Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1798 lines
66 KiB

#################################################################################
#
# Script: muimsi.pm
#
# (c) 2000 Microsoft Corporation. All rights reserved.
#
# Purpose: This script creates the msi package
#
# Version: 1.00 (06/27/2001) : (lguindon) Created
#
##################################################################################
# Set Package
package muimsi;
# Set the script name
$ENV{script_name} = 'muimsi.pm';
$cmdPrompt = 0;
# Set version
$VERSION = '1.00';
# Set required perl version
require 5.003;
# Use section
use lib $ENV{ "RazzleToolPath" };
use lib $ENV{ "RazzleToolPath" } . "\\PostBuildScripts";
use GetParams;
use LocalEnvEx;
use Logmsg;
use strict;
no strict 'vars';
use HashText;
use ParseTable;
use File::Copy;
use File::Find;
use Cwd;
use DirHandle;
use Win32::OLE;
use Win32::File;
# Require Section
require Exporter;
# Global variable section
%SKUList = (); #list of skus to include files for, populated in DefineConstants()
%SKUExList = (); # list of skus to excluse files for and to prevent installation on, populated in DefineConstants()
@DeleteTableList = ("AdminUISequence", "AdminExecuteSequence"); # names of MSI tables that we want to delete
##################################################################################
#
# Main entry point.
#
##################################################################################
sub Main
{
# if language is US (or not set), we just return and log a warning. This is
# so that we can include the script in prs postbuild so that
# we will repack the signed binaries back into the MSI
if (($lang=~/usa/i) || (!$lang))
{
logmsg("MUI MSI package is not produced for US build.");
}
else
{
# Check environment variables
if (!&VerifyEnvironment())
{
errmsg ("The environment is not correct for MSI Package build.");
return 0;
}
# Get language's LCID and ISO code
if (!&GetCodes())
{
errmsg ("The language's LCID and ISO code could not be extracted from the CODEFILE.");
return 0;
}
# Define some constants
if (!&DefineConstants())
{
errmsg ("Constants could not be defined.");
return 0;
}
# Make sure directories exist
if (!&CheckDirs())
{
errmsg ("The required directorys do not exist.");
return 0;
}
# Search for current build number
if (!&LookForBuildNumber())
{
errmsg ("No build number information found.");
}
# Apply XMLVAR to the template
if (!&XMLVAR())
{
errmsg ("Error with XMLVAR script.");
}
# Modify the template output generated by XMLVAR to include fusion support
if (!&GenFusionAssemblyXML())
{
errmsg ("Error with GenFusionAssemblyXML script.");
}
# Insert Eula content into the template
if (!&InsertEula())
{
errmsg ("Failed to insert Eula Text into the MSI Template - the EULA dialog will be empty in the build.");
}
# Insert the reserve cost nodes
if (!&InsertReserveCost())
{
errmsg ("Failed to insert reserved diskcost nodes for language packs into the MSI Template MSI diskcost will not include langpack disk costs.");
}
# Insert the skus that we want to merge into the final package
if (!&InsertSKUNodes())
{
errmsg ("Failed to delete the unused SKU merge module nodes from the MSI Template.");
}
# Generate file contents files
if (!&FileContents())
{
errmsg ("File contents couldn't be created.");
}
# Generate custom action files
if (!&CustomAction())
{
errmsg ("Error with Custom Action script.");
}
# Make MSI package
if (!&MakeMSI())
{
errmsg ("Error making the MSI Package.");
}
# Delete the unused MSI table generated as part of the WiX Build process
if (!&DeleteMSITables())
{
errmsg ("Error deleting the unused MSI tables.");
}
# insert the catalog files back into the FE printer packs
if (!&InsertCatFiles())
{
errmsg ("Error inserting catalog files into the FE printer MSI packages.");
}
}
} # Main
##################################################################################
#
# VerifyEnvironment()
#
# Validates necessary environment variables.
#
##################################################################################
sub VerifyEnvironment
{
logmsg ("Validating the environment.");
$RAZZLETOOLPATH=$ENV{RazzleToolPath};
$_NTPOSTBLD=$ENV{_NTPOSTBLD};
logmsg("------- RAZZLETOOLPATH is $RAZZLETOOLPATH");
logmsg("------- _NTPOSBLD is $_NTPOSTBLD");
logmsg("------- LANG is $LANG");
if ($LANG=~/psu_(.*)/i)
{
$Special_Lang=$1;
}
elsif ($LANG=~/psu/i || $LANG=~/mir/i || $LANG=~/FE/i)
{
if (defined( $ENV{"LANG_MUI"} ) )
{
$Special_Lang=$ENV{"LANG_MUI"};
}
elsif ($LANG=~/psu/i)
{
$Special_Lang="ro";
}
elsif ($LANG=~/FE/i)
{
$Special_Lang="jpn";
}
else
{
$Special_Lang="ara";
}
}
else
{
$Special_Lang=$LANG;
}
logmsg ("------- special lang is $Special_Lang");
$PROCESSOR=$ENV{PROCESSOR};
if ($ENV{_BuildArch} =~/x86/i)
{
$_BuildArch="i386";
}
else
{
$_BuildArch=$ENV{_BuildArch};
}
$_BuildArch_release=$ENV{_BuildArch};
$LOGS=$ENV{temp};
logmsg("------- Build architecture is $_BuildArch");
logmsg("------- LOGS is $LOGS");
if ($ENV{_BuildType} =~ /^chk$/i)
{
wrnmsg ("This does not run on chk machines");
return 0;
}
if(defined($SAFEMODE=$ENV{SAFEMODE}))
{
logmsg ("SAFEMODE is ON");
}
logmsg ("Success: Validating the environment.");
return 1;
} # VerifyEnvironment
##################################################################################
#
# DefineConstants()
#
# Defines global constants.
#
##################################################################################
sub DefineConstants
{
logmsg ("Definition of global constants.");
# Directories
$LOCBINDIR = "$_NTPOSTBLD";
$MUIDIR = "$LOCBINDIR\\mui";
$MSIDIR = "$MUIDIR\\MSI";
$CONTROLDIR = "$MUIDIR\\control";
$SOURCEDIR = "$MUIDIR\\$Special_Lang\\$_BuildArch.uncomp";
$INFFILE = "$MUIDIR\\mui.inf";
$CDLAYOUT = GetCDLayOut();
$DESTDIR = "$MUIDIR\\release\\$_BuildArch_release\\$CDLAYOUT";
$TMPBUILD = "$MUIDIR\\$Special_Lang\\tmp";
$TEMPLATE = "$MSIDIR\\muimsi_template.wim";
logmsg("------- LOCBINDIR is $LOCBINDIR");
logmsg("------- MUIDIR is $MUIDIR");
logmsg("------- MSIDIR is $MSIDIR");
logmsg("------- CONTROLDIR is $CONTROLDIR");
logmsg("------- SOURCEDIR is $SOURCEDIR");
logmsg("------- DESTDIR is $DESTDIR");
logmsg("------- TMPBUILD is $TMPBUILD");
logmsg("------- CDLAYOUT is $CDLAYOUT");
# Filenames
# kenhsu - some build filenames are different on IA64
if (($ENV{_BuildArch} =~/x86/i))
{
$GUIDFILE = "$MSIDIR\\guidlang.txt";
$PLATFORM = "Intel";
$IA64CONDITION = "NOT VersionNT64";
$ISWIN64 = "no";
$SYSFOLDERPROP = "SystemFolder";
}
else
{
$GUIDFILE = "$MSIDIR\\guidlang64.txt";
$PLATFORM = "Intel64";
$IA64CONDITION = "VersionNT64";
$ISWIN64 = "yes";
$SYSFOLDERPROP = "System64Folder";
}
logmsg("MSI Package platform is $PLATFORM");
logmsg("MSI Template file used is $TEMPLATE");
logmsg("MSI GUID file used is $GUIDFILE");
$SRCRELNOTE = "$MSIDIR\\msinotes.htm";
$SRCXPBITMAP = "$MSIDIR\\muimsi_hl.bmp";
$SRCMUISETUP = "$MUIDIR\\muisetup.exe";
$SRCMUIMSIDLL = "$MSIDIR\\muimsidll.dll";
$DSTRELNOTE = "$DESTDIR\\msinotes.htm";
$MUIMSIXML = "$TMPBUILD\\$LCID_SHORT.wim";
$MUIMSIXMLTemp = "$TMPBUILD\\Temp$LCID_SHORT.wim";
$MUIMSIWIX = "$TMPBUILD\\$LCID_SHORT.msi";
$MUIMSI = "$DESTDIR\\$LCID_SHORT.msi";;
$FILECNT_CORE = "$TMPBUILD\\FileContent_CORE.wxm"; #core files required in every flavour
$FILELST_CORE = "$TMPBUILD\\FileContent_CORE.msm";
$FILECNT_PRO = "$TMPBUILD\\FileContent_PRO.wxm"; #professional build of NT
$FILELST_PRO = "$TMPBUILD\\FileContent_PRO.msm";
$FILECNT_SRV = "$TMPBUILD\\FileContent_SRV.wxm"; #standard server build of NT
$FILELST_SRV = "$TMPBUILD\\FileContent_SRV.msm";
$FILECNT_ADV = "$TMPBUILD\\FileContent_ADV.wxm"; #advanced server build of NT
$FILELST_ADV = "$TMPBUILD\\FileContent_ADV.msm";
$FILECNT_DTC = "$TMPBUILD\\FileContent_DTC.wxm"; #datacenter build of NT
$FILELST_DTC = "$TMPBUILD\\FileContent_DTC.msm";
$FILECNT_WEB = "$TMPBUILD\\FileContent_WEB.wxm"; #webserver (blade) build of NT
$FILELST_WEB = "$TMPBUILD\\FileContent_WEB.msm";
$FILECNT_SBS = "$TMPBUILD\\FileContent_SBS.wxm"; #small business build of NT
$FILELST_SBS = "$TMPBUILD\\FileContent_SBS.msm";
$CUSTACTION = "$CONTROLDIR\\Custom_action.wxm";
$CUSTACTIONCOMP = "$CONTROLDIR\\Custom_action.msm";
$WISCHEMA = "$MSIDIR\\WiSchema.xml";
# Applications
$INFPARSER = "infparser.exe";
$CANDLE = "CScript.exe $MSIDIR\\candle.wsf";
$LIGHT = "CScript.exe $MSIDIR\\light.wsf";
$XMLVAR = "perl.exe $MSIDIR\\xmlvar.bat";
$COPY = "copy";
# Flavor
if($_BuildArch =~ /i386/i)
{
$FLAVOR = "32";
}
elsif ($_BuildArch =~ /ia64/i)
{
$FLAVOR = "64";
}
logmsg("------- FLAVOR is $FLAVOR");
# GUID - read the language guid off the language-guid file, if it's not there
# generate one and put it in the file.
logmsg ("Get the language's MSI package ID from $GUIDFILE.");
my(@langguids, @newcontent);
# Search GUIDFILE for $Special_Lang and the associated MSI package guid
if(!open(GUIDFILE, "$GUIDFILE"))
{
errmsg ("Can't open language guid file $GUIDFILE for reading.");
return 0;
}
GUID_LOOP: while (<GUIDFILE>)
{
# add the file content to an array, in case we need to append additional data
# push(@newcontent, $_);
if (/^$Special_Lang\s+/i)
{
@langguids = split(/\s+/,$_);
$MSIGUID = $langguids[1]; # 2nd field should be the client language guid
$PRODUCTGUID = $langguids[2]; # 3rd field should be the product language guid
last GUID_LOOP; # exit out of the loop if found
}
}
close(GUIDFILE);
$MSIGUID =~ tr/a-z/A-Z/;
$PRODUCTGUID =~ tr/a-z/A-Z/;
# if the language is not found, add a new guid entry into GUIDFILE
if(!@langguids)
{
logmsg ("$Special_Lang is not found in $GUIDFILE, adding an entry for it.");
($MSIGUID) = `uuidgen`;
chomp $MSIGUID;
$MSIGUID =~ tr/a-z/A-Z/;
}
($REGISTRY1GUID) = `uuidgen`;
chomp $REGISTRY1GUID;
$REGISTRY1GUID =~ tr/a-z/A-Z/;
($REGISTRY3GUID) = `uuidgen`;
chomp $REGISTRY3GUID;
$REGISTRY3GUID =~ tr/a-z/A-Z/;
logmsg("------- REGISTRY1GUID is $REGISTRY1GUID");
logmsg("------- REGISTRY3GUID is $REGISTRY3GUID");
logmsg("------- MSIGUID is $MSIGUID");
logmsg("------- PRODUCTGUID is $PRODUCTGUID");
# Other GUIDs
$UPGRADEGUID = "177146D4-5102-4BD9-98A0-114A3ED39076";
$CONTENTGUID = "1AFE112F-290F-4A94-2000-AB4D3FD8B102";
# MSI version number
my ($src, $isdst);
($src, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime;
eval $hour;
eval $min;
eval $yday;
eval $wday;
$VERSIONNUMBER = 100*(12*($year-101)+$mon+1)+$mday;
logmsg("------- VERSIONNUMBER is $VERSIONNUMBER");
# Package name
$PACKAGENAME = "muiMSITemplate ($VERSIONNUMBER)";
logmsg("------- PACKAGENAME is $PACKAGENAME");
logmsg ("Success: Definition of global constants.");
$BuildChecksum=1;
logmsg("-------- Including CORE files.");
$SKUList{"Core"} = {
InfParserFlag => "c",
CntLstFileName => "$FILECNT_CORE",
MergModeFileName => "$FILELST_CORE",
};
if (defined($prosku))
{
logmsg("-------- Including files for Professional SKU.");
$SKUList{"Pro"} = {
InfParserFlag => "p",
CntLstFileName => "$FILECNT_PRO",
MergModeFileName => "$FILELST_PRO",
};
}
else
{
logmsg("-------- Excluding files for Professional SKU.");
$SKUExList{"Pro"} = {
Message => "The [ProductName] cannot be installed on Windows XP Professional Edition.",
Condition => "MsiNTProductType <> 1",
};
}
# If this is one of the Client(wks) languages we only want the Core File Content included and Pro Launch Condition
if ($FLV =~ /WKS/i)
{
return 1;
}
if (defined($srvsku))
{
logmsg("-------- Including files for Standard Server SKU.");
$SKUList{"Srv"} = {
InfParserFlag => "s",
CntLstFileName => "$FILECNT_SRV",
MergModeFileName => "$FILELST_SRV",
};
}
else
{
logmsg("-------- Excluding files for Standard Server SKU.");
$SKUExList{"Srv"} = {
Message => "The [ProductName] cannot be installed on Windows .NET 2003, Standard Edition.",
Condition => "MsiNTProductType<>1 AND NOT MsiNTSuiteDataCenter AND NOT MsiNTSuiteEnterprise AND NOT MsiNTSuiteWebServer AND NOT MsiNTSuiteSmallBusiness AND NOT MsiNTSuiteSmallBusinessRestricted",
};
}
if (defined($advsku))
{
logmsg("-------- Including files for Advanced Server SKU.");
$SKUList{"Adv"} = {
InfParserFlag => "a",
CntLstFileName => "$FILECNT_ADV",
MergModeFileName => "$FILELST_ADV",
};
}
else
{
logmsg("-------- Excluding files for Advanced Server SKU.");
$SKUExList{"Adv"} = {
Message => "The [ProductName] cannot be installed on Windows .NET 2003, Enterprise Edition.",
Condition => "NOT MsiNTSuiteEnterprise",
};
}
if (defined($dtcsku))
{
logmsg("-------- Including files for Datacenter Server SKU.");
$SKUList{"Dtc"} = {
InfParserFlag => "d",
CntLstFileName => "$FILECNT_DTC",
MergModeFileName => "$FILELST_DTC",
};
}
else
{
logmsg("-------- Excluding files for Datacenter Server SKU.");
$SKUExList{"Dtc"} = {
Message => "The [ProductName] cannot be installed on Windows .NET 2003, Datacenter Edition.",
Condition => "NOT MsiNTSuiteDataCenter",
};
}
if (defined($websku))
{
logmsg("-------- Including files for Blade Server SKU.");
$SKUList{"Web"} = {
InfParserFlag => "b",
CntLstFileName => "$FILECNT_WEB",
MergModeFileName => "$FILELST_WEB",
};
}
else
{
logmsg("-------- Excluding files for Web Server SKU.");
$SKUExList{"Web"} = {
Message => "The [ProductName] cannot be installed on Windows .NET 2003 Web Server.",
Condition => "NOT MsiNTSuiteWebServer",
};
}
if (defined($sbssku))
{
logmsg("-------- Including files for Small Business Server SKU.");
$SKUList{"Sbs"} = {
InfParserFlag => "l",
CntLstFileName => "$FILECNT_SBS",
MergModeFileName => "$FILELST_SBS",
};
}
else
{
logmsg("-------- Excluding files for Small Business Server SKU.");
$SKUExList{"Sbs"} = {
Message => "The [ProductName] cannot be installed on Windows .NET Server 2003 for Small Business Server.",
Condition => "NOT MsiNTSuiteSmallBusiness AND NOT MsiNTSuiteSmallBusinessRestricted",
};
}
return 1;
} # DefineConstants
##################################################################################
#
# GetCodes
#
# Gets the language's LCID and ISO language code from CODEFILE.
#
##################################################################################
sub GetCodes
{
#set the code file name to read
$CODEFILE = "$RAZZLETOOLPATH\\codes.txt";
$DESCFILE = "$_NTPOSTBLD\\mui\\MSI\\langdesc.txt";
logmsg ("Get the language's LCID and ISO language code from $CODEFILE.");
my(@data);
my(@data2);
# Don't allow nec_98 or CHT to be processed!
if($Special_Lang =~ /^(NEC_98|CHT)$/i)
{
logmsg ("No MUI available for $Special_Lang");
exit 0;
}
# Search CODEFILE for $Special_Lang, $LCID, $LANG_ISO, etc.
if(!open(CODEFILE, "$CODEFILE"))
{
errmsg ("Can't open lcid file $CODEFILE for reading.");
return 0;
}
CODES_LOOP: while (<CODEFILE>)
{
if (/^$Special_Lang\s+/i)
{
@data = split(/\s+/,$_);
last CODES_LOOP;
}
}
close(CODEFILE);
if(!@data)
{
logmsg ("$Special_Lang is an unknown language");
&Usage;
return 0;
}
$LCID = $data[2];
$LCID_SHORT = $LCID;
$LCID_SHORT =~ s/^0x//;
$LANG_ISO = $data[8];
$GUID = $data[11];
$FLV = $data[6];
#extract the language description - get this from our lang description file
if(!open(DESCFILE, "$DESCFILE"))
{
errmsg ("Can't open lcid description file $DESCFILE for reading.");
return 0;
}
DESC_LOOP: while (<DESCFILE>)
{
if (/^$LCID_SHORT\s+/i)
{
@data2 = split(/\s+/,$_);
last DESC_LOOP;
}
}
close(DESCFILE);
if(!@data2)
{
logmsg ("Cannot find a language description for $LCID_SHORT.");
return 0;
}
$LANG_NAME = $data2[1];
for ($i = 2; $i <= $#data2; $i++)
{
$LANG_NAME = "$LANG_NAME $data2[$i]";
}
logmsg ("Success: Get the language's LCID and ISO language code frrm $CODEFILE.");
logmsg("------- LCID is $LCID");
logmsg("------- LCID_SHORT is $LCID_SHORT");
logmsg("------- LANG_ISO is $LANG_ISO");
logmsg("------- GUID is $GUID");
logmsg("------- LANG_NAME is $LANG_NAME");
logmsg("------- FLV is $FLV");
return 1;
} # GetCodes
##################################################################################
#
# CheckDirs
#
# Makes sure that the necessary directories exist.
#
##################################################################################
sub CheckDirs
{
logmsg ("Make sure the necessary directories exist.");
my($status);
# Make sure the source directories exist
foreach ($LOCBINDIR,$MUIDIR,$CONTROLDIR,$SOURCEDIR,$MSIDIR)
{
if(! -e $_)
{
logmsg ("$0: ERROR: $_ does not exist");
return 0;
}
}
# Make sure destination directories exist
foreach ($DESTDIR,$TMPBUILD)
{
if(! -e $_)
{
$status = system("md $_");
if($status)
{
logmsg ("$0: ERROR: cannot create $_");
return 0;
}
}
}
logmsg ("del /q /f $TMPBUILD\\*.*");
$returnStatus = system("del /q /f $TMPBUILD\\*.*");
if ($returnStatus)
{
logmsg ("INFO: script did not find previously generated MSI build files.");
}
logmsg ("Success: Make sure the necessary directories exist.");
return 1;
} # CheckDirs
##################################################################################
#
# LookForBuildNumber
#
# Scan build log for the build number.
#
##################################################################################
sub LookForBuildNumber
{
logmsg ("Update mui.inf with the current build number.");
# Get current build number
$LOGS="$_NTPOSTBLD\\build_logs";
my $filename="$LOGS\\buildname.txt";
open (BUILDNAME, "< $filename") or die "Can't open $filename for reading: $!\n";
while (<BUILDNAME>)
{
$BLDNO = $_;
$BLDNO =~ s/\.[\w\W]*//i;
}
close BUILDNAME;
if ($BLDNO =~ /(^\d+)-*\d*$/) {
$BLDNO = $1;
}
else
{
errmsg ("Errorin LookForBuildNumber: BLDNO is $BLDNO");
return 0;
}
chomp($BLDNO);
logmsg("------- BLDNO is $BLDNO");
logmsg ("Success: Update mui.inf with the current build number.");
return 1;
} # LookForBuildNumber
##################################################################################
#
# FileContents()
#
# Generate different flavor of filecontents.wxm.
#
##################################################################################
sub FileContents
{
logmsg ("Generate MSI file contents.");
for $skuitem (keys %SKUList)
{
$infparserParam = "/P:$CDLAYOUT /i:$LOCBINDIR /b:$FLAVOR /l:$Special_Lang /f:$SKUList{$skuitem}{InfParserFlag} /s:$MUIDIR /o:$SKUList{$skuitem}{CntLstFileName}";
$compileParam = "-c $SKUList{$skuitem}{MergModeFileName} $SKUList{$skuitem}{CntLstFileName}";
$linkParam = $SKUList{$skuitem}{MergModeFileName};
logmsg ("Generate MSI $skuitem content.");
logmsg ("$INFPARSER $infparserParam");
$returnResult = system("$INFPARSER $infparserParam");
if ($returnResult)
{
logmsg("ERROR: unable to generate MSI $skuitem content! ");
return 0;
}
# Compile core file content
logmsg ("Compile MSI $skuitem content.");
logmsg ("$CANDLE $compileParam");
$returnResult = system("$CANDLE $compileParam");
if ($returnResult)
{
logmsg("ERROR: unable to compile MSI $skuitem content! ");
return 0;
}
# Link core file content
logmsg ("Link MSI $skuitem contents.");
logmsg ("$LIGHT $linkParam");
$returnResult = system("$LIGHT $linkParam");
if ($returnResult)
{
logmsg("ERROR: unable to link MSI $skuitem content! ");
return 0;
}
}
logmsg ("Success: Generate MSI file contents.");
return 1;
} #FileContents
##################################################################################
#
# CustomAction()
#
# Generate file associated with custom action if needed.
#
##################################################################################
sub CustomAction
{
logmsg ("Generate MSI file contents.");
# Do something
logmsg ("Success: Generate MSI file contents.");
return 1;
} #CustomAction
##################################################################################
#
# XMLVAR()
#
# Generate a language specific msi template.
#
##################################################################################
sub XMLVAR
{
my ($xmlvarParam);
logmsg ("Generate language specific msi template.");
$xmlvarParam = " srcmuiinf=\"$INFFILE\"";
$xmlvarParam .= " regkey1guid=\"$REGISTRY1GUID\"";
$xmlvarParam .= " regkey3guid=\"$REGISTRY3GUID\"";
$xmlvarParam .= " IA64CONDITION=\"$IA64CONDITION\"";
$xmlvarParam .= " PLATFORM=\"$PLATFORM\"";
$xmlvarParam .= " LANG=\"$Special_Lang\"";
$xmlvarParam .= " srcmuimsidll=\"$SRCMUIMSIDLL\"";
$xmlvarParam .= " srcmuisetup=\"$SRCMUISETUP\"";
$xmlvarParam .= " srcxpbitmap=\"$SRCXPBITMAP\"";
$xmlvarParam .= " msiguid=\"$MSIGUID\"";
$xmlvarParam .= " productguid=\"$PRODUCTGUID\"";
$xmlvarParam .= " namePackage=\"$PACKAGENAME\"";
$xmlvarParam .= " ver=\"1.0.$VERSIONNUMBER.0\"";
$xmlvarParam .= " guidUpgradeCode=\"$UPGRADEGUID\"";
$xmlvarParam .= " debugdir=\"$TMPBUILD\"";
$xmlvarParam .= " Language=\"$LANG_NAME\"";
$xmlvarParam .= " BLD=\"$BLDNO\"";
$xmlvarParam .= " CORESRC=\"$FILELST_CORE\"";
$xmlvarParam .= " LCID=\"$LCID_SHORT\"";
$xmlvarParam .= " ISWIN64=\"$ISWIN64\"";
$xmlvarParam .= " SYSFOLDERPROP=\"$SYSFOLDERPROP\"";
$xmlvarParam .= " srcSchema=\"$WISCHEMA\"";
$xmlvarParam .= " < $TEMPLATE > $MUIMSIXMLTemp";
# Generate [LCID].WIM based on the template
logmsg ("Run XMLVAR on the generic template.");
logmsg("$XMLVAR $xmlvarParam");
$returnResult = system("$XMLVAR $xmlvarParam");
if ($returnResult)
{
logmsg("ERROR: XMLVAR failed!");
return 0;
}
logmsg ("Success: Generate language specific msi template.");
return 1;
} #XMLVAR
##################################################################################
#
# MakeMSI()
#
# Build the MSI package.
#
##################################################################################
sub MakeMSI
{
$MEGEMODDLL = "$RAZZLETOOLPATH\\x86\\mergemod.dll";
logmsg ("Create MSI package.");
logmsg ("Registering mergemod.dll");
logmsg ("regsvr32 /s $MEGEMODDLL");
$returnResult = system("regsvr32 /s $MEGEMODDLL");
if ($returnResult)
{
logmsg("ERROR: failed to register $MEGEMODDLL!");
return 0;
}
# Compile language specific template
logmsg ("Compile the language specific template.");
logmsg("$CANDLE -o -e -c $MUIMSIWIX $MUIMSIXML");
$returnResult = system("$CANDLE -o -e -c $MUIMSIWIX $MUIMSIXML");
if ($returnResult)
{
logmsg("ERROR: failed to compile language specific template");
return 0;
}
# Link language specific template
logmsg ("Link the language specific template.");
logmsg ("$LIGHT -r -b $TMPBUILD -o $MUIMSI $MUIMSIWIX");
$returnResult = system("$LIGHT -r -b $TMPBUILD -o $MUIMSI $MUIMSIWIX ");
if ($returnResult)
{
logmsg("ERROR: failed to link $MEGEMODDLL!");
return 0;
}
# copy the release notes file to the release directory
logmsg ("Copying release notes file.");
logmsg ("$COPY /y $SRCRELNOTE $DSTRELNOTE");
$returnResult = system("$COPY /y $SRCRELNOTE $DSTRELNOTE");
if ($returnResult)
{
logmsg("ERROR: failed to copy the MSI release notes file!");
return 0;
}
logmsg ("Success: Create MSI package.");
return 1;
} #MakeMSI
##################################################################################
#
# ValidateParams()
#
# VAlidate parameters.
#
##################################################################################
sub ValidateParams
{
#<Add your code for validating the parameters here>
}
##################################################################################
#
# Usage()
#
# Print usage.
#
##################################################################################
sub Usage
{
print <<USAGE;
muimsi.pm is used to create the MUI MSI package for the specified language.
Usage: $0 [-l lang] [-p] [-s] [-a] [-d] [-w] [-b]
-l Language
-p include Professional SKU MUI files
-s include Standard Server SKU MUI files
-a include Advanced Server SKU MUI files
-d include Datacenter server SKU MUI files
-w include Blade server SKU MUI files
-b include Small Business Server MUI files
-? Displays usage
Example:
$0 -l jpn
Generate MUI MSI package for Japanese language, include only core files.
$0 -l jpn -p -s
Generate MUI MSI package for Japanese language, include core, professional and standard server files.
USAGE
}
##################################################################################
#
# GetParams()
#
# Get language parameter.
#
##################################################################################
sub GetParams
{
# Step 1: Call pm getparams with specified arguments
&GetParams::getparams(@_);
# Step 2: Call the usage if specified by /?
if ($HELP)
{
&Usage();
exit 1;
}
# Step 3: Set the language into the enviroment
$ENV{lang}=$lang;
logmsg("-------- lang parameter passed in is $lang");
}
##################################################################################
#
# GetCDLayOut
#
# Get CD layout from MUI.INF
#
##################################################################################
sub GetCDLayOut
{
my(@cd_layout, @lang_map, $muilang, $lang_id);
# Map lang
logmsg("------ INFFILE is $INFFILE");
@lang_map = `perl $RAZZLETOOLPATH\\PostBuildScripts\\parseinf.pl $INFFILE Languages`;
foreach $muilang (@lang_map)
{
if ($muilang =~ /(.*)=$LANG\.mui/i)
{
# Get layout
$langid = $1;
# Try ia64 section first if we're building ia64 MUI
if ($_BuildArch =~ /ia64/i)
{
@cd_layout = `perl $RAZZLETOOLPATH\\PostBuildScripts\\parseinf.pl $INFFILE CD_LAYOUT_IA64`;
foreach $layout (@cd_layout)
{
chomp($layout);
if ($layout =~ /$langid=(\d+)/i)
{
return uc("cd$1");
}
}
}
@cd_layout = `perl $RAZZLETOOLPATH\\PostBuildScripts\\parseinf.pl $INFFILE CD_LAYOUT`;
foreach $layout (@cd_layout)
{
chomp($layout);
if ($layout =~ /$langid=(\d+)/i)
{
return uc("cd$1");
}
}
last;
}
}
return lc("psu");
}
##################################################################################
#
# GenFusionAssemblyXML
#
# This function will walk the MUI postbuild directory, find the fusion assembly files, locate the manifest and generate the
# required xml and put it in a variable for insertion into the WiX template using XMLVAR. Below is an example of the required
# generated xml snippet
#
# Component Specification:
#
# <Directory Name='SourceDir'>TARGETDIR
# <Directory Name='Windows'>WindowsFolder
# <Directory Name='MUI'>MUI
# <Directory Name='FALLBACK'>
# <Directory Name='0411'>
# <Directory Name="ASMS" LongName="ASMS">ASMS
# <Directory Name="6000" LongName="6000">6000
# <Directory Name="msft" LongName="msft">msft
# <Directory Name="vcrtlmui" LongName="vcrtlmui">vcrtlmui
# <Component Id='1D56E371-0202-4BD7-A050-69415A59007B'>Asms6000Msftvcrtlmui
# <File DiskId='1' Name="vcrtlmui.cat" LongName="vcrtlmui.cat" src="c:\nt.relbins.x86fre\jpn\mui\jpn\i386.uncomp\asms\6000\msft\vcrtlmui\vcrtlmui.cat">vcrtlmui.cat.2</File>
# <File DiskId='1' Name="vcrtlmui.man" LongName="vcrtlmui.man" src="c:\nt.relbins.x86fre\jpn\mui\jpn\i386.uncomp\asms\6000\msft\vcrtlmui\vcrtlmui.man">vcrtlmui.man.2</File>
# <File DiskId='1' Name="MFC42D~1.mui" LongName="mfc42.dll.mui" src="c:\nt.relbins.x86fre\jpn\mui\jpn\i386.uncomp\asms\6000\msft\vcrtlmui\mfc42.dll.mui">mfc42.dll.mui.2</File>
# </Component>
# </Directory>
# </Directory>
# </Directory>
# </Directory>
# </Directory>
# </Directory>
# </Directory>
# </Directory>
# </Directory>
#
# Assembly Specification:
#
# <Feature Title='FusionInstall' Display='hidden' Level='1' AllowAdvertise='system' FollowParent='yes'>MUIFusionInstall
# <Component>Asms6000Msftvcrtlmui
# <Assembly Manifest='vcrtlmui.man.2' Type='win32'>MUIFusionAssembly
# <Property Value='Microsoft.Tools.VisualCPlusPlus.Runtime-Libraries.mui'>name</Property>
# <Property Value='6.0.0.0'>version</Property>
# ...... etc.
# </Assembly>
# </Component>
# </Feature>
#
##################################################################################
sub GenFusionAssemblyXML
{
Win32::OLE::CreateObject("Msxml2.DOMDocument", $AsmManifestDoc) or die "Can't create XMLDOM\n";
Win32::OLE::CreateObject("Msxml2.DOMDocument", $MsiTemplateDoc) or die "Can't create XMLDOM\n";
$MsiTemplateDoc->{async} = 0;
$AsmManifestDoc->{async} = 0;
my $MsiASMType, $MsiASMName, $MsiASMVersion, $MsiASMSN;
$CURRENTDIR = Win32::GetCwd();
$FUSIONROOT = "ASMS\\$Special_Lang"; # root directory where all possible Fusion assemblies are stored under $MUIDIR\\Drop
$FUSIONSRC = "$MUIDIR\\Drop\\$FUSIONROOT";
$FileCounter = 2; # this counter is appended to the end of files in each assembly to generate unique file IDs
logmsg("------- FUSIONSRC is $FUSIONSRC");
logmsg("------- CURRENTDIR is $CURRENTDIR");
# if we can't find the fusionsrc directory, just exit - there are no fusion component for this mui build
if (! -e $FUSIONSRC)
{
logmsg("Cannot locate fusion source directory $FUSIONSRC, there are no fusion components for this MUI build.");
logmsg("copy /Y $MUIMSIXMLTemp $MUIMSIXML");
`copy /Y $MUIMSIXMLTemp $MUIMSIXML`;
return 1;
}
# load the xmlvar'ed template
my $loadresult = $MsiTemplateDoc->load($MUIMSIXMLTemp);
$docError = $MsiTemplateDoc->{parseError};
if ($docError->{errorCode} != 0)
{
logmsg("Parse error occurred in manifest file, exiting\n");
logmsg("Line: [$docError->{line}], LinePos: [$docError->{linepos}]\n");
logmsg("Reason: [$docError->{reason}]\n");
return 0;
}
# find the directory root where we want to insert our assembly components
my $MsiCompRoot;
$MuiRootDirNode = $MsiTemplateDoc->{documentElement}->selectSingleNode("//Directory[\@Name = \"MUI\"]");
if (defined($MuiRootDirNode))
{
logmsg("Found MUI directory node.");
$MuiFallbackDirNode = $MuiRootDirNode->selectSingleNode("//Directory[\@Name = \"FALLBACK\"]");
if (defined($MuiFallbackDirNode))
{
logmsg("Found MUI/FALLBACK directory node.");
$MsiCompRoot = $MuiFallbackDirNode->selectSingleNode("//Directory[\@Name = \"$LCID_SHORT\"]");
}
else
{
logmsg("Cannot find FALLBACK directory node within the MUI Directory Node in the MSI Template!");
}
}
else
{
logmsg("Cannot find MUI Directory Node in the MSI Template!");
return 0;
}
if (!defined($MsiCompRoot))
{
logmsg("Cannot find the proper directory node MUI/FALLBACK/$LCID_SHORT for fusion insertion!");
return 0;
}
else
{
logmsg("Found MUI/FALLBACK/$LCID_SHORT directory node.");
}
# find the feature root where we want to insert our assembly feature
$MsiFeatureRoot = $MsiTemplateDoc->{documentElement}->selectSingleNode("//Feature[\@Title = \"BasicInstall\"]");
if (!defined($MsiFeatureRoot))
{
logmsg("Cannot find the proper feature node BasicInstall for fusion insertion!");
return 0;
}
$bCreatedFeatureNode = 0;
# see if there are fusion components to install
if ((-e $FUSIONSRC) && (-d $FUSIONSRC))
{
logmsg("------- $FUSIONSRC exists.");
opendir(DIRHANDLE1, $FUSIONSRC);
@DIRFILES1 = grep { $_ ne '.' and $_ ne '..' } readdir(DIRHANDLE1);
# $SUBDIR1 - subdirectory under fusionroot e.g. "6000" for directory i386.uncomp\asms\6000\msft\vcrtlmui
foreach $SUBDIR1 (@DIRFILES1)
{
logmsg("------- SUBDIR1 is $SUBDIR1");
if (-d "$FUSIONSRC\\$SUBDIR1")
{
opendir(DIRHANDLE2, "$FUSIONSRC\\$SUBDIR1");
@DIRFILES2 = grep { $_ ne '.' and $_ ne '..' } readdir(DIRHANDLE2);
$TempDirNode1 = $MsiTemplateDoc->createElement("Directory");
$TempDirNode1->setAttribute("Name", $SUBDIR1);
$TempDirNode1->{text} = "_$SUBDIR1.$FileCounter"; # prepending a "_" to conform to MSI ID naming convention
# $SUBDIR2 - subdirectory under subdir1 e.g. "msft" for directory i386.uncomp\asms\6000\msft\vcrtlmui
foreach $SUBDIR2 (@DIRFILES2)
{
logmsg("------- SUBDIR2 is $SUBDIR2");
if (-d "$FUSIONSRC\\$SUBDIR1\\$SUBDIR2")
{
opendir(DIRHANDLE3, "$FUSIONSRC\\$SUBDIR1\\$SUBDIR2\\$SUBDIR3");
@DIRFILES3 = grep { $_ ne '.' and $_ ne '..' } readdir(DIRHANDLE3);
$TempDirNode2 = $MsiTemplateDoc->createElement("Directory");
$TempDirNode2->setAttribute("Name", $SUBDIR2);
$TempDirNode2->{text} = "_$SUBDIR2.$FileCounter"; # prepending a "_" to conform to MSI ID naming convention
$TempDirNode1->appendChild($TempDirNode2);
# $SUBDIR3 - subdirectory under subdir2 e.g. "vcrtlmui" for directory i386.uncomp\asms\6000\msft\vcrtlmui
foreach $SUBDIR3 (@DIRFILES3)
{
logmsg("------- SUBDIR3 is $SUBDIR3");
# only continue if we can change to this directory and can find a manifest file
if (chdir "$FUSIONSRC\\$SUBDIR1\\$SUBDIR2\\$SUBDIR3")
{
# if we can't find a .man or .manifest file, skip this component
if (!defined ($ASMMANFILE = glob("*.man")))
{
if (!defined ($ASMMANFILE = glob("*.manifest")))
{
logmsg("Skipping $FUSIONSRC\\$SUBDIR1\\$SUBDIR2\\$SUBDIR3 directory, can't find manifest file.");
next;
}
}
logmsg("Found assembly directory - $FUSIONSRC\\$SUBDIR1\\$SUBDIR2\\$SUBDIR3\n");
logmsg("Assembly manifest file is $ASMMANFILE");
$TempDirNode3 = $MsiTemplateDoc->createElement("Directory");
$TempDirNode3->setAttribute("Name", $SUBDIR3);
$TempDirNode3->{text} = "_$SUBDIR3.$FileCounter"; # prepending a "_" to conform to MSI ID naming convention
$TempDirNode2->appendChild($TempDirNode3);
# process manifest file for the assembly
my $result = $AsmManifestDoc->load("$FUSIONSRC\\$SUBDIR1\\$SUBDIR2\\$SUBDIR3\\$ASMMANFILE");
$docError = $AsmManifestDoc->{parseError};
$ValidateSuccess = 1;
if ($docError->{errorCode} != 0)
{
logmsg("Parse error occurred in manifest file, skipping this component\n");
logmsg("Line: [$docError->{line}], LinePos: [$docError->{linepos}]\n");
logmsg("Reason: [$docError->{reason}]\n");
$ValidateSuccess = 0;
}
else
{
$ASMIDNode = $AsmManifestDoc->{documentElement}->selectSingleNode("assemblyIdentity");
if (defined ($ASMIDNode))
{
if (defined ($ASMIDNode->{attributes}))
{
# create an assembly node for insertion later into our msi template
$TempAsmNode = $MsiTemplateDoc->createElement("Assembly");
$TempAsmNode->setAttribute("Manifest", "$ASMMANFILE.$FileCounter");
$TempAsmNode->{text} = "_$SUBDIR3.$FileCounter"; # prepending a "_" to conform to MSI ID naming convention
# here, we want to go through the list of attributes on the assembly id node, and create a set of properties for it
# in the MsiAssemblyName table, except type, which goes into assembly node as well.
for ($i = 0; $i < $ASMIDNode->{attributes}->{length}; $i++)
{
$AsmIDNodeAttribute = $ASMIDNode->{attributes}->item($i);
if ($AsmIDNodeAttribute->{nodeName} =~ /^type$/i)
{
$TempAsmNode->setAttribute("Type", $AsmIDNodeAttribute->{nodeValue});
}
$TempPropNode1 = $MsiTemplateDoc->createElement("Property");
$TempPropNode1->setAttribute("Value", "$AsmIDNodeAttribute->{nodeValue}");
$TempPropNode1->{text} = $AsmIDNodeAttribute->{nodeName};
$TempAsmNode->appendChild($TempPropNode1);
logmsg("MsiAssemblyName: Name is $AsmIDNodeAttribute->{nodeName}, Value is $AsmIDNodeAttribute->{nodeValue}");
}
}
}
else
{
# no assemblyIdentity node found, this manifest is invalid, log it and continue
$ValidateSuccess = 0;
logmsg("Found manifest file is invalid, it does not contain an assembly identity node.");
}
}
if ($ValidateSuccess)
{
# generate a GUID for this component
($ASMGUID) = `uuidgen`;
chomp $ASMGUID;
$ASMGUID =~ tr/a-z/A-Z/;
$MsiComponentID = "_$SUBDIR1$SUBDIR2$SUBDIR3"; # prepending a "_" to conform to MSI ID naming convention
# add all files in the directory to the msitemplate
opendir(DIRHANDLE4, "$FUSIONSRC\\$SUBDIR1\\$SUBDIR2\\$SUBDIR3");
@ASMFILES = grep { $_ ne '.' and $_ ne '..' } readdir(DIRHANDLE4);
$TempCompNode = $MsiTemplateDoc->createElement("Component");
$TempCompNode->setAttribute("Id", $ASMGUID);
$TempCompNode->setAttribute("Win64", $ISWIN64);
$TempCompNode->{text} = $MsiComponentID;
$TempDirNode3->appendChild($TempCompNode);
foreach $ASMFILE (@ASMFILES)
{
# when processing the manifest file, we also note down its manifest file ID in the MSI
logmsg("This file is $ASMFILE");
$TempFileNode = $MsiTemplateDoc->createElement("File");
$TempFileNode->{text} = "$ASMFILE.$FileCounter";
$TempFileNode->setAttribute("DiskId", "1");
$TempFileNode->setAttribute("Name", Win32::GetShortPathName($ASMFILE));
$TempFileNode->setAttribute("LongName", $ASMFILE);
$TempFileNode->setAttribute("src", "$FUSIONSRC\\$SUBDIR1\\$SUBDIR2\\$SUBDIR3\\$ASMFILE");
$TempCompNode->appendChild($TempFileNode);
}
$MsiCompRoot->appendChild($TempDirNode1);
# create the assembly feature root node first if we have not done so
if (!$bCreatedFeatureNode)
{
$TempFeatureRoot = $MsiTemplateDoc->createElement("Feature");
$TempFeatureRoot->setAttribute("Title", "FusionInstall");
$TempFeatureRoot->setAttribute("Display", "hidden");
$TempFeatureRoot->setAttribute("Level", "1");
$TempFeatureRoot->setAttribute("AllowAdvertise", "system");
$TempFeatureRoot->setAttribute("FollowParent", "yes");
$TempFeatureRoot->{text} = "MUIFusionInstall";
$MsiFeatureRoot->appendChild($TempFeatureRoot);
$MsiFeatureRoot = $TempFeatureRoot;
$bCreatedFeatureNode = 1;
}
# add the feature component specification for the assembly
if (defined($TempAsmNode))
{
$TempCompNode = $MsiTemplateDoc->createElement("Component");
$TempCompNode->{text} = $MsiComponentID;
$TempCompNode->appendChild($TempAsmNode);
$MsiFeatureRoot->appendChild($TempCompNode);
}
$FileCounter += 1;
}
closedir(DIRHANDLE3);
}
}
closedir(DIRHANDLE2);
}
}
closedir(DIRHANDLE1);
}
}
}
# save the xml file
$MsiTemplateDoc->save($MUIMSIXML);
chdir $CURRENTDIR; # go back to the old current directory
return 1;
}
##################################################################################
#
# InsertEula
#
# This function will look for a specially marked xml node inside the MSI template
# called <MUIEULAText>, and then it will create a <TEXT> sibling xml node to
# the found node and insert the EULA text content in the TEXT node. Then it will
# delete the MUIEULAText node from the template
#
##################################################################################
sub InsertEula
{
Win32::OLE::CreateObject("Msxml2.DOMDocument", $MsiTemplateDoc) or die "Can't create XMLDOM\n";
$MsiTemplateDoc->{async} = 0;
$MsiEulaStart = "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033{\\fonttbl{\\f0\\fnil\\fcharset0 MS Shell Dlg;}}\n";
$MsiEulaFirstLine = "\\viewkind4\\uc1\\pard\\f0\\fs17";
$MsiEulaEnd = "}";
$MsiEulaLinePrefix = "\\par";
$EulaLineCounter = 0;
$EulaContent = "";
$EULASRC = "$MUIDIR\\eula.txt";
# load the xmlvar'ed template
my $loadresult = $MsiTemplateDoc->load($MUIMSIXML);
$docError = $MsiTemplateDoc->{parseError};
if ($docError->{errorCode} != 0)
{
logmsg("Parse error occurred in MSI Template file, exiting\n");
logmsg("Line: [$docError->{line}], LinePos: [$docError->{linepos}]\n");
logmsg("Reason: [$docError->{reason}]\n");
return 0;
}
# find the EULA Text node in the template
$MUIEULATextNode = $MsiTemplateDoc->{documentElement}->selectSingleNode("//MUIEULAText");
if (defined($MUIEULATextNode))
{
logmsg("Found MUI Eula Text node.");
}
else
{
logmsg("Cannot find MUI Eula Text Node in the MSI Template!");
return 0;
}
# create a node "Text" under MUIEULAText node's parent
$TempTextNode = $MsiTemplateDoc->createElement("Text");
$ParentNode = $MUIEULATextNode->{parentNode};
$ParentNode->appendChild($TempTextNode);
$ParentNode->removeChild($MUIEULATextNode);
# read the EULA text into memory, format it properly for insertion into the text node
if(!open(EULASRCFILE, "$EULASRC"))
{
logmsg("Cannot find MUI Eula Text file at $EULASRC!");
return 0;
}
$EulaContent = $MsiEulaStart;
$EulaLineCounter = 1;
while (<EULASRCFILE>)
{
if ($EulaLineCounter == 1)
{
$EulaContent .= "$MsiEulaFirstLine $_";
}
else
{
$EulaContent .= " $MsiEulaLinePrefix $_";
}
$EulaLineCounter++;
}
close(EULASRCFILE);
$EulaContent .= $MsiEulaEnd;
# rename the node "Text" instead of "MUIEULAText" and add in the new EulaContent
# $TempTextNode->{nodeName} = "Text";
$TempTextNode->{text} = $EulaContent;
# save the xml file
$MsiTemplateDoc->save($MUIMSIXML);
logmsg("Successfully inserted EULA text content into the template file.");
return 1;
}
##################################################################################
#
# InsertReserveCost
#
# This function will read the LangpackCost section of the mui.inf file, and
# build them into the ReserveCost table inside the MSI template.
#
##################################################################################
sub InsertReserveCost
{
my(@langpackcost, $section_name);
Win32::OLE::CreateObject("Msxml2.DOMDocument", $MsiTemplateDoc) or die "Can't create XMLDOM\n";
$MsiTemplateDoc->{async} = 0;
# load the xmlvar'ed template
my $loadresult = $MsiTemplateDoc->load($MUIMSIXML);
$docError = $MsiTemplateDoc->{parseError};
if ($docError->{errorCode} != 0)
{
logmsg("Parse error occurred in MSI Template file, exiting\n");
logmsg("Line: [$docError->{line}], LinePos: [$docError->{linepos}]\n");
logmsg("Reason: [$docError->{reason}]\n");
return 0;
}
$MuiRootDirNode = $MsiTemplateDoc->{documentElement}->selectSingleNode("//Directory[\@Name = \"SourceDir\"]");
if (defined($MuiRootDirNode))
{
logmsg("InsertReserveCost: Found MUI root directory node.");
}
else
{
logmsg("InsertReserveCost: Cannot find MUI Directory Node in the MSI Template!");
return 0;
}
# find the feature root where we want to insert our assembly feature
$MsiFeatureRoot = $MsiTemplateDoc->{documentElement}->selectSingleNode("//Feature[\@Title = \"BasicInstall\"]");
if (!defined($MsiFeatureRoot))
{
logmsg("InsertReserveCost: Cannot find the proper feature node BasicInstall!");
return 0;
}
else
{
logmsg("InsertReserveCost: Found the proper feature node BasicInstall!");
}
if ($_BuildArch =~ /ia64/i)
{
$section_name = "FileSize_LPK_IA64";
}
else
{
$section_name = "FileSize_LPK";
}
@langpackcost = `perl $RAZZLETOOLPATH\\PostBuildScripts\\parseinf.pl $INFFILE $section_name`;
# for every entry, we write it into the diretory as a component, and also include the component into the
# feature table
foreach $lpkitem (@langpackcost)
{
chop($lpkitem);
if ($lpkitem =~ /(.*)=(.*)/)
{
$lpklcid = $1;
$lpkcost = $2;
logmsg("----- InsertReserveCost: Langpack LCID is $lpklcid, Langpack filesize is $lpkcost");
# basic error checking
if (!defined($lpklcid) || !defined($lpkcost) || ($lpklcid == 0) )
{
logmsg("InsertReserveCost: error reading langpack file size in mui.inf.");
return 0;
}
$TempDirCompNode = $MsiTemplateDoc->createElement("Component");
$TempFeaCompNode = $MsiTemplateDoc->createElement("Component");
$TempReserveCostNode = $MsiTemplateDoc->createElement("ReserveCost");
$TempConditionNode = $MsiTemplateDoc->createElement("Condition");
if (!defined($TempDirCompNode) || !defined($TempFeaCompNode) || !defined($TempReserveCostNode) || !defined($TempConditionNode))
{
logmsg("InsertReserveCost: failed to create xml nodes for insertion into MSI template.");
return 0;
}
($LPKGUID) = `uuidgen`;
chomp $LPKGUID;
$LPKGUID =~ tr/a-z/A-Z/;
$LPKID = "LANGPACKFileCost$lpklcid";
$TempDirCompNode->setAttribute("Id", $LPKGUID);
$TempDirCompNode->setAttribute("Win64", $ISWIN64);
$TempDirCompNode->{text} = $LPKID;
$TempFeaCompNode->{text} = $LPKID;
$TempReserveCostNode->setAttribute("Directory", "SystemFolder");
$TempReserveCostNode->setAttribute("RunLocal", $lpkcost);
$TempReserveCostNode->setAttribute("RunFromSource", "0");
$TempReserveCostNode->{text} = "$LPKID.1";
$TempConditionNode->{text} = "MsiRequireLangPack AND MuiLCID=\"$lpklcid\"";
$TempDirCompNode->appendChild($TempReserveCostNode);
$TempDirCompNode->appendChild($TempConditionNode);
$MuiRootDirNode->appendChild($TempDirCompNode);
$MsiFeatureRoot->appendChild($TempFeaCompNode);
}
}
# save the xml file
$MsiTemplateDoc->save($MUIMSIXML);
logmsg("Successfully inserted ReserveCost table rows into the MSI template.");
return 1;
}
##################################################################################
#
# InsertSKUNodes
#
# This function insert the necessary xml nodes that are going to be
# included in the MSI template as specified by the caller.
#
##################################################################################
sub InsertSKUNodes
{
Win32::OLE::CreateObject("Msxml2.DOMDocument", $MsiTemplateDoc) or die "Can't create XMLDOM\n";
$MsiTemplateDoc->{async} = 0;
# load the xmlvar'ed template
my $loadresult = $MsiTemplateDoc->load($MUIMSIXML);
$docError = $MsiTemplateDoc->{parseError};
if ($docError->{errorCode} != 0)
{
logmsg("Parse error occurred in MSI Template file, exiting\n");
logmsg("Line: [$docError->{line}], LinePos: [$docError->{linepos}]\n");
logmsg("Reason: [$docError->{reason}]\n");
return 0;
}
$MuiRootDirNode = $MsiTemplateDoc->{documentElement}->selectSingleNode("//Directory[\@Name = \"SourceDir\"]");
if (defined($MuiRootDirNode))
{
logmsg("InsertSKUNodes: Found MUI root directory node.");
}
else
{
logmsg("InsertSKUNodes: Cannot find MUI Directory Node in the MSI Template!");
return 0;
}
# find the feature root where we want to insert our assembly feature
$MsiFeatureRoot = $MsiTemplateDoc->{documentElement}->selectSingleNode("//Feature[\@Title = \"BasicInstall\"]");
if (!defined($MsiFeatureRoot))
{
logmsg("InsertSKUNodes: Cannot find the proper feature node BasicInstall!");
return 0;
}
else
{
logmsg("InsertSKUNodes: Found the proper feature node BasicInstall!");
}
for $skuitem (keys %SKUList)
{
logmsg("InsertSKUNodes: Inserting $skuitem SKU Nodes into Directory and Feature nodelists");
$TempDirModNode = $MsiTemplateDoc->createElement("Module");
$TempFeaModNode = $MsiTemplateDoc->createElement("Module");
$TempDirModNode->setAttribute("DiskId", "1");
$TempDirModNode->setAttribute("src", $SKUList{$skuitem}{MergModeFileName});
$TempDirModNode->{text} = $skuitem;
$TempFeaModNode->{text} = $skuitem;
$MuiRootDirNode->appendChild($TempDirModNode);
$MsiFeatureRoot->appendChild($TempFeaModNode);
}
# insert an launch condition for each of the SKU not present so that the MSI package can't be run
# on that SKU
for $skuexitem (keys %SKUExList)
{
logmsg("InsertSKUNodes: Inserting launch condition exclusion node for $skuexitem SKU into MSI template.");
$TempCondNode = $MsiTemplateDoc->createElement("Condition");
$TempCondNode->setAttribute("Message", $SKUExList{$skuexitem}{Message});
$TempCondNode->{text} = $SKUExList{$skuexitem}{Condition};
$TempHeadCondNode = $MsiTemplateDoc->{documentElement}->selectSingleNode("Condition[1]");
if (defined ($TempHeadCondNode))
{
$MsiTemplateDoc->{documentElement}->insertBefore($TempCondNode, $TempHeadCondNode);
}
else
{
$MsiTemplateDoc->{documentElement}->appendChild($TempCondNode);
}
}
# save the xml file
$MsiTemplateDoc->save($MUIMSIXML);
logmsg("Successfully inserted SKU merge module XML nodes.");
return 1;
}
##################################################################################
#
# DeleteMSITables
#
# This function is used to remove the unused tables AdminUISequence,
# AdminExecuteSequence, AdvUISequence and AdvExecuteSequence tables
#
##################################################################################
sub DeleteMSITables
{
my($Installer, $MUIMSIDB, $SqlQuery, $MUIMSIView);
Win32::OLE::CreateObject("WindowsInstaller.Installer", $Installer) or die "Can't create Windows Installer Object\n";
# check if the result MSI package exist
if (!(-e $MUIMSI))
{
errmsg ("DeleteMSITables error: Cannot locate the MSI package $MUIMSI.");
return 0;
}
logmsg("Opening MSI Package $MUIMSI for table deletion.");
# if so, open the package
$MUIMSIDB = $Installer->OpenDatabase($MUIMSI, 2); # open in direct, no transaction
if (!defined($MUIMSIDB))
{
errmsg ("DeleteMSITables error: Cannot open the MSI package $MUIMSI.");
return 0;
}
# delete the tables we don't want
foreach (@DeleteTableList)
{
$SqlQuery = "DROP TABLE $_";
$MUIMSIView = $MUIMSIDB->OpenView($SqlQuery);
if (!defined($MUIMSIView))
{
$MUIMSIDB->Commit(); # flush all the buffers, even though this is a fatal error.
errmsg ("DeleteMSITables error: Cannot open a view on the MSI package.");
return 0;
}
$MUIMSIView->Execute();
$MUIMSIView->Close();
logmsg("Deleted MSI table $_");
}
# commit the changes to the package
$MUIMSIDB->Commit();
logmsg("DeleteMSITables: Successfully deleted unused tables from the MSI package.");
return 1;
}
##################################################################################
#
# InsertCatFiles
#
# This function will reinsert the catalog files back into the asian printer
# driver msi packages. We do this for all 4 msi packages regardless of what
# language is being built.
#
##################################################################################
sub InsertCatFiles
{
$MUI_PRINTER_DRIVER_DIR = "$DESTDIR\\printer";
$MUI_CAT_DIR = "$MUIDIR\\printer";
# we will do this only for cd1 in RC2, will need to change this and muimake for final release
if ((uc($CDLAYOUT) eq "CD1" ) && (-e $MUI_PRINTER_DRIVER_DIR) && (-e $MUI_CAT_DIR))
{
%PrinterDriver = (); # list of printer driver files msi file names and the catalog file names
$PrinterDriver{"chs"} = {
MsiFile=> "chsprint.msi",
CatFile => "chspack.cat",
};
$PrinterDriver{"cht"} = {
MsiFile=> "chtprint.msi",
CatFile => "chtpack.cat",
};
$PrinterDriver{"jpn"} = {
MsiFile=> "jpnprint.msi",
CatFile => "jpnpack.cat",
};
$PrinterDriver{"kor"} = {
MsiFile=> "korprint.msi",
CatFile => "korpack.cat",
};
for $langitem (keys %PrinterDriver)
{
logmsg("Printer driver MSI file is at $MUI_PRINTER_DRIVER_DIR\\$PrinterDriver{$langitem}{MsiFile}");
logmsg("Printer driver CAT file is at $MUI_CAT_DIR\\$PrinterDriver{$langitem}{CatFile}");
$returnResult = system("msicab.exe -r $MUI_PRINTER_DRIVER_DIR\\$PrinterDriver{$langitem}{MsiFile} $MUI_CAT_DIR\\$PrinterDriver{$langitem}{CatFile}");
if ($returnResult)
{
logmsg("ERROR: Failed to insert catalog file into the printer MSI file!");
return 0;
}
# also delete the cat file from the release directory - we don't want those to be there
if (-e "$MUI_PRINTER_DRIVER_DIR\\$PrinterDriver{$langitem}{CatFile}")
{
logmsg("Deleting $MUI_PRINTER_DRIVER_DIR\\$PrinterDriver{$langitem}{CatFile} from the release point.");
$returnedResult = system("del /F /Q $MUI_PRINTER_DRIVER_DIR\\$PrinterDriver{$langitem}{CatFile}");
if ($returnedResult)
{
logmsg("Warning: failed to delete catalog file $PrinterDriver{$langitem}{CatFile}.");
}
}
}
}
else
{
logmsg("INFO: InsertCatFiles skipped - CDLayout is $CDLAYOUT, MSI printer directory is $MUI_PRINTER_DRIVER_DIR, Catalog directory is $MUI_CAT_DIR");
}
logmsg("InsertCatFiles: Successfully inserted the catalog files into the Asian Printer Driver MSI Packages.");
return 1;
}
##################################################################################
#
# Cmd entry point for script.
#
##################################################################################
if (eval("\$0 =~ /" . __PACKAGE__ . "\\.pm\$/i"))
{
# Step 1: Parse the command line
# <run perl.exe GetParams.pm /? to get the complete usage for GetParams.pm>
&GetParams ('-o', 'l:psadwb', '-p', 'lang prosku srvsku advsku dtcsku websku sbssku', @ARGV);
# Include local environment extensions
&LocalEnvEx::localenvex('initialize');
# Set lang from the environment
$LANG=$ENV{lang};
# $Special_Lang = "JPN"; // commented out, these are set in GetCodes
# $LCID_SHORT = "0411"; // commented out, these are set in GetCodes
# Validate the option given as parameter.
&ValidateParams;
# Set flag indicating that we run from command prompt.
$cmdPrompt = 1;
# Step 4: Call the main function
&muimsi::Main();
# End local environment extensions.
&LocalEnvEx::localenvex('end');
}