cmCPackArchiveGenerator.cxx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmCPackArchiveGenerator.h"
  4. #include "cmCPackComponentGroup.h"
  5. #include "cmCPackGenerator.h"
  6. #include "cmCPackLog.h"
  7. #include "cmGeneratedFileStream.h"
  8. #include "cmSystemTools.h"
  9. #include "cmWorkingDirectory.h"
  10. #include <cstring>
  11. #include <map>
  12. #include <ostream>
  13. #include <utility>
  14. #include <vector>
  15. cmCPackArchiveGenerator::cmCPackArchiveGenerator(cmArchiveWrite::Compress t,
  16. std::string const& format)
  17. {
  18. this->Compress = t;
  19. this->ArchiveFormat = format;
  20. }
  21. cmCPackArchiveGenerator::~cmCPackArchiveGenerator()
  22. {
  23. }
  24. std::string cmCPackArchiveGenerator::GetArchiveComponentFileName(
  25. const std::string& component, bool isGroupName)
  26. {
  27. std::string componentUpper(cmSystemTools::UpperCase(component));
  28. std::string packageFileName;
  29. if (this->IsSet("CPACK_ARCHIVE_" + componentUpper + "_FILE_NAME")) {
  30. packageFileName +=
  31. this->GetOption("CPACK_ARCHIVE_" + componentUpper + "_FILE_NAME");
  32. } else if (this->IsSet("CPACK_ARCHIVE_FILE_NAME")) {
  33. packageFileName += GetComponentPackageFileName(
  34. this->GetOption("CPACK_ARCHIVE_FILE_NAME"), component, isGroupName);
  35. } else {
  36. packageFileName += GetComponentPackageFileName(
  37. this->GetOption("CPACK_PACKAGE_FILE_NAME"), component, isGroupName);
  38. }
  39. packageFileName += this->GetOutputExtension();
  40. return packageFileName;
  41. }
  42. int cmCPackArchiveGenerator::InitializeInternal()
  43. {
  44. this->SetOptionIfNotSet("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", "1");
  45. return this->Superclass::InitializeInternal();
  46. }
  47. int cmCPackArchiveGenerator::addOneComponentToArchive(
  48. cmArchiveWrite& archive, cmCPackComponent* component)
  49. {
  50. cmCPackLogger(cmCPackLog::LOG_VERBOSE,
  51. " - packaging component: " << component->Name << std::endl);
  52. // Add the files of this component to the archive
  53. std::string localToplevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY"));
  54. localToplevel += "/" + component->Name;
  55. // Change to local toplevel
  56. cmWorkingDirectory workdir(localToplevel);
  57. if (workdir.Failed()) {
  58. cmCPackLogger(cmCPackLog::LOG_ERROR,
  59. "Failed to change working directory to "
  60. << localToplevel << " : "
  61. << std::strerror(workdir.GetLastResult()) << std::endl);
  62. return 0;
  63. }
  64. std::string filePrefix;
  65. if (this->IsOn("CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY")) {
  66. filePrefix = this->GetOption("CPACK_PACKAGE_FILE_NAME");
  67. filePrefix += "/";
  68. }
  69. const char* installPrefix =
  70. this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX");
  71. if (installPrefix && installPrefix[0] == '/' && installPrefix[1] != 0) {
  72. // add to file prefix and remove the leading '/'
  73. filePrefix += installPrefix + 1;
  74. filePrefix += "/";
  75. }
  76. for (std::string const& file : component->Files) {
  77. std::string rp = filePrefix + file;
  78. cmCPackLogger(cmCPackLog::LOG_DEBUG, "Adding file: " << rp << std::endl);
  79. archive.Add(rp, 0, nullptr, false);
  80. if (!archive) {
  81. cmCPackLogger(cmCPackLog::LOG_ERROR,
  82. "ERROR while packaging files: " << archive.GetError()
  83. << std::endl);
  84. return 0;
  85. }
  86. }
  87. return 1;
  88. }
  89. /*
  90. * The macro will open/create a file 'filename'
  91. * an declare and open the associated
  92. * cmArchiveWrite 'archive' object.
  93. */
  94. #define DECLARE_AND_OPEN_ARCHIVE(filename, archive) \
  95. cmGeneratedFileStream gf; \
  96. gf.Open((filename), false, true); \
  97. if (!GenerateHeader(&gf)) { \
  98. cmCPackLogger(cmCPackLog::LOG_ERROR, \
  99. "Problem to generate Header for archive <" \
  100. << (filename) << ">." << std::endl); \
  101. return 0; \
  102. } \
  103. cmArchiveWrite archive(gf, this->Compress, this->ArchiveFormat); \
  104. do { \
  105. if (!(archive)) { \
  106. cmCPackLogger(cmCPackLog::LOG_ERROR, \
  107. "Problem to create archive <" \
  108. << (filename) << ">, ERROR = " << (archive).GetError() \
  109. << std::endl); \
  110. return 0; \
  111. } \
  112. } while (false)
  113. int cmCPackArchiveGenerator::PackageComponents(bool ignoreGroup)
  114. {
  115. packageFileNames.clear();
  116. // The default behavior is to have one package by component group
  117. // unless CPACK_COMPONENTS_IGNORE_GROUP is specified.
  118. if (!ignoreGroup) {
  119. for (auto const& compG : this->ComponentGroups) {
  120. cmCPackLogger(cmCPackLog::LOG_VERBOSE,
  121. "Packaging component group: " << compG.first << std::endl);
  122. // Begin the archive for this group
  123. std::string packageFileName = std::string(toplevel) + "/" +
  124. this->GetArchiveComponentFileName(compG.first, true);
  125. // open a block in order to automatically close archive
  126. // at the end of the block
  127. {
  128. DECLARE_AND_OPEN_ARCHIVE(packageFileName, archive);
  129. // now iterate over the component of this group
  130. for (cmCPackComponent* comp : (compG.second).Components) {
  131. // Add the files of this component to the archive
  132. addOneComponentToArchive(archive, comp);
  133. }
  134. }
  135. // add the generated package to package file names list
  136. packageFileNames.push_back(std::move(packageFileName));
  137. }
  138. // Handle Orphan components (components not belonging to any groups)
  139. for (auto& comp : this->Components) {
  140. // Does the component belong to a group?
  141. if (comp.second.Group == nullptr) {
  142. cmCPackLogger(
  143. cmCPackLog::LOG_VERBOSE,
  144. "Component <"
  145. << comp.second.Name
  146. << "> does not belong to any group, package it separately."
  147. << std::endl);
  148. std::string localToplevel(
  149. this->GetOption("CPACK_TEMPORARY_DIRECTORY"));
  150. std::string packageFileName = std::string(toplevel);
  151. localToplevel += "/" + comp.first;
  152. packageFileName +=
  153. "/" + this->GetArchiveComponentFileName(comp.first, false);
  154. {
  155. DECLARE_AND_OPEN_ARCHIVE(packageFileName, archive);
  156. // Add the files of this component to the archive
  157. addOneComponentToArchive(archive, &(comp.second));
  158. }
  159. // add the generated package to package file names list
  160. packageFileNames.push_back(std::move(packageFileName));
  161. }
  162. }
  163. }
  164. // CPACK_COMPONENTS_IGNORE_GROUPS is set
  165. // We build 1 package per component
  166. else {
  167. for (auto& comp : this->Components) {
  168. std::string localToplevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY"));
  169. std::string packageFileName = std::string(toplevel);
  170. localToplevel += "/" + comp.first;
  171. packageFileName +=
  172. "/" + this->GetArchiveComponentFileName(comp.first, false);
  173. {
  174. DECLARE_AND_OPEN_ARCHIVE(packageFileName, archive);
  175. // Add the files of this component to the archive
  176. addOneComponentToArchive(archive, &(comp.second));
  177. }
  178. // add the generated package to package file names list
  179. packageFileNames.push_back(std::move(packageFileName));
  180. }
  181. }
  182. return 1;
  183. }
  184. int cmCPackArchiveGenerator::PackageComponentsAllInOne()
  185. {
  186. // reset the package file names
  187. packageFileNames.clear();
  188. packageFileNames.emplace_back(toplevel);
  189. packageFileNames[0] += "/";
  190. if (this->IsSet("CPACK_ARCHIVE_FILE_NAME")) {
  191. packageFileNames[0] += this->GetOption("CPACK_ARCHIVE_FILE_NAME");
  192. } else {
  193. packageFileNames[0] += this->GetOption("CPACK_PACKAGE_FILE_NAME");
  194. }
  195. packageFileNames[0] += this->GetOutputExtension();
  196. cmCPackLogger(cmCPackLog::LOG_VERBOSE,
  197. "Packaging all groups in one package..."
  198. "(CPACK_COMPONENTS_ALL_GROUPS_IN_ONE_PACKAGE is set)"
  199. << std::endl);
  200. DECLARE_AND_OPEN_ARCHIVE(packageFileNames[0], archive);
  201. // The ALL COMPONENTS in ONE package case
  202. for (auto& comp : this->Components) {
  203. // Add the files of this component to the archive
  204. addOneComponentToArchive(archive, &(comp.second));
  205. }
  206. // archive goes out of scope so it will finalized and closed.
  207. return 1;
  208. }
  209. int cmCPackArchiveGenerator::PackageFiles()
  210. {
  211. cmCPackLogger(cmCPackLog::LOG_DEBUG, "Toplevel: " << toplevel << std::endl);
  212. if (WantsComponentInstallation()) {
  213. // CASE 1 : COMPONENT ALL-IN-ONE package
  214. // If ALL COMPONENTS in ONE package has been requested
  215. // then the package file is unique and should be open here.
  216. if (componentPackageMethod == ONE_PACKAGE) {
  217. return PackageComponentsAllInOne();
  218. }
  219. // CASE 2 : COMPONENT CLASSICAL package(s) (i.e. not all-in-one)
  220. // There will be 1 package for each component group
  221. // however one may require to ignore component group and
  222. // in this case you'll get 1 package for each component.
  223. return PackageComponents(componentPackageMethod ==
  224. ONE_PACKAGE_PER_COMPONENT);
  225. }
  226. // CASE 3 : NON COMPONENT package.
  227. DECLARE_AND_OPEN_ARCHIVE(packageFileNames[0], archive);
  228. cmWorkingDirectory workdir(toplevel);
  229. if (workdir.Failed()) {
  230. cmCPackLogger(cmCPackLog::LOG_ERROR,
  231. "Failed to change working directory to "
  232. << toplevel << " : "
  233. << std::strerror(workdir.GetLastResult()) << std::endl);
  234. return 0;
  235. }
  236. for (std::string const& file : files) {
  237. // Get the relative path to the file
  238. std::string rp = cmSystemTools::RelativePath(toplevel, file);
  239. archive.Add(rp, 0, nullptr, false);
  240. if (!archive) {
  241. cmCPackLogger(cmCPackLog::LOG_ERROR,
  242. "Problem while adding file <"
  243. << file << "> to archive <" << packageFileNames[0]
  244. << ">, ERROR = " << archive.GetError() << std::endl);
  245. return 0;
  246. }
  247. }
  248. // The destructor of cmArchiveWrite will close and finish the write
  249. return 1;
  250. }
  251. int cmCPackArchiveGenerator::GenerateHeader(std::ostream* /*unused*/)
  252. {
  253. return 1;
  254. }
  255. bool cmCPackArchiveGenerator::SupportsComponentInstallation() const
  256. {
  257. // The Component installation support should only
  258. // be activated if explicitly requested by the user
  259. // (for backward compatibility reason)
  260. return IsOn("CPACK_ARCHIVE_COMPONENT_INSTALL");
  261. }