cmCPackBundleGenerator.cxx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  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. //----------------------------------------------------------------------
  17. cmCPackBundleGenerator::cmCPackBundleGenerator()
  18. {
  19. }
  20. //----------------------------------------------------------------------
  21. cmCPackBundleGenerator::~cmCPackBundleGenerator()
  22. {
  23. }
  24. //----------------------------------------------------------------------
  25. int cmCPackBundleGenerator::InitializeInternal()
  26. {
  27. const std::string hdiutil_path = cmSystemTools::FindProgram("hdiutil",
  28. std::vector<std::string>(), false);
  29. if(hdiutil_path.empty())
  30. {
  31. cmCPackLogger(cmCPackLog::LOG_ERROR,
  32. "Cannot locate hdiutil command"
  33. << std::endl);
  34. return 0;
  35. }
  36. this->SetOptionIfNotSet("CPACK_COMMAND_HDIUTIL", hdiutil_path.c_str());
  37. const std::string setfile_path = cmSystemTools::FindProgram("SetFile",
  38. std::vector<std::string>(1, "/Developer/Tools"), false);
  39. if(setfile_path.empty())
  40. {
  41. cmCPackLogger(cmCPackLog::LOG_ERROR,
  42. "Cannot locate SetFile command"
  43. << std::endl);
  44. return 0;
  45. }
  46. this->SetOptionIfNotSet("CPACK_COMMAND_SETFILE", setfile_path.c_str());
  47. return this->Superclass::InitializeInternal();
  48. }
  49. //----------------------------------------------------------------------
  50. const char* cmCPackBundleGenerator::GetOutputExtension()
  51. {
  52. return ".dmg";
  53. }
  54. //----------------------------------------------------------------------
  55. const char* cmCPackBundleGenerator::GetPackagingInstallPrefix()
  56. {
  57. this->InstallPrefix = "/";
  58. this->InstallPrefix += this->GetOption("CPACK_BUNDLE_NAME");
  59. this->InstallPrefix += ".app/Contents/Resources";
  60. return this->InstallPrefix.c_str();
  61. }
  62. //----------------------------------------------------------------------
  63. int cmCPackBundleGenerator::CompressFiles(const char* outFileName,
  64. const char* toplevel, const std::vector<std::string>& files)
  65. {
  66. (void) files;
  67. // Get required arguments ...
  68. const std::string cpack_bundle_name = this->GetOption("CPACK_BUNDLE_NAME") ? this->GetOption("CPACK_BUNDLE_NAME") : "";
  69. if(cpack_bundle_name.empty())
  70. {
  71. cmCPackLogger(cmCPackLog::LOG_ERROR,
  72. "CPACK_BUNDLE_NAME must be set."
  73. << std::endl);
  74. return 0;
  75. }
  76. const std::string cpack_bundle_plist = this->GetOption("CPACK_BUNDLE_PLIST") ? this->GetOption("CPACK_BUNDLE_PLIST") : "";
  77. if(cpack_bundle_plist.empty())
  78. {
  79. cmCPackLogger(cmCPackLog::LOG_ERROR,
  80. "CPACK_BUNDLE_PLIST must be set."
  81. << std::endl);
  82. return 0;
  83. }
  84. const std::string cpack_bundle_icon = this->GetOption("CPACK_BUNDLE_ICON") ? this->GetOption("CPACK_BUNDLE_ICON") : "";
  85. if(cpack_bundle_icon.empty())
  86. {
  87. cmCPackLogger(cmCPackLog::LOG_ERROR,
  88. "CPACK_BUNDLE_ICON must be set."
  89. << std::endl);
  90. return 0;
  91. }
  92. const std::string cpack_bundle_startup_command = this->GetOption("CPACK_BUNDLE_STARTUP_COMMAND") ? this->GetOption("CPACK_BUNDLE_STARTUP_COMMAND") : "";
  93. if(cpack_bundle_startup_command.empty())
  94. {
  95. cmCPackLogger(cmCPackLog::LOG_ERROR,
  96. "CPACK_BUNDLE_STARTUP_COMMAND must be set."
  97. << std::endl);
  98. return 0;
  99. }
  100. // Get optional arguments ...
  101. const std::string cpack_package_icon = this->GetOption("CPACK_PACKAGE_ICON") ? this->GetOption("CPACK_PACKAGE_ICON") : "";
  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. // Install a user-provided startup command (could be an executable or a
  138. // script) ...
  139. cmOStringStream command_source;
  140. command_source << cpack_bundle_startup_command;
  141. cmOStringStream command_target;
  142. command_target << application.str() << "/" << cpack_bundle_name;
  143. if(!this->CopyFile(command_source, command_target))
  144. {
  145. cmCPackLogger(cmCPackLog::LOG_ERROR,
  146. "Error copying startup command. Check the value of CPACK_BUNDLE_STARTUP_COMMAND."
  147. << std::endl);
  148. return 0;
  149. }
  150. cmSystemTools::SetPermissions(command_target.str().c_str(), 0777);
  151. // Add a symlink to /Applications so users can drag-and-drop the bundle
  152. // into it
  153. cmOStringStream application_link;
  154. application_link << staging.str() << "/Applications";
  155. cmSystemTools::CreateSymlink("/Applications",
  156. application_link.str().c_str());
  157. // Optionally add a custom volume icon ...
  158. if(!cpack_package_icon.empty())
  159. {
  160. cmOStringStream package_icon_source;
  161. package_icon_source << cpack_package_icon;
  162. cmOStringStream package_icon_destination;
  163. package_icon_destination << staging.str() << "/.VolumeIcon.icns";
  164. if(!this->CopyFile(package_icon_source, package_icon_destination))
  165. {
  166. cmCPackLogger(cmCPackLog::LOG_ERROR,
  167. "Error copying disk volume icon. Check the value of CPACK_PACKAGE_ICON."
  168. << std::endl);
  169. return 0;
  170. }
  171. }
  172. // Create a temporary read-write disk image ...
  173. cmOStringStream temp_image;
  174. temp_image << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "/temp.dmg";
  175. cmOStringStream temp_image_command;
  176. temp_image_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
  177. temp_image_command << " create";
  178. temp_image_command << " -ov";
  179. temp_image_command << " -srcfolder \"" << staging.str() << "\"";
  180. temp_image_command << " -volname \""
  181. << this->GetOption("CPACK_PACKAGE_FILE_NAME") << "\"";
  182. temp_image_command << " -format UDRW";
  183. temp_image_command << " \"" << temp_image.str() << "\"";
  184. if(!this->RunCommand(temp_image_command))
  185. {
  186. cmCPackLogger(cmCPackLog::LOG_ERROR,
  187. "Error generating temporary disk image."
  188. << std::endl);
  189. return 0;
  190. }
  191. // Optionally set the custom icon flag for the image ...
  192. if(!cpack_package_icon.empty())
  193. {
  194. cmOStringStream temp_mount;
  195. temp_mount << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "/mnt";
  196. cmSystemTools::MakeDirectory(temp_mount.str().c_str());
  197. cmOStringStream attach_command;
  198. attach_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
  199. attach_command << " attach";
  200. attach_command << " -mountpoint \"" << temp_mount.str() << "\"";
  201. attach_command << " \"" << temp_image.str() << "\"";
  202. if(!this->RunCommand(attach_command))
  203. {
  204. cmCPackLogger(cmCPackLog::LOG_ERROR,
  205. "Error attaching temporary disk image."
  206. << std::endl);
  207. return 0;
  208. }
  209. cmOStringStream setfile_command;
  210. setfile_command << this->GetOption("CPACK_COMMAND_SETFILE");
  211. setfile_command << " -a C";
  212. setfile_command << " \"" << temp_mount.str() << "\"";
  213. if(!this->RunCommand(setfile_command))
  214. {
  215. cmCPackLogger(cmCPackLog::LOG_ERROR,
  216. "Error assigning custom icon to temporary disk image."
  217. << std::endl);
  218. return 0;
  219. }
  220. cmOStringStream detach_command;
  221. detach_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
  222. detach_command << " detach";
  223. detach_command << " \"" << temp_mount.str() << "\"";
  224. if(!this->RunCommand(detach_command))
  225. {
  226. cmCPackLogger(cmCPackLog::LOG_ERROR,
  227. "Error detaching temporary disk image."
  228. << std::endl);
  229. return 0;
  230. }
  231. }
  232. // Create the final compressed read-only disk image ...
  233. cmOStringStream final_image_command;
  234. final_image_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
  235. final_image_command << " convert \"" << temp_image.str() << "\"";
  236. final_image_command << " -format UDZO";
  237. final_image_command << " -imagekey";
  238. final_image_command << " zlib-level=9";
  239. final_image_command << " -o \"" << outFileName << "\"";
  240. if(!this->RunCommand(final_image_command))
  241. {
  242. cmCPackLogger(cmCPackLog::LOG_ERROR,
  243. "Error compressing disk image."
  244. << std::endl);
  245. return 0;
  246. }
  247. return 1;
  248. }
  249. //----------------------------------------------------------------------
  250. bool cmCPackBundleGenerator::CopyFile(cmOStringStream& source,
  251. cmOStringStream& target)
  252. {
  253. if(!cmSystemTools::CopyFileIfDifferent(
  254. source.str().c_str(),
  255. target.str().c_str()))
  256. {
  257. cmCPackLogger(cmCPackLog::LOG_ERROR,
  258. "Error copying "
  259. << source.str()
  260. << " to "
  261. << target.str()
  262. << std::endl);
  263. return false;
  264. }
  265. return true;
  266. }
  267. //----------------------------------------------------------------------
  268. bool cmCPackBundleGenerator::RunCommand(cmOStringStream& command)
  269. {
  270. std::string output;
  271. int exit_code = 1;
  272. bool result = cmSystemTools::RunSingleCommand(
  273. command.str().c_str(),
  274. &output,
  275. &exit_code,
  276. 0,
  277. this->GeneratorVerbose,
  278. 0);
  279. if(!result || exit_code)
  280. {
  281. cmCPackLogger(cmCPackLog::LOG_ERROR,
  282. "Error executing: "
  283. << command.str()
  284. << std::endl);
  285. return false;
  286. }
  287. return true;
  288. }