cmCPackWIXGenerator.cxx 34 KB

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