CMake Nightly Date Stamp
[kiteware-cmake.git] / Source / CPack / cmCPackGenerator.cxx
blob82116b3727c3a614cd9ae236f78df124dc9f8081
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 "cmCPackGenerator.h"
5 #include <algorithm>
6 #include <memory>
7 #include <utility>
9 #include <cmext/string_view>
11 #include "cmsys/FStream.hxx"
12 #include "cmsys/Glob.hxx"
13 #include "cmsys/RegularExpression.hxx"
15 #include "cmCPackComponentGroup.h"
16 #include "cmCPackLog.h"
17 #include "cmCryptoHash.h"
18 #include "cmDuration.h"
19 #include "cmFSPermissions.h"
20 #include "cmFileTimes.h"
21 #include "cmGeneratedFileStream.h"
22 #include "cmGlobalGenerator.h"
23 #include "cmList.h"
24 #include "cmMakefile.h"
25 #include "cmState.h"
26 #include "cmStateSnapshot.h"
27 #include "cmStringAlgorithms.h"
28 #include "cmSystemTools.h"
29 #include "cmValue.h"
30 #include "cmVersion.h"
31 #include "cmWorkingDirectory.h"
32 #include "cmXMLSafe.h"
33 #include "cmake.h"
35 #if defined(__HAIKU__)
36 # include <FindDirectory.h>
37 # include <StorageDefs.h>
38 #endif
40 cmCPackGenerator::cmCPackGenerator()
42 this->GeneratorVerbose = cmSystemTools::OUTPUT_NONE;
43 this->MakefileMap = nullptr;
44 this->Logger = nullptr;
45 this->componentPackageMethod = ONE_PACKAGE_PER_GROUP;
48 cmCPackGenerator::~cmCPackGenerator()
50 this->MakefileMap = nullptr;
53 void cmCPackGenerator::DisplayVerboseOutput(const std::string& msg,
54 float /*unused*/)
56 cmCPackLogger(cmCPackLog::LOG_VERBOSE, msg << std::endl);
59 int cmCPackGenerator::PrepareNames()
61 cmCPackLogger(cmCPackLog::LOG_DEBUG, "Create temp directory." << std::endl);
63 // checks CPACK_SET_DESTDIR support
64 if (this->IsOn("CPACK_SET_DESTDIR")) {
65 if (SETDESTDIR_UNSUPPORTED == this->SupportsSetDestdir()) {
66 cmCPackLogger(cmCPackLog::LOG_ERROR,
67 "CPACK_SET_DESTDIR is set to ON but the '"
68 << this->Name << "' generator does NOT support it."
69 << std::endl);
70 return 0;
72 if (SETDESTDIR_SHOULD_NOT_BE_USED == this->SupportsSetDestdir()) {
73 cmCPackLogger(cmCPackLog::LOG_WARNING,
74 "CPACK_SET_DESTDIR is set to ON but it is "
75 << "usually a bad idea to do that with '" << this->Name
76 << "' generator. Use at your own risk." << std::endl);
80 // Determine package-directory.
81 cmValue pkgDirectory = this->GetOption("CPACK_PACKAGE_DIRECTORY");
82 if (!pkgDirectory) {
83 cmCPackLogger(cmCPackLog::LOG_ERROR,
84 "CPACK_PACKAGE_DIRECTORY not specified" << std::endl);
85 return 0;
87 // Determine base-filename of the package.
88 cmValue pkgBaseFileName = this->GetOption("CPACK_PACKAGE_FILE_NAME");
89 if (!pkgBaseFileName) {
90 cmCPackLogger(cmCPackLog::LOG_ERROR,
91 "CPACK_PACKAGE_FILE_NAME not specified" << std::endl);
92 return 0;
94 // Determine filename of the package.
95 if (!this->GetOutputExtension()) {
96 cmCPackLogger(cmCPackLog::LOG_ERROR,
97 "No output extension specified" << std::endl);
98 return 0;
100 std::string pkgFileName =
101 cmStrCat(pkgBaseFileName, this->GetOutputExtension());
102 // Determine path to the package.
103 std::string pkgFilePath = cmStrCat(pkgDirectory, "/", pkgFileName);
104 // Determine top-level directory for packaging.
105 std::string topDirectory = cmStrCat(pkgDirectory, "/_CPack_Packages/");
107 cmValue toplevelTag = this->GetOption("CPACK_TOPLEVEL_TAG");
108 if (toplevelTag) {
109 topDirectory += cmStrCat(toplevelTag, "/");
112 topDirectory += *this->GetOption("CPACK_GENERATOR");
113 // Determine temporary packaging-directory.
114 std::string tmpDirectory = cmStrCat(topDirectory, "/", pkgBaseFileName);
115 // Determine path to temporary package file.
116 std::string tmpPkgFilePath = topDirectory + "/" + pkgFileName;
118 // Set CPack variables which are not set already.
119 this->SetOptionIfNotSet("CPACK_REMOVE_TOPLEVEL_DIRECTORY", "1");
120 this->SetOptionIfNotSet("CPACK_TOPLEVEL_DIRECTORY", topDirectory);
121 this->SetOptionIfNotSet("CPACK_TEMPORARY_DIRECTORY", tmpDirectory);
122 this->SetOptionIfNotSet("CPACK_TEMPORARY_INSTALL_DIRECTORY", tmpDirectory);
123 this->SetOptionIfNotSet("CPACK_OUTPUT_FILE_PREFIX", pkgDirectory);
124 this->SetOptionIfNotSet("CPACK_OUTPUT_FILE_NAME", pkgFileName);
125 this->SetOptionIfNotSet("CPACK_OUTPUT_FILE_PATH", pkgFilePath);
126 this->SetOptionIfNotSet("CPACK_TEMPORARY_PACKAGE_FILE_NAME", tmpPkgFilePath);
127 this->SetOptionIfNotSet("CPACK_INSTALL_DIRECTORY", this->GetInstallPath());
128 this->SetOptionIfNotSet(
129 "CPACK_NATIVE_INSTALL_DIRECTORY",
130 cmsys::SystemTools::ConvertToOutputPath(this->GetInstallPath()));
132 // Determine description of the package and set as CPack variable,
133 // if not already set.
134 cmCPackLogger(cmCPackLog::LOG_DEBUG,
135 "Look for: CPACK_PACKAGE_DESCRIPTION_FILE" << std::endl);
136 cmValue descFileName = this->GetOption("CPACK_PACKAGE_DESCRIPTION_FILE");
137 if (descFileName && !this->GetOption("CPACK_PACKAGE_DESCRIPTION")) {
138 cmCPackLogger(cmCPackLog::LOG_DEBUG,
139 "Look for: " << *descFileName << std::endl);
140 if (!cmSystemTools::FileExists(*descFileName)) {
141 cmCPackLogger(cmCPackLog::LOG_ERROR,
142 "Cannot find description file name: ["
143 << *descFileName << "]" << std::endl);
144 return 0;
146 cmsys::ifstream ifs(descFileName->c_str());
147 if (!ifs) {
148 cmCPackLogger(cmCPackLog::LOG_ERROR,
149 "Cannot open description file name: " << *descFileName
150 << std::endl);
151 return 0;
153 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
154 "Read description file: " << *descFileName << std::endl);
155 std::ostringstream ostr;
156 std::string line;
157 while (ifs && cmSystemTools::GetLineFromStream(ifs, line)) {
158 ostr << cmXMLSafe(line) << std::endl;
161 this->SetOption("CPACK_PACKAGE_DESCRIPTION", ostr.str());
162 cmValue defFileName =
163 this->GetOption("CPACK_DEFAULT_PACKAGE_DESCRIPTION_FILE");
164 if (defFileName && (*defFileName == *descFileName)) {
165 this->SetOption("CPACK_USED_DEFAULT_PACKAGE_DESCRIPTION_FILE", "ON");
168 if (!this->GetOption("CPACK_PACKAGE_DESCRIPTION")) {
169 cmCPackLogger(
170 cmCPackLog::LOG_ERROR,
171 "Project description not specified. Please specify "
172 "CPACK_PACKAGE_DESCRIPTION or CPACK_PACKAGE_DESCRIPTION_FILE."
173 << std::endl);
174 return 0;
177 // Check algorithm for calculating the checksum of the package.
178 cmValue algoSignature = this->GetOption("CPACK_PACKAGE_CHECKSUM");
179 if (algoSignature) {
180 if (!cmCryptoHash::New(*algoSignature)) {
181 cmCPackLogger(cmCPackLog::LOG_ERROR,
182 "Cannot recognize algorithm: " << algoSignature
183 << std::endl);
184 return 0;
188 return 1;
191 int cmCPackGenerator::InstallProject()
193 cmCPackLogger(cmCPackLog::LOG_OUTPUT, "Install projects" << std::endl);
194 this->CleanTemporaryDirectory();
196 std::string bareTempInstallDirectory =
197 this->GetOption("CPACK_TEMPORARY_DIRECTORY");
198 std::string tempInstallDirectory = bareTempInstallDirectory;
199 bool setDestDir = this->GetOption("CPACK_SET_DESTDIR").IsOn() ||
200 cmIsInternallyOn(this->GetOption("CPACK_SET_DESTDIR"));
201 if (!setDestDir) {
202 tempInstallDirectory += this->GetPackagingInstallPrefix();
205 int res = 1;
206 if (!cmsys::SystemTools::MakeDirectory(bareTempInstallDirectory)) {
207 cmCPackLogger(
208 cmCPackLog::LOG_ERROR,
209 "Problem creating temporary directory: "
210 << (!tempInstallDirectory.empty() ? tempInstallDirectory : "(NULL)")
211 << std::endl);
212 return 0;
215 if (setDestDir) {
216 std::string destDir = cmStrCat("DESTDIR=", tempInstallDirectory);
217 cmSystemTools::PutEnv(destDir);
218 } else {
219 // Make sure there is no destdir
220 cmSystemTools::PutEnv("DESTDIR=");
223 // prepare default created directory permissions
224 mode_t default_dir_mode_v = 0;
225 mode_t* default_dir_mode = nullptr;
226 cmValue default_dir_install_permissions =
227 this->GetOption("CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS");
228 if (cmNonempty(default_dir_install_permissions)) {
229 cmList items{ default_dir_install_permissions };
230 for (const auto& arg : items) {
231 if (!cmFSPermissions::stringToModeT(arg, default_dir_mode_v)) {
232 cmCPackLogger(cmCPackLog::LOG_ERROR,
233 "Invalid permission value '"
234 << arg
235 << "'."
236 " CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS "
237 "value is invalid."
238 << std::endl);
239 return 0;
243 default_dir_mode = &default_dir_mode_v;
246 // If the CPackConfig file sets CPACK_INSTALL_COMMANDS then run them
247 // as listed
248 if (!this->InstallProjectViaInstallCommands(setDestDir,
249 tempInstallDirectory)) {
250 return 0;
253 // If the CPackConfig file sets CPACK_INSTALL_SCRIPT(S) then run them
254 // as listed
255 if (!this->InstallProjectViaInstallScript(setDestDir,
256 tempInstallDirectory)) {
257 return 0;
260 // If the CPackConfig file sets CPACK_INSTALLED_DIRECTORIES
261 // then glob it and copy it to CPACK_TEMPORARY_DIRECTORY
262 // This is used in Source packaging
263 if (!this->InstallProjectViaInstalledDirectories(
264 setDestDir, tempInstallDirectory, default_dir_mode)) {
265 return 0;
268 // If the project is a CMAKE project then run pre-install
269 // and then read the cmake_install script to run it
270 if (!this->InstallProjectViaInstallCMakeProjects(
271 setDestDir, bareTempInstallDirectory, default_dir_mode)) {
272 return 0;
275 // Run pre-build actions
276 cmValue preBuildScripts = this->GetOption("CPACK_PRE_BUILD_SCRIPTS");
277 if (preBuildScripts) {
278 const cmList scripts{ preBuildScripts };
279 for (const auto& script : scripts) {
280 cmCPackLogger(cmCPackLog::LOG_OUTPUT,
281 "Executing pre-build script: " << script << std::endl);
283 if (!this->MakefileMap->ReadListFile(script)) {
284 cmCPackLogger(cmCPackLog::LOG_ERROR,
285 "The pre-build script not found: " << script
286 << std::endl);
287 return 0;
292 if (setDestDir) {
293 cmSystemTools::PutEnv("DESTDIR=");
296 return res;
299 int cmCPackGenerator::InstallProjectViaInstallCommands(
300 bool setDestDir, const std::string& tempInstallDirectory)
302 (void)setDestDir;
303 cmValue installCommands = this->GetOption("CPACK_INSTALL_COMMANDS");
304 if (cmNonempty(installCommands)) {
305 std::string tempInstallDirectoryEnv =
306 cmStrCat("CMAKE_INSTALL_PREFIX=", tempInstallDirectory);
307 cmSystemTools::PutEnv(tempInstallDirectoryEnv);
308 cmList installCommandsVector{ installCommands };
309 for (std::string const& ic : installCommandsVector) {
310 cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << ic << std::endl);
311 std::string output;
312 int retVal = 1;
313 bool resB = cmSystemTools::RunSingleCommand(
314 ic, &output, &output, &retVal, nullptr, this->GeneratorVerbose,
315 cmDuration::zero());
316 if (!resB || retVal) {
317 std::string tmpFile = cmStrCat(
318 this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/InstallOutput.log");
319 cmGeneratedFileStream ofs(tmpFile);
320 ofs << "# Run command: " << ic << std::endl
321 << "# Output:" << std::endl
322 << output << std::endl;
323 cmCPackLogger(cmCPackLog::LOG_ERROR,
324 "Problem running install command: "
325 << ic << std::endl
326 << "Please check " << tmpFile << " for errors"
327 << std::endl);
328 return 0;
332 return 1;
335 int cmCPackGenerator::InstallProjectViaInstalledDirectories(
336 bool setDestDir, const std::string& tempInstallDirectory,
337 const mode_t* default_dir_mode)
339 (void)setDestDir;
340 (void)tempInstallDirectory;
341 std::vector<cmsys::RegularExpression> ignoreFilesRegex;
342 cmValue cpackIgnoreFiles = this->GetOption("CPACK_IGNORE_FILES");
343 if (cpackIgnoreFiles) {
344 cmList ignoreFilesRegexString{ cpackIgnoreFiles };
345 for (std::string const& ifr : ignoreFilesRegexString) {
346 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
347 "Create ignore files regex for: " << ifr << std::endl);
348 ignoreFilesRegex.emplace_back(ifr);
351 cmValue installDirectories = this->GetOption("CPACK_INSTALLED_DIRECTORIES");
352 if (cmNonempty(installDirectories)) {
353 cmList installDirectoriesList{ installDirectories };
354 if (installDirectoriesList.size() % 2 != 0) {
355 cmCPackLogger(
356 cmCPackLog::LOG_ERROR,
357 "CPACK_INSTALLED_DIRECTORIES should contain pairs of <directory> "
358 "and "
359 "<subdirectory>. The <subdirectory> can be '.' to be installed in "
360 "the toplevel directory of installation."
361 << std::endl);
362 return 0;
364 cmList::iterator it;
365 const std::string& tempDir = tempInstallDirectory;
366 for (it = installDirectoriesList.begin();
367 it != installDirectoriesList.end(); ++it) {
368 std::vector<std::pair<std::string, std::string>> symlinkedFiles;
369 cmCPackLogger(cmCPackLog::LOG_DEBUG, "Find files" << std::endl);
370 cmsys::Glob gl;
371 std::string top = *it;
372 ++it;
373 std::string subdir = *it;
374 std::string findExpr = cmStrCat(top, "/*");
375 cmCPackLogger(cmCPackLog::LOG_OUTPUT,
376 "- Install directory: " << top << std::endl);
377 gl.RecurseOn();
378 gl.SetRecurseListDirs(true);
379 gl.SetRecurseThroughSymlinks(false);
380 if (!gl.FindFiles(findExpr)) {
381 cmCPackLogger(cmCPackLog::LOG_ERROR,
382 "Cannot find any files in the installed directory"
383 << std::endl);
384 return 0;
386 this->files = gl.GetFiles();
387 for (std::string const& gf : this->files) {
388 bool skip = false;
389 std::string inFile = gf;
390 if (cmSystemTools::FileIsDirectory(gf) &&
391 !cmSystemTools::FileIsSymlink(gf)) {
392 inFile += '/';
394 for (cmsys::RegularExpression& reg : ignoreFilesRegex) {
395 if (reg.find(inFile)) {
396 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
397 "Ignore file: " << inFile << std::endl);
398 skip = true;
401 if (skip) {
402 continue;
404 std::string filePath = cmStrCat(tempDir, '/', subdir, '/',
405 cmSystemTools::RelativePath(top, gf));
406 cmCPackLogger(cmCPackLog::LOG_DEBUG,
407 "Copy file: " << inFile << " -> " << filePath
408 << std::endl);
409 /* If the file is a symlink we will have to re-create it */
410 if (cmSystemTools::FileIsSymlink(inFile)) {
411 std::string targetFile;
412 std::string inFileRelative =
413 cmSystemTools::RelativePath(top, inFile);
414 cmSystemTools::ReadSymlink(inFile, targetFile);
415 symlinkedFiles.emplace_back(std::move(targetFile),
416 std::move(inFileRelative));
418 /* If it is not a symlink then do a plain copy */
419 else if (!(cmSystemTools::CopyFileIfDifferent(inFile, filePath) &&
420 cmFileTimes::Copy(inFile, filePath))) {
421 cmCPackLogger(cmCPackLog::LOG_ERROR,
422 "Problem copying file: " << inFile << " -> "
423 << filePath << std::endl);
424 return 0;
427 /* rebuild symlinks in the installed tree */
428 if (!symlinkedFiles.empty()) {
429 std::string goToDir = cmStrCat(tempDir, '/', subdir);
430 cmCPackLogger(cmCPackLog::LOG_DEBUG,
431 "Change dir to: " << goToDir << std::endl);
432 cmWorkingDirectory workdir(goToDir);
433 if (workdir.Failed()) {
434 cmCPackLogger(cmCPackLog::LOG_ERROR,
435 workdir.GetError() << std::endl);
436 return 0;
438 for (auto const& symlinked : symlinkedFiles) {
439 cmCPackLogger(cmCPackLog::LOG_DEBUG,
440 "Will create a symlink: " << symlinked.second << "--> "
441 << symlinked.first
442 << std::endl);
443 // make sure directory exists for symlink
444 std::string destDir =
445 cmSystemTools::GetFilenamePath(symlinked.second);
446 if (!destDir.empty() &&
447 !cmSystemTools::MakeDirectory(destDir, default_dir_mode)) {
448 cmCPackLogger(cmCPackLog::LOG_ERROR,
449 "Cannot create dir: "
450 << destDir << "\nTrying to create symlink: "
451 << symlinked.second << "--> " << symlinked.first
452 << std::endl);
454 if (!cmSystemTools::CreateSymlink(symlinked.first,
455 symlinked.second)) {
456 cmCPackLogger(cmCPackLog::LOG_ERROR,
457 "Cannot create symlink: "
458 << symlinked.second << "--> " << symlinked.first
459 << std::endl);
460 return 0;
463 cmCPackLogger(cmCPackLog::LOG_DEBUG,
464 "Going back to: " << workdir.GetOldDirectory()
465 << std::endl);
469 return 1;
472 int cmCPackGenerator::InstallProjectViaInstallScript(
473 bool setDestDir, const std::string& tempInstallDirectory)
475 cmValue cmakeScripts = this->GetOption("CPACK_INSTALL_SCRIPTS");
477 cmValue const cmakeScript = this->GetOption("CPACK_INSTALL_SCRIPT");
478 if (cmakeScript && cmakeScripts) {
479 cmCPackLogger(
480 cmCPackLog::LOG_WARNING,
481 "Both CPACK_INSTALL_SCRIPTS and CPACK_INSTALL_SCRIPT are set, "
482 "the latter will be ignored."
483 << std::endl);
484 } else if (cmakeScript && !cmakeScripts) {
485 cmakeScripts = cmakeScript;
488 if (cmakeScripts && !cmakeScripts->empty()) {
489 cmCPackLogger(cmCPackLog::LOG_OUTPUT,
490 "- Install scripts: " << cmakeScripts << std::endl);
491 cmList cmakeScriptsVector{ cmakeScripts };
492 for (std::string const& installScript : cmakeScriptsVector) {
494 cmCPackLogger(cmCPackLog::LOG_OUTPUT,
495 "- Install script: " << installScript << std::endl);
497 if (setDestDir) {
498 // For DESTDIR based packaging, use the *project*
499 // CMAKE_INSTALL_PREFIX underneath the tempInstallDirectory. The
500 // value of the project's CMAKE_INSTALL_PREFIX is sent in here as the
501 // value of the CPACK_INSTALL_PREFIX variable.
503 std::string dir;
504 if (this->GetOption("CPACK_INSTALL_PREFIX")) {
505 dir += *this->GetOption("CPACK_INSTALL_PREFIX");
507 this->SetOption("CMAKE_INSTALL_PREFIX", dir);
508 cmCPackLogger(
509 cmCPackLog::LOG_DEBUG,
510 "- Using DESTDIR + CPACK_INSTALL_PREFIX... (this->SetOption)"
511 << std::endl);
512 cmCPackLogger(cmCPackLog::LOG_DEBUG,
513 "- Setting CMAKE_INSTALL_PREFIX to '" << dir << "'"
514 << std::endl);
515 } else {
516 this->SetOption("CMAKE_INSTALL_PREFIX", tempInstallDirectory);
518 cmCPackLogger(cmCPackLog::LOG_DEBUG,
519 "- Using non-DESTDIR install... (this->SetOption)"
520 << std::endl);
521 cmCPackLogger(cmCPackLog::LOG_DEBUG,
522 "- Setting CMAKE_INSTALL_PREFIX to '"
523 << tempInstallDirectory << "'" << std::endl);
526 this->SetOptionIfNotSet("CMAKE_CURRENT_BINARY_DIR",
527 tempInstallDirectory);
528 this->SetOptionIfNotSet("CMAKE_CURRENT_SOURCE_DIR",
529 tempInstallDirectory);
530 bool res = this->MakefileMap->ReadListFile(installScript);
531 if (cmSystemTools::GetErrorOccurredFlag() || !res) {
532 return 0;
536 return 1;
539 int cmCPackGenerator::InstallProjectViaInstallCMakeProjects(
540 bool setDestDir, const std::string& baseTempInstallDirectory,
541 const mode_t* default_dir_mode)
543 cmValue cmakeProjects = this->GetOption("CPACK_INSTALL_CMAKE_PROJECTS");
544 cmValue cmakeGenerator = this->GetOption("CPACK_CMAKE_GENERATOR");
545 std::string absoluteDestFiles;
546 if (cmNonempty(cmakeProjects)) {
547 if (!cmakeGenerator) {
548 cmCPackLogger(cmCPackLog::LOG_ERROR,
549 "CPACK_INSTALL_CMAKE_PROJECTS is specified, but "
550 "CPACK_CMAKE_GENERATOR is not. CPACK_CMAKE_GENERATOR "
551 "is required to install the project."
552 << std::endl);
553 return 0;
555 cmList cmakeProjectsList{ cmakeProjects };
556 cmList::iterator it;
557 for (it = cmakeProjectsList.begin(); it != cmakeProjectsList.end(); ++it) {
558 if (it + 1 == cmakeProjectsList.end() ||
559 it + 2 == cmakeProjectsList.end() ||
560 it + 3 == cmakeProjectsList.end()) {
561 cmCPackLogger(
562 cmCPackLog::LOG_ERROR,
563 "Not enough items on list: CPACK_INSTALL_CMAKE_PROJECTS. "
564 "CPACK_INSTALL_CMAKE_PROJECTS should hold quadruplet of install "
565 "directory, install project name, install component, and install "
566 "subdirectory."
567 << std::endl);
568 return 0;
570 std::string installDirectory = *it;
571 ++it;
572 std::string installProjectName = *it;
573 ++it;
574 cmCPackInstallCMakeProject project;
576 project.Directory = installDirectory;
577 project.ProjectName = installProjectName;
578 project.Component = *it;
579 ++it;
580 project.SubDirectory = *it;
582 cmList componentsList;
584 bool componentInstall = false;
586 * We do a component install iff
587 * - the CPack generator support component
588 * - the user did not request Monolithic install
589 * (this works at CPack time too)
591 if (this->SupportsComponentInstallation() &&
592 !(this->IsOn("CPACK_MONOLITHIC_INSTALL"))) {
593 // Determine the installation types for this project (if provided).
594 std::string installTypesVar = "CPACK_" +
595 cmSystemTools::UpperCase(project.Component) + "_INSTALL_TYPES";
596 cmValue installTypes = this->GetOption(installTypesVar);
597 if (!installTypes.IsEmpty()) {
598 cmList installTypesList{ installTypes };
599 for (std::string const& installType : installTypesList) {
600 project.InstallationTypes.push_back(
601 this->GetInstallationType(project.ProjectName, installType));
605 // Determine the set of components that will be used in this project
606 std::string componentsVar =
607 "CPACK_COMPONENTS_" + cmSystemTools::UpperCase(project.Component);
608 cmValue components = this->GetOption(componentsVar);
609 if (!components.IsEmpty()) {
610 componentsList.assign(components);
611 for (auto const& comp : componentsList) {
612 project.Components.push_back(
613 this->GetComponent(project.ProjectName, comp));
615 componentInstall = true;
618 if (componentsList.empty()) {
619 componentsList.push_back(project.Component);
622 cmList buildConfigs;
624 // Try get configuration names given via `-C` CLI option
625 buildConfigs.assign(this->GetOption("CPACK_BUILD_CONFIG"));
627 // Remove duplicates
628 std::sort(buildConfigs.begin(), buildConfigs.end());
629 buildConfigs.erase(std::unique(buildConfigs.begin(), buildConfigs.end()),
630 buildConfigs.end());
632 // Ensure we have at least one configuration.
633 if (buildConfigs.empty()) {
634 buildConfigs.emplace_back();
637 std::unique_ptr<cmGlobalGenerator> globalGenerator =
638 this->MakefileMap->GetCMakeInstance()->CreateGlobalGenerator(
639 *cmakeGenerator);
640 if (!globalGenerator) {
641 cmCPackLogger(cmCPackLog::LOG_ERROR,
642 "Specified package generator not found. "
643 "CPACK_CMAKE_GENERATOR value is invalid."
644 << std::endl);
645 return 0;
647 // set the global flag for unix style paths on cmSystemTools as
648 // soon as the generator is set. This allows gmake to be used
649 // on windows.
650 cmSystemTools::SetForceUnixPaths(globalGenerator->GetForceUnixPaths());
652 // Run the installation for the selected build configurations
653 for (auto const& buildConfig : buildConfigs) {
654 if (!this->RunPreinstallTarget(project.ProjectName, project.Directory,
655 globalGenerator.get(), buildConfig)) {
656 return 0;
659 cmCPackLogger(cmCPackLog::LOG_OUTPUT,
660 "- Install project: " << project.ProjectName << " ["
661 << buildConfig << ']'
662 << std::endl);
663 // Run the installation for each component
664 for (std::string const& component : componentsList) {
665 if (!this->InstallCMakeProject(
666 setDestDir, project.Directory, baseTempInstallDirectory,
667 default_dir_mode, component, componentInstall,
668 project.SubDirectory, buildConfig, absoluteDestFiles)) {
669 return 0;
674 this->CMakeProjects.emplace_back(std::move(project));
677 this->SetOption("CPACK_ABSOLUTE_DESTINATION_FILES", absoluteDestFiles);
678 return 1;
681 int cmCPackGenerator::RunPreinstallTarget(
682 const std::string& installProjectName, const std::string& installDirectory,
683 cmGlobalGenerator* globalGenerator, const std::string& buildConfig)
685 // Does this generator require pre-install?
686 if (const char* preinstall = globalGenerator->GetPreinstallTargetName()) {
687 std::string buildCommand = globalGenerator->GenerateCMakeBuildCommand(
688 preinstall, buildConfig, "", "", false);
689 cmCPackLogger(cmCPackLog::LOG_DEBUG,
690 "- Install command: " << buildCommand << std::endl);
691 cmCPackLogger(cmCPackLog::LOG_OUTPUT,
692 "- Run preinstall target for: " << installProjectName
693 << std::endl);
694 std::string output;
695 int retVal = 1;
696 bool resB = cmSystemTools::RunSingleCommand(
697 buildCommand, &output, &output, &retVal, installDirectory.c_str(),
698 this->GeneratorVerbose, cmDuration::zero());
699 if (!resB || retVal) {
700 std::string tmpFile = cmStrCat(
701 this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/PreinstallOutput.log");
702 cmGeneratedFileStream ofs(tmpFile);
703 ofs << "# Run command: " << buildCommand << std::endl
704 << "# Directory: " << installDirectory << std::endl
705 << "# Output:" << std::endl
706 << output << std::endl;
707 cmCPackLogger(cmCPackLog::LOG_ERROR,
708 "Problem running install command: "
709 << buildCommand << std::endl
710 << "Please check " << tmpFile << " for errors"
711 << std::endl);
712 return 0;
716 return 1;
719 int cmCPackGenerator::InstallCMakeProject(
720 bool setDestDir, const std::string& installDirectory,
721 const std::string& baseTempInstallDirectory, const mode_t* default_dir_mode,
722 const std::string& component, bool componentInstall,
723 const std::string& installSubDirectory, const std::string& buildConfig,
724 std::string& absoluteDestFiles)
726 std::string tempInstallDirectory = baseTempInstallDirectory;
727 std::string installFile = installDirectory + "/cmake_install.cmake";
729 if (componentInstall) {
730 cmCPackLogger(cmCPackLog::LOG_OUTPUT,
731 "- Install component: " << component << std::endl);
734 cmake cm(cmake::RoleScript, cmState::CPack);
735 cm.SetHomeDirectory("");
736 cm.SetHomeOutputDirectory("");
737 cm.GetCurrentSnapshot().SetDefaultDefinitions();
738 cm.AddCMakePaths();
739 cm.SetProgressCallback([this](const std::string& msg, float prog) {
740 this->DisplayVerboseOutput(msg, prog);
742 cm.SetTrace(this->Trace);
743 cm.SetTraceExpand(this->TraceExpand);
744 cmGlobalGenerator gg(&cm);
745 cmMakefile mf(&gg, cm.GetCurrentSnapshot());
746 if (!installSubDirectory.empty() && installSubDirectory != "/" &&
747 installSubDirectory != ".") {
748 tempInstallDirectory += installSubDirectory;
750 if (componentInstall) {
751 tempInstallDirectory += "/";
752 // Some CPack generators would rather chose
753 // the local installation directory suffix.
754 // Some (e.g. RPM) use
755 // one install directory for each component **GROUP**
756 // instead of the default
757 // one install directory for each component.
758 tempInstallDirectory += this->GetComponentInstallDirNameSuffix(component);
760 if (this->IsOn("CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY")) {
761 tempInstallDirectory += "/";
762 tempInstallDirectory += *this->GetOption("CPACK_PACKAGE_FILE_NAME");
766 cmValue default_dir_inst_permissions =
767 this->GetOption("CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS");
768 if (cmNonempty(default_dir_inst_permissions)) {
769 mf.AddDefinition("CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS",
770 default_dir_inst_permissions);
773 if (!setDestDir) {
774 tempInstallDirectory += this->GetPackagingInstallPrefix();
777 if (setDestDir) {
778 // For DESTDIR based packaging, use the *project*
779 // CMAKE_INSTALL_PREFIX underneath the tempInstallDirectory. The
780 // value of the project's CMAKE_INSTALL_PREFIX is sent in here as
781 // the value of the CPACK_INSTALL_PREFIX variable.
783 // If DESTDIR has been 'internally set ON' this means that
784 // the underlying CPack specific generator did ask for that
785 // In this case we may override CPACK_INSTALL_PREFIX with
786 // CPACK_PACKAGING_INSTALL_PREFIX
787 // I know this is tricky and awkward but it's the price for
788 // CPACK_SET_DESTDIR backward compatibility.
789 if (cmIsInternallyOn(this->GetOption("CPACK_SET_DESTDIR"))) {
790 this->SetOption("CPACK_INSTALL_PREFIX",
791 this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX"));
793 std::string dir;
794 if (this->GetOption("CPACK_INSTALL_PREFIX")) {
795 dir += *this->GetOption("CPACK_INSTALL_PREFIX");
797 mf.AddDefinition("CMAKE_INSTALL_PREFIX", dir);
799 cmCPackLogger(
800 cmCPackLog::LOG_DEBUG,
801 "- Using DESTDIR + CPACK_INSTALL_PREFIX... (mf.AddDefinition)"
802 << std::endl);
803 cmCPackLogger(cmCPackLog::LOG_DEBUG,
804 "- Setting CMAKE_INSTALL_PREFIX to '" << dir << "'"
805 << std::endl);
807 // Make sure that DESTDIR + CPACK_INSTALL_PREFIX directory
808 // exists:
810 if (cmHasLiteralPrefix(dir, "/")) {
811 dir = tempInstallDirectory + dir;
812 } else {
813 dir = tempInstallDirectory + "/" + dir;
816 * We must re-set DESTDIR for each component
817 * We must not add the CPACK_INSTALL_PREFIX part because
818 * it will be added using the override of CMAKE_INSTALL_PREFIX
819 * The main reason for this awkward trick is that
820 * are using DESTDIR for 2 different reasons:
821 * - Because it was asked by the CPack Generator or the user
822 * using CPACK_SET_DESTDIR
823 * - Because it was already used for component install
824 * in order to put things in subdirs...
826 cmSystemTools::PutEnv("DESTDIR=" + tempInstallDirectory);
827 cmCPackLogger(cmCPackLog::LOG_DEBUG,
828 "- Creating directory: '" << dir << "'" << std::endl);
830 if (!cmsys::SystemTools::MakeDirectory(dir, default_dir_mode)) {
831 cmCPackLogger(cmCPackLog::LOG_ERROR,
832 "Problem creating temporary directory: " << dir
833 << std::endl);
834 return 0;
836 } else {
837 mf.AddDefinition("CMAKE_INSTALL_PREFIX", tempInstallDirectory);
839 if (!cmsys::SystemTools::MakeDirectory(tempInstallDirectory,
840 default_dir_mode)) {
841 cmCPackLogger(cmCPackLog::LOG_ERROR,
842 "Problem creating temporary directory: "
843 << tempInstallDirectory << std::endl);
844 return 0;
847 cmCPackLogger(cmCPackLog::LOG_DEBUG,
848 "- Using non-DESTDIR install... (mf.AddDefinition)"
849 << std::endl);
850 cmCPackLogger(cmCPackLog::LOG_DEBUG,
851 "- Setting CMAKE_INSTALL_PREFIX to '" << tempInstallDirectory
852 << "'" << std::endl);
855 if (!buildConfig.empty()) {
856 mf.AddDefinition("BUILD_TYPE", buildConfig);
858 std::string installComponentLowerCase = cmSystemTools::LowerCase(component);
859 if (installComponentLowerCase != "all") {
860 mf.AddDefinition("CMAKE_INSTALL_COMPONENT", component);
863 // strip on TRUE, ON, 1, one or several file names, but not on
864 // FALSE, OFF, 0 and an empty string
865 if (!this->GetOption("CPACK_STRIP_FILES").IsOff()) {
866 mf.AddDefinition("CMAKE_INSTALL_DO_STRIP", "1");
868 // Remember the list of files before installation
869 // of the current component (if we are in component install)
870 std::string const& InstallPrefix = tempInstallDirectory;
871 std::vector<std::string> filesBefore;
872 std::string findExpr = tempInstallDirectory;
873 if (componentInstall) {
874 cmsys::Glob glB;
875 findExpr += "/*";
876 glB.RecurseOn();
877 glB.SetRecurseListDirs(true);
878 glB.SetRecurseThroughSymlinks(false);
879 glB.FindFiles(findExpr);
880 filesBefore = glB.GetFiles();
881 std::sort(filesBefore.begin(), filesBefore.end());
884 // If CPack was asked to warn on ABSOLUTE INSTALL DESTINATION
885 // then forward request to cmake_install.cmake script
886 if (this->IsOn("CPACK_WARN_ON_ABSOLUTE_INSTALL_DESTINATION")) {
887 mf.AddDefinition("CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION", "1");
889 // If current CPack generator does not support
890 // ABSOLUTE INSTALL DESTINATION or CPack has been asked for
891 // then ask cmake_install.cmake script to error out
892 // as soon as it occurs (before installing file)
893 if (!this->SupportsAbsoluteDestination() ||
894 this->IsOn("CPACK_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION")) {
895 mf.AddDefinition("CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION", "1");
898 cmList custom_variables{ this->MakefileMap->GetDefinition(
899 "CPACK_CUSTOM_INSTALL_VARIABLES") };
901 for (auto const& custom_variable : custom_variables) {
902 std::string value;
904 auto i = custom_variable.find('=');
906 if (i != std::string::npos) {
907 value = custom_variable.substr(i + 1);
910 mf.AddDefinition(custom_variable.substr(0, i), value);
913 // do installation
914 bool res = mf.ReadListFile(installFile);
915 // forward definition of CMAKE_ABSOLUTE_DESTINATION_FILES
916 // to CPack (may be used by generators like CPack RPM or DEB)
917 // in order to transparently handle ABSOLUTE PATH
918 if (cmValue def = mf.GetDefinition("CMAKE_ABSOLUTE_DESTINATION_FILES")) {
919 mf.AddDefinition("CPACK_ABSOLUTE_DESTINATION_FILES", *def);
922 // Now rebuild the list of files after installation
923 // of the current component (if we are in component install)
924 if (componentInstall) {
925 cmsys::Glob glA;
926 glA.RecurseOn();
927 glA.SetRecurseListDirs(true);
928 glA.SetRecurseThroughSymlinks(false);
929 glA.FindFiles(findExpr);
930 std::vector<std::string> filesAfter = glA.GetFiles();
931 std::sort(filesAfter.begin(), filesAfter.end());
932 std::vector<std::string>::iterator diff;
933 std::vector<std::string> result(filesAfter.size());
934 diff = std::set_difference(filesAfter.begin(), filesAfter.end(),
935 filesBefore.begin(), filesBefore.end(),
936 result.begin());
938 std::vector<std::string>::iterator fit;
939 std::string localFileName;
940 // Populate the File field of each component
941 for (fit = result.begin(); fit != diff; ++fit) {
942 localFileName = cmSystemTools::RelativePath(InstallPrefix, *fit);
943 localFileName =
944 localFileName.substr(localFileName.find_first_not_of('/'));
945 this->Components[component].Files.push_back(localFileName);
946 cmCPackLogger(cmCPackLog::LOG_DEBUG,
947 "Adding file <" << localFileName << "> to component <"
948 << component << ">" << std::endl);
952 if (cmValue d = mf.GetDefinition("CPACK_ABSOLUTE_DESTINATION_FILES")) {
953 if (!absoluteDestFiles.empty()) {
954 absoluteDestFiles += ";";
956 absoluteDestFiles += *d;
957 cmCPackLogger(cmCPackLog::LOG_DEBUG,
958 "Got some ABSOLUTE DESTINATION FILES: " << absoluteDestFiles
959 << std::endl);
960 // define component specific var
961 if (componentInstall) {
962 std::string absoluteDestFileComponent =
963 std::string("CPACK_ABSOLUTE_DESTINATION_FILES") + "_" +
964 this->GetComponentInstallSuffix(component);
965 if (this->GetOption(absoluteDestFileComponent)) {
966 std::string absoluteDestFilesListComponent =
967 cmStrCat(this->GetOption(absoluteDestFileComponent), ';', *d);
968 this->SetOption(absoluteDestFileComponent,
969 absoluteDestFilesListComponent);
970 } else {
971 this->SetOption(absoluteDestFileComponent,
972 mf.GetDefinition("CPACK_ABSOLUTE_DESTINATION_FILES"));
976 if (cmSystemTools::GetErrorOccurredFlag() || !res) {
977 return 0;
979 return 1;
982 bool cmCPackGenerator::GenerateChecksumFile(cmCryptoHash& crypto,
983 cm::string_view filename) const
985 std::string packageFileName =
986 cmStrCat(this->GetOption("CPACK_OUTPUT_FILE_PREFIX"), "/", filename);
987 std::string hashFile = cmStrCat(
988 packageFileName, "." + cmSystemTools::LowerCase(crypto.GetHashAlgoName()));
989 cmsys::ofstream outF(hashFile.c_str());
990 if (!outF) {
991 cmCPackLogger(cmCPackLog::LOG_ERROR,
992 "Cannot create checksum file: " << hashFile << std::endl);
993 return false;
995 outF << crypto.HashFile(packageFileName) << " " << filename << "\n";
996 cmCPackLogger(cmCPackLog::LOG_OUTPUT,
997 "- checksum file: " << hashFile << " generated." << std::endl);
998 return true;
1001 bool cmCPackGenerator::CopyPackageFile(const std::string& srcFilePath,
1002 cm::string_view filename) const
1004 std::string destFilePath =
1005 cmStrCat(this->GetOption("CPACK_OUTPUT_FILE_PREFIX"), "/", filename);
1006 cmCPackLogger(cmCPackLog::LOG_DEBUG,
1007 "Copy final package(s): "
1008 << (!srcFilePath.empty() ? srcFilePath : "(NULL)") << " to "
1009 << (!destFilePath.empty() ? destFilePath : "(NULL)")
1010 << std::endl);
1011 if (!cmSystemTools::CopyFileIfDifferent(srcFilePath, destFilePath)) {
1012 cmCPackLogger(
1013 cmCPackLog::LOG_ERROR,
1014 "Problem copying the package: "
1015 << (!srcFilePath.empty() ? srcFilePath : "(NULL)") << " to "
1016 << (!destFilePath.empty() ? destFilePath : "(NULL)") << std::endl);
1017 return false;
1019 cmCPackLogger(cmCPackLog::LOG_OUTPUT,
1020 "- package: " << destFilePath << " generated." << std::endl);
1021 return true;
1024 bool cmCPackGenerator::ReadListFile(const char* moduleName)
1026 bool retval;
1027 std::string fullPath = this->MakefileMap->GetModulesFile(moduleName);
1028 retval = this->MakefileMap->ReadListFile(fullPath);
1029 // include FATAL_ERROR and ERROR in the return status
1030 retval = retval && (!cmSystemTools::GetErrorOccurredFlag());
1031 return retval;
1034 template <typename ValueType>
1035 void cmCPackGenerator::StoreOptionIfNotSet(const std::string& op,
1036 ValueType value)
1038 cmValue def = this->MakefileMap->GetDefinition(op);
1039 if (cmNonempty(def)) {
1040 return;
1042 this->StoreOption(op, value);
1045 void cmCPackGenerator::SetOptionIfNotSet(const std::string& op,
1046 const char* value)
1048 this->StoreOptionIfNotSet(op, value);
1050 void cmCPackGenerator::SetOptionIfNotSet(const std::string& op, cmValue value)
1052 this->StoreOptionIfNotSet(op, value);
1055 template <typename ValueType>
1056 void cmCPackGenerator::StoreOption(const std::string& op, ValueType value)
1058 if (!value) {
1059 this->MakefileMap->RemoveDefinition(op);
1060 return;
1062 cmCPackLogger(cmCPackLog::LOG_DEBUG,
1063 this->GetNameOfClass() << "::SetOption(" << op << ", " << value
1064 << ")" << std::endl);
1065 this->MakefileMap->AddDefinition(op, value);
1068 void cmCPackGenerator::SetOption(const std::string& op, const char* value)
1070 this->StoreOption(op, value);
1072 void cmCPackGenerator::SetOption(const std::string& op, cmValue value)
1074 this->StoreOption(op, value);
1077 int cmCPackGenerator::DoPackage()
1079 cmCPackLogger(cmCPackLog::LOG_OUTPUT,
1080 "Create package using " << this->Name << std::endl);
1082 // Prepare CPack internal name and check
1083 // values for many CPACK_xxx vars
1084 if (!this->PrepareNames()) {
1085 return 0;
1088 // Digest Component grouping specification
1089 if (!this->PrepareGroupingKind()) {
1090 return 0;
1093 // Possibly remove the top-level packaging-directory.
1094 if (this->GetOption("CPACK_REMOVE_TOPLEVEL_DIRECTORY").IsOn()) {
1095 cmValue toplevelDirectory = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
1096 if (toplevelDirectory && cmSystemTools::FileExists(*toplevelDirectory)) {
1097 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
1098 "Remove toplevel directory: " << *toplevelDirectory
1099 << std::endl);
1100 if (!cmSystemTools::RepeatedRemoveDirectory(*toplevelDirectory)) {
1101 cmCPackLogger(cmCPackLog::LOG_ERROR,
1102 "Problem removing toplevel directory: "
1103 << *toplevelDirectory << std::endl);
1104 return 0;
1109 // Install the project (to the temporary install-directory).
1110 cmCPackLogger(cmCPackLog::LOG_DEBUG,
1111 "About to install project " << std::endl);
1112 if (!this->InstallProject()) {
1113 return 0;
1115 cmCPackLogger(cmCPackLog::LOG_DEBUG, "Done install project " << std::endl);
1117 // Determine the temporary directory whose content shall be packaged.
1118 cmValue tempDirectory = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
1120 // Determine and store internally the list of files to be installed.
1121 cmCPackLogger(cmCPackLog::LOG_DEBUG, "Find files" << std::endl);
1123 cmsys::Glob gl;
1124 std::string findExpr = cmStrCat(tempDirectory, "/*");
1125 gl.RecurseOn();
1126 gl.SetRecurseListDirs(true);
1127 gl.SetRecurseThroughSymlinks(false);
1128 if (!gl.FindFiles(findExpr)) {
1129 cmCPackLogger(cmCPackLog::LOG_ERROR,
1130 "Cannot find any files in the packaging tree"
1131 << std::endl);
1132 return 0;
1134 this->files = gl.GetFiles();
1137 // Determine and store internally the directory that shall be packaged.
1138 if (this->GetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY").IsOn()) {
1139 tempDirectory = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
1141 this->toplevel = *tempDirectory;
1143 cmCPackLogger(cmCPackLog::LOG_OUTPUT, "Create package" << std::endl);
1145 // Determine and store internally the list of packages to create.
1146 // Note: Initially, this only contains a single package.
1148 cmValue tempPackageFileName =
1149 this->GetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME");
1150 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
1151 "Package files to: "
1152 << (tempPackageFileName ? *tempPackageFileName : "(NULL)")
1153 << std::endl);
1154 if (tempPackageFileName &&
1155 cmSystemTools::FileExists(*tempPackageFileName)) {
1156 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
1157 "Remove old package file" << std::endl);
1158 cmSystemTools::RemoveFile(*tempPackageFileName);
1160 this->packageFileNames.clear();
1161 /* Put at least one file name into the list of
1162 * wanted packageFileNames. The specific generator
1163 * may update this during PackageFiles.
1164 * (either putting several names or updating the provided one)
1166 this->packageFileNames.emplace_back(tempPackageFileName);
1169 // Do package the files (using the derived CPack generators.
1170 { // scope that enables package generators to run internal scripts with
1171 // latest CMake policies enabled
1172 cmMakefile::ScopePushPop pp{ this->MakefileMap };
1173 this->MakefileMap->SetPolicyVersion(cmVersion::GetCMakeVersion(),
1174 std::string());
1176 if (!this->PackageFiles() || cmSystemTools::GetErrorOccurredFlag()) {
1177 cmCPackLogger(cmCPackLog::LOG_ERROR,
1178 "Problem compressing the directory" << std::endl);
1179 return 0;
1183 // Run post-build actions
1184 cmValue postBuildScripts = this->GetOption("CPACK_POST_BUILD_SCRIPTS");
1185 if (postBuildScripts) {
1186 this->MakefileMap->AddDefinition(
1187 "CPACK_PACKAGE_FILES", cmList::to_string(this->packageFileNames));
1189 const cmList scripts{ postBuildScripts };
1190 for (const auto& script : scripts) {
1191 cmCPackLogger(cmCPackLog::LOG_OUTPUT,
1192 "Executing post-build script: " << script << std::endl);
1194 if (!this->MakefileMap->ReadListFile(script)) {
1195 cmCPackLogger(cmCPackLog::LOG_ERROR,
1196 "The post-build script not found: " << script
1197 << std::endl);
1198 return 0;
1203 /* Prepare checksum algorithm*/
1204 cmValue algo = this->GetOption("CPACK_PACKAGE_CHECKSUM");
1205 std::unique_ptr<cmCryptoHash> crypto = cmCryptoHash::New(*algo);
1208 * Copy the generated packages to final destination
1209 * - there may be several of them
1210 * - the initially provided name may have changed
1211 * (because the specific generator did 'normalize' it)
1213 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
1214 "Copying final package(s) [" << this->packageFileNames.size()
1215 << "]:" << std::endl);
1216 /* now copy package one by one */
1217 for (std::string const& pkgFileName : this->packageFileNames) {
1218 std::string filename(cmSystemTools::GetFilenameName(pkgFileName));
1219 if (!this->CopyPackageFile(pkgFileName, filename)) {
1220 return 0;
1222 /* Generate checksum file */
1223 if (crypto) {
1224 if (!this->GenerateChecksumFile(*crypto, filename)) {
1225 return 0;
1230 return 1;
1233 int cmCPackGenerator::Initialize(const std::string& name, cmMakefile* mf)
1235 this->MakefileMap = mf;
1236 this->Name = name;
1237 // set the running generator name
1238 this->SetOption("CPACK_GENERATOR", this->Name);
1239 // Load the project specific config file
1240 cmValue config = this->GetOption("CPACK_PROJECT_CONFIG_FILE");
1241 if (config) {
1242 mf->ReadListFile(*config);
1244 int result = this->InitializeInternal();
1245 if (cmSystemTools::GetErrorOccurredFlag()) {
1246 return 0;
1249 // If a generator subclass did not already set this option in its
1250 // InitializeInternal implementation, and the project did not already set
1251 // it, the default value should be:
1252 this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/");
1254 // Special handling for CPACK_TEMPORARY[_INSTALL]_DIRECTORY.
1255 // Note: Make sure that if only one of these variables is already set, the
1256 // other will be set to the same value. If they are set to different
1257 // values, however, we cannot proceed.
1258 cmValue val1 =
1259 this->MakefileMap->GetDefinition("CPACK_TEMPORARY_INSTALL_DIRECTORY");
1260 cmValue val2 = this->MakefileMap->GetDefinition("CPACK_TEMPORARY_DIRECTORY");
1261 if (val1 != val2) {
1262 // One variable is set but not the other?
1263 // Then set the other variable to the same value (even if it is invalid).
1264 if (val1.Get() && !val2.Get()) {
1265 cmCPackLogger(cmCPackLog::LOG_WARNING,
1266 "Variable CPACK_TEMPORARY_INSTALL_DIRECTORY is set, which "
1267 "is not recommended. For backwards-compatibility we will "
1268 "also set CPACK_TEMPORARY_DIRECTORY to the same value and "
1269 "proceed. However, better set neither of them!"
1270 << std::endl);
1271 this->MakefileMap->AddDefinition("CPACK_TEMPORARY_DIRECTORY", val1);
1272 } else if (!val1.Get() && val2.Get()) {
1273 cmCPackLogger(
1274 cmCPackLog::LOG_WARNING,
1275 "Variable CPACK_TEMPORARY_DIRECTORY is set, which is not recommended."
1276 << std::endl);
1277 cmCPackLogger(
1278 cmCPackLog::LOG_DEBUG,
1279 "For backwards-compatibility we will set "
1280 "CPACK_TEMPORARY_INSTALL_DIRECTORY to the same value as "
1281 "CPACK_TEMPORARY_DIRECTORY. However, better set neither of them!"
1282 << std::endl);
1283 this->MakefileMap->AddDefinition("CPACK_TEMPORARY_INSTALL_DIRECTORY",
1284 val2);
1285 } else {
1286 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
1287 "CPACK_TEMPORARY_INSTALL_DIRECTORY is already set to: "
1288 << val1 << std::endl);
1289 cmCPackLogger(
1290 cmCPackLog::LOG_VERBOSE,
1291 "CPACK_TEMPORARY_DIRECTORY is already set to: " << val2 << std::endl);
1292 cmCPackLogger(cmCPackLog::LOG_ERROR,
1293 "Variables CPACK_TEMPORARY_DIRECTORY and "
1294 "CPACK_TEMPORARY_INSTALL_DIRECTORY are both set but to "
1295 "different values. This is not supported!"
1296 << std::endl);
1297 return 0;
1299 } else if (val1.Get() && val2.Get()) {
1300 cmCPackLogger(cmCPackLog::LOG_WARNING,
1301 "Variables CPACK_TEMPORARY_DIRECTORY and "
1302 "CPACK_TEMPORARY_INSTALL_DIRECTORY are both set. Because "
1303 "they are set to the same value we can still proceed. "
1304 "However, better set neither of them!"
1305 << std::endl);
1308 return result;
1311 int cmCPackGenerator::InitializeInternal()
1313 return 1;
1316 bool cmCPackGenerator::IsSet(const std::string& name) const
1318 return this->MakefileMap->IsSet(name);
1321 cmValue cmCPackGenerator::GetOptionIfSet(const std::string& name) const
1323 cmValue ret = this->MakefileMap->GetDefinition(name);
1324 if (!ret || ret->empty() || cmIsNOTFOUND(*ret)) {
1325 return {};
1327 return ret;
1330 bool cmCPackGenerator::IsOn(const std::string& name) const
1332 return this->GetOption(name).IsOn();
1335 bool cmCPackGenerator::IsSetToOff(const std::string& op) const
1337 cmValue ret = this->MakefileMap->GetDefinition(op);
1338 if (cmNonempty(ret)) {
1339 return ret.IsOff();
1341 return false;
1344 bool cmCPackGenerator::IsSetToEmpty(const std::string& op) const
1346 cmValue ret = this->MakefileMap->GetDefinition(op);
1347 if (ret) {
1348 return ret->empty();
1350 return false;
1353 cmValue cmCPackGenerator::GetOption(const std::string& op) const
1355 cmValue ret = this->MakefileMap->GetDefinition(op);
1356 if (!ret) {
1357 cmCPackLogger(cmCPackLog::LOG_DEBUG,
1358 "Warning, GetOption return NULL for: " << op << std::endl);
1360 return ret;
1363 std::vector<std::string> cmCPackGenerator::GetOptions() const
1365 return this->MakefileMap->GetDefinitions();
1368 int cmCPackGenerator::PackageFiles()
1370 return 0;
1373 const char* cmCPackGenerator::GetInstallPath()
1375 if (!this->InstallPath.empty()) {
1376 return this->InstallPath.c_str();
1378 #if defined(_WIN32) && !defined(__CYGWIN__)
1379 std::string prgfiles;
1380 std::string sysDrive;
1381 if (cmsys::SystemTools::GetEnv("ProgramFiles", prgfiles)) {
1382 this->InstallPath = prgfiles;
1383 } else if (cmsys::SystemTools::GetEnv("SystemDrive", sysDrive)) {
1384 this->InstallPath = cmStrCat(sysDrive, "/Program Files");
1385 } else {
1386 this->InstallPath = "c:/Program Files";
1388 this->InstallPath += "/";
1389 this->InstallPath += this->GetOption("CPACK_PACKAGE_NAME");
1390 this->InstallPath += "-";
1391 this->InstallPath += this->GetOption("CPACK_PACKAGE_VERSION");
1392 #elif defined(__HAIKU__)
1393 char dir[B_PATH_NAME_LENGTH];
1394 if (find_directory(B_SYSTEM_DIRECTORY, -1, false, dir, sizeof(dir)) ==
1395 B_OK) {
1396 this->InstallPath = dir;
1397 } else {
1398 this->InstallPath = "/boot/system";
1400 #else
1401 this->InstallPath = "/usr/local/";
1402 #endif
1403 return this->InstallPath.c_str();
1406 const char* cmCPackGenerator::GetPackagingInstallPrefix()
1408 cmCPackLogger(cmCPackLog::LOG_DEBUG,
1409 "GetPackagingInstallPrefix: '"
1410 << this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX") << "'"
1411 << std::endl);
1413 return this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX")->c_str();
1416 std::string cmCPackGenerator::FindTemplate(cm::string_view name,
1417 cm::optional<cm::string_view> alt)
1419 cmCPackLogger(cmCPackLog::LOG_DEBUG,
1420 "Look for template: " << name << std::endl);
1421 // Search CMAKE_MODULE_PATH for a custom template.
1422 std::string ffile = this->MakefileMap->GetModulesFile(name);
1423 if (ffile.empty()) {
1424 // Fall back to our internal builtin default.
1425 ffile = cmStrCat(cmSystemTools::GetCMakeRoot(), "/Modules/Internal/CPack/",
1426 alt ? *alt : ""_s, name);
1427 cmSystemTools::ConvertToUnixSlashes(ffile);
1428 if (!cmSystemTools::FileExists(ffile)) {
1429 ffile.clear();
1432 cmCPackLogger(cmCPackLog::LOG_DEBUG,
1433 "Found template: " << ffile << std::endl);
1434 return ffile;
1437 bool cmCPackGenerator::ConfigureString(const std::string& inString,
1438 std::string& outString)
1440 this->MakefileMap->ConfigureString(inString, outString, true, false);
1441 return true;
1444 bool cmCPackGenerator::ConfigureFile(const std::string& inName,
1445 const std::string& outName,
1446 bool copyOnly /* = false */)
1448 return this->MakefileMap->ConfigureFile(inName, outName, copyOnly, true,
1449 false) == 1;
1452 int cmCPackGenerator::CleanTemporaryDirectory()
1454 std::string tempInstallDirectory =
1455 this->GetOption("CPACK_TEMPORARY_DIRECTORY");
1456 if (cmsys::SystemTools::FileExists(tempInstallDirectory)) {
1457 cmCPackLogger(cmCPackLog::LOG_OUTPUT,
1458 "- Clean temporary : " << tempInstallDirectory << std::endl);
1459 if (!cmSystemTools::RepeatedRemoveDirectory(tempInstallDirectory)) {
1460 cmCPackLogger(cmCPackLog::LOG_ERROR,
1461 "Problem removing temporary directory: "
1462 << tempInstallDirectory << std::endl);
1463 return 0;
1466 return 1;
1469 cmInstalledFile const* cmCPackGenerator::GetInstalledFile(
1470 std::string const& name) const
1472 cmake const* cm = this->MakefileMap->GetCMakeInstance();
1473 return cm->GetInstalledFile(name);
1476 int cmCPackGenerator::PrepareGroupingKind()
1478 // find a component package method specified by the user
1479 ComponentPackageMethod method = UNKNOWN_COMPONENT_PACKAGE_METHOD;
1481 if (this->GetOption("CPACK_COMPONENTS_ALL_IN_ONE_PACKAGE")) {
1482 method = ONE_PACKAGE;
1485 if (this->GetOption("CPACK_COMPONENTS_IGNORE_GROUPS")) {
1486 method = ONE_PACKAGE_PER_COMPONENT;
1489 if (this->GetOption("CPACK_COMPONENTS_ONE_PACKAGE_PER_GROUP")) {
1490 method = ONE_PACKAGE_PER_GROUP;
1493 // Second way to specify grouping
1494 std::string groupingType = *this->GetOption("CPACK_COMPONENTS_GROUPING");
1496 if (!groupingType.empty()) {
1497 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
1498 "[" << this->Name << "]"
1499 << " requested component grouping = " << groupingType
1500 << std::endl);
1501 if (groupingType == "ALL_COMPONENTS_IN_ONE") {
1502 method = ONE_PACKAGE;
1503 } else if (groupingType == "IGNORE") {
1504 method = ONE_PACKAGE_PER_COMPONENT;
1505 } else if (groupingType == "ONE_PER_GROUP") {
1506 method = ONE_PACKAGE_PER_GROUP;
1507 } else {
1508 cmCPackLogger(
1509 cmCPackLog::LOG_WARNING,
1510 "[" << this->Name << "]"
1511 << " requested component grouping type <" << groupingType
1512 << "> UNKNOWN not in (ALL_COMPONENTS_IN_ONE,IGNORE,ONE_PER_GROUP)"
1513 << std::endl);
1517 // Some components were defined but NO group
1518 // fallback to default if not group based
1519 if (method == ONE_PACKAGE_PER_GROUP && this->ComponentGroups.empty() &&
1520 !this->Components.empty()) {
1521 if (this->componentPackageMethod == ONE_PACKAGE) {
1522 method = ONE_PACKAGE;
1523 } else {
1524 method = ONE_PACKAGE_PER_COMPONENT;
1526 cmCPackLogger(
1527 cmCPackLog::LOG_WARNING,
1528 "[" << this->Name << "]"
1529 << " One package per component group requested, "
1530 << "but NO component groups exist: Ignoring component group."
1531 << std::endl);
1534 // if user specified packaging method, override the default packaging
1535 // method
1536 if (method != UNKNOWN_COMPONENT_PACKAGE_METHOD) {
1537 this->componentPackageMethod = method;
1540 const char* method_names[] = { "ALL_COMPONENTS_IN_ONE", "IGNORE",
1541 "ONE_PER_GROUP", "UNKNOWN" };
1543 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
1544 "[" << this->Name << "]"
1545 << " requested component grouping = "
1546 << method_names[this->componentPackageMethod]
1547 << std::endl);
1549 return 1;
1552 std::string cmCPackGenerator::GetSanitizedDirOrFileName(
1553 const std::string& name, bool isFullName) const
1555 if (isFullName) {
1556 #ifdef _WIN32
1557 // Given name matches a reserved name (on Windows)?
1558 // Then return it prepended with an underscore.
1559 // See https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1560 cmsys::RegularExpression reserved_pattern("^("
1561 "[Cc][Oo][Nn]|"
1562 "[Pp][Rr][Nn]|"
1563 "[Aa][Uu][Xx]|"
1564 "[Nn][Uu][Ll]|"
1565 "[Cc][Oo][Mm][1-9]|"
1566 "[Ll][Pp][Tt][1-9]"
1567 ")[.]?$");
1568 if (reserved_pattern.find(name)) {
1569 return "_" + name;
1571 // Given name ends in a dot (on Windows)?
1572 // Then return it appended with an underscore.
1573 if (name.back() == '.') {
1574 return name + '_';
1576 #endif
1579 #ifndef _WIN32
1580 constexpr const char* prohibited_chars = "<>\"/\\|?*`";
1581 #else
1582 // Note: Windows also excludes the colon.
1583 constexpr const char* prohibited_chars = "<>\"/\\|?*`:";
1584 #endif
1585 // Given name contains non-supported character?
1586 // Then return its MD5 hash.
1587 if (name.find_first_of(prohibited_chars) != std::string::npos) {
1588 cmCryptoHash hasher(cmCryptoHash::AlgoMD5);
1589 return hasher.HashString(name);
1592 // Otherwise return unmodified.
1593 return name;
1596 std::string cmCPackGenerator::GetComponentInstallSuffix(
1597 const std::string& componentName)
1599 return componentName;
1602 std::string cmCPackGenerator::GetComponentInstallDirNameSuffix(
1603 const std::string& componentName)
1605 return this->GetSanitizedDirOrFileName(
1606 this->GetComponentInstallSuffix(componentName));
1609 std::string cmCPackGenerator::GetComponentPackageFileName(
1610 const std::string& initialPackageFileName,
1611 const std::string& groupOrComponentName, bool isGroupName)
1615 * the default behavior is to use the
1616 * component [group] name as a suffix
1618 std::string suffix = "-" + groupOrComponentName;
1619 /* check if we should use DISPLAY name */
1620 std::string dispNameVar =
1621 "CPACK_" + this->Name + "_USE_DISPLAY_NAME_IN_FILENAME";
1622 if (this->IsOn(dispNameVar)) {
1623 /* the component Group case */
1624 if (isGroupName) {
1625 std::string groupDispVar = "CPACK_COMPONENT_GROUP_" +
1626 cmSystemTools::UpperCase(groupOrComponentName) + "_DISPLAY_NAME";
1627 cmValue groupDispName = this->GetOption(groupDispVar);
1628 if (groupDispName) {
1629 suffix = "-" + *groupDispName;
1632 /* the [single] component case */
1633 else {
1634 std::string dispVar = "CPACK_COMPONENT_" +
1635 cmSystemTools::UpperCase(groupOrComponentName) + "_DISPLAY_NAME";
1636 cmValue dispName = this->GetOption(dispVar);
1637 if (dispName) {
1638 suffix = "-" + *dispName;
1642 return initialPackageFileName + suffix;
1645 enum cmCPackGenerator::CPackSetDestdirSupport
1646 cmCPackGenerator::SupportsSetDestdir() const
1648 return cmCPackGenerator::SETDESTDIR_SUPPORTED;
1651 bool cmCPackGenerator::SupportsAbsoluteDestination() const
1653 return true;
1656 bool cmCPackGenerator::SupportsComponentInstallation() const
1658 return false;
1661 bool cmCPackGenerator::WantsComponentInstallation() const
1663 return (!this->IsOn("CPACK_MONOLITHIC_INSTALL") &&
1664 this->SupportsComponentInstallation()
1665 // check that we have at least one group or component
1666 && (!this->ComponentGroups.empty() || !this->Components.empty()));
1669 cmCPackInstallationType* cmCPackGenerator::GetInstallationType(
1670 const std::string& projectName, const std::string& name)
1672 (void)projectName;
1673 bool hasInstallationType = this->InstallationTypes.count(name) != 0;
1674 cmCPackInstallationType* installType = &this->InstallationTypes[name];
1675 if (!hasInstallationType) {
1676 // Define the installation type
1677 std::string macroPrefix =
1678 "CPACK_INSTALL_TYPE_" + cmsys::SystemTools::UpperCase(name);
1679 installType->Name = name;
1681 cmValue displayName = this->GetOption(macroPrefix + "_DISPLAY_NAME");
1682 if (cmNonempty(displayName)) {
1683 installType->DisplayName = *displayName;
1684 } else {
1685 installType->DisplayName = installType->Name;
1688 installType->Index = static_cast<unsigned>(this->InstallationTypes.size());
1690 return installType;
1693 cmCPackComponent* cmCPackGenerator::GetComponent(
1694 const std::string& projectName, const std::string& name)
1696 bool hasComponent = this->Components.count(name) != 0;
1697 cmCPackComponent* component = &this->Components[name];
1698 if (!hasComponent) {
1699 // Define the component
1700 std::string macroPrefix =
1701 "CPACK_COMPONENT_" + cmsys::SystemTools::UpperCase(name);
1702 component->Name = name;
1703 cmValue displayName = this->GetOption(macroPrefix + "_DISPLAY_NAME");
1704 if (cmNonempty(displayName)) {
1705 component->DisplayName = *displayName;
1706 } else {
1707 component->DisplayName = component->Name;
1709 component->IsHidden = this->IsOn(macroPrefix + "_HIDDEN");
1710 component->IsRequired = this->IsOn(macroPrefix + "_REQUIRED");
1711 component->IsDisabledByDefault = this->IsOn(macroPrefix + "_DISABLED");
1712 component->IsDownloaded = this->IsOn(macroPrefix + "_DOWNLOADED") ||
1713 this->GetOption("CPACK_DOWNLOAD_ALL").IsOn();
1715 cmValue archiveFile = this->GetOption(macroPrefix + "_ARCHIVE_FILE");
1716 if (cmNonempty(archiveFile)) {
1717 component->ArchiveFile = *archiveFile;
1720 cmValue plist = this->GetOption(macroPrefix + "_PLIST");
1721 if (cmNonempty(plist)) {
1722 component->Plist = *plist;
1725 cmValue groupName = this->GetOption(macroPrefix + "_GROUP");
1726 if (cmNonempty(groupName)) {
1727 component->Group = this->GetComponentGroup(projectName, *groupName);
1728 component->Group->Components.push_back(component);
1729 } else {
1730 component->Group = nullptr;
1733 cmValue description = this->GetOption(macroPrefix + "_DESCRIPTION");
1734 if (cmNonempty(description)) {
1735 component->Description = *description;
1738 // Determine the installation types.
1739 cmValue installTypes = this->GetOption(macroPrefix + "_INSTALL_TYPES");
1740 if (cmNonempty(installTypes)) {
1741 cmList installTypesList{ installTypes };
1742 for (auto const& installType : installTypesList) {
1743 component->InstallationTypes.push_back(
1744 this->GetInstallationType(projectName, installType));
1748 // Determine the component dependencies.
1749 cmValue depends = this->GetOption(macroPrefix + "_DEPENDS");
1750 if (cmNonempty(depends)) {
1751 cmList dependsList{ depends };
1752 for (auto const& depend : dependsList) {
1753 cmCPackComponent* child = this->GetComponent(projectName, depend);
1754 component->Dependencies.push_back(child);
1755 child->ReverseDependencies.push_back(component);
1759 return component;
1762 cmCPackComponentGroup* cmCPackGenerator::GetComponentGroup(
1763 const std::string& projectName, const std::string& name)
1765 (void)projectName;
1766 std::string macroPrefix =
1767 "CPACK_COMPONENT_GROUP_" + cmsys::SystemTools::UpperCase(name);
1768 bool hasGroup = this->ComponentGroups.count(name) != 0;
1769 cmCPackComponentGroup* group = &this->ComponentGroups[name];
1770 if (!hasGroup) {
1771 // Define the group
1772 group->Name = name;
1773 cmValue displayName = this->GetOption(macroPrefix + "_DISPLAY_NAME");
1774 if (cmNonempty(displayName)) {
1775 group->DisplayName = *displayName;
1776 } else {
1777 group->DisplayName = group->Name;
1780 cmValue description = this->GetOption(macroPrefix + "_DESCRIPTION");
1781 if (cmNonempty(description)) {
1782 group->Description = *description;
1784 group->IsBold = this->IsOn(macroPrefix + "_BOLD_TITLE");
1785 group->IsExpandedByDefault = this->IsOn(macroPrefix + "_EXPANDED");
1786 cmValue parentGroupName = this->GetOption(macroPrefix + "_PARENT_GROUP");
1787 if (cmNonempty(parentGroupName)) {
1788 group->ParentGroup =
1789 this->GetComponentGroup(projectName, *parentGroupName);
1790 group->ParentGroup->Subgroups.push_back(group);
1791 } else {
1792 group->ParentGroup = nullptr;
1795 return group;