Merge topic 'cpack-deb-fakeroot-removal'

becb14c9 CPack/DEB: test preserve extra config file permissions
7044e8ee CPackDeb: use of libarchive and removal of fakeroot
415405a3 cmArchiveWrite: control user/group, permissions and recursive file adding
4f2ff601 Tests: Make RunCMake.CPack error messages more readable
81b748ae cmGeneratedFileStream: Fix spelling in comment
This commit is contained in:
Brad King 2015-09-17 15:34:02 -04:00 committed by CMake Topic Stage
commit eb154697c0
12 changed files with 422 additions and 141 deletions

View File

@ -0,0 +1,4 @@
cpack-deb-fakeroot-removal
--------------------------
* :module:`CPackDeb` no longer uses fakeroot and system tar program for packaging.

View File

@ -326,6 +326,34 @@
# #
# set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA # set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA
# "${CMAKE_CURRENT_SOURCE_DIR/prerm;${CMAKE_CURRENT_SOURCE_DIR}/postrm") # "${CMAKE_CURRENT_SOURCE_DIR/prerm;${CMAKE_CURRENT_SOURCE_DIR}/postrm")
#
# .. note::
#
# The original permissions of the files will be used in the final
# package unless the variable
# :variable:`CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION` is set.
# In particular, the scripts should have the proper executable
# flag prior to the generation of the package.
#
# .. variable:: CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION
# CPACK_DEBIAN_<COMPONENT>_PACKAGE_CONTROL_STRICT_PERMISSION
#
# This variable indicates if the Debian policy on control files should be
# strictly followed.
#
# * Mandatory : NO
# * Default : FALSE
#
# Usage::
#
# set(CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION TRUE)
#
# .. note::
#
# This overrides the permissions on the original files, following the rules
# set by Debian policy
# https://www.debian.org/doc/debian-policy/ch-files.html#s-permissions-owners
#
#============================================================================= #=============================================================================
@ -362,11 +390,6 @@ function(cpack_deb_prepare_package_vars)
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS OFF) set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS OFF)
endif() endif()
find_program(FAKEROOT_EXECUTABLE fakeroot)
if(FAKEROOT_EXECUTABLE)
set(CPACK_DEBIAN_FAKEROOT_EXECUTABLE ${FAKEROOT_EXECUTABLE})
endif()
set(WDIR "${CPACK_TOPLEVEL_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}${CPACK_DEB_PACKAGE_COMPONENT_PART_PATH}") set(WDIR "${CPACK_TOPLEVEL_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}${CPACK_DEB_PACKAGE_COMPONENT_PART_PATH}")
# per component automatic discover: some of the component might not have # per component automatic discover: some of the component might not have
@ -635,7 +658,7 @@ function(cpack_deb_prepare_package_vars)
# Are we packaging components ? # Are we packaging components ?
if(CPACK_DEB_PACKAGE_COMPONENT) if(CPACK_DEB_PACKAGE_COMPONENT)
# override values with per component version if set # override values with per component version if set
foreach(VAR_NAME_ "PACKAGE_CONTROL_EXTRA") foreach(VAR_NAME_ "PACKAGE_CONTROL_EXTRA" "PACKAGE_CONTROL_STRICT_PERMISSION")
if(CPACK_DEBIAN_${_local_component_name}_${VAR_NAME_}) if(CPACK_DEBIAN_${_local_component_name}_${VAR_NAME_})
set(CPACK_DEBIAN_${VAR_NAME_} "${CPACK_DEBIAN_${_local_component_name}_${VAR_NAME_}}") set(CPACK_DEBIAN_${VAR_NAME_} "${CPACK_DEBIAN_${_local_component_name}_${VAR_NAME_}}")
endif() endif()
@ -657,6 +680,7 @@ function(cpack_deb_prepare_package_vars)
message("CPackDeb:Debug: CPACK_PACKAGE_FILE_NAME = ${CPACK_PACKAGE_FILE_NAME}") message("CPackDeb:Debug: CPACK_PACKAGE_FILE_NAME = ${CPACK_PACKAGE_FILE_NAME}")
message("CPackDeb:Debug: CPACK_PACKAGE_INSTALL_DIRECTORY = ${CPACK_PACKAGE_INSTALL_DIRECTORY}") message("CPackDeb:Debug: CPACK_PACKAGE_INSTALL_DIRECTORY = ${CPACK_PACKAGE_INSTALL_DIRECTORY}")
message("CPackDeb:Debug: CPACK_TEMPORARY_PACKAGE_FILE_NAME = ${CPACK_TEMPORARY_PACKAGE_FILE_NAME}") message("CPackDeb:Debug: CPACK_TEMPORARY_PACKAGE_FILE_NAME = ${CPACK_TEMPORARY_PACKAGE_FILE_NAME}")
message("CPackDeb:Debug: CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION = ${CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION}")
endif() endif()
# For debian source packages: # For debian source packages:
@ -682,7 +706,6 @@ function(cpack_deb_prepare_package_vars)
set(GEN_CPACK_DEBIAN_PACKAGE_MAINTAINER "${CPACK_DEBIAN_PACKAGE_MAINTAINER}" PARENT_SCOPE) set(GEN_CPACK_DEBIAN_PACKAGE_MAINTAINER "${CPACK_DEBIAN_PACKAGE_MAINTAINER}" PARENT_SCOPE)
set(GEN_CPACK_DEBIAN_PACKAGE_DESCRIPTION "${CPACK_DEBIAN_PACKAGE_DESCRIPTION}" PARENT_SCOPE) set(GEN_CPACK_DEBIAN_PACKAGE_DESCRIPTION "${CPACK_DEBIAN_PACKAGE_DESCRIPTION}" PARENT_SCOPE)
set(GEN_CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS}" PARENT_SCOPE) set(GEN_CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS}" PARENT_SCOPE)
set(GEN_CPACK_DEBIAN_FAKEROOT_EXECUTABLE "${CPACK_DEBIAN_FAKEROOT_EXECUTABLE}" PARENT_SCOPE)
set(GEN_CPACK_DEBIAN_COMPRESSION_TYPE "${CPACK_DEBIAN_COMPRESSION_TYPE}" PARENT_SCOPE) set(GEN_CPACK_DEBIAN_COMPRESSION_TYPE "${CPACK_DEBIAN_COMPRESSION_TYPE}" PARENT_SCOPE)
set(GEN_CPACK_DEBIAN_PACKAGE_RECOMMENDS "${CPACK_DEBIAN_PACKAGE_RECOMMENDS}" PARENT_SCOPE) set(GEN_CPACK_DEBIAN_PACKAGE_RECOMMENDS "${CPACK_DEBIAN_PACKAGE_RECOMMENDS}" PARENT_SCOPE)
set(GEN_CPACK_DEBIAN_PACKAGE_SUGGESTS "${CPACK_DEBIAN_PACKAGE_SUGGESTS}" PARENT_SCOPE) set(GEN_CPACK_DEBIAN_PACKAGE_SUGGESTS "${CPACK_DEBIAN_PACKAGE_SUGGESTS}" PARENT_SCOPE)
@ -694,6 +717,8 @@ function(cpack_deb_prepare_package_vars)
set(GEN_CPACK_DEBIAN_PACKAGE_PROVIDES "${CPACK_DEBIAN_PACKAGE_PROVIDES}" PARENT_SCOPE) set(GEN_CPACK_DEBIAN_PACKAGE_PROVIDES "${CPACK_DEBIAN_PACKAGE_PROVIDES}" PARENT_SCOPE)
set(GEN_CPACK_DEBIAN_PACKAGE_REPLACES "${CPACK_DEBIAN_PACKAGE_REPLACES}" PARENT_SCOPE) set(GEN_CPACK_DEBIAN_PACKAGE_REPLACES "${CPACK_DEBIAN_PACKAGE_REPLACES}" PARENT_SCOPE)
set(GEN_CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA}" PARENT_SCOPE) set(GEN_CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA}" PARENT_SCOPE)
set(GEN_CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION
"${CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION}" PARENT_SCOPE)
set(GEN_WDIR "${WDIR}" PARENT_SCOPE) set(GEN_WDIR "${WDIR}" PARENT_SCOPE)
endfunction() endfunction()

