cmCPackPackageMakerGenerator.cxx 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  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 "cmCPackPackageMakerGenerator.h"
  14. #include "cmake.h"
  15. #include "cmGlobalGenerator.h"
  16. #include "cmLocalGenerator.h"
  17. #include "cmSystemTools.h"
  18. #include "cmMakefile.h"
  19. #include "cmGeneratedFileStream.h"
  20. #include "cmCPackComponentGroup.h"
  21. #include "cmCPackLog.h"
  22. #include <cmsys/SystemTools.hxx>
  23. #include <cmsys/Glob.hxx>
  24. //----------------------------------------------------------------------
  25. cmCPackPackageMakerGenerator::cmCPackPackageMakerGenerator()
  26. {
  27. this->PackageMakerVersion = 0.0;
  28. }
  29. //----------------------------------------------------------------------
  30. cmCPackPackageMakerGenerator::~cmCPackPackageMakerGenerator()
  31. {
  32. }
  33. //----------------------------------------------------------------------
  34. bool cmCPackPackageMakerGenerator::SupportsComponentInstallation() const
  35. {
  36. return true;
  37. }
  38. //----------------------------------------------------------------------
  39. int cmCPackPackageMakerGenerator::CopyInstallScript(const char* resdir,
  40. const char* script,
  41. const char* name)
  42. {
  43. std::string dst = resdir;
  44. dst += "/";
  45. dst += name;
  46. cmSystemTools::CopyFileAlways(script, dst.c_str());
  47. cmSystemTools::SetPermissions(dst.c_str(),0777);
  48. cmCPackLogger(cmCPackLog::LOG_VERBOSE,
  49. "copy script : " << script << "\ninto " << dst.c_str() <<
  50. std::endl);
  51. return 1;
  52. }
  53. //----------------------------------------------------------------------
  54. int cmCPackPackageMakerGenerator::CompressFiles(const char* outFileName,
  55. const char* toplevel,
  56. const std::vector<std::string>& files)
  57. {
  58. (void) files; // TODO: Fix api to not need files.
  59. (void) toplevel; // TODO: Use toplevel
  60. std::string resDir; // Where this package's resources will go.
  61. std::string packageDirFileName
  62. = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
  63. if (this->Components.empty())
  64. {
  65. packageDirFileName += ".pkg";
  66. resDir = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
  67. resDir += "/Resources";
  68. }
  69. else
  70. {
  71. packageDirFileName += ".mpkg";
  72. if ( !cmsys::SystemTools::MakeDirectory(packageDirFileName.c_str()))
  73. {
  74. cmCPackLogger(cmCPackLog::LOG_ERROR,
  75. "unable to create package directory "
  76. << packageDirFileName << std::endl);
  77. return 0;
  78. }
  79. resDir = packageDirFileName;
  80. resDir += "/Contents";
  81. if ( !cmsys::SystemTools::MakeDirectory(resDir.c_str()))
  82. {
  83. cmCPackLogger(cmCPackLog::LOG_ERROR,
  84. "unable to create package subdirectory " << resDir
  85. << std::endl);
  86. return 0;
  87. }
  88. resDir += "/Resources";
  89. if ( !cmsys::SystemTools::MakeDirectory(resDir.c_str()))
  90. {
  91. cmCPackLogger(cmCPackLog::LOG_ERROR,
  92. "unable to create package subdirectory " << resDir
  93. << std::endl);
  94. return 0;
  95. }
  96. resDir += "/en.lproj";
  97. }
  98. // Create directory structure
  99. std::string preflightDirName = resDir + "/PreFlight";
  100. std::string postflightDirName = resDir + "/PostFlight";
  101. const char* preflight = this->GetOption("CPACK_PREFLIGHT_SCRIPT");
  102. const char* postflight = this->GetOption("CPACK_POSTFLIGHT_SCRIPT");
  103. const char* postupgrade = this->GetOption("CPACK_POSTUPGRADE_SCRIPT");
  104. // if preflight or postflight scripts not there create directories
  105. // of the same name, I think this makes it work
  106. if(!preflight)
  107. {
  108. if ( !cmsys::SystemTools::MakeDirectory(preflightDirName.c_str()))
  109. {
  110. cmCPackLogger(cmCPackLog::LOG_ERROR,
  111. "Problem creating installer directory: "
  112. << preflightDirName.c_str() << std::endl);
  113. return 0;
  114. }
  115. }
  116. if(!postflight)
  117. {
  118. if ( !cmsys::SystemTools::MakeDirectory(postflightDirName.c_str()))
  119. {
  120. cmCPackLogger(cmCPackLog::LOG_ERROR,
  121. "Problem creating installer directory: "
  122. << postflightDirName.c_str() << std::endl);
  123. return 0;
  124. }
  125. }
  126. // if preflight, postflight, or postupgrade are set
  127. // then copy them into the resource directory and make
  128. // them executable
  129. if(preflight)
  130. {
  131. this->CopyInstallScript(resDir.c_str(),
  132. preflight,
  133. "preflight");
  134. }
  135. if(postflight)
  136. {
  137. this->CopyInstallScript(resDir.c_str(),
  138. postflight,
  139. "postflight");
  140. }
  141. if(postupgrade)
  142. {
  143. this->CopyInstallScript(resDir.c_str(),
  144. postupgrade,
  145. "postupgrade");
  146. }
  147. if (!this->Components.empty())
  148. {
  149. // Create the directory where component packages will be installed.
  150. std::string basePackageDir = packageDirFileName;
  151. basePackageDir += "/Contents/Packages";
  152. if (!cmsys::SystemTools::MakeDirectory(basePackageDir.c_str()))
  153. {
  154. cmCPackLogger(cmCPackLog::LOG_ERROR,
  155. "Problem creating component packages directory: "
  156. << basePackageDir.c_str() << std::endl);
  157. return 0;
  158. }
  159. // Create packages for each component
  160. std::map<std::string, cmCPackComponent>::iterator compIt;
  161. for (compIt = this->Components.begin(); compIt != this->Components.end();
  162. ++compIt)
  163. {
  164. std::string packageFile = basePackageDir;
  165. packageFile += '/';
  166. packageFile += GetPackageName(compIt->second);
  167. std::string packageDir = toplevel;
  168. packageDir += '/';
  169. packageDir += compIt->first;
  170. if (!this->GenerateComponentPackage(packageFile.c_str(),
  171. packageDir.c_str(),
  172. compIt->second))
  173. {
  174. return 0;
  175. }
  176. }
  177. }
  178. this->SetOption("CPACK_MODULE_VERSION_SUFFIX", "");
  179. // Copy or create all of the resource files we need.
  180. if ( !this->CopyCreateResourceFile("License", resDir.c_str())
  181. || !this->CopyCreateResourceFile("ReadMe", resDir.c_str())
  182. || !this->CopyCreateResourceFile("Welcome", resDir.c_str())
  183. || !this->CopyResourcePlistFile("Info.plist")
  184. || !this->CopyResourcePlistFile("Description.plist") )
  185. {
  186. cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem copying the resource files"
  187. << std::endl);
  188. return 0;
  189. }
  190. if (this->Components.empty())
  191. {
  192. // Use PackageMaker to build the package.
  193. cmOStringStream pkgCmd;
  194. pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
  195. << "\" -build -p \"" << packageDirFileName << "\"";
  196. if (this->Components.empty())
  197. {
  198. pkgCmd << " -f \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY");
  199. }
  200. else
  201. {
  202. pkgCmd << " -mi \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY")
  203. << "/packages/";
  204. }
  205. pkgCmd << "\" -r \"" << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
  206. << "/Resources\" -i \""
  207. << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
  208. << "/Info.plist\" -d \""
  209. << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
  210. << "/Description.plist\"";
  211. if ( this->PackageMakerVersion > 2.0 )
  212. {
  213. pkgCmd << " -v";
  214. }
  215. if (!RunPackageMaker(pkgCmd.str().c_str(), packageDirFileName.c_str()))
  216. return 0;
  217. }
  218. else
  219. {
  220. // We have built the package in place. Generate the
  221. // distribution.dist file to describe it for the installer.
  222. WriteDistributionFile(packageDirFileName.c_str());
  223. }
  224. std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
  225. tmpFile += "/hdiutilOutput.log";
  226. cmOStringStream dmgCmd;
  227. dmgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM_DISK_IMAGE")
  228. << "\" create -ov -format UDZO -srcfolder \"" << packageDirFileName
  229. << "\" \"" << outFileName << "\"";
  230. std::string output;
  231. int retVal = 1;
  232. bool res = cmSystemTools::RunSingleCommand(dmgCmd.str().c_str(), &output,
  233. &retVal, 0, this->GeneratorVerbose, 0);
  234. if ( !res || retVal )
  235. {
  236. cmGeneratedFileStream ofs(tmpFile.c_str());
  237. ofs << "# Run command: " << dmgCmd.str().c_str() << std::endl
  238. << "# Output:" << std::endl
  239. << output.c_str() << std::endl;
  240. cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running hdiutil command: "
  241. << dmgCmd.str().c_str() << std::endl
  242. << "Please check " << tmpFile.c_str() << " for errors" << std::endl);
  243. return 0;
  244. }
  245. return 1;
  246. }
  247. //----------------------------------------------------------------------
  248. int cmCPackPackageMakerGenerator::InitializeInternal()
  249. {
  250. cmCPackLogger(cmCPackLog::LOG_DEBUG,
  251. "cmCPackPackageMakerGenerator::Initialize()" << std::endl);
  252. this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/usr");
  253. std::vector<std::string> path;
  254. std::string pkgPath
  255. = "/Developer/Applications/Utilities/PackageMaker.app/Contents";
  256. std::string versionFile = pkgPath + "/version.plist";
  257. if ( !cmSystemTools::FileExists(versionFile.c_str()) )
  258. {
  259. pkgPath = "/Developer/Applications/PackageMaker.app/Contents";
  260. std::string newVersionFile = pkgPath + "/version.plist";
  261. if ( !cmSystemTools::FileExists(newVersionFile.c_str()) )
  262. {
  263. cmCPackLogger(cmCPackLog::LOG_ERROR,
  264. "Cannot find PackageMaker compiler version file: "
  265. << versionFile.c_str() << " or " << newVersionFile.c_str()
  266. << std::endl);
  267. return 0;
  268. }
  269. versionFile = newVersionFile;
  270. }
  271. std::ifstream ifs(versionFile.c_str());
  272. if ( !ifs )
  273. {
  274. cmCPackLogger(cmCPackLog::LOG_ERROR,
  275. "Cannot open PackageMaker compiler version file" << std::endl);
  276. return 0;
  277. }
  278. // Check the PackageMaker version
  279. cmsys::RegularExpression rexKey("<key>CFBundleShortVersionString</key>");
  280. cmsys::RegularExpression rexVersion("<string>([0-9]+.[0-9.]+)</string>");
  281. std::string line;
  282. bool foundKey = false;
  283. while ( cmSystemTools::GetLineFromStream(ifs, line) )
  284. {
  285. if ( rexKey.find(line) )
  286. {
  287. foundKey = true;
  288. break;
  289. }
  290. }
  291. if ( !foundKey )
  292. {
  293. cmCPackLogger(cmCPackLog::LOG_ERROR,
  294. "Cannot find CFBundleShortVersionString in the PackageMaker compiler "
  295. "version file" << std::endl);
  296. return 0;
  297. }
  298. if ( !cmSystemTools::GetLineFromStream(ifs, line) ||
  299. !rexVersion.find(line) )
  300. {
  301. cmCPackLogger(cmCPackLog::LOG_ERROR,
  302. "Problem reading the PackageMaker compiler version file: "
  303. << versionFile.c_str() << std::endl);
  304. return 0;
  305. }
  306. this->PackageMakerVersion = atof(rexVersion.match(1).c_str());
  307. if ( this->PackageMakerVersion < 1.0 )
  308. {
  309. cmCPackLogger(cmCPackLog::LOG_ERROR, "Require PackageMaker 1.0 or higher"
  310. << std::endl);
  311. return 0;
  312. }
  313. cmCPackLogger(cmCPackLog::LOG_DEBUG, "PackageMaker version is: "
  314. << this->PackageMakerVersion << std::endl);
  315. pkgPath += "/MacOS";
  316. path.push_back(pkgPath);
  317. pkgPath = cmSystemTools::FindProgram("PackageMaker", path, false);
  318. if ( pkgPath.empty() )
  319. {
  320. cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find PackageMaker compiler"
  321. << std::endl);
  322. return 0;
  323. }
  324. this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM", pkgPath.c_str());
  325. pkgPath = cmSystemTools::FindProgram("hdiutil", path, false);
  326. if ( pkgPath.empty() )
  327. {
  328. cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find hdiutil compiler"
  329. << std::endl);
  330. return 0;
  331. }
  332. this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM_DISK_IMAGE",
  333. pkgPath.c_str());
  334. return this->Superclass::InitializeInternal();
  335. }
  336. //----------------------------------------------------------------------
  337. bool cmCPackPackageMakerGenerator::CopyCreateResourceFile(const char* name,
  338. const char* dirName)
  339. {
  340. std::string uname = cmSystemTools::UpperCase(name);
  341. std::string cpackVar = "CPACK_RESOURCE_FILE_" + uname;
  342. const char* inFileName = this->GetOption(cpackVar.c_str());
  343. if ( !inFileName )
  344. {
  345. cmCPackLogger(cmCPackLog::LOG_ERROR, "CPack option: " << cpackVar.c_str()
  346. << " not specified. It should point to "
  347. << (name ? name : "(NULL)")
  348. << ".rtf, " << name
  349. << ".html, or " << name << ".txt file" << std::endl);
  350. return false;
  351. }
  352. if ( !cmSystemTools::FileExists(inFileName) )
  353. {
  354. cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find "
  355. << (name ? name : "(NULL)")
  356. << " resource file: " << inFileName << std::endl);
  357. return false;
  358. }
  359. std::string ext = cmSystemTools::GetFilenameLastExtension(inFileName);
  360. if ( ext != ".rtfd" && ext != ".rtf" && ext != ".html" && ext != ".txt" )
  361. {
  362. cmCPackLogger(cmCPackLog::LOG_ERROR, "Bad file extension specified: "
  363. << ext << ". Currently only .rtfd, .rtf, .html, and .txt files allowed."
  364. << std::endl);
  365. return false;
  366. }
  367. std::string destFileName = dirName;
  368. destFileName += '/';
  369. destFileName += name + ext;
  370. // Set this so that distribution.dist gets the right name (without
  371. // the path).
  372. this->SetOption(("CPACK_RESOURCE_FILE_" + uname + "_NOPATH").c_str(),
  373. (name + ext).c_str());
  374. cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: "
  375. << (inFileName ? inFileName : "(NULL)")
  376. << " to " << destFileName.c_str() << std::endl);
  377. this->ConfigureFile(inFileName, destFileName.c_str());
  378. return true;
  379. }
  380. bool cmCPackPackageMakerGenerator::CopyResourcePlistFile(const char* name,
  381. const char* outName)
  382. {
  383. if (!outName)
  384. {
  385. outName = name;
  386. }
  387. std::string inFName = "CPack.";
  388. inFName += name;
  389. inFName += ".in";
  390. std::string inFileName = this->FindTemplate(inFName.c_str());
  391. if ( inFileName.empty() )
  392. {
  393. cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find input file: "
  394. << inFName << std::endl);
  395. return false;
  396. }
  397. std::string destFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
  398. destFileName += "/";
  399. destFileName += outName;
  400. cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: "
  401. << inFileName.c_str() << " to " << destFileName.c_str() << std::endl);
  402. this->ConfigureFile(inFileName.c_str(), destFileName.c_str());
  403. return true;
  404. }
  405. //----------------------------------------------------------------------
  406. bool cmCPackPackageMakerGenerator::RunPackageMaker(const char *command,
  407. const char *packageFile)
  408. {
  409. std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
  410. tmpFile += "/PackageMakerOutput.log";
  411. cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << command << std::endl);
  412. std::string output;
  413. int retVal = 1;
  414. bool res = cmSystemTools::RunSingleCommand(command, &output, &retVal, 0,
  415. this->GeneratorVerbose, 0);
  416. cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Done running package maker"
  417. << std::endl);
  418. if ( !res || retVal )
  419. {
  420. cmGeneratedFileStream ofs(tmpFile.c_str());
  421. ofs << "# Run command: " << command << std::endl
  422. << "# Output:" << std::endl
  423. << output.c_str() << std::endl;
  424. cmCPackLogger(cmCPackLog::LOG_ERROR,
  425. "Problem running PackageMaker command: " << command
  426. << std::endl << "Please check " << tmpFile.c_str() << " for errors"
  427. << std::endl);
  428. return false;
  429. }
  430. // sometimes the command finishes but the directory is not yet
  431. // created, so try 10 times to see if it shows up
  432. int tries = 10;
  433. while(tries > 0 &&
  434. !cmSystemTools::FileExists(packageFile))
  435. {
  436. cmSystemTools::Delay(500);
  437. tries--;
  438. }
  439. if(!cmSystemTools::FileExists(packageFile))
  440. {
  441. cmCPackLogger(
  442. cmCPackLog::LOG_ERROR,
  443. "Problem running PackageMaker command: " << command
  444. << std::endl << "Package not created: " << packageFile
  445. << std::endl);
  446. return false;
  447. }
  448. return true;
  449. }
  450. //----------------------------------------------------------------------
  451. std::string
  452. cmCPackPackageMakerGenerator::GetPackageName(const cmCPackComponent& component)
  453. {
  454. std::string packagesDir = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
  455. packagesDir += ".dummy";
  456. cmOStringStream out;
  457. out << cmSystemTools::GetFilenameWithoutLastExtension(packagesDir)
  458. << "-" << component.Name << ".pkg";
  459. return out.str();
  460. }
  461. //----------------------------------------------------------------------
  462. bool
  463. cmCPackPackageMakerGenerator::
  464. GenerateComponentPackage(const char *packageFile,
  465. const char *packageDir,
  466. const cmCPackComponent& component)
  467. {
  468. cmCPackLogger(cmCPackLog::LOG_OUTPUT,
  469. "- Building component package: " << packageFile << std::endl);
  470. // Create the description file for this component.
  471. std::string descriptionFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
  472. descriptionFile += '/' + component.Name + "-Description.plist";
  473. std::ofstream out(descriptionFile.c_str());
  474. out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl
  475. << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\""
  476. << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">" << std::endl
  477. << "<plist version=\"1.4\">" << std::endl
  478. << "<dict>" << std::endl
  479. << " <key>IFPkgDescriptionTitle</key>" << std::endl
  480. << " <string>" << component.DisplayName << "</string>" << std::endl
  481. << " <key>IFPkgDescriptionVersion</key>" << std::endl
  482. << " <string>" << this->GetOption("CPACK_PACKAGE_VERSION")
  483. << "</string>" << std::endl
  484. << " <key>IFPkgDescriptionDescription</key>" << std::endl
  485. << " <string>" + this->EscapeForXML(component.Description)
  486. << "</string>" << std::endl
  487. << "</dict>" << std::endl
  488. << "</plist>" << std::endl;
  489. out.close();
  490. // Create the Info.plist file for this component
  491. std::string moduleVersionSuffix = ".";
  492. moduleVersionSuffix += component.Name;
  493. this->SetOption("CPACK_MODULE_VERSION_SUFFIX", moduleVersionSuffix.c_str());
  494. std::string infoFileName = component.Name;
  495. infoFileName += "-Info.plist";
  496. if (!this->CopyResourcePlistFile("Info.plist", infoFileName.c_str()))
  497. {
  498. return false;
  499. }
  500. // Run PackageMaker
  501. cmOStringStream pkgCmd;
  502. pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
  503. << "\" -build -p \"" << packageFile << "\""
  504. << " -f \"" << packageDir << "\""
  505. << " -i \"" << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
  506. << "/" << infoFileName << "\""
  507. << " -d \"" << descriptionFile << "\"";
  508. return RunPackageMaker(pkgCmd.str().c_str(), packageFile);
  509. }
  510. //----------------------------------------------------------------------
  511. void
  512. cmCPackPackageMakerGenerator::
  513. WriteDistributionFile(const char* metapackageFile)
  514. {
  515. std::string distributionTemplate
  516. = this->FindTemplate("CPack.distribution.dist.in");
  517. if ( distributionTemplate.empty() )
  518. {
  519. cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find input file: "
  520. << distributionTemplate << std::endl);
  521. return;
  522. }
  523. std::string distributionFile = metapackageFile;
  524. distributionFile += "/Contents/distribution.dist";
  525. // Create the choice outline, which provides a tree-based view of
  526. // the components in their groups.
  527. cmOStringStream choiceOut;
  528. choiceOut << "<choices-outline>" << std::endl;
  529. // Emit the outline for the groups
  530. std::map<std::string, cmCPackComponentGroup>::iterator groupIt;
  531. for (groupIt = this->ComponentGroups.begin();
  532. groupIt != this->ComponentGroups.end();
  533. ++groupIt)
  534. {
  535. CreateChoiceOutline(groupIt->second, choiceOut);
  536. }
  537. // Emit the outline for the non-grouped components
  538. std::map<std::string, cmCPackComponent>::iterator compIt;
  539. for (compIt = this->Components.begin(); compIt != this->Components.end();
  540. ++compIt)
  541. {
  542. if (!compIt->second.Group)
  543. {
  544. choiceOut << "<line choice=\"" << compIt->first << "Choice\"></line>"
  545. << std::endl;
  546. }
  547. }
  548. choiceOut << "</choices-outline>" << std::endl;
  549. // Create the actual choices
  550. for (groupIt = this->ComponentGroups.begin();
  551. groupIt != this->ComponentGroups.end();
  552. ++groupIt)
  553. {
  554. CreateChoice(groupIt->second, choiceOut);
  555. }
  556. for (compIt = this->Components.begin(); compIt != this->Components.end();
  557. ++compIt)
  558. {
  559. CreateChoice(compIt->second, choiceOut);
  560. }
  561. this->SetOption("CPACK_PACKAGEMAKER_CHOICES", choiceOut.str().c_str());
  562. // Create the distribution.dist file in the metapackage to turn it
  563. // into a distribution package.
  564. this->ConfigureFile(distributionTemplate.c_str(),
  565. distributionFile.c_str());
  566. }
  567. //----------------------------------------------------------------------
  568. void
  569. cmCPackPackageMakerGenerator::
  570. CreateChoiceOutline(const cmCPackComponentGroup& group, cmOStringStream& out)
  571. {
  572. out << "<line choice=\"" << group.Name << "Choice\">" << std::endl;
  573. std::vector<cmCPackComponent*>::const_iterator compIt;
  574. for (compIt = group.Components.begin(); compIt != group.Components.end();
  575. ++compIt)
  576. {
  577. out << " <line choice=\"" << (*compIt)->Name << "Choice\"></line>"
  578. << std::endl;
  579. }
  580. out << "</line>" << std::endl;
  581. }
  582. //----------------------------------------------------------------------
  583. void
  584. cmCPackPackageMakerGenerator::CreateChoice(const cmCPackComponentGroup& group,
  585. cmOStringStream& out)
  586. {
  587. out << "<choice id=\"" << group.Name << "Choice\" "
  588. << "title=\"" << group.DisplayName << "\" "
  589. << "start_selected=\"true\" "
  590. << "start_enabled=\"true\" "
  591. << "start_visible=\"true\" ";
  592. if (!group.Description.empty())
  593. {
  594. out << "description=\"" << EscapeForXML(group.Description)
  595. << "\"";
  596. }
  597. out << "></choice>" << std::endl;
  598. }
  599. //----------------------------------------------------------------------
  600. void
  601. cmCPackPackageMakerGenerator::CreateChoice(const cmCPackComponent& component,
  602. cmOStringStream& out)
  603. {
  604. std::string packageId = "com.";
  605. packageId += this->GetOption("CPACK_PACKAGE_VENDOR");
  606. packageId += '.';
  607. packageId += this->GetOption("CPACK_PACKAGE_NAME");
  608. packageId += '.';
  609. packageId += this->GetOption("CPACK_PACKAGE_VERSION");
  610. packageId += '.';
  611. packageId += component.Name;
  612. out << "<choice id=\"" << component.Name << "Choice\" "
  613. << "title=\"" << component.DisplayName << "\" "
  614. << "start_selected=\""
  615. << (component.IsDisabledByDefault && !component.IsRequired? "false" : "true")
  616. << "\" "
  617. << "start_enabled=\""
  618. << (component.IsRequired? "false" : "true")
  619. << "\" "
  620. << "start_visible=\"" << (component.IsHidden? "false" : "true") << "\" ";
  621. if (!component.Description.empty())
  622. {
  623. out << "description=\"" << EscapeForXML(component.Description)
  624. << "\" ";
  625. }
  626. if (!component.Dependencies.empty() || !component.ReverseDependencies.empty())
  627. {
  628. // The "selected" expression is evaluated each time any choice is
  629. // selected, for all choices *except* the one that the user
  630. // selected. A component is marked selected if it has been
  631. // selected (my.choice.selected in Javascript) and all of the
  632. // components it depends on have been selected (transitively) or
  633. // if any of the components that depend on it have been selected
  634. // (transitively). Assume that we have components A, B, C, D, and
  635. // E, where each component depends on the previous component (B
  636. // depends on A, C depends on B, D depends on C, and E depends on
  637. // D). The expression we build for the component C will be
  638. // my.choice.selected && B && A || D || E
  639. // This way, selecting C will automatically select everything it depends
  640. // on (B and A), while selecting something that depends on C--either D
  641. // or E--will automatically cause C to get selected.
  642. out << "selected=\"my.choice.selected";
  643. AddDependencyAttributes(component, out);
  644. AddReverseDependencyAttributes(component, out);
  645. out << "\"";
  646. }
  647. out << ">" << std::endl;
  648. out << " <pkg-ref id=\"" << packageId << "\"></pkg-ref>" << std::endl;
  649. out << "</choice>" << std::endl;
  650. // Create a description of the package associated with this
  651. // component.
  652. std::string relativePackageLocation = "Contents/Packages/";
  653. relativePackageLocation += GetPackageName(component);
  654. // Determine the installed size of the package. To do so, we dig
  655. // into the Info.plist file from the generated package to retrieve
  656. // this size.
  657. int installedSize = 0;
  658. std::string infoPlistFile = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
  659. infoPlistFile += ".mpkg/";
  660. infoPlistFile += relativePackageLocation;
  661. infoPlistFile += "/Contents/Info.plist";
  662. bool foundFlagInstalledSize = false;
  663. std::string line;
  664. std::ifstream ifs(infoPlistFile.c_str());
  665. while ( cmSystemTools::GetLineFromStream(ifs, line) )
  666. {
  667. if (foundFlagInstalledSize)
  668. {
  669. std::string::size_type pos = line.find("<integer>");
  670. if (pos == std::string::npos)
  671. {
  672. cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot parse package size in "
  673. << infoPlistFile << std::endl
  674. << "String is \"" << line << "\"" << std::endl);
  675. }
  676. else
  677. {
  678. line.erase(0, pos + 9);
  679. pos = line.find("</integer>");
  680. if (pos == std::string::npos)
  681. {
  682. cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot parse package size in "
  683. << infoPlistFile << std::endl);
  684. }
  685. else
  686. {
  687. line.erase(pos, std::string::npos);
  688. installedSize = atoi(line.c_str());
  689. }
  690. }
  691. foundFlagInstalledSize = false;
  692. }
  693. else
  694. {
  695. foundFlagInstalledSize
  696. = line.find("IFPkgFlagInstalledSize") != std::string::npos;
  697. }
  698. }
  699. out << "<pkg-ref id=\"" << packageId << "\" "
  700. << "version=\"" << this->GetOption("CPACK_PACKAGE_VERSION") << "\" "
  701. << "installKBytes=\"" << installedSize << "\" "
  702. << "auth=\"Admin\" onConclusion=\"None\">"
  703. << "file:./" << relativePackageLocation << "</pkg-ref>" << std::endl;
  704. }
  705. //----------------------------------------------------------------------
  706. void
  707. cmCPackPackageMakerGenerator::
  708. AddDependencyAttributes(const cmCPackComponent& component, cmOStringStream& out)
  709. {
  710. std::vector<cmCPackComponent *>::const_iterator dependIt;
  711. for (dependIt = component.Dependencies.begin();
  712. dependIt != component.Dependencies.end();
  713. ++dependIt)
  714. {
  715. out << " &amp;&amp; choices['" << (*dependIt)->Name << "Choice'].selected";
  716. AddDependencyAttributes(**dependIt, out);
  717. }
  718. }
  719. //----------------------------------------------------------------------
  720. void
  721. cmCPackPackageMakerGenerator::
  722. AddReverseDependencyAttributes(const cmCPackComponent& component,
  723. cmOStringStream& out)
  724. {
  725. std::vector<cmCPackComponent *>::const_iterator dependIt;
  726. for (dependIt = component.ReverseDependencies.begin();
  727. dependIt != component.ReverseDependencies.end();
  728. ++dependIt)
  729. {
  730. out << " || choices['" << (*dependIt)->Name << "Choice'].selected";
  731. AddReverseDependencyAttributes(**dependIt, out);
  732. }
  733. }
  734. //----------------------------------------------------------------------
  735. std::string cmCPackPackageMakerGenerator::EscapeForXML(std::string str)
  736. {
  737. cmSystemTools::ReplaceString(str, "&", "&amp;");
  738. cmSystemTools::ReplaceString(str, "<", "&lt;");
  739. cmSystemTools::ReplaceString(str, ">", "&gt;");
  740. cmSystemTools::ReplaceString(str, "\"", "&quot;");
  741. return str;
  742. }