cmCPackWIXGenerator.cxx 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167
  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 "cmCPackComponentGroup.h"
  5. #include "cmCPackLog.h"
  6. #include "cmCryptoHash.h"
  7. #include "cmGeneratedFileStream.h"
  8. #include "cmInstalledFile.h"
  9. #include "cmStringAlgorithms.h"
  10. #include "cmSystemTools.h"
  11. #include "cmUuid.h"
  12. #include <algorithm>
  13. #include "cmWIXDirectoriesSourceWriter.h"
  14. #include "cmWIXFeaturesSourceWriter.h"
  15. #include "cmWIXFilesSourceWriter.h"
  16. #include "cmWIXRichTextFormatWriter.h"
  17. #include "cmWIXSourceWriter.h"
  18. #include "cmsys/Directory.hxx"
  19. #include "cmsys/Encoding.hxx"
  20. #include "cmsys/FStream.hxx"
  21. #include "cmsys/SystemTools.hxx"
  22. #ifdef _WIN32
  23. # include <rpc.h> // for GUID generation (windows only)
  24. #else
  25. # include <uuid/uuid.h> // for GUID generation (libuuid)
  26. #endif
  27. #include "cmCMakeToWixPath.h"
  28. cmCPackWIXGenerator::cmCPackWIXGenerator()
  29. : Patch(0)
  30. , ComponentGuidType(cmWIXSourceWriter::WIX_GENERATED_GUID)
  31. {
  32. }
  33. cmCPackWIXGenerator::~cmCPackWIXGenerator()
  34. {
  35. if (this->Patch) {
  36. delete this->Patch;
  37. }
  38. }
  39. int cmCPackWIXGenerator::InitializeInternal()
  40. {
  41. componentPackageMethod = ONE_PACKAGE;
  42. this->Patch = new cmWIXPatch(this->Logger);
  43. return this->Superclass::InitializeInternal();
  44. }
  45. bool cmCPackWIXGenerator::RunWiXCommand(std::string const& command)
  46. {
  47. std::string logFileName = this->CPackTopLevel + "/wix.log";
  48. cmCPackLogger(cmCPackLog::LOG_DEBUG,
  49. "Running WiX command: " << command << std::endl);
  50. std::string output;
  51. int returnValue = 0;
  52. bool status = cmSystemTools::RunSingleCommand(
  53. command, &output, &output, &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. cmCPackLogger(cmCPackLog::LOG_ERROR,
  60. "Problem running WiX candle. "
  61. "Please check '"
  62. << logFileName << "' for errors." << std::endl);
  63. return false;
  64. }
  65. return true;
  66. }
  67. bool cmCPackWIXGenerator::RunCandleCommand(std::string const& sourceFile,
  68. std::string const& objectFile)
  69. {
  70. std::string executable;
  71. if (!RequireOption("CPACK_WIX_CANDLE_EXECUTABLE", executable)) {
  72. return false;
  73. }
  74. std::ostringstream command;
  75. command << QuotePath(executable);
  76. command << " -nologo";
  77. command << " -arch " << GetArchitecture();
  78. command << " -out " << QuotePath(objectFile);
  79. for (std::string const& ext : CandleExtensions) {
  80. command << " -ext " << QuotePath(ext);
  81. }
  82. if (sourceFile.rfind(this->CPackTopLevel, 0) != 0) {
  83. command << " " << QuotePath("-I" + this->CPackTopLevel);
  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. return false;
  94. }
  95. std::ostringstream command;
  96. command << QuotePath(executable);
  97. command << " -nologo";
  98. command << " -out " << QuotePath(CMakeToWixPath(packageFileNames.at(0)));
  99. for (std::string const& ext : this->LightExtensions) {
  100. command << " -ext " << QuotePath(ext);
  101. }
  102. const char* const cultures = GetOption("CPACK_WIX_CULTURES");
  103. if (cultures) {
  104. command << " -cultures:" << cultures;
  105. }
  106. AddCustomFlags("CPACK_WIX_LIGHT_EXTRA_FLAGS", command);
  107. command << " " << objectFiles;
  108. return RunWiXCommand(command.str());
  109. }
  110. int cmCPackWIXGenerator::PackageFiles()
  111. {
  112. if (!PackageFilesImpl() || cmSystemTools::GetErrorOccuredFlag()) {
  113. cmCPackLogger(cmCPackLog::LOG_ERROR,
  114. "Fatal WiX Generator Error" << std::endl);
  115. return false;
  116. }
  117. return true;
  118. }
  119. bool cmCPackWIXGenerator::InitializeWiXConfiguration()
  120. {
  121. if (!ReadListFile("Internal/CPack/CPackWIX.cmake")) {
  122. cmCPackLogger(cmCPackLog::LOG_ERROR,
  123. "Error while executing CPackWIX.cmake" << std::endl);
  124. return false;
  125. }
  126. if (GetOption("CPACK_WIX_PRODUCT_GUID") == 0) {
  127. std::string guid = GenerateGUID();
  128. SetOption("CPACK_WIX_PRODUCT_GUID", guid.c_str());
  129. cmCPackLogger(cmCPackLog::LOG_VERBOSE,
  130. "CPACK_WIX_PRODUCT_GUID implicitly set to " << guid << " . "
  131. << std::endl);
  132. }
  133. if (GetOption("CPACK_WIX_UPGRADE_GUID") == 0) {
  134. std::string guid = GenerateGUID();
  135. SetOption("CPACK_WIX_UPGRADE_GUID", guid.c_str());
  136. cmCPackLogger(cmCPackLog::LOG_WARNING,
  137. "CPACK_WIX_UPGRADE_GUID implicitly set to "
  138. << guid
  139. << " . "
  140. "Please refer to the documentation on how and why "
  141. "you might want to set this explicitly."
  142. << std::endl);
  143. }
  144. if (!RequireOption("CPACK_TOPLEVEL_DIRECTORY", this->CPackTopLevel)) {
  145. return false;
  146. }
  147. if (GetOption("CPACK_WIX_LICENSE_RTF") == 0) {
  148. std::string licenseFilename = this->CPackTopLevel + "/License.rtf";
  149. SetOption("CPACK_WIX_LICENSE_RTF", licenseFilename.c_str());
  150. if (!CreateLicenseFile()) {
  151. return false;
  152. }
  153. }
  154. if (GetOption("CPACK_PACKAGE_VENDOR") == 0) {
  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 "
  159. << defaultVendor << " . " << std::endl);
  160. }
  161. if (GetOption("CPACK_WIX_UI_REF") == 0) {
  162. std::string defaultRef = "WixUI_InstallDir";
  163. if (!this->Components.empty()) {
  164. defaultRef = "WixUI_FeatureTree";
  165. }
  166. SetOption("CPACK_WIX_UI_REF", defaultRef.c_str());
  167. }
  168. const char* packageContact = GetOption("CPACK_PACKAGE_CONTACT");
  169. if (packageContact != 0 && GetOption("CPACK_WIX_PROPERTY_ARPCONTACT") == 0) {
  170. SetOption("CPACK_WIX_PROPERTY_ARPCONTACT", packageContact);
  171. }
  172. CollectExtensions("CPACK_WIX_EXTENSIONS", this->CandleExtensions);
  173. CollectExtensions("CPACK_WIX_CANDLE_EXTENSIONS", this->CandleExtensions);
  174. this->LightExtensions.insert("WixUIExtension");
  175. CollectExtensions("CPACK_WIX_EXTENSIONS", this->LightExtensions);
  176. CollectExtensions("CPACK_WIX_LIGHT_EXTENSIONS", this->LightExtensions);
  177. const char* patchFilePath = GetOption("CPACK_WIX_PATCH_FILE");
  178. if (patchFilePath) {
  179. std::vector<std::string> patchFilePaths;
  180. cmExpandList(patchFilePath, patchFilePaths);
  181. for (std::string const& p : patchFilePaths) {
  182. if (!this->Patch->LoadFragments(p)) {
  183. return false;
  184. }
  185. }
  186. }
  187. // if install folder is supposed to be set absolutely, the default
  188. // component guid "*" cannot be used
  189. if (cmSystemTools::IsOn(GetOption("CPACK_WIX_SKIP_PROGRAM_FOLDER"))) {
  190. this->ComponentGuidType = cmWIXSourceWriter::CMAKE_GENERATED_GUID;
  191. }
  192. return true;
  193. }
  194. bool cmCPackWIXGenerator::PackageFilesImpl()
  195. {
  196. if (!InitializeWiXConfiguration()) {
  197. return false;
  198. }
  199. CreateWiXVariablesIncludeFile();
  200. CreateWiXPropertiesIncludeFile();
  201. CreateWiXProductFragmentIncludeFile();
  202. if (!CreateWiXSourceFiles()) {
  203. return false;
  204. }
  205. AppendUserSuppliedExtraSources();
  206. std::set<std::string> usedBaseNames;
  207. std::ostringstream objectFiles;
  208. for (std::string const& sourceFilename : this->WixSources) {
  209. std::string baseName =
  210. cmSystemTools::GetFilenameWithoutLastExtension(sourceFilename);
  211. unsigned int counter = 0;
  212. std::string uniqueBaseName = baseName;
  213. while (usedBaseNames.find(uniqueBaseName) != usedBaseNames.end()) {
  214. std::ostringstream tmp;
  215. tmp << baseName << ++counter;
  216. uniqueBaseName = tmp.str();
  217. }
  218. usedBaseNames.insert(uniqueBaseName);
  219. std::string objectFilename =
  220. this->CPackTopLevel + "/" + uniqueBaseName + ".wixobj";
  221. if (!RunCandleCommand(CMakeToWixPath(sourceFilename),
  222. CMakeToWixPath(objectFilename))) {
  223. return false;
  224. }
  225. objectFiles << " " << QuotePath(CMakeToWixPath(objectFilename));
  226. }
  227. AppendUserSuppliedExtraObjects(objectFiles);
  228. return RunLightCommand(objectFiles.str());
  229. }
  230. void cmCPackWIXGenerator::AppendUserSuppliedExtraSources()
  231. {
  232. const char* cpackWixExtraSources = GetOption("CPACK_WIX_EXTRA_SOURCES");
  233. if (!cpackWixExtraSources)
  234. return;
  235. cmExpandList(cpackWixExtraSources, this->WixSources);
  236. }
  237. void cmCPackWIXGenerator::AppendUserSuppliedExtraObjects(std::ostream& stream)
  238. {
  239. const char* cpackWixExtraObjects = GetOption("CPACK_WIX_EXTRA_OBJECTS");
  240. if (!cpackWixExtraObjects)
  241. return;
  242. std::vector<std::string> expandedExtraObjects;
  243. cmExpandList(cpackWixExtraObjects, expandedExtraObjects);
  244. for (std::string const& obj : expandedExtraObjects) {
  245. stream << " " << QuotePath(obj);
  246. }
  247. }
  248. void cmCPackWIXGenerator::CreateWiXVariablesIncludeFile()
  249. {
  250. std::string includeFilename = this->CPackTopLevel + "/cpack_variables.wxi";
  251. cmWIXSourceWriter includeFile(this->Logger, includeFilename,
  252. this->ComponentGuidType,
  253. cmWIXSourceWriter::INCLUDE_ELEMENT_ROOT);
  254. CopyDefinition(includeFile, "CPACK_WIX_PRODUCT_GUID");
  255. CopyDefinition(includeFile, "CPACK_WIX_UPGRADE_GUID");
  256. CopyDefinition(includeFile, "CPACK_PACKAGE_VENDOR");
  257. CopyDefinition(includeFile, "CPACK_PACKAGE_NAME");
  258. CopyDefinition(includeFile, "CPACK_PACKAGE_VERSION");
  259. CopyDefinition(includeFile, "CPACK_WIX_LICENSE_RTF", DefinitionType::PATH);
  260. CopyDefinition(includeFile, "CPACK_WIX_PRODUCT_ICON", DefinitionType::PATH);
  261. CopyDefinition(includeFile, "CPACK_WIX_UI_BANNER", DefinitionType::PATH);
  262. CopyDefinition(includeFile, "CPACK_WIX_UI_DIALOG", DefinitionType::PATH);
  263. SetOptionIfNotSet("CPACK_WIX_PROGRAM_MENU_FOLDER",
  264. GetOption("CPACK_PACKAGE_NAME"));
  265. CopyDefinition(includeFile, "CPACK_WIX_PROGRAM_MENU_FOLDER");
  266. CopyDefinition(includeFile, "CPACK_WIX_UI_REF");
  267. }
  268. void cmCPackWIXGenerator::CreateWiXPropertiesIncludeFile()
  269. {
  270. std::string includeFilename = this->CPackTopLevel + "/properties.wxi";
  271. cmWIXSourceWriter includeFile(this->Logger, includeFilename,
  272. this->ComponentGuidType,
  273. cmWIXSourceWriter::INCLUDE_ELEMENT_ROOT);
  274. std::string prefix = "CPACK_WIX_PROPERTY_";
  275. std::vector<std::string> options = GetOptions();
  276. for (std::string const& name : options) {
  277. if (name.length() > prefix.length() &&
  278. name.substr(0, prefix.length()) == prefix) {
  279. std::string id = name.substr(prefix.length());
  280. std::string value = GetOption(name.c_str());
  281. includeFile.BeginElement("Property");
  282. includeFile.AddAttribute("Id", id);
  283. includeFile.AddAttribute("Value", value);
  284. includeFile.EndElement("Property");
  285. }
  286. }
  287. if (GetOption("CPACK_WIX_PROPERTY_ARPINSTALLLOCATION") == 0) {
  288. includeFile.BeginElement("Property");
  289. includeFile.AddAttribute("Id", "INSTALL_ROOT");
  290. includeFile.AddAttribute("Secure", "yes");
  291. includeFile.BeginElement("RegistrySearch");
  292. includeFile.AddAttribute("Id", "FindInstallLocation");
  293. includeFile.AddAttribute("Root", "HKLM");
  294. includeFile.AddAttribute(
  295. "Key",
  296. "Software\\Microsoft\\Windows\\"
  297. "CurrentVersion\\Uninstall\\[WIX_UPGRADE_DETECTED]");
  298. includeFile.AddAttribute("Name", "InstallLocation");
  299. includeFile.AddAttribute("Type", "raw");
  300. includeFile.EndElement("RegistrySearch");
  301. includeFile.EndElement("Property");
  302. includeFile.BeginElement("SetProperty");
  303. includeFile.AddAttribute("Id", "ARPINSTALLLOCATION");
  304. includeFile.AddAttribute("Value", "[INSTALL_ROOT]");
  305. includeFile.AddAttribute("After", "CostFinalize");
  306. includeFile.EndElement("SetProperty");
  307. }
  308. }
  309. void cmCPackWIXGenerator::CreateWiXProductFragmentIncludeFile()
  310. {
  311. std::string includeFilename = this->CPackTopLevel + "/product_fragment.wxi";
  312. cmWIXSourceWriter includeFile(this->Logger, includeFilename,
  313. this->ComponentGuidType,
  314. cmWIXSourceWriter::INCLUDE_ELEMENT_ROOT);
  315. this->Patch->ApplyFragment("#PRODUCT", includeFile);
  316. }
  317. void cmCPackWIXGenerator::CopyDefinition(cmWIXSourceWriter& source,
  318. std::string const& name,
  319. DefinitionType type)
  320. {
  321. const char* value = GetOption(name.c_str());
  322. if (value) {
  323. if (type == DefinitionType::PATH) {
  324. AddDefinition(source, name, CMakeToWixPath(value));
  325. } else {
  326. AddDefinition(source, name, value);
  327. }
  328. }
  329. }
  330. void cmCPackWIXGenerator::AddDefinition(cmWIXSourceWriter& source,
  331. std::string const& name,
  332. std::string const& value)
  333. {
  334. std::ostringstream tmp;
  335. tmp << name << "=\"" << value << '"';
  336. source.AddProcessingInstruction("define", tmp.str());
  337. }
  338. bool cmCPackWIXGenerator::CreateWiXSourceFiles()
  339. {
  340. // if install folder is supposed to be set absolutely, the default
  341. // component guid "*" cannot be used
  342. std::string directoryDefinitionsFilename =
  343. this->CPackTopLevel + "/directories.wxs";
  344. this->WixSources.push_back(directoryDefinitionsFilename);
  345. cmWIXDirectoriesSourceWriter directoryDefinitions(
  346. this->Logger, directoryDefinitionsFilename, this->ComponentGuidType);
  347. directoryDefinitions.BeginElement("Fragment");
  348. std::string installRoot;
  349. if (!RequireOption("CPACK_PACKAGE_INSTALL_DIRECTORY", installRoot)) {
  350. return false;
  351. }
  352. directoryDefinitions.BeginElement("Directory");
  353. directoryDefinitions.AddAttribute("Id", "TARGETDIR");
  354. directoryDefinitions.AddAttribute("Name", "SourceDir");
  355. size_t installRootSize =
  356. directoryDefinitions.BeginInstallationPrefixDirectory(GetRootFolderId(),
  357. installRoot);
  358. std::string fileDefinitionsFilename = this->CPackTopLevel + "/files.wxs";
  359. this->WixSources.push_back(fileDefinitionsFilename);
  360. cmWIXFilesSourceWriter fileDefinitions(this->Logger, fileDefinitionsFilename,
  361. this->ComponentGuidType);
  362. fileDefinitions.BeginElement("Fragment");
  363. std::string featureDefinitionsFilename =
  364. this->CPackTopLevel + "/features.wxs";
  365. this->WixSources.push_back(featureDefinitionsFilename);
  366. cmWIXFeaturesSourceWriter featureDefinitions(
  367. this->Logger, featureDefinitionsFilename, this->ComponentGuidType);
  368. featureDefinitions.BeginElement("Fragment");
  369. featureDefinitions.BeginElement("Feature");
  370. featureDefinitions.AddAttribute("Id", "ProductFeature");
  371. featureDefinitions.AddAttribute("Display", "expand");
  372. featureDefinitions.AddAttribute("Absent", "disallow");
  373. featureDefinitions.AddAttribute("ConfigurableDirectory", "INSTALL_ROOT");
  374. std::string cpackPackageName;
  375. if (!RequireOption("CPACK_PACKAGE_NAME", cpackPackageName)) {
  376. return false;
  377. }
  378. std::string featureTitle = cpackPackageName;
  379. if (const char* title = GetOption("CPACK_WIX_ROOT_FEATURE_TITLE")) {
  380. featureTitle = title;
  381. }
  382. featureDefinitions.AddAttribute("Title", featureTitle);
  383. if (const char* desc = GetOption("CPACK_WIX_ROOT_FEATURE_DESCRIPTION")) {
  384. featureDefinitions.AddAttribute("Description", desc);
  385. }
  386. featureDefinitions.AddAttribute("Level", "1");
  387. this->Patch->ApplyFragment("#PRODUCTFEATURE", featureDefinitions);
  388. const char* package = GetOption("CPACK_WIX_CMAKE_PACKAGE_REGISTRY");
  389. if (package) {
  390. featureDefinitions.CreateCMakePackageRegistryEntry(
  391. package, GetOption("CPACK_WIX_UPGRADE_GUID"));
  392. }
  393. if (!CreateFeatureHierarchy(featureDefinitions)) {
  394. return false;
  395. }
  396. featureDefinitions.EndElement("Feature");
  397. std::set<cmWIXShortcuts::Type> emittedShortcutTypes;
  398. cmWIXShortcuts globalShortcuts;
  399. if (Components.empty()) {
  400. AddComponentsToFeature(toplevel, "ProductFeature", directoryDefinitions,
  401. fileDefinitions, featureDefinitions,
  402. globalShortcuts);
  403. globalShortcuts.AddShortcutTypes(emittedShortcutTypes);
  404. } else {
  405. for (auto const& i : this->Components) {
  406. cmCPackComponent const& component = i.second;
  407. std::string componentPath = toplevel;
  408. componentPath += "/";
  409. componentPath += 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 (cmSystemTools::IsOn(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 (!desktopExecutables.empty() &&
  743. std::find(desktopExecutables.begin(), desktopExecutables.end(),
  744. executableName) != desktopExecutables.end()) {
  745. shortcuts.insert(cmWIXShortcuts::DESKTOP, id, shortcut);
  746. }
  747. }
  748. }
  749. }
  750. }
  751. }
  752. bool cmCPackWIXGenerator::RequireOption(std::string const& name,
  753. std::string& value) const
  754. {
  755. const char* tmp = GetOption(name.c_str());
  756. if (tmp) {
  757. value = tmp;
  758. return true;
  759. } else {
  760. cmCPackLogger(cmCPackLog::LOG_ERROR,
  761. "Required variable " << name << " not set" << std::endl);
  762. return false;
  763. }
  764. }
  765. std::string cmCPackWIXGenerator::GetArchitecture() const
  766. {
  767. std::string void_p_size;
  768. RequireOption("CPACK_WIX_SIZEOF_VOID_P", void_p_size);
  769. if (void_p_size == "8") {
  770. return "x64";
  771. } else {
  772. return "x86";
  773. }
  774. }
  775. std::string cmCPackWIXGenerator::GenerateGUID()
  776. {
  777. #ifdef _WIN32
  778. UUID guid;
  779. UuidCreate(&guid);
  780. unsigned short* tmp = 0;
  781. UuidToStringW(&guid, &tmp);
  782. std::string result =
  783. cmsys::Encoding::ToNarrow(reinterpret_cast<wchar_t*>(tmp));
  784. RpcStringFreeW(&tmp);
  785. #else
  786. uuid_t guid;
  787. char guid_ch[37] = { 0 };
  788. uuid_generate(guid);
  789. uuid_unparse(guid, guid_ch);
  790. std::string result = guid_ch;
  791. #endif
  792. return cmSystemTools::UpperCase(result);
  793. }
  794. std::string cmCPackWIXGenerator::QuotePath(std::string const& path)
  795. {
  796. return std::string("\"") + path + '"';
  797. }
  798. std::string cmCPackWIXGenerator::GetRightmostExtension(
  799. std::string const& filename)
  800. {
  801. std::string extension;
  802. std::string::size_type i = filename.rfind(".");
  803. if (i != std::string::npos) {
  804. extension = filename.substr(i);
  805. }
  806. return cmSystemTools::LowerCase(extension);
  807. }
  808. std::string cmCPackWIXGenerator::PathToId(std::string const& path)
  809. {
  810. id_map_t::const_iterator i = PathToIdMap.find(path);
  811. if (i != PathToIdMap.end())
  812. return i->second;
  813. std::string id = CreateNewIdForPath(path);
  814. return id;
  815. }
  816. std::string cmCPackWIXGenerator::CreateNewIdForPath(std::string const& path)
  817. {
  818. std::vector<std::string> components;
  819. cmSystemTools::SplitPath(path.c_str(), components, false);
  820. size_t replacementCount = 0;
  821. std::string identifier;
  822. std::string currentComponent;
  823. for (size_t i = 1; i < components.size(); ++i) {
  824. if (i != 1)
  825. identifier += '.';
  826. currentComponent =
  827. NormalizeComponentForId(components[i], replacementCount);
  828. identifier += currentComponent;
  829. }
  830. std::string idPrefix = "P";
  831. size_t replacementPercent = replacementCount * 100 / identifier.size();
  832. if (replacementPercent > 33 || identifier.size() > 60) {
  833. identifier = CreateHashedId(path, currentComponent);
  834. idPrefix = "H";
  835. }
  836. std::ostringstream result;
  837. result << idPrefix << "_" << identifier;
  838. size_t ambiguityCount = ++IdAmbiguityCounter[identifier];
  839. if (ambiguityCount > 999) {
  840. cmCPackLogger(cmCPackLog::LOG_ERROR,
  841. "Error while trying to generate a unique Id for '"
  842. << path << "'" << std::endl);
  843. return std::string();
  844. } else if (ambiguityCount > 1) {
  845. result << "_" << ambiguityCount;
  846. }
  847. std::string resultString = result.str();
  848. PathToIdMap[path] = resultString;
  849. return resultString;
  850. }
  851. std::string cmCPackWIXGenerator::CreateHashedId(
  852. std::string const& path, std::string const& normalizedFilename)
  853. {
  854. cmCryptoHash sha1(cmCryptoHash::AlgoSHA1);
  855. std::string const hash = sha1.HashString(path);
  856. std::string identifier;
  857. identifier += hash.substr(0, 7) + "_";
  858. const size_t maxFileNameLength = 52;
  859. if (normalizedFilename.length() > maxFileNameLength) {
  860. identifier += normalizedFilename.substr(0, maxFileNameLength - 3);
  861. identifier += "...";
  862. } else {
  863. identifier += normalizedFilename;
  864. }
  865. return identifier;
  866. }
  867. std::string cmCPackWIXGenerator::NormalizeComponentForId(
  868. std::string const& component, size_t& replacementCount)
  869. {
  870. std::string result;
  871. result.resize(component.size());
  872. for (size_t i = 0; i < component.size(); ++i) {
  873. char c = component[i];
  874. if (IsLegalIdCharacter(c)) {
  875. result[i] = c;
  876. } else {
  877. result[i] = '_';
  878. ++replacementCount;
  879. }
  880. }
  881. return result;
  882. }
  883. bool cmCPackWIXGenerator::IsLegalIdCharacter(char c)
  884. {
  885. return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
  886. (c >= 'A' && c <= 'Z') || c == '_' || c == '.';
  887. }
  888. void cmCPackWIXGenerator::CollectExtensions(std::string const& variableName,
  889. extension_set_t& extensions)
  890. {
  891. const char* variableContent = GetOption(variableName.c_str());
  892. if (!variableContent)
  893. return;
  894. std::vector<std::string> list;
  895. cmExpandList(variableContent, list);
  896. extensions.insert(list.begin(), list.end());
  897. }
  898. void cmCPackWIXGenerator::AddCustomFlags(std::string const& variableName,
  899. std::ostream& stream)
  900. {
  901. const char* variableContent = GetOption(variableName.c_str());
  902. if (!variableContent)
  903. return;
  904. std::vector<std::string> list;
  905. cmExpandList(variableContent, list);
  906. for (std::string const& i : list) {
  907. stream << " " << QuotePath(i);
  908. }
  909. }
  910. std::string cmCPackWIXGenerator::RelativePathWithoutComponentPrefix(
  911. std::string const& path)
  912. {
  913. if (this->Components.empty()) {
  914. return path;
  915. }
  916. std::string::size_type pos = path.find('/');
  917. return path.substr(pos + 1);
  918. }