View File

@ -15,6 +15,7 @@
#include "cmMakefile.h" #include "cmMakefile.h"
#include "cmGeneratedFileStream.h" #include "cmGeneratedFileStream.h"
#include "cmCPackLog.h" #include "cmCPackLog.h"
#include "cmArchiveWrite.h"
#include <cmsys/SystemTools.hxx> #include <cmsys/SystemTools.hxx>
#include <cmsys/Glob.hxx> #include <cmsys/Glob.hxx>
@ -388,9 +389,9 @@ int cmCPackDebGenerator::createDeb()
{ {
std::string dirName = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); std::string dirName = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
dirName += '/'; dirName += '/';
for (std::vector<std::string>::const_iterator fileIt = for (std::vector<std::string>::const_iterator fileIt =
packageFiles.begin(); packageFiles.begin();
fileIt != packageFiles.end(); ++ fileIt ) fileIt != packageFiles.end(); ++ fileIt )
{ {
totalSize += cmSystemTools::FileLength(*fileIt); totalSize += cmSystemTools::FileLength(*fileIt);
} }
@ -401,8 +402,9 @@ int cmCPackDebGenerator::createDeb()
out << std::endl; out << std::endl;
} }
std::string cmd(this->GetOption("GEN_CPACK_DEBIAN_FAKEROOT_EXECUTABLE")); const std::string strGenWDIR(this->GetOption("GEN_WDIR"));
cmArchiveWrite::Compress tar_compression_type = cmArchiveWrite::CompressGZip;
const char* debian_compression_type = const char* debian_compression_type =
this->GetOption("GEN_CPACK_DEBIAN_COMPRESSION_TYPE"); this->GetOption("GEN_CPACK_DEBIAN_COMPRESSION_TYPE");
if(!debian_compression_type) if(!debian_compression_type)
@ -410,102 +412,127 @@ int cmCPackDebGenerator::createDeb()
debian_compression_type = "gzip"; debian_compression_type = "gzip";
} }
std::string cmake_tar = " ", compression_modifier = "a", compression_suffix; std::string compression_suffix;
if(!strcmp(debian_compression_type, "lzma")) { if(!strcmp(debian_compression_type, "lzma")) {
compression_suffix = ".lzma"; compression_suffix = ".lzma";
tar_compression_type = cmArchiveWrite::CompressLZMA;
} else if(!strcmp(debian_compression_type, "xz")) { } else if(!strcmp(debian_compression_type, "xz")) {
compression_suffix = ".xz"; compression_suffix = ".xz";
tar_compression_type = cmArchiveWrite::CompressXZ;
} else if(!strcmp(debian_compression_type, "bzip2")) { } else if(!strcmp(debian_compression_type, "bzip2")) {
compression_suffix = ".bz2"; compression_suffix = ".bz2";
compression_modifier = "j"; tar_compression_type = cmArchiveWrite::CompressBZip2;
cmake_tar += "\"" + cmSystemTools::GetCMakeCommand() + "\" -E ";
} else if(!strcmp(debian_compression_type, "gzip")) { } else if(!strcmp(debian_compression_type, "gzip")) {
compression_suffix = ".gz"; compression_suffix = ".gz";
compression_modifier = "z"; tar_compression_type = cmArchiveWrite::CompressGZip;
cmake_tar += "\"" + cmSystemTools::GetCMakeCommand() + "\" -E ";
} else if(!strcmp(debian_compression_type, "none")) { } else if(!strcmp(debian_compression_type, "none")) {
compression_suffix = ""; compression_suffix = "";
compression_modifier = ""; tar_compression_type = cmArchiveWrite::CompressNone;
cmake_tar += "\"" + cmSystemTools::GetCMakeCommand() + "\" -E ";
} else { } else {
cmCPackLogger(cmCPackLog::LOG_ERROR, cmCPackLogger(cmCPackLog::LOG_ERROR,
"Error unrecognized compression type: " "Error unrecognized compression type: "
<< debian_compression_type << std::endl); << debian_compression_type << std::endl);
} }
cmd += cmake_tar + "tar c" + compression_modifier + "f data.tar"
+ compression_suffix;
// now add all directories which have to be compressed std::string filename_data_tar = strGenWDIR
// collect all top level install dirs for that + "/data.tar" + compression_suffix;
// e.g. /opt/bin/foo, /usr/bin/bar and /usr/bin/baz would give /usr and /opt
size_t topLevelLength = std::string(this->GetOption("GEN_WDIR")).length(); // atomic file generation for data.tar
cmCPackLogger(cmCPackLog::LOG_DEBUG, "WDIR: \"" {
<< this->GetOption("GEN_WDIR") cmGeneratedFileStream fileStream_data_tar;
<< "\", length = " << topLevelLength fileStream_data_tar.Open(filename_data_tar.c_str(), false, true);
<< std::endl); if(!fileStream_data_tar)
std::set<std::string> installDirs;
for (std::vector<std::string>::const_iterator fileIt =
packageFiles.begin();
fileIt != packageFiles.end(); ++ fileIt )
{
cmCPackLogger(cmCPackLog::LOG_DEBUG, "FILEIT: \"" << *fileIt << "\""
<< std::endl);
std::string::size_type slashPos = fileIt->find('/', topLevelLength+1);
std::string relativeDir = fileIt->substr(topLevelLength,
slashPos - topLevelLength);
cmCPackLogger(cmCPackLog::LOG_DEBUG, "RELATIVEDIR: \"" << relativeDir
<< "\"" << std::endl);
if (installDirs.find(relativeDir) == installDirs.end())
{ {
installDirs.insert(relativeDir); cmCPackLogger(cmCPackLog::LOG_ERROR,
cmd += " ."; "Error opening the file \"" << filename_data_tar << "\" for writing"
cmd += relativeDir; << std::endl);
return 0;
} }
} cmArchiveWrite data_tar(fileStream_data_tar, tar_compression_type, "paxr");
std::string output; // uid/gid should be the one of the root user, and this root user has
int retval = -1; // always uid/gid equal to 0.
int res = cmSystemTools::RunSingleCommand(cmd.c_str(), &output, &output, data_tar.SetUIDAndGID(0u, 0u);
&retval, this->GetOption("GEN_WDIR"), this->GeneratorVerbose, 0); data_tar.SetUNAMEAndGNAME("root", "root");
if ( !res || retval ) // now add all directories which have to be compressed
{ // collect all top level install dirs for that
std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); // e.g. /opt/bin/foo, /usr/bin/bar and /usr/bin/baz would
tmpFile += "/Deb.log"; // give /usr and /opt
cmGeneratedFileStream ofs(tmpFile.c_str()); size_t topLevelLength = strGenWDIR.length();
ofs << "# Run command: " << cmd << std::endl cmCPackLogger(cmCPackLog::LOG_DEBUG, "WDIR: \""
<< "# Working directory: " << toplevel << std::endl << strGenWDIR
<< "# Output:" << std::endl << "\", length = " << topLevelLength
<< output << std::endl; << std::endl);
cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running tar command: " std::set<std::string> orderedFiles;
<< cmd << std::endl
<< "Please check " << tmpFile << " for errors" << std::endl);
return 0;
}
std::string md5filename; // we have to reconstruct the parent folders as well
md5filename = this->GetOption("GEN_WDIR");
md5filename += "/md5sums";
{ // the scope is needed for cmGeneratedFileStream for (std::vector<std::string>::const_iterator fileIt =
packageFiles.begin();
fileIt != packageFiles.end(); ++ fileIt )
{
std::string currentPath = *fileIt;
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::set<std::string>::const_iterator fileIt =
orderedFiles.begin();
fileIt != orderedFiles.end(); ++ fileIt )
{
cmCPackLogger(cmCPackLog::LOG_DEBUG, "FILEIT: \"" << *fileIt << "\""
<< std::endl);
std::string::size_type slashPos = fileIt->find('/', topLevelLength+1);
std::string relativeDir = fileIt->substr(topLevelLength,
slashPos - topLevelLength);
cmCPackLogger(cmCPackLog::LOG_DEBUG, "RELATIVEDIR: \"" << relativeDir
<< "\"" << std::endl);
// do not recurse because the loop will do it
if(!data_tar.Add(*fileIt, topLevelLength, ".", false))
{
cmCPackLogger(cmCPackLog::LOG_ERROR,
"Problem adding file to tar:" << std::endl
<< "#top level directory: "
<< strGenWDIR << std::endl
<< "#file: " << *fileIt << 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()); cmGeneratedFileStream out(md5filename.c_str());
std::vector<std::string>::const_iterator fileIt;
// std::string topLevelWithTrailingSlash = toplevel;
std::string topLevelWithTrailingSlash = std::string topLevelWithTrailingSlash =
this->GetOption("CPACK_TEMPORARY_DIRECTORY"); this->GetOption("CPACK_TEMPORARY_DIRECTORY");
topLevelWithTrailingSlash += '/'; topLevelWithTrailingSlash += '/';
for ( fileIt = packageFiles.begin(); for (std::vector<std::string>::const_iterator fileIt =
fileIt != packageFiles.end(); ++ fileIt ) packageFiles.begin();
fileIt != packageFiles.end(); ++ fileIt )
{ {
cmd = "\""; std::string cmd = "\"";
cmd += cmSystemTools::GetCMakeCommand(); cmd += cmSystemTools::GetCMakeCommand();
cmd += "\" -E md5sum \""; cmd += "\" -E md5sum \"";
cmd += *fileIt; cmd += *fileIt;
cmd += "\""; cmd += "\"";
//std::string output;
//int retVal = -1; std::string output;
res = cmSystemTools::RunSingleCommand(cmd.c_str(), &output, &output, int retval = -1;
int res = cmSystemTools::RunSingleCommand(cmd.c_str(), &output, &output,
&retval, toplevel.c_str(), this->GeneratorVerbose, 0); &retval, toplevel.c_str(), this->GeneratorVerbose, 0);
if ( !res || retval ) if ( !res || retval )
{ {
@ -521,70 +548,116 @@ int cmCPackDebGenerator::createDeb()
} }
// each line contains a eol. // each line contains a eol.
// Do not end the md5sum file with yet another (invalid) // Do not end the md5sum file with yet another (invalid)
} }
// set md5sum file permissins to RW-R--R-- so that deb lintian doesn't warn
// about it
cmSystemTools::SetPermissions(md5filename.c_str(),
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
cmd = this->GetOption("GEN_CPACK_DEBIAN_FAKEROOT_EXECUTABLE");
cmd += cmake_tar + "tar czf control.tar.gz ./control ./md5sums"; 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,
tar_compression_type,
"paxr");
// 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 = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
const mode_t permissionExecute = S_IXUSR | S_IXGRP | S_IXOTH;
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;
}
// 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 = const char* controlExtra =
this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA"); this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA");
if( controlExtra ) if( controlExtra )
{
std::vector<std::string> controlExtraList;
cmSystemTools::ExpandListArgument(controlExtra, controlExtraList);
for(std::vector<std::string>::iterator i =
controlExtraList.begin(); i != controlExtraList.end(); ++i)
{ {
std::string filenamename = // permissions are now controlled by the original file permissions
cmsys::SystemTools::GetFilenameName(*i);
std::string localcopy = this->GetOption("GEN_WDIR"); const bool permissionStrictPolicy =
localcopy += "/"; this->IsSet("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION");
localcopy += filenamename;
// if we can copy the file, it means it does exist, let's add it: static const char* strictFiles[] = {
if( cmsys::SystemTools::CopyFileIfDifferent( "config", "postinst", "postrm", "preinst", "prerm"
*i, localcopy) ) };
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::vector<std::string>::iterator i = controlExtraList.begin();
i != controlExtraList.end(); ++i)
{ {
// debian is picky and need relative to ./ path in the tar.* std::string filenamename =
cmd += " ./"; cmsys::SystemTools::GetFilenameName(*i);
cmd += filenamename; 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(), ".");
}
} }
} }
} }
res = cmSystemTools::RunSingleCommand(cmd.c_str(), &output, &output,
&retval, this->GetOption("GEN_WDIR"), this->GeneratorVerbose, 0);
if ( !res || retval )
{
std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
tmpFile += "/Deb.log";
cmGeneratedFileStream ofs(tmpFile.c_str());
ofs << "# Run command: " << cmd << std::endl
<< "# Working directory: " << toplevel << std::endl
<< "# Output:" << std::endl
<< output << std::endl;
cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running tar command: "
<< cmd << std::endl
<< "Please check " << tmpFile << " for errors" << std::endl);
return 0;
}
// ar -r your-package-name.deb debian-binary control.tar.* data.tar.* // ar -r your-package-name.deb debian-binary control.tar.* data.tar.*
// since debian packages require BSD ar (most Linux distros and even // since debian packages require BSD ar (most Linux distros and even
// FreeBSD and NetBSD ship GNU ar) we use a copy of OpenBSD ar here. // FreeBSD and NetBSD ship GNU ar) we use a copy of OpenBSD ar here.
std::vector<std::string> arFiles; std::vector<std::string> arFiles;
std::string topLevelString = this->GetOption("GEN_WDIR"); std::string topLevelString = strGenWDIR + "/";
topLevelString += "/";
arFiles.push_back(topLevelString + "debian-binary"); arFiles.push_back(topLevelString + "debian-binary");
arFiles.push_back(topLevelString + "control.tar.gz"); arFiles.push_back(topLevelString + "control.tar.gz");
arFiles.push_back(topLevelString + "data.tar" + compression_suffix); arFiles.push_back(topLevelString + "data.tar" + compression_suffix);
std::string outputFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); std::string outputFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
outputFileName += "/"; outputFileName += "/";
outputFileName += this->GetOption("CPACK_OUTPUT_FILE_NAME"); outputFileName += this->GetOption("CPACK_OUTPUT_FILE_NAME");
res = ar_append(outputFileName.c_str(), arFiles); int res = ar_append(outputFileName.c_str(), arFiles);
if ( res!=0 ) if ( res!=0 )
{ {
std::string tmpFile = this->GetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME"); std::string tmpFile = this->GetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME");

View File

@ -181,7 +181,10 @@ cmArchiveWrite::~cmArchiveWrite()
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
bool cmArchiveWrite::Add(std::string path, size_t skip, const char* prefix) bool cmArchiveWrite::Add(std::string path,
size_t skip,
const char* prefix,
bool recursive)
{ {
if(this->Okay()) if(this->Okay())
{ {
@ -189,20 +192,21 @@ bool cmArchiveWrite::Add(std::string path, size_t skip, const char* prefix)
{ {
path.erase(path.size()-1); path.erase(path.size()-1);
} }
this->AddPath(path.c_str(), skip, prefix); this->AddPath(path.c_str(), skip, prefix, recursive);
} }
return this->Okay(); return this->Okay();
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
bool cmArchiveWrite::AddPath(const char* path, bool cmArchiveWrite::AddPath(const char* path,
size_t skip, const char* prefix) size_t skip, const char* prefix,
bool recursive)
{ {
if(!this->AddFile(path, skip, prefix)) if(!this->AddFile(path, skip, prefix))
{ {
return false; return false;
} }
if(!cmSystemTools::FileIsDirectory(path) || if((!cmSystemTools::FileIsDirectory(path) || !recursive) ||
cmSystemTools::FileIsSymlink(path)) cmSystemTools::FileIsSymlink(path))
{ {
return true; return true;
@ -278,6 +282,33 @@ bool cmArchiveWrite::AddFile(const char* file,
} }
archive_entry_set_mtime(e, t, 0); archive_entry_set_mtime(e, t, 0);
} }
// manages the uid/guid of the entry (if any)
if (this->Uid.IsSet() && this->Gid.IsSet())
{
archive_entry_set_uid(e, this->Uid.Get());
archive_entry_set_gid(e, this->Gid.Get());
}
if (this->Uname.size() && this->Gname.size())
{
archive_entry_set_uname(e, this->Uname.c_str());
archive_entry_set_gname(e, this->Gname.c_str());
}
// manages the permissions
if (this->Permissions.IsSet())
{
archive_entry_set_perm(e, this->Permissions.Get());
}
if (this->PermissionsMask.IsSet())
{
mode_t perm = archive_entry_perm(e);
archive_entry_set_perm(e, perm & this->PermissionsMask.Get());
}
// Clear acl and xattr fields not useful for distribution. // Clear acl and xattr fields not useful for distribution.
archive_entry_acl_clear(e); archive_entry_acl_clear(e);
archive_entry_xattr_clear(e); archive_entry_xattr_clear(e);

View File

@ -18,6 +18,22 @@
# error "cmArchiveWrite not allowed during bootstrap build!" # error "cmArchiveWrite not allowed during bootstrap build!"
#endif #endif
template<typename T>
class cmArchiveWriteOptional
{
public:
cmArchiveWriteOptional() {this->Clear();}
explicit cmArchiveWriteOptional(T val) {this->Set(val);}
void Set(T val) {this->IsValueSet = true; this->Value=val;}
void Clear() {this->IsValueSet = false;}
bool IsSet() const {return this->IsValueSet;}
T Get() const {return Value;}
private:
T Value;
bool IsValueSet;
};
/** \class cmArchiveWrite /** \class cmArchiveWrite
* \brief Wrapper around libarchive for writing. * \brief Wrapper around libarchive for writing.
* *
@ -52,7 +68,10 @@ public:
* skip. The remaining part of the input path is appended to the * skip. The remaining part of the input path is appended to the
* "prefix" value to construct the final name in the archive. * "prefix" value to construct the final name in the archive.
*/ */
bool Add(std::string path, size_t skip = 0, const char* prefix = 0); bool Add(std::string path,
size_t skip = 0,
const char* prefix = 0,
bool recursive = true);
/** Returns true if there has been no error. */ /** Returns true if there has been no error. */
operator safe_bool() const operator safe_bool() const
@ -69,9 +88,65 @@ public:
void SetVerbose(bool v) { this->Verbose = v; } void SetVerbose(bool v) { this->Verbose = v; }
void SetMTime(std::string const& t) { this->MTime = t; } void SetMTime(std::string const& t) { this->MTime = t; }
//! Sets the permissions of the added files/folders
void SetPermissions(mode_t permissions_)
{
this->Permissions.Set(permissions_);
}
//! Clears permissions - default is used instead
void ClearPermissions() { this->Permissions.Clear(); }
//! Sets the permissions mask of files/folders
//!
//! The permissions will be copied from the existing file
//! or folder. The mask will then be applied to unset
//! some of them
void SetPermissionsMask(mode_t permissionsMask_)
{
this->PermissionsMask.Set(permissionsMask_);
}
//! Clears permissions mask - default is used instead
void ClearPermissionsMask()
{
this->PermissionsMask.Clear();
}
//! Sets UID and GID to be used in the tar file
void SetUIDAndGID(int uid_, int gid_)
{
this->Uid.Set(uid_);
this->Gid.Set(gid_);
}
//! Clears UID and GID to be used in the tar file - default is used instead
void ClearUIDAndGID()
{
this->Uid.Clear();
this->Gid.Clear();
}
//! Sets UNAME and GNAME to be used in the tar file
void SetUNAMEAndGNAME(const std::string& uname_, const std::string& gname_)
{
this->Uname = uname_;
this->Gname = gname_;
}
//! Clears UNAME and GNAME to be used in the tar file
//! default is used instead
void ClearUNAMEAndGNAME()
{
this->Uname = "";
this->Gname = "";
}
private: private:
bool Okay() const { return this->Error.empty(); } bool Okay() const { return this->Error.empty(); }
bool AddPath(const char* path, size_t skip, const char* prefix); bool AddPath(const char* path, size_t skip, const char* prefix,
bool recursive = true);
bool AddFile(const char* file, size_t skip, const char* prefix); bool AddFile(const char* file, size_t skip, const char* prefix);
bool AddData(const char* file, size_t size); bool AddData(const char* file, size_t size);
@ -87,6 +162,22 @@ private:
std::string Format; std::string Format;
std::string Error; std::string Error;
std::string MTime; std::string MTime;
//! UID of the user in the tar file
cmArchiveWriteOptional<int> Uid;
//! GUID of the user in the tar file
cmArchiveWriteOptional<int> Gid;
//! UNAME/GNAME of the user (does not override UID/GID)
//!@{
std::string Uname;
std::string Gname;
//!@}
//! Permissions on files/folders
cmArchiveWriteOptional<mode_t> Permissions;
cmArchiveWriteOptional<mode_t> PermissionsMask;
}; };
#endif #endif

View File

@ -56,10 +56,10 @@ protected:
// Whether the real file stream was valid when it was closed. // Whether the real file stream was valid when it was closed.
bool Okay; bool Okay;
// Whether the destionation file is compressed // Whether the destination file is compressed
bool Compress; bool Compress;
// Whether the destionation file is compressed // Whether the destination file is compressed
bool CompressExtraExtension; bool CompressExtraExtension;
}; };

