cmCPackArchiveGenerator.cxx 10 KB

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