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 "cmSystemTools.h"
  14. #include "cmMakefile.h"
  15. #include "cmGeneratedFileStream.h"
  16. #include "cmCPackComponentGroup.h"
  17. #include "cmCPackLog.h"
  18. #include <cmsys/SystemTools.hxx>
  19. #include <cmsys/Glob.hxx>
  20. #include <cmsys/FStream.hxx>
  21. #include <assert.h>
  22. static inline
  23. unsigned int getVersion(unsigned int major, unsigned int minor)
  24. {
  25. assert(major < 256 && minor < 256);
  26. return ((major & 0xFF) << 16 | minor);
  27. }
  28. //----------------------------------------------------------------------
  29. cmCPackPackageMakerGenerator::cmCPackPackageMakerGenerator()
  30. {
  31. this->PackageMakerVersion = 0.0;
  32. this->PackageCompatibilityVersion = getVersion(10, 4);
  33. }
  34. //----------------------------------------------------------------------
  35. cmCPackPackageMakerGenerator::~cmCPackPackageMakerGenerator()
  36. {
  37. }
  38. //----------------------------------------------------------------------
  39. bool cmCPackPackageMakerGenerator::SupportsComponentInstallation() const
  40. {
  41. return this->PackageCompatibilityVersion >= getVersion(10, 4);
  42. }
  43. //----------------------------------------------------------------------
  44. int cmCPackPackageMakerGenerator::CopyInstallScript(const std::string& resdir,
  45. const std::string& script,
  46. const std::string& name)
  47. {
  48. std::string dst = resdir;
  49. dst += "/";
  50. dst += name;
  51. cmSystemTools::CopyFileAlways(script.c_str(), dst.c_str());
  52. cmSystemTools::SetPermissions(dst.c_str(),0777);
  53. cmCPackLogger(cmCPackLog::LOG_VERBOSE,
  54. "copy script : " << script << "\ninto " << dst.c_str() <<
  55. std::endl);
  56. return 1;
  57. }
  58. //----------------------------------------------------------------------
  59. int cmCPackPackageMakerGenerator::PackageFiles()
  60. {
  61. // TODO: Use toplevel
  62. // It is used! Is this an obsolete comment?
  63. std::string resDir; // Where this package's resources will go.
  64. std::string packageDirFileName
  65. = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
  66. if (this->Components.empty())
  67. {
  68. packageDirFileName += ".pkg";
  69. resDir = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
  70. resDir += "/Resources";
  71. }
  72. else
  73. {
  74. packageDirFileName += ".mpkg";
  75. if ( !cmsys::SystemTools::MakeDirectory(packageDirFileName.c_str()))
  76. {
  77. cmCPackLogger(cmCPackLog::LOG_ERROR,
  78. "unable to create package directory "
  79. << packageDirFileName << std::endl);
  80. return 0;
  81. }
  82. resDir = packageDirFileName;
  83. resDir += "/Contents";
  84. if ( !cmsys::SystemTools::MakeDirectory(resDir.c_str()))
  85. {
  86. cmCPackLogger(cmCPackLog::LOG_ERROR,
  87. "unable to create package subdirectory " << resDir
  88. << std::endl);
  89. return 0;
  90. }
  91. resDir += "/Resources";
  92. if ( !cmsys::SystemTools::MakeDirectory(resDir.c_str()))
  93. {
  94. cmCPackLogger(cmCPackLog::LOG_ERROR,
  95. "unable to create package subdirectory " << resDir
  96. << std::endl);
  97. return 0;
  98. }
  99. resDir += "/en.lproj";
  100. }
  101. const char* preflight = this->GetOption("CPACK_PREFLIGHT_SCRIPT");
  102. const char* postflight = this->GetOption("CPACK_POSTFLIGHT_SCRIPT");
  103. const char* postupgrade = this->GetOption("CPACK_POSTUPGRADE_SCRIPT");
  104. if(this->Components.empty())
  105. {
  106. // Create directory structure
  107. std::string preflightDirName = resDir + "/PreFlight";
  108. std::string postflightDirName = resDir + "/PostFlight";
  109. // if preflight or postflight scripts not there create directories
  110. // of the same name, I think this makes it work
  111. if(!preflight)
  112. {
  113. if ( !cmsys::SystemTools::MakeDirectory(preflightDirName.c_str()))
  114. {
  115. cmCPackLogger(cmCPackLog::LOG_ERROR,
  116. "Problem creating installer directory: "
  117. << preflightDirName.c_str() << std::endl);
  118. return 0;
  119. }
  120. }
  121. if(!postflight)
  122. {
  123. if ( !cmsys::SystemTools::MakeDirectory(postflightDirName.c_str()))
  124. {
  125. cmCPackLogger(cmCPackLog::LOG_ERROR,
  126. "Problem creating installer directory: "
  127. << postflightDirName.c_str() << std::endl);
  128. return 0;
  129. }
  130. }
  131. // if preflight, postflight, or postupgrade are set
  132. // then copy them into the resource directory and make
  133. // them executable
  134. if(preflight)
  135. {
  136. this->CopyInstallScript(resDir.c_str(),
  137. preflight,
  138. "preflight");
  139. }
  140. if(postflight)
  141. {
  142. this->CopyInstallScript(resDir.c_str(),
  143. postflight,
  144. "postflight");
  145. }
  146. if(postupgrade)
  147. {
  148. this->CopyInstallScript(resDir.c_str(),
  149. postupgrade,
  150. "postupgrade");
  151. }
  152. }
  153. else if(postflight)
  154. {
  155. // create a postflight component to house the script
  156. this->PostFlightComponent.Name = "PostFlight";
  157. this->PostFlightComponent.DisplayName = "PostFlight";
  158. this->PostFlightComponent.Description = "PostFlight";
  159. this->PostFlightComponent.IsHidden = true;
  160. // empty directory for pkg contents
  161. std::string packageDir = toplevel + "/" + PostFlightComponent.Name;
  162. if (!cmsys::SystemTools::MakeDirectory(packageDir.c_str()))
  163. {
  164. cmCPackLogger(cmCPackLog::LOG_ERROR,
  165. "Problem creating component packages directory: "
  166. << packageDir.c_str() << std::endl);
  167. return 0;
  168. }
  169. // create package
  170. std::string packageFileDir = packageDirFileName + "/Contents/Packages/";
  171. if (!cmsys::SystemTools::MakeDirectory(packageFileDir.c_str()))
  172. {
  173. cmCPackLogger(cmCPackLog::LOG_ERROR,
  174. "Problem creating component PostFlight Packages directory: "
  175. << packageFileDir.c_str() << std::endl);
  176. return 0;
  177. }
  178. std::string packageFile = packageFileDir +
  179. this->GetPackageName(PostFlightComponent);
  180. if (!this->GenerateComponentPackage(packageFile.c_str(),
  181. packageDir.c_str(),
  182. PostFlightComponent))
  183. {
  184. return 0;
  185. }
  186. // copy postflight script into resource directory of .pkg
  187. std::string resourceDir = packageFile + "/Contents/Resources";
  188. this->CopyInstallScript(resourceDir.c_str(),
  189. postflight,
  190. "postflight");
  191. }
  192. if (!this->Components.empty())
  193. {
  194. // Create the directory where component packages will be built.
  195. std::string basePackageDir = packageDirFileName;
  196. basePackageDir += "/Contents/Packages";
  197. if (!cmsys::SystemTools::MakeDirectory(basePackageDir.c_str()))
  198. {
  199. cmCPackLogger(cmCPackLog::LOG_ERROR,
  200. "Problem creating component packages directory: "
  201. << basePackageDir.c_str() << std::endl);
  202. return 0;
  203. }
  204. // Create the directory where downloaded component packages will
  205. // be placed.
  206. const char* userUploadDirectory =
  207. this->GetOption("CPACK_UPLOAD_DIRECTORY");
  208. std::string uploadDirectory;
  209. if (userUploadDirectory && *userUploadDirectory)
  210. {
  211. uploadDirectory = userUploadDirectory;
  212. }
  213. else
  214. {
  215. uploadDirectory= this->GetOption("CPACK_PACKAGE_DIRECTORY");
  216. uploadDirectory += "/CPackUploads";
  217. }
  218. // Create packages for each component
  219. bool warnedAboutDownloadCompatibility = false;
  220. std::map<std::string, cmCPackComponent>::iterator compIt;
  221. for (compIt = this->Components.begin(); compIt != this->Components.end();
  222. ++compIt)
  223. {
  224. std::string packageFile;
  225. if (compIt->second.IsDownloaded)
  226. {
  227. if (this->PackageCompatibilityVersion >= getVersion(10, 5) &&
  228. this->PackageMakerVersion >= 3.0)
  229. {
  230. // Build this package within the upload directory.
  231. packageFile = uploadDirectory;
  232. if(!cmSystemTools::FileExists(uploadDirectory.c_str()))
  233. {
  234. if (!cmSystemTools::MakeDirectory(uploadDirectory.c_str()))
  235. {
  236. cmCPackLogger(cmCPackLog::LOG_ERROR,
  237. "Unable to create package upload directory "
  238. << uploadDirectory << std::endl);
  239. return 0;
  240. }
  241. }
  242. }
  243. else if (!warnedAboutDownloadCompatibility)
  244. {
  245. if (this->PackageCompatibilityVersion < getVersion(10, 5))
  246. {
  247. cmCPackLogger(
  248. cmCPackLog::LOG_WARNING,
  249. "CPack warning: please set CPACK_OSX_PACKAGE_VERSION to 10.5 "
  250. "or greater enable downloaded packages. CPack will build a "
  251. "non-downloaded package."
  252. << std::endl);
  253. }
  254. if (this->PackageMakerVersion < 3)
  255. {
  256. cmCPackLogger(cmCPackLog::LOG_WARNING,
  257. "CPack warning: unable to build downloaded "
  258. "packages with PackageMaker versions prior "
  259. "to 3.0. CPack will build a non-downloaded package."
  260. << std::endl);
  261. }
  262. warnedAboutDownloadCompatibility = true;
  263. }
  264. }
  265. if (packageFile.empty())
  266. {
  267. // Build this package within the overall distribution
  268. // metapackage.
  269. packageFile = basePackageDir;
  270. // We're not downloading this component, even if the user
  271. // requested it.
  272. compIt->second.IsDownloaded = false;
  273. }
  274. packageFile += '/';
  275. packageFile += GetPackageName(compIt->second);
  276. std::string packageDir = toplevel;
  277. packageDir += '/';
  278. packageDir += compIt->first;
  279. if (!this->GenerateComponentPackage(packageFile.c_str(),
  280. packageDir.c_str(),
  281. compIt->second))
  282. {
  283. return 0;
  284. }
  285. }
  286. }
  287. this->SetOption("CPACK_MODULE_VERSION_SUFFIX", "");
  288. // Copy or create all of the resource files we need.
  289. if ( !this->CopyCreateResourceFile("License", resDir.c_str())
  290. || !this->CopyCreateResourceFile("ReadMe", resDir.c_str())
  291. || !this->CopyCreateResourceFile("Welcome", resDir.c_str())
  292. || !this->CopyResourcePlistFile("Info.plist")
  293. || !this->CopyResourcePlistFile("Description.plist") )
  294. {
  295. cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem copying the resource files"
  296. << std::endl);
  297. return 0;
  298. }
  299. if (this->Components.empty())
  300. {
  301. // Use PackageMaker to build the package.
  302. std::ostringstream pkgCmd;
  303. pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
  304. << "\" -build -p \"" << packageDirFileName << "\"";
  305. if (this->Components.empty())
  306. {
  307. pkgCmd << " -f \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY");
  308. }
  309. else
  310. {
  311. pkgCmd << " -mi \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY")
  312. << "/packages/";
  313. }
  314. pkgCmd << "\" -r \"" << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
  315. << "/Resources\" -i \""
  316. << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
  317. << "/Info.plist\" -d \""
  318. << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
  319. << "/Description.plist\"";
  320. if ( this->PackageMakerVersion > 2.0 )
  321. {
  322. pkgCmd << " -v";
  323. }
  324. if (!RunPackageMaker(pkgCmd.str().c_str(), packageDirFileName.c_str()))
  325. return 0;
  326. }
  327. else
  328. {
  329. // We have built the package in place. Generate the
  330. // distribution.dist file to describe it for the installer.
  331. WriteDistributionFile(packageDirFileName.c_str());
  332. }
  333. std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
  334. tmpFile += "/hdiutilOutput.log";
  335. std::ostringstream dmgCmd;
  336. dmgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM_DISK_IMAGE")
  337. << "\" create -ov -format UDZO -srcfolder \"" << packageDirFileName
  338. << "\" \"" << packageFileNames[0] << "\"";
  339. std::string output;
  340. int retVal = 1;
  341. int numTries = 10;
  342. bool res = false;
  343. while(numTries > 0)
  344. {
  345. res = cmSystemTools::RunSingleCommand(
  346. dmgCmd.str().c_str(), &output, &output,
  347. &retVal, 0, this->GeneratorVerbose, 0);
  348. if ( res && !retVal )
  349. {
  350. numTries = -1;
  351. break;
  352. }
  353. cmSystemTools::Delay(500);
  354. numTries--;
  355. }
  356. if ( !res || retVal )
  357. {
  358. cmGeneratedFileStream ofs(tmpFile.c_str());
  359. ofs << "# Run command: " << dmgCmd.str().c_str() << std::endl
  360. << "# Output:" << std::endl
  361. << output.c_str() << std::endl;
  362. cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running hdiutil command: "
  363. << dmgCmd.str().c_str() << std::endl
  364. << "Please check " << tmpFile.c_str() << " for errors" << std::endl);
  365. return 0;
  366. }
  367. return 1;
  368. }
  369. //----------------------------------------------------------------------
  370. int cmCPackPackageMakerGenerator::InitializeInternal()
  371. {
  372. cmCPackLogger(cmCPackLog::LOG_DEBUG,
  373. "cmCPackPackageMakerGenerator::Initialize()" << std::endl);
  374. this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/usr");
  375. // Starting with Xcode 4.3, PackageMaker is a separate app, and you
  376. // can put it anywhere you want. So... use a variable for its location.
  377. // People who put it in unexpected places can use the variable to tell
  378. // us where it is.
  379. //
  380. // Use the following locations, in "most recent installation" order,
  381. // to search for the PackageMaker app. Assume people who copy it into
  382. // the new Xcode 4.3 app in "/Applications" will copy it into the nested
  383. // Applications folder inside the Xcode bundle itself. Or directly in
  384. // the "/Applications" directory.
  385. //
  386. // If found, save result in the CPACK_INSTALLER_PROGRAM variable.
  387. std::vector<std::string> paths;
  388. paths.push_back(
  389. "/Applications/Xcode.app/Contents/Applications"
  390. "/PackageMaker.app/Contents/MacOS");
  391. paths.push_back(
  392. "/Applications/Utilities"
  393. "/PackageMaker.app/Contents/MacOS");
  394. paths.push_back(
  395. "/Applications"
  396. "/PackageMaker.app/Contents/MacOS");
  397. paths.push_back(
  398. "/Developer/Applications/Utilities"
  399. "/PackageMaker.app/Contents/MacOS");
  400. paths.push_back(
  401. "/Developer/Applications"
  402. "/PackageMaker.app/Contents/MacOS");
  403. std::string pkgPath;
  404. const char *inst_program = this->GetOption("CPACK_INSTALLER_PROGRAM");
  405. if (inst_program && *inst_program)
  406. {
  407. pkgPath = inst_program;
  408. }
  409. else
  410. {
  411. pkgPath = cmSystemTools::FindProgram("PackageMaker", paths, false);
  412. if ( pkgPath.empty() )
  413. {
  414. cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find PackageMaker compiler"
  415. << std::endl);
  416. return 0;
  417. }
  418. this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM", pkgPath.c_str());
  419. }
  420. // Get path to the real PackageMaker, not a symlink:
  421. pkgPath = cmSystemTools::GetRealPath(pkgPath.c_str());
  422. // Up from there to find the version.plist file in the "Contents" dir:
  423. std::string contents_dir;
  424. contents_dir = cmSystemTools::GetFilenamePath(pkgPath);
  425. contents_dir = cmSystemTools::GetFilenamePath(contents_dir);
  426. std::string versionFile = contents_dir + "/version.plist";
  427. if ( !cmSystemTools::FileExists(versionFile.c_str()) )
  428. {
  429. cmCPackLogger(cmCPackLog::LOG_ERROR,
  430. "Cannot find PackageMaker compiler version file: "
  431. << versionFile.c_str()
  432. << std::endl);
  433. return 0;
  434. }
  435. cmsys::ifstream ifs(versionFile.c_str());
  436. if ( !ifs )
  437. {
  438. cmCPackLogger(cmCPackLog::LOG_ERROR,
  439. "Cannot open PackageMaker compiler version file" << std::endl);
  440. return 0;
  441. }
  442. // Check the PackageMaker version
  443. cmsys::RegularExpression rexKey("<key>CFBundleShortVersionString</key>");
  444. cmsys::RegularExpression rexVersion("<string>([0-9]+.[0-9.]+)</string>");
  445. std::string line;
  446. bool foundKey = false;
  447. while ( cmSystemTools::GetLineFromStream(ifs, line) )
  448. {
  449. if ( rexKey.find(line) )
  450. {
  451. foundKey = true;
  452. break;
  453. }
  454. }
  455. if ( !foundKey )
  456. {
  457. cmCPackLogger(cmCPackLog::LOG_ERROR,
  458. "Cannot find CFBundleShortVersionString in the PackageMaker compiler "
  459. "version file" << std::endl);
  460. return 0;
  461. }
  462. if ( !cmSystemTools::GetLineFromStream(ifs, line) ||
  463. !rexVersion.find(line) )
  464. {
  465. cmCPackLogger(cmCPackLog::LOG_ERROR,
  466. "Problem reading the PackageMaker compiler version file: "
  467. << versionFile.c_str() << std::endl);
  468. return 0;
  469. }
  470. this->PackageMakerVersion = atof(rexVersion.match(1).c_str());
  471. if ( this->PackageMakerVersion < 1.0 )
  472. {
  473. cmCPackLogger(cmCPackLog::LOG_ERROR, "Require PackageMaker 1.0 or higher"
  474. << std::endl);
  475. return 0;
  476. }
  477. cmCPackLogger(cmCPackLog::LOG_DEBUG, "PackageMaker version is: "
  478. << this->PackageMakerVersion << std::endl);
  479. // Determine the package compatibility version. If it wasn't
  480. // specified by the user, we define it based on which features the
  481. // user requested.
  482. const char *packageCompat = this->GetOption("CPACK_OSX_PACKAGE_VERSION");
  483. if (packageCompat && *packageCompat)
  484. {
  485. unsigned int majorVersion = 10;
  486. unsigned int minorVersion = 5;
  487. int res = sscanf(packageCompat, "%u.%u", &majorVersion, &minorVersion);
  488. if (res == 2)
  489. {
  490. this->PackageCompatibilityVersion =
  491. getVersion(majorVersion, minorVersion);
  492. }
  493. }
  494. else if (this->GetOption("CPACK_DOWNLOAD_SITE"))
  495. {
  496. this->SetOption("CPACK_OSX_PACKAGE_VERSION", "10.5");
  497. this->PackageCompatibilityVersion = getVersion(10, 5);
  498. }
  499. else if (this->GetOption("CPACK_COMPONENTS_ALL"))
  500. {
  501. this->SetOption("CPACK_OSX_PACKAGE_VERSION", "10.4");
  502. this->PackageCompatibilityVersion = getVersion(10, 4);
  503. }
  504. else
  505. {
  506. this->SetOption("CPACK_OSX_PACKAGE_VERSION", "10.3");
  507. this->PackageCompatibilityVersion = getVersion(10, 3);
  508. }
  509. std::vector<std::string> no_paths;
  510. pkgPath = cmSystemTools::FindProgram("hdiutil", no_paths, false);
  511. if ( pkgPath.empty() )
  512. {
  513. cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find hdiutil compiler"
  514. << std::endl);
  515. return 0;
  516. }
  517. this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM_DISK_IMAGE",
  518. pkgPath.c_str());
  519. return this->Superclass::InitializeInternal();
  520. }
  521. //----------------------------------------------------------------------
  522. bool cmCPackPackageMakerGenerator::CopyCreateResourceFile(
  523. const std::string& name,
  524. const std::string& dirName)
  525. {
  526. std::string uname = cmSystemTools::UpperCase(name);
  527. std::string cpackVar = "CPACK_RESOURCE_FILE_" + uname;
  528. const char* inFileName = this->GetOption(cpackVar.c_str());
  529. if ( !inFileName )
  530. {
  531. cmCPackLogger(cmCPackLog::LOG_ERROR, "CPack option: " << cpackVar.c_str()
  532. << " not specified. It should point to "
  533. << (!name.empty() ? name : "<empty>")
  534. << ".rtf, " << name
  535. << ".html, or " << name << ".txt file" << std::endl);
  536. return false;
  537. }
  538. if ( !cmSystemTools::FileExists(inFileName) )
  539. {
  540. cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find "
  541. << (!name.empty() ? name : "<empty>")
  542. << " resource file: " << inFileName << std::endl);
  543. return false;
  544. }
  545. std::string ext = cmSystemTools::GetFilenameLastExtension(inFileName);
  546. if ( ext != ".rtfd" && ext != ".rtf" && ext != ".html" && ext != ".txt" )
  547. {
  548. cmCPackLogger(cmCPackLog::LOG_ERROR, "Bad file extension specified: "
  549. << ext << ". Currently only .rtfd, .rtf, .html, and .txt files allowed."
  550. << std::endl);
  551. return false;
  552. }
  553. std::string destFileName = dirName;
  554. destFileName += '/';
  555. destFileName += name + ext;
  556. // Set this so that distribution.dist gets the right name (without
  557. // the path).
  558. this->SetOption(("CPACK_RESOURCE_FILE_" + uname + "_NOPATH").c_str(),
  559. (name + ext).c_str());
  560. cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: "
  561. << (inFileName ? inFileName : "(NULL)")
  562. << " to " << destFileName.c_str() << std::endl);
  563. this->ConfigureFile(inFileName, destFileName.c_str());
  564. return true;
  565. }
  566. bool cmCPackPackageMakerGenerator::CopyResourcePlistFile(
  567. const std::string& name,
  568. const char* outName)
  569. {
  570. if (!outName)
  571. {
  572. outName = name.c_str();
  573. }
  574. std::string inFName = "CPack.";
  575. inFName += name;
  576. inFName += ".in";
  577. std::string inFileName = this->FindTemplate(inFName.c_str());
  578. if ( inFileName.empty() )
  579. {
  580. cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find input file: "
  581. << inFName << std::endl);
  582. return false;
  583. }
  584. std::string destFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
  585. destFileName += "/";
  586. destFileName += outName;
  587. cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: "
  588. << inFileName.c_str() << " to " << destFileName.c_str() << std::endl);
  589. this->ConfigureFile(inFileName.c_str(), destFileName.c_str());
  590. return true;
  591. }
  592. //----------------------------------------------------------------------
  593. bool cmCPackPackageMakerGenerator::RunPackageMaker(const char *command,
  594. const char *packageFile)
  595. {
  596. std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
  597. tmpFile += "/PackageMakerOutput.log";
  598. cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << command << std::endl);
  599. std::string output;
  600. int retVal = 1;
  601. bool res = cmSystemTools::RunSingleCommand(
  602. command, &output, &output,
  603. &retVal, 0, 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. }