View File

@ -80,6 +80,25 @@ set(CPACK_COMPONENT_HEADERS_DESCRIPTION
# depend on the libraries component. # depend on the libraries component.
set(CPACK_COMPONENT_HEADERS_DEPENDS libraries) set(CPACK_COMPONENT_HEADERS_DEPENDS libraries)
# creates preinst/prerm scripts with specific permissions. Those permissions
# (especially executable) should be in the final archive
find_program(CHMOD_PROG chmod)
if(CHMOD_PROG)
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/preinst "echo default_preinst")
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/prerm "echo default_prerm")
# Those should have 755 permission normally. We mess it up to see if
# CPACK_DEBIAN_APPLICATIONS_PACKAGE_CONTROL_STRICT_PERMISSION is able to fix
# it.
execute_process(COMMAND ${CHMOD_PROG} 640 ${CMAKE_CURRENT_BINARY_DIR}/preinst)
execute_process(COMMAND ${CHMOD_PROG} 640 ${CMAKE_CURRENT_BINARY_DIR}/prerm)
set(CPACK_DEBIAN_APPLICATIONS_PACKAGE_CONTROL_EXTRA
"${CMAKE_CURRENT_BINARY_DIR}/preinst;${CMAKE_CURRENT_BINARY_DIR}/prerm")
set(CPACK_DEBIAN_APPLICATIONS_PACKAGE_CONTROL_STRICT_PERMISSION TRUE)
endif()
# We may use the CPack specific config file in order # We may use the CPack specific config file in order
# to tailor CPack behavior on a CPack generator specific way # to tailor CPack behavior on a CPack generator specific way
# (Behavior would be different for RPM or TGZ or DEB ...) # (Behavior would be different for RPM or TGZ or DEB ...)

