cmCPackPackageMakerGenerator.cxx 32 KB

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