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"
11 #include "cmCPackComponentGroup.h"
12 #include "cmCPackGenerator.h"
13 #include "cmCPackLog.h"
14 #include "cmGeneratedFileStream.h"
15 #include "cmStringAlgorithms.h"
16 #include "cmSystemTools.h"
18 #include "cmWorkingDirectory.h"
20 cmCPackGenerator* cmCPackArchiveGenerator::Create7ZGenerator()
22 return new cmCPackArchiveGenerator(cmArchiveWrite::CompressNone, "7zip",
26 cmCPackGenerator* cmCPackArchiveGenerator::CreateTBZ2Generator()
28 return new cmCPackArchiveGenerator(cmArchiveWrite::CompressBZip2, "paxr",
32 cmCPackGenerator* cmCPackArchiveGenerator::CreateTGZGenerator()
34 return new cmCPackArchiveGenerator(cmArchiveWrite::CompressGZip, "paxr",
38 cmCPackGenerator* cmCPackArchiveGenerator::CreateTXZGenerator()
40 return new cmCPackArchiveGenerator(cmArchiveWrite::CompressXZ, "paxr",
44 cmCPackGenerator* cmCPackArchiveGenerator::CreateTZGenerator()
46 return new cmCPackArchiveGenerator(cmArchiveWrite::CompressCompress, "paxr",
50 cmCPackGenerator* cmCPackArchiveGenerator::CreateTZSTGenerator()
52 return new cmCPackArchiveGenerator(cmArchiveWrite::CompressZstd, "paxr",
56 cmCPackGenerator* cmCPackArchiveGenerator::CreateZIPGenerator()
58 return new cmCPackArchiveGenerator(cmArchiveWrite::CompressNone, "zip",
62 cmCPackArchiveGenerator::cmCPackArchiveGenerator(
63 cmArchiveWrite::Compress compress, std::string format, std::string extension)
65 , ArchiveFormat(std::move(format))
66 , OutputExtension(std::move(extension))
70 cmCPackArchiveGenerator::~cmCPackArchiveGenerator() = default;
72 std::string cmCPackArchiveGenerator::GetArchiveComponentFileName(
73 const std::string& component, bool isGroupName)
75 std::string componentUpper(cmSystemTools::UpperCase(component));
76 std::string packageFileName;
78 if (this->IsSet("CPACK_ARCHIVE_" + componentUpper + "_FILE_NAME")) {
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);
85 packageFileName += this->GetComponentPackageFileName(
86 this->GetOption("CPACK_PACKAGE_FILE_NAME"), component, isGroupName);
89 packageFileName += this->GetOutputExtension();
91 return packageFileName;
94 int cmCPackArchiveGenerator::InitializeInternal()
96 this->SetOptionIfNotSet("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", "1");
97 return this->Superclass::InitializeInternal();
100 int cmCPackArchiveGenerator::addOneComponentToArchive(
101 cmArchiveWrite& archive, cmCPackComponent* component)
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);
117 std::string filePrefix;
118 if (this->IsOn("CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY")) {
119 filePrefix = cmStrCat(this->GetOption("CPACK_PACKAGE_FILE_NAME"), '/');
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);
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);
133 cmCPackLogger(cmCPackLog::LOG_ERROR,
134 "ERROR while packaging files: " << archive.GetError()
143 * The macro will open/create a file 'filename'
144 * an declare and open the associated
145 * cmArchiveWrite 'archive' object.
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); \
156 cmArchiveWrite archive(gf, this->Compress, this->ArchiveFormat, 0, \
157 this->GetThreadCount()); \
159 if (!archive.Open()) { \
160 cmCPackLogger(cmCPackLog::LOG_ERROR, \
161 "Problem to open archive <" \
162 << (filename) << ">, ERROR = " << (archive).GetError() \
167 cmCPackLogger(cmCPackLog::LOG_ERROR, \
168 "Problem to create archive <" \
169 << (filename) << ">, ERROR = " << (archive).GetError() \
175 int cmCPackArchiveGenerator::PackageComponents(bool ignoreGroup)
177 this->packageFileNames.clear();
178 // The default behavior is to have one package by component group
179 // unless CPACK_COMPONENTS_IGNORE_GROUP is specified.
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);
188 // open a block in order to automatically close archive
189 // at the end of the block
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);
198 // add the generated package to package file names list
199 this->packageFileNames.push_back(std::move(packageFileName));
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) {
206 cmCPackLog::LOG_VERBOSE,
209 << "> does not belong to any group, package it separately."
211 std::string localToplevel(
212 this->GetOption("CPACK_TEMPORARY_DIRECTORY"));
213 std::string packageFileName = std::string(this->toplevel);
215 localToplevel += "/" + comp.first;
217 "/" + this->GetArchiveComponentFileName(comp.first, false);
220 DECLARE_AND_OPEN_ARCHIVE(packageFileName, archive);
221 // Add the files of this component to the archive
222 this->addOneComponentToArchive(archive, &(comp.second));
224 // add the generated package to package file names list
225 this->packageFileNames.push_back(std::move(packageFileName));
229 // CPACK_COMPONENTS_IGNORE_GROUPS is set
230 // We build 1 package per component
232 for (auto& comp : this->Components) {
233 std::string localToplevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY"));
234 std::string packageFileName = std::string(this->toplevel);
236 localToplevel += "/" + comp.first;
238 "/" + this->GetArchiveComponentFileName(comp.first, false);
241 DECLARE_AND_OPEN_ARCHIVE(packageFileName, archive);
242 // Add the files of this component to the archive
243 this->addOneComponentToArchive(archive, &(comp.second));
245 // add the generated package to package file names list
246 this->packageFileNames.push_back(std::move(packageFileName));
252 int cmCPackArchiveGenerator::PackageComponentsAllInOne()
254 // reset the package file names
255 this->packageFileNames.clear();
256 this->packageFileNames.emplace_back(this->toplevel);
257 this->packageFileNames[0] += "/";
259 if (this->IsSet("CPACK_ARCHIVE_FILE_NAME")) {
260 this->packageFileNames[0] += *this->GetOption("CPACK_ARCHIVE_FILE_NAME");
262 this->packageFileNames[0] += *this->GetOption("CPACK_PACKAGE_FILE_NAME");
265 this->packageFileNames[0] += this->GetOutputExtension();
267 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
268 "Packaging all groups in one package..."
269 "(CPACK_COMPONENTS_ALL_GROUPS_IN_ONE_PACKAGE is set)"
271 DECLARE_AND_OPEN_ARCHIVE(packageFileNames[0], archive);
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));
279 // archive goes out of scope so it will finalized and closed.
283 int cmCPackArchiveGenerator::PackageFiles()
285 cmCPackLogger(cmCPackLog::LOG_DEBUG,
286 "Toplevel: " << this->toplevel << std::endl);
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();
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);
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);
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);
318 cmCPackLogger(cmCPackLog::LOG_ERROR,
319 "Problem while adding file <"
320 << file << "> to archive <" << this->packageFileNames[0]
321 << ">, ERROR = " << archive.GetError() << std::endl);
325 // The destructor of cmArchiveWrite will close and finish the write
329 int cmCPackArchiveGenerator::GenerateHeader(std::ostream* /*unused*/)
334 bool cmCPackArchiveGenerator::SupportsComponentInstallation() const
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");
342 int cmCPackArchiveGenerator::GetThreadCount() const
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"));