View File

@ -1,11 +1,17 @@
set(foo_preinst "^echo default_preinst$") set(foo_preinst "^echo default_preinst$")
set(foo_preinst_permissions_regex "-rwxr-xr-x .*")
set(foo_prerm "^echo default_prerm$") set(foo_prerm "^echo default_prerm$")
set(foo_prerm_permissions_regex "-rwxr-xr-x .*")
verifyDebControl("${FOUND_FILE_1}" "foo" "preinst;prerm") verifyDebControl("${FOUND_FILE_1}" "foo" "preinst;prerm")
set(bar_preinst "^echo bar_preinst$") set(bar_preinst "^echo bar_preinst$")
set(bar_prerm_permissions_regex "-rwx------ .*")
set(bar_prerm "^echo bar_prerm$") set(bar_prerm "^echo bar_prerm$")
set(bar_prerm_permissions_regex "-rwx------ .*")
verifyDebControl("${FOUND_FILE_2}" "bar" "preinst;prerm") verifyDebControl("${FOUND_FILE_2}" "bar" "preinst;prerm")
set(bas_preinst "^echo default_preinst$") set(bas_preinst "^echo default_preinst$")
set(bas_prerm_permissions_regex "-rwxr-xr-x .*")
set(bas_prerm "^echo default_prerm$") set(bas_prerm "^echo default_prerm$")
set(bas_prerm_permissions_regex "-rwxr-xr-x .*")
verifyDebControl("${FOUND_FILE_3}" "bas" "preinst;prerm") verifyDebControl("${FOUND_FILE_3}" "bas" "preinst;prerm")

