mirror of
https://github.com/Kitware/CMake.git
synced 2026-05-08 07:10:12 -05:00
48bc8b2b82
These modules are not meant to be included by user code, they are only an internal implementation detail for CPack. Having them live in the main Modules directory with documentation was misleading, so they have been moved into Modules/Internal/CPack, and their documentation has been stripped following its move into the new "CPack Generators" section. No-op modules which contained only documentation have been removed entirely. The only module that hasn't been moved is CPackIFW, because it contains user-facing macros which would be lost if it were moved. So, the CPackIFW module has been updated with a note explaining what needs to (eventually) happen.
705 lines
26 KiB
C++
705 lines
26 KiB
C++
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
|
#include "cmCPackDebGenerator.h"
|
|
|
|
#include "cmArchiveWrite.h"
|
|
#include "cmCPackComponentGroup.h"
|
|
#include "cmCPackGenerator.h"
|
|
#include "cmCPackLog.h"
|
|
#include "cmCryptoHash.h"
|
|
#include "cmGeneratedFileStream.h"
|
|
#include "cmSystemTools.h"
|
|
#include "cm_sys_stat.h"
|
|
|
|
#include "cmsys/Glob.hxx"
|
|
#include <ostream>
|
|
#include <set>
|
|
#include <string.h>
|
|
#include <utility>
|
|
|
|
cmCPackDebGenerator::cmCPackDebGenerator()
|
|
{
|
|
}
|
|
|
|
cmCPackDebGenerator::~cmCPackDebGenerator()
|
|
{
|
|
}
|
|
|
|
int cmCPackDebGenerator::InitializeInternal()
|
|
{
|
|
this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/usr");
|
|
if (cmSystemTools::IsOff(this->GetOption("CPACK_SET_DESTDIR"))) {
|
|
this->SetOption("CPACK_SET_DESTDIR", "I_ON");
|
|
}
|
|
return this->Superclass::InitializeInternal();
|
|
}
|
|
|
|
int cmCPackDebGenerator::PackageOnePack(std::string const& initialTopLevel,
|
|
std::string const& packageName)
|
|
{
|
|
int retval = 1;
|
|
// Begin the archive for this pack
|
|
std::string localToplevel(initialTopLevel);
|
|
std::string packageFileName(cmSystemTools::GetParentDirectory(toplevel));
|
|
std::string outputFileName(
|
|
std::string(this->GetOption("CPACK_PACKAGE_FILE_NAME")) + "-" +
|
|
packageName + this->GetOutputExtension());
|
|
|
|
localToplevel += "/" + packageName;
|
|
/* replace the TEMP DIRECTORY with the component one */
|
|
this->SetOption("CPACK_TEMPORARY_DIRECTORY", localToplevel.c_str());
|
|
packageFileName += "/" + outputFileName;
|
|
/* replace proposed CPACK_OUTPUT_FILE_NAME */
|
|
this->SetOption("CPACK_OUTPUT_FILE_NAME", outputFileName.c_str());
|
|
/* replace the TEMPORARY package file name */
|
|
this->SetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME",
|
|
packageFileName.c_str());
|
|
// Tell CPackDeb.cmake the name of the component GROUP.
|
|
this->SetOption("CPACK_DEB_PACKAGE_COMPONENT", packageName.c_str());
|
|
// Tell CPackDeb.cmake the path where the component is.
|
|
std::string component_path = "/";
|
|
component_path += packageName;
|
|
this->SetOption("CPACK_DEB_PACKAGE_COMPONENT_PART_PATH",
|
|
component_path.c_str());
|
|
if (!this->ReadListFile("Internal/CPack/CPackDeb.cmake")) {
|
|
cmCPackLogger(cmCPackLog::LOG_ERROR,
|
|
"Error while execution CPackDeb.cmake" << std::endl);
|
|
retval = 0;
|
|
return retval;
|
|
}
|
|
|
|
cmsys::Glob gl;
|
|
std::string findExpr(this->GetOption("GEN_WDIR"));
|
|
findExpr += "/*";
|
|
gl.RecurseOn();
|
|
gl.SetRecurseListDirs(true);
|
|
if (!gl.FindFiles(findExpr)) {
|
|
cmCPackLogger(cmCPackLog::LOG_ERROR,
|
|
"Cannot find any files in the installed directory"
|
|
<< std::endl);
|
|
return 0;
|
|
}
|
|
packageFiles = gl.GetFiles();
|
|
|
|
int res = createDeb();
|
|
if (res != 1) {
|
|
retval = 0;
|
|
}
|
|
// add the generated package to package file names list
|
|
packageFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
|
|
packageFileName += "/";
|
|
packageFileName += this->GetOption("GEN_CPACK_OUTPUT_FILE_NAME");
|
|
packageFileNames.push_back(std::move(packageFileName));
|
|
return retval;
|
|
}
|
|
|
|
int cmCPackDebGenerator::PackageComponents(bool ignoreGroup)
|
|
{
|
|
int retval = 1;
|
|
/* Reset package file name list it will be populated during the
|
|
* component packaging run*/
|
|
packageFileNames.clear();
|
|
std::string initialTopLevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY"));
|
|
|
|
// 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
|
|
retval &= PackageOnePack(initialTopLevel, compG.first);
|
|
}
|
|
// Handle Orphan components (components not belonging to any groups)
|
|
for (auto const& 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);
|
|
// Begin the archive for this orphan component
|
|
retval &= PackageOnePack(initialTopLevel, comp.first);
|
|
}
|
|
}
|
|
}
|
|
// CPACK_COMPONENTS_IGNORE_GROUPS is set
|
|
// We build 1 package per component
|
|
else {
|
|
for (auto const& comp : this->Components) {
|
|
retval &= PackageOnePack(initialTopLevel, comp.first);
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
int cmCPackDebGenerator::PackageComponentsAllInOne(
|
|
const std::string& compInstDirName)
|
|
{
|
|
int retval = 1;
|
|
/* Reset package file name list it will be populated during the
|
|
* component packaging run*/
|
|
packageFileNames.clear();
|
|
std::string initialTopLevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY"));
|
|
|
|
cmCPackLogger(cmCPackLog::LOG_VERBOSE,
|
|
"Packaging all groups in one package..."
|
|
"(CPACK_COMPONENTS_ALL_[GROUPS_]IN_ONE_PACKAGE is set)"
|
|
<< std::endl);
|
|
|
|
// The ALL GROUPS in ONE package case
|
|
std::string localToplevel(initialTopLevel);
|
|
std::string packageFileName(cmSystemTools::GetParentDirectory(toplevel));
|
|
std::string outputFileName(
|
|
std::string(this->GetOption("CPACK_PACKAGE_FILE_NAME")) +
|
|
this->GetOutputExtension());
|
|
// all GROUP in one vs all COMPONENT in one
|
|
// if must be here otherwise non component paths have a trailing / while
|
|
// components don't
|
|
if (!compInstDirName.empty()) {
|
|
localToplevel += "/" + compInstDirName;
|
|
}
|
|
|
|
/* replace the TEMP DIRECTORY with the component one */
|
|
this->SetOption("CPACK_TEMPORARY_DIRECTORY", localToplevel.c_str());
|
|
packageFileName += "/" + outputFileName;
|
|
/* replace proposed CPACK_OUTPUT_FILE_NAME */
|
|
this->SetOption("CPACK_OUTPUT_FILE_NAME", outputFileName.c_str());
|
|
/* replace the TEMPORARY package file name */
|
|
this->SetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME",
|
|
packageFileName.c_str());
|
|
|
|
if (!compInstDirName.empty()) {
|
|
// Tell CPackDeb.cmake the path where the component is.
|
|
std::string component_path = "/";
|
|
component_path += compInstDirName;
|
|
this->SetOption("CPACK_DEB_PACKAGE_COMPONENT_PART_PATH",
|
|
component_path.c_str());
|
|
}
|
|
if (!this->ReadListFile("Internal/CPack/CPackDeb.cmake")) {
|
|
cmCPackLogger(cmCPackLog::LOG_ERROR,
|
|
"Error while execution CPackDeb.cmake" << std::endl);
|
|
retval = 0;
|
|
return retval;
|
|
}
|
|
|
|
cmsys::Glob gl;
|
|
std::string findExpr(this->GetOption("GEN_WDIR"));
|
|
findExpr += "/*";
|
|
gl.RecurseOn();
|
|
gl.SetRecurseListDirs(true);
|
|
if (!gl.FindFiles(findExpr)) {
|
|
cmCPackLogger(cmCPackLog::LOG_ERROR,
|
|
"Cannot find any files in the installed directory"
|
|
<< std::endl);
|
|
return 0;
|
|
}
|
|
packageFiles = gl.GetFiles();
|
|
|
|
int res = createDeb();
|
|
if (res != 1) {
|
|
retval = 0;
|
|
}
|
|
// add the generated package to package file names list
|
|
packageFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
|
|
packageFileName += "/";
|
|
packageFileName += this->GetOption("GEN_CPACK_OUTPUT_FILE_NAME");
|
|
packageFileNames.push_back(std::move(packageFileName));
|
|
return retval;
|
|
}
|
|
|
|
int cmCPackDebGenerator::PackageFiles()
|
|
{
|
|
/* Are we in the component packaging case */
|
|
if (WantsComponentInstallation()) {
|
|
// CASE 1 : COMPONENT ALL-IN-ONE package
|
|
// If ALL GROUPS or 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("ALL_COMPONENTS_IN_ONE");
|
|
}
|
|
// 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.
|
|
return PackageComponentsAllInOne("");
|
|
}
|
|
|
|
int cmCPackDebGenerator::createDeb()
|
|
{
|
|
// debian-binary file
|
|
const std::string strGenWDIR(this->GetOption("GEN_WDIR"));
|
|
const std::string dbfilename = strGenWDIR + "/debian-binary";
|
|
{ // the scope is needed for cmGeneratedFileStream
|
|
cmGeneratedFileStream out(dbfilename.c_str());
|
|
out << "2.0";
|
|
out << std::endl; // required for valid debian package
|
|
}
|
|
|
|
// control file
|
|
std::string ctlfilename = strGenWDIR + "/control";
|
|
|
|
// debian policy enforce lower case for package name
|
|
// mandatory entries:
|
|
std::string debian_pkg_name = cmsys::SystemTools::LowerCase(
|
|
this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_NAME"));
|
|
const char* debian_pkg_version =
|
|
this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_VERSION");
|
|
const char* debian_pkg_section =
|
|
this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SECTION");
|
|
const char* debian_pkg_priority =
|
|
this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_PRIORITY");
|
|
const char* debian_pkg_arch =
|
|
this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_ARCHITECTURE");
|
|
const char* maintainer =
|
|
this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_MAINTAINER");
|
|
const char* desc = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_DESCRIPTION");
|
|
|
|
// optional entries
|
|
const char* debian_pkg_dep =
|
|
this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_DEPENDS");
|
|
const char* debian_pkg_rec =
|
|
this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_RECOMMENDS");
|
|
const char* debian_pkg_sug =
|
|
this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SUGGESTS");
|
|
const char* debian_pkg_url =
|
|
this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_HOMEPAGE");
|
|
const char* debian_pkg_predep =
|
|
this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_PREDEPENDS");
|
|
const char* debian_pkg_enhances =
|
|
this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_ENHANCES");
|
|
const char* debian_pkg_breaks =
|
|
this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_BREAKS");
|
|
const char* debian_pkg_conflicts =
|
|
this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_CONFLICTS");
|
|
const char* debian_pkg_provides =
|
|
this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_PROVIDES");
|
|
const char* debian_pkg_replaces =
|
|
this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_REPLACES");
|
|
const char* debian_pkg_source =
|
|
this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SOURCE");
|
|
|
|
{ // the scope is needed for cmGeneratedFileStream
|
|
cmGeneratedFileStream out(ctlfilename.c_str());
|
|
out << "Package: " << debian_pkg_name << "\n";
|
|
out << "Version: " << debian_pkg_version << "\n";
|
|
out << "Section: " << debian_pkg_section << "\n";
|
|
out << "Priority: " << debian_pkg_priority << "\n";
|
|
out << "Architecture: " << debian_pkg_arch << "\n";
|
|
if (debian_pkg_source && *debian_pkg_source) {
|
|
out << "Source: " << debian_pkg_source << "\n";
|
|
}
|
|
if (debian_pkg_dep && *debian_pkg_dep) {
|
|
out << "Depends: " << debian_pkg_dep << "\n";
|
|
}
|
|
if (debian_pkg_rec && *debian_pkg_rec) {
|
|
out << "Recommends: " << debian_pkg_rec << "\n";
|
|
}
|
|
if (debian_pkg_sug && *debian_pkg_sug) {
|
|
out << "Suggests: " << debian_pkg_sug << "\n";
|
|
}
|
|
if (debian_pkg_url && *debian_pkg_url) {
|
|
out << "Homepage: " << debian_pkg_url << "\n";
|
|
}
|
|
if (debian_pkg_predep && *debian_pkg_predep) {
|
|
out << "Pre-Depends: " << debian_pkg_predep << "\n";
|
|
}
|
|
if (debian_pkg_enhances && *debian_pkg_enhances) {
|
|
out << "Enhances: " << debian_pkg_enhances << "\n";
|
|
}
|
|
if (debian_pkg_breaks && *debian_pkg_breaks) {
|
|
out << "Breaks: " << debian_pkg_breaks << "\n";
|
|
}
|
|
if (debian_pkg_conflicts && *debian_pkg_conflicts) {
|
|
out << "Conflicts: " << debian_pkg_conflicts << "\n";
|
|
}
|
|
if (debian_pkg_provides && *debian_pkg_provides) {
|
|
out << "Provides: " << debian_pkg_provides << "\n";
|
|
}
|
|
if (debian_pkg_replaces && *debian_pkg_replaces) {
|
|
out << "Replaces: " << debian_pkg_replaces << "\n";
|
|
}
|
|
unsigned long totalSize = 0;
|
|
{
|
|
std::string dirName = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
|
|
dirName += '/';
|
|
for (std::string const& file : packageFiles) {
|
|
totalSize += cmSystemTools::FileLength(file);
|
|
}
|
|
}
|
|
out << "Installed-Size: " << (totalSize + 1023) / 1024 << "\n";
|
|
out << "Maintainer: " << maintainer << "\n";
|
|
out << "Description: " << desc << "\n";
|
|
out << std::endl;
|
|
}
|
|
|
|
const std::string shlibsfilename = strGenWDIR + "/shlibs";
|
|
|
|
const char* debian_pkg_shlibs =
|
|
this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SHLIBS");
|
|
const bool gen_shibs = this->IsOn("CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS") &&
|
|
debian_pkg_shlibs && *debian_pkg_shlibs;
|
|
if (gen_shibs) {
|
|
cmGeneratedFileStream out(shlibsfilename.c_str());
|
|
out << debian_pkg_shlibs;
|
|
out << std::endl;
|
|
}
|
|
|
|
const std::string postinst = strGenWDIR + "/postinst";
|
|
const std::string postrm = strGenWDIR + "/postrm";
|
|
if (this->IsOn("GEN_CPACK_DEBIAN_GENERATE_POSTINST")) {
|
|
cmGeneratedFileStream out(postinst.c_str());
|
|
out << "#!/bin/sh\n\n"
|
|
"set -e\n\n"
|
|
"if [ \"$1\" = \"configure\" ]; then\n"
|
|
"\tldconfig\n"
|
|
"fi\n";
|
|
}
|
|
if (this->IsOn("GEN_CPACK_DEBIAN_GENERATE_POSTRM")) {
|
|
cmGeneratedFileStream out(postrm.c_str());
|
|
out << "#!/bin/sh\n\n"
|
|
"set -e\n\n"
|
|
"if [ \"$1\" = \"remove\" ]; then\n"
|
|
"\tldconfig\n"
|
|
"fi\n";
|
|
}
|
|
|
|
cmArchiveWrite::Compress tar_compression_type = cmArchiveWrite::CompressGZip;
|
|
const char* debian_compression_type =
|
|
this->GetOption("GEN_CPACK_DEBIAN_COMPRESSION_TYPE");
|
|
if (!debian_compression_type) {
|
|
debian_compression_type = "gzip";
|
|
}
|
|
|
|
std::string compression_suffix;
|
|
if (!strcmp(debian_compression_type, "lzma")) {
|
|
compression_suffix = ".lzma";
|
|
tar_compression_type = cmArchiveWrite::CompressLZMA;
|
|
} else if (!strcmp(debian_compression_type, "xz")) {
|
|
compression_suffix = ".xz";
|
|
tar_compression_type = cmArchiveWrite::CompressXZ;
|
|
} else if (!strcmp(debian_compression_type, "bzip2")) {
|
|
compression_suffix = ".bz2";
|
|
tar_compression_type = cmArchiveWrite::CompressBZip2;
|
|
} else if (!strcmp(debian_compression_type, "gzip")) {
|
|
compression_suffix = ".gz";
|
|
tar_compression_type = cmArchiveWrite::CompressGZip;
|
|
} else if (!strcmp(debian_compression_type, "none")) {
|
|
compression_suffix.clear();
|
|
tar_compression_type = cmArchiveWrite::CompressNone;
|
|
} else {
|
|
cmCPackLogger(cmCPackLog::LOG_ERROR,
|
|
"Error unrecognized compression type: "
|
|
<< debian_compression_type << std::endl);
|
|
}
|
|
|
|
const char* debian_archive_type =
|
|
this->GetOption("GEN_CPACK_DEBIAN_ARCHIVE_TYPE");
|
|
if (!debian_archive_type) {
|
|
debian_archive_type = "paxr";
|
|
}
|
|
|
|
std::string filename_data_tar =
|
|
strGenWDIR + "/data.tar" + compression_suffix;
|
|
|
|
// atomic file generation for data.tar
|
|
{
|
|
cmGeneratedFileStream fileStream_data_tar;
|
|
fileStream_data_tar.Open(filename_data_tar.c_str(), false, true);
|
|
if (!fileStream_data_tar) {
|
|
cmCPackLogger(cmCPackLog::LOG_ERROR,
|
|
"Error opening the file \""
|
|
<< filename_data_tar << "\" for writing" << std::endl);
|
|
return 0;
|
|
}
|
|
cmArchiveWrite data_tar(fileStream_data_tar, tar_compression_type,
|
|
debian_archive_type);
|
|
|
|
// uid/gid should be the one of the root user, and this root user has
|
|
// always uid/gid equal to 0.
|
|
data_tar.SetUIDAndGID(0u, 0u);
|
|
data_tar.SetUNAMEAndGNAME("root", "root");
|
|
|
|
// now add all directories which have to be compressed
|
|
// collect all top level install dirs for that
|
|
// e.g. /opt/bin/foo, /usr/bin/bar and /usr/bin/baz would
|
|
// give /usr and /opt
|
|
size_t topLevelLength = strGenWDIR.length();
|
|
cmCPackLogger(cmCPackLog::LOG_DEBUG,
|
|
"WDIR: \"" << strGenWDIR << "\", length = " << topLevelLength
|
|
<< std::endl);
|
|
std::set<std::string> orderedFiles;
|
|
|
|
// we have to reconstruct the parent folders as well
|
|
|
|
for (std::string currentPath : packageFiles) {
|
|
while (currentPath != strGenWDIR) {
|
|
// the last one IS strGenWDIR, but we do not want this one:
|
|
// XXX/application/usr/bin/myprogram with GEN_WDIR=XXX/application
|
|
// should not add XXX/application
|
|
orderedFiles.insert(currentPath);
|
|
currentPath = cmSystemTools::CollapseCombinedPath(currentPath, "..");
|
|
}
|
|
}
|
|
|
|
for (std::string const& file : orderedFiles) {
|
|
cmCPackLogger(cmCPackLog::LOG_DEBUG,
|
|
"FILEIT: \"" << file << "\"" << std::endl);
|
|
std::string::size_type slashPos = file.find('/', topLevelLength + 1);
|
|
std::string relativeDir =
|
|
file.substr(topLevelLength, slashPos - topLevelLength);
|
|
cmCPackLogger(cmCPackLog::LOG_DEBUG,
|
|
"RELATIVEDIR: \"" << relativeDir << "\"" << std::endl);
|
|
|
|
#ifdef WIN32
|
|
std::string mode_t_adt_filename = file + ":cmake_mode_t";
|
|
cmsys::ifstream permissionStream(mode_t_adt_filename.c_str());
|
|
|
|
mode_t permissions = 0;
|
|
|
|
if (permissionStream) {
|
|
permissionStream >> std::oct >> permissions;
|
|
}
|
|
|
|
if (permissions != 0) {
|
|
data_tar.SetPermissions(permissions);
|
|
} else if (cmSystemTools::FileIsDirectory(file)) {
|
|
data_tar.SetPermissions(0755);
|
|
} else {
|
|
data_tar.ClearPermissions();
|
|
}
|
|
#endif
|
|
|
|
// do not recurse because the loop will do it
|
|
if (!data_tar.Add(file, topLevelLength, ".", false)) {
|
|
cmCPackLogger(cmCPackLog::LOG_ERROR,
|
|
"Problem adding file to tar:"
|
|
<< std::endl
|
|
<< "#top level directory: " << strGenWDIR << std::endl
|
|
<< "#file: " << file << std::endl
|
|
<< "#error:" << data_tar.GetError() << std::endl);
|
|
return 0;
|
|
}
|
|
}
|
|
} // scope for file generation
|
|
|
|
std::string md5filename = strGenWDIR + "/md5sums";
|
|
{
|
|
// the scope is needed for cmGeneratedFileStream
|
|
cmGeneratedFileStream out(md5filename.c_str());
|
|
|
|
std::string topLevelWithTrailingSlash =
|
|
this->GetOption("CPACK_TEMPORARY_DIRECTORY");
|
|
topLevelWithTrailingSlash += '/';
|
|
for (std::string const& file : packageFiles) {
|
|
// hash only regular files
|
|
if (cmSystemTools::FileIsDirectory(file) ||
|
|
cmSystemTools::FileIsSymlink(file)) {
|
|
continue;
|
|
}
|
|
|
|
std::string output =
|
|
cmSystemTools::ComputeFileHash(file, cmCryptoHash::AlgoMD5);
|
|
if (output.empty()) {
|
|
cmCPackLogger(cmCPackLog::LOG_ERROR,
|
|
"Problem computing the md5 of " << file << std::endl);
|
|
}
|
|
|
|
output += " " + file + "\n";
|
|
// debian md5sums entries are like this:
|
|
// 014f3604694729f3bf19263bac599765 usr/bin/ccmake
|
|
// thus strip the full path (with the trailing slash)
|
|
cmSystemTools::ReplaceString(output, topLevelWithTrailingSlash.c_str(),
|
|
"");
|
|
out << output;
|
|
}
|
|
// each line contains a eol.
|
|
// Do not end the md5sum file with yet another (invalid)
|
|
}
|
|
|
|
std::string filename_control_tar = strGenWDIR + "/control.tar.gz";
|
|
// atomic file generation for control.tar
|
|
{
|
|
cmGeneratedFileStream fileStream_control_tar;
|
|
fileStream_control_tar.Open(filename_control_tar.c_str(), false, true);
|
|
if (!fileStream_control_tar) {
|
|
cmCPackLogger(cmCPackLog::LOG_ERROR,
|
|
"Error opening the file \"" << filename_control_tar
|
|
<< "\" for writing"
|
|
<< std::endl);
|
|
return 0;
|
|
}
|
|
cmArchiveWrite control_tar(fileStream_control_tar,
|
|
cmArchiveWrite::CompressGZip,
|
|
debian_archive_type);
|
|
|
|
// sets permissions and uid/gid for the files
|
|
control_tar.SetUIDAndGID(0u, 0u);
|
|
control_tar.SetUNAMEAndGNAME("root", "root");
|
|
|
|
/* permissions are set according to
|
|
https://www.debian.org/doc/debian-policy/ch-files.html#s-permissions-owners
|
|
and
|
|
https://lintian.debian.org/tags/control-file-has-bad-permissions.html
|
|
*/
|
|
const mode_t permission644 = 0644;
|
|
const mode_t permissionExecute = 0111;
|
|
const mode_t permission755 = permission644 | permissionExecute;
|
|
|
|
// for md5sum and control (that we have generated here), we use 644
|
|
// (RW-R--R--)
|
|
// so that deb lintian doesn't warn about it
|
|
control_tar.SetPermissions(permission644);
|
|
|
|
// adds control and md5sums
|
|
if (!control_tar.Add(md5filename, strGenWDIR.length(), ".") ||
|
|
!control_tar.Add(strGenWDIR + "/control", strGenWDIR.length(), ".")) {
|
|
cmCPackLogger(cmCPackLog::LOG_ERROR,
|
|
"Error adding file to tar:"
|
|
<< std::endl
|
|
<< "#top level directory: " << strGenWDIR << std::endl
|
|
<< "#file: \"control\" or \"md5sums\"" << std::endl
|
|
<< "#error:" << control_tar.GetError() << std::endl);
|
|
return 0;
|
|
}
|
|
|
|
// adds generated shlibs file
|
|
if (gen_shibs) {
|
|
if (!control_tar.Add(shlibsfilename, strGenWDIR.length(), ".")) {
|
|
cmCPackLogger(cmCPackLog::LOG_ERROR,
|
|
"Error adding file to tar:"
|
|
<< std::endl
|
|
<< "#top level directory: " << strGenWDIR << std::endl
|
|
<< "#file: \"shlibs\"" << std::endl
|
|
<< "#error:" << control_tar.GetError() << std::endl);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// adds LDCONFIG related files
|
|
if (this->IsOn("GEN_CPACK_DEBIAN_GENERATE_POSTINST")) {
|
|
control_tar.SetPermissions(permission755);
|
|
if (!control_tar.Add(postinst, strGenWDIR.length(), ".")) {
|
|
cmCPackLogger(cmCPackLog::LOG_ERROR,
|
|
"Error adding file to tar:"
|
|
<< std::endl
|
|
<< "#top level directory: " << strGenWDIR << std::endl
|
|
<< "#file: \"postinst\"" << std::endl
|
|
<< "#error:" << control_tar.GetError() << std::endl);
|
|
return 0;
|
|
}
|
|
control_tar.SetPermissions(permission644);
|
|
}
|
|
|
|
if (this->IsOn("GEN_CPACK_DEBIAN_GENERATE_POSTRM")) {
|
|
control_tar.SetPermissions(permission755);
|
|
if (!control_tar.Add(postrm, strGenWDIR.length(), ".")) {
|
|
cmCPackLogger(cmCPackLog::LOG_ERROR,
|
|
"Error adding file to tar:"
|
|
<< std::endl
|
|
<< "#top level directory: " << strGenWDIR << std::endl
|
|
<< "#file: \"postinst\"" << std::endl
|
|
<< "#error:" << control_tar.GetError() << std::endl);
|
|
return 0;
|
|
}
|
|
control_tar.SetPermissions(permission644);
|
|
}
|
|
|
|
// for the other files, we use
|
|
// -either the original permission on the files
|
|
// -either a permission strictly defined by the Debian policies
|
|
const char* controlExtra =
|
|
this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA");
|
|
if (controlExtra) {
|
|
// permissions are now controlled by the original file permissions
|
|
|
|
const bool permissionStrictPolicy =
|
|
this->IsSet("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION");
|
|
|
|
static const char* strictFiles[] = { "config", "postinst", "postrm",
|
|
"preinst", "prerm" };
|
|
std::set<std::string> setStrictFiles(
|
|
strictFiles,
|
|
strictFiles + sizeof(strictFiles) / sizeof(strictFiles[0]));
|
|
|
|
// default
|
|
control_tar.ClearPermissions();
|
|
|
|
std::vector<std::string> controlExtraList;
|
|
cmSystemTools::ExpandListArgument(controlExtra, controlExtraList);
|
|
for (std::string const& i : controlExtraList) {
|
|
std::string filenamename = cmsys::SystemTools::GetFilenameName(i);
|
|
std::string localcopy = strGenWDIR + "/" + filenamename;
|
|
|
|
if (permissionStrictPolicy) {
|
|
control_tar.SetPermissions(setStrictFiles.count(filenamename)
|
|
? permission755
|
|
: permission644);
|
|
}
|
|
|
|
// if we can copy the file, it means it does exist, let's add it:
|
|
if (cmsys::SystemTools::CopyFileIfDifferent(i, localcopy)) {
|
|
control_tar.Add(localcopy, strGenWDIR.length(), ".");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ar -r your-package-name.deb debian-binary control.tar.* data.tar.*
|
|
// A debian package .deb is simply an 'ar' archive. The only subtle
|
|
// difference is that debian uses the BSD ar style archive whereas most
|
|
// Linux distro have a GNU ar.
|
|
// See http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=161593 for more info
|
|
std::string const outputDir = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
|
|
std::string const outputName = this->GetOption("GEN_CPACK_OUTPUT_FILE_NAME");
|
|
std::string const outputPath = outputDir + "/" + outputName;
|
|
std::string const tlDir = strGenWDIR + "/";
|
|
cmGeneratedFileStream debStream;
|
|
debStream.Open(outputPath.c_str(), false, true);
|
|
cmArchiveWrite deb(debStream, cmArchiveWrite::CompressNone, "arbsd");
|
|
if (!deb.Add(tlDir + "debian-binary", tlDir.length()) ||
|
|
!deb.Add(tlDir + "control.tar.gz", tlDir.length()) ||
|
|
!deb.Add(tlDir + "data.tar" + compression_suffix, tlDir.length())) {
|
|
cmCPackLogger(cmCPackLog::LOG_ERROR,
|
|
"Error creating debian package:"
|
|
<< std::endl
|
|
<< "#top level directory: " << outputDir << std::endl
|
|
<< "#file: " << outputName << std::endl
|
|
<< "#error:" << deb.GetError() << std::endl);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
bool cmCPackDebGenerator::SupportsComponentInstallation() const
|
|
{
|
|
return IsOn("CPACK_DEB_COMPONENT_INSTALL");
|
|
}
|
|
|
|
std::string cmCPackDebGenerator::GetComponentInstallDirNameSuffix(
|
|
const std::string& componentName)
|
|
{
|
|
if (componentPackageMethod == ONE_PACKAGE_PER_COMPONENT) {
|
|
return componentName;
|
|
}
|
|
|
|
if (componentPackageMethod == ONE_PACKAGE) {
|
|
return std::string("ALL_COMPONENTS_IN_ONE");
|
|
}
|
|
// We have to find the name of the COMPONENT GROUP
|
|
// the current COMPONENT belongs to.
|
|
std::string groupVar =
|
|
"CPACK_COMPONENT_" + cmSystemTools::UpperCase(componentName) + "_GROUP";
|
|
if (nullptr != GetOption(groupVar)) {
|
|
return std::string(GetOption(groupVar));
|
|
}
|
|
return componentName;
|
|
}
|