cmCPackArchiveGenerator.cxx 12 KB

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