cmCPackPackageMakerGenerator.cxx 35 KB

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