cmCPackArchiveGenerator.cxx 14 KB

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