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