diff options
author | Andrew Fuller <afuller@teradici.com> | 2018-09-18 17:57:27 (GMT) |
---|---|---|
committer | Andrew Fuller <afuller@teradici.com> | 2018-09-21 20:19:31 (GMT) |
commit | d8a3939aef44f6176ac3ec03fb8cdd396fa96ca3 (patch) | |
tree | 3efba7fbf8d9fdee86b568a50604d31ed2178173 /Source/CPack/cmCPackDebGenerator.cxx | |
parent | 42fbff45e4529d3cfbeb3cbcef8c5616df3f06f1 (diff) | |
download | CMake-d8a3939aef44f6176ac3ec03fb8cdd396fa96ca3.zip CMake-d8a3939aef44f6176ac3ec03fb8cdd396fa96ca3.tar.gz CMake-d8a3939aef44f6176ac3ec03fb8cdd396fa96ca3.tar.bz2 |
CPack/Deb: Add ability to split out debug symbols into .ddeb package
Diffstat (limited to 'Source/CPack/cmCPackDebGenerator.cxx')
-rw-r--r-- | Source/CPack/cmCPackDebGenerator.cxx | 944 |
1 files changed, 560 insertions, 384 deletions
diff --git a/Source/CPack/cmCPackDebGenerator.cxx b/Source/CPack/cmCPackDebGenerator.cxx index 972fe6f..ea0ee58 100644 --- a/Source/CPack/cmCPackDebGenerator.cxx +++ b/Source/CPack/cmCPackDebGenerator.cxx @@ -12,11 +12,430 @@ #include "cm_sys_stat.h" #include "cmsys/Glob.hxx" +#include <map> #include <ostream> #include <set> #include <string.h> #include <utility> +namespace { + +class DebGenerator +{ +public: + DebGenerator(cmCPackLog* logger, std::string const& outputName, + std::string const& workDir, std::string const& topLevelDir, + std::string const& temporaryDir, + const char* debianCompressionType, + const char* debianArchiveType, + std::map<std::string, std::string> const& controlValues, + bool genShLibs, std::string const& shLibsFilename, + bool genPostInst, std::string const& postInst, bool genPostRm, + std::string const& postRm, const char* controlExtra, + bool permissionStrctPolicy, + std::vector<std::string> const& packageFiles); + + bool generate() const; + +private: + void generateDebianBinaryFile() const; + void generateControlFile() const; + bool generateDataTar() const; + std::string generateMD5File() const; + bool generateControlTar(std::string const& md5Filename) const; + bool generateDeb() const; + + cmCPackLog* Logger; + const std::string OutputName; + const std::string WorkDir; + std::string CompressionSuffix; + const std::string TopLevelDir; + const std::string TemporaryDir; + const char* DebianArchiveType; + const std::map<std::string, std::string> ControlValues; + const bool GenShLibs; + const std::string ShLibsFilename; + const bool GenPostInst; + const std::string PostInst; + const bool GenPostRm; + const std::string PostRm; + const char* ControlExtra; + const bool PermissionStrictPolicy; + const std::vector<std::string> PackageFiles; + cmArchiveWrite::Compress TarCompressionType; +}; + +DebGenerator::DebGenerator( + cmCPackLog* logger, std::string const& outputName, + std::string const& workDir, std::string const& topLevelDir, + std::string const& temporaryDir, const char* debianCompressionType, + const char* debianArchiveType, + std::map<std::string, std::string> const& controlValues, bool genShLibs, + std::string const& shLibsFilename, bool genPostInst, + std::string const& postInst, bool genPostRm, std::string const& postRm, + const char* controlExtra, bool permissionStrictPolicy, + std::vector<std::string> const& packageFiles) + : Logger(logger) + , OutputName(outputName) + , WorkDir(workDir) + , TopLevelDir(topLevelDir) + , TemporaryDir(temporaryDir) + , DebianArchiveType(debianArchiveType ? debianArchiveType : "paxr") + , ControlValues(controlValues) + , GenShLibs(genShLibs) + , ShLibsFilename(shLibsFilename) + , GenPostInst(genPostInst) + , PostInst(postInst) + , GenPostRm(genPostRm) + , PostRm(postRm) + , ControlExtra(controlExtra) + , PermissionStrictPolicy(permissionStrictPolicy) + , PackageFiles(packageFiles) +{ + if (!debianCompressionType) { + debianCompressionType = "gzip"; + } + + if (!strcmp(debianCompressionType, "lzma")) { + CompressionSuffix = ".lzma"; + TarCompressionType = cmArchiveWrite::CompressLZMA; + } else if (!strcmp(debianCompressionType, "xz")) { + CompressionSuffix = ".xz"; + TarCompressionType = cmArchiveWrite::CompressXZ; + } else if (!strcmp(debianCompressionType, "bzip2")) { + CompressionSuffix = ".bz2"; + TarCompressionType = cmArchiveWrite::CompressBZip2; + } else if (!strcmp(debianCompressionType, "gzip")) { + CompressionSuffix = ".gz"; + TarCompressionType = cmArchiveWrite::CompressGZip; + } else if (!strcmp(debianCompressionType, "none")) { + CompressionSuffix.clear(); + TarCompressionType = cmArchiveWrite::CompressNone; + } else { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error unrecognized compression type: " + << debianCompressionType << std::endl); + } +} + +bool DebGenerator::generate() const +{ + generateDebianBinaryFile(); + generateControlFile(); + if (!generateDataTar()) { + return false; + } + std::string md5Filename = generateMD5File(); + if (!generateControlTar(md5Filename)) { + return false; + } + return generateDeb(); +} + +void DebGenerator::generateDebianBinaryFile() const +{ + // debian-binary file + const std::string dbfilename = WorkDir + "/debian-binary"; + cmGeneratedFileStream out(dbfilename); + out << "2.0"; + out << std::endl; // required for valid debian package +} + +void DebGenerator::generateControlFile() const +{ + std::string ctlfilename = WorkDir + "/control"; + + cmGeneratedFileStream out(ctlfilename); + for (auto const& kv : ControlValues) { + out << kv.first << ": " << kv.second << "\n"; + } + + unsigned long totalSize = 0; + { + std::string dirName = TemporaryDir; + dirName += '/'; + for (std::string const& file : PackageFiles) { + totalSize += cmSystemTools::FileLength(file); + } + } + out << "Installed-Size: " << (totalSize + 1023) / 1024 << "\n"; + out << std::endl; +} + +bool DebGenerator::generateDataTar() const +{ + std::string filename_data_tar = WorkDir + "/data.tar" + CompressionSuffix; + cmGeneratedFileStream fileStream_data_tar; + fileStream_data_tar.Open(filename_data_tar, false, true); + if (!fileStream_data_tar) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error opening the file \"" + << filename_data_tar << "\" for writing" << std::endl); + return false; + } + cmArchiveWrite data_tar(fileStream_data_tar, TarCompressionType, + DebianArchiveType); + + // 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 = WorkDir.length(); + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "WDIR: \"" << WorkDir << "\", 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 != WorkDir) { + // the last one IS WorkDir, 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: " << WorkDir << std::endl + << "#file: " << file << std::endl + << "#error:" << data_tar.GetError() << std::endl); + return false; + } + } + return true; +} + +std::string DebGenerator::generateMD5File() const +{ + std::string md5filename = WorkDir + "/md5sums"; + + cmGeneratedFileStream out(md5filename); + + std::string topLevelWithTrailingSlash = TemporaryDir; + 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) + return md5filename; +} + +bool DebGenerator::generateControlTar(std::string const& md5Filename) const +{ + std::string filename_control_tar = WorkDir + "/control.tar.gz"; + + cmGeneratedFileStream fileStream_control_tar; + fileStream_control_tar.Open(filename_control_tar, false, true); + if (!fileStream_control_tar) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error opening the file \"" + << filename_control_tar << "\" for writing" << std::endl); + return false; + } + cmArchiveWrite control_tar(fileStream_control_tar, + cmArchiveWrite::CompressGZip, DebianArchiveType); + + // 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, WorkDir.length(), ".") || + !control_tar.Add(WorkDir + "/control", WorkDir.length(), ".")) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error adding file to tar:" + << std::endl + << "#top level directory: " << WorkDir << std::endl + << "#file: \"control\" or \"md5sums\"" << std::endl + << "#error:" << control_tar.GetError() << std::endl); + return false; + } + + // adds generated shlibs file + if (GenShLibs) { + if (!control_tar.Add(ShLibsFilename, WorkDir.length(), ".")) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error adding file to tar:" + << std::endl + << "#top level directory: " << WorkDir << std::endl + << "#file: \"shlibs\"" << std::endl + << "#error:" << control_tar.GetError() << std::endl); + return false; + } + } + + // adds LDCONFIG related files + if (GenPostInst) { + control_tar.SetPermissions(permission755); + if (!control_tar.Add(PostInst, WorkDir.length(), ".")) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error adding file to tar:" + << std::endl + << "#top level directory: " << WorkDir << std::endl + << "#file: \"postinst\"" << std::endl + << "#error:" << control_tar.GetError() << std::endl); + return false; + } + control_tar.SetPermissions(permission644); + } + + if (GenPostRm) { + control_tar.SetPermissions(permission755); + if (!control_tar.Add(PostRm, WorkDir.length(), ".")) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error adding file to tar:" + << std::endl + << "#top level directory: " << WorkDir << std::endl + << "#file: \"postinst\"" << std::endl + << "#error:" << control_tar.GetError() << std::endl); + return false; + } + 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 + if (ControlExtra) { + // permissions are now controlled by the original file permissions + + 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 = WorkDir + "/" + 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, WorkDir.length(), "."); + } + } + } + + return true; +} + +bool DebGenerator::generateDeb() const +{ + // 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 outputPath = TopLevelDir + "/" + OutputName; + std::string const tlDir = WorkDir + "/"; + cmGeneratedFileStream debStream; + debStream.Open(outputPath, false, true); + cmArchiveWrite deb(debStream, cmArchiveWrite::CompressNone, "arbsd"); + + // uid/gid should be the one of the root user, and this root user has + // always uid/gid equal to 0. + deb.SetUIDAndGID(0u, 0u); + deb.SetUNAMEAndGNAME("root", "root"); + + if (!deb.Add(tlDir + "debian-binary", tlDir.length()) || + !deb.Add(tlDir + "control.tar.gz", tlDir.length()) || + !deb.Add(tlDir + "data.tar" + CompressionSuffix, tlDir.length())) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error creating debian package:" + << std::endl + << "#top level directory: " << TopLevelDir << std::endl + << "#file: " << OutputName << std::endl + << "#error:" << deb.GetError() << std::endl); + return false; + } + return true; +} + +} // end anonymous namespace + cmCPackDebGenerator::cmCPackDebGenerator() { } @@ -68,18 +487,20 @@ int cmCPackDebGenerator::PackageOnePack(std::string const& initialTopLevel, 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; + { // Isolate globbing of binaries vs. dbgsyms + 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(); } - packageFiles = gl.GetFiles(); int res = createDeb(); if (res != 1) { @@ -90,6 +511,32 @@ int cmCPackDebGenerator::PackageOnePack(std::string const& initialTopLevel, packageFileName += "/"; packageFileName += this->GetOption("GEN_CPACK_OUTPUT_FILE_NAME"); packageFileNames.push_back(std::move(packageFileName)); + + if (this->IsOn("GEN_CPACK_DEBIAN_DEBUGINFO_PACKAGE")) { + cmsys::Glob gl; + std::string findExpr(this->GetOption("GEN_DBGSYMDIR")); + 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(); + + res = createDbgsymDDeb(); + 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_DBGSYM_OUTPUT_FILE_NAME"); + packageFileNames.push_back(std::move(packageFileName)); + } + return retval; } @@ -234,112 +681,81 @@ int cmCPackDebGenerator::PackageFiles() 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); - out << "2.0"; - out << std::endl; // required for valid debian package - } - - // control file - std::string ctlfilename = strGenWDIR + "/control"; + std::map<std::string, std::string> controlValues; // debian policy enforce lower case for package name - // mandatory entries: - std::string debian_pkg_name = cmsys::SystemTools::LowerCase( + controlValues["Package"] = cmsys::SystemTools::LowerCase( this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_NAME")); - const char* debian_pkg_version = + controlValues["Version"] = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_VERSION"); - const char* debian_pkg_section = + controlValues["Section"] = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SECTION"); - const char* debian_pkg_priority = + controlValues["Priority"] = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_PRIORITY"); - const char* debian_pkg_arch = + controlValues["Architecture"] = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_ARCHITECTURE"); - const char* maintainer = + controlValues["Maintainer"] = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_MAINTAINER"); - const char* desc = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_DESCRIPTION"); + controlValues["Description"] = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_DESCRIPTION"); - // optional entries + const char* debian_pkg_source = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SOURCE"); + if (debian_pkg_source && *debian_pkg_source) { + controlValues["Source"] = debian_pkg_source; + } const char* debian_pkg_dep = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_DEPENDS"); + if (debian_pkg_dep && *debian_pkg_dep) { + controlValues["Depends"] = debian_pkg_dep; + } const char* debian_pkg_rec = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_RECOMMENDS"); + if (debian_pkg_rec && *debian_pkg_rec) { + controlValues["Recommends"] = debian_pkg_rec; + } const char* debian_pkg_sug = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SUGGESTS"); + if (debian_pkg_sug && *debian_pkg_sug) { + controlValues["Suggests"] = debian_pkg_sug; + } const char* debian_pkg_url = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_HOMEPAGE"); + if (debian_pkg_url && *debian_pkg_url) { + controlValues["Homepage"] = debian_pkg_url; + } const char* debian_pkg_predep = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_PREDEPENDS"); + if (debian_pkg_predep && *debian_pkg_predep) { + controlValues["Pre-Depends"] = debian_pkg_predep; + } const char* debian_pkg_enhances = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_ENHANCES"); + if (debian_pkg_enhances && *debian_pkg_enhances) { + controlValues["Enhances"] = debian_pkg_enhances; + } const char* debian_pkg_breaks = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_BREAKS"); + if (debian_pkg_breaks && *debian_pkg_breaks) { + controlValues["Breaks"] = debian_pkg_breaks; + } const char* debian_pkg_conflicts = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_CONFLICTS"); + if (debian_pkg_conflicts && *debian_pkg_conflicts) { + controlValues["Conflicts"] = debian_pkg_conflicts; + } const char* debian_pkg_provides = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_PROVIDES"); + if (debian_pkg_provides && *debian_pkg_provides) { + controlValues["Provides"] = debian_pkg_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); - 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; + if (debian_pkg_replaces && *debian_pkg_replaces) { + controlValues["Replaces"] = debian_pkg_replaces; } + const std::string strGenWDIR(this->GetOption("GEN_WDIR")); const std::string shlibsfilename = strGenWDIR + "/shlibs"; const char* debian_pkg_shlibs = @@ -371,314 +787,74 @@ int cmCPackDebGenerator::createDeb() "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"; + DebGenerator gen( + Logger, this->GetOption("GEN_CPACK_OUTPUT_FILE_NAME"), strGenWDIR, + this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), + this->GetOption("CPACK_TEMPORARY_DIRECTORY"), + this->GetOption("GEN_CPACK_DEBIAN_COMPRESSION_TYPE"), + this->GetOption("GEN_CPACK_DEBIAN_ARCHIVE_TYPE"), controlValues, gen_shibs, + shlibsfilename, this->IsOn("GEN_CPACK_DEBIAN_GENERATE_POSTINST"), postinst, + this->IsOn("GEN_CPACK_DEBIAN_GENERATE_POSTRM"), postrm, + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA"), + this->IsSet("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION"), + packageFiles); + + if (!gen.generate()) { + return 0; } + return 1; +} - 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, 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 +int cmCPackDebGenerator::createDbgsymDDeb() +{ + // Packages containing debug symbols follow the same structure as .debs + // but have different metadata and content. - std::string md5filename = strGenWDIR + "/md5sums"; - { - // the scope is needed for cmGeneratedFileStream - cmGeneratedFileStream out(md5filename); - - 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::map<std::string, std::string> controlValues; + // debian policy enforce lower case for package name + std::string packageNameLower = cmsys::SystemTools::LowerCase( + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_NAME")); + const char* debian_pkg_version = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_VERSION"); - std::string output = - cmSystemTools::ComputeFileHash(file, cmCryptoHash::AlgoMD5); - if (output.empty()) { - cmCPackLogger(cmCPackLog::LOG_ERROR, - "Problem computing the md5 of " << file << std::endl); - } + controlValues["Package"] = packageNameLower + "-dbgsym"; + controlValues["Package-Type"] = "ddeb"; + controlValues["Version"] = debian_pkg_version; + controlValues["Auto-Built-Package"] = "debug-symbols"; + controlValues["Depends"] = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_NAME") + + std::string(" (= ") + debian_pkg_version + ")"; + controlValues["Section"] = "debug"; + controlValues["Priority"] = "optional"; + controlValues["Architecture"] = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_ARCHITECTURE"); + controlValues["Maintainer"] = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_MAINTAINER"); + controlValues["Description"] = + std::string("debug symbols for ") + packageNameLower; - 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) + const char* debian_pkg_source = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SOURCE"); + if (debian_pkg_source && *debian_pkg_source) { + controlValues["Source"] = debian_pkg_source; } - - 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, 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(), "."); - } - } - } + const char* debian_build_ids = this->GetOption("GEN_BUILD_IDS"); + if (debian_build_ids && *debian_build_ids) { + controlValues["Build-Ids"] = debian_build_ids; } - // 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, false, true); - cmArchiveWrite deb(debStream, cmArchiveWrite::CompressNone, "arbsd"); + DebGenerator gen( + Logger, this->GetOption("GEN_CPACK_DBGSYM_OUTPUT_FILE_NAME"), + this->GetOption("GEN_DBGSYMDIR"), - // uid/gid should be the one of the root user, and this root user has - // always uid/gid equal to 0. - deb.SetUIDAndGID(0u, 0u); - deb.SetUNAMEAndGNAME("root", "root"); + this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), + this->GetOption("CPACK_TEMPORARY_DIRECTORY"), + this->GetOption("GEN_CPACK_DEBIAN_COMPRESSION_TYPE"), + this->GetOption("GEN_CPACK_DEBIAN_ARCHIVE_TYPE"), controlValues, false, "", + false, "", false, "", nullptr, + this->IsSet("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION"), + packageFiles); - 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); + if (!gen.generate()) { return 0; } return 1; |