Imported Upstream version 3.21.4
[platform/upstream/cmake.git] / Source / CPack / cmCPackDebGenerator.cxx
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"
4
5 #include <cstdlib>
6 #include <cstring>
7 #include <map>
8 #include <ostream>
9 #include <set>
10 #include <utility>
11
12 #include "cmsys/Glob.hxx"
13
14 #include "cm_sys_stat.h"
15
16 #include "cmArchiveWrite.h"
17 #include "cmCPackComponentGroup.h"
18 #include "cmCPackGenerator.h"
19 #include "cmCPackLog.h"
20 #include "cmCryptoHash.h"
21 #include "cmGeneratedFileStream.h"
22 #include "cmStringAlgorithms.h"
23 #include "cmSystemTools.h"
24
25 namespace {
26
27 class DebGenerator
28 {
29 public:
30   DebGenerator(cmCPackLog* logger, std::string outputName, std::string workDir,
31                std::string topLevelDir, std::string temporaryDir,
32                const char* debianCompressionType, const char* numThreads,
33                const char* debianArchiveType,
34                std::map<std::string, std::string> controlValues,
35                bool genShLibs, std::string shLibsFilename, bool genPostInst,
36                std::string postInst, bool genPostRm, std::string postRm,
37                const char* controlExtra, bool permissionStrctPolicy,
38                std::vector<std::string> packageFiles);
39
40   bool generate() const;
41
42 private:
43   void generateDebianBinaryFile() const;
44   void generateControlFile() const;
45   bool generateDataTar() const;
46   std::string generateMD5File() const;
47   bool generateControlTar(std::string const& md5Filename) const;
48   bool generateDeb() const;
49
50   cmCPackLog* Logger;
51   const std::string OutputName;
52   const std::string WorkDir;
53   std::string CompressionSuffix;
54   const std::string TopLevelDir;
55   const std::string TemporaryDir;
56   const char* DebianArchiveType;
57   int NumThreads;
58   const std::map<std::string, std::string> ControlValues;
59   const bool GenShLibs;
60   const std::string ShLibsFilename;
61   const bool GenPostInst;
62   const std::string PostInst;
63   const bool GenPostRm;
64   const std::string PostRm;
65   const char* ControlExtra;
66   const bool PermissionStrictPolicy;
67   const std::vector<std::string> PackageFiles;
68   cmArchiveWrite::Compress TarCompressionType;
69 };
70
71 DebGenerator::DebGenerator(
72   cmCPackLog* logger, std::string outputName, std::string workDir,
73   std::string topLevelDir, std::string temporaryDir,
74   const char* debianCompressionType, const char* numThreads,
75   const char* debianArchiveType,
76   std::map<std::string, std::string> controlValues, bool genShLibs,
77   std::string shLibsFilename, bool genPostInst, std::string postInst,
78   bool genPostRm, std::string postRm, const char* controlExtra,
79   bool permissionStrictPolicy, std::vector<std::string> packageFiles)
80   : Logger(logger)
81   , OutputName(std::move(outputName))
82   , WorkDir(std::move(workDir))
83   , TopLevelDir(std::move(topLevelDir))
84   , TemporaryDir(std::move(temporaryDir))
85   , DebianArchiveType(debianArchiveType ? debianArchiveType : "gnutar")
86   , ControlValues(std::move(controlValues))
87   , GenShLibs(genShLibs)
88   , ShLibsFilename(std::move(shLibsFilename))
89   , GenPostInst(genPostInst)
90   , PostInst(std::move(postInst))
91   , GenPostRm(genPostRm)
92   , PostRm(std::move(postRm))
93   , ControlExtra(controlExtra)
94   , PermissionStrictPolicy(permissionStrictPolicy)
95   , PackageFiles(std::move(packageFiles))
96 {
97   if (!debianCompressionType) {
98     debianCompressionType = "gzip";
99   }
100
101   if (!strcmp(debianCompressionType, "lzma")) {
102     this->CompressionSuffix = ".lzma";
103     this->TarCompressionType = cmArchiveWrite::CompressLZMA;
104   } else if (!strcmp(debianCompressionType, "xz")) {
105     this->CompressionSuffix = ".xz";
106     this->TarCompressionType = cmArchiveWrite::CompressXZ;
107   } else if (!strcmp(debianCompressionType, "bzip2")) {
108     this->CompressionSuffix = ".bz2";
109     this->TarCompressionType = cmArchiveWrite::CompressBZip2;
110   } else if (!strcmp(debianCompressionType, "gzip")) {
111     this->CompressionSuffix = ".gz";
112     this->TarCompressionType = cmArchiveWrite::CompressGZip;
113   } else if (!strcmp(debianCompressionType, "none")) {
114     this->CompressionSuffix.clear();
115     this->TarCompressionType = cmArchiveWrite::CompressNone;
116   } else {
117     cmCPackLogger(cmCPackLog::LOG_ERROR,
118                   "Error unrecognized compression type: "
119                     << debianCompressionType << std::endl);
120   }
121
122   if (numThreads == nullptr) {
123     numThreads = "1";
124   }
125
126   char* endptr;
127   this->NumThreads = static_cast<int>(strtol(numThreads, &endptr, 10));
128   if (numThreads != endptr && *endptr != '\0') {
129     cmCPackLogger(cmCPackLog::LOG_ERROR,
130                   "Unrecognized number of threads: " << numThreads
131                                                      << std::endl);
132   }
133 }
134
135 bool DebGenerator::generate() const
136 {
137   this->generateDebianBinaryFile();
138   this->generateControlFile();
139   if (!this->generateDataTar()) {
140     return false;
141   }
142   std::string md5Filename = this->generateMD5File();
143   if (!this->generateControlTar(md5Filename)) {
144     return false;
145   }
146   return this->generateDeb();
147 }
148
149 void DebGenerator::generateDebianBinaryFile() const
150 {
151   // debian-binary file
152   const std::string dbfilename = this->WorkDir + "/debian-binary";
153   cmGeneratedFileStream out;
154   out.Open(dbfilename, false, true);
155   out << "2.0\n"; // required for valid debian package
156 }
157
158 void DebGenerator::generateControlFile() const
159 {
160   std::string ctlfilename = this->WorkDir + "/control";
161
162   cmGeneratedFileStream out;
163   out.Open(ctlfilename, false, true);
164   for (auto const& kv : this->ControlValues) {
165     out << kv.first << ": " << kv.second << "\n";
166   }
167
168   unsigned long totalSize = 0;
169   {
170     for (std::string const& file : this->PackageFiles) {
171       totalSize += cmSystemTools::FileLength(file);
172     }
173   }
174   out << "Installed-Size: " << (totalSize + 1023) / 1024 << "\n\n";
175 }
176
177 bool DebGenerator::generateDataTar() const
178 {
179   std::string filename_data_tar =
180     this->WorkDir + "/data.tar" + this->CompressionSuffix;
181   cmGeneratedFileStream fileStream_data_tar;
182   fileStream_data_tar.Open(filename_data_tar, false, true);
183   if (!fileStream_data_tar) {
184     cmCPackLogger(cmCPackLog::LOG_ERROR,
185                   "Error opening the file \""
186                     << filename_data_tar << "\" for writing" << std::endl);
187     return false;
188   }
189   cmArchiveWrite data_tar(fileStream_data_tar, this->TarCompressionType,
190                           this->DebianArchiveType, 0, this->NumThreads);
191   data_tar.Open();
192
193   // uid/gid should be the one of the root user, and this root user has
194   // always uid/gid equal to 0.
195   data_tar.SetUIDAndGID(0u, 0u);
196   data_tar.SetUNAMEAndGNAME("root", "root");
197
198   // now add all directories which have to be compressed
199   // collect all top level install dirs for that
200   // e.g. /opt/bin/foo, /usr/bin/bar and /usr/bin/baz would
201   // give /usr and /opt
202   size_t topLevelLength = this->WorkDir.length();
203   cmCPackLogger(cmCPackLog::LOG_DEBUG,
204                 "WDIR: \"" << this->WorkDir
205                            << "\", length = " << topLevelLength << std::endl);
206   std::set<std::string> orderedFiles;
207
208   // we have to reconstruct the parent folders as well
209
210   for (std::string currentPath : this->PackageFiles) {
211     while (currentPath != this->WorkDir) {
212       // the last one IS WorkDir, but we do not want this one:
213       // XXX/application/usr/bin/myprogram with GEN_WDIR=XXX/application
214       // should not add XXX/application
215       orderedFiles.insert(currentPath);
216       currentPath = cmSystemTools::CollapseFullPath("..", currentPath);
217     }
218   }
219
220   for (std::string const& file : orderedFiles) {
221     cmCPackLogger(cmCPackLog::LOG_DEBUG,
222                   "FILEIT: \"" << file << "\"" << std::endl);
223     std::string::size_type slashPos = file.find('/', topLevelLength + 1);
224     std::string relativeDir =
225       file.substr(topLevelLength, slashPos - topLevelLength);
226     cmCPackLogger(cmCPackLog::LOG_DEBUG,
227                   "RELATIVEDIR: \"" << relativeDir << "\"" << std::endl);
228
229 #ifdef _WIN32
230     std::string mode_t_adt_filename = file + ":cmake_mode_t";
231     cmsys::ifstream permissionStream(mode_t_adt_filename.c_str());
232
233     mode_t permissions = 0;
234
235     if (permissionStream) {
236       permissionStream >> std::oct >> permissions;
237     }
238
239     if (permissions != 0) {
240       data_tar.SetPermissions(permissions);
241     } else if (cmSystemTools::FileIsDirectory(file)) {
242       data_tar.SetPermissions(0755);
243     } else {
244       data_tar.ClearPermissions();
245     }
246 #endif
247
248     // do not recurse because the loop will do it
249     if (!data_tar.Add(file, topLevelLength, ".", false)) {
250       cmCPackLogger(cmCPackLog::LOG_ERROR,
251                     "Problem adding file to tar:"
252                       << std::endl
253                       << "#top level directory: " << this->WorkDir << std::endl
254                       << "#file: " << file << std::endl
255                       << "#error:" << data_tar.GetError() << std::endl);
256       return false;
257     }
258   }
259   return true;
260 }
261
262 std::string DebGenerator::generateMD5File() const
263 {
264   std::string md5filename = this->WorkDir + "/md5sums";
265
266   cmGeneratedFileStream out;
267   out.Open(md5filename, false, true);
268
269   std::string topLevelWithTrailingSlash = cmStrCat(this->TemporaryDir, '/');
270   for (std::string const& file : this->PackageFiles) {
271     // hash only regular files
272     if (cmSystemTools::FileIsDirectory(file) ||
273         cmSystemTools::FileIsSymlink(file)) {
274       continue;
275     }
276
277     std::string output =
278       cmSystemTools::ComputeFileHash(file, cmCryptoHash::AlgoMD5);
279     if (output.empty()) {
280       cmCPackLogger(cmCPackLog::LOG_ERROR,
281                     "Problem computing the md5 of " << file << std::endl);
282     }
283
284     output += "  " + file + "\n";
285     // debian md5sums entries are like this:
286     // 014f3604694729f3bf19263bac599765  usr/bin/ccmake
287     // thus strip the full path (with the trailing slash)
288     cmSystemTools::ReplaceString(output, topLevelWithTrailingSlash.c_str(),
289                                  "");
290     out << output;
291   }
292   // each line contains a eol.
293   // Do not end the md5sum file with yet another (invalid)
294   return md5filename;
295 }
296
297 bool DebGenerator::generateControlTar(std::string const& md5Filename) const
298 {
299   std::string filename_control_tar = this->WorkDir + "/control.tar.gz";
300
301   cmGeneratedFileStream fileStream_control_tar;
302   fileStream_control_tar.Open(filename_control_tar, false, true);
303   if (!fileStream_control_tar) {
304     cmCPackLogger(cmCPackLog::LOG_ERROR,
305                   "Error opening the file \""
306                     << filename_control_tar << "\" for writing" << std::endl);
307     return false;
308   }
309   cmArchiveWrite control_tar(fileStream_control_tar,
310                              cmArchiveWrite::CompressGZip,
311                              this->DebianArchiveType);
312   control_tar.Open();
313
314   // sets permissions and uid/gid for the files
315   control_tar.SetUIDAndGID(0u, 0u);
316   control_tar.SetUNAMEAndGNAME("root", "root");
317
318   /* permissions are set according to
319   https://www.debian.org/doc/debian-policy/ch-files.html#s-permissions-owners
320   and
321   https://lintian.debian.org/tags/control-file-has-bad-permissions.html
322   */
323   const mode_t permission644 = 0644;
324   const mode_t permissionExecute = 0111;
325   const mode_t permission755 = permission644 | permissionExecute;
326
327   // for md5sum and control (that we have generated here), we use 644
328   // (RW-R--R--)
329   // so that deb lintian doesn't warn about it
330   control_tar.SetPermissions(permission644);
331
332   // adds control and md5sums
333   if (!control_tar.Add(md5Filename, this->WorkDir.length(), ".") ||
334       !control_tar.Add(this->WorkDir + "/control", this->WorkDir.length(),
335                        ".")) {
336     cmCPackLogger(cmCPackLog::LOG_ERROR,
337                   "Error adding file to tar:"
338                     << std::endl
339                     << "#top level directory: " << this->WorkDir << std::endl
340                     << "#file: \"control\" or \"md5sums\"" << std::endl
341                     << "#error:" << control_tar.GetError() << std::endl);
342     return false;
343   }
344
345   // adds generated shlibs file
346   if (this->GenShLibs) {
347     if (!control_tar.Add(this->ShLibsFilename, this->WorkDir.length(), ".")) {
348       cmCPackLogger(cmCPackLog::LOG_ERROR,
349                     "Error adding file to tar:"
350                       << std::endl
351                       << "#top level directory: " << this->WorkDir << std::endl
352                       << "#file: \"shlibs\"" << std::endl
353                       << "#error:" << control_tar.GetError() << std::endl);
354       return false;
355     }
356   }
357
358   // adds LDCONFIG related files
359   if (this->GenPostInst) {
360     control_tar.SetPermissions(permission755);
361     if (!control_tar.Add(this->PostInst, this->WorkDir.length(), ".")) {
362       cmCPackLogger(cmCPackLog::LOG_ERROR,
363                     "Error adding file to tar:"
364                       << std::endl
365                       << "#top level directory: " << this->WorkDir << std::endl
366                       << "#file: \"postinst\"" << std::endl
367                       << "#error:" << control_tar.GetError() << std::endl);
368       return false;
369     }
370     control_tar.SetPermissions(permission644);
371   }
372
373   if (this->GenPostRm) {
374     control_tar.SetPermissions(permission755);
375     if (!control_tar.Add(this->PostRm, this->WorkDir.length(), ".")) {
376       cmCPackLogger(cmCPackLog::LOG_ERROR,
377                     "Error adding file to tar:"
378                       << std::endl
379                       << "#top level directory: " << this->WorkDir << std::endl
380                       << "#file: \"postinst\"" << std::endl
381                       << "#error:" << control_tar.GetError() << std::endl);
382       return false;
383     }
384     control_tar.SetPermissions(permission644);
385   }
386
387   // for the other files, we use
388   // -either the original permission on the files
389   // -either a permission strictly defined by the Debian policies
390   if (this->ControlExtra) {
391     // permissions are now controlled by the original file permissions
392
393     static const char* strictFiles[] = { "config", "postinst", "postrm",
394                                          "preinst", "prerm" };
395     std::set<std::string> setStrictFiles(
396       strictFiles, strictFiles + sizeof(strictFiles) / sizeof(strictFiles[0]));
397
398     // default
399     control_tar.ClearPermissions();
400
401     std::vector<std::string> controlExtraList =
402       cmExpandedList(this->ControlExtra);
403     for (std::string const& i : controlExtraList) {
404       std::string filenamename = cmsys::SystemTools::GetFilenameName(i);
405       std::string localcopy = this->WorkDir + "/" + filenamename;
406
407       if (this->PermissionStrictPolicy) {
408         control_tar.SetPermissions(
409           setStrictFiles.count(filenamename) ? permission755 : permission644);
410       }
411
412       // if we can copy the file, it means it does exist, let's add it:
413       if (!cmsys::SystemTools::FileExists(i)) {
414         cmCPackLogger(cmCPackLog::LOG_WARNING,
415                       "Adding file to tar:" << std::endl
416                                             << "#top level directory: "
417                                             << this->WorkDir << std::endl
418                                             << "#missing file: " << i
419                                             << std::endl);
420       }
421
422       if (cmsys::SystemTools::CopyFileIfDifferent(i, localcopy)) {
423         control_tar.Add(localcopy, this->WorkDir.length(), ".");
424       }
425     }
426   }
427
428   return true;
429 }
430
431 bool DebGenerator::generateDeb() const
432 {
433   // ar -r your-package-name.deb debian-binary control.tar.* data.tar.*
434   // A debian package .deb is simply an 'ar' archive. The only subtle
435   // difference is that debian uses the BSD ar style archive whereas most
436   // Linux distro have a GNU ar.
437   // See http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=161593 for more info
438   std::string const outputPath = this->TopLevelDir + "/" + this->OutputName;
439   std::string const tlDir = this->WorkDir + "/";
440   cmGeneratedFileStream debStream;
441   debStream.Open(outputPath, false, true);
442   cmArchiveWrite deb(debStream, cmArchiveWrite::CompressNone, "arbsd");
443   deb.Open();
444
445   // uid/gid should be the one of the root user, and this root user has
446   // always uid/gid equal to 0.
447   deb.SetUIDAndGID(0u, 0u);
448   deb.SetUNAMEAndGNAME("root", "root");
449
450   if (!deb.Add(tlDir + "debian-binary", tlDir.length()) ||
451       !deb.Add(tlDir + "control.tar.gz", tlDir.length()) ||
452       !deb.Add(tlDir + "data.tar" + this->CompressionSuffix, tlDir.length())) {
453     cmCPackLogger(cmCPackLog::LOG_ERROR,
454                   "Error creating debian package:"
455                     << std::endl
456                     << "#top level directory: " << this->TopLevelDir
457                     << std::endl
458                     << "#file: " << this->OutputName << std::endl
459                     << "#error:" << deb.GetError() << std::endl);
460     return false;
461   }
462   return true;
463 }
464
465 } // end anonymous namespace
466
467 cmCPackDebGenerator::cmCPackDebGenerator() = default;
468
469 cmCPackDebGenerator::~cmCPackDebGenerator() = default;
470
471 int cmCPackDebGenerator::InitializeInternal()
472 {
473   this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/usr");
474   if (cmIsOff(this->GetOption("CPACK_SET_DESTDIR"))) {
475     this->SetOption("CPACK_SET_DESTDIR", "I_ON");
476   }
477   return this->Superclass::InitializeInternal();
478 }
479
480 int cmCPackDebGenerator::PackageOnePack(std::string const& initialTopLevel,
481                                         std::string const& packageName)
482 {
483   int retval = 1;
484   // Begin the archive for this pack
485   std::string localToplevel(initialTopLevel);
486   std::string packageFileName(
487     cmSystemTools::GetParentDirectory(this->toplevel));
488   std::string outputFileName(
489     std::string(this->GetOption("CPACK_PACKAGE_FILE_NAME")) + "-" +
490     packageName + this->GetOutputExtension());
491
492   localToplevel += "/" + packageName;
493   /* replace the TEMP DIRECTORY with the component one */
494   this->SetOption("CPACK_TEMPORARY_DIRECTORY", localToplevel.c_str());
495   packageFileName += "/" + outputFileName;
496   /* replace proposed CPACK_OUTPUT_FILE_NAME */
497   this->SetOption("CPACK_OUTPUT_FILE_NAME", outputFileName.c_str());
498   /* replace the TEMPORARY package file name */
499   this->SetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME",
500                   packageFileName.c_str());
501   // Tell CPackDeb.cmake the name of the component GROUP.
502   this->SetOption("CPACK_DEB_PACKAGE_COMPONENT", packageName.c_str());
503   // Tell CPackDeb.cmake the path where the component is.
504   std::string component_path = cmStrCat('/', packageName);
505   this->SetOption("CPACK_DEB_PACKAGE_COMPONENT_PART_PATH",
506                   component_path.c_str());
507   if (!this->ReadListFile("Internal/CPack/CPackDeb.cmake")) {
508     cmCPackLogger(cmCPackLog::LOG_ERROR,
509                   "Error while execution CPackDeb.cmake" << std::endl);
510     retval = 0;
511     return retval;
512   }
513
514   { // Isolate globbing of binaries vs. dbgsyms
515     cmsys::Glob gl;
516     std::string findExpr(this->GetOption("GEN_WDIR"));
517     findExpr += "/*";
518     gl.RecurseOn();
519     gl.SetRecurseListDirs(true);
520     gl.SetRecurseThroughSymlinks(false);
521     if (!gl.FindFiles(findExpr)) {
522       cmCPackLogger(cmCPackLog::LOG_ERROR,
523                     "Cannot find any files in the installed directory"
524                       << std::endl);
525       return 0;
526     }
527     this->packageFiles = gl.GetFiles();
528   }
529
530   int res = this->createDeb();
531   if (res != 1) {
532     retval = 0;
533   }
534   // add the generated package to package file names list
535   packageFileName = cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), '/',
536                              this->GetOption("GEN_CPACK_OUTPUT_FILE_NAME"));
537   this->packageFileNames.push_back(std::move(packageFileName));
538
539   if (this->IsOn("GEN_CPACK_DEBIAN_DEBUGINFO_PACKAGE") &&
540       this->GetOption("GEN_DBGSYMDIR")) {
541     cmsys::Glob gl;
542     std::string findExpr(this->GetOption("GEN_DBGSYMDIR"));
543     findExpr += "/*";
544     gl.RecurseOn();
545     gl.SetRecurseListDirs(true);
546     gl.SetRecurseThroughSymlinks(false);
547     if (!gl.FindFiles(findExpr)) {
548       cmCPackLogger(cmCPackLog::LOG_ERROR,
549                     "Cannot find any files in the installed directory"
550                       << std::endl);
551       return 0;
552     }
553     this->packageFiles = gl.GetFiles();
554
555     res = this->createDbgsymDDeb();
556     if (res != 1) {
557       retval = 0;
558     }
559     // add the generated package to package file names list
560     packageFileName =
561       cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), '/',
562                this->GetOption("GEN_CPACK_DBGSYM_OUTPUT_FILE_NAME"));
563     this->packageFileNames.push_back(std::move(packageFileName));
564   }
565
566   return retval;
567 }
568
569 int cmCPackDebGenerator::PackageComponents(bool ignoreGroup)
570 {
571   int retval = 1;
572   /* Reset package file name list it will be populated during the
573    * component packaging run*/
574   this->packageFileNames.clear();
575   std::string initialTopLevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY"));
576
577   // The default behavior is to have one package by component group
578   // unless CPACK_COMPONENTS_IGNORE_GROUP is specified.
579   if (!ignoreGroup) {
580     for (auto const& compG : this->ComponentGroups) {
581       cmCPackLogger(cmCPackLog::LOG_VERBOSE,
582                     "Packaging component group: " << compG.first << std::endl);
583       // Begin the archive for this group
584       retval &= this->PackageOnePack(initialTopLevel, compG.first);
585     }
586     // Handle Orphan components (components not belonging to any groups)
587     for (auto const& comp : this->Components) {
588       // Does the component belong to a group?
589       if (comp.second.Group == nullptr) {
590         cmCPackLogger(
591           cmCPackLog::LOG_VERBOSE,
592           "Component <"
593             << comp.second.Name
594             << "> does not belong to any group, package it separately."
595             << std::endl);
596         // Begin the archive for this orphan component
597         retval &= this->PackageOnePack(initialTopLevel, comp.first);
598       }
599     }
600   }
601   // CPACK_COMPONENTS_IGNORE_GROUPS is set
602   // We build 1 package per component
603   else {
604     for (auto const& comp : this->Components) {
605       retval &= this->PackageOnePack(initialTopLevel, comp.first);
606     }
607   }
608   return retval;
609 }
610
611 //----------------------------------------------------------------------
612 int cmCPackDebGenerator::PackageComponentsAllInOne(
613   const std::string& compInstDirName)
614 {
615   int retval = 1;
616   /* Reset package file name list it will be populated during the
617    * component packaging run*/
618   this->packageFileNames.clear();
619   std::string initialTopLevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY"));
620
621   cmCPackLogger(cmCPackLog::LOG_VERBOSE,
622                 "Packaging all groups in one package..."
623                 "(CPACK_COMPONENTS_ALL_[GROUPS_]IN_ONE_PACKAGE is set)"
624                   << std::endl);
625
626   // The ALL GROUPS in ONE package case
627   std::string localToplevel(initialTopLevel);
628   std::string packageFileName(
629     cmSystemTools::GetParentDirectory(this->toplevel));
630   std::string outputFileName(
631     std::string(this->GetOption("CPACK_PACKAGE_FILE_NAME")) +
632     this->GetOutputExtension());
633   // all GROUP in one vs all COMPONENT in one
634   // if must be here otherwise non component paths have a trailing / while
635   // components don't
636   if (!compInstDirName.empty()) {
637     localToplevel += "/" + compInstDirName;
638   }
639
640   /* replace the TEMP DIRECTORY with the component one */
641   this->SetOption("CPACK_TEMPORARY_DIRECTORY", localToplevel.c_str());
642   packageFileName += "/" + outputFileName;
643   /* replace proposed CPACK_OUTPUT_FILE_NAME */
644   this->SetOption("CPACK_OUTPUT_FILE_NAME", outputFileName.c_str());
645   /* replace the TEMPORARY package file name */
646   this->SetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME",
647                   packageFileName.c_str());
648
649   if (!compInstDirName.empty()) {
650     // Tell CPackDeb.cmake the path where the component is.
651     std::string component_path = cmStrCat('/', compInstDirName);
652     this->SetOption("CPACK_DEB_PACKAGE_COMPONENT_PART_PATH",
653                     component_path.c_str());
654   }
655   if (!this->ReadListFile("Internal/CPack/CPackDeb.cmake")) {
656     cmCPackLogger(cmCPackLog::LOG_ERROR,
657                   "Error while execution CPackDeb.cmake" << std::endl);
658     retval = 0;
659     return retval;
660   }
661
662   cmsys::Glob gl;
663   std::string findExpr(this->GetOption("GEN_WDIR"));
664   findExpr += "/*";
665   gl.RecurseOn();
666   gl.SetRecurseListDirs(true);
667   gl.SetRecurseThroughSymlinks(false);
668   if (!gl.FindFiles(findExpr)) {
669     cmCPackLogger(cmCPackLog::LOG_ERROR,
670                   "Cannot find any files in the installed directory"
671                     << std::endl);
672     return 0;
673   }
674   this->packageFiles = gl.GetFiles();
675
676   int res = this->createDeb();
677   if (res != 1) {
678     retval = 0;
679   }
680   // add the generated package to package file names list
681   packageFileName = cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), '/',
682                              this->GetOption("GEN_CPACK_OUTPUT_FILE_NAME"));
683   this->packageFileNames.push_back(std::move(packageFileName));
684   return retval;
685 }
686
687 int cmCPackDebGenerator::PackageFiles()
688 {
689   /* Are we in the component packaging case */
690   if (this->WantsComponentInstallation()) {
691     // CASE 1 : COMPONENT ALL-IN-ONE package
692     // If ALL GROUPS or ALL COMPONENTS in ONE package has been requested
693     // then the package file is unique and should be open here.
694     if (this->componentPackageMethod == ONE_PACKAGE) {
695       return this->PackageComponentsAllInOne("ALL_COMPONENTS_IN_ONE");
696     }
697     // CASE 2 : COMPONENT CLASSICAL package(s) (i.e. not all-in-one)
698     // There will be 1 package for each component group
699     // however one may require to ignore component group and
700     // in this case you'll get 1 package for each component.
701     return this->PackageComponents(this->componentPackageMethod ==
702                                    ONE_PACKAGE_PER_COMPONENT);
703   }
704   // CASE 3 : NON COMPONENT package.
705   return this->PackageComponentsAllInOne("");
706 }
707
708 int cmCPackDebGenerator::createDeb()
709 {
710   std::map<std::string, std::string> controlValues;
711
712   // debian policy enforce lower case for package name
713   controlValues["Package"] = cmsys::SystemTools::LowerCase(
714     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_NAME"));
715   controlValues["Version"] =
716     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_VERSION");
717   controlValues["Section"] =
718     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SECTION");
719   controlValues["Priority"] =
720     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_PRIORITY");
721   controlValues["Architecture"] =
722     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_ARCHITECTURE");
723   controlValues["Maintainer"] =
724     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_MAINTAINER");
725   controlValues["Description"] =
726     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_DESCRIPTION");
727
728   const char* debian_pkg_source =
729     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SOURCE");
730   if (cmNonempty(debian_pkg_source)) {
731     controlValues["Source"] = debian_pkg_source;
732   }
733   const char* debian_pkg_dep =
734     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_DEPENDS");
735   if (cmNonempty(debian_pkg_dep)) {
736     controlValues["Depends"] = debian_pkg_dep;
737   }
738   const char* debian_pkg_rec =
739     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_RECOMMENDS");
740   if (cmNonempty(debian_pkg_rec)) {
741     controlValues["Recommends"] = debian_pkg_rec;
742   }
743   const char* debian_pkg_sug =
744     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SUGGESTS");
745   if (cmNonempty(debian_pkg_sug)) {
746     controlValues["Suggests"] = debian_pkg_sug;
747   }
748   const char* debian_pkg_url =
749     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_HOMEPAGE");
750   if (cmNonempty(debian_pkg_url)) {
751     controlValues["Homepage"] = debian_pkg_url;
752   }
753   const char* debian_pkg_predep =
754     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_PREDEPENDS");
755   if (cmNonempty(debian_pkg_predep)) {
756     controlValues["Pre-Depends"] = debian_pkg_predep;
757   }
758   const char* debian_pkg_enhances =
759     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_ENHANCES");
760   if (cmNonempty(debian_pkg_enhances)) {
761     controlValues["Enhances"] = debian_pkg_enhances;
762   }
763   const char* debian_pkg_breaks =
764     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_BREAKS");
765   if (cmNonempty(debian_pkg_breaks)) {
766     controlValues["Breaks"] = debian_pkg_breaks;
767   }
768   const char* debian_pkg_conflicts =
769     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_CONFLICTS");
770   if (cmNonempty(debian_pkg_conflicts)) {
771     controlValues["Conflicts"] = debian_pkg_conflicts;
772   }
773   const char* debian_pkg_provides =
774     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_PROVIDES");
775   if (cmNonempty(debian_pkg_provides)) {
776     controlValues["Provides"] = debian_pkg_provides;
777   }
778   const char* debian_pkg_replaces =
779     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_REPLACES");
780   if (cmNonempty(debian_pkg_replaces)) {
781     controlValues["Replaces"] = debian_pkg_replaces;
782   }
783
784   const std::string strGenWDIR(this->GetOption("GEN_WDIR"));
785   const std::string shlibsfilename = strGenWDIR + "/shlibs";
786
787   const char* debian_pkg_shlibs =
788     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SHLIBS");
789   const bool gen_shibs = this->IsOn("CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS") &&
790     cmNonempty(debian_pkg_shlibs);
791   if (gen_shibs) {
792     cmGeneratedFileStream out;
793     out.Open(shlibsfilename, false, true);
794     out << debian_pkg_shlibs;
795     out << '\n';
796   }
797
798   const std::string postinst = strGenWDIR + "/postinst";
799   const std::string postrm = strGenWDIR + "/postrm";
800   if (this->IsOn("GEN_CPACK_DEBIAN_GENERATE_POSTINST")) {
801     cmGeneratedFileStream out;
802     out.Open(postinst, false, true);
803     out << "#!/bin/sh\n\n"
804            "set -e\n\n"
805            "if [ \"$1\" = \"configure\" ]; then\n"
806            "\tldconfig\n"
807            "fi\n";
808   }
809   if (this->IsOn("GEN_CPACK_DEBIAN_GENERATE_POSTRM")) {
810     cmGeneratedFileStream out;
811     out.Open(postrm, false, true);
812     out << "#!/bin/sh\n\n"
813            "set -e\n\n"
814            "if [ \"$1\" = \"remove\" ]; then\n"
815            "\tldconfig\n"
816            "fi\n";
817   }
818
819   DebGenerator gen(
820     this->Logger, this->GetOption("GEN_CPACK_OUTPUT_FILE_NAME"), strGenWDIR,
821     this->GetOption("CPACK_TOPLEVEL_DIRECTORY"),
822     this->GetOption("CPACK_TEMPORARY_DIRECTORY"),
823     this->GetOption("GEN_CPACK_DEBIAN_COMPRESSION_TYPE"),
824     this->GetOption("CPACK_THREADS"),
825     this->GetOption("GEN_CPACK_DEBIAN_ARCHIVE_TYPE"), controlValues, gen_shibs,
826     shlibsfilename, this->IsOn("GEN_CPACK_DEBIAN_GENERATE_POSTINST"), postinst,
827     this->IsOn("GEN_CPACK_DEBIAN_GENERATE_POSTRM"), postrm,
828     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA"),
829     this->IsSet("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION"),
830     this->packageFiles);
831
832   if (!gen.generate()) {
833     return 0;
834   }
835   return 1;
836 }
837
838 int cmCPackDebGenerator::createDbgsymDDeb()
839 {
840   // Packages containing debug symbols follow the same structure as .debs
841   // but have different metadata and content.
842
843   std::map<std::string, std::string> controlValues;
844   // debian policy enforce lower case for package name
845   std::string packageNameLower = cmsys::SystemTools::LowerCase(
846     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_NAME"));
847   const char* debian_pkg_version =
848     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_VERSION");
849
850   controlValues["Package"] = packageNameLower + "-dbgsym";
851   controlValues["Package-Type"] = "ddeb";
852   controlValues["Version"] = debian_pkg_version;
853   controlValues["Auto-Built-Package"] = "debug-symbols";
854   controlValues["Depends"] = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_NAME") +
855     std::string(" (= ") + debian_pkg_version + ")";
856   controlValues["Section"] = "debug";
857   controlValues["Priority"] = "optional";
858   controlValues["Architecture"] =
859     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_ARCHITECTURE");
860   controlValues["Maintainer"] =
861     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_MAINTAINER");
862   controlValues["Description"] =
863     std::string("debug symbols for ") + packageNameLower;
864
865   const char* debian_pkg_source =
866     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SOURCE");
867   if (cmNonempty(debian_pkg_source)) {
868     controlValues["Source"] = debian_pkg_source;
869   }
870   const char* debian_build_ids = this->GetOption("GEN_BUILD_IDS");
871   if (cmNonempty(debian_build_ids)) {
872     controlValues["Build-Ids"] = debian_build_ids;
873   }
874
875   DebGenerator gen(
876     this->Logger, this->GetOption("GEN_CPACK_DBGSYM_OUTPUT_FILE_NAME"),
877     this->GetOption("GEN_DBGSYMDIR"),
878
879     this->GetOption("CPACK_TOPLEVEL_DIRECTORY"),
880     this->GetOption("CPACK_TEMPORARY_DIRECTORY"),
881     this->GetOption("GEN_CPACK_DEBIAN_COMPRESSION_TYPE"),
882     this->GetOption("CPACK_THREADS"),
883     this->GetOption("GEN_CPACK_DEBIAN_ARCHIVE_TYPE"), controlValues, false, "",
884     false, "", false, "", nullptr,
885     this->IsSet("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION"),
886     this->packageFiles);
887
888   if (!gen.generate()) {
889     return 0;
890   }
891   return 1;
892 }
893
894 bool cmCPackDebGenerator::SupportsComponentInstallation() const
895 {
896   return this->IsOn("CPACK_DEB_COMPONENT_INSTALL");
897 }
898
899 std::string cmCPackDebGenerator::GetComponentInstallDirNameSuffix(
900   const std::string& componentName)
901 {
902   if (this->componentPackageMethod == ONE_PACKAGE_PER_COMPONENT) {
903     return componentName;
904   }
905
906   if (this->componentPackageMethod == ONE_PACKAGE) {
907     return std::string("ALL_COMPONENTS_IN_ONE");
908   }
909   // We have to find the name of the COMPONENT GROUP
910   // the current COMPONENT belongs to.
911   std::string groupVar =
912     "CPACK_COMPONENT_" + cmSystemTools::UpperCase(componentName) + "_GROUP";
913   if (nullptr != this->GetOption(groupVar)) {
914     return std::string(this->GetOption(groupVar));
915   }
916   return componentName;
917 }