| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| file Copyright.txt or https://cmake.org/licensing for details. */ |
| #include "cmCPackArchiveGenerator.h" |
| |
| #include "cmCPackComponentGroup.h" |
| #include "cmCPackGenerator.h" |
| #include "cmCPackLog.h" |
| #include "cmGeneratedFileStream.h" |
| #include "cmSystemTools.h" |
| #include "cmWorkingDirectory.h" |
| |
| #include <cstring> |
| #include <ostream> |
| #include <utility> |
| #include <vector> |
| |
| cmCPackArchiveGenerator::cmCPackArchiveGenerator(cmArchiveWrite::Compress t, |
| std::string const& format) |
| { |
| this->Compress = t; |
| this->ArchiveFormat = format; |
| } |
| |
| cmCPackArchiveGenerator::~cmCPackArchiveGenerator() |
| { |
| } |
| |
| std::string cmCPackArchiveGenerator::GetArchiveComponentFileName( |
| const std::string& component, bool isGroupName) |
| { |
| std::string componentUpper(cmSystemTools::UpperCase(component)); |
| std::string packageFileName; |
| |
| if (this->IsSet("CPACK_ARCHIVE_" + componentUpper + "_FILE_NAME")) { |
| packageFileName += |
| this->GetOption("CPACK_ARCHIVE_" + componentUpper + "_FILE_NAME"); |
| } else if (this->IsSet("CPACK_ARCHIVE_FILE_NAME")) { |
| packageFileName += GetComponentPackageFileName( |
| this->GetOption("CPACK_ARCHIVE_FILE_NAME"), component, isGroupName); |
| } else { |
| packageFileName += GetComponentPackageFileName( |
| this->GetOption("CPACK_PACKAGE_FILE_NAME"), component, isGroupName); |
| } |
| |
| packageFileName += this->GetOutputExtension(); |
| |
| return packageFileName; |
| } |
| |
| int cmCPackArchiveGenerator::InitializeInternal() |
| { |
| this->SetOptionIfNotSet("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", "1"); |
| return this->Superclass::InitializeInternal(); |
| } |
| |
| int cmCPackArchiveGenerator::addOneComponentToArchive( |
| cmArchiveWrite& archive, cmCPackComponent* component) |
| { |
| cmCPackLogger(cmCPackLog::LOG_VERBOSE, |
| " - packaging component: " << component->Name << std::endl); |
| // Add the files of this component to the archive |
| std::string localToplevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY")); |
| localToplevel += "/" + component->Name; |
| // Change to local toplevel |
| cmWorkingDirectory workdir(localToplevel); |
| if (workdir.Failed()) { |
| cmCPackLogger(cmCPackLog::LOG_ERROR, |
| "Failed to change working directory to " |
| << localToplevel << " : " |
| << std::strerror(workdir.GetLastResult()) << std::endl); |
| return 0; |
| } |
| std::string filePrefix; |
| if (this->IsOn("CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY")) { |
| filePrefix = this->GetOption("CPACK_PACKAGE_FILE_NAME"); |
| filePrefix += "/"; |
| } |
| const char* installPrefix = |
| this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX"); |
| if (installPrefix && installPrefix[0] == '/' && installPrefix[1] != 0) { |
| // add to file prefix and remove the leading '/' |
| filePrefix += installPrefix + 1; |
| filePrefix += "/"; |
| } |
| for (std::string const& file : component->Files) { |
| std::string rp = filePrefix + file; |
| cmCPackLogger(cmCPackLog::LOG_DEBUG, "Adding file: " << rp << std::endl); |
| archive.Add(rp, 0, nullptr, false); |
| if (!archive) { |
| cmCPackLogger(cmCPackLog::LOG_ERROR, |
| "ERROR while packaging files: " << archive.GetError() |
| << std::endl); |
| return 0; |
| } |
| } |
| return 1; |
| } |
| |
| /* |
| * The macro will open/create a file 'filename' |
| * an declare and open the associated |
| * cmArchiveWrite 'archive' object. |
| */ |
| #define DECLARE_AND_OPEN_ARCHIVE(filename, archive) \ |
| cmGeneratedFileStream gf; \ |
| gf.Open((filename), false, true); \ |
| if (!GenerateHeader(&gf)) { \ |
| cmCPackLogger(cmCPackLog::LOG_ERROR, \ |
| "Problem to generate Header for archive <" \ |
| << (filename) << ">." << std::endl); \ |
| return 0; \ |
| } \ |
| cmArchiveWrite archive(gf, this->Compress, this->ArchiveFormat); \ |
| if (!(archive)) { \ |
| cmCPackLogger(cmCPackLog::LOG_ERROR, \ |
| "Problem to create archive <" \ |
| << (filename) << ">, ERROR = " << (archive).GetError() \ |
| << std::endl); \ |
| return 0; \ |
| } |
| |
| int cmCPackArchiveGenerator::PackageComponents(bool ignoreGroup) |
| { |
| packageFileNames.clear(); |
| // The default behavior is to have one package by component group |
| // unless CPACK_COMPONENTS_IGNORE_GROUP is specified. |
| if (!ignoreGroup) { |
| for (auto const& compG : this->ComponentGroups) { |
| cmCPackLogger(cmCPackLog::LOG_VERBOSE, |
| "Packaging component group: " << compG.first << std::endl); |
| // Begin the archive for this group |
| std::string packageFileName = std::string(toplevel) + "/" + |
| this->GetArchiveComponentFileName(compG.first, true); |
| |
| // open a block in order to automatically close archive |
| // at the end of the block |
| { |
| DECLARE_AND_OPEN_ARCHIVE(packageFileName, archive); |
| // now iterate over the component of this group |
| for (cmCPackComponent* comp : (compG.second).Components) { |
| // Add the files of this component to the archive |
| addOneComponentToArchive(archive, comp); |
| } |
| } |
| // add the generated package to package file names list |
| packageFileNames.push_back(std::move(packageFileName)); |
| } |
| // Handle Orphan components (components not belonging to any groups) |
| for (auto& comp : this->Components) { |
| // Does the component belong to a group? |
| if (comp.second.Group == nullptr) { |
| cmCPackLogger( |
| cmCPackLog::LOG_VERBOSE, |
| "Component <" |
| << comp.second.Name |
| << "> does not belong to any group, package it separately." |
| << std::endl); |
| std::string localToplevel( |
| this->GetOption("CPACK_TEMPORARY_DIRECTORY")); |
| std::string packageFileName = std::string(toplevel); |
| |
| localToplevel += "/" + comp.first; |
| packageFileName += |
| "/" + this->GetArchiveComponentFileName(comp.first, false); |
| |
| { |
| DECLARE_AND_OPEN_ARCHIVE(packageFileName, archive); |
| // Add the files of this component to the archive |
| addOneComponentToArchive(archive, &(comp.second)); |
| } |
| // add the generated package to package file names list |
| packageFileNames.push_back(std::move(packageFileName)); |
| } |
| } |
| } |
| // CPACK_COMPONENTS_IGNORE_GROUPS is set |
| // We build 1 package per component |
| else { |
| for (auto& comp : this->Components) { |
| std::string localToplevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY")); |
| std::string packageFileName = std::string(toplevel); |
| |
| localToplevel += "/" + comp.first; |
| packageFileName += |
| "/" + this->GetArchiveComponentFileName(comp.first, false); |
| |
| { |
| DECLARE_AND_OPEN_ARCHIVE(packageFileName, archive); |
| // Add the files of this component to the archive |
| addOneComponentToArchive(archive, &(comp.second)); |
| } |
| // add the generated package to package file names list |
| packageFileNames.push_back(std::move(packageFileName)); |
| } |
| } |
| return 1; |
| } |
| |
| int cmCPackArchiveGenerator::PackageComponentsAllInOne() |
| { |
| // reset the package file names |
| packageFileNames.clear(); |
| packageFileNames.emplace_back(toplevel); |
| packageFileNames[0] += "/"; |
| |
| if (this->IsSet("CPACK_ARCHIVE_FILE_NAME")) { |
| packageFileNames[0] += this->GetOption("CPACK_ARCHIVE_FILE_NAME"); |
| } else { |
| packageFileNames[0] += this->GetOption("CPACK_PACKAGE_FILE_NAME"); |
| } |
| |
| packageFileNames[0] += this->GetOutputExtension(); |
| |
| cmCPackLogger(cmCPackLog::LOG_VERBOSE, |
| "Packaging all groups in one package..." |
| "(CPACK_COMPONENTS_ALL_GROUPS_IN_ONE_PACKAGE is set)" |
| << std::endl); |
| DECLARE_AND_OPEN_ARCHIVE(packageFileNames[0], archive); |
| |
| // The ALL COMPONENTS in ONE package case |
| for (auto& comp : this->Components) { |
| // Add the files of this component to the archive |
| addOneComponentToArchive(archive, &(comp.second)); |
| } |
| |
| // archive goes out of scope so it will finalized and closed. |
| return 1; |
| } |
| |
| int cmCPackArchiveGenerator::PackageFiles() |
| { |
| cmCPackLogger(cmCPackLog::LOG_DEBUG, "Toplevel: " << toplevel << std::endl); |
| |
| if (WantsComponentInstallation()) { |
| // CASE 1 : COMPONENT ALL-IN-ONE package |
| // If ALL COMPONENTS in ONE package has been requested |
| // then the package file is unique and should be open here. |
| if (componentPackageMethod == ONE_PACKAGE) { |
| return PackageComponentsAllInOne(); |
| } |
| // CASE 2 : COMPONENT CLASSICAL package(s) (i.e. not all-in-one) |
| // There will be 1 package for each component group |
| // however one may require to ignore component group and |
| // in this case you'll get 1 package for each component. |
| return PackageComponents(componentPackageMethod == |
| ONE_PACKAGE_PER_COMPONENT); |
| } |
| |
| // CASE 3 : NON COMPONENT package. |
| DECLARE_AND_OPEN_ARCHIVE(packageFileNames[0], archive); |
| cmWorkingDirectory workdir(toplevel); |
| if (workdir.Failed()) { |
| cmCPackLogger(cmCPackLog::LOG_ERROR, |
| "Failed to change working directory to " |
| << toplevel << " : " |
| << std::strerror(workdir.GetLastResult()) << std::endl); |
| return 0; |
| } |
| for (std::string const& file : files) { |
| // Get the relative path to the file |
| std::string rp = cmSystemTools::RelativePath(toplevel, file); |
| archive.Add(rp, 0, nullptr, false); |
| if (!archive) { |
| cmCPackLogger(cmCPackLog::LOG_ERROR, |
| "Problem while adding file <" |
| << file << "> to archive <" << packageFileNames[0] |
| << ">, ERROR = " << archive.GetError() << std::endl); |
| return 0; |
| } |
| } |
| // The destructor of cmArchiveWrite will close and finish the write |
| return 1; |
| } |
| |
| int cmCPackArchiveGenerator::GenerateHeader(std::ostream* /*unused*/) |
| { |
| return 1; |
| } |
| |
| bool cmCPackArchiveGenerator::SupportsComponentInstallation() const |
| { |
| // The Component installation support should only |
| // be activated if explicitly requested by the user |
| // (for backward compatibility reason) |
| return IsOn("CPACK_ARCHIVE_COMPONENT_INSTALL"); |
| } |