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

  1. #################################################################################
  2. #
  3. # Script: muimsi.pm
  4. #
  5. # (c) 2000 Microsoft Corporation. All rights reserved.
  6. #
  7. # Purpose: This script creates the msi package
  8. #
  9. # Version: 1.00 (06/27/2001) : (lguindon) Created
  10. #
  11. ##################################################################################
  12. # Set Package
  13. package muimsi;
  14. # Set the script name
  15. $ENV{script_name} = 'muimsi.pm';
  16. $cmdPrompt = 0;
  17. # Set version
  18. $VERSION = '1.00';
  19. # Set required perl version
  20. require 5.003;
  21. # Use section
  22. use lib $ENV{ "RazzleToolPath" };
  23. use lib $ENV{ "RazzleToolPath" } . "\\PostBuildScripts";
  24. use GetParams;
  25. use LocalEnvEx;
  26. use Logmsg;
  27. use strict;
  28. no strict 'vars';
  29. use HashText;
  30. use ParseTable;
  31. use File::Copy;
  32. use File::Find;
  33. use Cwd;
  34. use DirHandle;
  35. use Win32::OLE;
  36. use Win32::File;
  37. # Require Section
  38. require Exporter;
  39. # Global variable section
  40. %SKUList = (); #list of skus to include files for, populated in DefineConstants()
  41. %SKUExList = (); # list of skus to excluse files for and to prevent installation on, populated in DefineConstants()
  42. @DeleteTableList = ("AdminUISequence", "AdminExecuteSequence"); # names of MSI tables that we want to delete
  43. ##################################################################################
  44. #
  45. # Main entry point.
  46. #
  47. ##################################################################################
  48. sub Main
  49. {
  50. # if language is US (or not set), we just return and log a warning. This is
  51. # so that we can include the script in prs postbuild so that
  52. # we will repack the signed binaries back into the MSI
  53. if (($lang=~/usa/i) || (!$lang))
  54. {
  55. logmsg("MUI MSI package is not produced for US build.");
  56. }
  57. else
  58. {
  59. # Check environment variables
  60. if (!&VerifyEnvironment())
  61. {
  62. errmsg ("The environment is not correct for MSI Package build.");
  63. return 0;
  64. }
  65. # Get language's LCID and ISO code
  66. if (!&GetCodes())
  67. {
  68. errmsg ("The language's LCID and ISO code could not be extracted from the CODEFILE.");
  69. return 0;
  70. }
  71. # Define some constants
  72. if (!&DefineConstants())
  73. {
  74. errmsg ("Constants could not be defined.");
  75. return 0;
  76. }
  77. # Make sure directories exist
  78. if (!&CheckDirs())
  79. {
  80. errmsg ("The required directorys do not exist.");
  81. return 0;
  82. }
  83. # Search for current build number
  84. if (!&LookForBuildNumber())
  85. {
  86. errmsg ("No build number information found.");
  87. }
  88. # Apply XMLVAR to the template
  89. if (!&XMLVAR())
  90. {
  91. errmsg ("Error with XMLVAR script.");
  92. }
  93. # Modify the template output generated by XMLVAR to include fusion support
  94. if (!&GenFusionAssemblyXML())
  95. {
  96. errmsg ("Error with GenFusionAssemblyXML script.");
  97. }
  98. # Insert Eula content into the template
  99. if (!&InsertEula())
  100. {
  101. errmsg ("Failed to insert Eula Text into the MSI Template - the EULA dialog will be empty in the build.");
  102. }
  103. # Insert the reserve cost nodes
  104. if (!&InsertReserveCost())
  105. {
  106. errmsg ("Failed to insert reserved diskcost nodes for language packs into the MSI Template MSI diskcost will not include langpack disk costs.");
  107. }
  108. # Insert the skus that we want to merge into the final package
  109. if (!&InsertSKUNodes())
  110. {
  111. errmsg ("Failed to delete the unused SKU merge module nodes from the MSI Template.");
  112. }
  113. # Generate file contents files
  114. if (!&FileContents())
  115. {
  116. errmsg ("File contents couldn't be created.");
  117. }
  118. # Generate custom action files
  119. if (!&CustomAction())
  120. {
  121. errmsg ("Error with Custom Action script.");
  122. }
  123. # Make MSI package
  124. if (!&MakeMSI())
  125. {
  126. errmsg ("Error making the MSI Package.");
  127. }
  128. # Delete the unused MSI table generated as part of the WiX Build process
  129. if (!&DeleteMSITables())
  130. {
  131. errmsg ("Error deleting the unused MSI tables.");
  132. }
  133. # insert the catalog files back into the FE printer packs
  134. if (!&InsertCatFiles())
  135. {
  136. errmsg ("Error inserting catalog files into the FE printer MSI packages.");
  137. }
  138. }
  139. } # Main
  140. ##################################################################################
  141. #
  142. # VerifyEnvironment()
  143. #
  144. # Validates necessary environment variables.
  145. #
  146. ##################################################################################
  147. sub VerifyEnvironment
  148. {
  149. logmsg ("Validating the environment.");
  150. $RAZZLETOOLPATH=$ENV{RazzleToolPath};
  151. $_NTPOSTBLD=$ENV{_NTPOSTBLD};
  152. logmsg("------- RAZZLETOOLPATH is $RAZZLETOOLPATH");
  153. logmsg("------- _NTPOSBLD is $_NTPOSTBLD");
  154. logmsg("------- LANG is $LANG");
  155. if ($LANG=~/psu_(.*)/i)
  156. {
  157. $Special_Lang=$1;
  158. }
  159. elsif ($LANG=~/psu/i || $LANG=~/mir/i || $LANG=~/FE/i)
  160. {
  161. if (defined( $ENV{"LANG_MUI"} ) )
  162. {
  163. $Special_Lang=$ENV{"LANG_MUI"};
  164. }
  165. elsif ($LANG=~/psu/i)
  166. {
  167. $Special_Lang="ro";
  168. }
  169. elsif ($LANG=~/FE/i)
  170. {
  171. $Special_Lang="jpn";
  172. }
  173. else
  174. {
  175. $Special_Lang="ara";
  176. }
  177. }
  178. else
  179. {
  180. $Special_Lang=$LANG;
  181. }
  182. logmsg ("------- special lang is $Special_Lang");
  183. $PROCESSOR=$ENV{PROCESSOR};
  184. if ($ENV{_BuildArch} =~/x86/i)
  185. {
  186. $_BuildArch="i386";
  187. }
  188. else
  189. {
  190. $_BuildArch=$ENV{_BuildArch};
  191. }
  192. $_BuildArch_release=$ENV{_BuildArch};
  193. $LOGS=$ENV{temp};
  194. logmsg("------- Build architecture is $_BuildArch");
  195. logmsg("------- LOGS is $LOGS");
  196. if ($ENV{_BuildType} =~ /^chk$/i)
  197. {
  198. wrnmsg ("This does not run on chk machines");
  199. return 0;
  200. }
  201. if(defined($SAFEMODE=$ENV{SAFEMODE}))
  202. {
  203. logmsg ("SAFEMODE is ON");
  204. }
  205. logmsg ("Success: Validating the environment.");
  206. return 1;
  207. } # VerifyEnvironment
  208. ##################################################################################
  209. #
  210. # DefineConstants()
  211. #
  212. # Defines global constants.
  213. #
  214. ##################################################################################
  215. sub DefineConstants
  216. {
  217. logmsg ("Definition of global constants.");
  218. # Directories
  219. $LOCBINDIR = "$_NTPOSTBLD";
  220. $MUIDIR = "$LOCBINDIR\\mui";
  221. $MSIDIR = "$MUIDIR\\MSI";
  222. $CONTROLDIR = "$MUIDIR\\control";
  223. $SOURCEDIR = "$MUIDIR\\$Special_Lang\\$_BuildArch.uncomp";
  224. $INFFILE = "$MUIDIR\\mui.inf";
  225. $CDLAYOUT = GetCDLayOut();
  226. $DESTDIR = "$MUIDIR\\release\\$_BuildArch_release\\$CDLAYOUT";
  227. $TMPBUILD = "$MUIDIR\\$Special_Lang\\tmp";
  228. $TEMPLATE = "$MSIDIR\\muimsi_template.wim";
  229. logmsg("------- LOCBINDIR is $LOCBINDIR");
  230. logmsg("------- MUIDIR is $MUIDIR");
  231. logmsg("------- MSIDIR is $MSIDIR");
  232. logmsg("------- CONTROLDIR is $CONTROLDIR");
  233. logmsg("------- SOURCEDIR is $SOURCEDIR");
  234. logmsg("------- DESTDIR is $DESTDIR");
  235. logmsg("------- TMPBUILD is $TMPBUILD");
  236. logmsg("------- CDLAYOUT is $CDLAYOUT");
  237. # Filenames
  238. # kenhsu - some build filenames are different on IA64
  239. if (($ENV{_BuildArch} =~/x86/i))
  240. {
  241. $GUIDFILE = "$MSIDIR\\guidlang.txt";
  242. $PLATFORM = "Intel";
  243. $IA64CONDITION = "NOT VersionNT64";
  244. $ISWIN64 = "no";
  245. $SYSFOLDERPROP = "SystemFolder";
  246. }
  247. else
  248. {
  249. $GUIDFILE = "$MSIDIR\\guidlang64.txt";
  250. $PLATFORM = "Intel64";
  251. $IA64CONDITION = "VersionNT64";
  252. $ISWIN64 = "yes";
  253. $SYSFOLDERPROP = "System64Folder";
  254. }
  255. logmsg("MSI Package platform is $PLATFORM");
  256. logmsg("MSI Template file used is $TEMPLATE");
  257. logmsg("MSI GUID file used is $GUIDFILE");
  258. $SRCRELNOTE = "$MSIDIR\\msinotes.htm";
  259. $SRCXPBITMAP = "$MSIDIR\\muimsi_hl.bmp";
  260. $SRCMUISETUP = "$MUIDIR\\muisetup.exe";
  261. $SRCMUIMSIDLL = "$MSIDIR\\muimsidll.dll";
  262. $DSTRELNOTE = "$DESTDIR\\msinotes.htm";
  263. $MUIMSIXML = "$TMPBUILD\\$LCID_SHORT.wim";
  264. $MUIMSIXMLTemp = "$TMPBUILD\\Temp$LCID_SHORT.wim";
  265. $MUIMSIWIX = "$TMPBUILD\\$LCID_SHORT.msi";
  266. $MUIMSI = "$DESTDIR\\$LCID_SHORT.msi";;
  267. $FILECNT_CORE = "$TMPBUILD\\FileContent_CORE.wxm"; #core files required in every flavour
  268. $FILELST_CORE = "$TMPBUILD\\FileContent_CORE.msm";
  269. $FILECNT_PRO = "$TMPBUILD\\FileContent_PRO.wxm"; #professional build of NT
  270. $FILELST_PRO = "$TMPBUILD\\FileContent_PRO.msm";
  271. $FILECNT_SRV = "$TMPBUILD\\FileContent_SRV.wxm"; #standard server build of NT
  272. $FILELST_SRV = "$TMPBUILD\\FileContent_SRV.msm";
  273. $FILECNT_ADV = "$TMPBUILD\\FileContent_ADV.wxm"; #advanced server build of NT
  274. $FILELST_ADV = "$TMPBUILD\\FileContent_ADV.msm";
  275. $FILECNT_DTC = "$TMPBUILD\\FileContent_DTC.wxm"; #datacenter build of NT
  276. $FILELST_DTC = "$TMPBUILD\\FileContent_DTC.msm";
  277. $FILECNT_WEB = "$TMPBUILD\\FileContent_WEB.wxm"; #webserver (blade) build of NT
  278. $FILELST_WEB = "$TMPBUILD\\FileContent_WEB.msm";
  279. $FILECNT_SBS = "$TMPBUILD\\FileContent_SBS.wxm"; #small business build of NT
  280. $FILELST_SBS = "$TMPBUILD\\FileContent_SBS.msm";
  281. $CUSTACTION = "$CONTROLDIR\\Custom_action.wxm";
  282. $CUSTACTIONCOMP = "$CONTROLDIR\\Custom_action.msm";
  283. $WISCHEMA = "$MSIDIR\\WiSchema.xml";
  284. # Applications
  285. $INFPARSER = "infparser.exe";
  286. $CANDLE = "CScript.exe $MSIDIR\\candle.wsf";
  287. $LIGHT = "CScript.exe $MSIDIR\\light.wsf";
  288. $XMLVAR = "perl.exe $MSIDIR\\xmlvar.bat";
  289. $COPY = "copy";
  290. # Flavor
  291. if($_BuildArch =~ /i386/i)
  292. {
  293. $FLAVOR = "32";
  294. }
  295. elsif ($_BuildArch =~ /ia64/i)
  296. {
  297. $FLAVOR = "64";
  298. }
  299. logmsg("------- FLAVOR is $FLAVOR");
  300. # GUID - read the language guid off the language-guid file, if it's not there
  301. # generate one and put it in the file.
  302. logmsg ("Get the language's MSI package ID from $GUIDFILE.");
  303. my(@langguids, @newcontent);
  304. # Search GUIDFILE for $Special_Lang and the associated MSI package guid
  305. if(!open(GUIDFILE, "$GUIDFILE"))
  306. {
  307. errmsg ("Can't open language guid file $GUIDFILE for reading.");
  308. return 0;
  309. }
  310. GUID_LOOP: while (<GUIDFILE>)
  311. {
  312. # add the file content to an array, in case we need to append additional data
  313. # push(@newcontent, $_);
  314. if (/^$Special_Lang\s+/i)
  315. {
  316. @langguids = split(/\s+/,$_);
  317. $MSIGUID = $langguids[1]; # 2nd field should be the client language guid
  318. $PRODUCTGUID = $langguids[2]; # 3rd field should be the product language guid
  319. last GUID_LOOP; # exit out of the loop if found
  320. }
  321. }
  322. close(GUIDFILE);
  323. $MSIGUID =~ tr/a-z/A-Z/;
  324. $PRODUCTGUID =~ tr/a-z/A-Z/;
  325. # if the language is not found, add a new guid entry into GUIDFILE
  326. if(!@langguids)
  327. {
  328. logmsg ("$Special_Lang is not found in $GUIDFILE, adding an entry for it.");
  329. ($MSIGUID) = `uuidgen`;
  330. chomp $MSIGUID;
  331. $MSIGUID =~ tr/a-z/A-Z/;
  332. }
  333. ($REGISTRY1GUID) = `uuidgen`;
  334. chomp $REGISTRY1GUID;
  335. $REGISTRY1GUID =~ tr/a-z/A-Z/;
  336. ($REGISTRY3GUID) = `uuidgen`;
  337. chomp $REGISTRY3GUID;
  338. $REGISTRY3GUID =~ tr/a-z/A-Z/;
  339. logmsg("------- REGISTRY1GUID is $REGISTRY1GUID");
  340. logmsg("------- REGISTRY3GUID is $REGISTRY3GUID");
  341. logmsg("------- MSIGUID is $MSIGUID");
  342. logmsg("------- PRODUCTGUID is $PRODUCTGUID");
  343. # Other GUIDs
  344. $UPGRADEGUID = "177146D4-5102-4BD9-98A0-114A3ED39076";
  345. $CONTENTGUID = "1AFE112F-290F-4A94-2000-AB4D3FD8B102";
  346. # MSI version number
  347. my ($src, $isdst);
  348. ($src, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime;
  349. eval $hour;
  350. eval $min;
  351. eval $yday;
  352. eval $wday;
  353. $VERSIONNUMBER = 100*(12*($year-101)+$mon+1)+$mday;
  354. logmsg("------- VERSIONNUMBER is $VERSIONNUMBER");
  355. # Package name
  356. $PACKAGENAME = "muiMSITemplate ($VERSIONNUMBER)";
  357. logmsg("------- PACKAGENAME is $PACKAGENAME");
  358. logmsg ("Success: Definition of global constants.");
  359. $BuildChecksum=1;
  360. logmsg("-------- Including CORE files.");
  361. $SKUList{"Core"} = {
  362. InfParserFlag => "c",
  363. CntLstFileName => "$FILECNT_CORE",
  364. MergModeFileName => "$FILELST_CORE",
  365. };
  366. if (defined($prosku))
  367. {
  368. logmsg("-------- Including files for Professional SKU.");
  369. $SKUList{"Pro"} = {
  370. InfParserFlag => "p",
  371. CntLstFileName => "$FILECNT_PRO",
  372. MergModeFileName => "$FILELST_PRO",
  373. };
  374. }
  375. else
  376. {
  377. logmsg("-------- Excluding files for Professional SKU.");
  378. $SKUExList{"Pro"} = {
  379. Message => "The [ProductName] cannot be installed on Windows XP Professional Edition.",
  380. Condition => "MsiNTProductType <> 1",
  381. };
  382. }
  383. # If this is one of the Client(wks) languages we only want the Core File Content included and Pro Launch Condition
  384. if ($FLV =~ /WKS/i)
  385. {
  386. return 1;
  387. }
  388. if (defined($srvsku))
  389. {
  390. logmsg("-------- Including files for Standard Server SKU.");
  391. $SKUList{"Srv"} = {
  392. InfParserFlag => "s",
  393. CntLstFileName => "$FILECNT_SRV",
  394. MergModeFileName => "$FILELST_SRV",
  395. };
  396. }
  397. else
  398. {
  399. logmsg("-------- Excluding files for Standard Server SKU.");
  400. $SKUExList{"Srv"} = {
  401. Message => "The [ProductName] cannot be installed on Windows .NET 2003, Standard Edition.",
  402. Condition => "MsiNTProductType<>1 AND NOT MsiNTSuiteDataCenter AND NOT MsiNTSuiteEnterprise AND NOT MsiNTSuiteWebServer AND NOT MsiNTSuiteSmallBusiness AND NOT MsiNTSuiteSmallBusinessRestricted",
  403. };
  404. }
  405. if (defined($advsku))
  406. {
  407. logmsg("-------- Including files for Advanced Server SKU.");
  408. $SKUList{"Adv"} = {
  409. InfParserFlag => "a",
  410. CntLstFileName => "$FILECNT_ADV",
  411. MergModeFileName => "$FILELST_ADV",
  412. };
  413. }
  414. else
  415. {
  416. logmsg("-------- Excluding files for Advanced Server SKU.");
  417. $SKUExList{"Adv"} = {
  418. Message => "The [ProductName] cannot be installed on Windows .NET 2003, Enterprise Edition.",
  419. Condition => "NOT MsiNTSuiteEnterprise",
  420. };
  421. }
  422. if (defined($dtcsku))
  423. {
  424. logmsg("-------- Including files for Datacenter Server SKU.");
  425. $SKUList{"Dtc"} = {
  426. InfParserFlag => "d",
  427. CntLstFileName => "$FILECNT_DTC",
  428. MergModeFileName => "$FILELST_DTC",
  429. };
  430. }
  431. else
  432. {
  433. logmsg("-------- Excluding files for Datacenter Server SKU.");
  434. $SKUExList{"Dtc"} = {
  435. Message => "The [ProductName] cannot be installed on Windows .NET 2003, Datacenter Edition.",
  436. Condition => "NOT MsiNTSuiteDataCenter",
  437. };
  438. }
  439. if (defined($websku))
  440. {
  441. logmsg("-------- Including files for Blade Server SKU.");
  442. $SKUList{"Web"} = {
  443. InfParserFlag => "b",
  444. CntLstFileName => "$FILECNT_WEB",
  445. MergModeFileName => "$FILELST_WEB",
  446. };
  447. }
  448. else
  449. {
  450. logmsg("-------- Excluding files for Web Server SKU.");
  451. $SKUExList{"Web"} = {
  452. Message => "The [ProductName] cannot be installed on Windows .NET 2003 Web Server.",
  453. Condition => "NOT MsiNTSuiteWebServer",
  454. };
  455. }
  456. if (defined($sbssku))
  457. {
  458. logmsg("-------- Including files for Small Business Server SKU.");
  459. $SKUList{"Sbs"} = {
  460. InfParserFlag => "l",
  461. CntLstFileName => "$FILECNT_SBS",
  462. MergModeFileName => "$FILELST_SBS",
  463. };
  464. }
  465. else
  466. {
  467. logmsg("-------- Excluding files for Small Business Server SKU.");
  468. $SKUExList{"Sbs"} = {
  469. Message => "The [ProductName] cannot be installed on Windows .NET Server 2003 for Small Business Server.",
  470. Condition => "NOT MsiNTSuiteSmallBusiness AND NOT MsiNTSuiteSmallBusinessRestricted",
  471. };
  472. }
  473. return 1;
  474. } # DefineConstants
  475. ##################################################################################
  476. #
  477. # GetCodes
  478. #
  479. # Gets the language's LCID and ISO language code from CODEFILE.
  480. #
  481. ##################################################################################
  482. sub GetCodes
  483. {
  484. #set the code file name to read
  485. $CODEFILE = "$RAZZLETOOLPATH\\codes.txt";
  486. $DESCFILE = "$_NTPOSTBLD\\mui\\MSI\\langdesc.txt";
  487. logmsg ("Get the language's LCID and ISO language code from $CODEFILE.");
  488. my(@data);
  489. my(@data2);
  490. # Don't allow nec_98 or CHT to be processed!
  491. if($Special_Lang =~ /^(NEC_98|CHT)$/i)
  492. {
  493. logmsg ("No MUI available for $Special_Lang");
  494. exit 0;
  495. }
  496. # Search CODEFILE for $Special_Lang, $LCID, $LANG_ISO, etc.
  497. if(!open(CODEFILE, "$CODEFILE"))
  498. {
  499. errmsg ("Can't open lcid file $CODEFILE for reading.");
  500. return 0;
  501. }
  502. CODES_LOOP: while (<CODEFILE>)
  503. {
  504. if (/^$Special_Lang\s+/i)
  505. {
  506. @data = split(/\s+/,$_);
  507. last CODES_LOOP;
  508. }
  509. }
  510. close(CODEFILE);
  511. if(!@data)
  512. {
  513. logmsg ("$Special_Lang is an unknown language");
  514. &Usage;
  515. return 0;
  516. }
  517. $LCID = $data[2];
  518. $LCID_SHORT = $LCID;
  519. $LCID_SHORT =~ s/^0x//;
  520. $LANG_ISO = $data[8];
  521. $GUID = $data[11];
  522. $FLV = $data[6];
  523. #extract the language description - get this from our lang description file
  524. if(!open(DESCFILE, "$DESCFILE"))
  525. {
  526. errmsg ("Can't open lcid description file $DESCFILE for reading.");
  527. return 0;
  528. }
  529. DESC_LOOP: while (<DESCFILE>)
  530. {
  531. if (/^$LCID_SHORT\s+/i)
  532. {
  533. @data2 = split(/\s+/,$_);
  534. last DESC_LOOP;
  535. }
  536. }
  537. close(DESCFILE);
  538. if(!@data2)
  539. {
  540. logmsg ("Cannot find a language description for $LCID_SHORT.");
  541. return 0;
  542. }
  543. $LANG_NAME = $data2[1];
  544. for ($i = 2; $i <= $#data2; $i++)
  545. {
  546. $LANG_NAME = "$LANG_NAME $data2[$i]";
  547. }
  548. logmsg ("Success: Get the language's LCID and ISO language code frrm $CODEFILE.");
  549. logmsg("------- LCID is $LCID");
  550. logmsg("------- LCID_SHORT is $LCID_SHORT");
  551. logmsg("------- LANG_ISO is $LANG_ISO");
  552. logmsg("------- GUID is $GUID");
  553. logmsg("------- LANG_NAME is $LANG_NAME");
  554. logmsg("------- FLV is $FLV");
  555. return 1;
  556. } # GetCodes
  557. ##################################################################################
  558. #
  559. # CheckDirs
  560. #
  561. # Makes sure that the necessary directories exist.
  562. #
  563. ##################################################################################
  564. sub CheckDirs
  565. {
  566. logmsg ("Make sure the necessary directories exist.");
  567. my($status);
  568. # Make sure the source directories exist
  569. foreach ($LOCBINDIR,$MUIDIR,$CONTROLDIR,$SOURCEDIR,$MSIDIR)
  570. {
  571. if(! -e $_)
  572. {
  573. logmsg ("$0: ERROR: $_ does not exist");
  574. return 0;
  575. }
  576. }
  577. # Make sure destination directories exist
  578. foreach ($DESTDIR,$TMPBUILD)
  579. {
  580. if(! -e $_)
  581. {
  582. $status = system("md $_");
  583. if($status)
  584. {
  585. logmsg ("$0: ERROR: cannot create $_");
  586. return 0;
  587. }
  588. }
  589. }
  590. logmsg ("del /q /f $TMPBUILD\\*.*");
  591. $returnStatus = system("del /q /f $TMPBUILD\\*.*");
  592. if ($returnStatus)
  593. {
  594. logmsg ("INFO: script did not find previously generated MSI build files.");
  595. }
  596. logmsg ("Success: Make sure the necessary directories exist.");
  597. return 1;
  598. } # CheckDirs
  599. ##################################################################################
  600. #
  601. # LookForBuildNumber
  602. #
  603. # Scan build log for the build number.
  604. #
  605. ##################################################################################
  606. sub LookForBuildNumber
  607. {
  608. logmsg ("Update mui.inf with the current build number.");
  609. # Get current build number
  610. $LOGS="$_NTPOSTBLD\\build_logs";
  611. my $filename="$LOGS\\buildname.txt";
  612. open (BUILDNAME, "< $filename") or die "Can't open $filename for reading: $!\n";
  613. while (<BUILDNAME>)
  614. {
  615. $BLDNO = $_;
  616. $BLDNO =~ s/\.[\w\W]*//i;
  617. }
  618. close BUILDNAME;
  619. if ($BLDNO =~ /(^\d+)-*\d*$/) {
  620. $BLDNO = $1;
  621. }
  622. else
  623. {
  624. errmsg ("Errorin LookForBuildNumber: BLDNO is $BLDNO");
  625. return 0;
  626. }
  627. chomp($BLDNO);
  628. logmsg("------- BLDNO is $BLDNO");
  629. logmsg ("Success: Update mui.inf with the current build number.");
  630. return 1;
  631. } # LookForBuildNumber
  632. ##################################################################################
  633. #
  634. # FileContents()
  635. #
  636. # Generate different flavor of filecontents.wxm.
  637. #
  638. ##################################################################################
  639. sub FileContents
  640. {
  641. logmsg ("Generate MSI file contents.");
  642. for $skuitem (keys %SKUList)
  643. {
  644. $infparserParam = "/P:$CDLAYOUT /i:$LOCBINDIR /b:$FLAVOR /l:$Special_Lang /f:$SKUList{$skuitem}{InfParserFlag} /s:$MUIDIR /o:$SKUList{$skuitem}{CntLstFileName}";
  645. $compileParam = "-c $SKUList{$skuitem}{MergModeFileName} $SKUList{$skuitem}{CntLstFileName}";
  646. $linkParam = $SKUList{$skuitem}{MergModeFileName};
  647. logmsg ("Generate MSI $skuitem content.");
  648. logmsg ("$INFPARSER $infparserParam");
  649. $returnResult = system("$INFPARSER $infparserParam");
  650. if ($returnResult)
  651. {
  652. logmsg("ERROR: unable to generate MSI $skuitem content! ");
  653. return 0;
  654. }
  655. # Compile core file content
  656. logmsg ("Compile MSI $skuitem content.");
  657. logmsg ("$CANDLE $compileParam");
  658. $returnResult = system("$CANDLE $compileParam");
  659. if ($returnResult)
  660. {
  661. logmsg("ERROR: unable to compile MSI $skuitem content! ");
  662. return 0;
  663. }
  664. # Link core file content
  665. logmsg ("Link MSI $skuitem contents.");
  666. logmsg ("$LIGHT $linkParam");
  667. $returnResult = system("$LIGHT $linkParam");
  668. if ($returnResult)
  669. {
  670. logmsg("ERROR: unable to link MSI $skuitem content! ");
  671. return 0;
  672. }
  673. }
  674. logmsg ("Success: Generate MSI file contents.");
  675. return 1;
  676. } #FileContents
  677. ##################################################################################
  678. #
  679. # CustomAction()
  680. #
  681. # Generate file associated with custom action if needed.
  682. #
  683. ##################################################################################
  684. sub CustomAction
  685. {
  686. logmsg ("Generate MSI file contents.");
  687. # Do something
  688. logmsg ("Success: Generate MSI file contents.");
  689. return 1;
  690. } #CustomAction
  691. ##################################################################################
  692. #
  693. # XMLVAR()
  694. #
  695. # Generate a language specific msi template.
  696. #
  697. ##################################################################################
  698. sub XMLVAR
  699. {
  700. my ($xmlvarParam);
  701. logmsg ("Generate language specific msi template.");
  702. $xmlvarParam = " srcmuiinf=\"$INFFILE\"";
  703. $xmlvarParam .= " regkey1guid=\"$REGISTRY1GUID\"";
  704. $xmlvarParam .= " regkey3guid=\"$REGISTRY3GUID\"";
  705. $xmlvarParam .= " IA64CONDITION=\"$IA64CONDITION\"";
  706. $xmlvarParam .= " PLATFORM=\"$PLATFORM\"";
  707. $xmlvarParam .= " LANG=\"$Special_Lang\"";
  708. $xmlvarParam .= " srcmuimsidll=\"$SRCMUIMSIDLL\"";
  709. $xmlvarParam .= " srcmuisetup=\"$SRCMUISETUP\"";
  710. $xmlvarParam .= " srcxpbitmap=\"$SRCXPBITMAP\"";
  711. $xmlvarParam .= " msiguid=\"$MSIGUID\"";
  712. $xmlvarParam .= " productguid=\"$PRODUCTGUID\"";
  713. $xmlvarParam .= " namePackage=\"$PACKAGENAME\"";
  714. $xmlvarParam .= " ver=\"1.0.$VERSIONNUMBER.0\"";
  715. $xmlvarParam .= " guidUpgradeCode=\"$UPGRADEGUID\"";
  716. $xmlvarParam .= " debugdir=\"$TMPBUILD\"";
  717. $xmlvarParam .= " Language=\"$LANG_NAME\"";
  718. $xmlvarParam .= " BLD=\"$BLDNO\"";
  719. $xmlvarParam .= " CORESRC=\"$FILELST_CORE\"";
  720. $xmlvarParam .= " LCID=\"$LCID_SHORT\"";
  721. $xmlvarParam .= " ISWIN64=\"$ISWIN64\"";
  722. $xmlvarParam .= " SYSFOLDERPROP=\"$SYSFOLDERPROP\"";
  723. $xmlvarParam .= " srcSchema=\"$WISCHEMA\"";
  724. $xmlvarParam .= " < $TEMPLATE > $MUIMSIXMLTemp";
  725. # Generate [LCID].WIM based on the template
  726. logmsg ("Run XMLVAR on the generic template.");
  727. logmsg("$XMLVAR $xmlvarParam");
  728. $returnResult = system("$XMLVAR $xmlvarParam");
  729. if ($returnResult)
  730. {
  731. logmsg("ERROR: XMLVAR failed!");
  732. return 0;
  733. }
  734. logmsg ("Success: Generate language specific msi template.");
  735. return 1;
  736. } #XMLVAR
  737. ##################################################################################
  738. #
  739. # MakeMSI()
  740. #
  741. # Build the MSI package.
  742. #
  743. ##################################################################################
  744. sub MakeMSI
  745. {
  746. $MEGEMODDLL = "$RAZZLETOOLPATH\\x86\\mergemod.dll";
  747. logmsg ("Create MSI package.");
  748. logmsg ("Registering mergemod.dll");
  749. logmsg ("regsvr32 /s $MEGEMODDLL");
  750. $returnResult = system("regsvr32 /s $MEGEMODDLL");
  751. if ($returnResult)
  752. {
  753. logmsg("ERROR: failed to register $MEGEMODDLL!");
  754. return 0;
  755. }
  756. # Compile language specific template
  757. logmsg ("Compile the language specific template.");
  758. logmsg("$CANDLE -o -e -c $MUIMSIWIX $MUIMSIXML");
  759. $returnResult = system("$CANDLE -o -e -c $MUIMSIWIX $MUIMSIXML");
  760. if ($returnResult)
  761. {
  762. logmsg("ERROR: failed to compile language specific template");
  763. return 0;
  764. }
  765. # Link language specific template
  766. logmsg ("Link the language specific template.");
  767. logmsg ("$LIGHT -r -b $TMPBUILD -o $MUIMSI $MUIMSIWIX");
  768. $returnResult = system("$LIGHT -r -b $TMPBUILD -o $MUIMSI $MUIMSIWIX ");
  769. if ($returnResult)
  770. {
  771. logmsg("ERROR: failed to link $MEGEMODDLL!");
  772. return 0;
  773. }
  774. # copy the release notes file to the release directory
  775. logmsg ("Copying release notes file.");
  776. logmsg ("$COPY /y $SRCRELNOTE $DSTRELNOTE");
  777. $returnResult = system("$COPY /y $SRCRELNOTE $DSTRELNOTE");
  778. if ($returnResult)
  779. {
  780. logmsg("ERROR: failed to copy the MSI release notes file!");
  781. return 0;
  782. }
  783. logmsg ("Success: Create MSI package.");
  784. return 1;
  785. } #MakeMSI
  786. ##################################################################################
  787. #
  788. # ValidateParams()
  789. #
  790. # VAlidate parameters.
  791. #
  792. ##################################################################################
  793. sub ValidateParams
  794. {
  795. #<Add your code for validating the parameters here>
  796. }
  797. ##################################################################################
  798. #
  799. # Usage()
  800. #
  801. # Print usage.
  802. #
  803. ##################################################################################
  804. sub Usage
  805. {
  806. print <<USAGE;
  807. muimsi.pm is used to create the MUI MSI package for the specified language.
  808. Usage: $0 [-l lang] [-p] [-s] [-a] [-d] [-w] [-b]
  809. -l Language
  810. -p include Professional SKU MUI files
  811. -s include Standard Server SKU MUI files
  812. -a include Advanced Server SKU MUI files
  813. -d include Datacenter server SKU MUI files
  814. -w include Blade server SKU MUI files
  815. -b include Small Business Server MUI files
  816. -? Displays usage
  817. Example:
  818. $0 -l jpn
  819. Generate MUI MSI package for Japanese language, include only core files.
  820. $0 -l jpn -p -s
  821. Generate MUI MSI package for Japanese language, include core, professional and standard server files.
  822. USAGE
  823. }
  824. ##################################################################################
  825. #
  826. # GetParams()
  827. #
  828. # Get language parameter.
  829. #
  830. ##################################################################################
  831. sub GetParams
  832. {
  833. # Step 1: Call pm getparams with specified arguments
  834. &GetParams::getparams(@_);
  835. # Step 2: Call the usage if specified by /?
  836. if ($HELP)
  837. {
  838. &Usage();
  839. exit 1;
  840. }
  841. # Step 3: Set the language into the enviroment
  842. $ENV{lang}=$lang;
  843. logmsg("-------- lang parameter passed in is $lang");
  844. }
  845. ##################################################################################
  846. #
  847. # GetCDLayOut
  848. #
  849. # Get CD layout from MUI.INF
  850. #
  851. ##################################################################################
  852. sub GetCDLayOut
  853. {
  854. my(@cd_layout, @lang_map, $muilang, $lang_id);
  855. # Map lang
  856. logmsg("------ INFFILE is $INFFILE");
  857. @lang_map = `perl $RAZZLETOOLPATH\\PostBuildScripts\\parseinf.pl $INFFILE Languages`;
  858. foreach $muilang (@lang_map)
  859. {
  860. if ($muilang =~ /(.*)=$LANG\.mui/i)
  861. {
  862. # Get layout
  863. $langid = $1;
  864. # Try ia64 section first if we're building ia64 MUI
  865. if ($_BuildArch =~ /ia64/i)
  866. {
  867. @cd_layout = `perl $RAZZLETOOLPATH\\PostBuildScripts\\parseinf.pl $INFFILE CD_LAYOUT_IA64`;
  868. foreach $layout (@cd_layout)
  869. {
  870. chomp($layout);
  871. if ($layout =~ /$langid=(\d+)/i)
  872. {
  873. return uc("cd$1");
  874. }
  875. }
  876. }
  877. @cd_layout = `perl $RAZZLETOOLPATH\\PostBuildScripts\\parseinf.pl $INFFILE CD_LAYOUT`;
  878. foreach $layout (@cd_layout)
  879. {
  880. chomp($layout);
  881. if ($layout =~ /$langid=(\d+)/i)
  882. {
  883. return uc("cd$1");
  884. }
  885. }
  886. last;
  887. }
  888. }
  889. return lc("psu");
  890. }
  891. ##################################################################################
  892. #
  893. # GenFusionAssemblyXML
  894. #
  895. # This function will walk the MUI postbuild directory, find the fusion assembly files, locate the manifest and generate the
  896. # required xml and put it in a variable for insertion into the WiX template using XMLVAR. Below is an example of the required
  897. # generated xml snippet
  898. #
  899. # Component Specification:
  900. #
  901. # <Directory Name='SourceDir'>TARGETDIR
  902. # <Directory Name='Windows'>WindowsFolder
  903. # <Directory Name='MUI'>MUI
  904. # <Directory Name='FALLBACK'>
  905. # <Directory Name='0411'>
  906. # <Directory Name="ASMS" LongName="ASMS">ASMS
  907. # <Directory Name="6000" LongName="6000">6000
  908. # <Directory Name="msft" LongName="msft">msft
  909. # <Directory Name="vcrtlmui" LongName="vcrtlmui">vcrtlmui
  910. # <Component Id='1D56E371-0202-4BD7-A050-69415A59007B'>Asms6000Msftvcrtlmui
  911. # <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>
  912. # <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>
  913. # <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>
  914. # </Component>
  915. # </Directory>
  916. # </Directory>
  917. # </Directory>
  918. # </Directory>
  919. # </Directory>
  920. # </Directory>
  921. # </Directory>
  922. # </Directory>
  923. # </Directory>
  924. #
  925. # Assembly Specification:
  926. #
  927. # <Feature Title='FusionInstall' Display='hidden' Level='1' AllowAdvertise='system' FollowParent='yes'>MUIFusionInstall
  928. # <Component>Asms6000Msftvcrtlmui
  929. # <Assembly Manifest='vcrtlmui.man.2' Type='win32'>MUIFusionAssembly
  930. # <Property Value='Microsoft.Tools.VisualCPlusPlus.Runtime-Libraries.mui'>name</Property>
  931. # <Property Value='6.0.0.0'>version</Property>
  932. # ...... etc.
  933. # </Assembly>
  934. # </Component>
  935. # </Feature>
  936. #
  937. ##################################################################################
  938. sub GenFusionAssemblyXML
  939. {
  940. Win32::OLE::CreateObject("Msxml2.DOMDocument", $AsmManifestDoc) or die "Can't create XMLDOM\n";
  941. Win32::OLE::CreateObject("Msxml2.DOMDocument", $MsiTemplateDoc) or die "Can't create XMLDOM\n";
  942. $MsiTemplateDoc->{async} = 0;
  943. $AsmManifestDoc->{async} = 0;
  944. my $MsiASMType, $MsiASMName, $MsiASMVersion, $MsiASMSN;
  945. $CURRENTDIR = Win32::GetCwd();
  946. $FUSIONROOT = "ASMS\\$Special_Lang"; # root directory where all possible Fusion assemblies are stored under $MUIDIR\\Drop
  947. $FUSIONSRC = "$MUIDIR\\Drop\\$FUSIONROOT";
  948. $FileCounter = 2; # this counter is appended to the end of files in each assembly to generate unique file IDs
  949. logmsg("------- FUSIONSRC is $FUSIONSRC");
  950. logmsg("------- CURRENTDIR is $CURRENTDIR");
  951. # if we can't find the fusionsrc directory, just exit - there are no fusion component for this mui build
  952. if (! -e $FUSIONSRC)
  953. {
  954. logmsg("Cannot locate fusion source directory $FUSIONSRC, there are no fusion components for this MUI build.");
  955. logmsg("copy /Y $MUIMSIXMLTemp $MUIMSIXML");
  956. `copy /Y $MUIMSIXMLTemp $MUIMSIXML`;
  957. return 1;
  958. }
  959. # load the xmlvar'ed template
  960. my $loadresult = $MsiTemplateDoc->load($MUIMSIXMLTemp);
  961. $docError = $MsiTemplateDoc->{parseError};
  962. if ($docError->{errorCode} != 0)
  963. {
  964. logmsg("Parse error occurred in manifest file, exiting\n");
  965. logmsg("Line: [$docError->{line}], LinePos: [$docError->{linepos}]\n");
  966. logmsg("Reason: [$docError->{reason}]\n");
  967. return 0;
  968. }
  969. # find the directory root where we want to insert our assembly components
  970. my $MsiCompRoot;
  971. $MuiRootDirNode = $MsiTemplateDoc->{documentElement}->selectSingleNode("//Directory[\@Name = \"MUI\"]");
  972. if (defined($MuiRootDirNode))
  973. {
  974. logmsg("Found MUI directory node.");
  975. $MuiFallbackDirNode = $MuiRootDirNode->selectSingleNode("//Directory[\@Name = \"FALLBACK\"]");
  976. if (defined($MuiFallbackDirNode))
  977. {
  978. logmsg("Found MUI/FALLBACK directory node.");
  979. $MsiCompRoot = $MuiFallbackDirNode->selectSingleNode("//Directory[\@Name = \"$LCID_SHORT\"]");
  980. }
  981. else
  982. {
  983. logmsg("Cannot find FALLBACK directory node within the MUI Directory Node in the MSI Template!");
  984. }
  985. }
  986. else
  987. {
  988. logmsg("Cannot find MUI Directory Node in the MSI Template!");
  989. return 0;
  990. }
  991. if (!defined($MsiCompRoot))
  992. {
  993. logmsg("Cannot find the proper directory node MUI/FALLBACK/$LCID_SHORT for fusion insertion!");
  994. return 0;
  995. }
  996. else
  997. {
  998. logmsg("Found MUI/FALLBACK/$LCID_SHORT directory node.");
  999. }
  1000. # find the feature root where we want to insert our assembly feature
  1001. $MsiFeatureRoot = $MsiTemplateDoc->{documentElement}->selectSingleNode("//Feature[\@Title = \"BasicInstall\"]");
  1002. if (!defined($MsiFeatureRoot))
  1003. {
  1004. logmsg("Cannot find the proper feature node BasicInstall for fusion insertion!");
  1005. return 0;
  1006. }
  1007. $bCreatedFeatureNode = 0;
  1008. # see if there are fusion components to install
  1009. if ((-e $FUSIONSRC) && (-d $FUSIONSRC))
  1010. {
  1011. logmsg("------- $FUSIONSRC exists.");
  1012. opendir(DIRHANDLE1, $FUSIONSRC);
  1013. @DIRFILES1 = grep { $_ ne '.' and $_ ne '..' } readdir(DIRHANDLE1);
  1014. # $SUBDIR1 - subdirectory under fusionroot e.g. "6000" for directory i386.uncomp\asms\6000\msft\vcrtlmui
  1015. foreach $SUBDIR1 (@DIRFILES1)
  1016. {
  1017. logmsg("------- SUBDIR1 is $SUBDIR1");
  1018. if (-d "$FUSIONSRC\\$SUBDIR1")
  1019. {
  1020. opendir(DIRHANDLE2, "$FUSIONSRC\\$SUBDIR1");
  1021. @DIRFILES2 = grep { $_ ne '.' and $_ ne '..' } readdir(DIRHANDLE2);
  1022. $TempDirNode1 = $MsiTemplateDoc->createElement("Directory");
  1023. $TempDirNode1->setAttribute("Name", $SUBDIR1);
  1024. $TempDirNode1->{text} = "_$SUBDIR1.$FileCounter"; # prepending a "_" to conform to MSI ID naming convention
  1025. # $SUBDIR2 - subdirectory under subdir1 e.g. "msft" for directory i386.uncomp\asms\6000\msft\vcrtlmui
  1026. foreach $SUBDIR2 (@DIRFILES2)
  1027. {
  1028. logmsg("------- SUBDIR2 is $SUBDIR2");
  1029. if (-d "$FUSIONSRC\\$SUBDIR1\\$SUBDIR2")
  1030. {
  1031. opendir(DIRHANDLE3, "$FUSIONSRC\\$SUBDIR1\\$SUBDIR2\\$SUBDIR3");
  1032. @DIRFILES3 = grep { $_ ne '.' and $_ ne '..' } readdir(DIRHANDLE3);
  1033. $TempDirNode2 = $MsiTemplateDoc->createElement("Directory");
  1034. $TempDirNode2->setAttribute("Name", $SUBDIR2);
  1035. $TempDirNode2->{text} = "_$SUBDIR2.$FileCounter"; # prepending a "_" to conform to MSI ID naming convention
  1036. $TempDirNode1->appendChild($TempDirNode2);
  1037. # $SUBDIR3 - subdirectory under subdir2 e.g. "vcrtlmui" for directory i386.uncomp\asms\6000\msft\vcrtlmui
  1038. foreach $SUBDIR3 (@DIRFILES3)
  1039. {
  1040. logmsg("------- SUBDIR3 is $SUBDIR3");
  1041. # only continue if we can change to this directory and can find a manifest file
  1042. if (chdir "$FUSIONSRC\\$SUBDIR1\\$SUBDIR2\\$SUBDIR3")
  1043. {
  1044. # if we can't find a .man or .manifest file, skip this component
  1045. if (!defined ($ASMMANFILE = glob("*.man")))
  1046. {
  1047. if (!defined ($ASMMANFILE = glob("*.manifest")))
  1048. {
  1049. logmsg("Skipping $FUSIONSRC\\$SUBDIR1\\$SUBDIR2\\$SUBDIR3 directory, can't find manifest file.");
  1050. next;
  1051. }
  1052. }
  1053. logmsg("Found assembly directory - $FUSIONSRC\\$SUBDIR1\\$SUBDIR2\\$SUBDIR3\n");
  1054. logmsg("Assembly manifest file is $ASMMANFILE");
  1055. $TempDirNode3 = $MsiTemplateDoc->createElement("Directory");
  1056. $TempDirNode3->setAttribute("Name", $SUBDIR3);
  1057. $TempDirNode3->{text} = "_$SUBDIR3.$FileCounter"; # prepending a "_" to conform to MSI ID naming convention
  1058. $TempDirNode2->appendChild($TempDirNode3);
  1059. # process manifest file for the assembly
  1060. my $result = $AsmManifestDoc->load("$FUSIONSRC\\$SUBDIR1\\$SUBDIR2\\$SUBDIR3\\$ASMMANFILE");
  1061. $docError = $AsmManifestDoc->{parseError};
  1062. $ValidateSuccess = 1;
  1063. if ($docError->{errorCode} != 0)
  1064. {
  1065. logmsg("Parse error occurred in manifest file, skipping this component\n");
  1066. logmsg("Line: [$docError->{line}], LinePos: [$docError->{linepos}]\n");
  1067. logmsg("Reason: [$docError->{reason}]\n");
  1068. $ValidateSuccess = 0;
  1069. }
  1070. else
  1071. {
  1072. $ASMIDNode = $AsmManifestDoc->{documentElement}->selectSingleNode("assemblyIdentity");
  1073. if (defined ($ASMIDNode))
  1074. {
  1075. if (defined ($ASMIDNode->{attributes}))
  1076. {
  1077. # create an assembly node for insertion later into our msi template
  1078. $TempAsmNode = $MsiTemplateDoc->createElement("Assembly");
  1079. $TempAsmNode->setAttribute("Manifest", "$ASMMANFILE.$FileCounter");
  1080. $TempAsmNode->{text} = "_$SUBDIR3.$FileCounter"; # prepending a "_" to conform to MSI ID naming convention
  1081. # here, we want to go through the list of attributes on the assembly id node, and create a set of properties for it
  1082. # in the MsiAssemblyName table, except type, which goes into assembly node as well.
  1083. for ($i = 0; $i < $ASMIDNode->{attributes}->{length}; $i++)
  1084. {
  1085. $AsmIDNodeAttribute = $ASMIDNode->{attributes}->item($i);
  1086. if ($AsmIDNodeAttribute->{nodeName} =~ /^type$/i)
  1087. {
  1088. $TempAsmNode->setAttribute("Type", $AsmIDNodeAttribute->{nodeValue});
  1089. }
  1090. $TempPropNode1 = $MsiTemplateDoc->createElement("Property");
  1091. $TempPropNode1->setAttribute("Value", "$AsmIDNodeAttribute->{nodeValue}");
  1092. $TempPropNode1->{text} = $AsmIDNodeAttribute->{nodeName};
  1093. $TempAsmNode->appendChild($TempPropNode1);
  1094. logmsg("MsiAssemblyName: Name is $AsmIDNodeAttribute->{nodeName}, Value is $AsmIDNodeAttribute->{nodeValue}");
  1095. }
  1096. }
  1097. }
  1098. else
  1099. {
  1100. # no assemblyIdentity node found, this manifest is invalid, log it and continue
  1101. $ValidateSuccess = 0;
  1102. logmsg("Found manifest file is invalid, it does not contain an assembly identity node.");
  1103. }
  1104. }
  1105. if ($ValidateSuccess)
  1106. {
  1107. # generate a GUID for this component
  1108. ($ASMGUID) = `uuidgen`;
  1109. chomp $ASMGUID;
  1110. $ASMGUID =~ tr/a-z/A-Z/;
  1111. $MsiComponentID = "_$SUBDIR1$SUBDIR2$SUBDIR3"; # prepending a "_" to conform to MSI ID naming convention
  1112. # add all files in the directory to the msitemplate
  1113. opendir(DIRHANDLE4, "$FUSIONSRC\\$SUBDIR1\\$SUBDIR2\\$SUBDIR3");
  1114. @ASMFILES = grep { $_ ne '.' and $_ ne '..' } readdir(DIRHANDLE4);
  1115. $TempCompNode = $MsiTemplateDoc->createElement("Component");
  1116. $TempCompNode->setAttribute("Id", $ASMGUID);
  1117. $TempCompNode->setAttribute("Win64", $ISWIN64);
  1118. $TempCompNode->{text} = $MsiComponentID;
  1119. $TempDirNode3->appendChild($TempCompNode);
  1120. foreach $ASMFILE (@ASMFILES)
  1121. {
  1122. # when processing the manifest file, we also note down its manifest file ID in the MSI
  1123. logmsg("This file is $ASMFILE");
  1124. $TempFileNode = $MsiTemplateDoc->createElement("File");
  1125. $TempFileNode->{text} = "$ASMFILE.$FileCounter";
  1126. $TempFileNode->setAttribute("DiskId", "1");
  1127. $TempFileNode->setAttribute("Name", Win32::GetShortPathName($ASMFILE));
  1128. $TempFileNode->setAttribute("LongName", $ASMFILE);
  1129. $TempFileNode->setAttribute("src", "$FUSIONSRC\\$SUBDIR1\\$SUBDIR2\\$SUBDIR3\\$ASMFILE");
  1130. $TempCompNode->appendChild($TempFileNode);
  1131. }
  1132. $MsiCompRoot->appendChild($TempDirNode1);
  1133. # create the assembly feature root node first if we have not done so
  1134. if (!$bCreatedFeatureNode)
  1135. {
  1136. $TempFeatureRoot = $MsiTemplateDoc->createElement("Feature");
  1137. $TempFeatureRoot->setAttribute("Title", "FusionInstall");
  1138. $TempFeatureRoot->setAttribute("Display", "hidden");
  1139. $TempFeatureRoot->setAttribute("Level", "1");
  1140. $TempFeatureRoot->setAttribute("AllowAdvertise", "system");
  1141. $TempFeatureRoot->setAttribute("FollowParent", "yes");
  1142. $TempFeatureRoot->{text} = "MUIFusionInstall";
  1143. $MsiFeatureRoot->appendChild($TempFeatureRoot);
  1144. $MsiFeatureRoot = $TempFeatureRoot;
  1145. $bCreatedFeatureNode = 1;
  1146. }
  1147. # add the feature component specification for the assembly
  1148. if (defined($TempAsmNode))
  1149. {
  1150. $TempCompNode = $MsiTemplateDoc->createElement("Component");
  1151. $TempCompNode->{text} = $MsiComponentID;
  1152. $TempCompNode->appendChild($TempAsmNode);
  1153. $MsiFeatureRoot->appendChild($TempCompNode);
  1154. }
  1155. $FileCounter += 1;
  1156. }
  1157. closedir(DIRHANDLE3);
  1158. }
  1159. }
  1160. closedir(DIRHANDLE2);
  1161. }
  1162. }
  1163. closedir(DIRHANDLE1);
  1164. }
  1165. }
  1166. }
  1167. # save the xml file
  1168. $MsiTemplateDoc->save($MUIMSIXML);
  1169. chdir $CURRENTDIR; # go back to the old current directory
  1170. return 1;
  1171. }
  1172. ##################################################################################
  1173. #
  1174. # InsertEula
  1175. #
  1176. # This function will look for a specially marked xml node inside the MSI template
  1177. # called <MUIEULAText>, and then it will create a <TEXT> sibling xml node to
  1178. # the found node and insert the EULA text content in the TEXT node. Then it will
  1179. # delete the MUIEULAText node from the template
  1180. #
  1181. ##################################################################################
  1182. sub InsertEula
  1183. {
  1184. Win32::OLE::CreateObject("Msxml2.DOMDocument", $MsiTemplateDoc) or die "Can't create XMLDOM\n";
  1185. $MsiTemplateDoc->{async} = 0;
  1186. $MsiEulaStart = "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033{\\fonttbl{\\f0\\fnil\\fcharset0 MS Shell Dlg;}}\n";
  1187. $MsiEulaFirstLine = "\\viewkind4\\uc1\\pard\\f0\\fs17";
  1188. $MsiEulaEnd = "}";
  1189. $MsiEulaLinePrefix = "\\par";
  1190. $EulaLineCounter = 0;
  1191. $EulaContent = "";
  1192. $EULASRC = "$MUIDIR\\eula.txt";
  1193. # load the xmlvar'ed template
  1194. my $loadresult = $MsiTemplateDoc->load($MUIMSIXML);
  1195. $docError = $MsiTemplateDoc->{parseError};
  1196. if ($docError->{errorCode} != 0)
  1197. {
  1198. logmsg("Parse error occurred in MSI Template file, exiting\n");
  1199. logmsg("Line: [$docError->{line}], LinePos: [$docError->{linepos}]\n");
  1200. logmsg("Reason: [$docError->{reason}]\n");
  1201. return 0;
  1202. }
  1203. # find the EULA Text node in the template
  1204. $MUIEULATextNode = $MsiTemplateDoc->{documentElement}->selectSingleNode("//MUIEULAText");
  1205. if (defined($MUIEULATextNode))
  1206. {
  1207. logmsg("Found MUI Eula Text node.");
  1208. }
  1209. else
  1210. {
  1211. logmsg("Cannot find MUI Eula Text Node in the MSI Template!");
  1212. return 0;
  1213. }
  1214. # create a node "Text" under MUIEULAText node's parent
  1215. $TempTextNode = $MsiTemplateDoc->createElement("Text");
  1216. $ParentNode = $MUIEULATextNode->{parentNode};
  1217. $ParentNode->appendChild($TempTextNode);
  1218. $ParentNode->removeChild($MUIEULATextNode);
  1219. # read the EULA text into memory, format it properly for insertion into the text node
  1220. if(!open(EULASRCFILE, "$EULASRC"))
  1221. {
  1222. logmsg("Cannot find MUI Eula Text file at $EULASRC!");
  1223. return 0;
  1224. }
  1225. $EulaContent = $MsiEulaStart;
  1226. $EulaLineCounter = 1;
  1227. while (<EULASRCFILE>)
  1228. {
  1229. if ($EulaLineCounter == 1)
  1230. {
  1231. $EulaContent .= "$MsiEulaFirstLine $_";
  1232. }
  1233. else
  1234. {
  1235. $EulaContent .= " $MsiEulaLinePrefix $_";
  1236. }
  1237. $EulaLineCounter++;
  1238. }
  1239. close(EULASRCFILE);
  1240. $EulaContent .= $MsiEulaEnd;
  1241. # rename the node "Text" instead of "MUIEULAText" and add in the new EulaContent
  1242. # $TempTextNode->{nodeName} = "Text";
  1243. $TempTextNode->{text} = $EulaContent;
  1244. # save the xml file
  1245. $MsiTemplateDoc->save($MUIMSIXML);
  1246. logmsg("Successfully inserted EULA text content into the template file.");
  1247. return 1;
  1248. }
  1249. ##################################################################################
  1250. #
  1251. # InsertReserveCost
  1252. #
  1253. # This function will read the LangpackCost section of the mui.inf file, and
  1254. # build them into the ReserveCost table inside the MSI template.
  1255. #
  1256. ##################################################################################
  1257. sub InsertReserveCost
  1258. {
  1259. my(@langpackcost, $section_name);
  1260. Win32::OLE::CreateObject("Msxml2.DOMDocument", $MsiTemplateDoc) or die "Can't create XMLDOM\n";
  1261. $MsiTemplateDoc->{async} = 0;
  1262. # load the xmlvar'ed template
  1263. my $loadresult = $MsiTemplateDoc->load($MUIMSIXML);
  1264. $docError = $MsiTemplateDoc->{parseError};
  1265. if ($docError->{errorCode} != 0)
  1266. {
  1267. logmsg("Parse error occurred in MSI Template file, exiting\n");
  1268. logmsg("Line: [$docError->{line}], LinePos: [$docError->{linepos}]\n");
  1269. logmsg("Reason: [$docError->{reason}]\n");
  1270. return 0;
  1271. }
  1272. $MuiRootDirNode = $MsiTemplateDoc->{documentElement}->selectSingleNode("//Directory[\@Name = \"SourceDir\"]");
  1273. if (defined($MuiRootDirNode))
  1274. {
  1275. logmsg("InsertReserveCost: Found MUI root directory node.");
  1276. }
  1277. else
  1278. {
  1279. logmsg("InsertReserveCost: Cannot find MUI Directory Node in the MSI Template!");
  1280. return 0;
  1281. }
  1282. # find the feature root where we want to insert our assembly feature
  1283. $MsiFeatureRoot = $MsiTemplateDoc->{documentElement}->selectSingleNode("//Feature[\@Title = \"BasicInstall\"]");
  1284. if (!defined($MsiFeatureRoot))
  1285. {
  1286. logmsg("InsertReserveCost: Cannot find the proper feature node BasicInstall!");
  1287. return 0;
  1288. }
  1289. else
  1290. {
  1291. logmsg("InsertReserveCost: Found the proper feature node BasicInstall!");
  1292. }
  1293. if ($_BuildArch =~ /ia64/i)
  1294. {
  1295. $section_name = "FileSize_LPK_IA64";
  1296. }
  1297. else
  1298. {
  1299. $section_name = "FileSize_LPK";
  1300. }
  1301. @langpackcost = `perl $RAZZLETOOLPATH\\PostBuildScripts\\parseinf.pl $INFFILE $section_name`;
  1302. # for every entry, we write it into the diretory as a component, and also include the component into the
  1303. # feature table
  1304. foreach $lpkitem (@langpackcost)
  1305. {
  1306. chop($lpkitem);
  1307. if ($lpkitem =~ /(.*)=(.*)/)
  1308. {
  1309. $lpklcid = $1;
  1310. $lpkcost = $2;
  1311. logmsg("----- InsertReserveCost: Langpack LCID is $lpklcid, Langpack filesize is $lpkcost");
  1312. # basic error checking
  1313. if (!defined($lpklcid) || !defined($lpkcost) || ($lpklcid == 0) )
  1314. {
  1315. logmsg("InsertReserveCost: error reading langpack file size in mui.inf.");
  1316. return 0;
  1317. }
  1318. $TempDirCompNode = $MsiTemplateDoc->createElement("Component");
  1319. $TempFeaCompNode = $MsiTemplateDoc->createElement("Component");
  1320. $TempReserveCostNode = $MsiTemplateDoc->createElement("ReserveCost");
  1321. $TempConditionNode = $MsiTemplateDoc->createElement("Condition");
  1322. if (!defined($TempDirCompNode) || !defined($TempFeaCompNode) || !defined($TempReserveCostNode) || !defined($TempConditionNode))
  1323. {
  1324. logmsg("InsertReserveCost: failed to create xml nodes for insertion into MSI template.");
  1325. return 0;
  1326. }
  1327. ($LPKGUID) = `uuidgen`;
  1328. chomp $LPKGUID;
  1329. $LPKGUID =~ tr/a-z/A-Z/;
  1330. $LPKID = "LANGPACKFileCost$lpklcid";
  1331. $TempDirCompNode->setAttribute("Id", $LPKGUID);
  1332. $TempDirCompNode->setAttribute("Win64", $ISWIN64);
  1333. $TempDirCompNode->{text} = $LPKID;
  1334. $TempFeaCompNode->{text} = $LPKID;
  1335. $TempReserveCostNode->setAttribute("Directory", "SystemFolder");
  1336. $TempReserveCostNode->setAttribute("RunLocal", $lpkcost);
  1337. $TempReserveCostNode->setAttribute("RunFromSource", "0");
  1338. $TempReserveCostNode->{text} = "$LPKID.1";
  1339. $TempConditionNode->{text} = "MsiRequireLangPack AND MuiLCID=\"$lpklcid\"";
  1340. $TempDirCompNode->appendChild($TempReserveCostNode);
  1341. $TempDirCompNode->appendChild($TempConditionNode);
  1342. $MuiRootDirNode->appendChild($TempDirCompNode);
  1343. $MsiFeatureRoot->appendChild($TempFeaCompNode);
  1344. }
  1345. }
  1346. # save the xml file
  1347. $MsiTemplateDoc->save($MUIMSIXML);
  1348. logmsg("Successfully inserted ReserveCost table rows into the MSI template.");
  1349. return 1;
  1350. }
  1351. ##################################################################################
  1352. #
  1353. # InsertSKUNodes
  1354. #
  1355. # This function insert the necessary xml nodes that are going to be
  1356. # included in the MSI template as specified by the caller.
  1357. #
  1358. ##################################################################################
  1359. sub InsertSKUNodes
  1360. {
  1361. Win32::OLE::CreateObject("Msxml2.DOMDocument", $MsiTemplateDoc) or die "Can't create XMLDOM\n";
  1362. $MsiTemplateDoc->{async} = 0;
  1363. # load the xmlvar'ed template
  1364. my $loadresult = $MsiTemplateDoc->load($MUIMSIXML);
  1365. $docError = $MsiTemplateDoc->{parseError};
  1366. if ($docError->{errorCode} != 0)
  1367. {
  1368. logmsg("Parse error occurred in MSI Template file, exiting\n");
  1369. logmsg("Line: [$docError->{line}], LinePos: [$docError->{linepos}]\n");
  1370. logmsg("Reason: [$docError->{reason}]\n");
  1371. return 0;
  1372. }
  1373. $MuiRootDirNode = $MsiTemplateDoc->{documentElement}->selectSingleNode("//Directory[\@Name = \"SourceDir\"]");
  1374. if (defined($MuiRootDirNode))
  1375. {
  1376. logmsg("InsertSKUNodes: Found MUI root directory node.");
  1377. }
  1378. else
  1379. {
  1380. logmsg("InsertSKUNodes: Cannot find MUI Directory Node in the MSI Template!");
  1381. return 0;
  1382. }
  1383. # find the feature root where we want to insert our assembly feature
  1384. $MsiFeatureRoot = $MsiTemplateDoc->{documentElement}->selectSingleNode("//Feature[\@Title = \"BasicInstall\"]");
  1385. if (!defined($MsiFeatureRoot))
  1386. {
  1387. logmsg("InsertSKUNodes: Cannot find the proper feature node BasicInstall!");
  1388. return 0;
  1389. }
  1390. else
  1391. {
  1392. logmsg("InsertSKUNodes: Found the proper feature node BasicInstall!");
  1393. }
  1394. for $skuitem (keys %SKUList)
  1395. {
  1396. logmsg("InsertSKUNodes: Inserting $skuitem SKU Nodes into Directory and Feature nodelists");
  1397. $TempDirModNode = $MsiTemplateDoc->createElement("Module");
  1398. $TempFeaModNode = $MsiTemplateDoc->createElement("Module");
  1399. $TempDirModNode->setAttribute("DiskId", "1");
  1400. $TempDirModNode->setAttribute("src", $SKUList{$skuitem}{MergModeFileName});
  1401. $TempDirModNode->{text} = $skuitem;
  1402. $TempFeaModNode->{text} = $skuitem;
  1403. $MuiRootDirNode->appendChild($TempDirModNode);
  1404. $MsiFeatureRoot->appendChild($TempFeaModNode);
  1405. }
  1406. # insert an launch condition for each of the SKU not present so that the MSI package can't be run
  1407. # on that SKU
  1408. for $skuexitem (keys %SKUExList)
  1409. {
  1410. logmsg("InsertSKUNodes: Inserting launch condition exclusion node for $skuexitem SKU into MSI template.");
  1411. $TempCondNode = $MsiTemplateDoc->createElement("Condition");
  1412. $TempCondNode->setAttribute("Message", $SKUExList{$skuexitem}{Message});
  1413. $TempCondNode->{text} = $SKUExList{$skuexitem}{Condition};
  1414. $TempHeadCondNode = $MsiTemplateDoc->{documentElement}->selectSingleNode("Condition[1]");
  1415. if (defined ($TempHeadCondNode))
  1416. {
  1417. $MsiTemplateDoc->{documentElement}->insertBefore($TempCondNode, $TempHeadCondNode);
  1418. }
  1419. else
  1420. {
  1421. $MsiTemplateDoc->{documentElement}->appendChild($TempCondNode);
  1422. }
  1423. }
  1424. # save the xml file
  1425. $MsiTemplateDoc->save($MUIMSIXML);
  1426. logmsg("Successfully inserted SKU merge module XML nodes.");
  1427. return 1;
  1428. }
  1429. ##################################################################################
  1430. #
  1431. # DeleteMSITables
  1432. #
  1433. # This function is used to remove the unused tables AdminUISequence,
  1434. # AdminExecuteSequence, AdvUISequence and AdvExecuteSequence tables
  1435. #
  1436. ##################################################################################
  1437. sub DeleteMSITables
  1438. {
  1439. my($Installer, $MUIMSIDB, $SqlQuery, $MUIMSIView);
  1440. Win32::OLE::CreateObject("WindowsInstaller.Installer", $Installer) or die "Can't create Windows Installer Object\n";
  1441. # check if the result MSI package exist
  1442. if (!(-e $MUIMSI))
  1443. {
  1444. errmsg ("DeleteMSITables error: Cannot locate the MSI package $MUIMSI.");
  1445. return 0;
  1446. }
  1447. logmsg("Opening MSI Package $MUIMSI for table deletion.");
  1448. # if so, open the package
  1449. $MUIMSIDB = $Installer->OpenDatabase($MUIMSI, 2); # open in direct, no transaction
  1450. if (!defined($MUIMSIDB))
  1451. {
  1452. errmsg ("DeleteMSITables error: Cannot open the MSI package $MUIMSI.");
  1453. return 0;
  1454. }
  1455. # delete the tables we don't want
  1456. foreach (@DeleteTableList)
  1457. {
  1458. $SqlQuery = "DROP TABLE $_";
  1459. $MUIMSIView = $MUIMSIDB->OpenView($SqlQuery);
  1460. if (!defined($MUIMSIView))
  1461. {
  1462. $MUIMSIDB->Commit(); # flush all the buffers, even though this is a fatal error.
  1463. errmsg ("DeleteMSITables error: Cannot open a view on the MSI package.");
  1464. return 0;
  1465. }
  1466. $MUIMSIView->Execute();
  1467. $MUIMSIView->Close();
  1468. logmsg("Deleted MSI table $_");
  1469. }
  1470. # commit the changes to the package
  1471. $MUIMSIDB->Commit();
  1472. logmsg("DeleteMSITables: Successfully deleted unused tables from the MSI package.");
  1473. return 1;
  1474. }
  1475. ##################################################################################
  1476. #
  1477. # InsertCatFiles
  1478. #
  1479. # This function will reinsert the catalog files back into the asian printer
  1480. # driver msi packages. We do this for all 4 msi packages regardless of what
  1481. # language is being built.
  1482. #
  1483. ##################################################################################
  1484. sub InsertCatFiles
  1485. {
  1486. $MUI_PRINTER_DRIVER_DIR = "$DESTDIR\\printer";
  1487. $MUI_CAT_DIR = "$MUIDIR\\printer";
  1488. # we will do this only for cd1 in RC2, will need to change this and muimake for final release
  1489. if ((uc($CDLAYOUT) eq "CD1" ) && (-e $MUI_PRINTER_DRIVER_DIR) && (-e $MUI_CAT_DIR))
  1490. {
  1491. %PrinterDriver = (); # list of printer driver files msi file names and the catalog file names
  1492. $PrinterDriver{"chs"} = {
  1493. MsiFile=> "chsprint.msi",
  1494. CatFile => "chspack.cat",
  1495. };
  1496. $PrinterDriver{"cht"} = {
  1497. MsiFile=> "chtprint.msi",
  1498. CatFile => "chtpack.cat",
  1499. };
  1500. $PrinterDriver{"jpn"} = {
  1501. MsiFile=> "jpnprint.msi",
  1502. CatFile => "jpnpack.cat",
  1503. };
  1504. $PrinterDriver{"kor"} = {
  1505. MsiFile=> "korprint.msi",
  1506. CatFile => "korpack.cat",
  1507. };
  1508. for $langitem (keys %PrinterDriver)
  1509. {
  1510. logmsg("Printer driver MSI file is at $MUI_PRINTER_DRIVER_DIR\\$PrinterDriver{$langitem}{MsiFile}");
  1511. logmsg("Printer driver CAT file is at $MUI_CAT_DIR\\$PrinterDriver{$langitem}{CatFile}");
  1512. $returnResult = system("msicab.exe -r $MUI_PRINTER_DRIVER_DIR\\$PrinterDriver{$langitem}{MsiFile} $MUI_CAT_DIR\\$PrinterDriver{$langitem}{CatFile}");
  1513. if ($returnResult)
  1514. {
  1515. logmsg("ERROR: Failed to insert catalog file into the printer MSI file!");
  1516. return 0;
  1517. }
  1518. # also delete the cat file from the release directory - we don't want those to be there
  1519. if (-e "$MUI_PRINTER_DRIVER_DIR\\$PrinterDriver{$langitem}{CatFile}")
  1520. {
  1521. logmsg("Deleting $MUI_PRINTER_DRIVER_DIR\\$PrinterDriver{$langitem}{CatFile} from the release point.");
  1522. $returnedResult = system("del /F /Q $MUI_PRINTER_DRIVER_DIR\\$PrinterDriver{$langitem}{CatFile}");
  1523. if ($returnedResult)
  1524. {
  1525. logmsg("Warning: failed to delete catalog file $PrinterDriver{$langitem}{CatFile}.");
  1526. }
  1527. }
  1528. }
  1529. }
  1530. else
  1531. {
  1532. logmsg("INFO: InsertCatFiles skipped - CDLayout is $CDLAYOUT, MSI printer directory is $MUI_PRINTER_DRIVER_DIR, Catalog directory is $MUI_CAT_DIR");
  1533. }
  1534. logmsg("InsertCatFiles: Successfully inserted the catalog files into the Asian Printer Driver MSI Packages.");
  1535. return 1;
  1536. }
  1537. ##################################################################################
  1538. #
  1539. # Cmd entry point for script.
  1540. #
  1541. ##################################################################################
  1542. if (eval("\$0 =~ /" . __PACKAGE__ . "\\.pm\$/i"))
  1543. {
  1544. # Step 1: Parse the command line
  1545. # <run perl.exe GetParams.pm /? to get the complete usage for GetParams.pm>
  1546. &GetParams ('-o', 'l:psadwb', '-p', 'lang prosku srvsku advsku dtcsku websku sbssku', @ARGV);
  1547. # Include local environment extensions
  1548. &LocalEnvEx::localenvex('initialize');
  1549. # Set lang from the environment
  1550. $LANG=$ENV{lang};
  1551. # $Special_Lang = "JPN"; // commented out, these are set in GetCodes
  1552. # $LCID_SHORT = "0411"; // commented out, these are set in GetCodes
  1553. # Validate the option given as parameter.
  1554. &ValidateParams;
  1555. # Set flag indicating that we run from command prompt.
  1556. $cmdPrompt = 1;
  1557. # Step 4: Call the main function
  1558. &muimsi::Main();
  1559. # End local environment extensions.
  1560. &LocalEnvEx::localenvex('end');
  1561. }