You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
cmake/Source/CPack/cmCPackArchiveGenerator.cxx

367 lines
14 KiB

/* 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 <cstring>
#include <map>
#include <ostream>
#include <utility>
#include <vector>
#include <cm3p/archive.h>
#include "cmCPackComponentGroup.h"
#include "cmCPackGenerator.h"
#include "cmCPackLog.h"
#include "cmGeneratedFileStream.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmWorkingDirectory.h"
cmCPackGenerator* cmCPackArchiveGenerator::Create7ZGenerator()
{
return new cmCPackArchiveGenerator(cmArchiveWrite::CompressNone, "7zip",
".7z");
}
cmCPackGenerator* cmCPackArchiveGenerator::CreateTBZ2Generator()
{
return new cmCPackArchiveGenerator(cmArchiveWrite::CompressBZip2, "paxr",
".tar.bz2");
}
cmCPackGenerator* cmCPackArchiveGenerator::CreateTGZGenerator()
{
return new cmCPackArchiveGenerator(cmArchiveWrite::CompressGZip, "paxr",
".tar.gz");
}
cmCPackGenerator* cmCPackArchiveGenerator::CreateTXZGenerator()
{
return new cmCPackArchiveGenerator(cmArchiveWrite::CompressXZ, "paxr",
".tar.xz");
}
cmCPackGenerator* cmCPackArchiveGenerator::CreateTZGenerator()
{
return new cmCPackArchiveGenerator(cmArchiveWrite::CompressCompress, "paxr",
".tar.Z");
}
cmCPackGenerator* cmCPackArchiveGenerator::CreateTZSTGenerator()
{
return new cmCPackArchiveGenerator(cmArchiveWrite::CompressZstd, "paxr",
".tar.zst");
}
cmCPackGenerator* cmCPackArchiveGenerator::CreateZIPGenerator()
{
return new cmCPackArchiveGenerator(cmArchiveWrite::CompressNone, "zip",
".zip");
}
cmCPackArchiveGenerator::cmCPackArchiveGenerator(
cmArchiveWrite::Compress compress, std::string format, std::string extension)
: Compress(compress)
, ArchiveFormat(std::move(format))
, OutputExtension(std::move(extension))
{
}
cmCPackArchiveGenerator::~cmCPackArchiveGenerator() = default;
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 = cmStrCat(this->GetOption("CPACK_PACKAGE_FILE_NAME"), '/');
}
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); \
do { \
if (!this->SetArchiveOptions(&archive)) { \
cmCPackLogger(cmCPackLog::LOG_ERROR, \
"Problem to set archive options <" \
<< (filename) << ">, ERROR = " << (archive).GetError() \
<< std::endl); \
return 0; \
} \
if (!archive.Open()) { \
cmCPackLogger(cmCPackLog::LOG_ERROR, \
"Problem to open archive <" \
<< (filename) << ">, ERROR = " << (archive).GetError() \
<< std::endl); \
return 0; \
} \
if (!(archive)) { \
cmCPackLogger(cmCPackLog::LOG_ERROR, \
"Problem to create archive <" \
<< (filename) << ">, ERROR = " << (archive).GetError() \
<< std::endl); \
return 0; \
} \
} while (false)
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");
}
bool cmCPackArchiveGenerator::SetArchiveOptions(cmArchiveWrite* archive)
{
#if ARCHIVE_VERSION_NUMBER >= 3004000
// Upstream fixed an issue with their integer parsing in 3.4.0 which would
// cause spurious errors to be raised from `strtoull`.
if (this->Compress == cmArchiveWrite::CompressXZ) {
const char* threads = "1";
if (this->IsSet("CPACK_ARCHIVE_THREADS")) {
threads = this->GetOption("CPACK_ARCHIVE_THREADS");
}
if (!archive->SetFilterOption("xz", "threads", threads)) {
return false;
}
}
#endif
return true;
}