cmCPackPackageMakerGenerator.cxx 36 KB

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