|
|
################################################################################# # # 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'); }
|