cmCPackWIXGenerator.cxx 33 KB

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