1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
15 #include <cm/optional>
16 #include <cmext/algorithm>
18 #include "cmsys/Encoding.hxx"
20 #include "cmCMakePresetsGraph.h"
21 #include "cmCPackGenerator.h"
22 #include "cmCPackGeneratorFactory.h"
23 #include "cmCPackLog.h"
24 #include "cmCommandLineArgument.h"
25 #include "cmConsoleBuf.h"
26 #include "cmDocumentation.h"
27 #include "cmDocumentationEntry.h"
28 #include "cmDocumentationFormatter.h"
29 #include "cmGlobalGenerator.h"
30 #include "cmMakefile.h"
32 #include "cmStateSnapshot.h"
33 #include "cmStringAlgorithms.h"
34 #include "cmSystemTools.h"
39 const char* cmDocumentationName[][2] = {
40 { nullptr, " cpack - Packaging driver provided by CMake." },
44 const char* cmDocumentationUsage[][2] = {
46 { nullptr, " cpack [options]" },
51 const char* cmDocumentationOptions[][2] = {
52 { "-G <generators>", "Override/define CPACK_GENERATOR" },
53 { "-C <Configuration>", "Specify the project configuration" },
54 { "-D <var>=<value>", "Set a CPack variable." },
55 { "--config <configFile>", "Specify the config file." },
56 { "-V,--verbose", "Enable verbose output" },
57 { "--trace", "Put underlying cmake scripts in trace mode." },
58 { "--trace-expand", "Put underlying cmake scripts in expanded trace mode." },
59 { "--debug", "Enable debug output (for CPack developers)" },
60 { "-P <packageName>", "Override/define CPACK_PACKAGE_NAME" },
61 { "-R <packageVersion>", "Override/define CPACK_PACKAGE_VERSION" },
62 { "-B <packageDirectory>", "Override/define CPACK_PACKAGE_DIRECTORY" },
63 { "--vendor <vendorName>", "Override/define CPACK_PACKAGE_VENDOR" },
64 { "--preset", "Read arguments from a package preset" },
65 { "--list-presets", "List available package presets" },
69 void cpackProgressCallback(const std::string& message, float /*unused*/)
71 std::cout << "-- " << message << std::endl;
76 int main(int argc, char const* const* argv)
78 cmSystemTools::EnsureStdPipes();
80 // Replace streambuf so we can output Unicode to console
81 cmConsoleBuf consoleBuf;
82 consoleBuf.SetUTF8Pipes();
84 cmsys::Encoding::CommandLineArguments args =
85 cmsys::Encoding::CommandLineArguments::Main(argc, argv);
89 std::vector<std::string> inputArgs;
90 inputArgs.reserve(argc - 1);
91 cm::append(inputArgs, argv + 1, argv + argc);
93 cmSystemTools::InitializeLibUV();
94 cmSystemTools::FindCMakeResources(argv[0]);
97 log.SetErrorPrefix("CPack Error: ");
98 log.SetWarningPrefix("CPack Warning: ");
99 log.SetOutputPrefix("CPack: ");
100 log.SetVerbosePrefix("CPack Verbose: ");
102 if (cmSystemTools::GetCurrentWorkingDirectory().empty()) {
103 cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
104 "Current working directory cannot be established."
109 std::string generator;
111 bool helpVersion = false;
112 std::string helpFull;
114 std::string helpHTML;
116 std::string cpackProjectName;
117 std::string cpackProjectDirectory;
118 std::string cpackBuildConfig;
119 std::string cpackProjectVersion;
120 std::string cpackProjectPatch;
121 std::string cpackProjectVendor;
122 std::string cpackConfigFile;
125 bool listPresets = false;
127 std::map<std::string, std::string> definitions;
129 auto const verboseLambda = [&log](const std::string&, cmake*,
130 cmMakefile*) -> bool {
131 log.SetVerbose(true);
132 cmCPack_Log(&log, cmCPackLog::LOG_OUTPUT, "Enable Verbose" << std::endl);
136 auto const debugLambda = [&log](const std::string&, cmake*,
137 cmMakefile*) -> bool {
139 cmCPack_Log(&log, cmCPackLog::LOG_OUTPUT, "Enable Debug" << std::endl);
143 auto const traceLambda = [](const std::string&, cmake* state,
144 cmMakefile*) -> bool {
145 state->SetTrace(true);
149 auto const traceExpandLambda = [](const std::string&, cmake* state,
150 cmMakefile*) -> bool {
151 state->SetTrace(true);
152 state->SetTraceExpand(true);
156 using CommandArgument =
157 cmCommandLineArgument<bool(std::string const&, cmake*, cmMakefile*)>;
159 std::vector<CommandArgument> arguments = {
160 CommandArgument{ "--help", CommandArgument::Values::Zero,
161 CommandArgument::setToTrue(help) },
162 CommandArgument{ "--help-full", CommandArgument::Values::Zero,
163 CommandArgument::setToValue(helpFull) },
164 CommandArgument{ "--help-html", CommandArgument::Values::Zero,
165 CommandArgument::setToValue(helpHTML) },
166 CommandArgument{ "--help-man", CommandArgument::Values::Zero,
167 CommandArgument::setToValue(helpMAN) },
168 CommandArgument{ "--version", CommandArgument::Values::Zero,
169 CommandArgument::setToTrue(helpVersion) },
170 CommandArgument{ "-V", CommandArgument::Values::Zero, verboseLambda },
171 CommandArgument{ "--verbose", CommandArgument::Values::Zero,
173 CommandArgument{ "--debug", CommandArgument::Values::Zero, debugLambda },
174 CommandArgument{ "--config", CommandArgument::Values::One,
175 CommandArgument::setToValue(cpackConfigFile) },
176 CommandArgument{ "--trace", CommandArgument::Values::Zero, traceLambda },
177 CommandArgument{ "--trace-expand", CommandArgument::Values::Zero,
179 CommandArgument{ "-C", CommandArgument::Values::One,
180 CommandArgument::setToValue(cpackBuildConfig) },
181 CommandArgument{ "-G", CommandArgument::Values::One,
182 CommandArgument::setToValue(generator) },
183 CommandArgument{ "-P", CommandArgument::Values::One,
184 CommandArgument::setToValue(cpackProjectName) },
185 CommandArgument{ "-R", CommandArgument::Values::One,
186 CommandArgument::setToValue(cpackProjectVersion) },
187 CommandArgument{ "-B", CommandArgument::Values::One,
188 CommandArgument::setToValue(cpackProjectDirectory) },
189 CommandArgument{ "--patch", CommandArgument::Values::One,
190 CommandArgument::setToValue(cpackProjectPatch) },
191 CommandArgument{ "--vendor", CommandArgument::Values::One,
192 CommandArgument::setToValue(cpackProjectVendor) },
193 CommandArgument{ "--preset", CommandArgument::Values::One,
194 CommandArgument::setToValue(preset) },
195 CommandArgument{ "--list-presets", CommandArgument::Values::Zero,
196 CommandArgument::setToTrue(listPresets) },
198 "-D", CommandArgument::Values::One,
199 [&log, &definitions](const std::string& arg, cmake*,
200 cmMakefile*) -> bool {
201 std::string value = arg;
202 size_t pos = value.find_first_of('=');
203 if (pos == std::string::npos) {
204 cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
205 "Please specify CPack definitions as: KEY=VALUE"
209 std::string key = value.substr(0, pos);
210 value.erase(0, pos + 1);
211 definitions[key] = value;
212 cmCPack_Log(&log, cmCPackLog::LOG_DEBUG,
213 "Set CPack variable: " << key << " to \"" << value << "\""
219 cmake cminst(cmake::RoleScript, cmState::CPack);
220 cminst.SetHomeDirectory("");
221 cminst.SetHomeOutputDirectory("");
222 cminst.SetProgressCallback(cpackProgressCallback);
223 cminst.GetCurrentSnapshot().SetDefaultDefinitions();
224 cmGlobalGenerator cmgg(&cminst);
225 cmMakefile globalMF(&cmgg, cminst.GetCurrentSnapshot());
226 #if defined(__CYGWIN__)
227 globalMF.AddDefinition("CMAKE_LEGACY_CYGWIN_WIN32", "0");
231 for (std::size_t i = 0; i < inputArgs.size(); i++) {
232 auto const& arg = inputArgs[i];
233 for (auto const& m : arguments) {
234 if (m.matches(arg)) {
235 if (!m.parse(arg, i, inputArgs, &cminst, &globalMF)) {
243 cmCPackGeneratorFactory generators;
244 generators.SetLogger(&log);
247 if (!preset.empty() || listPresets) {
248 const auto workingDirectory = cmSystemTools::GetCurrentWorkingDirectory();
250 auto const presetGeneratorsPresent =
251 [&generators](const cmCMakePresetsGraph::PackagePreset& p) {
252 return std::all_of(p.Generators.begin(), p.Generators.end(),
253 [&generators](const std::string& gen) {
254 return generators.GetGeneratorsList().count(
259 cmCMakePresetsGraph presetsGraph;
260 auto result = presetsGraph.ReadProjectPresets(workingDirectory);
261 if (result != cmCMakePresetsGraph::ReadFileResult::READ_OK) {
262 cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
263 "Could not read presets from "
264 << workingDirectory << ": "
265 << cmCMakePresetsGraph::ResultToString(result)
271 presetsGraph.PrintPackagePresetList(presetGeneratorsPresent);
275 auto presetPair = presetsGraph.PackagePresets.find(preset);
276 if (presetPair == presetsGraph.PackagePresets.end()) {
277 cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
278 "No such package preset in " << workingDirectory << ": \""
279 << preset << '"' << std::endl);
280 presetsGraph.PrintPackagePresetList(presetGeneratorsPresent);
284 if (presetPair->second.Unexpanded.Hidden) {
285 cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
286 "Cannot use hidden package preset in "
287 << workingDirectory << ": \"" << preset << '"'
289 presetsGraph.PrintPackagePresetList(presetGeneratorsPresent);
293 auto const& expandedPreset = presetPair->second.Expanded;
294 if (!expandedPreset) {
295 cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
296 "Could not evaluate package preset \""
297 << preset << "\": Invalid macro expansion" << std::endl);
298 presetsGraph.PrintPackagePresetList(presetGeneratorsPresent);
302 if (!expandedPreset->ConditionResult) {
303 cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
304 "Cannot use disabled package preset in "
305 << workingDirectory << ": \"" << preset << '"'
307 presetsGraph.PrintPackagePresetList(presetGeneratorsPresent);
311 if (!presetGeneratorsPresent(presetPair->second.Unexpanded)) {
312 cmCPack_Log(&log, cmCPackLog::LOG_ERROR, "Cannot use preset");
313 presetsGraph.PrintPackagePresetList(presetGeneratorsPresent);
317 auto configurePresetPair =
318 presetsGraph.ConfigurePresets.find(expandedPreset->ConfigurePreset);
319 if (configurePresetPair == presetsGraph.ConfigurePresets.end()) {
320 cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
321 "No such configure preset in "
322 << workingDirectory << ": \""
323 << expandedPreset->ConfigurePreset << '"' << std::endl);
324 presetsGraph.PrintConfigurePresetList();
328 if (configurePresetPair->second.Unexpanded.Hidden) {
329 cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
330 "Cannot use hidden configure preset in "
331 << workingDirectory << ": \""
332 << expandedPreset->ConfigurePreset << '"' << std::endl);
333 presetsGraph.PrintConfigurePresetList();
337 auto const& expandedConfigurePreset = configurePresetPair->second.Expanded;
338 if (!expandedConfigurePreset) {
339 cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
340 "Could not evaluate configure preset \""
341 << expandedPreset->ConfigurePreset
342 << "\": Invalid macro expansion" << std::endl);
346 cmSystemTools::ChangeDirectory(expandedConfigurePreset->BinaryDir);
348 auto presetEnvironment = expandedPreset->Environment;
349 for (auto const& var : presetEnvironment) {
351 cmSystemTools::PutEnv(cmStrCat(var.first, '=', *var.second));
355 if (!expandedPreset->ConfigFile.empty() && cpackConfigFile.empty()) {
356 cpackConfigFile = expandedPreset->ConfigFile;
359 if (!expandedPreset->Generators.empty() && generator.empty()) {
360 generator = cmJoin(expandedPreset->Generators, ";");
363 if (!expandedPreset->Configurations.empty() && cpackBuildConfig.empty()) {
364 cpackBuildConfig = cmJoin(expandedPreset->Configurations, ";");
367 definitions.insert(expandedPreset->Variables.begin(),
368 expandedPreset->Variables.end());
370 if (expandedPreset->DebugOutput == true) {
371 debugLambda("", &cminst, &globalMF);
374 if (expandedPreset->VerboseOutput == true) {
375 verboseLambda("", &cminst, &globalMF);
378 if (!expandedPreset->PackageName.empty() && cpackProjectName.empty()) {
379 cpackProjectName = expandedPreset->PackageName;
382 if (!expandedPreset->PackageVersion.empty() &&
383 cpackProjectVersion.empty()) {
384 cpackProjectVersion = expandedPreset->PackageVersion;
387 if (!expandedPreset->PackageDirectory.empty() &&
388 cpackProjectDirectory.empty()) {
389 cpackProjectDirectory = expandedPreset->PackageDirectory;
392 if (!expandedPreset->VendorName.empty() && cpackProjectVendor.empty()) {
393 cpackProjectVendor = expandedPreset->VendorName;
397 cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE,
398 "Read CPack config file: " << cpackConfigFile << std::endl);
400 bool cpackConfigFileSpecified = true;
401 if (cpackConfigFile.empty()) {
402 cpackConfigFile = cmStrCat(cmSystemTools::GetCurrentWorkingDirectory(),
403 "/CPackConfig.cmake");
404 cpackConfigFileSpecified = false;
408 doc.addCPackStandardDocSections();
409 /* Were we invoked to display doc or to do some work ?
410 * Unlike cmake launching cpack with zero argument
411 * should launch cpack using "cpackConfigFile" if it exists
412 * in the current directory.
414 help = doc.CheckOptions(argc, argv, "-G") && argc != 1;
416 // This part is used for cpack documentation lookup as well.
417 cminst.AddCMakePaths();
419 if (parsed && !help) {
420 // find out which system cpack is running on, so it can setup the search
421 // paths, so FIND_XXX() commands can be used in scripts
422 std::string systemFile =
423 globalMF.GetModulesFile("CMakeDetermineSystem.cmake");
424 if (!globalMF.ReadListFile(systemFile)) {
425 cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
426 "Error reading CMakeDetermineSystem.cmake" << std::endl);
431 globalMF.GetModulesFile("CMakeSystemSpecificInformation.cmake");
432 if (!globalMF.ReadListFile(systemFile)) {
433 cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
434 "Error reading CMakeSystemSpecificInformation.cmake"
439 if (!cpackBuildConfig.empty()) {
440 globalMF.AddDefinition("CPACK_BUILD_CONFIG", cpackBuildConfig);
443 if (cmSystemTools::FileExists(cpackConfigFile)) {
444 cpackConfigFile = cmSystemTools::CollapseFullPath(cpackConfigFile);
445 cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE,
446 "Read CPack configuration file: " << cpackConfigFile
448 if (!globalMF.ReadListFile(cpackConfigFile)) {
449 cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
450 "Problem reading CPack config file: \""
451 << cpackConfigFile << "\"" << std::endl);
454 } else if (cpackConfigFileSpecified) {
455 cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
456 "Cannot find CPack config file: \"" << cpackConfigFile
457 << "\"" << std::endl);
461 if (!generator.empty()) {
462 globalMF.AddDefinition("CPACK_GENERATOR", generator);
464 if (!cpackProjectName.empty()) {
465 globalMF.AddDefinition("CPACK_PACKAGE_NAME", cpackProjectName);
467 if (!cpackProjectVersion.empty()) {
468 globalMF.AddDefinition("CPACK_PACKAGE_VERSION", cpackProjectVersion);
470 if (!cpackProjectVendor.empty()) {
471 globalMF.AddDefinition("CPACK_PACKAGE_VENDOR", cpackProjectVendor);
473 // if this is not empty it has been set on the command line
474 // go for it. Command line override values set in config file.
475 if (!cpackProjectDirectory.empty()) {
476 globalMF.AddDefinition("CPACK_PACKAGE_DIRECTORY", cpackProjectDirectory);
478 // The value has not been set on the command line
480 // get a default value (current working directory)
481 cpackProjectDirectory = cmSystemTools::GetCurrentWorkingDirectory();
482 // use default value if no value has been provided by the config file
483 if (!globalMF.IsSet("CPACK_PACKAGE_DIRECTORY")) {
484 globalMF.AddDefinition("CPACK_PACKAGE_DIRECTORY",
485 cpackProjectDirectory);
488 for (auto const& cd : definitions) {
489 globalMF.AddDefinition(cd.first, cd.second);
492 // Force CPACK_PACKAGE_DIRECTORY as absolute path
493 cpackProjectDirectory =
494 globalMF.GetSafeDefinition("CPACK_PACKAGE_DIRECTORY");
495 cpackProjectDirectory =
496 cmSystemTools::CollapseFullPath(cpackProjectDirectory);
497 globalMF.AddDefinition("CPACK_PACKAGE_DIRECTORY", cpackProjectDirectory);
499 cmValue cpackModulesPath = globalMF.GetDefinition("CPACK_MODULE_PATH");
500 if (cpackModulesPath) {
501 globalMF.AddDefinition("CMAKE_MODULE_PATH", *cpackModulesPath);
503 cmValue genList = globalMF.GetDefinition("CPACK_GENERATOR");
505 cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
506 "CPack generator not specified" << std::endl);
508 std::vector<std::string> generatorsVector = cmExpandedList(*genList);
509 for (std::string const& gen : generatorsVector) {
510 cmMakefile::ScopePushPop raii(&globalMF);
511 cmMakefile* mf = &globalMF;
512 cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE,
513 "Specified generator: " << gen << std::endl);
514 if (!mf->GetDefinition("CPACK_PACKAGE_NAME")) {
515 cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
516 "CPack project name not specified" << std::endl);
520 !(mf->GetDefinition("CPACK_PACKAGE_VERSION") ||
521 (mf->GetDefinition("CPACK_PACKAGE_VERSION_MAJOR") &&
522 mf->GetDefinition("CPACK_PACKAGE_VERSION_MINOR") &&
523 mf->GetDefinition("CPACK_PACKAGE_VERSION_PATCH")))) {
524 cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
525 "CPack project version not specified"
527 << "Specify CPACK_PACKAGE_VERSION, or "
528 "CPACK_PACKAGE_VERSION_MAJOR, "
529 "CPACK_PACKAGE_VERSION_MINOR, and "
530 "CPACK_PACKAGE_VERSION_PATCH."
535 std::unique_ptr<cmCPackGenerator> cpackGenerator =
536 generators.NewGenerator(gen);
537 if (cpackGenerator) {
538 cpackGenerator->SetTrace(cminst.GetTrace());
539 cpackGenerator->SetTraceExpand(cminst.GetTraceExpand());
541 cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
542 "Could not create CPack generator: " << gen
544 // Print out all the valid generators
545 cmDocumentation generatorDocs;
546 std::vector<cmDocumentationEntry> v;
547 for (auto const& g : generators.GetGeneratorsList()) {
548 cmDocumentationEntry e;
551 v.push_back(std::move(e));
553 generatorDocs.SetSection("Generators", v);
555 generatorDocs.PrintDocumentation(cmDocumentation::ListGenerators,
560 if (parsed && !cpackGenerator->Initialize(gen, mf)) {
561 cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
562 "Cannot initialize the generator " << gen
567 if (!mf->GetDefinition("CPACK_INSTALL_COMMANDS") &&
568 !mf->GetDefinition("CPACK_INSTALL_SCRIPT") &&
569 !mf->GetDefinition("CPACK_INSTALLED_DIRECTORIES") &&
570 !mf->GetDefinition("CPACK_INSTALL_CMAKE_PROJECTS")) {
572 &log, cmCPackLog::LOG_ERROR,
573 "Please specify build tree of the project that uses CMake "
574 "using CPACK_INSTALL_CMAKE_PROJECTS, specify "
575 "CPACK_INSTALL_COMMANDS, CPACK_INSTALL_SCRIPT, or "
576 "CPACK_INSTALLED_DIRECTORIES."
581 cmValue projName = mf->GetDefinition("CPACK_PACKAGE_NAME");
582 cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE,
583 "Use generator: " << cpackGenerator->GetNameOfClass()
585 cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE,
586 "For project: " << *projName << std::endl);
588 cmValue projVersion = mf->GetDefinition("CPACK_PACKAGE_VERSION");
590 cmValue projVersionMajor =
591 mf->GetDefinition("CPACK_PACKAGE_VERSION_MAJOR");
592 cmValue projVersionMinor =
593 mf->GetDefinition("CPACK_PACKAGE_VERSION_MINOR");
594 cmValue projVersionPatch =
595 mf->GetDefinition("CPACK_PACKAGE_VERSION_PATCH");
596 std::ostringstream ostr;
597 ostr << *projVersionMajor << "." << *projVersionMinor << "."
598 << *projVersionPatch;
599 mf->AddDefinition("CPACK_PACKAGE_VERSION", ostr.str());
602 int res = cpackGenerator->DoPackage();
604 cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
605 "Error when generating package: " << *projName
615 /* In this case we are building the documentation object
616 * instance in order to create appropriate structure
617 * in order to satisfy the appropriate --help-xxx request
620 // Construct and print requested documentation.
622 doc.SetName("cpack");
623 doc.SetSection("Name", cmDocumentationName);
624 doc.SetSection("Usage", cmDocumentationUsage);
625 doc.PrependSection("Options", cmDocumentationOptions);
627 std::vector<cmDocumentationEntry> v;
628 for (auto const& g : generators.GetGeneratorsList()) {
629 cmDocumentationEntry e;
632 v.push_back(std::move(e));
634 doc.SetSection("Generators", v);
636 return doc.PrintRequestedDocumentation(std::cout) ? 0 : 1;
639 if (cmSystemTools::GetErrorOccurredFlag()) {