cmCPackArchiveGenerator.cxx 11 KB

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