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 cmValue newExtensionValue = this->GetOption("CPACK_ARCHIVE_FILE_EXTENSION");
98 if (!newExtensionValue.IsEmpty()) {
99 std::string newExtension = *newExtensionValue;
100 if (!cmHasLiteralPrefix(newExtension, ".")) {
101 newExtension = cmStrCat('.', newExtension);
103 cmCPackLogger(cmCPackLog::LOG_DEBUG,
104 "Using user-provided file extension "
105 << newExtension << " instead of the default "
106 << this->OutputExtension << std::endl);
107 this->OutputExtension = std::move(newExtension);
109 return this->Superclass::InitializeInternal();
112 int cmCPackArchiveGenerator::addOneComponentToArchive(
113 cmArchiveWrite& archive, cmCPackComponent* component)
115 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
116 " - packaging component: " << component->Name << std::endl);
117 // Add the files of this component to the archive
118 std::string localToplevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY"));
119 localToplevel += "/" + component->Name;
120 // Change to local toplevel
121 cmWorkingDirectory workdir(localToplevel);
122 if (workdir.Failed()) {
123 cmCPackLogger(cmCPackLog::LOG_ERROR,
124 "Failed to change working directory to "
125 << localToplevel << " : "
126 << std::strerror(workdir.GetLastResult()) << std::endl);
129 std::string filePrefix;
130 if (this->IsOn("CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY")) {
131 filePrefix = cmStrCat(this->GetOption("CPACK_PACKAGE_FILE_NAME"), '/');
133 cmValue installPrefix = this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX");
134 if (installPrefix && installPrefix->size() > 1 &&
135 (*installPrefix)[0] == '/') {
136 // add to file prefix and remove the leading '/'
137 filePrefix += installPrefix->substr(1);
140 for (std::string const& file : component->Files) {
141 std::string rp = filePrefix + file;
142 cmCPackLogger(cmCPackLog::LOG_DEBUG, "Adding file: " << rp << std::endl);
143 archive.Add(rp, 0, nullptr, false);
145 cmCPackLogger(cmCPackLog::LOG_ERROR,
146 "ERROR while packaging files: " << archive.GetError()
155 * The macro will open/create a file 'filename'
156 * an declare and open the associated
157 * cmArchiveWrite 'archive' object.
159 #define DECLARE_AND_OPEN_ARCHIVE(filename, archive) \
160 cmGeneratedFileStream gf; \
161 gf.Open((filename), false, true); \
162 if (!GenerateHeader(&gf)) { \
163 cmCPackLogger(cmCPackLog::LOG_ERROR, \
164 "Problem to generate Header for archive <" \
165 << (filename) << ">." << std::endl); \
168 cmArchiveWrite archive(gf, this->Compress, this->ArchiveFormat, 0, \
169 this->GetThreadCount()); \
171 if (!archive.Open()) { \
172 cmCPackLogger(cmCPackLog::LOG_ERROR, \
173 "Problem to open archive <" \
174 << (filename) << ">, ERROR = " << (archive).GetError() \
179 cmCPackLogger(cmCPackLog::LOG_ERROR, \
180 "Problem to create archive <" \
181 << (filename) << ">, ERROR = " << (archive).GetError() \
187 int cmCPackArchiveGenerator::PackageComponents(bool ignoreGroup)
189 this->packageFileNames.clear();
190 // The default behavior is to have one package by component group
191 // unless CPACK_COMPONENTS_IGNORE_GROUP is specified.
193 for (auto const& compG : this->ComponentGroups) {
194 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
195 "Packaging component group: " << compG.first << std::endl);
196 // Begin the archive for this group
197 std::string packageFileName = std::string(this->toplevel) + "/" +
198 this->GetArchiveComponentFileName(compG.first, true);
200 // open a block in order to automatically close archive
201 // at the end of the block
203 DECLARE_AND_OPEN_ARCHIVE(packageFileName, archive);
204 // now iterate over the component of this group
205 for (cmCPackComponent* comp : (compG.second).Components) {
206 // Add the files of this component to the archive
207 this->addOneComponentToArchive(archive, comp);
210 // add the generated package to package file names list
211 this->packageFileNames.push_back(std::move(packageFileName));
213 // Handle Orphan components (components not belonging to any groups)
214 for (auto& comp : this->Components) {
215 // Does the component belong to a group?
216 if (comp.second.Group == nullptr) {
218 cmCPackLog::LOG_VERBOSE,
221 << "> does not belong to any group, package it separately."
223 std::string localToplevel(
224 this->GetOption("CPACK_TEMPORARY_DIRECTORY"));
225 std::string packageFileName = std::string(this->toplevel);
227 localToplevel += "/" + comp.first;
229 "/" + this->GetArchiveComponentFileName(comp.first, false);
232 DECLARE_AND_OPEN_ARCHIVE(packageFileName, archive);
233 // Add the files of this component to the archive
234 this->addOneComponentToArchive(archive, &(comp.second));
236 // add the generated package to package file names list
237 this->packageFileNames.push_back(std::move(packageFileName));
241 // CPACK_COMPONENTS_IGNORE_GROUPS is set
242 // We build 1 package per component
244 for (auto& comp : this->Components) {
245 std::string localToplevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY"));
246 std::string packageFileName = std::string(this->toplevel);
248 localToplevel += "/" + comp.first;
250 "/" + this->GetArchiveComponentFileName(comp.first, false);
253 DECLARE_AND_OPEN_ARCHIVE(packageFileName, archive);
254 // Add the files of this component to the archive
255 this->addOneComponentToArchive(archive, &(comp.second));
257 // add the generated package to package file names list
258 this->packageFileNames.push_back(std::move(packageFileName));
264 int cmCPackArchiveGenerator::PackageComponentsAllInOne()
266 // reset the package file names
267 this->packageFileNames.clear();
268 this->packageFileNames.emplace_back(this->toplevel);
269 this->packageFileNames[0] += "/";
271 if (this->IsSet("CPACK_ARCHIVE_FILE_NAME")) {
272 this->packageFileNames[0] += *this->GetOption("CPACK_ARCHIVE_FILE_NAME");
274 this->packageFileNames[0] += *this->GetOption("CPACK_PACKAGE_FILE_NAME");
277 this->packageFileNames[0] += this->GetOutputExtension();
279 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
280 "Packaging all groups in one package..."
281 "(CPACK_COMPONENTS_ALL_GROUPS_IN_ONE_PACKAGE is set)"
283 DECLARE_AND_OPEN_ARCHIVE(packageFileNames[0], archive);
285 // The ALL COMPONENTS in ONE package case
286 for (auto& comp : this->Components) {
287 // Add the files of this component to the archive
288 this->addOneComponentToArchive(archive, &(comp.second));
291 // archive goes out of scope so it will finalized and closed.
295 int cmCPackArchiveGenerator::PackageFiles()
297 cmCPackLogger(cmCPackLog::LOG_DEBUG,
298 "Toplevel: " << this->toplevel << std::endl);
300 if (this->WantsComponentInstallation()) {
301 // CASE 1 : COMPONENT ALL-IN-ONE package
302 // If ALL COMPONENTS in ONE package has been requested
303 // then the package file is unique and should be open here.
304 if (this->componentPackageMethod == ONE_PACKAGE) {
305 return this->PackageComponentsAllInOne();
307 // CASE 2 : COMPONENT CLASSICAL package(s) (i.e. not all-in-one)
308 // There will be 1 package for each component group
309 // however one may require to ignore component group and
310 // in this case you'll get 1 package for each component.
311 return this->PackageComponents(this->componentPackageMethod ==
312 ONE_PACKAGE_PER_COMPONENT);
315 // CASE 3 : NON COMPONENT package.
316 DECLARE_AND_OPEN_ARCHIVE(packageFileNames[0], archive);
317 cmWorkingDirectory workdir(this->toplevel);
318 if (workdir.Failed()) {
319 cmCPackLogger(cmCPackLog::LOG_ERROR,
320 "Failed to change working directory to "
321 << this->toplevel << " : "
322 << std::strerror(workdir.GetLastResult()) << std::endl);
325 for (std::string const& file : this->files) {
326 // Get the relative path to the file
327 std::string rp = cmSystemTools::RelativePath(this->toplevel, file);
328 archive.Add(rp, 0, nullptr, false);
330 cmCPackLogger(cmCPackLog::LOG_ERROR,
331 "Problem while adding file <"
332 << file << "> to archive <" << this->packageFileNames[0]
333 << ">, ERROR = " << archive.GetError() << std::endl);
337 // The destructor of cmArchiveWrite will close and finish the write
341 int cmCPackArchiveGenerator::GenerateHeader(std::ostream* /*unused*/)
346 bool cmCPackArchiveGenerator::SupportsComponentInstallation() const
348 // The Component installation support should only
349 // be activated if explicitly requested by the user
350 // (for backward compatibility reason)
351 return this->IsOn("CPACK_ARCHIVE_COMPONENT_INSTALL");
354 int cmCPackArchiveGenerator::GetThreadCount() const
358 // CPACK_ARCHIVE_THREADS overrides CPACK_THREADS
359 if (this->IsSet("CPACK_ARCHIVE_THREADS")) {
360 threads = std::stoi(this->GetOption("CPACK_ARCHIVE_THREADS"));
361 } else if (this->IsSet("CPACK_THREADS")) {
362 threads = std::stoi(this->GetOption("CPACK_THREADS"));