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"
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"
24 #include "cmMakefile.h"
26 #include "cmStateSnapshot.h"
27 #include "cmStringAlgorithms.h"
28 #include "cmSystemTools.h"
30 #include "cmVersion.h"
31 #include "cmWorkingDirectory.h"
32 #include "cmXMLSafe.h"
35 #if defined(__HAIKU__)
36 # include <FindDirectory.h>
37 # include <StorageDefs.h>
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
,
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."
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");
83 cmCPackLogger(cmCPackLog::LOG_ERROR
,
84 "CPACK_PACKAGE_DIRECTORY not specified" << std::endl
);
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
);
94 // Determine filename of the package.
95 if (!this->GetOutputExtension()) {
96 cmCPackLogger(cmCPackLog::LOG_ERROR
,
97 "No output extension specified" << std::endl
);
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");
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
);
146 cmsys::ifstream
ifs(descFileName
->c_str());
148 cmCPackLogger(cmCPackLog::LOG_ERROR
,
149 "Cannot open description file name: " << *descFileName
153 cmCPackLogger(cmCPackLog::LOG_VERBOSE
,
154 "Read description file: " << *descFileName
<< std::endl
);
155 std::ostringstream ostr
;
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")) {
170 cmCPackLog::LOG_ERROR
,
171 "Project description not specified. Please specify "
172 "CPACK_PACKAGE_DESCRIPTION or CPACK_PACKAGE_DESCRIPTION_FILE."
177 // Check algorithm for calculating the checksum of the package.
178 cmValue algoSignature
= this->GetOption("CPACK_PACKAGE_CHECKSUM");
180 if (!cmCryptoHash::New(*algoSignature
)) {
181 cmCPackLogger(cmCPackLog::LOG_ERROR
,
182 "Cannot recognize algorithm: " << algoSignature
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"));
202 tempInstallDirectory
+= this->GetPackagingInstallPrefix();
206 if (!cmsys::SystemTools::MakeDirectory(bareTempInstallDirectory
)) {
208 cmCPackLog::LOG_ERROR
,
209 "Problem creating temporary directory: "
210 << (!tempInstallDirectory
.empty() ? tempInstallDirectory
: "(NULL)")
216 std::string destDir
= cmStrCat("DESTDIR=", tempInstallDirectory
);
217 cmSystemTools::PutEnv(destDir
);
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 '"
236 " CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS "
243 default_dir_mode
= &default_dir_mode_v
;
246 // If the CPackConfig file sets CPACK_INSTALL_COMMANDS then run them
248 if (!this->InstallProjectViaInstallCommands(setDestDir
,
249 tempInstallDirectory
)) {
253 // If the CPackConfig file sets CPACK_INSTALL_SCRIPT(S) then run them
255 if (!this->InstallProjectViaInstallScript(setDestDir
,
256 tempInstallDirectory
)) {
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
)) {
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
)) {
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
293 cmSystemTools::PutEnv("DESTDIR=");
299 int cmCPackGenerator::InstallProjectViaInstallCommands(
300 bool setDestDir
, const std::string
& tempInstallDirectory
)
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
);
313 bool resB
= cmSystemTools::RunSingleCommand(
314 ic
, &output
, &output
, &retVal
, nullptr, this->GeneratorVerbose
,
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: "
326 << "Please check " << tmpFile
<< " for errors"
335 int cmCPackGenerator::InstallProjectViaInstalledDirectories(
336 bool setDestDir
, const std::string
& tempInstallDirectory
,
337 const mode_t
* default_dir_mode
)
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) {
356 cmCPackLog::LOG_ERROR
,
357 "CPACK_INSTALLED_DIRECTORIES should contain pairs of <directory> "
359 "<subdirectory>. The <subdirectory> can be '.' to be installed in "
360 "the toplevel directory of installation."
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
);
371 std::string top
= *it
;
373 std::string subdir
= *it
;
374 std::string findExpr
= cmStrCat(top
, "/*");
375 cmCPackLogger(cmCPackLog::LOG_OUTPUT
,
376 "- Install directory: " << top
<< std::endl
);
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"
386 this->files
= gl
.GetFiles();
387 for (std::string
const& gf
: this->files
) {
389 std::string inFile
= gf
;
390 if (cmSystemTools::FileIsDirectory(gf
) &&
391 !cmSystemTools::FileIsSymlink(gf
)) {
394 for (cmsys::RegularExpression
& reg
: ignoreFilesRegex
) {
395 if (reg
.find(inFile
)) {
396 cmCPackLogger(cmCPackLog::LOG_VERBOSE
,
397 "Ignore file: " << inFile
<< std::endl
);
404 std::string filePath
= cmStrCat(tempDir
, '/', subdir
, '/',
405 cmSystemTools::RelativePath(top
, gf
));
406 cmCPackLogger(cmCPackLog::LOG_DEBUG
,
407 "Copy file: " << inFile
<< " -> " << filePath
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
);
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
);
438 for (auto const& symlinked
: symlinkedFiles
) {
439 cmCPackLogger(cmCPackLog::LOG_DEBUG
,
440 "Will create a symlink: " << symlinked
.second
<< "--> "
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
454 if (!cmSystemTools::CreateSymlink(symlinked
.first
,
456 cmCPackLogger(cmCPackLog::LOG_ERROR
,
457 "Cannot create symlink: "
458 << symlinked
.second
<< "--> " << symlinked
.first
463 cmCPackLogger(cmCPackLog::LOG_DEBUG
,
464 "Going back to: " << workdir
.GetOldDirectory()
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
) {
480 cmCPackLog::LOG_WARNING
,
481 "Both CPACK_INSTALL_SCRIPTS and CPACK_INSTALL_SCRIPT are set, "
482 "the latter will be ignored."
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
);
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.
504 if (this->GetOption("CPACK_INSTALL_PREFIX")) {
505 dir
+= *this->GetOption("CPACK_INSTALL_PREFIX");
507 this->SetOption("CMAKE_INSTALL_PREFIX", dir
);
509 cmCPackLog::LOG_DEBUG
,
510 "- Using DESTDIR + CPACK_INSTALL_PREFIX... (this->SetOption)"
512 cmCPackLogger(cmCPackLog::LOG_DEBUG
,
513 "- Setting CMAKE_INSTALL_PREFIX to '" << dir
<< "'"
516 this->SetOption("CMAKE_INSTALL_PREFIX", tempInstallDirectory
);
518 cmCPackLogger(cmCPackLog::LOG_DEBUG
,
519 "- Using non-DESTDIR install... (this->SetOption)"
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
) {
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."
555 cmList cmakeProjectsList
{ cmakeProjects
};
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()) {
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 "
570 std::string installDirectory
= *it
;
572 std::string installProjectName
= *it
;
574 cmCPackInstallCMakeProject project
;
576 project
.Directory
= installDirectory
;
577 project
.ProjectName
= installProjectName
;
578 project
.Component
= *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
);
624 // Try get configuration names given via `-C` CLI option
625 buildConfigs
.assign(this->GetOption("CPACK_BUILD_CONFIG"));
628 std::sort(buildConfigs
.begin(), buildConfigs
.end());
629 buildConfigs
.erase(std::unique(buildConfigs
.begin(), 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(
640 if (!globalGenerator
) {
641 cmCPackLogger(cmCPackLog::LOG_ERROR
,
642 "Specified package generator not found. "
643 "CPACK_CMAKE_GENERATOR value is invalid."
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
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
)) {
659 cmCPackLogger(cmCPackLog::LOG_OUTPUT
,
660 "- Install project: " << project
.ProjectName
<< " ["
661 << buildConfig
<< ']'
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
)) {
674 this->CMakeProjects
.emplace_back(std::move(project
));
677 this->SetOption("CPACK_ABSOLUTE_DESTINATION_FILES", absoluteDestFiles
);
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
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"
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();
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
);
774 tempInstallDirectory
+= this->GetPackagingInstallPrefix();
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"));
794 if (this->GetOption("CPACK_INSTALL_PREFIX")) {
795 dir
+= *this->GetOption("CPACK_INSTALL_PREFIX");
797 mf
.AddDefinition("CMAKE_INSTALL_PREFIX", dir
);
800 cmCPackLog::LOG_DEBUG
,
801 "- Using DESTDIR + CPACK_INSTALL_PREFIX... (mf.AddDefinition)"
803 cmCPackLogger(cmCPackLog::LOG_DEBUG
,
804 "- Setting CMAKE_INSTALL_PREFIX to '" << dir
<< "'"
807 // Make sure that DESTDIR + CPACK_INSTALL_PREFIX directory
810 if (cmHasLiteralPrefix(dir
, "/")) {
811 dir
= tempInstallDirectory
+ dir
;
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
837 mf
.AddDefinition("CMAKE_INSTALL_PREFIX", tempInstallDirectory
);
839 if (!cmsys::SystemTools::MakeDirectory(tempInstallDirectory
,
841 cmCPackLogger(cmCPackLog::LOG_ERROR
,
842 "Problem creating temporary directory: "
843 << tempInstallDirectory
<< std::endl
);
847 cmCPackLogger(cmCPackLog::LOG_DEBUG
,
848 "- Using non-DESTDIR install... (mf.AddDefinition)"
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
) {
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
) {
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
);
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
) {
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(),
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
);
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
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
);
971 this->SetOption(absoluteDestFileComponent
,
972 mf
.GetDefinition("CPACK_ABSOLUTE_DESTINATION_FILES"));
976 if (cmSystemTools::GetErrorOccurredFlag() || !res
) {
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());
991 cmCPackLogger(cmCPackLog::LOG_ERROR
,
992 "Cannot create checksum file: " << hashFile
<< std::endl
);
995 outF
<< crypto
.HashFile(packageFileName
) << " " << filename
<< "\n";
996 cmCPackLogger(cmCPackLog::LOG_OUTPUT
,
997 "- checksum file: " << hashFile
<< " generated." << std::endl
);
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)")
1011 if (!cmSystemTools::CopyFileIfDifferent(srcFilePath
, destFilePath
)) {
1013 cmCPackLog::LOG_ERROR
,
1014 "Problem copying the package: "
1015 << (!srcFilePath
.empty() ? srcFilePath
: "(NULL)") << " to "
1016 << (!destFilePath
.empty() ? destFilePath
: "(NULL)") << std::endl
);
1019 cmCPackLogger(cmCPackLog::LOG_OUTPUT
,
1020 "- package: " << destFilePath
<< " generated." << std::endl
);
1024 bool cmCPackGenerator::ReadListFile(const char* moduleName
)
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());
1034 template <typename ValueType
>
1035 void cmCPackGenerator::StoreOptionIfNotSet(const std::string
& op
,
1038 cmValue def
= this->MakefileMap
->GetDefinition(op
);
1039 if (cmNonempty(def
)) {
1042 this->StoreOption(op
, value
);
1045 void cmCPackGenerator::SetOptionIfNotSet(const std::string
& op
,
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
)
1059 this->MakefileMap
->RemoveDefinition(op
);
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()) {
1088 // Digest Component grouping specification
1089 if (!this->PrepareGroupingKind()) {
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
1100 if (!cmSystemTools::RepeatedRemoveDirectory(*toplevelDirectory
)) {
1101 cmCPackLogger(cmCPackLog::LOG_ERROR
,
1102 "Problem removing toplevel directory: "
1103 << *toplevelDirectory
<< std::endl
);
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()) {
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
);
1124 std::string findExpr
= cmStrCat(tempDirectory
, "/*");
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"
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)")
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(),
1176 if (!this->PackageFiles() || cmSystemTools::GetErrorOccurredFlag()) {
1177 cmCPackLogger(cmCPackLog::LOG_ERROR
,
1178 "Problem compressing the directory" << std::endl
);
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
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
)) {
1222 /* Generate checksum file */
1224 if (!this->GenerateChecksumFile(*crypto
, filename
)) {
1233 int cmCPackGenerator::Initialize(const std::string
& name
, cmMakefile
* mf
)
1235 this->MakefileMap
= mf
;
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");
1242 mf
->ReadListFile(*config
);
1244 int result
= this->InitializeInternal();
1245 if (cmSystemTools::GetErrorOccurredFlag()) {
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.
1259 this->MakefileMap
->GetDefinition("CPACK_TEMPORARY_INSTALL_DIRECTORY");
1260 cmValue val2
= this->MakefileMap
->GetDefinition("CPACK_TEMPORARY_DIRECTORY");
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!"
1271 this->MakefileMap
->AddDefinition("CPACK_TEMPORARY_DIRECTORY", val1
);
1272 } else if (!val1
.Get() && val2
.Get()) {
1274 cmCPackLog::LOG_WARNING
,
1275 "Variable CPACK_TEMPORARY_DIRECTORY is set, which is not recommended."
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!"
1283 this->MakefileMap
->AddDefinition("CPACK_TEMPORARY_INSTALL_DIRECTORY",
1286 cmCPackLogger(cmCPackLog::LOG_VERBOSE
,
1287 "CPACK_TEMPORARY_INSTALL_DIRECTORY is already set to: "
1288 << val1
<< std::endl
);
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!"
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!"
1311 int cmCPackGenerator::InitializeInternal()
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
)) {
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
)) {
1344 bool cmCPackGenerator::IsSetToEmpty(const std::string
& op
) const
1346 cmValue ret
= this->MakefileMap
->GetDefinition(op
);
1348 return ret
->empty();
1353 cmValue
cmCPackGenerator::GetOption(const std::string
& op
) const
1355 cmValue ret
= this->MakefileMap
->GetDefinition(op
);
1357 cmCPackLogger(cmCPackLog::LOG_DEBUG
,
1358 "Warning, GetOption return NULL for: " << op
<< std::endl
);
1363 std::vector
<std::string
> cmCPackGenerator::GetOptions() const
1365 return this->MakefileMap
->GetDefinitions();
1368 int cmCPackGenerator::PackageFiles()
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");
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
)) ==
1396 this->InstallPath
= dir
;
1398 this->InstallPath
= "/boot/system";
1401 this->InstallPath
= "/usr/local/";
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") << "'"
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
)) {
1432 cmCPackLogger(cmCPackLog::LOG_DEBUG
,
1433 "Found template: " << ffile
<< std::endl
);
1437 bool cmCPackGenerator::ConfigureString(const std::string
& inString
,
1438 std::string
& outString
)
1440 this->MakefileMap
->ConfigureString(inString
, outString
, true, false);
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,
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
);
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
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
;
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)"
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
;
1524 method
= ONE_PACKAGE_PER_COMPONENT
;
1527 cmCPackLog::LOG_WARNING
,
1528 "[" << this->Name
<< "]"
1529 << " One package per component group requested, "
1530 << "but NO component groups exist: Ignoring component group."
1534 // if user specified packaging method, override the default packaging
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
]
1552 std::string
cmCPackGenerator::GetSanitizedDirOrFileName(
1553 const std::string
& name
, bool isFullName
) const
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("^("
1565 "[Cc][Oo][Mm][1-9]|"
1568 if (reserved_pattern
.find(name
)) {
1571 // Given name ends in a dot (on Windows)?
1572 // Then return it appended with an underscore.
1573 if (name
.back() == '.') {
1580 constexpr const char* prohibited_chars
= "<>\"/\\|?*`";
1582 // Note: Windows also excludes the colon.
1583 constexpr const char* prohibited_chars
= "<>\"/\\|?*`:";
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.
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 */
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 */
1634 std::string dispVar
= "CPACK_COMPONENT_" +
1635 cmSystemTools::UpperCase(groupOrComponentName
) + "_DISPLAY_NAME";
1636 cmValue dispName
= this->GetOption(dispVar
);
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
1656 bool cmCPackGenerator::SupportsComponentInstallation() const
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
)
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
;
1685 installType
->DisplayName
= installType
->Name
;
1688 installType
->Index
= static_cast<unsigned>(this->InstallationTypes
.size());
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
;
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
);
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
);
1762 cmCPackComponentGroup
* cmCPackGenerator::GetComponentGroup(
1763 const std::string
& projectName
, const std::string
& name
)
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
];
1773 cmValue displayName
= this->GetOption(macroPrefix
+ "_DISPLAY_NAME");
1774 if (cmNonempty(displayName
)) {
1775 group
->DisplayName
= *displayName
;
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
);
1792 group
->ParentGroup
= nullptr;