cmCPackArchiveGenerator.cxx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  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. cmValue newExtensionValue = this->GetOption("CPACK_ARCHIVE_FILE_EXTENSION");
  82. if (!newExtensionValue.IsEmpty()) {
  83. std::string newExtension = *newExtensionValue;
  84. if (!cmHasLiteralPrefix(newExtension, ".")) {
  85. newExtension = cmStrCat('.', newExtension);
  86. }
  87. cmCPackLogger(cmCPackLog::LOG_DEBUG,
  88. "Using user-provided file extension "
  89. << newExtension << " instead of the default "
  90. << this->OutputExtension << std::endl);
  91. this->OutputExtension = std::move(newExtension);
  92. }
  93. return this->Superclass::InitializeInternal();
  94. }
  95. int cmCPackArchiveGenerator::addOneComponentToArchive(
  96. cmArchiveWrite& archive, cmCPackComponent* component)
  97. {
  98. cmCPackLogger(cmCPackLog::LOG_VERBOSE,
  99. " - packaging component: " << component->Name << std::endl);
  100. // Add the files of this component to the archive
  101. std::string localToplevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY"));
  102. localToplevel += "/" + component->Name;
  103. // Change to local toplevel
  104. cmWorkingDirectory workdir(localToplevel);
  105. if (workdir.Failed()) {
  106. cmCPackLogger(cmCPackLog::LOG_ERROR,
  107. "Failed to change working directory to "
  108. << localToplevel << " : "
  109. << std::strerror(workdir.GetLastResult()) << std::endl);
  110. return 0;
  111. }
  112. std::string filePrefix;
  113. if (this->IsOn("CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY")) {
  114. filePrefix = cmStrCat(this->GetOption("CPACK_PACKAGE_FILE_NAME"), '/');
  115. }
  116. cmValue installPrefix = this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX");
  117. if (installPrefix && installPrefix->size() > 1 &&
  118. (*installPrefix)[0] == '/') {
  119. // add to file prefix and remove the leading '/'
  120. filePrefix += installPrefix->substr(1);
  121. filePrefix += "/";
  122. }
  123. for (std::string const& file : component->Files) {
  124. std::string rp = filePrefix + file;
  125. cmCPackLogger(cmCPackLog::LOG_DEBUG, "Adding file: " << rp << std::endl);
  126. archive.Add(rp, 0, nullptr, false);
  127. if (!archive) {
  128. cmCPackLogger(cmCPackLog::LOG_ERROR,
  129. "ERROR while packaging files: " << archive.GetError()
  130. << std::endl);
  131. return 0;
  132. }
  133. }
  134. return 1;
  135. }
  136. /*
  137. * The macro will open/create a file 'filename'
  138. * an declare and open the associated
  139. * cmArchiveWrite 'archive' object.
  140. */
  141. #define DECLARE_AND_OPEN_ARCHIVE(filename, archive) \
  142. cmGeneratedFileStream gf; \
  143. gf.Open((filename), false, true); \
  144. if (!GenerateHeader(&gf)) { \
  145. cmCPackLogger(cmCPackLog::LOG_ERROR, \
  146. "Problem to generate Header for archive <" \
  147. << (filename) << ">." << std::endl); \
  148. return 0; \
  149. } \
  150. cmArchiveWrite archive(gf, this->Compress, this->ArchiveFormat, 0, \
  151. this->GetThreadCount()); \
  152. do { \
  153. if (!archive.Open()) { \
  154. cmCPackLogger(cmCPackLog::LOG_ERROR, \
  155. "Problem to open archive <" \
  156. << (filename) << ">, ERROR = " << (archive).GetError() \
  157. << std::endl); \
  158. return 0; \
  159. } \
  160. if (!(archive)) { \
  161. cmCPackLogger(cmCPackLog::LOG_ERROR, \
  162. "Problem to create archive <" \
  163. << (filename) << ">, ERROR = " << (archive).GetError() \
  164. << std::endl); \
  165. return 0; \
  166. } \
  167. } while (false)
  168. int cmCPackArchiveGenerator::PackageComponents(bool ignoreGroup)
  169. {
  170. this->packageFileNames.clear();
  171. // The default behavior is to have one package by component group
  172. // unless CPACK_COMPONENTS_IGNORE_GROUP is specified.
  173. if (!ignoreGroup) {
  174. for (auto const& compG : this->ComponentGroups) {
  175. cmCPackLogger(cmCPackLog::LOG_VERBOSE,
  176. "Packaging component group: " << compG.first << std::endl);
  177. // Begin the archive for this group
  178. std::string packageFileName = std::string(this->toplevel) + "/" +
  179. this->GetArchiveComponentFileName(compG.first, true);
  180. // open a block in order to automatically close archive
  181. // at the end of the block
  182. {
  183. DECLARE_AND_OPEN_ARCHIVE(packageFileName, archive);
  184. // now iterate over the component of this group
  185. for (cmCPackComponent* comp : (compG.second).Components) {
  186. // Add the files of this component to the archive
  187. this->addOneComponentToArchive(archive, comp);
  188. }
  189. }
  190. // add the generated package to package file names list
  191. this->packageFileNames.push_back(std::move(packageFileName));
  192. }
  193. // Handle Orphan components (components not belonging to any groups)
  194. for (auto& comp : this->Components) {
  195. // Does the component belong to a group?
  196. if (comp.second.Group == nullptr) {
  197. cmCPackLogger(
  198. cmCPackLog::LOG_VERBOSE,
  199. "Component <"
  200. << comp.second.Name
  201. << "> does not belong to any group, package it separately."
  202. << std::endl);
  203. std::string localToplevel(
  204. this->GetOption("CPACK_TEMPORARY_DIRECTORY"));
  205. std::string packageFileName = std::string(this->toplevel);
  206. localToplevel += "/" + comp.first;
  207. packageFileName +=
  208. "/" + this->GetArchiveComponentFileName(comp.first, false);
  209. {
  210. DECLARE_AND_OPEN_ARCHIVE(packageFileName, archive);
  211. // Add the files of this component to the archive
  212. this->addOneComponentToArchive(archive, &(comp.second));
  213. }
  214. // add the generated package to package file names list
  215. this->packageFileNames.push_back(std::move(packageFileName));
  216. }
  217. }
  218. }
  219. // CPACK_COMPONENTS_IGNORE_GROUPS is set
  220. // We build 1 package per component
  221. else {
  222. for (auto& comp : this->Components) {
  223. std::string localToplevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY"));
  224. std::string packageFileName = std::string(this->toplevel);
  225. localToplevel += "/" + comp.first;
  226. packageFileName +=
  227. "/" + this->GetArchiveComponentFileName(comp.first, false);
  228. {
  229. DECLARE_AND_OPEN_ARCHIVE(packageFileName, archive);
  230. // Add the files of this component to the archive
  231. this->addOneComponentToArchive(archive, &(comp.second));
  232. }
  233. // add the generated package to package file names list
  234. this->packageFileNames.push_back(std::move(packageFileName));
  235. }
  236. }
  237. return 1;
  238. }
  239. int cmCPackArchiveGenerator::PackageComponentsAllInOne()
  240. {
  241. // reset the package file names
  242. this->packageFileNames.clear();
  243. this->packageFileNames.emplace_back(this->toplevel);
  244. this->packageFileNames[0] += "/";
  245. if (this->IsSet("CPACK_ARCHIVE_FILE_NAME")) {
  246. this->packageFileNames[0] += *this->GetOption("CPACK_ARCHIVE_FILE_NAME");
  247. } else {
  248. this->packageFileNames[0] += *this->GetOption("CPACK_PACKAGE_FILE_NAME");
  249. }
  250. this->packageFileNames[0] += this->GetOutputExtension();
  251. cmCPackLogger(cmCPackLog::LOG_VERBOSE,
  252. "Packaging all groups in one package..."
  253. "(CPACK_COMPONENTS_ALL_GROUPS_IN_ONE_PACKAGE is set)"
  254. << std::endl);
  255. DECLARE_AND_OPEN_ARCHIVE(packageFileNames[0], archive);
  256. // The ALL COMPONENTS in ONE package case
  257. for (auto& comp : this->Components) {
  258. // Add the files of this component to the archive
  259. this->addOneComponentToArchive(archive, &(comp.second));
  260. }
  261. // archive goes out of scope so it will finalized and closed.
  262. return 1;
  263. }
  264. int cmCPackArchiveGenerator::PackageFiles()
  265. {
  266. cmCPackLogger(cmCPackLog::LOG_DEBUG,
  267. "Toplevel: " << this->toplevel << std::endl);
  268. if (this->WantsComponentInstallation()) {
  269. // CASE 1 : COMPONENT ALL-IN-ONE package
  270. // If ALL COMPONENTS in ONE package has been requested
  271. // then the package file is unique and should be open here.
  272. if (this->componentPackageMethod == ONE_PACKAGE) {
  273. return this->PackageComponentsAllInOne();
  274. }
  275. // CASE 2 : COMPONENT CLASSICAL package(s) (i.e. not all-in-one)
  276. // There will be 1 package for each component group
  277. // however one may require to ignore component group and
  278. // in this case you'll get 1 package for each component.
  279. return this->PackageComponents(this->componentPackageMethod ==
  280. ONE_PACKAGE_PER_COMPONENT);
  281. }
  282. // CASE 3 : NON COMPONENT package.
  283. DECLARE_AND_OPEN_ARCHIVE(packageFileNames[0], archive);
  284. cmWorkingDirectory workdir(this->toplevel);
  285. if (workdir.Failed()) {
  286. cmCPackLogger(cmCPackLog::LOG_ERROR,
  287. "Failed to change working directory to "
  288. << this->toplevel << " : "
  289. << std::strerror(workdir.GetLastResult()) << std::endl);
  290. return 0;
  291. }
  292. for (std::string const& file : this->files) {
  293. // Get the relative path to the file
  294. std::string rp = cmSystemTools::RelativePath(this->toplevel, file);
  295. archive.Add(rp, 0, nullptr, false);
  296. if (!archive) {
  297. cmCPackLogger(cmCPackLog::LOG_ERROR,
  298. "Problem while adding file <"
  299. << file << "> to archive <" << this->packageFileNames[0]
  300. << ">, ERROR = " << archive.GetError() << std::endl);
  301. return 0;
  302. }
  303. }
  304. // The destructor of cmArchiveWrite will close and finish the write
  305. return 1;
  306. }
  307. int cmCPackArchiveGenerator::GenerateHeader(std::ostream* /*unused*/)
  308. {
  309. return 1;
  310. }
  311. bool cmCPackArchiveGenerator::SupportsComponentInstallation() const
  312. {
  313. // The Component installation support should only
  314. // be activated if explicitly requested by the user
  315. // (for backward compatibility reason)
  316. return this->IsOn("CPACK_ARCHIVE_COMPONENT_INSTALL");
  317. }
  318. int cmCPackArchiveGenerator::GetThreadCount() const
  319. {
  320. int threads = 1;
  321. // CPACK_ARCHIVE_THREADS overrides CPACK_THREADS
  322. if (this->IsSet("CPACK_ARCHIVE_THREADS")) {
  323. threads = std::stoi(*this->GetOption("CPACK_ARCHIVE_THREADS"));
  324. } else if (this->IsSet("CPACK_THREADS")) {
  325. threads = std::stoi(*this->GetOption("CPACK_THREADS"));
  326. }
  327. return threads;
  328. }