1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmCPackDebGenerator.h"
13 #include "cmsys/Glob.hxx"
15 #include "cm_sys_stat.h"
17 #include "cmArchiveWrite.h"
18 #include "cmCPackComponentGroup.h"
19 #include "cmCPackGenerator.h"
20 #include "cmCPackLog.h"
21 #include "cmCryptoHash.h"
22 #include "cmGeneratedFileStream.h"
24 #include "cmStringAlgorithms.h"
25 #include "cmSystemTools.h"
33 DebGenerator(cmCPackLog
* logger
, std::string outputName
, std::string workDir
,
34 std::string topLevelDir
, std::string temporaryDir
,
35 cmValue debianCompressionType
, cmValue numThreads
,
36 cmValue debianArchiveType
,
37 std::map
<std::string
, std::string
> controlValues
,
38 bool genShLibs
, std::string shLibsFilename
, bool genPostInst
,
39 std::string postInst
, bool genPostRm
, std::string postRm
,
40 cmValue controlExtra
, bool permissionStrctPolicy
,
41 std::vector
<std::string
> packageFiles
);
43 bool generate() const;
46 void generateDebianBinaryFile() const;
47 void generateControlFile() const;
48 bool generateDataTar() const;
49 std::string
generateMD5File() const;
50 bool generateControlTar(std::string
const& md5Filename
) const;
51 bool generateDeb() const;
54 const std::string OutputName
;
55 const std::string WorkDir
;
56 std::string CompressionSuffix
;
57 const std::string TopLevelDir
;
58 const std::string TemporaryDir
;
59 const std::string DebianArchiveType
;
61 const std::map
<std::string
, std::string
> ControlValues
;
63 const std::string ShLibsFilename
;
64 const bool GenPostInst
;
65 const std::string PostInst
;
67 const std::string PostRm
;
69 const bool PermissionStrictPolicy
;
70 const std::vector
<std::string
> PackageFiles
;
71 cmArchiveWrite::Compress TarCompressionType
;
74 DebGenerator::DebGenerator(
75 cmCPackLog
* logger
, std::string outputName
, std::string workDir
,
76 std::string topLevelDir
, std::string temporaryDir
,
77 cmValue debCompressionType
, cmValue numThreads
, cmValue debianArchiveType
,
78 std::map
<std::string
, std::string
> controlValues
, bool genShLibs
,
79 std::string shLibsFilename
, bool genPostInst
, std::string postInst
,
80 bool genPostRm
, std::string postRm
, cmValue controlExtra
,
81 bool permissionStrictPolicy
, std::vector
<std::string
> packageFiles
)
83 , OutputName(std::move(outputName
))
84 , WorkDir(std::move(workDir
))
85 , TopLevelDir(std::move(topLevelDir
))
86 , TemporaryDir(std::move(temporaryDir
))
87 , DebianArchiveType(debianArchiveType
? *debianArchiveType
: "gnutar")
88 , ControlValues(std::move(controlValues
))
89 , GenShLibs(genShLibs
)
90 , ShLibsFilename(std::move(shLibsFilename
))
91 , GenPostInst(genPostInst
)
92 , PostInst(std::move(postInst
))
93 , GenPostRm(genPostRm
)
94 , PostRm(std::move(postRm
))
95 , ControlExtra(controlExtra
)
96 , PermissionStrictPolicy(permissionStrictPolicy
)
97 , PackageFiles(std::move(packageFiles
))
99 std::string debianCompressionType
= "gzip";
100 if (debCompressionType
) {
101 debianCompressionType
= *debCompressionType
;
104 if (debianCompressionType
== "lzma") {
105 this->CompressionSuffix
= ".lzma";
106 this->TarCompressionType
= cmArchiveWrite::CompressLZMA
;
107 } else if (debianCompressionType
== "xz") {
108 this->CompressionSuffix
= ".xz";
109 this->TarCompressionType
= cmArchiveWrite::CompressXZ
;
110 } else if (debianCompressionType
== "bzip2") {
111 this->CompressionSuffix
= ".bz2";
112 this->TarCompressionType
= cmArchiveWrite::CompressBZip2
;
113 } else if (debianCompressionType
== "gzip") {
114 this->CompressionSuffix
= ".gz";
115 this->TarCompressionType
= cmArchiveWrite::CompressGZip
;
116 } else if (debianCompressionType
== "zstd") {
117 this->CompressionSuffix
= ".zst";
118 this->TarCompressionType
= cmArchiveWrite::CompressZstd
;
119 } else if (debianCompressionType
== "none") {
120 this->CompressionSuffix
.clear();
121 this->TarCompressionType
= cmArchiveWrite::CompressNone
;
123 cmCPackLogger(cmCPackLog::LOG_ERROR
,
124 "Error unrecognized compression type: "
125 << debianCompressionType
<< std::endl
);
129 if (!cmStrToLong(*numThreads
, &this->NumThreads
)) {
130 this->NumThreads
= 1;
131 cmCPackLogger(cmCPackLog::LOG_ERROR
,
132 "Unrecognized number of threads: " << numThreads
136 this->NumThreads
= 1;
140 bool DebGenerator::generate() const
142 this->generateDebianBinaryFile();
143 this->generateControlFile();
144 if (!this->generateDataTar()) {
147 std::string md5Filename
= this->generateMD5File();
148 if (!this->generateControlTar(md5Filename
)) {
151 return this->generateDeb();
154 void DebGenerator::generateDebianBinaryFile() const
156 // debian-binary file
157 const std::string dbfilename
= this->WorkDir
+ "/debian-binary";
158 cmGeneratedFileStream out
;
159 out
.Open(dbfilename
, false, true);
160 out
<< "2.0\n"; // required for valid debian package
163 void DebGenerator::generateControlFile() const
165 std::string ctlfilename
= this->WorkDir
+ "/control";
167 cmGeneratedFileStream out
;
168 out
.Open(ctlfilename
, false, true);
169 for (auto const& kv
: this->ControlValues
) {
170 out
<< kv
.first
<< ": " << kv
.second
<< "\n";
173 unsigned long totalSize
= 0;
175 for (std::string
const& file
: this->PackageFiles
) {
176 totalSize
+= cmSystemTools::FileLength(file
);
179 out
<< "Installed-Size: " << (totalSize
+ 1023) / 1024 << "\n\n";
182 bool DebGenerator::generateDataTar() const
184 std::string filename_data_tar
=
185 this->WorkDir
+ "/data.tar" + this->CompressionSuffix
;
186 cmGeneratedFileStream fileStream_data_tar
;
187 fileStream_data_tar
.Open(filename_data_tar
, false, true);
188 if (!fileStream_data_tar
) {
189 cmCPackLogger(cmCPackLog::LOG_ERROR
,
190 "Error opening the file \""
191 << filename_data_tar
<< "\" for writing" << std::endl
);
194 cmArchiveWrite
data_tar(fileStream_data_tar
, this->TarCompressionType
,
195 this->DebianArchiveType
, 0,
196 static_cast<int>(this->NumThreads
));
197 if (!data_tar
.Open()) {
198 cmCPackLogger(cmCPackLog::LOG_ERROR
,
199 "Error opening the archive \""
201 << "\", ERROR = " << data_tar
.GetError() << std::endl
);
205 // uid/gid should be the one of the root user, and this root user has
206 // always uid/gid equal to 0.
207 data_tar
.SetUIDAndGID(0U, 0U);
208 data_tar
.SetUNAMEAndGNAME("root", "root");
210 // now add all directories which have to be compressed
211 // collect all top level install dirs for that
212 // e.g. /opt/bin/foo, /usr/bin/bar and /usr/bin/baz would
213 // give /usr and /opt
214 size_t topLevelLength
= this->WorkDir
.length();
215 cmCPackLogger(cmCPackLog::LOG_DEBUG
,
216 "WDIR: \"" << this->WorkDir
217 << "\", length = " << topLevelLength
<< std::endl
);
218 std::set
<std::string
> orderedFiles
;
220 // we have to reconstruct the parent folders as well
222 for (std::string currentPath
: this->PackageFiles
) {
223 while (currentPath
!= this->WorkDir
) {
224 // the last one IS WorkDir, but we do not want this one:
225 // XXX/application/usr/bin/myprogram with GEN_WDIR=XXX/application
226 // should not add XXX/application
227 orderedFiles
.insert(currentPath
);
228 currentPath
= cmSystemTools::GetFilenamePath(currentPath
);
232 for (std::string
const& file
: orderedFiles
) {
233 cmCPackLogger(cmCPackLog::LOG_DEBUG
,
234 "FILEIT: \"" << file
<< "\"" << std::endl
);
235 std::string::size_type slashPos
= file
.find('/', topLevelLength
+ 1);
236 std::string relativeDir
=
237 file
.substr(topLevelLength
, slashPos
- topLevelLength
);
238 cmCPackLogger(cmCPackLog::LOG_DEBUG
,
239 "RELATIVEDIR: \"" << relativeDir
<< "\"" << std::endl
);
242 std::string mode_t_adt_filename
= file
+ ":cmake_mode_t";
243 cmsys::ifstream
permissionStream(mode_t_adt_filename
.c_str());
245 mode_t permissions
= 0;
247 if (permissionStream
) {
248 permissionStream
>> std::oct
>> permissions
;
251 if (permissions
!= 0) {
252 data_tar
.SetPermissions(permissions
);
253 } else if (cmSystemTools::FileIsDirectory(file
)) {
254 data_tar
.SetPermissions(0755);
256 data_tar
.ClearPermissions();
260 // do not recurse because the loop will do it
261 if (!data_tar
.Add(file
, topLevelLength
, ".", false)) {
262 cmCPackLogger(cmCPackLog::LOG_ERROR
,
263 "Problem adding file to tar:\n"
264 "#top level directory: "
271 << data_tar
.GetError() << std::endl
);
278 std::string
DebGenerator::generateMD5File() const
280 std::string md5filename
= this->WorkDir
+ "/md5sums";
282 cmGeneratedFileStream out
;
283 out
.Open(md5filename
, false, true);
285 std::string topLevelWithTrailingSlash
= cmStrCat(this->TemporaryDir
, '/');
286 for (std::string
const& file
: this->PackageFiles
) {
287 // hash only regular files
288 if (cmSystemTools::FileIsDirectory(file
) ||
289 cmSystemTools::FileIsSymlink(file
)) {
293 cmCryptoHash
hasher(cmCryptoHash::AlgoMD5
);
294 std::string output
= hasher
.HashFile(file
);
295 if (output
.empty()) {
296 cmCPackLogger(cmCPackLog::LOG_ERROR
,
297 "Problem computing the md5 of " << file
<< std::endl
);
300 output
+= " " + file
+ "\n";
301 // debian md5sums entries are like this:
302 // 014f3604694729f3bf19263bac599765 usr/bin/ccmake
303 // thus strip the full path (with the trailing slash)
304 cmSystemTools::ReplaceString(output
, topLevelWithTrailingSlash
.c_str(),
308 // each line contains a eol.
309 // Do not end the md5sum file with yet another (invalid)
313 bool DebGenerator::generateControlTar(std::string
const& md5Filename
) const
315 std::string filename_control_tar
= this->WorkDir
+ "/control.tar.gz";
317 cmGeneratedFileStream fileStream_control_tar
;
318 fileStream_control_tar
.Open(filename_control_tar
, false, true);
319 if (!fileStream_control_tar
) {
320 cmCPackLogger(cmCPackLog::LOG_ERROR
,
321 "Error opening the file \""
322 << filename_control_tar
<< "\" for writing" << std::endl
);
325 cmArchiveWrite
control_tar(fileStream_control_tar
,
326 cmArchiveWrite::CompressGZip
,
327 this->DebianArchiveType
);
328 if (!control_tar
.Open()) {
329 cmCPackLogger(cmCPackLog::LOG_ERROR
,
330 "Error opening the archive \""
331 << filename_control_tar
332 << "\", ERROR = " << control_tar
.GetError() << std::endl
);
336 // sets permissions and uid/gid for the files
337 control_tar
.SetUIDAndGID(0u, 0u);
338 control_tar
.SetUNAMEAndGNAME("root", "root");
340 /* permissions are set according to
341 https://www.debian.org/doc/debian-policy/ch-files.html#s-permissions-owners
343 https://lintian.debian.org/tags/control-file-has-bad-permissions.html
345 const mode_t permission644
= 0644;
346 const mode_t permissionExecute
= 0111;
347 const mode_t permission755
= permission644
| permissionExecute
;
349 // for md5sum and control (that we have generated here), we use 644
351 // so that deb lintian doesn't warn about it
352 control_tar
.SetPermissions(permission644
);
354 // adds control and md5sums
355 if (!control_tar
.Add(md5Filename
, this->WorkDir
.length(), ".") ||
356 !control_tar
.Add(this->WorkDir
+ "/control", this->WorkDir
.length(),
358 cmCPackLogger(cmCPackLog::LOG_ERROR
,
359 "Error adding file to tar:\n"
360 "#top level directory: "
363 "#file: \"control\" or \"md5sums\"\n"
365 << control_tar
.GetError() << std::endl
);
369 // adds generated shlibs file
370 if (this->GenShLibs
) {
371 if (!control_tar
.Add(this->ShLibsFilename
, this->WorkDir
.length(), ".")) {
372 cmCPackLogger(cmCPackLog::LOG_ERROR
,
373 "Error adding file to tar:\n"
374 "#top level directory: "
377 "#file: \"shlibs\"\n"
379 << control_tar
.GetError() << std::endl
);
384 // adds LDCONFIG related files
385 if (this->GenPostInst
) {
386 control_tar
.SetPermissions(permission755
);
387 if (!control_tar
.Add(this->PostInst
, this->WorkDir
.length(), ".")) {
388 cmCPackLogger(cmCPackLog::LOG_ERROR
,
389 "Error adding file to tar:\n"
390 "#top level directory: "
393 "#file: \"postinst\"\n"
395 << control_tar
.GetError() << std::endl
);
398 control_tar
.SetPermissions(permission644
);
401 if (this->GenPostRm
) {
402 control_tar
.SetPermissions(permission755
);
403 if (!control_tar
.Add(this->PostRm
, this->WorkDir
.length(), ".")) {
404 cmCPackLogger(cmCPackLog::LOG_ERROR
,
405 "Error adding file to tar:\n"
406 "#top level directory: "
409 "#file: \"postinst\"\n"
411 << control_tar
.GetError() << std::endl
);
414 control_tar
.SetPermissions(permission644
);
417 // for the other files, we use
418 // -either the original permission on the files
419 // -either a permission strictly defined by the Debian policies
420 if (this->ControlExtra
) {
421 // permissions are now controlled by the original file permissions
423 static const char* strictFiles
[] = { "config", "postinst", "postrm",
424 "preinst", "prerm" };
425 std::set
<std::string
> setStrictFiles(
426 strictFiles
, strictFiles
+ sizeof(strictFiles
) / sizeof(strictFiles
[0]));
429 control_tar
.ClearPermissions();
431 cmList controlExtraList
{ this->ControlExtra
};
432 for (std::string
const& i
: controlExtraList
) {
433 std::string filenamename
= cmsys::SystemTools::GetFilenameName(i
);
434 std::string localcopy
= this->WorkDir
+ "/" + filenamename
;
436 if (this->PermissionStrictPolicy
) {
437 control_tar
.SetPermissions(
438 setStrictFiles
.count(filenamename
) ? permission755
: permission644
);
441 // if we can copy the file, it means it does exist, let's add it:
442 if (!cmsys::SystemTools::FileExists(i
)) {
443 cmCPackLogger(cmCPackLog::LOG_WARNING
,
444 "Adding file to tar:\n"
445 "#top level directory: "
452 if (cmsys::SystemTools::CopyFileIfDifferent(i
, localcopy
)) {
453 control_tar
.Add(localcopy
, this->WorkDir
.length(), ".");
461 bool DebGenerator::generateDeb() const
463 // ar -r your-package-name.deb debian-binary control.tar.* data.tar.*
464 // A debian package .deb is simply an 'ar' archive. The only subtle
465 // difference is that debian uses the BSD ar style archive whereas most
466 // Linux distro have a GNU ar.
467 // See http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=161593 for more info
468 std::string
const outputPath
= this->TopLevelDir
+ "/" + this->OutputName
;
469 std::string
const tlDir
= this->WorkDir
+ "/";
470 cmGeneratedFileStream debStream
;
471 debStream
.Open(outputPath
, false, true);
472 cmArchiveWrite
deb(debStream
, cmArchiveWrite::CompressNone
, "arbsd");
474 cmCPackLogger(cmCPackLog::LOG_ERROR
,
475 "Error opening the archive \""
476 << outputPath
<< "\", ERROR = " << deb
.GetError()
481 // uid/gid should be the one of the root user, and this root user has
482 // always uid/gid equal to 0.
483 deb
.SetUIDAndGID(0u, 0u);
484 deb
.SetUNAMEAndGNAME("root", "root");
486 if (!deb
.Add(tlDir
+ "debian-binary", tlDir
.length()) ||
487 !deb
.Add(tlDir
+ "control.tar.gz", tlDir
.length()) ||
488 !deb
.Add(tlDir
+ "data.tar" + this->CompressionSuffix
, tlDir
.length())) {
489 cmCPackLogger(cmCPackLog::LOG_ERROR
,
490 "Error creating debian package:\n"
491 "#top level directory: "
498 << deb
.GetError() << std::endl
);
504 std::vector
<std::string
> findFilesIn(const std::string
& path
)
507 std::string findExpr
= path
+ "/*";
509 gl
.SetRecurseListDirs(true);
510 gl
.SetRecurseThroughSymlinks(false);
511 if (!gl
.FindFiles(findExpr
)) {
512 throw std::runtime_error(
513 "Cannot find any files in the installed directory");
515 std::vector
<std::string
> files
{ gl
.GetFiles() };
516 // Sort files so that they have a reproducible order
517 std::sort(files
.begin(), files
.end());
521 } // end anonymous namespace
523 cmCPackDebGenerator::cmCPackDebGenerator() = default;
525 cmCPackDebGenerator::~cmCPackDebGenerator() = default;
527 int cmCPackDebGenerator::InitializeInternal()
529 this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/usr");
530 if (this->GetOption("CPACK_SET_DESTDIR").IsOff()) {
531 this->SetOption("CPACK_SET_DESTDIR", "I_ON");
533 return this->Superclass::InitializeInternal();
536 int cmCPackDebGenerator::PackageOnePack(std::string
const& initialTopLevel
,
537 std::string
const& packageName
)
539 // Determine the sanitized packaging directory-name that can be used on the
541 std::string sanitizedPkgDirName
=
542 this->GetSanitizedDirOrFileName(packageName
);
544 // Begin the archive for this pack
545 std::string
localToplevel(initialTopLevel
);
546 std::string
packageFileName(
547 cmSystemTools::GetParentDirectory(this->toplevel
));
548 std::string
outputFileName(*this->GetOption("CPACK_PACKAGE_FILE_NAME") +
549 "-" + packageName
+ this->GetOutputExtension());
551 localToplevel
+= "/" + sanitizedPkgDirName
;
552 /* replace the TEMP DIRECTORY with the component one */
553 this->SetOption("CPACK_TEMPORARY_DIRECTORY", localToplevel
);
554 packageFileName
+= "/" + outputFileName
;
555 /* replace proposed CPACK_OUTPUT_FILE_NAME */
556 this->SetOption("CPACK_OUTPUT_FILE_NAME", outputFileName
);
557 /* replace the TEMPORARY package file name */
558 this->SetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME", packageFileName
);
559 // Tell CPackDeb.cmake the name of the component GROUP.
560 this->SetOption("CPACK_DEB_PACKAGE_COMPONENT", packageName
);
561 // Tell CPackDeb.cmake the path where the component is.
562 std::string component_path
= cmStrCat('/', sanitizedPkgDirName
);
563 this->SetOption("CPACK_DEB_PACKAGE_COMPONENT_PART_PATH", component_path
);
564 if (!this->ReadListFile("Internal/CPack/CPackDeb.cmake")) {
565 cmCPackLogger(cmCPackLog::LOG_ERROR
,
566 "Error while execution CPackDeb.cmake" << std::endl
);
570 return this->createDebPackages();
573 int cmCPackDebGenerator::PackageComponents(bool ignoreGroup
)
575 // Reset package file name list it will be populated during the
576 // component packaging run
577 this->packageFileNames
.clear();
578 std::string
initialTopLevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY"));
581 // The default behavior is to have one package by component group
582 // unless CPACK_COMPONENTS_IGNORE_GROUP is specified.
584 // CPACK_COMPONENTS_IGNORE_GROUPS is set
585 // We build 1 package per component
586 for (auto const& comp
: this->Components
) {
587 retval
&= this->PackageOnePack(initialTopLevel
, comp
.first
);
592 for (auto const& compG
: this->ComponentGroups
) {
593 cmCPackLogger(cmCPackLog::LOG_VERBOSE
,
594 "Packaging component group: " << compG
.first
<< std::endl
);
595 // Begin the archive for this group
596 retval
&= this->PackageOnePack(initialTopLevel
, compG
.first
);
598 // Handle Orphan components (components not belonging to any groups)
599 for (auto const& comp
: this->Components
) {
600 // Does the component belong to a group?
601 if (comp
.second
.Group
== nullptr) {
603 cmCPackLog::LOG_VERBOSE
,
606 << "> does not belong to any group, package it separately."
608 // Begin the archive for this orphan component
609 retval
&= this->PackageOnePack(initialTopLevel
, comp
.first
);
615 //----------------------------------------------------------------------
616 int cmCPackDebGenerator::PackageComponentsAllInOne(
617 const std::string
& compInstDirName
)
619 /* Reset package file name list it will be populated during the
620 * component packaging run*/
621 this->packageFileNames
.clear();
622 std::string
initialTopLevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY"));
624 cmCPackLogger(cmCPackLog::LOG_VERBOSE
,
625 "Packaging all groups in one package..."
626 "(CPACK_COMPONENTS_ALL_[GROUPS_]IN_ONE_PACKAGE is set)"
629 // The ALL GROUPS in ONE package case
630 std::string
localToplevel(initialTopLevel
);
631 std::string
packageFileName(
632 cmSystemTools::GetParentDirectory(this->toplevel
));
633 std::string
outputFileName(*this->GetOption("CPACK_PACKAGE_FILE_NAME") +
634 this->GetOutputExtension());
635 // all GROUP in one vs all COMPONENT in one
636 // if must be here otherwise non component paths have a trailing / while
638 if (!compInstDirName
.empty()) {
639 localToplevel
+= "/" + compInstDirName
;
642 /* replace the TEMP DIRECTORY with the component one */
643 this->SetOption("CPACK_TEMPORARY_DIRECTORY", localToplevel
);
644 packageFileName
+= "/" + outputFileName
;
645 /* replace proposed CPACK_OUTPUT_FILE_NAME */
646 this->SetOption("CPACK_OUTPUT_FILE_NAME", outputFileName
);
647 /* replace the TEMPORARY package file name */
648 this->SetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME", packageFileName
);
650 if (!compInstDirName
.empty()) {
651 // Tell CPackDeb.cmake the path where the component is.
652 std::string component_path
= cmStrCat('/', compInstDirName
);
653 this->SetOption("CPACK_DEB_PACKAGE_COMPONENT_PART_PATH", component_path
);
655 if (!this->ReadListFile("Internal/CPack/CPackDeb.cmake")) {
656 cmCPackLogger(cmCPackLog::LOG_ERROR
,
657 "Error while execution CPackDeb.cmake" << std::endl
);
661 return this->createDebPackages();
664 int cmCPackDebGenerator::PackageFiles()
666 /* Are we in the component packaging case */
667 if (this->WantsComponentInstallation()) {
668 // CASE 1 : COMPONENT ALL-IN-ONE package
669 // If ALL GROUPS or ALL COMPONENTS in ONE package has been requested
670 // then the package file is unique and should be open here.
671 if (this->componentPackageMethod
== ONE_PACKAGE
) {
672 return this->PackageComponentsAllInOne("ALL_COMPONENTS_IN_ONE");
674 // CASE 2 : COMPONENT CLASSICAL package(s) (i.e. not all-in-one)
675 // There will be 1 package for each component group
676 // however one may require to ignore component group and
677 // in this case you'll get 1 package for each component.
678 return this->PackageComponents(this->componentPackageMethod
==
679 ONE_PACKAGE_PER_COMPONENT
);
681 // CASE 3 : NON COMPONENT package.
682 return this->PackageComponentsAllInOne("");
685 bool cmCPackDebGenerator::createDebPackages()
687 auto make_package
= [this](const std::string
& path
,
688 const char* const output_var
,
689 bool (cmCPackDebGenerator::*creator
)()) -> bool {
691 this->packageFiles
= findFilesIn(path
);
692 } catch (const std::runtime_error
& ex
) {
693 cmCPackLogger(cmCPackLog::LOG_ERROR
, ex
.what() << std::endl
);
697 if ((this->*creator
)()) {
698 // add the generated package to package file names list
699 this->packageFileNames
.emplace_back(
700 cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), '/',
701 this->GetOption(output_var
)));
707 make_package(this->GetOption("GEN_WDIR"), "GEN_CPACK_OUTPUT_FILE_NAME",
708 &cmCPackDebGenerator::createDeb
);
709 cmValue dbgsymdir_path
= this->GetOption("GEN_DBGSYMDIR");
710 if (this->IsOn("GEN_CPACK_DEBIAN_DEBUGINFO_PACKAGE") && dbgsymdir_path
) {
711 retval
= make_package(*dbgsymdir_path
, "GEN_CPACK_DBGSYM_OUTPUT_FILE_NAME",
712 &cmCPackDebGenerator::createDbgsymDDeb
) &&
715 return static_cast<int>(retval
);
718 bool cmCPackDebGenerator::createDeb()
720 std::map
<std::string
, std::string
> controlValues
;
722 // debian policy enforce lower case for package name
723 controlValues
["Package"] = cmsys::SystemTools::LowerCase(
724 this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_NAME"));
725 controlValues
["Version"] =
726 *this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_VERSION");
727 controlValues
["Section"] =
728 *this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SECTION");
729 controlValues
["Priority"] =
730 *this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_PRIORITY");
731 controlValues
["Architecture"] =
732 *this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_ARCHITECTURE");
733 controlValues
["Maintainer"] =
734 *this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_MAINTAINER");
735 controlValues
["Description"] =
736 *this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_DESCRIPTION");
738 cmValue debian_pkg_source
=
739 this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SOURCE");
740 if (cmNonempty(debian_pkg_source
)) {
741 controlValues
["Source"] = *debian_pkg_source
;
743 cmValue debian_pkg_dep
= this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_DEPENDS");
744 if (cmNonempty(debian_pkg_dep
)) {
745 controlValues
["Depends"] = *debian_pkg_dep
;
747 cmValue debian_pkg_rec
=
748 this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_RECOMMENDS");
749 if (cmNonempty(debian_pkg_rec
)) {
750 controlValues
["Recommends"] = *debian_pkg_rec
;
752 cmValue debian_pkg_sug
=
753 this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SUGGESTS");
754 if (cmNonempty(debian_pkg_sug
)) {
755 controlValues
["Suggests"] = *debian_pkg_sug
;
757 cmValue debian_pkg_url
=
758 this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_HOMEPAGE");
759 if (cmNonempty(debian_pkg_url
)) {
760 controlValues
["Homepage"] = *debian_pkg_url
;
762 cmValue debian_pkg_predep
=
763 this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_PREDEPENDS");
764 if (cmNonempty(debian_pkg_predep
)) {
765 controlValues
["Pre-Depends"] = *debian_pkg_predep
;
767 cmValue debian_pkg_enhances
=
768 this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_ENHANCES");
769 if (cmNonempty(debian_pkg_enhances
)) {
770 controlValues
["Enhances"] = *debian_pkg_enhances
;
772 cmValue debian_pkg_breaks
=
773 this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_BREAKS");
774 if (cmNonempty(debian_pkg_breaks
)) {
775 controlValues
["Breaks"] = *debian_pkg_breaks
;
777 cmValue debian_pkg_conflicts
=
778 this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_CONFLICTS");
779 if (cmNonempty(debian_pkg_conflicts
)) {
780 controlValues
["Conflicts"] = *debian_pkg_conflicts
;
782 cmValue debian_pkg_provides
=
783 this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_PROVIDES");
784 if (cmNonempty(debian_pkg_provides
)) {
785 controlValues
["Provides"] = *debian_pkg_provides
;
787 cmValue debian_pkg_replaces
=
788 this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_REPLACES");
789 if (cmNonempty(debian_pkg_replaces
)) {
790 controlValues
["Replaces"] = *debian_pkg_replaces
;
792 cmValue debian_pkg_multiarch
=
793 this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_MULTIARCH");
794 if (cmNonempty(debian_pkg_multiarch
)) {
795 // check for valid values: same, foreign, allowed
796 if (*debian_pkg_multiarch
!= "same" &&
797 *debian_pkg_multiarch
!= "foreign" &&
798 *debian_pkg_multiarch
!= "allowed") {
799 cmCPackLogger(cmCPackLog::LOG_ERROR
,
800 "Error: invalid value for Multi-Arch: "
801 << *debian_pkg_multiarch
802 << ". Valid values are: same, foreign, allowed\n");
805 controlValues
["Multi-Arch"] = *debian_pkg_multiarch
;
808 const std::string
strGenWDIR(this->GetOption("GEN_WDIR"));
809 const std::string shlibsfilename
= strGenWDIR
+ "/shlibs";
811 cmValue debian_pkg_shlibs
=
812 this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SHLIBS");
813 const bool gen_shibs
= this->IsOn("CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS") &&
814 cmNonempty(debian_pkg_shlibs
);
816 cmGeneratedFileStream out
;
817 out
.Open(shlibsfilename
, false, true);
818 out
<< debian_pkg_shlibs
;
822 const std::string postinst
= strGenWDIR
+ "/postinst";
823 const std::string postrm
= strGenWDIR
+ "/postrm";
824 if (this->IsOn("GEN_CPACK_DEBIAN_GENERATE_POSTINST")) {
825 cmGeneratedFileStream out
;
826 out
.Open(postinst
, false, true);
827 out
<< "#!/bin/sh\n\n"
829 "if [ \"$1\" = \"configure\" ]; then\n"
833 if (this->IsOn("GEN_CPACK_DEBIAN_GENERATE_POSTRM")) {
834 cmGeneratedFileStream out
;
835 out
.Open(postrm
, false, true);
836 out
<< "#!/bin/sh\n\n"
838 "if [ \"$1\" = \"remove\" ]; then\n"
844 this->Logger
, this->GetOption("GEN_CPACK_OUTPUT_FILE_NAME"), strGenWDIR
,
845 this->GetOption("CPACK_TOPLEVEL_DIRECTORY"),
846 this->GetOption("CPACK_TEMPORARY_DIRECTORY"),
847 this->GetOption("GEN_CPACK_DEBIAN_COMPRESSION_TYPE"),
848 this->GetOption("CPACK_THREADS"),
849 this->GetOption("GEN_CPACK_DEBIAN_ARCHIVE_TYPE"), controlValues
, gen_shibs
,
850 shlibsfilename
, this->IsOn("GEN_CPACK_DEBIAN_GENERATE_POSTINST"), postinst
,
851 this->IsOn("GEN_CPACK_DEBIAN_GENERATE_POSTRM"), postrm
,
852 this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA"),
853 this->IsSet("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION"),
856 return gen
.generate();
859 bool cmCPackDebGenerator::createDbgsymDDeb()
861 // Packages containing debug symbols follow the same structure as .debs
862 // but have different metadata and content.
864 std::map
<std::string
, std::string
> controlValues
;
865 // debian policy enforce lower case for package name
866 std::string packageNameLower
= cmsys::SystemTools::LowerCase(
867 this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_NAME"));
868 cmValue debian_pkg_version
=
869 this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_VERSION");
871 controlValues
["Package"] = packageNameLower
+ "-dbgsym";
872 controlValues
["Package-Type"] = "ddeb";
873 controlValues
["Version"] = *debian_pkg_version
;
874 controlValues
["Auto-Built-Package"] = "debug-symbols";
875 controlValues
["Depends"] =
876 *this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_NAME") + std::string(" (= ") +
877 *debian_pkg_version
+ ")";
878 controlValues
["Section"] = "debug";
879 controlValues
["Priority"] = "optional";
880 controlValues
["Architecture"] =
881 *this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_ARCHITECTURE");
882 controlValues
["Maintainer"] =
883 *this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_MAINTAINER");
884 controlValues
["Description"] =
885 std::string("debug symbols for ") + packageNameLower
;
887 cmValue debian_pkg_source
=
888 this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SOURCE");
889 if (cmNonempty(debian_pkg_source
)) {
890 controlValues
["Source"] = *debian_pkg_source
;
892 cmValue debian_build_ids
= this->GetOption("GEN_BUILD_IDS");
893 if (cmNonempty(debian_build_ids
)) {
894 controlValues
["Build-Ids"] = *debian_build_ids
;
898 this->Logger
, this->GetOption("GEN_CPACK_DBGSYM_OUTPUT_FILE_NAME"),
899 this->GetOption("GEN_DBGSYMDIR"),
900 this->GetOption("CPACK_TOPLEVEL_DIRECTORY"),
901 this->GetOption("CPACK_TEMPORARY_DIRECTORY"),
902 this->GetOption("GEN_CPACK_DEBIAN_COMPRESSION_TYPE"),
903 this->GetOption("CPACK_THREADS"),
904 this->GetOption("GEN_CPACK_DEBIAN_ARCHIVE_TYPE"), controlValues
, false, "",
905 false, "", false, "", nullptr,
906 this->IsSet("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION"),
909 return gen
.generate();
912 bool cmCPackDebGenerator::SupportsComponentInstallation() const
914 return this->IsOn("CPACK_DEB_COMPONENT_INSTALL");
917 std::string
cmCPackDebGenerator::GetComponentInstallSuffix(
918 const std::string
& componentName
)
920 if (this->componentPackageMethod
== ONE_PACKAGE_PER_COMPONENT
) {
921 return componentName
;
924 if (this->componentPackageMethod
== ONE_PACKAGE
) {
925 return { "ALL_COMPONENTS_IN_ONE" };
927 // We have to find the name of the COMPONENT GROUP
928 // the current COMPONENT belongs to.
929 std::string groupVar
=
930 "CPACK_COMPONENT_" + cmSystemTools::UpperCase(componentName
) + "_GROUP";
931 if (nullptr != this->GetOption(groupVar
)) {
932 return *this->GetOption(groupVar
);
934 return componentName
;
937 std::string
cmCPackDebGenerator::GetComponentInstallDirNameSuffix(
938 const std::string
& componentName
)
940 return this->GetSanitizedDirOrFileName(
941 this->GetComponentInstallSuffix(componentName
));