cmCPackArchiveGenerator.cxx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  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 "cmCPackComponentGroup.h"
  10. #include "cmCPackGenerator.h"
  11. #include "cmCPackLog.h"
  12. #include "cmGeneratedFileStream.h"
  13. #include "cmStringAlgorithms.h"
  14. #include "cmSystemTools.h"
  15. #include "cmValue.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. cmValue installPrefix = this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX");
  105. if (installPrefix && installPrefix->size() > 1 &&
  106. (*installPrefix)[0] == '/') {
  107. // add to file prefix and remove the leading '/'
  108. filePrefix += installPrefix->substr(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, 0, \
  139. this->GetThreadCount()); \
  140. do { \
  141. if (!archive.Open()) { \
  142. cmCPackLogger(cmCPackLog::LOG_ERROR, \
  143. "Problem to open archive <" \
  144. << (filename) << ">, ERROR = " << (archive).GetError() \
  145. << std::endl); \
  146. return 0; \
  147. } \
  148. if (!(archive)) { \
  149. cmCPackLogger(cmCPackLog::LOG_ERROR, \
  150. "Problem to create archive <" \
  151. << (filename) << ">, ERROR = " << (archive).GetError() \
  152. << std::endl); \
  153. return 0; \
  154. } \
  155. } while (false)
  156. int cmCPackArchiveGenerator::PackageComponents(bool ignoreGroup)
  157. {
  158. this->packageFileNames.clear();
  159. // The default behavior is to have one package by component group
  160. // unless CPACK_COMPONENTS_IGNORE_GROUP is specified.
  161. if (!ignoreGroup) {
  162. for (auto const& compG : this->ComponentGroups) {
  163. cmCPackLogger(cmCPackLog::LOG_VERBOSE,
  164. "Packaging component group: " << compG.first << std::endl);
  165. // Begin the archive for this group
  166. std::string packageFileName = std::string(this->toplevel) + "/" +
  167. this->GetArchiveComponentFileName(compG.first, true);
  168. // open a block in order to automatically close archive
  169. // at the end of the block
  170. {
  171. DECLARE_AND_OPEN_ARCHIVE(packageFileName, archive);
  172. // now iterate over the component of this group
  173. for (cmCPackComponent* comp : (compG.second).Components) {
  174. // Add the files of this component to the archive
  175. this->addOneComponentToArchive(archive, comp);
  176. }
  177. }
  178. // add the generated package to package file names list
  179. this->packageFileNames.push_back(std::move(packageFileName));
  180. }
  181. // Handle Orphan components (components not belonging to any groups)
  182. for (auto& comp : this->Components) {
  183. // Does the component belong to a group?
  184. if (comp.second.Group == nullptr) {
  185. cmCPackLogger(
  186. cmCPackLog::LOG_VERBOSE,
  187. "Component <"
  188. << comp.second.Name
  189. << "> does not belong to any group, package it separately."
  190. << std::endl);
  191. std::string localToplevel(
  192. this->GetOption("CPACK_TEMPORARY_DIRECTORY"));
  193. std::string packageFileName = std::string(this->toplevel);
  194. localToplevel += "/" + comp.first;
  195. packageFileName +=
  196. "/" + this->GetArchiveComponentFileName(comp.first, false);
  197. {
  198. DECLARE_AND_OPEN_ARCHIVE(packageFileName, archive);
  199. // Add the files of this component to the archive
  200. this->addOneComponentToArchive(archive, &(comp.second));
  201. }
  202. // add the generated package to package file names list
  203. this->packageFileNames.push_back(std::move(packageFileName));
  204. }
  205. }
  206. }
  207. // CPACK_COMPONENTS_IGNORE_GROUPS is set
  208. // We build 1 package per component
  209. else {
  210. for (auto& comp : this->Components) {
  211. std::string localToplevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY"));
  212. std::string packageFileName = std::string(this->toplevel);
  213. localToplevel += "/" + comp.first;
  214. packageFileName +=
  215. "/" + this->GetArchiveComponentFileName(comp.first, false);
  216. {
  217. DECLARE_AND_OPEN_ARCHIVE(packageFileName, archive);
  218. // Add the files of this component to the archive
  219. this->addOneComponentToArchive(archive, &(comp.second));
  220. }
  221. // add the generated package to package file names list
  222. this->packageFileNames.push_back(std::move(packageFileName));
  223. }
  224. }
  225. return 1;
  226. }
  227. int cmCPackArchiveGenerator::PackageComponentsAllInOne()
  228. {
  229. // reset the package file names
  230. this->packageFileNames.clear();
  231. this->packageFileNames.emplace_back(this->toplevel);
  232. this->packageFileNames[0] += "/";
  233. if (this->IsSet("CPACK_ARCHIVE_FILE_NAME")) {
  234. this->packageFileNames[0] += *this->GetOption("CPACK_ARCHIVE_FILE_NAME");
  235. } else {
  236. this->packageFileNames[0] += *this->GetOption("CPACK_PACKAGE_FILE_NAME");
  237. }
  238. this->packageFileNames[0] += this->GetOutputExtension();
  239. cmCPackLogger(cmCPackLog::LOG_VERBOSE,
  240. "Packaging all groups in one package..."
  241. "(CPACK_COMPONENTS_ALL_GROUPS_IN_ONE_PACKAGE is set)"
  242. << std::endl);
  243. DECLARE_AND_OPEN_ARCHIVE(packageFileNames[0], archive);
  244. // The ALL COMPONENTS in ONE package case
  245. for (auto& comp : this->Components) {
  246. // Add the files of this component to the archive
  247. this->addOneComponentToArchive(archive, &(comp.second));
  248. }
  249. // archive goes out of scope so it will finalized and closed.
  250. return 1;
  251. }
  252. int cmCPackArchiveGenerator::PackageFiles()
  253. {
  254. cmCPackLogger(cmCPackLog::LOG_DEBUG,
  255. "Toplevel: " << this->toplevel << std::endl);
  256. if (this->WantsComponentInstallation()) {
  257. // CASE 1 : COMPONENT ALL-IN-ONE package
  258. // If ALL COMPONENTS in ONE package has been requested
  259. // then the package file is unique and should be open here.
  260. if (this->componentPackageMethod == ONE_PACKAGE) {
  261. return this->PackageComponentsAllInOne();
  262. }
  263. // CASE 2 : COMPONENT CLASSICAL package(s) (i.e. not all-in-one)
  264. // There will be 1 package for each component group
  265. // however one may require to ignore component group and
  266. // in this case you'll get 1 package for each component.
  267. return this->PackageComponents(this->componentPackageMethod ==
  268. ONE_PACKAGE_PER_COMPONENT);
  269. }
  270. // CASE 3 : NON COMPONENT package.
  271. DECLARE_AND_OPEN_ARCHIVE(packageFileNames[0], archive);
  272. cmWorkingDirectory workdir(this->toplevel);
  273. if (workdir.Failed()) {
  274. cmCPackLogger(cmCPackLog::LOG_ERROR,
  275. "Failed to change working directory to "
  276. << this->toplevel << " : "
  277. << std::strerror(workdir.GetLastResult()) << std::endl);
  278. return 0;
  279. }
  280. for (std::string const& file : this->files) {
  281. // Get the relative path to the file
  282. std::string rp = cmSystemTools::RelativePath(this->toplevel, file);
  283. archive.Add(rp, 0, nullptr, false);
  284. if (!archive) {
  285. cmCPackLogger(cmCPackLog::LOG_ERROR,
  286. "Problem while adding file <"
  287. << file << "> to archive <" << this->packageFileNames[0]
  288. << ">, ERROR = " << archive.GetError() << std::endl);
  289. return 0;
  290. }
  291. }
  292. // The destructor of cmArchiveWrite will close and finish the write
  293. return 1;
  294. }
  295. int cmCPackArchiveGenerator::GenerateHeader(std::ostream* /*unused*/)
  296. {
  297. return 1;
  298. }
  299. bool cmCPackArchiveGenerator::SupportsComponentInstallation() const
  300. {
  301. // The Component installation support should only
  302. // be activated if explicitly requested by the user
  303. // (for backward compatibility reason)
  304. return this->IsOn("CPACK_ARCHIVE_COMPONENT_INSTALL");
  305. }
  306. int cmCPackArchiveGenerator::GetThreadCount() const
  307. {
  308. int threads = 1;
  309. // CPACK_ARCHIVE_THREADS overrides CPACK_THREADS
  310. if (this->IsSet("CPACK_ARCHIVE_THREADS")) {
  311. threads = std::stoi(this->GetOption("CPACK_ARCHIVE_THREADS"));
  312. } else if (this->IsSet("CPACK_THREADS")) {
  313. threads = std::stoi(this->GetOption("CPACK_THREADS"));
  314. }
  315. return threads;
  316. }