cmCPackPackageMakerGenerator.cxx 35 KB

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