cmCPackWIXGenerator.cxx 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234
  1. /*============================================================================
  2. CMake - Cross Platform Makefile Generator
  3. Copyright 2000-2013 Kitware, Inc., Insight Software Consortium
  4. Distributed under the OSI-approved BSD License (the "License");
  5. see accompanying file Copyright.txt for details.
  6. This software is distributed WITHOUT ANY WARRANTY; without even the
  7. implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  8. See the License for more information.
  9. ============================================================================*/
  10. #include "cmCPackWIXGenerator.h"
  11. #include <cmSystemTools.h>
  12. #include <cmGeneratedFileStream.h>
  13. #include <cmCryptoHash.h>
  14. #include <CPack/cmCPackLog.h>
  15. #include <CPack/cmCPackComponentGroup.h>
  16. #include "cmWIXSourceWriter.h"
  17. #include "cmWIXRichTextFormatWriter.h"
  18. #include <cmsys/SystemTools.hxx>
  19. #include <cmsys/Directory.hxx>
  20. #include <cmsys/Encoding.hxx>
  21. #include <cmsys/FStream.hxx>
  22. #include <rpc.h> // for GUID generation
  23. #include <sys/types.h>
  24. #include <sys/stat.h>
  25. int cmCPackWIXGenerator::InitializeInternal()
  26. {
  27. componentPackageMethod = ONE_PACKAGE;
  28. return this->Superclass::InitializeInternal();
  29. }
  30. bool cmCPackWIXGenerator::RunWiXCommand(const std::string& command)
  31. {
  32. std::string cpackTopLevel;
  33. if(!RequireOption("CPACK_TOPLEVEL_DIRECTORY", cpackTopLevel))
  34. {
  35. return false;
  36. }
  37. std::string logFileName = cpackTopLevel + "/wix.log";
  38. cmCPackLogger(cmCPackLog::LOG_DEBUG,
  39. "Running WiX command: " << command << std::endl);
  40. std::string output;
  41. int returnValue = 0;
  42. bool status = cmSystemTools::RunSingleCommand(command.c_str(), &output,
  43. &returnValue, 0, cmSystemTools::OUTPUT_NONE);
  44. cmsys::ofstream logFile(logFileName.c_str(), std::ios::app);
  45. logFile << command << std::endl;
  46. logFile << output;
  47. logFile.close();
  48. if(!status || returnValue)
  49. {
  50. cmCPackLogger(cmCPackLog::LOG_ERROR,
  51. "Problem running WiX candle. "
  52. "Please check '" << logFileName << "' for errors." << std::endl);
  53. return false;
  54. }
  55. return true;
  56. }
  57. bool cmCPackWIXGenerator::RunCandleCommand(
  58. const std::string& sourceFile, const std::string& objectFile)
  59. {
  60. std::string executable;
  61. if(!RequireOption("CPACK_WIX_CANDLE_EXECUTABLE", executable))
  62. {
  63. return false;
  64. }
  65. std::stringstream command;
  66. command << QuotePath(executable);
  67. command << " -nologo";
  68. command << " -arch " << GetArchitecture();
  69. command << " -out " << QuotePath(objectFile);
  70. for(extension_set_t::const_iterator i = CandleExtensions.begin();
  71. i != CandleExtensions.end(); ++i)
  72. {
  73. command << " -ext " << QuotePath(*i);
  74. }
  75. AddCustomFlags("CPACK_WIX_CANDLE_EXTRA_FLAGS", command);
  76. command << " " << QuotePath(sourceFile);
  77. return RunWiXCommand(command.str());
  78. }
  79. bool cmCPackWIXGenerator::RunLightCommand(const std::string& objectFiles)
  80. {
  81. std::string executable;
  82. if(!RequireOption("CPACK_WIX_LIGHT_EXECUTABLE", executable))
  83. {
  84. return false;
  85. }
  86. std::stringstream command;
  87. command << QuotePath(executable);
  88. command << " -nologo";
  89. command << " -out " << QuotePath(packageFileNames.at(0));
  90. for(extension_set_t::const_iterator i = LightExtensions.begin();
  91. i != LightExtensions.end(); ++i)
  92. {
  93. command << " -ext " << QuotePath(*i);
  94. }
  95. const char* const cultures = GetOption("CPACK_WIX_CULTURES");
  96. if(cultures)
  97. {
  98. command << " -cultures:" << cultures;
  99. }
  100. AddCustomFlags("CPACK_WIX_LIGHT_EXTRA_FLAGS", command);
  101. command << " " << objectFiles;
  102. return RunWiXCommand(command.str());
  103. }
  104. int cmCPackWIXGenerator::PackageFiles()
  105. {
  106. if(!PackageFilesImpl() || cmSystemTools::GetErrorOccuredFlag())
  107. {
  108. cmCPackLogger(cmCPackLog::LOG_ERROR,
  109. "Fatal WiX Generator Error" << std::endl);
  110. return false;
  111. }
  112. return true;
  113. }
  114. bool cmCPackWIXGenerator::InitializeWiXConfiguration()
  115. {
  116. if(!ReadListFile("CPackWIX.cmake"))
  117. {
  118. cmCPackLogger(cmCPackLog::LOG_ERROR,
  119. "Error while executing CPackWIX.cmake" << std::endl);
  120. return false;
  121. }
  122. if(GetOption("CPACK_WIX_PRODUCT_GUID") == 0)
  123. {
  124. std::string guid = GenerateGUID();
  125. SetOption("CPACK_WIX_PRODUCT_GUID", guid.c_str());
  126. cmCPackLogger(cmCPackLog::LOG_VERBOSE,
  127. "CPACK_WIX_PRODUCT_GUID implicitly set to " << guid << " . "
  128. << std::endl);
  129. }
  130. if(GetOption("CPACK_WIX_UPGRADE_GUID") == 0)
  131. {
  132. std::string guid = GenerateGUID();
  133. SetOption("CPACK_WIX_UPGRADE_GUID", guid.c_str());
  134. cmCPackLogger(cmCPackLog::LOG_WARNING,
  135. "CPACK_WIX_UPGRADE_GUID implicitly set to " << guid << " . "
  136. "Please refer to the documentation on how and why "
  137. "you might want to set this explicitly." << std::endl);
  138. }
  139. std::string cpackTopLevel;
  140. if(!RequireOption("CPACK_TOPLEVEL_DIRECTORY", cpackTopLevel))
  141. {
  142. return false;
  143. }
  144. if(GetOption("CPACK_WIX_LICENSE_RTF") == 0)
  145. {
  146. std::string licenseFilename = cpackTopLevel + "/License.rtf";
  147. SetOption("CPACK_WIX_LICENSE_RTF", licenseFilename.c_str());
  148. if(!CreateLicenseFile())
  149. {
  150. return false;
  151. }
  152. }
  153. if(GetOption("CPACK_PACKAGE_VENDOR") == 0)
  154. {
  155. std::string defaultVendor = "Humanity";
  156. SetOption("CPACK_PACKAGE_VENDOR", defaultVendor.c_str());
  157. cmCPackLogger(cmCPackLog::LOG_VERBOSE,
  158. "CPACK_PACKAGE_VENDOR implicitly set to " << defaultVendor << " . "
  159. << std::endl);
  160. }
  161. if(GetOption("CPACK_WIX_UI_REF") == 0)
  162. {
  163. std::string defaultRef = "WixUI_InstallDir";
  164. if(Components.size())
  165. {
  166. defaultRef = "WixUI_FeatureTree";
  167. }
  168. SetOption("CPACK_WIX_UI_REF", defaultRef.c_str());
  169. }
  170. CollectExtensions("CPACK_WIX_EXTENSIONS", CandleExtensions);
  171. CollectExtensions("CPACK_WIX_CANDLE_EXTENSIONS", CandleExtensions);
  172. LightExtensions.insert("WixUIExtension");
  173. CollectExtensions("CPACK_WIX_EXTENSIONS", LightExtensions);
  174. CollectExtensions("CPACK_WIX_LIGHT_EXTENSIONS", LightExtensions);
  175. const char* patchFilePath = GetOption("CPACK_WIX_PATCH_FILE");
  176. if(patchFilePath)
  177. {
  178. LoadPatchFragments(patchFilePath);
  179. }
  180. return true;
  181. }
  182. bool cmCPackWIXGenerator::PackageFilesImpl()
  183. {
  184. if(!InitializeWiXConfiguration())
  185. {
  186. return false;
  187. }
  188. if(!CreateWiXVariablesIncludeFile())
  189. {
  190. return false;
  191. }
  192. if(!CreateWiXSourceFiles())
  193. {
  194. return false;
  195. }
  196. AppendUserSuppliedExtraSources();
  197. std::stringstream objectFiles;
  198. for(size_t i = 0; i < WixSources.size(); ++i)
  199. {
  200. const std::string& sourceFilename = WixSources[i];
  201. std::string objectFilename =
  202. cmSystemTools::GetFilenameWithoutExtension(sourceFilename) + ".wixobj";
  203. if(!RunCandleCommand(sourceFilename, objectFilename))
  204. {
  205. return false;
  206. }
  207. objectFiles << " " << QuotePath(objectFilename);
  208. }
  209. AppendUserSuppliedExtraObjects(objectFiles);
  210. return RunLightCommand(objectFiles.str());
  211. }
  212. void cmCPackWIXGenerator::AppendUserSuppliedExtraSources()
  213. {
  214. const char *cpackWixExtraSources = GetOption("CPACK_WIX_EXTRA_SOURCES");
  215. if(!cpackWixExtraSources) return;
  216. cmSystemTools::ExpandListArgument(cpackWixExtraSources, WixSources);
  217. }
  218. void cmCPackWIXGenerator::AppendUserSuppliedExtraObjects(std::ostream& stream)
  219. {
  220. const char *cpackWixExtraObjects = GetOption("CPACK_WIX_EXTRA_OBJECTS");
  221. if(!cpackWixExtraObjects) return;
  222. std::vector<std::string> expandedExtraObjects;
  223. cmSystemTools::ExpandListArgument(
  224. cpackWixExtraObjects, expandedExtraObjects);
  225. for(size_t i = 0; i < expandedExtraObjects.size(); ++i)
  226. {
  227. stream << " " << QuotePath(expandedExtraObjects[i]);
  228. }
  229. }
  230. bool cmCPackWIXGenerator::CreateWiXVariablesIncludeFile()
  231. {
  232. std::string cpackTopLevel;
  233. if(!RequireOption("CPACK_TOPLEVEL_DIRECTORY", cpackTopLevel))
  234. {
  235. return false;
  236. }
  237. std::string includeFilename =
  238. cpackTopLevel + "/cpack_variables.wxi";
  239. cmWIXSourceWriter includeFile(Logger, includeFilename, true);
  240. CopyDefinition(includeFile, "CPACK_WIX_PRODUCT_GUID");
  241. CopyDefinition(includeFile, "CPACK_WIX_UPGRADE_GUID");
  242. CopyDefinition(includeFile, "CPACK_PACKAGE_VENDOR");
  243. CopyDefinition(includeFile, "CPACK_PACKAGE_NAME");
  244. CopyDefinition(includeFile, "CPACK_PACKAGE_VERSION");
  245. CopyDefinition(includeFile, "CPACK_WIX_LICENSE_RTF");
  246. CopyDefinition(includeFile, "CPACK_WIX_PRODUCT_ICON");
  247. CopyDefinition(includeFile, "CPACK_WIX_UI_BANNER");
  248. CopyDefinition(includeFile, "CPACK_WIX_UI_DIALOG");
  249. SetOptionIfNotSet("CPACK_WIX_PROGRAM_MENU_FOLDER",
  250. GetOption("CPACK_PACKAGE_NAME"));
  251. CopyDefinition(includeFile, "CPACK_WIX_PROGRAM_MENU_FOLDER");
  252. CopyDefinition(includeFile, "CPACK_WIX_UI_REF");
  253. return true;
  254. }
  255. void cmCPackWIXGenerator::CopyDefinition(
  256. cmWIXSourceWriter &source, const std::string &name)
  257. {
  258. const char* value = GetOption(name.c_str());
  259. if(value)
  260. {
  261. AddDefinition(source, name, value);
  262. }
  263. }
  264. void cmCPackWIXGenerator::AddDefinition(cmWIXSourceWriter& source,
  265. const std::string& name, const std::string& value)
  266. {
  267. std::stringstream tmp;
  268. tmp << name << "=\"" << value << '"';
  269. source.AddProcessingInstruction("define",
  270. cmWIXSourceWriter::WindowsCodepageToUtf8(tmp.str()));
  271. }
  272. bool cmCPackWIXGenerator::CreateWiXSourceFiles()
  273. {
  274. std::string cpackTopLevel;
  275. if(!RequireOption("CPACK_TOPLEVEL_DIRECTORY", cpackTopLevel))
  276. {
  277. return false;
  278. }
  279. std::string directoryDefinitionsFilename =
  280. cpackTopLevel + "/directories.wxs";
  281. WixSources.push_back(directoryDefinitionsFilename);
  282. cmWIXSourceWriter directoryDefinitions(Logger, directoryDefinitionsFilename);
  283. directoryDefinitions.BeginElement("Fragment");
  284. directoryDefinitions.BeginElement("Directory");
  285. directoryDefinitions.AddAttribute("Id", "TARGETDIR");
  286. directoryDefinitions.AddAttribute("Name", "SourceDir");
  287. directoryDefinitions.BeginElement("Directory");
  288. if(GetArchitecture() == "x86")
  289. {
  290. directoryDefinitions.AddAttribute("Id", "ProgramFilesFolder");
  291. }
  292. else
  293. {
  294. directoryDefinitions.AddAttribute("Id", "ProgramFiles64Folder");
  295. }
  296. std::vector<std::string> install_root;
  297. std::string tmp;
  298. if(!RequireOption("CPACK_PACKAGE_INSTALL_DIRECTORY", tmp))
  299. {
  300. return false;
  301. }
  302. cmSystemTools::SplitPath(tmp.c_str(), install_root);
  303. if(!install_root.empty() && install_root.back().empty())
  304. {
  305. install_root.pop_back();
  306. }
  307. for(size_t i = 1; i < install_root.size(); ++i)
  308. {
  309. directoryDefinitions.BeginElement("Directory");
  310. if(i == install_root.size() - 1)
  311. {
  312. directoryDefinitions.AddAttribute("Id", "INSTALL_ROOT");
  313. }
  314. else
  315. {
  316. std::stringstream ss;
  317. ss << "INSTALL_PREFIX_" << i;
  318. directoryDefinitions.AddAttribute("Id", ss.str());
  319. }
  320. directoryDefinitions.AddAttribute("Name", install_root[i]);
  321. }
  322. std::string fileDefinitionsFilename =
  323. cpackTopLevel + "/files.wxs";
  324. WixSources.push_back(fileDefinitionsFilename);
  325. cmWIXSourceWriter fileDefinitions(Logger, fileDefinitionsFilename);
  326. fileDefinitions.BeginElement("Fragment");
  327. std::string featureDefinitionsFilename =
  328. cpackTopLevel +"/features.wxs";
  329. WixSources.push_back(featureDefinitionsFilename);
  330. cmWIXSourceWriter featureDefinitions(Logger, featureDefinitionsFilename);
  331. featureDefinitions.BeginElement("Fragment");
  332. featureDefinitions.BeginElement("Feature");
  333. featureDefinitions.AddAttribute("Id", "ProductFeature");
  334. featureDefinitions.AddAttribute("Display", "expand");
  335. featureDefinitions.AddAttribute("ConfigurableDirectory", "INSTALL_ROOT");
  336. std::string cpackPackageName;
  337. if(!RequireOption("CPACK_PACKAGE_NAME", cpackPackageName))
  338. {
  339. return false;
  340. }
  341. featureDefinitions.AddAttribute("Title", cpackPackageName);
  342. featureDefinitions.AddAttribute("Level", "1");
  343. CreateFeatureHierarchy(featureDefinitions);
  344. featureDefinitions.EndElement("Feature");
  345. bool hasShortcuts = false;
  346. shortcut_map_t globalShortcuts;
  347. if(Components.empty())
  348. {
  349. AddComponentsToFeature(toplevel, "ProductFeature",
  350. directoryDefinitions, fileDefinitions, featureDefinitions,
  351. globalShortcuts);
  352. if(globalShortcuts.size())
  353. {
  354. hasShortcuts = true;
  355. }
  356. }
  357. else
  358. {
  359. for(std::map<std::string, cmCPackComponent>::const_iterator
  360. i = Components.begin(); i != Components.end(); ++i)
  361. {
  362. cmCPackComponent const& component = i->second;
  363. std::string componentPath = toplevel;
  364. componentPath += "/";
  365. componentPath += component.Name;
  366. std::string componentFeatureId = "CM_C_" + component.Name;
  367. shortcut_map_t featureShortcuts;
  368. AddComponentsToFeature(componentPath, componentFeatureId,
  369. directoryDefinitions, fileDefinitions,
  370. featureDefinitions, featureShortcuts);
  371. if(featureShortcuts.size())
  372. {
  373. hasShortcuts = true;
  374. }
  375. if(featureShortcuts.size())
  376. {
  377. if(!CreateStartMenuShortcuts(component.Name, componentFeatureId,
  378. featureShortcuts, fileDefinitions, featureDefinitions))
  379. {
  380. return false;
  381. }
  382. }
  383. }
  384. }
  385. if(hasShortcuts)
  386. {
  387. if(!CreateStartMenuShortcuts(std::string(), "ProductFeature",
  388. globalShortcuts, fileDefinitions, featureDefinitions))
  389. {
  390. return false;
  391. }
  392. }
  393. featureDefinitions.EndElement("Fragment");
  394. fileDefinitions.EndElement("Fragment");
  395. for(size_t i = 1; i < install_root.size(); ++i)
  396. {
  397. directoryDefinitions.EndElement("Directory");
  398. }
  399. directoryDefinitions.EndElement("Directory");
  400. if(hasShortcuts)
  401. {
  402. CreateStartMenuFolder(directoryDefinitions);
  403. }
  404. directoryDefinitions.EndElement("Directory");
  405. directoryDefinitions.EndElement("Fragment");
  406. std::string wixTemplate = FindTemplate("WIX.template.in");
  407. if(GetOption("CPACK_WIX_TEMPLATE") != 0)
  408. {
  409. wixTemplate = GetOption("CPACK_WIX_TEMPLATE");
  410. }
  411. if(wixTemplate.empty())
  412. {
  413. cmCPackLogger(cmCPackLog::LOG_ERROR,
  414. "Could not find CPack WiX template file WIX.template.in" << std::endl);
  415. return false;
  416. }
  417. std::string mainSourceFilePath = cpackTopLevel + "/main.wxs";
  418. if(!ConfigureFile(wixTemplate.c_str(), mainSourceFilePath .c_str()))
  419. {
  420. cmCPackLogger(cmCPackLog::LOG_ERROR,
  421. "Failed creating '" << mainSourceFilePath <<
  422. "'' from template." << std::endl);
  423. return false;
  424. }
  425. WixSources.push_back(mainSourceFilePath);
  426. std::string fragmentList;
  427. for(cmWIXPatchParser::fragment_map_t::const_iterator
  428. i = Fragments.begin(); i != Fragments.end(); ++i)
  429. {
  430. if(!fragmentList.empty())
  431. {
  432. fragmentList += ", ";
  433. }
  434. fragmentList += "'";
  435. fragmentList += i->first;
  436. fragmentList += "'";
  437. }
  438. if(fragmentList.size())
  439. {
  440. cmCPackLogger(cmCPackLog::LOG_ERROR,
  441. "Some XML patch fragments did not have matching IDs: " <<
  442. fragmentList << std::endl);
  443. return false;
  444. }
  445. return true;
  446. }
  447. bool cmCPackWIXGenerator::CreateFeatureHierarchy(
  448. cmWIXSourceWriter& featureDefinitions)
  449. {
  450. for(std::map<std::string, cmCPackComponentGroup>::const_iterator
  451. i = ComponentGroups.begin(); i != ComponentGroups.end(); ++i)
  452. {
  453. cmCPackComponentGroup const& group = i->second;
  454. if(group.ParentGroup == 0)
  455. {
  456. if(!EmitFeatureForComponentGroup(featureDefinitions, group))
  457. {
  458. return false;
  459. }
  460. }
  461. }
  462. for(std::map<std::string, cmCPackComponent>::const_iterator
  463. i = Components.begin(); i != Components.end(); ++i)
  464. {
  465. cmCPackComponent const& component = i->second;
  466. if(!component.Group)
  467. {
  468. if(!EmitFeatureForComponent(featureDefinitions, component))
  469. {
  470. return false;
  471. }
  472. }
  473. }
  474. return true;
  475. }
  476. bool cmCPackWIXGenerator::EmitFeatureForComponentGroup(
  477. cmWIXSourceWriter& featureDefinitions,
  478. cmCPackComponentGroup const& group)
  479. {
  480. featureDefinitions.BeginElement("Feature");
  481. featureDefinitions.AddAttribute("Id", "CM_G_" + group.Name);
  482. if(group.IsExpandedByDefault)
  483. {
  484. featureDefinitions.AddAttribute("Display", "expand");
  485. }
  486. featureDefinitions.AddAttributeUnlessEmpty(
  487. "Title", group.DisplayName);
  488. featureDefinitions.AddAttributeUnlessEmpty(
  489. "Description", group.Description);
  490. for(std::vector<cmCPackComponentGroup*>::const_iterator
  491. i = group.Subgroups.begin(); i != group.Subgroups.end(); ++i)
  492. {
  493. if(!EmitFeatureForComponentGroup(featureDefinitions, **i))
  494. {
  495. return false;
  496. }
  497. }
  498. for(std::vector<cmCPackComponent*>::const_iterator
  499. i = group.Components.begin(); i != group.Components.end(); ++i)
  500. {
  501. if(!EmitFeatureForComponent(featureDefinitions, **i))
  502. {
  503. return false;
  504. }
  505. }
  506. featureDefinitions.EndElement("Feature");
  507. return true;
  508. }
  509. bool cmCPackWIXGenerator::EmitFeatureForComponent(
  510. cmWIXSourceWriter& featureDefinitions,
  511. cmCPackComponent const& component)
  512. {
  513. featureDefinitions.BeginElement("Feature");
  514. featureDefinitions.AddAttribute("Id", "CM_C_" + component.Name);
  515. featureDefinitions.AddAttributeUnlessEmpty(
  516. "Title", component.DisplayName);
  517. featureDefinitions.AddAttributeUnlessEmpty(
  518. "Description", component.Description);
  519. if(component.IsRequired)
  520. {
  521. featureDefinitions.AddAttribute("Absent", "disallow");
  522. }
  523. if(component.IsHidden)
  524. {
  525. featureDefinitions.AddAttribute("Display", "hidden");
  526. }
  527. featureDefinitions.EndElement("Feature");
  528. return true;
  529. }
  530. bool cmCPackWIXGenerator::AddComponentsToFeature(
  531. std::string const& rootPath,
  532. std::string const& featureId,
  533. cmWIXSourceWriter& directoryDefinitions,
  534. cmWIXSourceWriter& fileDefinitions,
  535. cmWIXSourceWriter& featureDefinitions,
  536. shortcut_map_t& shortcutMap)
  537. {
  538. featureDefinitions.BeginElement("FeatureRef");
  539. featureDefinitions.AddAttribute("Id", featureId);
  540. std::vector<std::string> cpackPackageExecutablesList;
  541. const char *cpackPackageExecutables = GetOption("CPACK_PACKAGE_EXECUTABLES");
  542. if(cpackPackageExecutables)
  543. {
  544. cmSystemTools::ExpandListArgument(cpackPackageExecutables,
  545. cpackPackageExecutablesList);
  546. if(cpackPackageExecutablesList.size() % 2 != 0 )
  547. {
  548. cmCPackLogger(cmCPackLog::LOG_ERROR,
  549. "CPACK_PACKAGE_EXECUTABLES should contain pairs of <executable> and "
  550. "<text label>." << std::endl);
  551. return false;
  552. }
  553. }
  554. AddDirectoryAndFileDefinitons(
  555. rootPath, "INSTALL_ROOT",
  556. directoryDefinitions, fileDefinitions, featureDefinitions,
  557. cpackPackageExecutablesList, shortcutMap);
  558. featureDefinitions.EndElement("FeatureRef");
  559. return true;
  560. }
  561. bool cmCPackWIXGenerator::CreateStartMenuShortcuts(
  562. std::string const& cpackComponentName,
  563. std::string const& featureId,
  564. shortcut_map_t& shortcutMap,
  565. cmWIXSourceWriter& fileDefinitions,
  566. cmWIXSourceWriter& featureDefinitions)
  567. {
  568. featureDefinitions.BeginElement("FeatureRef");
  569. featureDefinitions.AddAttribute("Id", featureId);
  570. std::string cpackVendor;
  571. if(!RequireOption("CPACK_PACKAGE_VENDOR", cpackVendor))
  572. {
  573. return false;
  574. }
  575. std::string cpackPackageName;
  576. if(!RequireOption("CPACK_PACKAGE_NAME", cpackPackageName))
  577. {
  578. return false;
  579. }
  580. std::string idSuffix;
  581. if(!cpackComponentName.empty())
  582. {
  583. idSuffix += "_";
  584. idSuffix += cpackComponentName;
  585. }
  586. std::string componentId = "CM_SHORTCUT" + idSuffix;
  587. fileDefinitions.BeginElement("DirectoryRef");
  588. fileDefinitions.AddAttribute("Id", "PROGRAM_MENU_FOLDER");
  589. fileDefinitions.BeginElement("Component");
  590. fileDefinitions.AddAttribute("Id", componentId);
  591. fileDefinitions.AddAttribute("Guid", "*");
  592. for(shortcut_map_t::const_iterator
  593. i = shortcutMap.begin(); i != shortcutMap.end(); ++i)
  594. {
  595. std::string const& id = i->first;
  596. cmWIXShortcut const& shortcut = i->second;
  597. std::string shortcutId = std::string("CM_S") + id;
  598. std::string fileId = std::string("CM_F") + id;
  599. fileDefinitions.BeginElement("Shortcut");
  600. fileDefinitions.AddAttribute("Id", shortcutId);
  601. fileDefinitions.AddAttribute("Name", shortcut.textLabel);
  602. std::string target = "[#" + fileId + "]";
  603. fileDefinitions.AddAttribute("Target", target);
  604. fileDefinitions.AddAttribute("WorkingDirectory",
  605. shortcut.workingDirectoryId);
  606. fileDefinitions.EndElement("Shortcut");
  607. }
  608. if(cpackComponentName.empty())
  609. {
  610. CreateUninstallShortcut(cpackPackageName, fileDefinitions);
  611. }
  612. fileDefinitions.BeginElement("RemoveFolder");
  613. fileDefinitions.AddAttribute("Id", "PROGRAM_MENU_FOLDER" + idSuffix);
  614. fileDefinitions.AddAttribute("On", "uninstall");
  615. fileDefinitions.EndElement("RemoveFolder");
  616. std::string registryKey =
  617. std::string("Software\\") + cpackVendor + "\\" + cpackPackageName;
  618. fileDefinitions.BeginElement("RegistryValue");
  619. fileDefinitions.AddAttribute("Root", "HKCU");
  620. fileDefinitions.AddAttribute("Key", registryKey);
  621. std::string valueName;
  622. if(!cpackComponentName.empty())
  623. {
  624. valueName = cpackComponentName + "_";
  625. }
  626. valueName += "installed";
  627. fileDefinitions.AddAttribute("Name", valueName);
  628. fileDefinitions.AddAttribute("Type", "integer");
  629. fileDefinitions.AddAttribute("Value", "1");
  630. fileDefinitions.AddAttribute("KeyPath", "yes");
  631. fileDefinitions.EndElement("RegistryValue");
  632. fileDefinitions.EndElement("Component");
  633. fileDefinitions.EndElement("DirectoryRef");
  634. featureDefinitions.BeginElement("ComponentRef");
  635. featureDefinitions.AddAttribute("Id", componentId);
  636. featureDefinitions.EndElement("ComponentRef");
  637. featureDefinitions.EndElement("FeatureRef");
  638. return true;
  639. }
  640. void cmCPackWIXGenerator::CreateUninstallShortcut(
  641. std::string const& packageName,
  642. cmWIXSourceWriter& fileDefinitions)
  643. {
  644. fileDefinitions.BeginElement("Shortcut");
  645. fileDefinitions.AddAttribute("Id", "UNINSTALL");
  646. fileDefinitions.AddAttribute("Name", "Uninstall " + packageName);
  647. fileDefinitions.AddAttribute("Description", "Uninstalls " + packageName);
  648. fileDefinitions.AddAttribute("Target", "[SystemFolder]msiexec.exe");
  649. fileDefinitions.AddAttribute("Arguments", "/x [ProductCode]");
  650. fileDefinitions.EndElement("Shortcut");
  651. }
  652. bool cmCPackWIXGenerator::CreateLicenseFile()
  653. {
  654. std::string licenseSourceFilename;
  655. if(!RequireOption("CPACK_RESOURCE_FILE_LICENSE", licenseSourceFilename))
  656. {
  657. return false;
  658. }
  659. std::string licenseDestinationFilename;
  660. if(!RequireOption("CPACK_WIX_LICENSE_RTF", licenseDestinationFilename))
  661. {
  662. return false;
  663. }
  664. std::string extension = GetRightmostExtension(licenseSourceFilename);
  665. if(extension == ".rtf")
  666. {
  667. cmSystemTools::CopyAFile(
  668. licenseSourceFilename.c_str(),
  669. licenseDestinationFilename.c_str());
  670. }
  671. else if(extension == ".txt")
  672. {
  673. cmWIXRichTextFormatWriter rtfWriter(licenseDestinationFilename);
  674. cmsys::ifstream licenseSource(licenseSourceFilename.c_str());
  675. std::string line;
  676. while(std::getline(licenseSource, line))
  677. {
  678. rtfWriter.AddText(line);
  679. rtfWriter.AddText("\n");
  680. }
  681. }
  682. else
  683. {
  684. cmCPackLogger(cmCPackLog::LOG_ERROR,
  685. "unsupported WiX License file extension '" <<
  686. extension << "'" << std::endl);
  687. return false;
  688. }
  689. return true;
  690. }
  691. void cmCPackWIXGenerator::AddDirectoryAndFileDefinitons(
  692. const std::string& topdir,
  693. const std::string& directoryId,
  694. cmWIXSourceWriter& directoryDefinitions,
  695. cmWIXSourceWriter& fileDefinitions,
  696. cmWIXSourceWriter& featureDefinitions,
  697. const std::vector<std::string>& packageExecutables,
  698. shortcut_map_t& shortcutMap)
  699. {
  700. cmsys::Directory dir;
  701. dir.Load(topdir.c_str());
  702. for(size_t i = 0; i < dir.GetNumberOfFiles(); ++i)
  703. {
  704. std::string fileName = dir.GetFile(static_cast<unsigned long>(i));
  705. if(fileName == "." || fileName == "..")
  706. {
  707. continue;
  708. }
  709. std::string fullPath = topdir + "/" + fileName;
  710. std::string relativePath = cmSystemTools::RelativePath(
  711. toplevel.c_str(), fullPath.c_str());
  712. std::string id = PathToId(relativePath);
  713. if(cmSystemTools::FileIsDirectory(fullPath.c_str()))
  714. {
  715. std::string subDirectoryId = std::string("CM_D") + id;
  716. directoryDefinitions.BeginElement("Directory");
  717. directoryDefinitions.AddAttribute("Id", subDirectoryId);
  718. directoryDefinitions.AddAttribute("Name", fileName);
  719. AddDirectoryAndFileDefinitons(
  720. fullPath, subDirectoryId,
  721. directoryDefinitions,
  722. fileDefinitions,
  723. featureDefinitions,
  724. packageExecutables,
  725. shortcutMap);
  726. ApplyPatchFragment(subDirectoryId, directoryDefinitions);
  727. directoryDefinitions.EndElement("Directory");
  728. }
  729. else
  730. {
  731. std::string componentId = std::string("CM_C") + id;
  732. std::string fileId = std::string("CM_F") + id;
  733. fileDefinitions.BeginElement("DirectoryRef");
  734. fileDefinitions.AddAttribute("Id", directoryId);
  735. fileDefinitions.BeginElement("Component");
  736. fileDefinitions.AddAttribute("Id", componentId);
  737. fileDefinitions.AddAttribute("Guid", "*");
  738. fileDefinitions.BeginElement("File");
  739. fileDefinitions.AddAttribute("Id", fileId);
  740. fileDefinitions.AddAttribute("Source", fullPath);
  741. fileDefinitions.AddAttribute("KeyPath", "yes");
  742. mode_t fileMode = 0;
  743. cmSystemTools::GetPermissions(fullPath.c_str(), fileMode);
  744. if(!(fileMode & S_IWRITE))
  745. {
  746. fileDefinitions.AddAttribute("ReadOnly", "yes");
  747. }
  748. ApplyPatchFragment(fileId, fileDefinitions);
  749. fileDefinitions.EndElement("File");
  750. ApplyPatchFragment(componentId, fileDefinitions);
  751. fileDefinitions.EndElement("Component");
  752. fileDefinitions.EndElement("DirectoryRef");
  753. featureDefinitions.BeginElement("ComponentRef");
  754. featureDefinitions.AddAttribute("Id", componentId);
  755. featureDefinitions.EndElement("ComponentRef");
  756. for(size_t j = 0; j < packageExecutables.size(); ++j)
  757. {
  758. std::string const& executableName = packageExecutables[j++];
  759. std::string const& textLabel = packageExecutables[j];
  760. if(cmSystemTools::LowerCase(fileName) ==
  761. cmSystemTools::LowerCase(executableName) + ".exe")
  762. {
  763. cmWIXShortcut &shortcut = shortcutMap[id];
  764. shortcut.textLabel= textLabel;
  765. shortcut.workingDirectoryId = directoryId;
  766. }
  767. }
  768. }
  769. }
  770. }
  771. bool cmCPackWIXGenerator::RequireOption(
  772. const std::string& name, std::string &value) const
  773. {
  774. const char* tmp = GetOption(name.c_str());
  775. if(tmp)
  776. {
  777. value = tmp;
  778. return true;
  779. }
  780. else
  781. {
  782. cmCPackLogger(cmCPackLog::LOG_ERROR,
  783. "Required variable " << name << " not set" << std::endl);
  784. return false;
  785. }
  786. }
  787. std::string cmCPackWIXGenerator::GetArchitecture() const
  788. {
  789. std::string void_p_size;
  790. RequireOption("CPACK_WIX_SIZEOF_VOID_P", void_p_size);
  791. if(void_p_size == "8")
  792. {
  793. return "x64";
  794. }
  795. else
  796. {
  797. return "x86";
  798. }
  799. }
  800. std::string cmCPackWIXGenerator::GenerateGUID()
  801. {
  802. UUID guid;
  803. UuidCreate(&guid);
  804. unsigned short *tmp = 0;
  805. UuidToStringW(&guid, &tmp);
  806. std::string result =
  807. cmsys::Encoding::ToNarrow(reinterpret_cast<wchar_t*>(tmp));
  808. RpcStringFreeW(&tmp);
  809. return cmSystemTools::UpperCase(result);
  810. }
  811. std::string cmCPackWIXGenerator::QuotePath(const std::string& path)
  812. {
  813. return std::string("\"") + path + '"';
  814. }
  815. std::string cmCPackWIXGenerator::GetRightmostExtension(
  816. const std::string& filename)
  817. {
  818. std::string extension;
  819. std::string::size_type i = filename.rfind(".");
  820. if(i != std::string::npos)
  821. {
  822. extension = filename.substr(i);
  823. }
  824. return cmSystemTools::LowerCase(extension);
  825. }
  826. std::string cmCPackWIXGenerator::PathToId(const std::string& path)
  827. {
  828. id_map_t::const_iterator i = PathToIdMap.find(path);
  829. if(i != PathToIdMap.end()) return i->second;
  830. std::string id = CreateNewIdForPath(path);
  831. return id;
  832. }
  833. std::string cmCPackWIXGenerator::CreateNewIdForPath(const std::string& path)
  834. {
  835. std::vector<std::string> components;
  836. cmSystemTools::SplitPath(path.c_str(), components, false);
  837. size_t replacementCount = 0;
  838. std::string identifier;
  839. std::string currentComponent;
  840. for(size_t i = 1; i < components.size(); ++i)
  841. {
  842. if(i != 1) identifier += '.';
  843. currentComponent = NormalizeComponentForId(
  844. components[i], replacementCount);
  845. identifier += currentComponent;
  846. }
  847. std::string idPrefix = "P";
  848. size_t replacementPercent = replacementCount * 100 / identifier.size();
  849. if(replacementPercent > 33 || identifier.size() > 60)
  850. {
  851. identifier = CreateHashedId(path, currentComponent);
  852. idPrefix = "H";
  853. }
  854. std::stringstream result;
  855. result << idPrefix << "_" << identifier;
  856. size_t ambiguityCount = ++IdAmbiguityCounter[identifier];
  857. if(ambiguityCount > 999)
  858. {
  859. cmCPackLogger(cmCPackLog::LOG_ERROR,
  860. "Error while trying to generate a unique Id for '" <<
  861. path << "'" << std::endl);
  862. return std::string();
  863. }
  864. else if(ambiguityCount > 1)
  865. {
  866. result << "_" << ambiguityCount;
  867. }
  868. std::string resultString = result.str();
  869. PathToIdMap[path] = resultString;
  870. return resultString;
  871. }
  872. std::string cmCPackWIXGenerator::CreateHashedId(
  873. const std::string& path, const std::string& normalizedFilename)
  874. {
  875. cmsys::auto_ptr<cmCryptoHash> sha1 = cmCryptoHash::New("SHA1");
  876. std::string hash = sha1->HashString(path.c_str());
  877. std::string identifier;
  878. identifier += hash.substr(0, 7) + "_";
  879. const size_t maxFileNameLength = 52;
  880. if(normalizedFilename.length() > maxFileNameLength)
  881. {
  882. identifier += normalizedFilename.substr(0, maxFileNameLength - 3);
  883. identifier += "...";
  884. }
  885. else
  886. {
  887. identifier += normalizedFilename;
  888. }
  889. return identifier;
  890. }
  891. std::string cmCPackWIXGenerator::NormalizeComponentForId(
  892. const std::string& component, size_t& replacementCount)
  893. {
  894. std::string result;
  895. result.resize(component.size());
  896. for(size_t i = 0; i < component.size(); ++i)
  897. {
  898. char c = component[i];
  899. if(IsLegalIdCharacter(c))
  900. {
  901. result[i] = c;
  902. }
  903. else
  904. {
  905. result[i] = '_';
  906. ++ replacementCount;
  907. }
  908. }
  909. return result;
  910. }
  911. bool cmCPackWIXGenerator::IsLegalIdCharacter(char c)
  912. {
  913. return (c >= '0' && c <= '9') ||
  914. (c >= 'a' && c <= 'z') ||
  915. (c >= 'A' && c <= 'Z') ||
  916. c == '_' || c == '.';
  917. }
  918. void cmCPackWIXGenerator::CollectExtensions(
  919. const std::string& variableName, extension_set_t& extensions)
  920. {
  921. const char *variableContent = GetOption(variableName.c_str());
  922. if(!variableContent) return;
  923. std::vector<std::string> list;
  924. cmSystemTools::ExpandListArgument(variableContent, list);
  925. for(std::vector<std::string>::const_iterator i = list.begin();
  926. i != list.end(); ++i)
  927. {
  928. extensions.insert(*i);
  929. }
  930. }
  931. void cmCPackWIXGenerator::AddCustomFlags(
  932. const std::string& variableName, std::ostream& stream)
  933. {
  934. const char *variableContent = GetOption(variableName.c_str());
  935. if(!variableContent) return;
  936. std::vector<std::string> list;
  937. cmSystemTools::ExpandListArgument(variableContent, list);
  938. for(std::vector<std::string>::const_iterator i = list.begin();
  939. i != list.end(); ++i)
  940. {
  941. stream << " " << QuotePath(*i);
  942. }
  943. }
  944. void cmCPackWIXGenerator::CreateStartMenuFolder(
  945. cmWIXSourceWriter& directoryDefinitions)
  946. {
  947. directoryDefinitions.BeginElement("Directory");
  948. directoryDefinitions.AddAttribute("Id", "ProgramMenuFolder");
  949. directoryDefinitions.BeginElement("Directory");
  950. directoryDefinitions.AddAttribute("Id", "PROGRAM_MENU_FOLDER");
  951. const char *startMenuFolder = GetOption("CPACK_WIX_PROGRAM_MENU_FOLDER");
  952. directoryDefinitions.AddAttribute("Name", startMenuFolder);
  953. directoryDefinitions.EndElement("Directory");
  954. directoryDefinitions.EndElement("Directory");
  955. }
  956. void cmCPackWIXGenerator::LoadPatchFragments(const std::string& patchFilePath)
  957. {
  958. cmWIXPatchParser parser(Fragments, Logger);
  959. parser.ParseFile(patchFilePath.c_str());
  960. }
  961. void cmCPackWIXGenerator::ApplyPatchFragment(
  962. const std::string& id, cmWIXSourceWriter& writer)
  963. {
  964. cmWIXPatchParser::fragment_map_t::iterator i = Fragments.find(id);
  965. if(i == Fragments.end()) return;
  966. const cmWIXPatchElement& fragment = i->second;
  967. for(cmWIXPatchElement::child_list_t::const_iterator
  968. j = fragment.children.begin(); j != fragment.children.end(); ++j)
  969. {
  970. ApplyPatchElement(**j, writer);
  971. }
  972. Fragments.erase(i);
  973. }
  974. void cmCPackWIXGenerator::ApplyPatchElement(
  975. const cmWIXPatchElement& element, cmWIXSourceWriter& writer)
  976. {
  977. writer.BeginElement(element.name);
  978. for(cmWIXPatchElement::attributes_t::const_iterator
  979. i = element.attributes.begin(); i != element.attributes.end(); ++i)
  980. {
  981. writer.AddAttribute(i->first, i->second);
  982. }
  983. for(cmWIXPatchElement::child_list_t::const_iterator
  984. i = element.children.begin(); i != element.children.end(); ++i)
  985. {
  986. ApplyPatchElement(**i, writer);
  987. }
  988. writer.EndElement(element.name);
  989. }