View File

@ -14,7 +14,7 @@ function(verifyDebControl FILE PREFIX VERIFY_FILES)
ERROR_VARIABLE err_) ERROR_VARIABLE err_)
if(err_) if(err_)
message(FATAL_ERROR "Debian controll verification failed for file: " message(FATAL_ERROR "Debian control verification failed for file: "
"'${FILE}'; error output: '${err_}'") "'${FILE}'; error output: '${err_}'")
endif() endif()
@ -24,6 +24,19 @@ function(verifyDebControl FILE PREFIX VERIFY_FILES)
message(FATAL_ERROR "Unexpected content in for '${PREFIX}_${FILE_}'!" message(FATAL_ERROR "Unexpected content in for '${PREFIX}_${FILE_}'!"
" Content: '${content_}'") " Content: '${content_}'")
endif() endif()
execute_process(COMMAND ls -l "${CMAKE_CURRENT_BINARY_DIR}/control_${PREFIX}/${FILE_}"
OUTPUT_VARIABLE package_permissions_
ERROR_VARIABLE package_permissions_error_
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT package_permissions_error_)
if(NOT package_permissions_ MATCHES "${${PREFIX}_${FILE_}_permissions_regex}")
message(FATAL_ERROR "Unexpected file permissions for ${PREFIX}_${FILE_}: '${package_permissions_}'!")
endif()
else()
message(FATAL_ERROR "Listing file permissions failed (${package_permissions_error_})!")
endif()
endforeach() endforeach()
endfunction() endfunction()

