cmCPackArchiveGenerator.cxx 11 KB

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