1650368438d184b7b03b0903223b9f0bfab4f271
[platform/upstream/cmake.git] / Source / CPack / cpack.cxx
1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3
4 #include <cstddef>
5 #include <functional>
6 #include <iostream>
7 #include <map>
8 #include <memory>
9 #include <sstream>
10 #include <string>
11 #include <utility>
12 #include <vector>
13
14 #include <cmext/algorithm>
15
16 #include "cmsys/Encoding.hxx"
17
18 #include "cmCPackGenerator.h"
19 #include "cmCPackGeneratorFactory.h"
20 #include "cmCPackLog.h"
21 #include "cmCommandLineArgument.h"
22 #include "cmConsoleBuf.h"
23 #include "cmDocumentation.h"
24 #include "cmDocumentationEntry.h"
25 #include "cmDocumentationFormatter.h"
26 #include "cmGlobalGenerator.h"
27 #include "cmMakefile.h"
28 #include "cmState.h"
29 #include "cmStateSnapshot.h"
30 #include "cmStringAlgorithms.h"
31 #include "cmSystemTools.h"
32 #include "cmValue.h"
33 #include "cmake.h"
34
35 namespace {
36 const char* cmDocumentationName[][2] = {
37   { nullptr, "  cpack - Packaging driver provided by CMake." },
38   { nullptr, nullptr }
39 };
40
41 const char* cmDocumentationUsage[][2] = {
42   // clang-format off
43   { nullptr, "  cpack [options]" },
44   { nullptr, nullptr }
45   // clang-format on
46 };
47
48 const char* cmDocumentationOptions[][2] = {
49   { "-G <generators>", "Override/define CPACK_GENERATOR" },
50   { "-C <Configuration>", "Specify the project configuration" },
51   { "-D <var>=<value>", "Set a CPack variable." },
52   { "--config <configFile>", "Specify the config file." },
53   { "--verbose,-V", "Enable verbose output" },
54   { "--trace", "Put underlying cmake scripts in trace mode." },
55   { "--trace-expand", "Put underlying cmake scripts in expanded trace mode." },
56   { "--debug", "Enable debug output (for CPack developers)" },
57   { "-P <packageName>", "Override/define CPACK_PACKAGE_NAME" },
58   { "-R <packageVersion>", "Override/define CPACK_PACKAGE_VERSION" },
59   { "-B <packageDirectory>", "Override/define CPACK_PACKAGE_DIRECTORY" },
60   { "--vendor <vendorName>", "Override/define CPACK_PACKAGE_VENDOR" },
61   { nullptr, nullptr }
62 };
63
64 void cpackProgressCallback(const std::string& message, float /*unused*/)
65 {
66   std::cout << "-- " << message << std::endl;
67 }
68 } // namespace
69
70 // this is CPack.
71 int main(int argc, char const* const* argv)
72 {
73   cmSystemTools::EnsureStdPipes();
74
75   // Replace streambuf so we can output Unicode to console
76   cmConsoleBuf consoleBuf;
77   consoleBuf.SetUTF8Pipes();
78
79   cmsys::Encoding::CommandLineArguments args =
80     cmsys::Encoding::CommandLineArguments::Main(argc, argv);
81   argc = args.argc();
82   argv = args.argv();
83
84   std::vector<std::string> inputArgs;
85   inputArgs.reserve(argc - 1);
86   cm::append(inputArgs, argv + 1, argv + argc);
87
88   cmSystemTools::InitializeLibUV();
89   cmSystemTools::FindCMakeResources(argv[0]);
90   cmCPackLog log;
91
92   log.SetErrorPrefix("CPack Error: ");
93   log.SetWarningPrefix("CPack Warning: ");
94   log.SetOutputPrefix("CPack: ");
95   log.SetVerbosePrefix("CPack Verbose: ");
96
97   if (cmSystemTools::GetCurrentWorkingDirectory().empty()) {
98     cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
99                 "Current working directory cannot be established."
100                   << std::endl);
101     return 1;
102   }
103
104   std::string generator;
105   bool help = false;
106   bool helpVersion = false;
107   std::string helpFull;
108   std::string helpMAN;
109   std::string helpHTML;
110
111   std::string cpackProjectName;
112   std::string cpackProjectDirectory;
113   std::string cpackBuildConfig;
114   std::string cpackProjectVersion;
115   std::string cpackProjectPatch;
116   std::string cpackProjectVendor;
117   std::string cpackConfigFile;
118
119   std::map<std::string, std::string> definitions;
120
121   auto const verboseLambda = [&log](const std::string&, cmake*,
122                                     cmMakefile*) -> bool {
123     log.SetVerbose(true);
124     cmCPack_Log(&log, cmCPackLog::LOG_OUTPUT, "Enable Verbose" << std::endl);
125     return true;
126   };
127
128   auto const debugLambda = [&log](const std::string&, cmake*,
129                                   cmMakefile*) -> bool {
130     log.SetDebug(true);
131     cmCPack_Log(&log, cmCPackLog::LOG_OUTPUT, "Enable Debug" << std::endl);
132     return true;
133   };
134
135   auto const traceLambda = [](const std::string&, cmake* state,
136                               cmMakefile*) -> bool {
137     state->SetTrace(true);
138     return true;
139   };
140
141   auto const traceExpandLambda = [](const std::string&, cmake* state,
142                                     cmMakefile*) -> bool {
143     state->SetTrace(true);
144     state->SetTraceExpand(true);
145     return true;
146   };
147
148   using CommandArgument =
149     cmCommandLineArgument<bool(std::string const&, cmake*, cmMakefile*)>;
150
151   std::vector<CommandArgument> arguments = {
152     CommandArgument{ "--help", CommandArgument::Values::Zero,
153                      CommandArgument::setToTrue(help) },
154     CommandArgument{ "--help-full", CommandArgument::Values::Zero,
155                      CommandArgument::setToValue(helpFull) },
156     CommandArgument{ "--help-html", CommandArgument::Values::Zero,
157                      CommandArgument::setToValue(helpHTML) },
158     CommandArgument{ "--help-man", CommandArgument::Values::Zero,
159                      CommandArgument::setToValue(helpMAN) },
160     CommandArgument{ "--version", CommandArgument::Values::Zero,
161                      CommandArgument::setToTrue(helpVersion) },
162     CommandArgument{ "-V", CommandArgument::Values::Zero, verboseLambda },
163     CommandArgument{ "--verbose", CommandArgument::Values::Zero,
164                      verboseLambda },
165     CommandArgument{ "--debug", CommandArgument::Values::Zero, debugLambda },
166     CommandArgument{ "--config", CommandArgument::Values::One,
167                      CommandArgument::setToValue(cpackConfigFile) },
168     CommandArgument{ "--trace", CommandArgument::Values::Zero, traceLambda },
169     CommandArgument{ "--trace-expand", CommandArgument::Values::Zero,
170                      traceExpandLambda },
171     CommandArgument{ "-C", CommandArgument::Values::One,
172                      CommandArgument::setToValue(cpackBuildConfig) },
173     CommandArgument{ "-G", CommandArgument::Values::One,
174                      CommandArgument::setToValue(generator) },
175     CommandArgument{ "-P", CommandArgument::Values::One,
176                      CommandArgument::setToValue(cpackProjectName) },
177     CommandArgument{ "-R", CommandArgument::Values::One,
178                      CommandArgument::setToValue(cpackProjectVersion) },
179     CommandArgument{ "-B", CommandArgument::Values::One,
180                      CommandArgument::setToValue(cpackProjectDirectory) },
181     CommandArgument{ "--patch", CommandArgument::Values::One,
182                      CommandArgument::setToValue(cpackProjectPatch) },
183     CommandArgument{ "--vendor", CommandArgument::Values::One,
184                      CommandArgument::setToValue(cpackProjectVendor) },
185     CommandArgument{
186       "-D", CommandArgument::Values::One,
187       [&log, &definitions](const std::string& arg, cmake*,
188                            cmMakefile*) -> bool {
189         std::string value = arg;
190         size_t pos = value.find_first_of('=');
191         if (pos == std::string::npos) {
192           cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
193                       "Please specify CPack definitions as: KEY=VALUE"
194                         << std::endl);
195           return false;
196         }
197         std::string key = value.substr(0, pos);
198         value.erase(0, pos + 1);
199         definitions[key] = value;
200         cmCPack_Log(&log, cmCPackLog::LOG_DEBUG,
201                     "Set CPack variable: " << key << " to \"" << value << "\""
202                                            << std::endl);
203         return true;
204       } },
205   };
206
207   cmake cminst(cmake::RoleScript, cmState::CPack);
208   cminst.SetHomeDirectory("");
209   cminst.SetHomeOutputDirectory("");
210   cminst.SetProgressCallback(cpackProgressCallback);
211   cminst.GetCurrentSnapshot().SetDefaultDefinitions();
212   cmGlobalGenerator cmgg(&cminst);
213   cmMakefile globalMF(&cmgg, cminst.GetCurrentSnapshot());
214 #if defined(__CYGWIN__)
215   globalMF.AddDefinition("CMAKE_LEGACY_CYGWIN_WIN32", "0");
216 #endif
217
218   bool parsed = true;
219   for (std::size_t i = 0; i < inputArgs.size(); i++) {
220     auto const& arg = inputArgs[i];
221     for (auto const& m : arguments) {
222       if (m.matches(arg)) {
223         if (!m.parse(arg, i, inputArgs, &cminst, &globalMF)) {
224           parsed = false;
225         }
226         break;
227       }
228     }
229   }
230
231   cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE,
232               "Read CPack config file: " << cpackConfigFile << std::endl);
233
234   bool cpackConfigFileSpecified = true;
235   if (cpackConfigFile.empty()) {
236     cpackConfigFile = cmStrCat(cmSystemTools::GetCurrentWorkingDirectory(),
237                                "/CPackConfig.cmake");
238     cpackConfigFileSpecified = false;
239   }
240
241   cmCPackGeneratorFactory generators;
242   generators.SetLogger(&log);
243
244   cmDocumentation doc;
245   doc.addCPackStandardDocSections();
246   /* Were we invoked to display doc or to do some work ?
247    * Unlike cmake launching cpack with zero argument
248    * should launch cpack using "cpackConfigFile" if it exists
249    * in the current directory.
250    */
251   help = doc.CheckOptions(argc, argv, "-G") && argc != 1;
252
253   // This part is used for cpack documentation lookup as well.
254   cminst.AddCMakePaths();
255
256   if (parsed && !help) {
257     // find out which system cpack is running on, so it can setup the search
258     // paths, so FIND_XXX() commands can be used in scripts
259     std::string systemFile =
260       globalMF.GetModulesFile("CMakeDetermineSystem.cmake");
261     if (!globalMF.ReadListFile(systemFile)) {
262       cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
263                   "Error reading CMakeDetermineSystem.cmake" << std::endl);
264       return 1;
265     }
266
267     systemFile =
268       globalMF.GetModulesFile("CMakeSystemSpecificInformation.cmake");
269     if (!globalMF.ReadListFile(systemFile)) {
270       cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
271                   "Error reading CMakeSystemSpecificInformation.cmake"
272                     << std::endl);
273       return 1;
274     }
275
276     if (!cpackBuildConfig.empty()) {
277       globalMF.AddDefinition("CPACK_BUILD_CONFIG", cpackBuildConfig);
278     }
279
280     if (cmSystemTools::FileExists(cpackConfigFile)) {
281       cpackConfigFile = cmSystemTools::CollapseFullPath(cpackConfigFile);
282       cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE,
283                   "Read CPack configuration file: " << cpackConfigFile
284                                                     << std::endl);
285       if (!globalMF.ReadListFile(cpackConfigFile)) {
286         cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
287                     "Problem reading CPack config file: \""
288                       << cpackConfigFile << "\"" << std::endl);
289         return 1;
290       }
291     } else if (cpackConfigFileSpecified) {
292       cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
293                   "Cannot find CPack config file: \"" << cpackConfigFile
294                                                       << "\"" << std::endl);
295       return 1;
296     }
297
298     if (!generator.empty()) {
299       globalMF.AddDefinition("CPACK_GENERATOR", generator);
300     }
301     if (!cpackProjectName.empty()) {
302       globalMF.AddDefinition("CPACK_PACKAGE_NAME", cpackProjectName);
303     }
304     if (!cpackProjectVersion.empty()) {
305       globalMF.AddDefinition("CPACK_PACKAGE_VERSION", cpackProjectVersion);
306     }
307     if (!cpackProjectVendor.empty()) {
308       globalMF.AddDefinition("CPACK_PACKAGE_VENDOR", cpackProjectVendor);
309     }
310     // if this is not empty it has been set on the command line
311     // go for it. Command line override values set in config file.
312     if (!cpackProjectDirectory.empty()) {
313       globalMF.AddDefinition("CPACK_PACKAGE_DIRECTORY", cpackProjectDirectory);
314     }
315     // The value has not been set on the command line
316     else {
317       // get a default value (current working directory)
318       cpackProjectDirectory = cmSystemTools::GetCurrentWorkingDirectory();
319       // use default value if no value has been provided by the config file
320       if (!globalMF.IsSet("CPACK_PACKAGE_DIRECTORY")) {
321         globalMF.AddDefinition("CPACK_PACKAGE_DIRECTORY",
322                                cpackProjectDirectory);
323       }
324     }
325     for (auto const& cd : definitions) {
326       globalMF.AddDefinition(cd.first, cd.second);
327     }
328
329     // Force CPACK_PACKAGE_DIRECTORY as absolute path
330     cpackProjectDirectory =
331       globalMF.GetSafeDefinition("CPACK_PACKAGE_DIRECTORY");
332     cpackProjectDirectory =
333       cmSystemTools::CollapseFullPath(cpackProjectDirectory);
334     globalMF.AddDefinition("CPACK_PACKAGE_DIRECTORY", cpackProjectDirectory);
335
336     cmValue cpackModulesPath = globalMF.GetDefinition("CPACK_MODULE_PATH");
337     if (cpackModulesPath) {
338       globalMF.AddDefinition("CMAKE_MODULE_PATH", *cpackModulesPath);
339     }
340     cmValue genList = globalMF.GetDefinition("CPACK_GENERATOR");
341     if (!genList) {
342       cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
343                   "CPack generator not specified" << std::endl);
344     } else {
345       std::vector<std::string> generatorsVector = cmExpandedList(*genList);
346       for (std::string const& gen : generatorsVector) {
347         cmMakefile::ScopePushPop raii(&globalMF);
348         cmMakefile* mf = &globalMF;
349         cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE,
350                     "Specified generator: " << gen << std::endl);
351         if (!mf->GetDefinition("CPACK_PACKAGE_NAME")) {
352           cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
353                       "CPack project name not specified" << std::endl);
354           parsed = false;
355         }
356         if (parsed &&
357             !(mf->GetDefinition("CPACK_PACKAGE_VERSION") ||
358               (mf->GetDefinition("CPACK_PACKAGE_VERSION_MAJOR") &&
359                mf->GetDefinition("CPACK_PACKAGE_VERSION_MINOR") &&
360                mf->GetDefinition("CPACK_PACKAGE_VERSION_PATCH")))) {
361           cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
362                       "CPack project version not specified"
363                         << std::endl
364                         << "Specify CPACK_PACKAGE_VERSION, or "
365                            "CPACK_PACKAGE_VERSION_MAJOR, "
366                            "CPACK_PACKAGE_VERSION_MINOR, and "
367                            "CPACK_PACKAGE_VERSION_PATCH."
368                         << std::endl);
369           parsed = false;
370         }
371         if (parsed) {
372           std::unique_ptr<cmCPackGenerator> cpackGenerator =
373             generators.NewGenerator(gen);
374           if (cpackGenerator) {
375             cpackGenerator->SetTrace(cminst.GetTrace());
376             cpackGenerator->SetTraceExpand(cminst.GetTraceExpand());
377           } else {
378             cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
379                         "Could not create CPack generator: " << gen
380                                                              << std::endl);
381             // Print out all the valid generators
382             cmDocumentation generatorDocs;
383             std::vector<cmDocumentationEntry> v;
384             for (auto const& g : generators.GetGeneratorsList()) {
385               cmDocumentationEntry e;
386               e.Name = g.first;
387               e.Brief = g.second;
388               v.push_back(std::move(e));
389             }
390             generatorDocs.SetSection("Generators", v);
391             std::cerr << "\n";
392             generatorDocs.PrintDocumentation(cmDocumentation::ListGenerators,
393                                              std::cerr);
394             parsed = false;
395           }
396
397           if (parsed && !cpackGenerator->Initialize(gen, mf)) {
398             cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
399                         "Cannot initialize the generator " << gen
400                                                            << std::endl);
401             parsed = false;
402           }
403
404           if (!mf->GetDefinition("CPACK_INSTALL_COMMANDS") &&
405               !mf->GetDefinition("CPACK_INSTALL_SCRIPT") &&
406               !mf->GetDefinition("CPACK_INSTALLED_DIRECTORIES") &&
407               !mf->GetDefinition("CPACK_INSTALL_CMAKE_PROJECTS")) {
408             cmCPack_Log(
409               &log, cmCPackLog::LOG_ERROR,
410               "Please specify build tree of the project that uses CMake "
411               "using CPACK_INSTALL_CMAKE_PROJECTS, specify "
412               "CPACK_INSTALL_COMMANDS, CPACK_INSTALL_SCRIPT, or "
413               "CPACK_INSTALLED_DIRECTORIES."
414                 << std::endl);
415             parsed = false;
416           }
417           if (parsed) {
418             cmValue projName = mf->GetDefinition("CPACK_PACKAGE_NAME");
419             cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE,
420                         "Use generator: " << cpackGenerator->GetNameOfClass()
421                                           << std::endl);
422             cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE,
423                         "For project: " << *projName << std::endl);
424
425             cmValue projVersion = mf->GetDefinition("CPACK_PACKAGE_VERSION");
426             if (!projVersion) {
427               cmValue projVersionMajor =
428                 mf->GetDefinition("CPACK_PACKAGE_VERSION_MAJOR");
429               cmValue projVersionMinor =
430                 mf->GetDefinition("CPACK_PACKAGE_VERSION_MINOR");
431               cmValue projVersionPatch =
432                 mf->GetDefinition("CPACK_PACKAGE_VERSION_PATCH");
433               std::ostringstream ostr;
434               ostr << *projVersionMajor << "." << *projVersionMinor << "."
435                    << *projVersionPatch;
436               mf->AddDefinition("CPACK_PACKAGE_VERSION", ostr.str());
437             }
438
439             int res = cpackGenerator->DoPackage();
440             if (!res) {
441               cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
442                           "Error when generating package: " << *projName
443                                                             << std::endl);
444               return 1;
445             }
446           }
447         }
448       }
449     }
450   }
451
452   /* In this case we are building the documentation object
453    * instance in order to create appropriate structure
454    * in order to satisfy the appropriate --help-xxx request
455    */
456   if (help) {
457     // Construct and print requested documentation.
458
459     doc.SetName("cpack");
460     doc.SetSection("Name", cmDocumentationName);
461     doc.SetSection("Usage", cmDocumentationUsage);
462     doc.PrependSection("Options", cmDocumentationOptions);
463
464     std::vector<cmDocumentationEntry> v;
465     for (auto const& g : generators.GetGeneratorsList()) {
466       cmDocumentationEntry e;
467       e.Name = g.first;
468       e.Brief = g.second;
469       v.push_back(std::move(e));
470     }
471     doc.SetSection("Generators", v);
472
473     return doc.PrintRequestedDocumentation(std::cout) ? 0 : 1;
474   }
475
476   if (cmSystemTools::GetErrorOccurredFlag()) {
477     return 1;
478   }
479
480   return 0;
481 }