View File

@ -2,14 +2,32 @@ install(FILES CMakeLists.txt DESTINATION foo COMPONENT foo)
install(FILES CMakeLists.txt DESTINATION bar COMPONENT bar) install(FILES CMakeLists.txt DESTINATION bar COMPONENT bar)
install(FILES CMakeLists.txt DESTINATION bas COMPONENT bas) install(FILES CMakeLists.txt DESTINATION bas COMPONENT bas)
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/preinst "echo default_preinst") file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/tmp/preinst "echo default_preinst")
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/prerm "echo default_prerm") file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/tmp/prerm "echo default_prerm")
foreach(file_ preinst prerm)
file(COPY ${CMAKE_CURRENT_BINARY_DIR}/tmp/${file_}
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
FILE_PERMISSIONS
OWNER_READ OWNER_WRITE OWNER_EXECUTE
GROUP_READ GROUP_EXECUTE
WORLD_READ WORLD_EXECUTE)
endforeach()
set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA
"${CMAKE_CURRENT_BINARY_DIR}/preinst;${CMAKE_CURRENT_BINARY_DIR}/prerm") "${CMAKE_CURRENT_BINARY_DIR}/preinst;${CMAKE_CURRENT_BINARY_DIR}/prerm;${CMAKE_CURRENT_BINARY_DIR}/conffiles")
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/bar/preinst "echo bar_preinst") file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/bar_tmp/preinst "echo bar_preinst")
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/bar/prerm "echo bar_prerm") file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/bar_tmp/prerm "echo bar_prerm")
foreach(file_ preinst prerm)
# not acceptable permissions for lintian but we need to check that
# permissions are preserved
file(COPY ${CMAKE_CURRENT_BINARY_DIR}/bar_tmp/${file_}
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/bar
FILE_PERMISSIONS
OWNER_READ OWNER_WRITE OWNER_EXECUTE)
endforeach()
set(CPACK_DEBIAN_BAR_PACKAGE_CONTROL_EXTRA set(CPACK_DEBIAN_BAR_PACKAGE_CONTROL_EXTRA
"${CMAKE_CURRENT_BINARY_DIR}/bar/preinst;${CMAKE_CURRENT_BINARY_DIR}/bar/prerm") "${CMAKE_CURRENT_BINARY_DIR}/bar/preinst;${CMAKE_CURRENT_BINARY_DIR}/bar/prerm")

