cmCPackBundleGenerator.cxx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. /*=========================================================================
  2. Program: CMake - Cross-Platform Makefile Generator
  3. Module: $RCSfile$
  4. Language: C++
  5. Date: $Date$
  6. Version: $Revision$
  7. Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
  8. See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
  9. This software is distributed WITHOUT ANY WARRANTY; without even
  10. the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  11. PURPOSE. See the above copyright notices for more information.
  12. =========================================================================*/
  13. #include "cmCPackBundleGenerator.h"
  14. #include "cmCPackLog.h"
  15. #include "cmSystemTools.h"
  16. #include <cmsys/RegularExpression.hxx>
  17. //----------------------------------------------------------------------
  18. cmCPackBundleGenerator::cmCPackBundleGenerator()
  19. {
  20. }
  21. //----------------------------------------------------------------------
  22. cmCPackBundleGenerator::~cmCPackBundleGenerator()
  23. {
  24. }
  25. //----------------------------------------------------------------------
  26. int cmCPackBundleGenerator::InitializeInternal()
  27. {
  28. const std::string hdiutil_path = cmSystemTools::FindProgram("hdiutil",
  29. std::vector<std::string>(), false);
  30. if(hdiutil_path.empty())
  31. {
  32. cmCPackLogger(cmCPackLog::LOG_ERROR,
  33. "Cannot locate hdiutil command"
  34. << std::endl);
  35. return 0;
  36. }
  37. this->SetOptionIfNotSet("CPACK_COMMAND_HDIUTIL", hdiutil_path.c_str());
  38. const std::string setfile_path = cmSystemTools::FindProgram("SetFile",
  39. std::vector<std::string>(1, "/Developer/Tools"), false);
  40. if(setfile_path.empty())
  41. {
  42. cmCPackLogger(cmCPackLog::LOG_ERROR,
  43. "Cannot locate SetFile command"
  44. << std::endl);
  45. return 0;
  46. }
  47. this->SetOptionIfNotSet("CPACK_COMMAND_SETFILE", setfile_path.c_str());
  48. return this->Superclass::InitializeInternal();
  49. }
  50. //----------------------------------------------------------------------
  51. const char* cmCPackBundleGenerator::GetOutputExtension()
  52. {
  53. return ".dmg";
  54. }
  55. //----------------------------------------------------------------------
  56. const char* cmCPackBundleGenerator::GetPackagingInstallPrefix()
  57. {
  58. this->InstallPrefix = "/";
  59. this->InstallPrefix += this->GetOption("CPACK_BUNDLE_NAME");
  60. this->InstallPrefix += ".app/Contents/Resources";
  61. return this->InstallPrefix.c_str();
  62. }
  63. //----------------------------------------------------------------------
  64. int cmCPackBundleGenerator::CompressFiles(const char* outFileName,
  65. const char* toplevel, const std::vector<std::string>& files)
  66. {
  67. (void) files;
  68. // Get required arguments ...
  69. const std::string cpack_bundle_name = this->GetOption("CPACK_BUNDLE_NAME")
  70. ? this->GetOption("CPACK_BUNDLE_NAME") : "";
  71. if(cpack_bundle_name.empty())
  72. {
  73. cmCPackLogger(cmCPackLog::LOG_ERROR,
  74. "CPACK_BUNDLE_NAME must be set."
  75. << std::endl);
  76. return 0;
  77. }
  78. const std::string cpack_bundle_plist = this->GetOption("CPACK_BUNDLE_PLIST")
  79. ? this->GetOption("CPACK_BUNDLE_PLIST") : "";
  80. if(cpack_bundle_plist.empty())
  81. {
  82. cmCPackLogger(cmCPackLog::LOG_ERROR,
  83. "CPACK_BUNDLE_PLIST must be set."
  84. << std::endl);
  85. return 0;
  86. }
  87. const std::string cpack_bundle_icon = this->GetOption("CPACK_BUNDLE_ICON")
  88. ? this->GetOption("CPACK_BUNDLE_ICON") : "";
  89. if(cpack_bundle_icon.empty())
  90. {
  91. cmCPackLogger(cmCPackLog::LOG_ERROR,
  92. "CPACK_BUNDLE_ICON must be set."
  93. << std::endl);
  94. return 0;
  95. }
  96. // Get optional arguments ...
  97. const std::string cpack_package_icon = this->GetOption("CPACK_PACKAGE_ICON")
  98. ? this->GetOption("CPACK_PACKAGE_ICON") : "";
  99. const std::string cpack_bundle_startup_command =
  100. this->GetOption("CPACK_BUNDLE_STARTUP_COMMAND")
  101. ? this->GetOption("CPACK_BUNDLE_STARTUP_COMMAND") : "";
  102. // The staging directory contains everything that will end-up inside the
  103. // final disk image ...
  104. cmOStringStream staging;
  105. staging << toplevel;
  106. cmOStringStream contents;
  107. contents << staging.str() << "/" << cpack_bundle_name
  108. << ".app/" << "Contents";
  109. cmOStringStream application;
  110. application << contents.str() << "/" << "MacOS";
  111. cmOStringStream resources;
  112. resources << contents.str() << "/" << "Resources";
  113. // Install a required, user-provided bundle metadata file ...
  114. cmOStringStream plist_source;
  115. plist_source << cpack_bundle_plist;
  116. cmOStringStream plist_target;
  117. plist_target << contents.str() << "/" << "Info.plist";
  118. if(!this->CopyFile(plist_source, plist_target))
  119. {
  120. cmCPackLogger(cmCPackLog::LOG_ERROR,
  121. "Error copying plist. Check the value of CPACK_BUNDLE_PLIST."
  122. << std::endl);
  123. return 0;
  124. }
  125. // Install a user-provided bundle icon ...
  126. cmOStringStream icon_source;
  127. icon_source << cpack_bundle_icon;
  128. cmOStringStream icon_target;
  129. icon_target << resources.str() << "/" << cpack_bundle_name << ".icns";
  130. if(!this->CopyFile(icon_source, icon_target))
  131. {
  132. cmCPackLogger(cmCPackLog::LOG_ERROR,
  133. "Error copying bundle icon. Check the value of CPACK_BUNDLE_ICON."
  134. << std::endl);
  135. return 0;
  136. }
  137. // Optionally a user-provided startup command (could be an
  138. // executable or a script) ...
  139. if(!cpack_bundle_startup_command.empty())
  140. {
  141. cmOStringStream command_source;
  142. command_source << cpack_bundle_startup_command;
  143. cmOStringStream command_target;
  144. command_target << application.str() << "/" << cpack_bundle_name;
  145. if(!this->CopyFile(command_source, command_target))
  146. {
  147. cmCPackLogger(cmCPackLog::LOG_ERROR,
  148. "Error copying startup command. "
  149. " Check the value of CPACK_BUNDLE_STARTUP_COMMAND."
  150. << std::endl);
  151. return 0;
  152. }
  153. cmSystemTools::SetPermissions(command_target.str().c_str(), 0777);
  154. }
  155. // Add a symlink to /Applications so users can drag-and-drop the bundle
  156. // into it
  157. cmOStringStream application_link;
  158. application_link << staging.str() << "/Applications";
  159. cmSystemTools::CreateSymlink("/Applications",
  160. application_link.str().c_str());
  161. // Optionally add a custom volume icon ...
  162. if(!cpack_package_icon.empty())
  163. {
  164. cmOStringStream package_icon_source;
  165. package_icon_source << cpack_package_icon;
  166. cmOStringStream package_icon_destination;
  167. package_icon_destination << staging.str() << "/.VolumeIcon.icns";
  168. if(!this->CopyFile(package_icon_source, package_icon_destination))
  169. {
  170. cmCPackLogger(cmCPackLog::LOG_ERROR,
  171. "Error copying disk volume icon. "
  172. "Check the value of CPACK_PACKAGE_ICON."
  173. << std::endl);
  174. return 0;
  175. }
  176. }
  177. // Create a temporary read-write disk image ...
  178. cmOStringStream temp_image;
  179. temp_image << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "/temp.dmg";
  180. cmOStringStream temp_image_command;
  181. temp_image_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
  182. temp_image_command << " create";
  183. temp_image_command << " -ov";
  184. temp_image_command << " -srcfolder \"" << staging.str() << "\"";
  185. temp_image_command << " -volname \""
  186. << this->GetOption("CPACK_PACKAGE_FILE_NAME") << "\"";
  187. temp_image_command << " -format UDRW";
  188. temp_image_command << " \"" << temp_image.str() << "\"";
  189. if(!this->RunCommand(temp_image_command))
  190. {
  191. cmCPackLogger(cmCPackLog::LOG_ERROR,
  192. "Error generating temporary disk image."
  193. << std::endl);
  194. return 0;
  195. }
  196. // Optionally set the custom icon flag for the image ...
  197. if(!cpack_package_icon.empty())
  198. {
  199. cmOStringStream temp_mount;
  200. cmOStringStream attach_command;
  201. attach_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
  202. attach_command << " attach";
  203. attach_command << " \"" << temp_image.str() << "\"";
  204. std::string attach_output;
  205. if(!this->RunCommand(attach_command, &attach_output))
  206. {
  207. cmCPackLogger(cmCPackLog::LOG_ERROR,
  208. "Error attaching temporary disk image."
  209. << std::endl);
  210. return 0;
  211. }
  212. cmsys::RegularExpression mountpoint_regex(".*(/Volumes/[^\n]+)\n.*");
  213. mountpoint_regex.find(attach_output.c_str());
  214. temp_mount << mountpoint_regex.match(1);
  215. cmOStringStream setfile_command;
  216. setfile_command << this->GetOption("CPACK_COMMAND_SETFILE");
  217. setfile_command << " -a C";
  218. setfile_command << " \"" << temp_mount.str() << "\"";
  219. if(!this->RunCommand(setfile_command))
  220. {
  221. cmCPackLogger(cmCPackLog::LOG_ERROR,
  222. "Error assigning custom icon to temporary disk image."
  223. << std::endl);
  224. return 0;
  225. }
  226. cmOStringStream detach_command;
  227. detach_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
  228. detach_command << " detach";
  229. detach_command << " \"" << temp_mount.str() << "\"";
  230. if(!this->RunCommand(detach_command))
  231. {
  232. cmCPackLogger(cmCPackLog::LOG_ERROR,
  233. "Error detaching temporary disk image."
  234. << std::endl);
  235. return 0;
  236. }
  237. }
  238. // Create the final compressed read-only disk image ...
  239. cmOStringStream final_image_command;
  240. final_image_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
  241. final_image_command << " convert \"" << temp_image.str() << "\"";
  242. final_image_command << " -format UDZO";
  243. final_image_command << " -imagekey";
  244. final_image_command << " zlib-level=9";
  245. final_image_command << " -o \"" << outFileName << "\"";
  246. if(!this->RunCommand(final_image_command))
  247. {
  248. cmCPackLogger(cmCPackLog::LOG_ERROR,
  249. "Error compressing disk image."
  250. << std::endl);
  251. return 0;
  252. }
  253. return 1;
  254. }
  255. //----------------------------------------------------------------------
  256. bool cmCPackBundleGenerator::CopyFile(cmOStringStream& source,
  257. cmOStringStream& target)
  258. {
  259. if(!cmSystemTools::CopyFileIfDifferent(
  260. source.str().c_str(),
  261. target.str().c_str()))
  262. {
  263. cmCPackLogger(cmCPackLog::LOG_ERROR,
  264. "Error copying "
  265. << source.str()
  266. << " to "
  267. << target.str()
  268. << std::endl);
  269. return false;
  270. }
  271. return true;
  272. }
  273. //----------------------------------------------------------------------
  274. bool cmCPackBundleGenerator::RunCommand(cmOStringStream& command,
  275. std::string* output)
  276. {
  277. int exit_code = 1;
  278. bool result = cmSystemTools::RunSingleCommand(
  279. command.str().c_str(),
  280. output,
  281. &exit_code,
  282. 0,
  283. this->GeneratorVerbose,
  284. 0);
  285. if(!result || exit_code)
  286. {
  287. cmCPackLogger(cmCPackLog::LOG_ERROR,
  288. "Error executing: "
  289. << command.str()
  290. << std::endl);
  291. return false;
  292. }
  293. return true;
  294. }