cmCPackPKGGenerator.cxx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  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 "cmCPackPKGGenerator.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. cmCPackPKGGenerator::cmCPackPKGGenerator()
  22. {
  23. this->componentPackageMethod = ONE_PACKAGE;
  24. }
  25. cmCPackPKGGenerator::~cmCPackPKGGenerator()
  26. {
  27. }
  28. bool cmCPackPKGGenerator::SupportsComponentInstallation() const
  29. {
  30. return true;
  31. }
  32. int cmCPackPKGGenerator::InitializeInternal()
  33. {
  34. cmCPackLogger(cmCPackLog::LOG_DEBUG,
  35. "cmCPackPKGGenerator::Initialize()" << std::endl);
  36. return this->Superclass::InitializeInternal();
  37. }
  38. std::string cmCPackPKGGenerator::GetPackageName(
  39. const cmCPackComponent& component)
  40. {
  41. if (component.ArchiveFile.empty()) {
  42. std::string packagesDir = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
  43. packagesDir += ".dummy";
  44. std::ostringstream out;
  45. out << cmSystemTools::GetFilenameWithoutLastExtension(packagesDir)
  46. << "-" << component.Name << ".pkg";
  47. return out.str();
  48. } else {
  49. return component.ArchiveFile + ".pkg";
  50. }
  51. }
  52. void cmCPackPKGGenerator::WriteDistributionFile(
  53. const char* metapackageFile)
  54. {
  55. std::string distributionTemplate =
  56. this->FindTemplate("CPack.distribution.dist.in");
  57. if (distributionTemplate.empty()) {
  58. cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find input file: "
  59. << distributionTemplate << std::endl);
  60. return;
  61. }
  62. std::string distributionFile = metapackageFile;
  63. distributionFile += "/Contents/distribution.dist";
  64. // Create the choice outline, which provides a tree-based view of
  65. // the components in their groups.
  66. std::ostringstream choiceOut;
  67. choiceOut << "<choices-outline>" << std::endl;
  68. // Emit the outline for the groups
  69. std::map<std::string, cmCPackComponentGroup>::iterator groupIt;
  70. for (groupIt = this->ComponentGroups.begin();
  71. groupIt != this->ComponentGroups.end(); ++groupIt) {
  72. if (groupIt->second.ParentGroup == 0) {
  73. CreateChoiceOutline(groupIt->second, choiceOut);
  74. }
  75. }
  76. // Emit the outline for the non-grouped components
  77. std::map<std::string, cmCPackComponent>::iterator compIt;
  78. for (compIt = this->Components.begin(); compIt != this->Components.end();
  79. ++compIt) {
  80. if (!compIt->second.Group) {
  81. choiceOut << "<line choice=\"" << compIt->first << "Choice\"></line>"
  82. << std::endl;
  83. }
  84. }
  85. if (!this->PostFlightComponent.Name.empty()) {
  86. choiceOut << "<line choice=\"" << PostFlightComponent.Name
  87. << "Choice\"></line>" << std::endl;
  88. }
  89. choiceOut << "</choices-outline>" << std::endl;
  90. // Create the actual choices
  91. for (groupIt = this->ComponentGroups.begin();
  92. groupIt != this->ComponentGroups.end(); ++groupIt) {
  93. CreateChoice(groupIt->second, choiceOut);
  94. }
  95. for (compIt = this->Components.begin(); compIt != this->Components.end();
  96. ++compIt) {
  97. CreateChoice(compIt->second, choiceOut);
  98. }
  99. if (!this->PostFlightComponent.Name.empty()) {
  100. CreateChoice(PostFlightComponent, choiceOut);
  101. }
  102. this->SetOption("CPACK_PACKAGEMAKER_CHOICES", choiceOut.str().c_str());
  103. // Create the distribution.dist file in the metapackage to turn it
  104. // into a distribution package.
  105. this->ConfigureFile(distributionTemplate.c_str(), distributionFile.c_str());
  106. }
  107. void cmCPackPKGGenerator::CreateChoiceOutline(
  108. const cmCPackComponentGroup& group, std::ostringstream& out)
  109. {
  110. out << "<line choice=\"" << group.Name << "Choice\">" << std::endl;
  111. std::vector<cmCPackComponentGroup*>::const_iterator groupIt;
  112. for (groupIt = group.Subgroups.begin(); groupIt != group.Subgroups.end();
  113. ++groupIt) {
  114. CreateChoiceOutline(**groupIt, out);
  115. }
  116. std::vector<cmCPackComponent*>::const_iterator compIt;
  117. for (compIt = group.Components.begin(); compIt != group.Components.end();
  118. ++compIt) {
  119. out << " <line choice=\"" << (*compIt)->Name << "Choice\"></line>"
  120. << std::endl;
  121. }
  122. out << "</line>" << std::endl;
  123. }
  124. void cmCPackPKGGenerator::CreateChoice(
  125. const cmCPackComponentGroup& group, std::ostringstream& out)
  126. {
  127. out << "<choice id=\"" << group.Name << "Choice\" "
  128. << "title=\"" << group.DisplayName << "\" "
  129. << "start_selected=\"true\" "
  130. << "start_enabled=\"true\" "
  131. << "start_visible=\"true\" ";
  132. if (!group.Description.empty()) {
  133. out << "description=\"" << EscapeForXML(group.Description) << "\"";
  134. }
  135. out << "></choice>" << std::endl;
  136. }
  137. void cmCPackPKGGenerator::CreateChoice(
  138. const cmCPackComponent& component, std::ostringstream& out)
  139. {
  140. std::string packageId = "com.";
  141. packageId += this->GetOption("CPACK_PACKAGE_VENDOR");
  142. packageId += '.';
  143. packageId += this->GetOption("CPACK_PACKAGE_NAME");
  144. packageId += '.';
  145. packageId += component.Name;
  146. out << "<choice id=\"" << component.Name << "Choice\" "
  147. << "title=\"" << component.DisplayName << "\" "
  148. << "start_selected=\""
  149. << (component.IsDisabledByDefault && !component.IsRequired ? "false"
  150. : "true")
  151. << "\" "
  152. << "start_enabled=\"" << (component.IsRequired ? "false" : "true")
  153. << "\" "
  154. << "start_visible=\"" << (component.IsHidden ? "false" : "true")
  155. << "\" ";
  156. if (!component.Description.empty()) {
  157. out << "description=\"" << EscapeForXML(component.Description) << "\" ";
  158. }
  159. if (!component.Dependencies.empty() ||
  160. !component.ReverseDependencies.empty()) {
  161. // The "selected" expression is evaluated each time any choice is
  162. // selected, for all choices *except* the one that the user
  163. // selected. A component is marked selected if it has been
  164. // selected (my.choice.selected in Javascript) and all of the
  165. // components it depends on have been selected (transitively) or
  166. // if any of the components that depend on it have been selected
  167. // (transitively). Assume that we have components A, B, C, D, and
  168. // E, where each component depends on the previous component (B
  169. // depends on A, C depends on B, D depends on C, and E depends on
  170. // D). The expression we build for the component C will be
  171. // my.choice.selected && B && A || D || E
  172. // This way, selecting C will automatically select everything it depends
  173. // on (B and A), while selecting something that depends on C--either D
  174. // or E--will automatically cause C to get selected.
  175. out << "selected=\"my.choice.selected";
  176. std::set<const cmCPackComponent*> visited;
  177. AddDependencyAttributes(component, visited, out);
  178. visited.clear();
  179. AddReverseDependencyAttributes(component, visited, out);
  180. out << "\"";
  181. }
  182. out << ">" << std::endl;
  183. out << " <pkg-ref id=\"" << packageId << "\"></pkg-ref>" << std::endl;
  184. out << "</choice>" << std::endl;
  185. // Create a description of the package associated with this
  186. // component.
  187. std::string relativePackageLocation = "Contents/Packages/";
  188. relativePackageLocation += this->GetPackageName(component);
  189. // Determine the installed size of the package.
  190. std::string dirName = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
  191. dirName += '/';
  192. dirName += component.Name;
  193. dirName += this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX");
  194. unsigned long installedSize =
  195. component.GetInstalledSizeInKbytes(dirName.c_str());
  196. out << "<pkg-ref id=\"" << packageId << "\" "
  197. << "version=\"" << this->GetOption("CPACK_PACKAGE_VERSION") << "\" "
  198. << "installKBytes=\"" << installedSize << "\" "
  199. << ">";
  200. if (component.IsDownloaded) {
  201. out << this->GetOption("CPACK_DOWNLOAD_SITE")
  202. << this->GetPackageName(component);
  203. } else {
  204. out << "file:./" << relativePackageLocation;
  205. }
  206. out << "</pkg-ref>" << std::endl;
  207. }
  208. void cmCPackPKGGenerator::AddDependencyAttributes(
  209. const cmCPackComponent& component,
  210. std::set<const cmCPackComponent*>& visited, std::ostringstream& out)
  211. {
  212. if (visited.find(&component) != visited.end()) {
  213. return;
  214. }
  215. visited.insert(&component);
  216. std::vector<cmCPackComponent*>::const_iterator dependIt;
  217. for (dependIt = component.Dependencies.begin();
  218. dependIt != component.Dependencies.end(); ++dependIt) {
  219. out << " &amp;&amp; choices['" << (*dependIt)->Name << "Choice'].selected";
  220. AddDependencyAttributes(**dependIt, visited, out);
  221. }
  222. }
  223. void cmCPackPKGGenerator::AddReverseDependencyAttributes(
  224. const cmCPackComponent& component,
  225. std::set<const cmCPackComponent*>& visited, std::ostringstream& out)
  226. {
  227. if (visited.find(&component) != visited.end()) {
  228. return;
  229. }
  230. visited.insert(&component);
  231. std::vector<cmCPackComponent*>::const_iterator dependIt;
  232. for (dependIt = component.ReverseDependencies.begin();
  233. dependIt != component.ReverseDependencies.end(); ++dependIt) {
  234. out << " || choices['" << (*dependIt)->Name << "Choice'].selected";
  235. AddReverseDependencyAttributes(**dependIt, visited, out);
  236. }
  237. }
  238. std::string cmCPackPKGGenerator::EscapeForXML(std::string str)
  239. {
  240. cmSystemTools::ReplaceString(str, "&", "&amp;");
  241. cmSystemTools::ReplaceString(str, "<", "&lt;");
  242. cmSystemTools::ReplaceString(str, ">", "&gt;");
  243. cmSystemTools::ReplaceString(str, "\"", "&quot;");
  244. return str;
  245. }
  246. bool cmCPackPKGGenerator::CopyCreateResourceFile(
  247. const std::string& name, const std::string& dirName)
  248. {
  249. std::string uname = cmSystemTools::UpperCase(name);
  250. std::string cpackVar = "CPACK_RESOURCE_FILE_" + uname;
  251. const char* inFileName = this->GetOption(cpackVar.c_str());
  252. if (!inFileName) {
  253. cmCPackLogger(cmCPackLog::LOG_ERROR, "CPack option: "
  254. << cpackVar.c_str()
  255. << " not specified. It should point to "
  256. << (!name.empty() ? name : "<empty>") << ".rtf, " << name
  257. << ".html, or " << name << ".txt file" << std::endl);
  258. return false;
  259. }
  260. if (!cmSystemTools::FileExists(inFileName)) {
  261. cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find "
  262. << (!name.empty() ? name : "<empty>")
  263. << " resource file: " << inFileName << std::endl);
  264. return false;
  265. }
  266. std::string ext = cmSystemTools::GetFilenameLastExtension(inFileName);
  267. if (ext != ".rtfd" && ext != ".rtf" && ext != ".html" && ext != ".txt") {
  268. cmCPackLogger(
  269. cmCPackLog::LOG_ERROR, "Bad file extension specified: "
  270. << ext
  271. << ". Currently only .rtfd, .rtf, .html, and .txt files allowed."
  272. << std::endl);
  273. return false;
  274. }
  275. std::string destFileName = dirName;
  276. destFileName += '/';
  277. destFileName += name + ext;
  278. // Set this so that distribution.dist gets the right name (without
  279. // the path).
  280. this->SetOption(("CPACK_RESOURCE_FILE_" + uname + "_NOPATH").c_str(),
  281. (name + ext).c_str());
  282. cmCPackLogger(cmCPackLog::LOG_VERBOSE,
  283. "Configure file: " << (inFileName ? inFileName : "(NULL)")
  284. << " to " << destFileName << std::endl);
  285. this->ConfigureFile(inFileName, destFileName.c_str());
  286. return true;
  287. }
  288. bool cmCPackPKGGenerator::CopyResourcePlistFile(
  289. const std::string& name, const char* outName)
  290. {
  291. if (!outName) {
  292. outName = name.c_str();
  293. }
  294. std::string inFName = "CPack.";
  295. inFName += name;
  296. inFName += ".in";
  297. std::string inFileName = this->FindTemplate(inFName.c_str());
  298. if (inFileName.empty()) {
  299. cmCPackLogger(cmCPackLog::LOG_ERROR,
  300. "Cannot find input file: " << inFName << std::endl);
  301. return false;
  302. }
  303. std::string destFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
  304. destFileName += "/";
  305. destFileName += outName;
  306. cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: "
  307. << inFileName << " to " << destFileName << std::endl);
  308. this->ConfigureFile(inFileName.c_str(), destFileName.c_str());
  309. return true;
  310. }
  311. int cmCPackPKGGenerator::CopyInstallScript(const std::string& resdir,
  312. const std::string& script,
  313. const std::string& name)
  314. {
  315. std::string dst = resdir;
  316. dst += "/";
  317. dst += name;
  318. cmSystemTools::CopyFileAlways(script.c_str(), dst.c_str());
  319. cmSystemTools::SetPermissions(dst.c_str(), 0777);
  320. cmCPackLogger(cmCPackLog::LOG_VERBOSE,
  321. "copy script : " << script << "\ninto " << dst << std::endl);
  322. return 1;
  323. }