cmCPackDragNDropGenerator.cxx 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970
  1. /*============================================================================
  2. CMake - Cross Platform Makefile Generator
  3. Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
  4. Distributed under the OSI-approved BSD License (the "License");
  5. see accompanying file Copyright.txt for details.
  6. This software is distributed WITHOUT ANY WARRANTY; without even the
  7. implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  8. See the License for more information.
  9. ============================================================================*/
  10. #include "cmCPackDragNDropGenerator.h"
  11. #include "cmCPackLog.h"
  12. #include "cmSystemTools.h"
  13. #include "cmGeneratedFileStream.h"
  14. #include <cmsys/RegularExpression.hxx>
  15. #include <cmsys/FStream.hxx>
  16. #include <iomanip>
  17. #include <CoreFoundation/CFBase.h>
  18. #include <CoreFoundation/CFString.h>
  19. #include <CoreFoundation/CFLocale.h>
  20. // The carbon framework is deprecated, but the Region codes it supplies are
  21. // needed for the LPic data structure used for generating multi-lingual SLAs.
  22. // There does not seem to be a replacement API for these region codes.
  23. #if defined(__clang__)
  24. # pragma clang diagnostic push
  25. # pragma clang diagnostic ignored "-Wdeprecated-declarations"
  26. #endif
  27. #include <Carbon/Carbon.h>
  28. #if defined(__clang__)
  29. # pragma clang diagnostic pop
  30. #endif
  31. static const char* SLAHeader =
  32. "data 'LPic' (5000) {\n"
  33. " $\"0002 0011 0003 0001 0000 0000 0002 0000\"\n"
  34. " $\"0008 0003 0000 0001 0004 0000 0004 0005\"\n"
  35. " $\"0000 000E 0006 0001 0005 0007 0000 0007\"\n"
  36. " $\"0008 0000 0047 0009 0000 0034 000A 0001\"\n"
  37. " $\"0035 000B 0001 0020 000C 0000 0011 000D\"\n"
  38. " $\"0000 005B 0004 0000 0033 000F 0001 000C\"\n"
  39. " $\"0010 0000 000B 000E 0000\"\n"
  40. "};\n"
  41. "\n";
  42. static const char* SLASTREnglish =
  43. "resource 'STR#' (5002, \"English\") {\n"
  44. " {\n"
  45. " \"English\",\n"
  46. " \"Agree\",\n"
  47. " \"Disagree\",\n"
  48. " \"Print\",\n"
  49. " \"Save...\",\n"
  50. " \"You agree to the License Agreement terms when you click \"\n"
  51. " \"the \\\"Agree\\\" button.\",\n"
  52. " \"Software License Agreement\",\n"
  53. " \"This text cannot be saved. This disk may be full or locked, "
  54. "or the \"\n"
  55. " \"file may be locked.\",\n"
  56. " \"Unable to print. Make sure you have selected a printer.\"\n"
  57. " }\n"
  58. "};\n"
  59. "\n";
  60. //----------------------------------------------------------------------
  61. cmCPackDragNDropGenerator::cmCPackDragNDropGenerator()
  62. {
  63. // default to one package file for components
  64. this->componentPackageMethod = ONE_PACKAGE;
  65. }
  66. //----------------------------------------------------------------------
  67. cmCPackDragNDropGenerator::~cmCPackDragNDropGenerator()
  68. {
  69. }
  70. //----------------------------------------------------------------------
  71. int cmCPackDragNDropGenerator::InitializeInternal()
  72. {
  73. // Starting with Xcode 4.3, look in "/Applications/Xcode.app" first:
  74. //
  75. std::vector<std::string> paths;
  76. paths.push_back("/Applications/Xcode.app/Contents/Developer/Tools");
  77. paths.push_back("/Developer/Tools");
  78. const std::string hdiutil_path = cmSystemTools::FindProgram("hdiutil",
  79. std::vector<std::string>(), false);
  80. if(hdiutil_path.empty())
  81. {
  82. cmCPackLogger(cmCPackLog::LOG_ERROR,
  83. "Cannot locate hdiutil command"
  84. << std::endl);
  85. return 0;
  86. }
  87. this->SetOptionIfNotSet("CPACK_COMMAND_HDIUTIL", hdiutil_path.c_str());
  88. const std::string setfile_path = cmSystemTools::FindProgram("SetFile",
  89. paths, false);
  90. if(setfile_path.empty())
  91. {
  92. cmCPackLogger(cmCPackLog::LOG_ERROR,
  93. "Cannot locate SetFile command"
  94. << std::endl);
  95. return 0;
  96. }
  97. this->SetOptionIfNotSet("CPACK_COMMAND_SETFILE", setfile_path.c_str());
  98. const std::string rez_path = cmSystemTools::FindProgram("Rez",
  99. paths, false);
  100. if(rez_path.empty())
  101. {
  102. cmCPackLogger(cmCPackLog::LOG_ERROR,
  103. "Cannot locate Rez command"
  104. << std::endl);
  105. return 0;
  106. }
  107. this->SetOptionIfNotSet("CPACK_COMMAND_REZ", rez_path.c_str());
  108. if(this->IsSet("CPACK_DMG_SLA_DIR"))
  109. {
  110. slaDirectory = this->GetOption("CPACK_DMG_SLA_DIR");
  111. if(!slaDirectory.empty() && this->IsSet("CPACK_RESOURCE_FILE_LICENSE"))
  112. {
  113. std::string license_file =
  114. this->GetOption("CPACK_RESOURCE_FILE_LICENSE");
  115. if(!license_file.empty() &&
  116. (license_file.find("CPack.GenericLicense.txt") == std::string::npos))
  117. {
  118. cmCPackLogger(cmCPackLog::LOG_WARNING,
  119. "Both CPACK_DMG_SLA_DIR and CPACK_RESOURCE_FILE_LICENSE specified, "
  120. "defaulting to CPACK_DMG_SLA_DIR"
  121. << std::endl);
  122. }
  123. }
  124. if(!this->IsSet("CPACK_DMG_LANGUAGES"))
  125. {
  126. cmCPackLogger(cmCPackLog::LOG_ERROR,
  127. "CPACK_DMG_SLA_DIR set but no languages defined "
  128. "(set CPACK_DMG_LANGUAGES)"
  129. << std::endl);
  130. return 0;
  131. }
  132. if(!cmSystemTools::FileExists(slaDirectory, false))
  133. {
  134. cmCPackLogger(cmCPackLog::LOG_ERROR,
  135. "CPACK_DMG_SLA_DIR does not exist"
  136. << std::endl);
  137. return 0;
  138. }
  139. std::vector<std::string> languages;
  140. cmSystemTools::ExpandListArgument(this->GetOption("CPACK_DMG_LANGUAGES"),
  141. languages);
  142. if(languages.empty())
  143. {
  144. cmCPackLogger(cmCPackLog::LOG_ERROR,
  145. "CPACK_DMG_LANGUAGES set but empty"
  146. << std::endl);
  147. return 0;
  148. }
  149. for(size_t i = 0; i < languages.size(); ++i)
  150. {
  151. std::string license = slaDirectory + "/" + languages[i] + ".license.txt";
  152. if (!cmSystemTools::FileExists(license))
  153. {
  154. cmCPackLogger(cmCPackLog::LOG_ERROR,
  155. "Missing license file " << languages[i] << ".license.txt"
  156. << std::endl);
  157. return 0;
  158. }
  159. std::string menu = slaDirectory + "/" + languages[i] + ".menu.txt";
  160. if (!cmSystemTools::FileExists(menu))
  161. {
  162. cmCPackLogger(cmCPackLog::LOG_ERROR,
  163. "Missing menu file " << languages[i] << ".menu.txt"
  164. << std::endl);
  165. return 0;
  166. }
  167. }
  168. }
  169. return this->Superclass::InitializeInternal();
  170. }
  171. //----------------------------------------------------------------------
  172. const char* cmCPackDragNDropGenerator::GetOutputExtension()
  173. {
  174. return ".dmg";
  175. }
  176. //----------------------------------------------------------------------
  177. int cmCPackDragNDropGenerator::PackageFiles()
  178. {
  179. // gather which directories to make dmg files for
  180. // multiple directories occur if packaging components or groups separately
  181. // monolith
  182. if(this->Components.empty())
  183. {
  184. return this->CreateDMG(toplevel, packageFileNames[0]);
  185. }
  186. // component install
  187. std::vector<std::string> package_files;
  188. std::map<std::string, cmCPackComponent>::iterator compIt;
  189. for (compIt=this->Components.begin();
  190. compIt!=this->Components.end(); ++compIt )
  191. {
  192. std::string name = GetComponentInstallDirNameSuffix(compIt->first);
  193. package_files.push_back(name);
  194. }
  195. std::sort(package_files.begin(), package_files.end());
  196. package_files.erase(std::unique(package_files.begin(),
  197. package_files.end()),
  198. package_files.end());
  199. // loop to create dmg files
  200. packageFileNames.clear();
  201. for(size_t i=0; i<package_files.size(); i++)
  202. {
  203. std::string full_package_name = std::string(toplevel) + std::string("/");
  204. if(package_files[i] == "ALL_IN_ONE")
  205. {
  206. full_package_name += this->GetOption("CPACK_PACKAGE_FILE_NAME");
  207. }
  208. else
  209. {
  210. full_package_name += package_files[i];
  211. }
  212. full_package_name += std::string(GetOutputExtension());
  213. packageFileNames.push_back(full_package_name);
  214. std::string src_dir = toplevel;
  215. src_dir += "/";
  216. src_dir += package_files[i];
  217. if(0 == this->CreateDMG(src_dir, full_package_name))
  218. {
  219. return 0;
  220. }
  221. }
  222. return 1;
  223. }
  224. //----------------------------------------------------------------------
  225. bool cmCPackDragNDropGenerator::CopyFile(std::ostringstream& source,
  226. std::ostringstream& target)
  227. {
  228. if(!cmSystemTools::CopyFileIfDifferent(
  229. source.str().c_str(),
  230. target.str().c_str()))
  231. {
  232. cmCPackLogger(cmCPackLog::LOG_ERROR,
  233. "Error copying "
  234. << source.str()
  235. << " to "
  236. << target.str()
  237. << std::endl);
  238. return false;
  239. }
  240. return true;
  241. }
  242. //----------------------------------------------------------------------
  243. bool cmCPackDragNDropGenerator::CreateEmptyFile(std::ostringstream& target,
  244. size_t size)
  245. {
  246. cmsys::ofstream fout(target.str().c_str(),
  247. std::ios::out | std::ios::binary);
  248. if(!fout)
  249. {
  250. return false;
  251. }
  252. else
  253. {
  254. // Seek to desired size - 1 byte
  255. fout.seekp(size - 1, std::ios_base::beg);
  256. char byte = 0;
  257. // Write one byte to ensure file grows
  258. fout.write(&byte, 1);
  259. }
  260. return true;
  261. }
  262. //----------------------------------------------------------------------
  263. bool cmCPackDragNDropGenerator::RunCommand(std::ostringstream& command,
  264. std::string* output)
  265. {
  266. int exit_code = 1;
  267. bool result = cmSystemTools::RunSingleCommand(
  268. command.str().c_str(),
  269. output, output,
  270. &exit_code,
  271. 0,
  272. this->GeneratorVerbose,
  273. 0);
  274. if(!result || exit_code)
  275. {
  276. cmCPackLogger(cmCPackLog::LOG_ERROR,
  277. "Error executing: "
  278. << command.str()
  279. << std::endl);
  280. return false;
  281. }
  282. return true;
  283. }
  284. //----------------------------------------------------------------------
  285. int cmCPackDragNDropGenerator::CreateDMG(const std::string& src_dir,
  286. const std::string& output_file)
  287. {
  288. // Get optional arguments ...
  289. const std::string cpack_package_icon = this->GetOption("CPACK_PACKAGE_ICON")
  290. ? this->GetOption("CPACK_PACKAGE_ICON") : "";
  291. const std::string cpack_dmg_volume_name =
  292. this->GetOption("CPACK_DMG_VOLUME_NAME")
  293. ? this->GetOption("CPACK_DMG_VOLUME_NAME")
  294. : this->GetOption("CPACK_PACKAGE_FILE_NAME");
  295. const std::string cpack_dmg_format =
  296. this->GetOption("CPACK_DMG_FORMAT")
  297. ? this->GetOption("CPACK_DMG_FORMAT") : "UDZO";
  298. // Get optional arguments ...
  299. std::string cpack_license_file =
  300. this->GetOption("CPACK_RESOURCE_FILE_LICENSE") ?
  301. this->GetOption("CPACK_RESOURCE_FILE_LICENSE") : "";
  302. const std::string cpack_dmg_background_image =
  303. this->GetOption("CPACK_DMG_BACKGROUND_IMAGE")
  304. ? this->GetOption("CPACK_DMG_BACKGROUND_IMAGE") : "";
  305. const std::string cpack_dmg_ds_store =
  306. this->GetOption("CPACK_DMG_DS_STORE")
  307. ? this->GetOption("CPACK_DMG_DS_STORE") : "";
  308. const std::string cpack_dmg_languages =
  309. this->GetOption("CPACK_DMG_LANGUAGES")
  310. ? this->GetOption("CPACK_DMG_LANGUAGES") : "";
  311. const std::string cpack_dmg_ds_store_setup_script =
  312. this->GetOption("CPACK_DMG_DS_STORE_SETUP_SCRIPT")
  313. ? this->GetOption("CPACK_DMG_DS_STORE_SETUP_SCRIPT") : "";
  314. // only put license on dmg if is user provided
  315. if(!cpack_license_file.empty() &&
  316. cpack_license_file.find("CPack.GenericLicense.txt") != std::string::npos)
  317. {
  318. cpack_license_file = "";
  319. }
  320. // use sla_dir if both sla_dir and license_file are set
  321. if(!cpack_license_file.empty() &&
  322. !slaDirectory.empty())
  323. {
  324. cpack_license_file = "";
  325. }
  326. // The staging directory contains everything that will end-up inside the
  327. // final disk image ...
  328. std::ostringstream staging;
  329. staging << src_dir;
  330. // Add a symlink to /Applications so users can drag-and-drop the bundle
  331. // into it
  332. std::ostringstream application_link;
  333. application_link << staging.str() << "/Applications";
  334. cmSystemTools::CreateSymlink("/Applications",
  335. application_link.str().c_str());
  336. // Optionally add a custom volume icon ...
  337. if(!cpack_package_icon.empty())
  338. {
  339. std::ostringstream package_icon_source;
  340. package_icon_source << cpack_package_icon;
  341. std::ostringstream package_icon_destination;
  342. package_icon_destination << staging.str() << "/.VolumeIcon.icns";
  343. if(!this->CopyFile(package_icon_source, package_icon_destination))
  344. {
  345. cmCPackLogger(cmCPackLog::LOG_ERROR,
  346. "Error copying disk volume icon. "
  347. "Check the value of CPACK_PACKAGE_ICON."
  348. << std::endl);
  349. return 0;
  350. }
  351. }
  352. // Optionally add a custom .DS_Store file
  353. // (e.g. for setting background/layout) ...
  354. if(!cpack_dmg_ds_store.empty())
  355. {
  356. std::ostringstream package_settings_source;
  357. package_settings_source << cpack_dmg_ds_store;
  358. std::ostringstream package_settings_destination;
  359. package_settings_destination << staging.str() << "/.DS_Store";
  360. if(!this->CopyFile(package_settings_source, package_settings_destination))
  361. {
  362. cmCPackLogger(cmCPackLog::LOG_ERROR,
  363. "Error copying disk volume settings file. "
  364. "Check the value of CPACK_DMG_DS_STORE."
  365. << std::endl);
  366. return 0;
  367. }
  368. }
  369. // Optionally add a custom background image ...
  370. // Make sure the background file type is the same as the custom image
  371. // and that the file is hidden so it doesn't show up.
  372. if(!cpack_dmg_background_image.empty())
  373. {
  374. const std::string extension =
  375. cmSystemTools::GetFilenameLastExtension(cpack_dmg_background_image);
  376. std::ostringstream package_background_source;
  377. package_background_source << cpack_dmg_background_image;
  378. std::ostringstream package_background_destination;
  379. package_background_destination << staging.str()
  380. << "/.background/background" << extension;
  381. if(!this->CopyFile(package_background_source,
  382. package_background_destination))
  383. {
  384. cmCPackLogger(cmCPackLog::LOG_ERROR,
  385. "Error copying disk volume background image. "
  386. "Check the value of CPACK_DMG_BACKGROUND_IMAGE."
  387. << std::endl);
  388. return 0;
  389. }
  390. }
  391. bool remount_image = !cpack_package_icon.empty() ||
  392. !cpack_dmg_ds_store_setup_script.empty();
  393. // Create 1 MB dummy padding file in staging area when we need to remount
  394. // image, so we have enough space for storing changes ...
  395. if(remount_image)
  396. {
  397. std::ostringstream dummy_padding;
  398. dummy_padding << staging.str() << "/.dummy-padding-file";
  399. if(!this->CreateEmptyFile(dummy_padding, 1048576))
  400. {
  401. cmCPackLogger(cmCPackLog::LOG_ERROR,
  402. "Error creating dummy padding file."
  403. << std::endl);
  404. return 0;
  405. }
  406. }
  407. // Create a temporary read-write disk image ...
  408. std::string temp_image = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
  409. temp_image += "/temp.dmg";
  410. std::ostringstream temp_image_command;
  411. temp_image_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
  412. temp_image_command << " create";
  413. temp_image_command << " -ov";
  414. temp_image_command << " -srcfolder \"" << staging.str() << "\"";
  415. temp_image_command << " -volname \""
  416. << cpack_dmg_volume_name << "\"";
  417. temp_image_command << " -format UDRW";
  418. temp_image_command << " \"" << temp_image << "\"";
  419. if(!this->RunCommand(temp_image_command))
  420. {
  421. cmCPackLogger(cmCPackLog::LOG_ERROR,
  422. "Error generating temporary disk image."
  423. << std::endl);
  424. return 0;
  425. }
  426. if(remount_image)
  427. {
  428. // Store that we have a failure so that we always unmount the image
  429. // before we exit.
  430. bool had_error = false;
  431. std::ostringstream attach_command;
  432. attach_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
  433. attach_command << " attach";
  434. attach_command << " \"" << temp_image << "\"";
  435. std::string attach_output;
  436. if(!this->RunCommand(attach_command, &attach_output))
  437. {
  438. cmCPackLogger(cmCPackLog::LOG_ERROR,
  439. "Error attaching temporary disk image."
  440. << std::endl);
  441. return 0;
  442. }
  443. cmsys::RegularExpression mountpoint_regex(".*(/Volumes/[^\n]+)\n.*");
  444. mountpoint_regex.find(attach_output.c_str());
  445. std::ostringstream temp_mount;
  446. temp_mount << mountpoint_regex.match(1);
  447. // Remove dummy padding file so we have enough space on RW image ...
  448. std::ostringstream dummy_padding;
  449. dummy_padding << temp_mount.str() << "/.dummy-padding-file";
  450. if(!cmSystemTools::RemoveFile(dummy_padding.str()))
  451. {
  452. cmCPackLogger(cmCPackLog::LOG_ERROR,
  453. "Error removing dummy padding file."
  454. << std::endl);
  455. had_error = true;
  456. }
  457. // Optionally set the custom icon flag for the image ...
  458. if(!had_error && !cpack_package_icon.empty())
  459. {
  460. std::ostringstream setfile_command;
  461. setfile_command << this->GetOption("CPACK_COMMAND_SETFILE");
  462. setfile_command << " -a C";
  463. setfile_command << " \"" << temp_mount.str() << "\"";
  464. if(!this->RunCommand(setfile_command))
  465. {
  466. cmCPackLogger(cmCPackLog::LOG_ERROR,
  467. "Error assigning custom icon to temporary disk image."
  468. << std::endl);
  469. had_error = true;
  470. }
  471. }
  472. // Optionally we can execute a custom apple script to generate
  473. // the .DS_Store for the volume folder ...
  474. if(!had_error && !cpack_dmg_ds_store_setup_script.empty())
  475. {
  476. std::ostringstream setup_script_command;
  477. setup_script_command << "osascript"
  478. << " \"" << cpack_dmg_ds_store_setup_script << "\""
  479. << " \"" << cpack_dmg_volume_name << "\"";
  480. std::string error;
  481. if(!this->RunCommand(setup_script_command, &error))
  482. {
  483. cmCPackLogger(cmCPackLog::LOG_ERROR,
  484. "Error executing custom script on disk image." << std::endl
  485. << error
  486. << std::endl);
  487. had_error = true;
  488. }
  489. }
  490. std::ostringstream detach_command;
  491. detach_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
  492. detach_command << " detach";
  493. detach_command << " \"" << temp_mount.str() << "\"";
  494. if(!this->RunCommand(detach_command))
  495. {
  496. cmCPackLogger(cmCPackLog::LOG_ERROR,
  497. "Error detaching temporary disk image."
  498. << std::endl);
  499. return 0;
  500. }
  501. if(had_error)
  502. {
  503. return 0;
  504. }
  505. }
  506. if(!cpack_license_file.empty() || !slaDirectory.empty())
  507. {
  508. // Use old hardcoded style if sla_dir is not set
  509. bool oldStyle = slaDirectory.empty();
  510. std::string sla_r = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
  511. sla_r += "/sla.r";
  512. std::vector<std::string> languages;
  513. if(!oldStyle)
  514. {
  515. cmSystemTools::ExpandListArgument(cpack_dmg_languages, languages);
  516. }
  517. cmGeneratedFileStream ofs(sla_r.c_str());
  518. ofs << "#include <CoreServices/CoreServices.r>\n\n";
  519. if(oldStyle)
  520. {
  521. ofs << SLAHeader;
  522. ofs << "\n";
  523. }
  524. else
  525. {
  526. /*
  527. * LPic Layout
  528. * (https://github.com/pypt/dmg-add-license/blob/master/main.c)
  529. * as far as I can tell (no official documentation seems to exist):
  530. * struct LPic {
  531. * uint16_t default_language; // points to a resid, defaulting to 0,
  532. * // which is the first set language
  533. * uint16_t length;
  534. * struct {
  535. * uint16_t language_code;
  536. * uint16_t resid;
  537. * uint16_t encoding; // Encoding from TextCommon.h,
  538. * // forcing MacRoman (0) for now. Might need to
  539. * // allow overwrite per license by user later
  540. * } item[1];
  541. * }
  542. */
  543. // Create vector first for readability, then iterate to write to ofs
  544. std::vector<uint16_t> header_data;
  545. header_data.push_back(0);
  546. header_data.push_back(languages.size());
  547. for(size_t i = 0; i < languages.size(); ++i)
  548. {
  549. CFStringRef language_cfstring = CFStringCreateWithCString(
  550. NULL, languages[i].c_str(), kCFStringEncodingUTF8);
  551. CFStringRef iso_language =
  552. CFLocaleCreateCanonicalLanguageIdentifierFromString(
  553. NULL, language_cfstring);
  554. if (!iso_language)
  555. {
  556. cmCPackLogger(cmCPackLog::LOG_ERROR,
  557. languages[i] << " is not a recognized language"
  558. << std::endl);
  559. }
  560. char *iso_language_cstr = (char *) malloc(65);
  561. CFStringGetCString(iso_language, iso_language_cstr, 64,
  562. kCFStringEncodingMacRoman);
  563. LangCode lang = 0;
  564. RegionCode region = 0;
  565. OSStatus err = LocaleStringToLangAndRegionCodes(iso_language_cstr,
  566. &lang, &region);
  567. if (err != noErr)
  568. {
  569. cmCPackLogger(cmCPackLog::LOG_ERROR,
  570. "No language/region code available for " << iso_language_cstr
  571. << std::endl);
  572. free(iso_language_cstr);
  573. return 0;
  574. }
  575. free(iso_language_cstr);
  576. header_data.push_back(region);
  577. header_data.push_back(i);
  578. header_data.push_back(0);
  579. }
  580. ofs << "data 'LPic' (5000) {\n";
  581. ofs << std::hex << std::uppercase << std::setfill('0');
  582. for(size_t i = 0; i < header_data.size(); ++i)
  583. {
  584. if(i % 8 == 0)
  585. {
  586. ofs << " $\"";
  587. }
  588. ofs << std::setw(4) << header_data[i];
  589. if(i % 8 == 7 || i == header_data.size() - 1)
  590. {
  591. ofs << "\"\n";
  592. }
  593. else
  594. {
  595. ofs << " ";
  596. }
  597. }
  598. ofs << "};\n\n";
  599. // Reset ofs options
  600. ofs << std::dec << std::nouppercase << std::setfill(' ');
  601. }
  602. if(oldStyle)
  603. {
  604. WriteLicense(ofs, 0, "", cpack_license_file);
  605. }
  606. else
  607. {
  608. for(size_t i = 0; i < languages.size(); ++i)
  609. {
  610. WriteLicense(ofs, i + 5000, languages[i]);
  611. }
  612. }
  613. ofs.Close();
  614. // convert to UDCO
  615. std::string temp_udco = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
  616. temp_udco += "/temp-udco.dmg";
  617. std::ostringstream udco_image_command;
  618. udco_image_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
  619. udco_image_command << " convert \"" << temp_image << "\"";
  620. udco_image_command << " -format UDCO";
  621. udco_image_command << " -ov -o \"" << temp_udco << "\"";
  622. std::string error;
  623. if(!this->RunCommand(udco_image_command, &error))
  624. {
  625. cmCPackLogger(cmCPackLog::LOG_ERROR,
  626. "Error converting to UDCO dmg for adding SLA." << std::endl
  627. << error
  628. << std::endl);
  629. return 0;
  630. }
  631. // unflatten dmg
  632. std::ostringstream unflatten_command;
  633. unflatten_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
  634. unflatten_command << " unflatten ";
  635. unflatten_command << "\"" << temp_udco << "\"";
  636. if(!this->RunCommand(unflatten_command, &error))
  637. {
  638. cmCPackLogger(cmCPackLog::LOG_ERROR,
  639. "Error unflattening dmg for adding SLA." << std::endl
  640. << error
  641. << std::endl);
  642. return 0;
  643. }
  644. // Rez the SLA
  645. std::ostringstream embed_sla_command;
  646. embed_sla_command << this->GetOption("CPACK_COMMAND_REZ");
  647. const char* sysroot = this->GetOption("CPACK_OSX_SYSROOT");
  648. if(sysroot && sysroot[0] != '\0')
  649. {
  650. embed_sla_command << " -isysroot \"" << sysroot << "\"";
  651. }
  652. embed_sla_command << " \"" << sla_r << "\"";
  653. embed_sla_command << " -a -o ";
  654. embed_sla_command << "\"" << temp_udco << "\"";
  655. if(!this->RunCommand(embed_sla_command, &error))
  656. {
  657. cmCPackLogger(cmCPackLog::LOG_ERROR,
  658. "Error adding SLA." << std::endl
  659. << error
  660. << std::endl);
  661. return 0;
  662. }
  663. // flatten dmg
  664. std::ostringstream flatten_command;
  665. flatten_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
  666. flatten_command << " flatten ";
  667. flatten_command << "\"" << temp_udco << "\"";
  668. if(!this->RunCommand(flatten_command, &error))
  669. {
  670. cmCPackLogger(cmCPackLog::LOG_ERROR,
  671. "Error flattening dmg for adding SLA." << std::endl
  672. << error
  673. << std::endl);
  674. return 0;
  675. }
  676. temp_image = temp_udco;
  677. }
  678. // Create the final compressed read-only disk image ...
  679. std::ostringstream final_image_command;
  680. final_image_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
  681. final_image_command << " convert \"" << temp_image << "\"";
  682. final_image_command << " -format ";
  683. final_image_command << cpack_dmg_format;
  684. final_image_command << " -imagekey";
  685. final_image_command << " zlib-level=9";
  686. final_image_command << " -o \"" << output_file << "\"";
  687. if(!this->RunCommand(final_image_command))
  688. {
  689. cmCPackLogger(cmCPackLog::LOG_ERROR,
  690. "Error compressing disk image."
  691. << std::endl);
  692. return 0;
  693. }
  694. return 1;
  695. }
  696. bool cmCPackDragNDropGenerator::SupportsComponentInstallation() const
  697. {
  698. return true;
  699. }
  700. std::string
  701. cmCPackDragNDropGenerator::GetComponentInstallDirNameSuffix(
  702. const std::string& componentName)
  703. {
  704. // we want to group components together that go in the same dmg package
  705. std::string package_file_name = this->GetOption("CPACK_PACKAGE_FILE_NAME");
  706. // we have 3 mutually exclusive modes to work in
  707. // 1. all components in one package
  708. // 2. each group goes in its own package with left over
  709. // components in their own package
  710. // 3. ignore groups - if grouping is defined, it is ignored
  711. // and each component goes in its own package
  712. if(this->componentPackageMethod == ONE_PACKAGE)
  713. {
  714. return "ALL_IN_ONE";
  715. }
  716. if(this->componentPackageMethod == ONE_PACKAGE_PER_GROUP)
  717. {
  718. // We have to find the name of the COMPONENT GROUP
  719. // the current COMPONENT belongs to.
  720. std::string groupVar = "CPACK_COMPONENT_" +
  721. cmSystemTools::UpperCase(componentName) + "_GROUP";
  722. const char* _groupName = GetOption(groupVar.c_str());
  723. if (_groupName)
  724. {
  725. std::string groupName = _groupName;
  726. groupName = GetComponentPackageFileName(package_file_name,
  727. groupName, true);
  728. return groupName;
  729. }
  730. }
  731. return GetComponentPackageFileName(package_file_name, componentName, false);
  732. }
  733. void
  734. cmCPackDragNDropGenerator::WriteLicense(cmGeneratedFileStream& outputStream,
  735. int licenseNumber, std::string licenseLanguage, std::string licenseFile)
  736. {
  737. if(!licenseFile.empty())
  738. {
  739. licenseNumber = 5002;
  740. licenseLanguage = "English";
  741. }
  742. // License header
  743. outputStream << "data 'TEXT' (" << licenseNumber << ", \""
  744. << licenseLanguage << "\") {\n";
  745. // License body
  746. std::string actual_license = !licenseFile.empty() ? licenseFile :
  747. (slaDirectory + "/" + licenseLanguage + ".license.txt");
  748. cmsys::ifstream license_ifs;
  749. license_ifs.open(actual_license.c_str());
  750. if(license_ifs.is_open())
  751. {
  752. while(license_ifs.good())
  753. {
  754. std::string line;
  755. std::getline(license_ifs, line);
  756. if(!line.empty())
  757. {
  758. EscapeQuotes(line);
  759. std::vector<std::string> lines;
  760. BreakLongLine(line, lines);
  761. for(size_t i = 0; i < lines.size(); ++i)
  762. {
  763. outputStream << " \"" << lines[i] << "\"\n";
  764. }
  765. }
  766. outputStream << " \"\\n\"\n";
  767. }
  768. license_ifs.close();
  769. }
  770. // End of License
  771. outputStream << "};\n\n";
  772. if(!licenseFile.empty())
  773. {
  774. outputStream << SLASTREnglish;
  775. }
  776. else
  777. {
  778. // Menu header
  779. outputStream << "resource 'STR#' (" << licenseNumber << ", \""
  780. << licenseLanguage << "\") {\n";
  781. outputStream << " {\n";
  782. // Menu body
  783. cmsys::ifstream menu_ifs;
  784. menu_ifs.open((slaDirectory+"/"+licenseLanguage+".menu.txt").c_str());
  785. if(menu_ifs.is_open())
  786. {
  787. size_t lines_written = 0;
  788. while(menu_ifs.good())
  789. {
  790. // Lines written from original file, not from broken up lines
  791. std::string line;
  792. std::getline(menu_ifs, line);
  793. if(!line.empty())
  794. {
  795. EscapeQuotes(line);
  796. std::vector<std::string> lines;
  797. BreakLongLine(line, lines);
  798. for(size_t i = 0; i < lines.size(); ++i)
  799. {
  800. std::string comma;
  801. // We need a comma after every complete string,
  802. // but not on the very last line
  803. if(lines_written != 8 && i == lines.size() - 1)
  804. {
  805. comma = ",";
  806. }
  807. else
  808. {
  809. comma = "";
  810. }
  811. outputStream << " \"" << lines[i] << "\"" << comma << "\n";
  812. }
  813. ++lines_written;
  814. }
  815. }
  816. menu_ifs.close();
  817. }
  818. //End of menu
  819. outputStream << " }\n";
  820. outputStream << "};\n";
  821. outputStream << "\n";
  822. }
  823. }
  824. void
  825. cmCPackDragNDropGenerator::BreakLongLine(const std::string& line,
  826. std::vector<std::string>& lines)
  827. {
  828. const size_t max_line_length = 512;
  829. for(size_t i = 0; i < line.size(); i += max_line_length)
  830. {
  831. int line_length = max_line_length;
  832. if(i + max_line_length > line.size())
  833. {
  834. line_length = line.size() - i;
  835. }
  836. lines.push_back(line.substr(i, line_length));
  837. }
  838. }
  839. void
  840. cmCPackDragNDropGenerator::EscapeQuotes(std::string& line)
  841. {
  842. std::string::size_type pos = line.find('\"');
  843. while(pos != std::string::npos)
  844. {
  845. line.replace(pos, 1, "\\\"");
  846. pos = line.find('\"', pos + 2);
  847. }
  848. }