cmCPackPackageMakerGenerator.cxx 33 KB

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