f931e9dbcd802cae81dfdc2c74a0cdc6ba522e24
[platform/upstream/cmake.git] / Source / cmakemain.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 "cmConfigure.h" // IWYU pragma: keep
5
6 #include <algorithm>
7 #include <cassert>
8 #include <cctype>
9 #include <climits>
10 #include <cstdio>
11 #include <cstring>
12 #include <functional>
13 #include <iostream>
14 #include <sstream>
15 #include <string>
16 #include <utility>
17 #include <vector>
18
19 #include <cm/memory>
20 #include <cmext/algorithm>
21
22 #include <cm3p/uv.h>
23
24 #include "cmBuildOptions.h"
25 #include "cmCommandLineArgument.h"
26 #include "cmConsoleBuf.h"
27 #include "cmDocumentationEntry.h" // IWYU pragma: keep
28 #include "cmGlobalGenerator.h"
29 #include "cmMakefile.h"
30 #include "cmMessageMetadata.h"
31 #include "cmState.h"
32 #include "cmStateTypes.h"
33 #include "cmStringAlgorithms.h"
34 #include "cmSystemTools.h"
35 #include "cmValue.h"
36 #include "cmake.h"
37 #include "cmcmd.h"
38
39 #ifndef CMAKE_BOOTSTRAP
40 #  include "cmDocumentation.h"
41 #  include "cmDynamicLoader.h"
42 #endif
43
44 #include "cmsys/Encoding.hxx"
45 #include "cmsys/Terminal.h"
46
47 namespace {
48 #ifndef CMAKE_BOOTSTRAP
49 const char* cmDocumentationName[][2] = {
50   { nullptr, "  cmake - Cross-Platform Makefile Generator." },
51   { nullptr, nullptr }
52 };
53
54 const char* cmDocumentationUsage[][2] = {
55   { nullptr,
56     "  cmake [options] <path-to-source>\n"
57     "  cmake [options] <path-to-existing-build>\n"
58     "  cmake [options] -S <path-to-source> -B <path-to-build>" },
59   { nullptr,
60     "Specify a source directory to (re-)generate a build system for "
61     "it in the current working directory.  Specify an existing build "
62     "directory to re-generate its build system." },
63   { nullptr, nullptr }
64 };
65
66 const char* cmDocumentationUsageNote[][2] = {
67   { nullptr, "Run 'cmake --help' for more information." },
68   { nullptr, nullptr }
69 };
70
71 const char* cmDocumentationOptions[][2] = {
72   CMAKE_STANDARD_OPTIONS_TABLE,
73   { "--preset <preset>,--preset=<preset>", "Specify a configure preset." },
74   { "--list-presets", "List available presets." },
75   { "-E", "CMake command mode." },
76   { "-L[A][H]", "List non-advanced cached variables." },
77   { "--fresh",
78     "Configure a fresh build tree, removing any existing cache file." },
79   { "--build <dir>", "Build a CMake-generated project binary tree." },
80   { "--install <dir>", "Install a CMake-generated project binary tree." },
81   { "--open <dir>", "Open generated project in the associated application." },
82   { "-N", "View mode only." },
83   { "-P <file>", "Process script mode." },
84   { "--find-package", "Legacy pkg-config like mode.  Do not use." },
85   { "--graphviz=[file]",
86     "Generate graphviz of dependencies, see "
87     "CMakeGraphVizOptions.cmake for more." },
88   { "--system-information [file]", "Dump information about this system." },
89   { "--log-level=<ERROR|WARNING|NOTICE|STATUS|VERBOSE|DEBUG|TRACE>",
90     "Set the verbosity of messages from CMake files. "
91     "--loglevel is also accepted for backward compatibility reasons." },
92   { "--log-context", "Prepend log messages with context, if given" },
93   { "--debug-trycompile",
94     "Do not delete the try_compile build tree. Only "
95     "useful on one try_compile at a time." },
96   { "--debug-output", "Put cmake in a debug mode." },
97   { "--debug-find", "Put cmake find in a debug mode." },
98   { "--debug-find-pkg=<pkg-name>[,...]",
99     "Limit cmake debug-find to the comma-separated list of packages" },
100   { "--debug-find-var=<var-name>[,...]",
101     "Limit cmake debug-find to the comma-separated list of result variables" },
102   { "--trace", "Put cmake in trace mode." },
103   { "--trace-expand", "Put cmake in trace mode with variable expansion." },
104   { "--trace-format=<human|json-v1>", "Set the output format of the trace." },
105   { "--trace-source=<file>",
106     "Trace only this CMake file/module. Multiple options allowed." },
107   { "--trace-redirect=<file>",
108     "Redirect trace output to a file instead of stderr." },
109   { "--warn-uninitialized", "Warn about uninitialized values." },
110   { "--no-warn-unused-cli", "Don't warn about command line options." },
111   { "--check-system-vars",
112     "Find problems with variable usage in system "
113     "files." },
114   { "--compile-no-warning-as-error",
115     "Ignore COMPILE_WARNING_AS_ERROR property and "
116     "CMAKE_COMPILE_WARNING_AS_ERROR variable." },
117 #  if !defined(CMAKE_BOOTSTRAP)
118   { "--profiling-format=<fmt>",
119     "Output data for profiling CMake scripts. Supported formats: "
120     "google-trace" },
121   { "--profiling-output=<file>",
122     "Select an output path for the profiling data enabled through "
123     "--profiling-format." },
124 #  endif
125   { nullptr, nullptr }
126 };
127
128 #endif
129
130 int do_command(int ac, char const* const* av,
131                std::unique_ptr<cmConsoleBuf> consoleBuf)
132 {
133   std::vector<std::string> args;
134   args.reserve(ac - 1);
135   args.emplace_back(av[0]);
136   cm::append(args, av + 2, av + ac);
137   return cmcmd::ExecuteCMakeCommand(args, std::move(consoleBuf));
138 }
139
140 cmMakefile* cmakemainGetMakefile(cmake* cm)
141 {
142   if (cm && cm->GetDebugOutput()) {
143     cmGlobalGenerator* gg = cm->GetGlobalGenerator();
144     if (gg) {
145       return gg->GetCurrentMakefile();
146     }
147   }
148   return nullptr;
149 }
150
151 std::string cmakemainGetStack(cmake* cm)
152 {
153   std::string msg;
154   cmMakefile* mf = cmakemainGetMakefile(cm);
155   if (mf) {
156     msg = mf->FormatListFileStack();
157     if (!msg.empty()) {
158       msg = "\n   Called from: " + msg;
159     }
160   }
161
162   return msg;
163 }
164
165 void cmakemainMessageCallback(const std::string& m,
166                               const cmMessageMetadata& md, cmake* cm)
167 {
168 #if defined(_WIN32)
169   // FIXME: On Windows we replace cerr's streambuf with a custom
170   // implementation that converts our internal UTF-8 encoding to the
171   // console's encoding.  It also does *not* replace LF with CRLF.
172   // Since stderr does not convert encoding and does convert LF, we
173   // cannot use it to print messages.  Another implementation will
174   // be needed to print colored messages on Windows.
175   static_cast<void>(md);
176   std::cerr << m << cmakemainGetStack(cm) << '\n' << std::flush;
177 #else
178   cmsysTerminal_cfprintf(md.desiredColor, stderr, "%s", m.c_str());
179   fflush(stderr); // stderr is buffered in some cases.
180   std::cerr << cmakemainGetStack(cm) << '\n' << std::flush;
181 #endif
182 }
183
184 void cmakemainProgressCallback(const std::string& m, float prog, cmake* cm)
185 {
186   cmMakefile* mf = cmakemainGetMakefile(cm);
187   std::string dir;
188   if (mf && cmHasLiteralPrefix(m, "Configuring") && (prog < 0)) {
189     dir = cmStrCat(' ', mf->GetCurrentSourceDirectory());
190   } else if (mf && cmHasLiteralPrefix(m, "Generating")) {
191     dir = cmStrCat(' ', mf->GetCurrentBinaryDirectory());
192   }
193
194   if ((prog < 0) || (!dir.empty())) {
195     std::cout << "-- " << m << dir << cmakemainGetStack(cm) << std::endl;
196   }
197 }
198
199 int do_cmake(int ac, char const* const* av)
200 {
201   if (cmSystemTools::GetCurrentWorkingDirectory().empty()) {
202     std::cerr << "Current working directory cannot be established."
203               << std::endl;
204     return 1;
205   }
206
207 #ifndef CMAKE_BOOTSTRAP
208   cmDocumentation doc;
209   doc.addCMakeStandardDocSections();
210   if (doc.CheckOptions(ac, av, "--")) {
211     // Construct and print requested documentation.
212     cmake hcm(cmake::RoleInternal, cmState::Unknown);
213     hcm.SetHomeDirectory("");
214     hcm.SetHomeOutputDirectory("");
215     hcm.AddCMakePaths();
216
217     // the command line args are processed here so that you can do
218     // -DCMAKE_MODULE_PATH=/some/path and have this value accessible here
219     std::vector<std::string> args(av, av + ac);
220     hcm.SetCacheArgs(args);
221
222     auto generators = hcm.GetGeneratorsDocumentation();
223
224     doc.SetName("cmake");
225     doc.SetSection("Name", cmDocumentationName);
226     doc.SetSection("Usage", cmDocumentationUsage);
227     if (ac == 1) {
228       doc.AppendSection("Usage", cmDocumentationUsageNote);
229     }
230     doc.AppendSection("Generators", generators);
231     doc.PrependSection("Options", cmDocumentationOptions);
232
233     return doc.PrintRequestedDocumentation(std::cout) ? 0 : 1;
234   }
235 #else
236   if (ac == 1) {
237     std::cout
238       << "Bootstrap CMake should not be used outside CMake build process."
239       << std::endl;
240     return 0;
241   }
242 #endif
243
244   bool wizard_mode = false;
245   bool sysinfo = false;
246   bool list_cached = false;
247   bool list_all_cached = false;
248   bool list_help = false;
249   bool view_only = false;
250   cmake::WorkingMode workingMode = cmake::NORMAL_MODE;
251   std::vector<std::string> parsedArgs;
252
253   using CommandArgument =
254     cmCommandLineArgument<bool(std::string const& value)>;
255   std::vector<CommandArgument> arguments = {
256     CommandArgument{
257       "-i", CommandArgument::Values::Zero,
258       [&wizard_mode](std::string const&) -> bool {
259         /* clang-format off */
260         std::cerr <<
261           "The \"cmake -i\" wizard mode is no longer supported.\n"
262           "Use the -D option to set cache values on the command line.\n"
263           "Use cmake-gui or ccmake for an interactive dialog.\n";
264         /* clang-format on */
265         wizard_mode = true;
266         return true;
267       } },
268     CommandArgument{ "--system-information", CommandArgument::Values::Zero,
269                      CommandArgument::setToTrue(sysinfo) },
270     CommandArgument{ "-N", CommandArgument::Values::Zero,
271                      CommandArgument::setToTrue(view_only) },
272     CommandArgument{ "-LAH", CommandArgument::Values::Zero,
273                      CommandArgument::setToTrue(list_all_cached, list_help) },
274     CommandArgument{ "-LA", CommandArgument::Values::Zero,
275                      CommandArgument::setToTrue(list_all_cached) },
276     CommandArgument{ "-LH", CommandArgument::Values::Zero,
277                      CommandArgument::setToTrue(list_cached, list_help) },
278     CommandArgument{ "-L", CommandArgument::Values::Zero,
279                      CommandArgument::setToTrue(list_cached) },
280     CommandArgument{ "-P", "No script specified for argument -P",
281                      CommandArgument::Values::One,
282                      CommandArgument::RequiresSeparator::No,
283                      [&](std::string const& value) -> bool {
284                        workingMode = cmake::SCRIPT_MODE;
285                        parsedArgs.emplace_back("-P");
286                        parsedArgs.push_back(value);
287                        return true;
288                      } },
289     CommandArgument{ "--find-package", CommandArgument::Values::Zero,
290                      [&](std::string const&) -> bool {
291                        workingMode = cmake::FIND_PACKAGE_MODE;
292                        parsedArgs.emplace_back("--find-package");
293                        return true;
294                      } },
295     CommandArgument{ "--list-presets", CommandArgument::Values::ZeroOrOne,
296                      [&](std::string const& value) -> bool {
297                        workingMode = cmake::HELP_MODE;
298                        parsedArgs.emplace_back("--list-presets");
299                        parsedArgs.emplace_back(value);
300                        return true;
301                      } },
302   };
303
304   std::vector<std::string> inputArgs;
305   inputArgs.reserve(ac);
306   cm::append(inputArgs, av, av + ac);
307
308   for (decltype(inputArgs.size()) i = 0; i < inputArgs.size(); ++i) {
309     std::string const& arg = inputArgs[i];
310     bool matched = false;
311     for (auto const& m : arguments) {
312       if (m.matches(arg)) {
313         matched = true;
314         if (m.parse(arg, i, inputArgs)) {
315           break;
316         }
317         return 1; // failed to parse
318       }
319     }
320     if (!matched) {
321       parsedArgs.emplace_back(av[i]);
322     }
323   }
324
325   if (wizard_mode) {
326     return 1;
327   }
328
329   if (sysinfo) {
330     cmake cm(cmake::RoleProject, cmState::Project);
331     cm.SetHomeDirectory("");
332     cm.SetHomeOutputDirectory("");
333     int ret = cm.GetSystemInformation(parsedArgs);
334     return ret;
335   }
336   cmake::Role const role =
337     workingMode == cmake::SCRIPT_MODE ? cmake::RoleScript : cmake::RoleProject;
338   cmState::Mode mode = cmState::Unknown;
339   switch (workingMode) {
340     case cmake::NORMAL_MODE:
341     case cmake::HELP_MODE:
342       mode = cmState::Project;
343       break;
344     case cmake::SCRIPT_MODE:
345       mode = cmState::Script;
346       break;
347     case cmake::FIND_PACKAGE_MODE:
348       mode = cmState::FindPackage;
349       break;
350   }
351   cmake cm(role, mode);
352   cm.SetHomeDirectory("");
353   cm.SetHomeOutputDirectory("");
354   cmSystemTools::SetMessageCallback(
355     [&cm](const std::string& msg, const cmMessageMetadata& md) {
356       cmakemainMessageCallback(msg, md, &cm);
357     });
358   cm.SetProgressCallback([&cm](const std::string& msg, float prog) {
359     cmakemainProgressCallback(msg, prog, &cm);
360   });
361   cm.SetWorkingMode(workingMode);
362
363   int res = cm.Run(parsedArgs, view_only);
364   if (list_cached || list_all_cached) {
365     std::cout << "-- Cache values" << std::endl;
366     std::vector<std::string> keys = cm.GetState()->GetCacheEntryKeys();
367     for (std::string const& k : keys) {
368       cmStateEnums::CacheEntryType t = cm.GetState()->GetCacheEntryType(k);
369       if (t != cmStateEnums::INTERNAL && t != cmStateEnums::STATIC &&
370           t != cmStateEnums::UNINITIALIZED) {
371         cmValue advancedProp =
372           cm.GetState()->GetCacheEntryProperty(k, "ADVANCED");
373         if (list_all_cached || !advancedProp) {
374           if (list_help) {
375             cmValue help =
376               cm.GetState()->GetCacheEntryProperty(k, "HELPSTRING");
377             std::cout << "// " << (help ? *help : "") << std::endl;
378           }
379           std::cout << k << ":" << cmState::CacheEntryTypeToString(t) << "="
380                     << cm.GetState()->GetSafeCacheEntryValue(k) << std::endl;
381           if (list_help) {
382             std::cout << std::endl;
383           }
384         }
385       }
386     }
387   }
388
389   // Always return a non-negative value.  Windows tools do not always
390   // interpret negative return values as errors.
391   if (res != 0) {
392     return 1;
393   }
394   return 0;
395 }
396
397 #ifndef CMAKE_BOOTSTRAP
398 int extract_job_number(std::string const& command,
399                        std::string const& jobString)
400 {
401   int jobs = -1;
402   unsigned long numJobs = 0;
403   if (jobString.empty()) {
404     jobs = cmake::DEFAULT_BUILD_PARALLEL_LEVEL;
405   } else if (cmStrToULong(jobString, &numJobs)) {
406     if (numJobs == 0) {
407       std::cerr
408         << "The <jobs> value requires a positive integer argument.\n\n";
409     } else if (numJobs > INT_MAX) {
410       std::cerr << "The <jobs> value is too large.\n\n";
411     } else {
412       jobs = static_cast<int>(numJobs);
413     }
414   } else {
415     std::cerr << "'" << command << "' invalid number '" << jobString
416               << "' given.\n\n";
417   }
418   return jobs;
419 }
420 #endif
421
422 int do_build(int ac, char const* const* av)
423 {
424 #ifdef CMAKE_BOOTSTRAP
425   std::cerr << "This cmake does not support --build\n";
426   return -1;
427 #else
428   int jobs = cmake::NO_BUILD_PARALLEL_LEVEL;
429   std::vector<std::string> targets;
430   std::string config;
431   std::string dir;
432   std::vector<std::string> nativeOptions;
433   bool nativeOptionsPassed = false;
434   bool cleanFirst = false;
435   bool foundClean = false;
436   bool foundNonClean = false;
437   PackageResolveMode resolveMode = PackageResolveMode::Default;
438   bool verbose = cmSystemTools::HasEnv("VERBOSE");
439   std::string presetName;
440   bool listPresets = false;
441
442   auto jLambda = [&](std::string const& value) -> bool {
443     jobs = extract_job_number("-j", value);
444     if (jobs < 0) {
445       dir.clear();
446     }
447     return true;
448   };
449   auto parallelLambda = [&](std::string const& value) -> bool {
450     jobs = extract_job_number("--parallel", value);
451     if (jobs < 0) {
452       dir.clear();
453     }
454     return true;
455   };
456   auto targetLambda = [&](std::string const& value) -> bool {
457     if (!value.empty()) {
458       std::vector<std::string> values = cmExpandedList(value);
459       for (auto const& v : values) {
460         targets.emplace_back(v);
461         if (v == "clean") {
462           foundClean = true;
463         } else {
464           foundNonClean = true;
465         }
466       }
467       return true;
468     }
469     return false;
470   };
471   auto resolvePackagesLambda = [&](std::string const& value) -> bool {
472     std::string v = value;
473     std::transform(v.begin(), v.end(), v.begin(), ::tolower);
474
475     if (v == "on") {
476       resolveMode = PackageResolveMode::Force;
477     } else if (v == "only") {
478       resolveMode = PackageResolveMode::OnlyResolve;
479     } else if (v == "off") {
480       resolveMode = PackageResolveMode::Disable;
481     } else {
482       return false;
483     }
484
485     return true;
486   };
487   auto verboseLambda = [&](std::string const&) -> bool {
488     verbose = true;
489     return true;
490   };
491
492   using CommandArgument =
493     cmCommandLineArgument<bool(std::string const& value)>;
494
495   std::vector<CommandArgument> arguments = {
496     CommandArgument{ "--preset", CommandArgument::Values::One,
497                      CommandArgument::setToValue(presetName) },
498     CommandArgument{ "--list-presets", CommandArgument::Values::Zero,
499                      CommandArgument::setToTrue(listPresets) },
500     CommandArgument{ "-j", CommandArgument::Values::ZeroOrOne,
501                      CommandArgument::RequiresSeparator::No, jLambda },
502     CommandArgument{ "--parallel", CommandArgument::Values::ZeroOrOne,
503                      CommandArgument::RequiresSeparator::No, parallelLambda },
504     CommandArgument{ "-t", CommandArgument::Values::OneOrMore, targetLambda },
505     CommandArgument{ "--target", CommandArgument::Values::OneOrMore,
506                      targetLambda },
507     CommandArgument{ "--config", CommandArgument::Values::One,
508                      CommandArgument::setToValue(config) },
509     CommandArgument{ "--clean-first", CommandArgument::Values::Zero,
510                      CommandArgument::setToTrue(cleanFirst) },
511     CommandArgument{ "--resolve-package-references",
512                      CommandArgument::Values::One, resolvePackagesLambda },
513     CommandArgument{ "-v", CommandArgument::Values::Zero, verboseLambda },
514     CommandArgument{ "--verbose", CommandArgument::Values::Zero,
515                      verboseLambda },
516     /* legacy option no-op*/
517     CommandArgument{ "--use-stderr", CommandArgument::Values::Zero,
518                      [](std::string const&) -> bool { return true; } },
519     CommandArgument{ "--", CommandArgument::Values::Zero,
520                      CommandArgument::setToTrue(nativeOptionsPassed) },
521   };
522
523   if (ac >= 3) {
524     std::vector<std::string> inputArgs;
525
526     bool hasPreset = false;
527     for (int i = 2; i < ac; ++i) {
528       if (strcmp(av[i], "--list-presets") == 0 ||
529           cmHasLiteralPrefix(av[i], "--preset=") ||
530           strcmp(av[i], "--preset") == 0) {
531         hasPreset = true;
532         break;
533       }
534     }
535
536     if (hasPreset) {
537       inputArgs.reserve(ac - 2);
538       cm::append(inputArgs, av + 2, av + ac);
539     } else {
540       dir = cmSystemTools::CollapseFullPath(av[2]);
541
542       inputArgs.reserve(ac - 3);
543       cm::append(inputArgs, av + 3, av + ac);
544     }
545
546     decltype(inputArgs.size()) i = 0;
547     for (; i < inputArgs.size() && !nativeOptionsPassed; ++i) {
548
549       std::string const& arg = inputArgs[i];
550       bool matched = false;
551       bool parsed = false;
552       for (auto const& m : arguments) {
553         matched = m.matches(arg);
554         if (matched) {
555           parsed = m.parse(arg, i, inputArgs);
556           break;
557         }
558       }
559       if (!(matched && parsed)) {
560         dir.clear();
561         if (!matched) {
562           std::cerr << "Unknown argument " << arg << std::endl;
563         }
564         break;
565       }
566     }
567
568     if (nativeOptionsPassed) {
569       cm::append(nativeOptions, inputArgs.begin() + i, inputArgs.end());
570     }
571   }
572
573   if (foundClean && foundNonClean) {
574     std::cerr << "Error: Building 'clean' and other targets together "
575                  "is not supported."
576               << std::endl;
577     dir.clear();
578   }
579
580   if (jobs == cmake::NO_BUILD_PARALLEL_LEVEL) {
581     std::string parallel;
582     if (cmSystemTools::GetEnv("CMAKE_BUILD_PARALLEL_LEVEL", parallel)) {
583       if (parallel.empty()) {
584         jobs = cmake::DEFAULT_BUILD_PARALLEL_LEVEL;
585       } else {
586         unsigned long numJobs = 0;
587         if (cmStrToULong(parallel, &numJobs)) {
588           if (numJobs == 0) {
589             std::cerr << "The CMAKE_BUILD_PARALLEL_LEVEL environment variable "
590                          "requires a positive integer argument.\n\n";
591             dir.clear();
592           } else if (numJobs > INT_MAX) {
593             std::cerr << "The CMAKE_BUILD_PARALLEL_LEVEL environment variable "
594                          "is too large.\n\n";
595             dir.clear();
596           } else {
597             jobs = static_cast<int>(numJobs);
598           }
599         } else {
600           std::cerr << "'CMAKE_BUILD_PARALLEL_LEVEL' environment variable\n"
601                     << "invalid number '" << parallel << "' given.\n\n";
602           dir.clear();
603         }
604       }
605     }
606   }
607
608   if (dir.empty() && presetName.empty() && !listPresets) {
609     /* clang-format off */
610     std::cerr <<
611       "Usage: cmake --build <dir>            "
612       " [options] [-- [native-options]]\n"
613       "       cmake --build --preset <preset>"
614       " [options] [-- [native-options]]\n"
615       "Options:\n"
616       "  <dir>          = Project binary directory to be built.\n"
617       "  --preset <preset>, --preset=<preset>\n"
618       "                 = Specify a build preset.\n"
619       "  --list-presets\n"
620       "                 = List available build presets.\n"
621       "  --parallel [<jobs>], -j [<jobs>]\n"
622       "                 = Build in parallel using the given number of jobs. \n"
623       "                   If <jobs> is omitted the native build tool's \n"
624       "                   default number is used.\n"
625       "                   The CMAKE_BUILD_PARALLEL_LEVEL environment "
626       "variable\n"
627       "                   specifies a default parallel level when this "
628       "option\n"
629       "                   is not given.\n"
630       "  --target <tgt>..., -t <tgt>... \n"
631       "                 = Build <tgt> instead of default targets.\n"
632       "  --config <cfg> = For multi-configuration tools, choose <cfg>.\n"
633       "  --clean-first  = Build target 'clean' first, then build.\n"
634       "                   (To clean only, use --target 'clean'.)\n"
635       "  --resolve-package-references={on|only|off}\n"
636       "                 = Restore/resolve package references during build.\n"
637       "  --verbose, -v  = Enable verbose output - if supported - including\n"
638       "                   the build commands to be executed. \n"
639       "  --             = Pass remaining options to the native tool.\n"
640       ;
641     /* clang-format on */
642     return 1;
643   }
644
645   cmake cm(cmake::RoleInternal, cmState::Project);
646   cmSystemTools::SetMessageCallback(
647     [&cm](const std::string& msg, const cmMessageMetadata& md) {
648       cmakemainMessageCallback(msg, md, &cm);
649     });
650   cm.SetProgressCallback([&cm](const std::string& msg, float prog) {
651     cmakemainProgressCallback(msg, prog, &cm);
652   });
653
654   cmBuildOptions buildOptions(cleanFirst, false, resolveMode);
655
656   return cm.Build(jobs, std::move(dir), std::move(targets), std::move(config),
657                   std::move(nativeOptions), buildOptions, verbose, presetName,
658                   listPresets);
659 #endif
660 }
661
662 bool parse_default_directory_permissions(const std::string& permissions,
663                                          std::string& parsedPermissionsVar)
664 {
665   std::vector<std::string> parsedPermissions;
666   enum Doing
667   {
668     DoingNone,
669     DoingOwner,
670     DoingGroup,
671     DoingWorld,
672     DoingOwnerAssignment,
673     DoingGroupAssignment,
674     DoingWorldAssignment,
675   };
676   Doing doing = DoingNone;
677
678   auto uniquePushBack = [&parsedPermissions](const std::string& e) {
679     if (std::find(parsedPermissions.begin(), parsedPermissions.end(), e) ==
680         parsedPermissions.end()) {
681       parsedPermissions.push_back(e);
682     }
683   };
684
685   for (auto const& e : permissions) {
686     switch (doing) {
687       case DoingNone:
688         if (e == 'u') {
689           doing = DoingOwner;
690         } else if (e == 'g') {
691           doing = DoingGroup;
692         } else if (e == 'o') {
693           doing = DoingWorld;
694         } else {
695           return false;
696         }
697         break;
698       case DoingOwner:
699         if (e == '=') {
700           doing = DoingOwnerAssignment;
701         } else {
702           return false;
703         }
704         break;
705       case DoingGroup:
706         if (e == '=') {
707           doing = DoingGroupAssignment;
708         } else {
709           return false;
710         }
711         break;
712       case DoingWorld:
713         if (e == '=') {
714           doing = DoingWorldAssignment;
715         } else {
716           return false;
717         }
718         break;
719       case DoingOwnerAssignment:
720         if (e == 'r') {
721           uniquePushBack("OWNER_READ");
722         } else if (e == 'w') {
723           uniquePushBack("OWNER_WRITE");
724         } else if (e == 'x') {
725           uniquePushBack("OWNER_EXECUTE");
726         } else if (e == ',') {
727           doing = DoingNone;
728         } else {
729           return false;
730         }
731         break;
732       case DoingGroupAssignment:
733         if (e == 'r') {
734           uniquePushBack("GROUP_READ");
735         } else if (e == 'w') {
736           uniquePushBack("GROUP_WRITE");
737         } else if (e == 'x') {
738           uniquePushBack("GROUP_EXECUTE");
739         } else if (e == ',') {
740           doing = DoingNone;
741         } else {
742           return false;
743         }
744         break;
745       case DoingWorldAssignment:
746         if (e == 'r') {
747           uniquePushBack("WORLD_READ");
748         } else if (e == 'w') {
749           uniquePushBack("WORLD_WRITE");
750         } else if (e == 'x') {
751           uniquePushBack("WORLD_EXECUTE");
752         } else if (e == ',') {
753           doing = DoingNone;
754         } else {
755           return false;
756         }
757         break;
758     }
759   }
760   if (doing != DoingOwnerAssignment && doing != DoingGroupAssignment &&
761       doing != DoingWorldAssignment) {
762     return false;
763   }
764
765   std::ostringstream oss;
766   for (auto i = 0u; i < parsedPermissions.size(); i++) {
767     if (i != 0) {
768       oss << ";";
769     }
770     oss << parsedPermissions[i];
771   }
772
773   parsedPermissionsVar = oss.str();
774   return true;
775 }
776
777 int do_install(int ac, char const* const* av)
778 {
779 #ifdef CMAKE_BOOTSTRAP
780   std::cerr << "This cmake does not support --install\n";
781   return -1;
782 #else
783   assert(1 < ac);
784
785   std::string config;
786   std::string component;
787   std::string defaultDirectoryPermissions;
788   std::string prefix;
789   std::string dir;
790   bool strip = false;
791   bool verbose = cmSystemTools::HasEnv("VERBOSE");
792
793   auto verboseLambda = [&](std::string const&) -> bool {
794     verbose = true;
795     return true;
796   };
797
798   using CommandArgument =
799     cmCommandLineArgument<bool(std::string const& value)>;
800
801   std::vector<CommandArgument> arguments = {
802     CommandArgument{ "--config", CommandArgument::Values::One,
803                      CommandArgument::setToValue(config) },
804     CommandArgument{ "--component", CommandArgument::Values::One,
805                      CommandArgument::setToValue(component) },
806     CommandArgument{
807       "--default-directory-permissions", CommandArgument::Values::One,
808       CommandArgument::setToValue(defaultDirectoryPermissions) },
809     CommandArgument{ "--prefix", CommandArgument::Values::One,
810                      CommandArgument::setToValue(prefix) },
811     CommandArgument{ "--strip", CommandArgument::Values::Zero,
812                      CommandArgument::setToTrue(strip) },
813     CommandArgument{ "-v", CommandArgument::Values::Zero, verboseLambda },
814     CommandArgument{ "--verbose", CommandArgument::Values::Zero,
815                      verboseLambda }
816   };
817
818   if (ac >= 3) {
819     dir = cmSystemTools::CollapseFullPath(av[2]);
820
821     std::vector<std::string> inputArgs;
822     inputArgs.reserve(ac - 3);
823     cm::append(inputArgs, av + 3, av + ac);
824     for (decltype(inputArgs.size()) i = 0; i < inputArgs.size(); ++i) {
825
826       std::string const& arg = inputArgs[i];
827       bool matched = false;
828       bool parsed = false;
829       for (auto const& m : arguments) {
830         matched = m.matches(arg);
831         if (matched) {
832           parsed = m.parse(arg, i, inputArgs);
833           break;
834         }
835       }
836       if (!(matched && parsed)) {
837         dir.clear();
838         if (!matched) {
839           std::cerr << "Unknown argument " << arg << std::endl;
840         }
841         break;
842       }
843     }
844   }
845
846   if (dir.empty()) {
847     /* clang-format off */
848     std::cerr <<
849       "Usage: cmake --install <dir> [options]\n"
850       "Options:\n"
851       "  <dir>              = Project binary directory to install.\n"
852       "  --config <cfg>     = For multi-configuration tools, choose <cfg>.\n"
853       "  --component <comp> = Component-based install. Only install <comp>.\n"
854       "  --default-directory-permissions <permission> \n"
855       "     Default install permission. Use default permission <permission>.\n"
856       "  --prefix <prefix>  = The installation prefix CMAKE_INSTALL_PREFIX.\n"
857       "  --strip            = Performing install/strip.\n"
858       "  -v --verbose       = Enable verbose output.\n"
859       ;
860     /* clang-format on */
861     return 1;
862   }
863
864   cmake cm(cmake::RoleScript, cmState::Script);
865
866   cmSystemTools::SetMessageCallback(
867     [&cm](const std::string& msg, const cmMessageMetadata& md) {
868       cmakemainMessageCallback(msg, md, &cm);
869     });
870   cm.SetProgressCallback([&cm](const std::string& msg, float prog) {
871     cmakemainProgressCallback(msg, prog, &cm);
872   });
873   cm.SetHomeDirectory("");
874   cm.SetHomeOutputDirectory("");
875   cm.SetDebugOutputOn(verbose);
876   cm.SetWorkingMode(cmake::SCRIPT_MODE);
877
878   std::vector<std::string> args{ av[0] };
879
880   if (!prefix.empty()) {
881     args.emplace_back("-DCMAKE_INSTALL_PREFIX=" + prefix);
882   }
883
884   if (!component.empty()) {
885     args.emplace_back("-DCMAKE_INSTALL_COMPONENT=" + component);
886   }
887
888   if (strip) {
889     args.emplace_back("-DCMAKE_INSTALL_DO_STRIP=1");
890   }
891
892   if (!config.empty()) {
893     args.emplace_back("-DCMAKE_INSTALL_CONFIG_NAME=" + config);
894   }
895
896   if (!defaultDirectoryPermissions.empty()) {
897     std::string parsedPermissionsVar;
898     if (!parse_default_directory_permissions(defaultDirectoryPermissions,
899                                              parsedPermissionsVar)) {
900       std::cerr << "--default-directory-permissions is in incorrect format"
901                 << std::endl;
902       return 1;
903     }
904     args.emplace_back("-DCMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS=" +
905                       parsedPermissionsVar);
906   }
907
908   args.emplace_back("-P");
909   args.emplace_back(dir + "/cmake_install.cmake");
910
911   return cm.Run(args) ? 1 : 0;
912 #endif
913 }
914
915 int do_open(int ac, char const* const* av)
916 {
917 #ifdef CMAKE_BOOTSTRAP
918   std::cerr << "This cmake does not support --open\n";
919   return -1;
920 #else
921   std::string dir;
922
923   enum Doing
924   {
925     DoingNone,
926     DoingDir,
927   };
928   Doing doing = DoingDir;
929   for (int i = 2; i < ac; ++i) {
930     switch (doing) {
931       case DoingDir:
932         dir = cmSystemTools::CollapseFullPath(av[i]);
933         doing = DoingNone;
934         break;
935       default:
936         std::cerr << "Unknown argument " << av[i] << std::endl;
937         dir.clear();
938         break;
939     }
940   }
941   if (dir.empty()) {
942     std::cerr << "Usage: cmake --open <dir>\n";
943     return 1;
944   }
945
946   cmake cm(cmake::RoleInternal, cmState::Unknown);
947   cmSystemTools::SetMessageCallback(
948     [&cm](const std::string& msg, const cmMessageMetadata& md) {
949       cmakemainMessageCallback(msg, md, &cm);
950     });
951   cm.SetProgressCallback([&cm](const std::string& msg, float prog) {
952     cmakemainProgressCallback(msg, prog, &cm);
953   });
954   return cm.Open(dir, false) ? 0 : 1;
955 #endif
956 }
957 } // namespace
958
959 int main(int ac, char const* const* av)
960 {
961   cmSystemTools::EnsureStdPipes();
962
963   // Replace streambuf so we can output Unicode to console
964   auto consoleBuf = cm::make_unique<cmConsoleBuf>();
965   consoleBuf->SetUTF8Pipes();
966
967   cmsys::Encoding::CommandLineArguments args =
968     cmsys::Encoding::CommandLineArguments::Main(ac, av);
969   ac = args.argc();
970   av = args.argv();
971
972   cmSystemTools::InitializeLibUV();
973   cmSystemTools::FindCMakeResources(av[0]);
974   if (ac > 1) {
975     if (strcmp(av[1], "--build") == 0) {
976       return do_build(ac, av);
977     }
978     if (strcmp(av[1], "--install") == 0) {
979       return do_install(ac, av);
980     }
981     if (strcmp(av[1], "--open") == 0) {
982       return do_open(ac, av);
983     }
984     if (strcmp(av[1], "-E") == 0) {
985       return do_command(ac, av, std::move(consoleBuf));
986     }
987   }
988   int ret = do_cmake(ac, av);
989 #ifndef CMAKE_BOOTSTRAP
990   cmDynamicLoader::FlushCache();
991 #endif
992   if (uv_loop_t* loop = uv_default_loop()) {
993     uv_loop_close(loop);
994   }
995   return ret;
996 }