cmCPackPackageMakerGenerator.cxx 32 KB

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