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"
4
5 #include <algorithm>
6 #include <cstring>
7 #include <memory>
8 #include <utility>
9
10 #include "cmsys/FStream.hxx"
11 #include "cmsys/Glob.hxx"
12 #include "cmsys/RegularExpression.hxx"
13
14 #include "cmCPackComponentGroup.h"
15 #include "cmCPackLog.h"
16 #include "cmCryptoHash.h"
17 #include "cmDuration.h"
18 #include "cmFSPermissions.h"
19 #include "cmFileTimes.h"
20 #include "cmGeneratedFileStream.h"
21 #include "cmGlobalGenerator.h"
22 #include "cmMakefile.h"
23 #include "cmState.h"
24 #include "cmStateSnapshot.h"
25 #include "cmStringAlgorithms.h"
26 #include "cmSystemTools.h"
27 #include "cmValue.h"
28 #include "cmVersion.h"
29 #include "cmWorkingDirectory.h"
30 #include "cmXMLSafe.h"
31 #include "cmake.h"
32
33 #if defined(__HAIKU__)
34 # include <FindDirectory.h>
35 # include <StorageDefs.h>
36 #endif
37
cmCPackGenerator()38 cmCPackGenerator::cmCPackGenerator()
39 {
40 this->GeneratorVerbose = cmSystemTools::OUTPUT_NONE;
41 this->MakefileMap = nullptr;
42 this->Logger = nullptr;
43 this->componentPackageMethod = ONE_PACKAGE_PER_GROUP;
44 }
45
~cmCPackGenerator()46 cmCPackGenerator::~cmCPackGenerator()
47 {
48 this->MakefileMap = nullptr;
49 }
50
DisplayVerboseOutput(const std::string & msg,float progress)51 void cmCPackGenerator::DisplayVerboseOutput(const std::string& msg,
52 float progress)
53 {
54 (void)progress;
55 cmCPackLogger(cmCPackLog::LOG_VERBOSE, "" << msg << std::endl);
56 }
57
PrepareNames()58 int cmCPackGenerator::PrepareNames()
59 {
60 cmCPackLogger(cmCPackLog::LOG_DEBUG, "Create temp directory." << std::endl);
61
62 // checks CPACK_SET_DESTDIR support
63 if (this->IsOn("CPACK_SET_DESTDIR")) {
64 if (SETDESTDIR_UNSUPPORTED == this->SupportsSetDestdir()) {
65 cmCPackLogger(cmCPackLog::LOG_ERROR,
66 "CPACK_SET_DESTDIR is set to ON but the '"
67 << this->Name << "' generator does NOT support it."
68 << std::endl);
69 return 0;
70 }
71 if (SETDESTDIR_SHOULD_NOT_BE_USED == this->SupportsSetDestdir()) {
72 cmCPackLogger(cmCPackLog::LOG_WARNING,
73 "CPACK_SET_DESTDIR is set to ON but it is "
74 << "usually a bad idea to do that with '" << this->Name
75 << "' generator. Use at your own risk." << std::endl);
76 }
77 }
78
79 std::string tempDirectory =
80 cmStrCat(this->GetOption("CPACK_PACKAGE_DIRECTORY"), "/_CPack_Packages/");
81 cmValue toplevelTag = this->GetOption("CPACK_TOPLEVEL_TAG");
82 if (toplevelTag) {
83 tempDirectory += *toplevelTag;
84 tempDirectory += "/";
85 }
86 tempDirectory += *this->GetOption("CPACK_GENERATOR");
87 std::string topDirectory = tempDirectory;
88 cmValue pfname = this->GetOption("CPACK_PACKAGE_FILE_NAME");
89 if (!pfname) {
90 cmCPackLogger(cmCPackLog::LOG_ERROR,
91 "CPACK_PACKAGE_FILE_NAME not specified" << std::endl);
92 return 0;
93 }
94 std::string outName = pfname;
95 tempDirectory += "/" + outName;
96 if (!this->GetOutputExtension()) {
97 cmCPackLogger(cmCPackLog::LOG_ERROR,
98 "No output extension specified" << std::endl);
99 return 0;
100 }
101 outName += this->GetOutputExtension();
102 cmValue pdir = this->GetOption("CPACK_PACKAGE_DIRECTORY");
103 if (!pdir) {
104 cmCPackLogger(cmCPackLog::LOG_ERROR,
105 "CPACK_PACKAGE_DIRECTORY not specified" << std::endl);
106 return 0;
107 }
108
109 std::string destFile = pdir;
110 this->SetOptionIfNotSet("CPACK_OUTPUT_FILE_PREFIX", destFile);
111 destFile += "/" + outName;
112 std::string outFile = topDirectory + "/" + outName;
113 this->SetOptionIfNotSet("CPACK_TOPLEVEL_DIRECTORY", topDirectory);
114 this->SetOptionIfNotSet("CPACK_TEMPORARY_DIRECTORY", tempDirectory);
115 this->SetOptionIfNotSet("CPACK_OUTPUT_FILE_NAME", outName);
116 this->SetOptionIfNotSet("CPACK_OUTPUT_FILE_PATH", destFile);
117 this->SetOptionIfNotSet("CPACK_TEMPORARY_PACKAGE_FILE_NAME", outFile);
118 this->SetOptionIfNotSet("CPACK_INSTALL_DIRECTORY", this->GetInstallPath());
119 this->SetOptionIfNotSet(
120 "CPACK_NATIVE_INSTALL_DIRECTORY",
121 cmsys::SystemTools::ConvertToOutputPath(this->GetInstallPath()));
122 this->SetOptionIfNotSet("CPACK_TEMPORARY_INSTALL_DIRECTORY", tempDirectory);
123
124 cmCPackLogger(cmCPackLog::LOG_DEBUG,
125 "Look for: CPACK_PACKAGE_DESCRIPTION_FILE" << std::endl);
126 cmValue descFileName = this->GetOption("CPACK_PACKAGE_DESCRIPTION_FILE");
127 if (descFileName && !this->GetOption("CPACK_PACKAGE_DESCRIPTION")) {
128 cmCPackLogger(cmCPackLog::LOG_DEBUG,
129 "Look for: " << descFileName << std::endl);
130 if (!cmSystemTools::FileExists(descFileName)) {
131 cmCPackLogger(cmCPackLog::LOG_ERROR,
132 "Cannot find description file name: ["
133 << descFileName << "]" << std::endl);
134 return 0;
135 }
136 cmsys::ifstream ifs(descFileName->c_str());
137 if (!ifs) {
138 cmCPackLogger(cmCPackLog::LOG_ERROR,
139 "Cannot open description file name: " << descFileName
140 << std::endl);
141 return 0;
142 }
143 std::ostringstream ostr;
144 std::string line;
145
146 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
147 "Read description file: " << descFileName << std::endl);
148 while (ifs && cmSystemTools::GetLineFromStream(ifs, line)) {
149 ostr << cmXMLSafe(line) << std::endl;
150 }
151 this->SetOption("CPACK_PACKAGE_DESCRIPTION", ostr.str());
152 cmValue defFileName =
153 this->GetOption("CPACK_DEFAULT_PACKAGE_DESCRIPTION_FILE");
154 if (defFileName && (defFileName == descFileName)) {
155 this->SetOption("CPACK_USED_DEFAULT_PACKAGE_DESCRIPTION_FILE", "ON");
156 }
157 }
158 if (!this->GetOption("CPACK_PACKAGE_DESCRIPTION")) {
159 cmCPackLogger(
160 cmCPackLog::LOG_ERROR,
161 "Project description not specified. Please specify "
162 "CPACK_PACKAGE_DESCRIPTION or CPACK_PACKAGE_DESCRIPTION_FILE."
163 << std::endl);
164 return 0;
165 }
166 cmValue algoSignature = this->GetOption("CPACK_PACKAGE_CHECKSUM");
167 if (algoSignature) {
168 if (!cmCryptoHash::New(*algoSignature)) {
169 cmCPackLogger(cmCPackLog::LOG_ERROR,
170 "Cannot recognize algorithm: " << algoSignature
171 << std::endl);
172 return 0;
173 }
174 }
175
176 this->SetOptionIfNotSet("CPACK_REMOVE_TOPLEVEL_DIRECTORY", "1");
177
178 return 1;
179 }
180
InstallProject()181 int cmCPackGenerator::InstallProject()
182 {
183 cmCPackLogger(cmCPackLog::LOG_OUTPUT, "Install projects" << std::endl);
184 this->CleanTemporaryDirectory();
185
186 std::string bareTempInstallDirectory =
187 this->GetOption("CPACK_TEMPORARY_INSTALL_DIRECTORY");
188 std::string tempInstallDirectoryStr = bareTempInstallDirectory;
189 bool setDestDir = cmIsOn(this->GetOption("CPACK_SET_DESTDIR")) |
190 cmIsInternallyOn(this->GetOption("CPACK_SET_DESTDIR"));
191 if (!setDestDir) {
192 tempInstallDirectoryStr += this->GetPackagingInstallPrefix();
193 }
194
195 const char* tempInstallDirectory = tempInstallDirectoryStr.c_str();
196 int res = 1;
197 if (!cmsys::SystemTools::MakeDirectory(bareTempInstallDirectory)) {
198 cmCPackLogger(cmCPackLog::LOG_ERROR,
199 "Problem creating temporary directory: "
200 << (tempInstallDirectory ? tempInstallDirectory : "(NULL)")
201 << std::endl);
202 return 0;
203 }
204
205 if (setDestDir) {
206 std::string destDir = cmStrCat("DESTDIR=", tempInstallDirectory);
207 cmSystemTools::PutEnv(destDir);
208 } else {
209 // Make sure there is no destdir
210 cmSystemTools::PutEnv("DESTDIR=");
211 }
212
213 // prepare default created directory permissions
214 mode_t default_dir_mode_v = 0;
215 mode_t* default_dir_mode = nullptr;
216 cmValue default_dir_install_permissions =
217 this->GetOption("CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS");
218 if (cmNonempty(default_dir_install_permissions)) {
219 std::vector<std::string> items =
220 cmExpandedList(default_dir_install_permissions);
221 for (const auto& arg : items) {
222 if (!cmFSPermissions::stringToModeT(arg, default_dir_mode_v)) {
223 cmCPackLogger(cmCPackLog::LOG_ERROR,
224 "Invalid permission value '"
225 << arg
226 << "'."
227 " CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS "
228 "value is invalid."
229 << std::endl);
230 return 0;
231 }
232 }
233
234 default_dir_mode = &default_dir_mode_v;
235 }
236
237 // If the CPackConfig file sets CPACK_INSTALL_COMMANDS then run them
238 // as listed
239 if (!this->InstallProjectViaInstallCommands(setDestDir,
240 tempInstallDirectory)) {
241 return 0;
242 }
243
244 // If the CPackConfig file sets CPACK_INSTALL_SCRIPT(S) then run them
245 // as listed
246 if (!this->InstallProjectViaInstallScript(setDestDir,
247 tempInstallDirectory)) {
248 return 0;
249 }
250
251 // If the CPackConfig file sets CPACK_INSTALLED_DIRECTORIES
252 // then glob it and copy it to CPACK_TEMPORARY_DIRECTORY
253 // This is used in Source packaging
254 if (!this->InstallProjectViaInstalledDirectories(
255 setDestDir, tempInstallDirectory, default_dir_mode)) {
256 return 0;
257 }
258
259 // If the project is a CMAKE project then run pre-install
260 // and then read the cmake_install script to run it
261 if (!this->InstallProjectViaInstallCMakeProjects(
262 setDestDir, bareTempInstallDirectory, default_dir_mode)) {
263 return 0;
264 }
265
266 // Run pre-build actions
267 cmValue preBuildScripts = this->GetOption("CPACK_PRE_BUILD_SCRIPTS");
268 if (preBuildScripts) {
269 const auto scripts = cmExpandedList(preBuildScripts, false);
270 for (const auto& script : scripts) {
271 cmCPackLogger(cmCPackLog::LOG_OUTPUT,
272 "Executing pre-build script: " << script << std::endl);
273
274 if (!this->MakefileMap->ReadListFile(script)) {
275 cmCPackLogger(cmCPackLog::LOG_ERROR,
276 "The pre-build script not found: " << script
277 << std::endl);
278 return 0;
279 }
280 }
281 }
282
283 if (setDestDir) {
284 cmSystemTools::PutEnv("DESTDIR=");
285 }
286
287 return res;
288 }
289
InstallProjectViaInstallCommands(bool setDestDir,const std::string & tempInstallDirectory)290 int cmCPackGenerator::InstallProjectViaInstallCommands(
291 bool setDestDir, const std::string& tempInstallDirectory)
292 {
293 (void)setDestDir;
294 cmValue installCommands = this->GetOption("CPACK_INSTALL_COMMANDS");
295 if (cmNonempty(installCommands)) {
296 std::string tempInstallDirectoryEnv =
297 cmStrCat("CMAKE_INSTALL_PREFIX=", tempInstallDirectory);
298 cmSystemTools::PutEnv(tempInstallDirectoryEnv);
299 std::vector<std::string> installCommandsVector =
300 cmExpandedList(installCommands);
301 for (std::string const& ic : installCommandsVector) {
302 cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << ic << std::endl);
303 std::string output;
304 int retVal = 1;
305 bool resB = cmSystemTools::RunSingleCommand(
306 ic, &output, &output, &retVal, nullptr, this->GeneratorVerbose,
307 cmDuration::zero());
308 if (!resB || retVal) {
309 std::string tmpFile = cmStrCat(
310 this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/InstallOutput.log");
311 cmGeneratedFileStream ofs(tmpFile);
312 ofs << "# Run command: " << ic << std::endl
313 << "# Output:" << std::endl
314 << output << std::endl;
315 cmCPackLogger(cmCPackLog::LOG_ERROR,
316 "Problem running install command: "
317 << ic << std::endl
318 << "Please check " << tmpFile << " for errors"
319 << std::endl);
320 return 0;
321 }
322 }
323 }
324 return 1;
325 }
326
InstallProjectViaInstalledDirectories(bool setDestDir,const std::string & tempInstallDirectory,const mode_t * default_dir_mode)327 int cmCPackGenerator::InstallProjectViaInstalledDirectories(
328 bool setDestDir, const std::string& tempInstallDirectory,
329 const mode_t* default_dir_mode)
330 {
331 (void)setDestDir;
332 (void)tempInstallDirectory;
333 std::vector<cmsys::RegularExpression> ignoreFilesRegex;
334 cmValue cpackIgnoreFiles = this->GetOption("CPACK_IGNORE_FILES");
335 if (cpackIgnoreFiles) {
336 std::vector<std::string> ignoreFilesRegexString =
337 cmExpandedList(cpackIgnoreFiles);
338 for (std::string const& ifr : ignoreFilesRegexString) {
339 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
340 "Create ignore files regex for: " << ifr << std::endl);
341 ignoreFilesRegex.emplace_back(ifr);
342 }
343 }
344 cmValue installDirectories = this->GetOption("CPACK_INSTALLED_DIRECTORIES");
345 if (cmNonempty(installDirectories)) {
346 std::vector<std::string> installDirectoriesVector =
347 cmExpandedList(installDirectories);
348 if (installDirectoriesVector.size() % 2 != 0) {
349 cmCPackLogger(
350 cmCPackLog::LOG_ERROR,
351 "CPACK_INSTALLED_DIRECTORIES should contain pairs of <directory> "
352 "and "
353 "<subdirectory>. The <subdirectory> can be '.' to be installed in "
354 "the toplevel directory of installation."
355 << std::endl);
356 return 0;
357 }
358 std::vector<std::string>::iterator it;
359 const std::string& tempDir = tempInstallDirectory;
360 for (it = installDirectoriesVector.begin();
361 it != installDirectoriesVector.end(); ++it) {
362 std::vector<std::pair<std::string, std::string>> symlinkedFiles;
363 cmCPackLogger(cmCPackLog::LOG_DEBUG, "Find files" << std::endl);
364 cmsys::Glob gl;
365 std::string top = *it;
366 it++;
367 std::string subdir = *it;
368 std::string findExpr = cmStrCat(top, "/*");
369 cmCPackLogger(cmCPackLog::LOG_OUTPUT,
370 "- Install directory: " << top << std::endl);
371 gl.RecurseOn();
372 gl.SetRecurseListDirs(true);
373 gl.SetRecurseThroughSymlinks(false);
374 if (!gl.FindFiles(findExpr)) {
375 cmCPackLogger(cmCPackLog::LOG_ERROR,
376 "Cannot find any files in the installed directory"
377 << std::endl);
378 return 0;
379 }
380 this->files = gl.GetFiles();
381 for (std::string const& gf : this->files) {
382 bool skip = false;
383 std::string inFile = gf;
384 if (cmSystemTools::FileIsDirectory(gf) &&
385 !cmSystemTools::FileIsSymlink(gf)) {
386 inFile += '/';
387 }
388 for (cmsys::RegularExpression& reg : ignoreFilesRegex) {
389 if (reg.find(inFile)) {
390 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
391 "Ignore file: " << inFile << std::endl);
392 skip = true;
393 }
394 }
395 if (skip) {
396 continue;
397 }
398 std::string filePath = cmStrCat(tempDir, '/', subdir, '/',
399 cmSystemTools::RelativePath(top, gf));
400 cmCPackLogger(cmCPackLog::LOG_DEBUG,
401 "Copy file: " << inFile << " -> " << filePath
402 << std::endl);
403 /* If the file is a symlink we will have to re-create it */
404 if (cmSystemTools::FileIsSymlink(inFile)) {
405 std::string targetFile;
406 std::string inFileRelative =
407 cmSystemTools::RelativePath(top, inFile);
408 cmSystemTools::ReadSymlink(inFile, targetFile);
409 symlinkedFiles.emplace_back(std::move(targetFile),
410 std::move(inFileRelative));
411 }
412 /* If it is not a symlink then do a plain copy */
413 else if (!(cmSystemTools::CopyFileIfDifferent(inFile, filePath) &&
414 cmFileTimes::Copy(inFile, filePath))) {
415 cmCPackLogger(cmCPackLog::LOG_ERROR,
416 "Problem copying file: " << inFile << " -> "
417 << filePath << std::endl);
418 return 0;
419 }
420 }
421 /* rebuild symlinks in the installed tree */
422 if (!symlinkedFiles.empty()) {
423 std::string goToDir = cmStrCat(tempDir, '/', subdir);
424 cmCPackLogger(cmCPackLog::LOG_DEBUG,
425 "Change dir to: " << goToDir << std::endl);
426 cmWorkingDirectory workdir(goToDir);
427 if (workdir.Failed()) {
428 cmCPackLogger(cmCPackLog::LOG_ERROR,
429 "Failed to change working directory to "
430 << goToDir << " : "
431 << std::strerror(workdir.GetLastResult())
432 << std::endl);
433 return 0;
434 }
435 for (auto const& symlinked : symlinkedFiles) {
436 cmCPackLogger(cmCPackLog::LOG_DEBUG,
437 "Will create a symlink: " << symlinked.second << "--> "
438 << symlinked.first
439 << std::endl);
440 // make sure directory exists for symlink
441 std::string destDir =
442 cmSystemTools::GetFilenamePath(symlinked.second);
443 if (!destDir.empty() &&
444 !cmSystemTools::MakeDirectory(destDir, default_dir_mode)) {
445 cmCPackLogger(cmCPackLog::LOG_ERROR,
446 "Cannot create dir: "
447 << destDir << "\nTrying to create symlink: "
448 << symlinked.second << "--> " << symlinked.first
449 << std::endl);
450 }
451 if (!cmSystemTools::CreateSymlink(symlinked.first,
452 symlinked.second)) {
453 cmCPackLogger(cmCPackLog::LOG_ERROR,
454 "Cannot create symlink: "
455 << symlinked.second << "--> " << symlinked.first
456 << std::endl);
457 return 0;
458 }
459 }
460 cmCPackLogger(cmCPackLog::LOG_DEBUG,
461 "Going back to: " << workdir.GetOldDirectory()
462 << std::endl);
463 }
464 }
465 }
466 return 1;
467 }
468
InstallProjectViaInstallScript(bool setDestDir,const std::string & tempInstallDirectory)469 int cmCPackGenerator::InstallProjectViaInstallScript(
470 bool setDestDir, const std::string& tempInstallDirectory)
471 {
472 cmValue cmakeScripts = this->GetOption("CPACK_INSTALL_SCRIPTS");
473 {
474 cmValue const cmakeScript = this->GetOption("CPACK_INSTALL_SCRIPT");
475 if (cmakeScript && cmakeScripts) {
476 cmCPackLogger(
477 cmCPackLog::LOG_WARNING,
478 "Both CPACK_INSTALL_SCRIPTS and CPACK_INSTALL_SCRIPT are set, "
479 "the latter will be ignored."
480 << std::endl);
481 } else if (cmakeScript && !cmakeScripts) {
482 cmakeScripts = cmakeScript;
483 }
484 }
485 if (cmakeScripts && !cmakeScripts->empty()) {
486 cmCPackLogger(cmCPackLog::LOG_OUTPUT,
487 "- Install scripts: " << cmakeScripts << std::endl);
488 std::vector<std::string> cmakeScriptsVector = cmExpandedList(cmakeScripts);
489 for (std::string const& installScript : cmakeScriptsVector) {
490
491 cmCPackLogger(cmCPackLog::LOG_OUTPUT,
492 "- Install script: " << installScript << std::endl);
493
494 if (setDestDir) {
495 // For DESTDIR based packaging, use the *project*
496 // CMAKE_INSTALL_PREFIX underneath the tempInstallDirectory. The
497 // value of the project's CMAKE_INSTALL_PREFIX is sent in here as the
498 // value of the CPACK_INSTALL_PREFIX variable.
499
500 std::string dir;
501 if (this->GetOption("CPACK_INSTALL_PREFIX")) {
502 dir += *this->GetOption("CPACK_INSTALL_PREFIX");
503 }
504 this->SetOption("CMAKE_INSTALL_PREFIX", dir);
505 cmCPackLogger(
506 cmCPackLog::LOG_DEBUG,
507 "- Using DESTDIR + CPACK_INSTALL_PREFIX... (this->SetOption)"
508 << std::endl);
509 cmCPackLogger(cmCPackLog::LOG_DEBUG,
510 "- Setting CMAKE_INSTALL_PREFIX to '" << dir << "'"
511 << std::endl);
512 } else {
513 this->SetOption("CMAKE_INSTALL_PREFIX", tempInstallDirectory);
514
515 cmCPackLogger(cmCPackLog::LOG_DEBUG,
516 "- Using non-DESTDIR install... (this->SetOption)"
517 << std::endl);
518 cmCPackLogger(cmCPackLog::LOG_DEBUG,
519 "- Setting CMAKE_INSTALL_PREFIX to '"
520 << tempInstallDirectory << "'" << std::endl);
521 }
522
523 this->SetOptionIfNotSet("CMAKE_CURRENT_BINARY_DIR",
524 tempInstallDirectory);
525 this->SetOptionIfNotSet("CMAKE_CURRENT_SOURCE_DIR",
526 tempInstallDirectory);
527 bool res = this->MakefileMap->ReadListFile(installScript);
528 if (cmSystemTools::GetErrorOccuredFlag() || !res) {
529 return 0;
530 }
531 }
532 }
533 return 1;
534 }
535
InstallProjectViaInstallCMakeProjects(bool setDestDir,const std::string & baseTempInstallDirectory,const mode_t * default_dir_mode)536 int cmCPackGenerator::InstallProjectViaInstallCMakeProjects(
537 bool setDestDir, const std::string& baseTempInstallDirectory,
538 const mode_t* default_dir_mode)
539 {
540 cmValue cmakeProjects = this->GetOption("CPACK_INSTALL_CMAKE_PROJECTS");
541 cmValue cmakeGenerator = this->GetOption("CPACK_CMAKE_GENERATOR");
542 std::string absoluteDestFiles;
543 if (cmNonempty(cmakeProjects)) {
544 if (!cmakeGenerator) {
545 cmCPackLogger(cmCPackLog::LOG_ERROR,
546 "CPACK_INSTALL_CMAKE_PROJECTS is specified, but "
547 "CPACK_CMAKE_GENERATOR is not. CPACK_CMAKE_GENERATOR "
548 "is required to install the project."
549 << std::endl);
550 return 0;
551 }
552 std::vector<std::string> cmakeProjectsVector =
553 cmExpandedList(cmakeProjects);
554 std::vector<std::string>::iterator it;
555 for (it = cmakeProjectsVector.begin(); it != cmakeProjectsVector.end();
556 ++it) {
557 if (it + 1 == cmakeProjectsVector.end() ||
558 it + 2 == cmakeProjectsVector.end() ||
559 it + 3 == cmakeProjectsVector.end()) {
560 cmCPackLogger(
561 cmCPackLog::LOG_ERROR,
562 "Not enough items on list: CPACK_INSTALL_CMAKE_PROJECTS. "
563 "CPACK_INSTALL_CMAKE_PROJECTS should hold quadruplet of install "
564 "directory, install project name, install component, and install "
565 "subdirectory."
566 << std::endl);
567 return 0;
568 }
569 std::string installDirectory = *it;
570 ++it;
571 std::string installProjectName = *it;
572 ++it;
573 cmCPackInstallCMakeProject project;
574
575 project.Directory = installDirectory;
576 project.ProjectName = installProjectName;
577 project.Component = *it;
578 ++it;
579 project.SubDirectory = *it;
580
581 std::vector<std::string> componentsVector;
582
583 bool componentInstall = false;
584 /*
585 * We do a component install iff
586 * - the CPack generator support component
587 * - the user did not request Monolithic install
588 * (this works at CPack time too)
589 */
590 if (this->SupportsComponentInstallation() &&
591 !(this->IsOn("CPACK_MONOLITHIC_INSTALL"))) {
592 // Determine the installation types for this project (if provided).
593 std::string installTypesVar = "CPACK_" +
594 cmSystemTools::UpperCase(project.Component) + "_INSTALL_TYPES";
595 cmValue installTypes = this->GetOption(installTypesVar);
596 if (cmNonempty(installTypes)) {
597 std::vector<std::string> installTypesVector =
598 cmExpandedList(installTypes);
599 for (std::string const& installType : installTypesVector) {
600 project.InstallationTypes.push_back(
601 this->GetInstallationType(project.ProjectName, installType));
602 }
603 }
604
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 (cmNonempty(components)) {
610 cmExpandList(components, componentsVector);
611 for (std::string const& comp : componentsVector) {
612 project.Components.push_back(
613 this->GetComponent(project.ProjectName, comp));
614 }
615 componentInstall = true;
616 }
617 }
618 if (componentsVector.empty()) {
619 componentsVector.push_back(project.Component);
620 }
621
622 std::vector<std::string> buildConfigs;
623
624 // Try get configuration names given via `-C` CLI option
625 cmExpandList(this->GetOption("CPACK_BUILD_CONFIG"), buildConfigs);
626
627 // Remove duplicates
628 std::sort(buildConfigs.begin(), buildConfigs.end());
629 buildConfigs.erase(std::unique(buildConfigs.begin(), buildConfigs.end()),
630 buildConfigs.end());
631
632 // Ensure we have at least one configuration.
633 if (buildConfigs.empty()) {
634 buildConfigs.emplace_back();
635 }
636
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;
646 }
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());
651
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;
657 }
658
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 : componentsVector) {
665 if (!this->InstallCMakeProject(
666 setDestDir, project.Directory, baseTempInstallDirectory,
667 default_dir_mode, component, componentInstall,
668 project.SubDirectory, buildConfig, absoluteDestFiles)) {
669 return 0;
670 }
671 }
672 }
673
674 this->CMakeProjects.emplace_back(std::move(project));
675 }
676 }
677 this->SetOption("CPACK_ABSOLUTE_DESTINATION_FILES", absoluteDestFiles);
678 return 1;
679 }
680
RunPreinstallTarget(const std::string & installProjectName,const std::string & installDirectory,cmGlobalGenerator * globalGenerator,const std::string & buildConfig)681 int cmCPackGenerator::RunPreinstallTarget(
682 const std::string& installProjectName, const std::string& installDirectory,
683 cmGlobalGenerator* globalGenerator, const std::string& buildConfig)
684 {
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;
713 }
714 }
715
716 return 1;
717 }
718
InstallCMakeProject(bool setDestDir,const std::string & installDirectory,const std::string & baseTempInstallDirectory,const mode_t * default_dir_mode,const std::string & component,bool componentInstall,const std::string & installSubDirectory,const std::string & buildConfig,std::string & absoluteDestFiles)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)
725 {
726 std::string tempInstallDirectory = baseTempInstallDirectory;
727 std::string installFile = installDirectory + "/cmake_install.cmake";
728
729 if (componentInstall) {
730 cmCPackLogger(cmCPackLog::LOG_OUTPUT,
731 "- Install component: " << component << std::endl);
732 }
733
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);
741 });
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;
749 }
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);
759 if (this->IsOn("CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY")) {
760 tempInstallDirectory += "/";
761 tempInstallDirectory += *this->GetOption("CPACK_PACKAGE_FILE_NAME");
762 }
763 }
764
765 cmValue default_dir_inst_permissions =
766 this->GetOption("CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS");
767 if (cmNonempty(default_dir_inst_permissions)) {
768 mf.AddDefinition("CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS",
769 default_dir_inst_permissions);
770 }
771
772 if (!setDestDir) {
773 tempInstallDirectory += this->GetPackagingInstallPrefix();
774 }
775
776 if (setDestDir) {
777 // For DESTDIR based packaging, use the *project*
778 // CMAKE_INSTALL_PREFIX underneath the tempInstallDirectory. The
779 // value of the project's CMAKE_INSTALL_PREFIX is sent in here as
780 // the value of the CPACK_INSTALL_PREFIX variable.
781 //
782 // If DESTDIR has been 'internally set ON' this means that
783 // the underlying CPack specific generator did ask for that
784 // In this case we may override CPACK_INSTALL_PREFIX with
785 // CPACK_PACKAGING_INSTALL_PREFIX
786 // I know this is tricky and awkward but it's the price for
787 // CPACK_SET_DESTDIR backward compatibility.
788 if (cmIsInternallyOn(this->GetOption("CPACK_SET_DESTDIR"))) {
789 this->SetOption("CPACK_INSTALL_PREFIX",
790 this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX"));
791 }
792 std::string dir;
793 if (this->GetOption("CPACK_INSTALL_PREFIX")) {
794 dir += *this->GetOption("CPACK_INSTALL_PREFIX");
795 }
796 mf.AddDefinition("CMAKE_INSTALL_PREFIX", dir);
797
798 cmCPackLogger(
799 cmCPackLog::LOG_DEBUG,
800 "- Using DESTDIR + CPACK_INSTALL_PREFIX... (mf.AddDefinition)"
801 << std::endl);
802 cmCPackLogger(cmCPackLog::LOG_DEBUG,
803 "- Setting CMAKE_INSTALL_PREFIX to '" << dir << "'"
804 << std::endl);
805
806 // Make sure that DESTDIR + CPACK_INSTALL_PREFIX directory
807 // exists:
808 //
809 if (cmHasLiteralPrefix(dir, "/")) {
810 dir = tempInstallDirectory + dir;
811 } else {
812 dir = tempInstallDirectory + "/" + dir;
813 }
814 /*
815 * We must re-set DESTDIR for each component
816 * We must not add the CPACK_INSTALL_PREFIX part because
817 * it will be added using the override of CMAKE_INSTALL_PREFIX
818 * The main reason for this awkward trick is that
819 * are using DESTDIR for 2 different reasons:
820 * - Because it was asked by the CPack Generator or the user
821 * using CPACK_SET_DESTDIR
822 * - Because it was already used for component install
823 * in order to put things in subdirs...
824 */
825 cmSystemTools::PutEnv("DESTDIR=" + tempInstallDirectory);
826 cmCPackLogger(cmCPackLog::LOG_DEBUG,
827 "- Creating directory: '" << dir << "'" << std::endl);
828
829 if (!cmsys::SystemTools::MakeDirectory(dir, default_dir_mode)) {
830 cmCPackLogger(cmCPackLog::LOG_ERROR,
831 "Problem creating temporary directory: " << dir
832 << std::endl);
833 return 0;
834 }
835 } else {
836 mf.AddDefinition("CMAKE_INSTALL_PREFIX", tempInstallDirectory);
837
838 if (!cmsys::SystemTools::MakeDirectory(tempInstallDirectory,
839 default_dir_mode)) {
840 cmCPackLogger(cmCPackLog::LOG_ERROR,
841 "Problem creating temporary directory: "
842 << tempInstallDirectory << std::endl);
843 return 0;
844 }
845
846 cmCPackLogger(cmCPackLog::LOG_DEBUG,
847 "- Using non-DESTDIR install... (mf.AddDefinition)"
848 << std::endl);
849 cmCPackLogger(cmCPackLog::LOG_DEBUG,
850 "- Setting CMAKE_INSTALL_PREFIX to '" << tempInstallDirectory
851 << "'" << std::endl);
852 }
853
854 if (!buildConfig.empty()) {
855 mf.AddDefinition("BUILD_TYPE", buildConfig);
856 }
857 std::string installComponentLowerCase = cmSystemTools::LowerCase(component);
858 if (installComponentLowerCase != "all") {
859 mf.AddDefinition("CMAKE_INSTALL_COMPONENT", component);
860 }
861
862 // strip on TRUE, ON, 1, one or several file names, but not on
863 // FALSE, OFF, 0 and an empty string
864 if (!cmIsOff(this->GetOption("CPACK_STRIP_FILES"))) {
865 mf.AddDefinition("CMAKE_INSTALL_DO_STRIP", "1");
866 }
867 // Remember the list of files before installation
868 // of the current component (if we are in component install)
869 std::string const& InstallPrefix = tempInstallDirectory;
870 std::vector<std::string> filesBefore;
871 std::string findExpr = tempInstallDirectory;
872 if (componentInstall) {
873 cmsys::Glob glB;
874 findExpr += "/*";
875 glB.RecurseOn();
876 glB.SetRecurseListDirs(true);
877 glB.SetRecurseThroughSymlinks(false);
878 glB.FindFiles(findExpr);
879 filesBefore = glB.GetFiles();
880 std::sort(filesBefore.begin(), filesBefore.end());
881 }
882
883 // If CPack was asked to warn on ABSOLUTE INSTALL DESTINATION
884 // then forward request to cmake_install.cmake script
885 if (this->IsOn("CPACK_WARN_ON_ABSOLUTE_INSTALL_DESTINATION")) {
886 mf.AddDefinition("CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION", "1");
887 }
888 // If current CPack generator does support
889 // ABSOLUTE INSTALL DESTINATION or CPack has been asked for
890 // then ask cmake_install.cmake script to error out
891 // as soon as it occurs (before installing file)
892 if (!this->SupportsAbsoluteDestination() ||
893 this->IsOn("CPACK_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION")) {
894 mf.AddDefinition("CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION", "1");
895 }
896
897 std::vector<std::string> custom_variables;
898 this->MakefileMap->GetDefExpandList("CPACK_CUSTOM_INSTALL_VARIABLES",
899 custom_variables);
900
901 for (auto const& custom_variable : custom_variables) {
902 std::string value;
903
904 auto i = custom_variable.find('=');
905
906 if (i != std::string::npos) {
907 value = custom_variable.substr(i + 1);
908 }
909
910 mf.AddDefinition(custom_variable.substr(0, i), value);
911 }
912
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);
920 }
921
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());
937
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);
949 }
950 }
951
952 if (cmValue d = mf.GetDefinition("CPACK_ABSOLUTE_DESTINATION_FILES")) {
953 if (!absoluteDestFiles.empty()) {
954 absoluteDestFiles += ";";
955 }
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->GetComponentInstallDirNameSuffix(component);
965 if (nullptr != 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"));
973 }
974 }
975 }
976 if (cmSystemTools::GetErrorOccuredFlag() || !res) {
977 return 0;
978 }
979 return 1;
980 }
981
ReadListFile(const char * moduleName)982 bool cmCPackGenerator::ReadListFile(const char* moduleName)
983 {
984 bool retval;
985 std::string fullPath = this->MakefileMap->GetModulesFile(moduleName);
986 retval = this->MakefileMap->ReadListFile(fullPath);
987 // include FATAL_ERROR and ERROR in the return status
988 retval = retval && (!cmSystemTools::GetErrorOccuredFlag());
989 return retval;
990 }
991
992 template <typename ValueType>
StoreOptionIfNotSet(const std::string & op,ValueType value)993 void cmCPackGenerator::StoreOptionIfNotSet(const std::string& op,
994 ValueType value)
995 {
996 cmValue def = this->MakefileMap->GetDefinition(op);
997 if (cmNonempty(def)) {
998 return;
999 }
1000 this->StoreOption(op, value);
1001 }
1002
SetOptionIfNotSet(const std::string & op,const char * value)1003 void cmCPackGenerator::SetOptionIfNotSet(const std::string& op,
1004 const char* value)
1005 {
1006 this->StoreOptionIfNotSet(op, value);
1007 }
SetOptionIfNotSet(const std::string & op,cmValue value)1008 void cmCPackGenerator::SetOptionIfNotSet(const std::string& op, cmValue value)
1009 {
1010 this->StoreOptionIfNotSet(op, value);
1011 }
1012
1013 template <typename ValueType>
StoreOption(const std::string & op,ValueType value)1014 void cmCPackGenerator::StoreOption(const std::string& op, ValueType value)
1015 {
1016 if (!value) {
1017 this->MakefileMap->RemoveDefinition(op);
1018 return;
1019 }
1020 cmCPackLogger(cmCPackLog::LOG_DEBUG,
1021 this->GetNameOfClass() << "::SetOption(" << op << ", " << value
1022 << ")" << std::endl);
1023 this->MakefileMap->AddDefinition(op, value);
1024 }
1025
SetOption(const std::string & op,const char * value)1026 void cmCPackGenerator::SetOption(const std::string& op, const char* value)
1027 {
1028 this->StoreOption(op, value);
1029 }
SetOption(const std::string & op,cmValue value)1030 void cmCPackGenerator::SetOption(const std::string& op, cmValue value)
1031 {
1032 this->StoreOption(op, value);
1033 }
1034
DoPackage()1035 int cmCPackGenerator::DoPackage()
1036 {
1037 cmCPackLogger(cmCPackLog::LOG_OUTPUT,
1038 "Create package using " << this->Name << std::endl);
1039
1040 // Prepare CPack internal name and check
1041 // values for many CPACK_xxx vars
1042 if (!this->PrepareNames()) {
1043 return 0;
1044 }
1045
1046 // Digest Component grouping specification
1047 if (!this->PrepareGroupingKind()) {
1048 return 0;
1049 }
1050
1051 if (cmIsOn(this->GetOption("CPACK_REMOVE_TOPLEVEL_DIRECTORY"))) {
1052 cmValue toplevelDirectory = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
1053 if (cmSystemTools::FileExists(toplevelDirectory)) {
1054 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
1055 "Remove toplevel directory: " << toplevelDirectory
1056 << std::endl);
1057 if (!cmSystemTools::RepeatedRemoveDirectory(toplevelDirectory)) {
1058 cmCPackLogger(cmCPackLog::LOG_ERROR,
1059 "Problem removing toplevel directory: "
1060 << toplevelDirectory << std::endl);
1061 return 0;
1062 }
1063 }
1064 }
1065 cmCPackLogger(cmCPackLog::LOG_DEBUG,
1066 "About to install project " << std::endl);
1067
1068 if (!this->InstallProject()) {
1069 return 0;
1070 }
1071 cmCPackLogger(cmCPackLog::LOG_DEBUG, "Done install project " << std::endl);
1072
1073 cmValue tempPackageFileName =
1074 this->GetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME");
1075 cmValue tempDirectory = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
1076
1077 cmCPackLogger(cmCPackLog::LOG_DEBUG, "Find files" << std::endl);
1078 cmsys::Glob gl;
1079 std::string findExpr = cmStrCat(tempDirectory, "/*");
1080 gl.RecurseOn();
1081 gl.SetRecurseListDirs(true);
1082 gl.SetRecurseThroughSymlinks(false);
1083 if (!gl.FindFiles(findExpr)) {
1084 cmCPackLogger(cmCPackLog::LOG_ERROR,
1085 "Cannot find any files in the packaging tree" << std::endl);
1086 return 0;
1087 }
1088
1089 cmCPackLogger(cmCPackLog::LOG_OUTPUT, "Create package" << std::endl);
1090 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
1091 "Package files to: "
1092 << (tempPackageFileName ? *tempPackageFileName : "(NULL)")
1093 << std::endl);
1094 if (cmSystemTools::FileExists(tempPackageFileName)) {
1095 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
1096 "Remove old package file" << std::endl);
1097 cmSystemTools::RemoveFile(tempPackageFileName);
1098 }
1099 if (cmIsOn(this->GetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY"))) {
1100 tempDirectory = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
1101 }
1102
1103 // The files to be installed
1104 this->files = gl.GetFiles();
1105
1106 this->packageFileNames.clear();
1107 /* Put at least one file name into the list of
1108 * wanted packageFileNames. The specific generator
1109 * may update this during PackageFiles.
1110 * (either putting several names or updating the provided one)
1111 */
1112 this->packageFileNames.emplace_back(tempPackageFileName);
1113 this->toplevel = *tempDirectory;
1114 { // scope that enables package generators to run internal scripts with
1115 // latest CMake policies enabled
1116 cmMakefile::ScopePushPop pp{ this->MakefileMap };
1117 this->MakefileMap->SetPolicyVersion(cmVersion::GetCMakeVersion(),
1118 std::string());
1119
1120 if (!this->PackageFiles() || cmSystemTools::GetErrorOccuredFlag()) {
1121 cmCPackLogger(cmCPackLog::LOG_ERROR,
1122 "Problem compressing the directory" << std::endl);
1123 return 0;
1124 }
1125 }
1126 // Run post-build actions
1127 cmValue postBuildScripts = this->GetOption("CPACK_POST_BUILD_SCRIPTS");
1128 if (postBuildScripts) {
1129 this->MakefileMap->AddDefinition("CPACK_PACKAGE_FILES",
1130 cmJoin(this->packageFileNames, ";"));
1131
1132 const auto scripts = cmExpandedList(postBuildScripts, false);
1133 for (const auto& script : scripts) {
1134 cmCPackLogger(cmCPackLog::LOG_OUTPUT,
1135 "Executing post-build script: " << script << std::endl);
1136
1137 if (!this->MakefileMap->ReadListFile(script)) {
1138 cmCPackLogger(cmCPackLog::LOG_ERROR,
1139 "The post-build script not found: " << script
1140 << std::endl);
1141 return 0;
1142 }
1143 }
1144 }
1145
1146 /* Prepare checksum algorithm*/
1147 cmValue algo = this->GetOption("CPACK_PACKAGE_CHECKSUM");
1148 std::unique_ptr<cmCryptoHash> crypto = cmCryptoHash::New(*algo);
1149
1150 /*
1151 * Copy the generated packages to final destination
1152 * - there may be several of them
1153 * - the initially provided name may have changed
1154 * (because the specific generator did 'normalize' it)
1155 */
1156 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
1157 "Copying final package(s) [" << this->packageFileNames.size()
1158 << "]:" << std::endl);
1159 /* now copy package one by one */
1160 for (std::string const& pkgFileName : this->packageFileNames) {
1161 std::string tmpPF(this->GetOption("CPACK_OUTPUT_FILE_PREFIX"));
1162 std::string filename(cmSystemTools::GetFilenameName(pkgFileName));
1163 tempPackageFileName = cmValue(pkgFileName);
1164 tmpPF += "/" + filename;
1165 const char* packageFileName = tmpPF.c_str();
1166 cmCPackLogger(cmCPackLog::LOG_DEBUG,
1167 "Copy final package(s): "
1168 << (tempPackageFileName ? *tempPackageFileName : "(NULL)")
1169 << " to " << (packageFileName ? packageFileName : "(NULL)")
1170 << std::endl);
1171 if (!cmSystemTools::CopyFileIfDifferent(pkgFileName, tmpPF)) {
1172 cmCPackLogger(
1173 cmCPackLog::LOG_ERROR,
1174 "Problem copying the package: "
1175 << (tempPackageFileName ? *tempPackageFileName : "(NULL)") << " to "
1176 << (packageFileName ? packageFileName : "(NULL)") << std::endl);
1177 return 0;
1178 }
1179 cmCPackLogger(cmCPackLog::LOG_OUTPUT,
1180 "- package: " << packageFileName << " generated."
1181 << std::endl);
1182
1183 /* Generate checksum file */
1184 if (crypto) {
1185 std::string hashFile(this->GetOption("CPACK_OUTPUT_FILE_PREFIX"));
1186 hashFile += "/" + filename;
1187 hashFile += "." + cmSystemTools::LowerCase(algo);
1188 cmsys::ofstream outF(hashFile.c_str());
1189 if (!outF) {
1190 cmCPackLogger(cmCPackLog::LOG_ERROR,
1191 "Cannot create checksum file: " << hashFile
1192 << std::endl);
1193 return 0;
1194 }
1195 outF << crypto->HashFile(packageFileName) << " " << filename << "\n";
1196 cmCPackLogger(cmCPackLog::LOG_OUTPUT,
1197 "- checksum file: " << hashFile << " generated."
1198 << std::endl);
1199 }
1200 }
1201
1202 return 1;
1203 }
1204
Initialize(const std::string & name,cmMakefile * mf)1205 int cmCPackGenerator::Initialize(const std::string& name, cmMakefile* mf)
1206 {
1207 this->MakefileMap = mf;
1208 this->Name = name;
1209 // set the running generator name
1210 this->SetOption("CPACK_GENERATOR", this->Name);
1211 // Load the project specific config file
1212 cmValue config = this->GetOption("CPACK_PROJECT_CONFIG_FILE");
1213 if (config) {
1214 mf->ReadListFile(config);
1215 }
1216 int result = this->InitializeInternal();
1217 if (cmSystemTools::GetErrorOccuredFlag()) {
1218 return 0;
1219 }
1220
1221 // If a generator subclass did not already set this option in its
1222 // InitializeInternal implementation, and the project did not already set
1223 // it, the default value should be:
1224 this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/");
1225
1226 return result;
1227 }
1228
InitializeInternal()1229 int cmCPackGenerator::InitializeInternal()
1230 {
1231 return 1;
1232 }
1233
IsSet(const std::string & name) const1234 bool cmCPackGenerator::IsSet(const std::string& name) const
1235 {
1236 return this->MakefileMap->IsSet(name);
1237 }
1238
IsOn(const std::string & name) const1239 bool cmCPackGenerator::IsOn(const std::string& name) const
1240 {
1241 return cmIsOn(this->GetOption(name));
1242 }
1243
IsSetToOff(const std::string & op) const1244 bool cmCPackGenerator::IsSetToOff(const std::string& op) const
1245 {
1246 cmValue ret = this->MakefileMap->GetDefinition(op);
1247 if (cmNonempty(ret)) {
1248 return cmIsOff(*ret);
1249 }
1250 return false;
1251 }
1252
IsSetToEmpty(const std::string & op) const1253 bool cmCPackGenerator::IsSetToEmpty(const std::string& op) const
1254 {
1255 cmValue ret = this->MakefileMap->GetDefinition(op);
1256 if (ret) {
1257 return ret->empty();
1258 }
1259 return false;
1260 }
1261
GetOption(const std::string & op) const1262 cmValue cmCPackGenerator::GetOption(const std::string& op) const
1263 {
1264 cmValue ret = this->MakefileMap->GetDefinition(op);
1265 if (!ret) {
1266 cmCPackLogger(cmCPackLog::LOG_DEBUG,
1267 "Warning, GetOption return NULL for: " << op << std::endl);
1268 }
1269 return ret;
1270 }
1271
GetOptions() const1272 std::vector<std::string> cmCPackGenerator::GetOptions() const
1273 {
1274 return this->MakefileMap->GetDefinitions();
1275 }
1276
PackageFiles()1277 int cmCPackGenerator::PackageFiles()
1278 {
1279 return 0;
1280 }
1281
GetInstallPath()1282 const char* cmCPackGenerator::GetInstallPath()
1283 {
1284 if (!this->InstallPath.empty()) {
1285 return this->InstallPath.c_str();
1286 }
1287 #if defined(_WIN32) && !defined(__CYGWIN__)
1288 std::string prgfiles;
1289 std::string sysDrive;
1290 if (cmsys::SystemTools::GetEnv("ProgramFiles", prgfiles)) {
1291 this->InstallPath = prgfiles;
1292 } else if (cmsys::SystemTools::GetEnv("SystemDrive", sysDrive)) {
1293 this->InstallPath = cmStrCat(sysDrive, "/Program Files");
1294 } else {
1295 this->InstallPath = "c:/Program Files";
1296 }
1297 this->InstallPath += "/";
1298 this->InstallPath += this->GetOption("CPACK_PACKAGE_NAME");
1299 this->InstallPath += "-";
1300 this->InstallPath += this->GetOption("CPACK_PACKAGE_VERSION");
1301 #elif defined(__HAIKU__)
1302 char dir[B_PATH_NAME_LENGTH];
1303 if (find_directory(B_SYSTEM_DIRECTORY, -1, false, dir, sizeof(dir)) ==
1304 B_OK) {
1305 this->InstallPath = dir;
1306 } else {
1307 this->InstallPath = "/boot/system";
1308 }
1309 #else
1310 this->InstallPath = "/usr/local/";
1311 #endif
1312 return this->InstallPath.c_str();
1313 }
1314
GetPackagingInstallPrefix()1315 const char* cmCPackGenerator::GetPackagingInstallPrefix()
1316 {
1317 cmCPackLogger(cmCPackLog::LOG_DEBUG,
1318 "GetPackagingInstallPrefix: '"
1319 << this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX") << "'"
1320 << std::endl);
1321
1322 return this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX")->c_str();
1323 }
1324
FindTemplate(const char * name)1325 std::string cmCPackGenerator::FindTemplate(const char* name)
1326 {
1327 cmCPackLogger(cmCPackLog::LOG_DEBUG,
1328 "Look for template: " << (name ? name : "(NULL)")
1329 << std::endl);
1330 // Search CMAKE_MODULE_PATH for a custom template.
1331 std::string ffile = this->MakefileMap->GetModulesFile(name);
1332 if (ffile.empty()) {
1333 // Fall back to our internal builtin default.
1334 ffile = cmStrCat(cmSystemTools::GetCMakeRoot(), "/Modules/Internal/CPack/",
1335 name);
1336 cmSystemTools::ConvertToUnixSlashes(ffile);
1337 if (!cmSystemTools::FileExists(ffile)) {
1338 ffile.clear();
1339 }
1340 }
1341 cmCPackLogger(cmCPackLog::LOG_DEBUG,
1342 "Found template: " << ffile << std::endl);
1343 return ffile;
1344 }
1345
ConfigureString(const std::string & inString,std::string & outString)1346 bool cmCPackGenerator::ConfigureString(const std::string& inString,
1347 std::string& outString)
1348 {
1349 this->MakefileMap->ConfigureString(inString, outString, true, false);
1350 return true;
1351 }
1352
ConfigureFile(const std::string & inName,const std::string & outName,bool copyOnly)1353 bool cmCPackGenerator::ConfigureFile(const std::string& inName,
1354 const std::string& outName,
1355 bool copyOnly /* = false */)
1356 {
1357 return this->MakefileMap->ConfigureFile(inName, outName, copyOnly, true,
1358 false) == 1;
1359 }
1360
CleanTemporaryDirectory()1361 int cmCPackGenerator::CleanTemporaryDirectory()
1362 {
1363 std::string tempInstallDirectory =
1364 this->GetOption("CPACK_TEMPORARY_INSTALL_DIRECTORY");
1365 if (cmsys::SystemTools::FileExists(tempInstallDirectory)) {
1366 cmCPackLogger(cmCPackLog::LOG_OUTPUT,
1367 "- Clean temporary : " << tempInstallDirectory << std::endl);
1368 if (!cmSystemTools::RepeatedRemoveDirectory(tempInstallDirectory)) {
1369 cmCPackLogger(cmCPackLog::LOG_ERROR,
1370 "Problem removing temporary directory: "
1371 << tempInstallDirectory << std::endl);
1372 return 0;
1373 }
1374 }
1375 return 1;
1376 }
1377
GetInstalledFile(std::string const & name) const1378 cmInstalledFile const* cmCPackGenerator::GetInstalledFile(
1379 std::string const& name) const
1380 {
1381 cmake const* cm = this->MakefileMap->GetCMakeInstance();
1382 return cm->GetInstalledFile(name);
1383 }
1384
PrepareGroupingKind()1385 int cmCPackGenerator::PrepareGroupingKind()
1386 {
1387 // find a component package method specified by the user
1388 ComponentPackageMethod method = UNKNOWN_COMPONENT_PACKAGE_METHOD;
1389
1390 if (this->GetOption("CPACK_COMPONENTS_ALL_IN_ONE_PACKAGE")) {
1391 method = ONE_PACKAGE;
1392 }
1393
1394 if (this->GetOption("CPACK_COMPONENTS_IGNORE_GROUPS")) {
1395 method = ONE_PACKAGE_PER_COMPONENT;
1396 }
1397
1398 if (this->GetOption("CPACK_COMPONENTS_ONE_PACKAGE_PER_GROUP")) {
1399 method = ONE_PACKAGE_PER_GROUP;
1400 }
1401
1402 // Second way to specify grouping
1403 std::string groupingType = *this->GetOption("CPACK_COMPONENTS_GROUPING");
1404
1405 if (!groupingType.empty()) {
1406 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
1407 "[" << this->Name << "]"
1408 << " requested component grouping = " << groupingType
1409 << std::endl);
1410 if (groupingType == "ALL_COMPONENTS_IN_ONE") {
1411 method = ONE_PACKAGE;
1412 } else if (groupingType == "IGNORE") {
1413 method = ONE_PACKAGE_PER_COMPONENT;
1414 } else if (groupingType == "ONE_PER_GROUP") {
1415 method = ONE_PACKAGE_PER_GROUP;
1416 } else {
1417 cmCPackLogger(
1418 cmCPackLog::LOG_WARNING,
1419 "[" << this->Name << "]"
1420 << " requested component grouping type <" << groupingType
1421 << "> UNKNOWN not in (ALL_COMPONENTS_IN_ONE,IGNORE,ONE_PER_GROUP)"
1422 << std::endl);
1423 }
1424 }
1425
1426 // Some components were defined but NO group
1427 // fallback to default if not group based
1428 if (method == ONE_PACKAGE_PER_GROUP && this->ComponentGroups.empty() &&
1429 !this->Components.empty()) {
1430 if (this->componentPackageMethod == ONE_PACKAGE) {
1431 method = ONE_PACKAGE;
1432 } else {
1433 method = ONE_PACKAGE_PER_COMPONENT;
1434 }
1435 cmCPackLogger(
1436 cmCPackLog::LOG_WARNING,
1437 "[" << this->Name << "]"
1438 << " One package per component group requested, "
1439 << "but NO component groups exist: Ignoring component group."
1440 << std::endl);
1441 }
1442
1443 // if user specified packaging method, override the default packaging
1444 // method
1445 if (method != UNKNOWN_COMPONENT_PACKAGE_METHOD) {
1446 this->componentPackageMethod = method;
1447 }
1448
1449 const char* method_names[] = { "ALL_COMPONENTS_IN_ONE", "IGNORE_GROUPS",
1450 "ONE_PER_GROUP" };
1451
1452 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
1453 "[" << this->Name << "]"
1454 << " requested component grouping = "
1455 << method_names[this->componentPackageMethod]
1456 << std::endl);
1457
1458 return 1;
1459 }
1460
GetComponentInstallDirNameSuffix(const std::string & componentName)1461 std::string cmCPackGenerator::GetComponentInstallDirNameSuffix(
1462 const std::string& componentName)
1463 {
1464 return componentName;
1465 }
GetComponentPackageFileName(const std::string & initialPackageFileName,const std::string & groupOrComponentName,bool isGroupName)1466 std::string cmCPackGenerator::GetComponentPackageFileName(
1467 const std::string& initialPackageFileName,
1468 const std::string& groupOrComponentName, bool isGroupName)
1469 {
1470
1471 /*
1472 * the default behavior is to use the
1473 * component [group] name as a suffix
1474 */
1475 std::string suffix = "-" + groupOrComponentName;
1476 /* check if we should use DISPLAY name */
1477 std::string dispNameVar =
1478 "CPACK_" + this->Name + "_USE_DISPLAY_NAME_IN_FILENAME";
1479 if (this->IsOn(dispNameVar)) {
1480 /* the component Group case */
1481 if (isGroupName) {
1482 std::string groupDispVar = "CPACK_COMPONENT_GROUP_" +
1483 cmSystemTools::UpperCase(groupOrComponentName) + "_DISPLAY_NAME";
1484 cmValue groupDispName = this->GetOption(groupDispVar);
1485 if (groupDispName) {
1486 suffix = "-" + *groupDispName;
1487 }
1488 }
1489 /* the [single] component case */
1490 else {
1491 std::string dispVar = "CPACK_COMPONENT_" +
1492 cmSystemTools::UpperCase(groupOrComponentName) + "_DISPLAY_NAME";
1493 cmValue dispName = this->GetOption(dispVar);
1494 if (dispName) {
1495 suffix = "-" + *dispName;
1496 }
1497 }
1498 }
1499 return initialPackageFileName + suffix;
1500 }
1501
1502 enum cmCPackGenerator::CPackSetDestdirSupport
SupportsSetDestdir() const1503 cmCPackGenerator::SupportsSetDestdir() const
1504 {
1505 return cmCPackGenerator::SETDESTDIR_SUPPORTED;
1506 }
1507
SupportsAbsoluteDestination() const1508 bool cmCPackGenerator::SupportsAbsoluteDestination() const
1509 {
1510 return true;
1511 }
1512
SupportsComponentInstallation() const1513 bool cmCPackGenerator::SupportsComponentInstallation() const
1514 {
1515 return false;
1516 }
1517
WantsComponentInstallation() const1518 bool cmCPackGenerator::WantsComponentInstallation() const
1519 {
1520 return (!this->IsOn("CPACK_MONOLITHIC_INSTALL") &&
1521 this->SupportsComponentInstallation()
1522 // check that we have at least one group or component
1523 && (!this->ComponentGroups.empty() || !this->Components.empty()));
1524 }
1525
GetInstallationType(const std::string & projectName,const std::string & name)1526 cmCPackInstallationType* cmCPackGenerator::GetInstallationType(
1527 const std::string& projectName, const std::string& name)
1528 {
1529 (void)projectName;
1530 bool hasInstallationType = this->InstallationTypes.count(name) != 0;
1531 cmCPackInstallationType* installType = &this->InstallationTypes[name];
1532 if (!hasInstallationType) {
1533 // Define the installation type
1534 std::string macroPrefix =
1535 "CPACK_INSTALL_TYPE_" + cmsys::SystemTools::UpperCase(name);
1536 installType->Name = name;
1537
1538 cmValue displayName = this->GetOption(macroPrefix + "_DISPLAY_NAME");
1539 if (cmNonempty(displayName)) {
1540 installType->DisplayName = *displayName;
1541 } else {
1542 installType->DisplayName = installType->Name;
1543 }
1544
1545 installType->Index = static_cast<unsigned>(this->InstallationTypes.size());
1546 }
1547 return installType;
1548 }
1549
GetComponent(const std::string & projectName,const std::string & name)1550 cmCPackComponent* cmCPackGenerator::GetComponent(
1551 const std::string& projectName, const std::string& name)
1552 {
1553 bool hasComponent = this->Components.count(name) != 0;
1554 cmCPackComponent* component = &this->Components[name];
1555 if (!hasComponent) {
1556 // Define the component
1557 std::string macroPrefix =
1558 "CPACK_COMPONENT_" + cmsys::SystemTools::UpperCase(name);
1559 component->Name = name;
1560 cmValue displayName = this->GetOption(macroPrefix + "_DISPLAY_NAME");
1561 if (cmNonempty(displayName)) {
1562 component->DisplayName = *displayName;
1563 } else {
1564 component->DisplayName = component->Name;
1565 }
1566 component->IsHidden = this->IsOn(macroPrefix + "_HIDDEN");
1567 component->IsRequired = this->IsOn(macroPrefix + "_REQUIRED");
1568 component->IsDisabledByDefault = this->IsOn(macroPrefix + "_DISABLED");
1569 component->IsDownloaded = this->IsOn(macroPrefix + "_DOWNLOADED") ||
1570 cmIsOn(this->GetOption("CPACK_DOWNLOAD_ALL"));
1571
1572 cmValue archiveFile = this->GetOption(macroPrefix + "_ARCHIVE_FILE");
1573 if (cmNonempty(archiveFile)) {
1574 component->ArchiveFile = *archiveFile;
1575 }
1576
1577 cmValue plist = this->GetOption(macroPrefix + "_PLIST");
1578 if (cmNonempty(plist)) {
1579 component->Plist = *plist;
1580 }
1581
1582 cmValue groupName = this->GetOption(macroPrefix + "_GROUP");
1583 if (cmNonempty(groupName)) {
1584 component->Group = this->GetComponentGroup(projectName, groupName);
1585 component->Group->Components.push_back(component);
1586 } else {
1587 component->Group = nullptr;
1588 }
1589
1590 cmValue description = this->GetOption(macroPrefix + "_DESCRIPTION");
1591 if (cmNonempty(description)) {
1592 component->Description = *description;
1593 }
1594
1595 // Determine the installation types.
1596 cmValue installTypes = this->GetOption(macroPrefix + "_INSTALL_TYPES");
1597 if (cmNonempty(installTypes)) {
1598 std::vector<std::string> installTypesVector =
1599 cmExpandedList(installTypes);
1600 for (std::string const& installType : installTypesVector) {
1601 component->InstallationTypes.push_back(
1602 this->GetInstallationType(projectName, installType));
1603 }
1604 }
1605
1606 // Determine the component dependencies.
1607 cmValue depends = this->GetOption(macroPrefix + "_DEPENDS");
1608 if (cmNonempty(depends)) {
1609 std::vector<std::string> dependsVector = cmExpandedList(depends);
1610 for (std::string const& depend : dependsVector) {
1611 cmCPackComponent* child = this->GetComponent(projectName, depend);
1612 component->Dependencies.push_back(child);
1613 child->ReverseDependencies.push_back(component);
1614 }
1615 }
1616 }
1617 return component;
1618 }
1619
GetComponentGroup(const std::string & projectName,const std::string & name)1620 cmCPackComponentGroup* cmCPackGenerator::GetComponentGroup(
1621 const std::string& projectName, const std::string& name)
1622 {
1623 (void)projectName;
1624 std::string macroPrefix =
1625 "CPACK_COMPONENT_GROUP_" + cmsys::SystemTools::UpperCase(name);
1626 bool hasGroup = this->ComponentGroups.count(name) != 0;
1627 cmCPackComponentGroup* group = &this->ComponentGroups[name];
1628 if (!hasGroup) {
1629 // Define the group
1630 group->Name = name;
1631 cmValue displayName = this->GetOption(macroPrefix + "_DISPLAY_NAME");
1632 if (cmNonempty(displayName)) {
1633 group->DisplayName = *displayName;
1634 } else {
1635 group->DisplayName = group->Name;
1636 }
1637
1638 cmValue description = this->GetOption(macroPrefix + "_DESCRIPTION");
1639 if (cmNonempty(description)) {
1640 group->Description = *description;
1641 }
1642 group->IsBold = this->IsOn(macroPrefix + "_BOLD_TITLE");
1643 group->IsExpandedByDefault = this->IsOn(macroPrefix + "_EXPANDED");
1644 cmValue parentGroupName = this->GetOption(macroPrefix + "_PARENT_GROUP");
1645 if (cmNonempty(parentGroupName)) {
1646 group->ParentGroup =
1647 this->GetComponentGroup(projectName, parentGroupName);
1648 group->ParentGroup->Subgroups.push_back(group);
1649 } else {
1650 group->ParentGroup = nullptr;
1651 }
1652 }
1653 return group;
1654 }
1655