View File

@ -32,14 +32,14 @@ different types of packages. This script is placed into CPack test root
directory even if it will be used for only one of the generators. directory even if it will be used for only one of the generators.
If test will be used for multiple generators but some of them require some If test will be used for multiple generators but some of them require some
generator speciffic commands then those commands should be added to a separate generator specific commands then those commands should be added to a separate
file that should be located in '<generator_name>/<test_name>-specifics.cmake' file that should be located in '<generator_name>/<test_name>-specifics.cmake'
in CPack test root directory. in CPack test root directory.
CPack execution phase: CPack execution phase:
---------------------- ----------------------
Only exececutes CPack for content that was generated during CMake execution Only executes CPack for content that was generated during CMake execution
phase. phase.
Verification of generated files: Verification of generated files:

View File

@ -28,8 +28,9 @@ if(NOT EXPECTED_FILES_COUNT EQUAL 0)
if(NOT expected_content_list) if(NOT expected_content_list)
message(FATAL_ERROR message(FATAL_ERROR
"Unexpected file content for file No. '${file_no_}'!" "Unexpected file content for file No. '${file_no_}'!\n"
" Content: '${PACKAGE_CONTENT}'" " Content: '${PACKAGE_CONTENT}'\n\n"
" Expected: '${EXPECTED_FILE_CONTENT_${file_no_}}'"
"${output_error_message}") "${output_error_message}")
endif() endif()
else() else()
@ -56,7 +57,7 @@ if(NOT EXPECTED_FILES_COUNT EQUAL 0)
"${output_error_message}") "${output_error_message}")
endif() endif()
# sanity check that we didn't accidentaly list wrong files with our regular # sanity check that we didn't accidentally list wrong files with our regular
# expressions # expressions
foreach(expected_ IN LISTS allFoundFiles_) foreach(expected_ IN LISTS allFoundFiles_)
list(FIND foundFiles_ "${expected_}" found_) list(FIND foundFiles_ "${expected_}" found_)