| /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying | 
 |    file Copyright.txt or https://cmake.org/licensing for details.  */ | 
 |  | 
 | #include "cmConfigure.h" // IWYU pragma: keep | 
 |  | 
 | #include <algorithm> | 
 | #include <cassert> | 
 | #include <cctype> | 
 | #include <climits> | 
 | #include <cstdio> | 
 | #include <cstring> | 
 | #include <functional> | 
 | #include <iostream> | 
 | #include <sstream> | 
 | #include <string> | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | #include <cm/memory> | 
 | #include <cmext/algorithm> | 
 |  | 
 | #include <cm3p/uv.h> | 
 |  | 
 | #include "cmBuildOptions.h" | 
 | #include "cmCommandLineArgument.h" | 
 | #include "cmConsoleBuf.h" | 
 | #include "cmDocumentationEntry.h" // IWYU pragma: keep | 
 | #include "cmGlobalGenerator.h" | 
 | #include "cmMakefile.h" | 
 | #include "cmMessageMetadata.h" | 
 | #include "cmState.h" | 
 | #include "cmStateTypes.h" | 
 | #include "cmStringAlgorithms.h" | 
 | #include "cmSystemTools.h" | 
 | #include "cmValue.h" | 
 | #include "cmake.h" | 
 | #include "cmcmd.h" | 
 |  | 
 | #ifndef CMAKE_BOOTSTRAP | 
 | #  include "cmDocumentation.h" | 
 | #  include "cmDynamicLoader.h" | 
 | #endif | 
 |  | 
 | #include "cmsys/Encoding.hxx" | 
 | #include "cmsys/Terminal.h" | 
 |  | 
 | namespace { | 
 | #ifndef CMAKE_BOOTSTRAP | 
 | const char* cmDocumentationName[][2] = { | 
 |   { nullptr, "  cmake - Cross-Platform Makefile Generator." }, | 
 |   { nullptr, nullptr } | 
 | }; | 
 |  | 
 | const char* cmDocumentationUsage[][2] = { | 
 |   { nullptr, | 
 |     "  cmake [options] <path-to-source>\n" | 
 |     "  cmake [options] <path-to-existing-build>\n" | 
 |     "  cmake [options] -S <path-to-source> -B <path-to-build>" }, | 
 |   { nullptr, | 
 |     "Specify a source directory to (re-)generate a build system for " | 
 |     "it in the current working directory.  Specify an existing build " | 
 |     "directory to re-generate its build system." }, | 
 |   { nullptr, nullptr } | 
 | }; | 
 |  | 
 | const char* cmDocumentationUsageNote[][2] = { | 
 |   { nullptr, "Run 'cmake --help' for more information." }, | 
 |   { nullptr, nullptr } | 
 | }; | 
 |  | 
 | const char* cmDocumentationOptions[][2] = { | 
 |   CMAKE_STANDARD_OPTIONS_TABLE, | 
 |   { "--preset <preset>,--preset=<preset>", "Specify a configure preset." }, | 
 |   { "--list-presets", "List available presets." }, | 
 |   { "-E", "CMake command mode." }, | 
 |   { "-L[A][H]", "List non-advanced cached variables." }, | 
 |   { "--fresh", | 
 |     "Configure a fresh build tree, removing any existing cache file." }, | 
 |   { "--build <dir>", "Build a CMake-generated project binary tree." }, | 
 |   { "--install <dir>", "Install a CMake-generated project binary tree." }, | 
 |   { "--open <dir>", "Open generated project in the associated application." }, | 
 |   { "-N", "View mode only." }, | 
 |   { "-P <file>", "Process script mode." }, | 
 |   { "--find-package", "Legacy pkg-config like mode.  Do not use." }, | 
 |   { "--graphviz=[file]", | 
 |     "Generate graphviz of dependencies, see " | 
 |     "CMakeGraphVizOptions.cmake for more." }, | 
 |   { "--system-information [file]", "Dump information about this system." }, | 
 |   { "--log-level=<ERROR|WARNING|NOTICE|STATUS|VERBOSE|DEBUG|TRACE>", | 
 |     "Set the verbosity of messages from CMake files. " | 
 |     "--loglevel is also accepted for backward compatibility reasons." }, | 
 |   { "--log-context", "Prepend log messages with context, if given" }, | 
 |   { "--debug-trycompile", | 
 |     "Do not delete the try_compile build tree. Only " | 
 |     "useful on one try_compile at a time." }, | 
 |   { "--debug-output", "Put cmake in a debug mode." }, | 
 |   { "--debug-find", "Put cmake find in a debug mode." }, | 
 |   { "--debug-find-pkg=<pkg-name>[,...]", | 
 |     "Limit cmake debug-find to the comma-separated list of packages" }, | 
 |   { "--debug-find-var=<var-name>[,...]", | 
 |     "Limit cmake debug-find to the comma-separated list of result variables" }, | 
 |   { "--trace", "Put cmake in trace mode." }, | 
 |   { "--trace-expand", "Put cmake in trace mode with variable expansion." }, | 
 |   { "--trace-format=<human|json-v1>", "Set the output format of the trace." }, | 
 |   { "--trace-source=<file>", | 
 |     "Trace only this CMake file/module. Multiple options allowed." }, | 
 |   { "--trace-redirect=<file>", | 
 |     "Redirect trace output to a file instead of stderr." }, | 
 |   { "--warn-uninitialized", "Warn about uninitialized values." }, | 
 |   { "--no-warn-unused-cli", "Don't warn about command line options." }, | 
 |   { "--check-system-vars", | 
 |     "Find problems with variable usage in system " | 
 |     "files." }, | 
 |   { "--compile-no-warning-as-error", | 
 |     "Ignore COMPILE_WARNING_AS_ERROR property and " | 
 |     "CMAKE_COMPILE_WARNING_AS_ERROR variable." }, | 
 | #  if !defined(CMAKE_BOOTSTRAP) | 
 |   { "--profiling-format=<fmt>", | 
 |     "Output data for profiling CMake scripts. Supported formats: " | 
 |     "google-trace" }, | 
 |   { "--profiling-output=<file>", | 
 |     "Select an output path for the profiling data enabled through " | 
 |     "--profiling-format." }, | 
 | #  endif | 
 |   { nullptr, nullptr } | 
 | }; | 
 |  | 
 | #endif | 
 |  | 
 | int do_command(int ac, char const* const* av, | 
 |                std::unique_ptr<cmConsoleBuf> consoleBuf) | 
 | { | 
 |   std::vector<std::string> args; | 
 |   args.reserve(ac - 1); | 
 |   args.emplace_back(av[0]); | 
 |   cm::append(args, av + 2, av + ac); | 
 |   return cmcmd::ExecuteCMakeCommand(args, std::move(consoleBuf)); | 
 | } | 
 |  | 
 | cmMakefile* cmakemainGetMakefile(cmake* cm) | 
 | { | 
 |   if (cm && cm->GetDebugOutput()) { | 
 |     cmGlobalGenerator* gg = cm->GetGlobalGenerator(); | 
 |     if (gg) { | 
 |       return gg->GetCurrentMakefile(); | 
 |     } | 
 |   } | 
 |   return nullptr; | 
 | } | 
 |  | 
 | std::string cmakemainGetStack(cmake* cm) | 
 | { | 
 |   std::string msg; | 
 |   cmMakefile* mf = cmakemainGetMakefile(cm); | 
 |   if (mf) { | 
 |     msg = mf->FormatListFileStack(); | 
 |     if (!msg.empty()) { | 
 |       msg = "\n   Called from: " + msg; | 
 |     } | 
 |   } | 
 |  | 
 |   return msg; | 
 | } | 
 |  | 
 | void cmakemainMessageCallback(const std::string& m, | 
 |                               const cmMessageMetadata& md, cmake* cm) | 
 | { | 
 | #if defined(_WIN32) | 
 |   // FIXME: On Windows we replace cerr's streambuf with a custom | 
 |   // implementation that converts our internal UTF-8 encoding to the | 
 |   // console's encoding.  It also does *not* replace LF with CRLF. | 
 |   // Since stderr does not convert encoding and does convert LF, we | 
 |   // cannot use it to print messages.  Another implementation will | 
 |   // be needed to print colored messages on Windows. | 
 |   static_cast<void>(md); | 
 |   std::cerr << m << cmakemainGetStack(cm) << '\n' << std::flush; | 
 | #else | 
 |   cmsysTerminal_cfprintf(md.desiredColor, stderr, "%s", m.c_str()); | 
 |   fflush(stderr); // stderr is buffered in some cases. | 
 |   std::cerr << cmakemainGetStack(cm) << '\n' << std::flush; | 
 | #endif | 
 | } | 
 |  | 
 | void cmakemainProgressCallback(const std::string& m, float prog, cmake* cm) | 
 | { | 
 |   cmMakefile* mf = cmakemainGetMakefile(cm); | 
 |   std::string dir; | 
 |   if (mf && cmHasLiteralPrefix(m, "Configuring") && (prog < 0)) { | 
 |     dir = cmStrCat(' ', mf->GetCurrentSourceDirectory()); | 
 |   } else if (mf && cmHasLiteralPrefix(m, "Generating")) { | 
 |     dir = cmStrCat(' ', mf->GetCurrentBinaryDirectory()); | 
 |   } | 
 |  | 
 |   if ((prog < 0) || (!dir.empty())) { | 
 |     std::cout << "-- " << m << dir << cmakemainGetStack(cm) << std::endl; | 
 |   } | 
 | } | 
 |  | 
 | int do_cmake(int ac, char const* const* av) | 
 | { | 
 |   if (cmSystemTools::GetCurrentWorkingDirectory().empty()) { | 
 |     std::cerr << "Current working directory cannot be established." | 
 |               << std::endl; | 
 |     return 1; | 
 |   } | 
 |  | 
 | #ifndef CMAKE_BOOTSTRAP | 
 |   cmDocumentation doc; | 
 |   doc.addCMakeStandardDocSections(); | 
 |   if (doc.CheckOptions(ac, av)) { | 
 |     // Construct and print requested documentation. | 
 |     cmake hcm(cmake::RoleInternal, cmState::Unknown); | 
 |     hcm.SetHomeDirectory(""); | 
 |     hcm.SetHomeOutputDirectory(""); | 
 |     hcm.AddCMakePaths(); | 
 |  | 
 |     // the command line args are processed here so that you can do | 
 |     // -DCMAKE_MODULE_PATH=/some/path and have this value accessible here | 
 |     std::vector<std::string> args(av, av + ac); | 
 |     hcm.SetCacheArgs(args); | 
 |  | 
 |     auto generators = hcm.GetGeneratorsDocumentation(); | 
 |  | 
 |     doc.SetName("cmake"); | 
 |     doc.SetSection("Name", cmDocumentationName); | 
 |     doc.SetSection("Usage", cmDocumentationUsage); | 
 |     if (ac == 1) { | 
 |       doc.AppendSection("Usage", cmDocumentationUsageNote); | 
 |     } | 
 |     doc.AppendSection("Generators", generators); | 
 |     doc.PrependSection("Options", cmDocumentationOptions); | 
 |  | 
 |     return doc.PrintRequestedDocumentation(std::cout) ? 0 : 1; | 
 |   } | 
 | #else | 
 |   if (ac == 1) { | 
 |     std::cout | 
 |       << "Bootstrap CMake should not be used outside CMake build process." | 
 |       << std::endl; | 
 |     return 0; | 
 |   } | 
 | #endif | 
 |  | 
 |   bool wizard_mode = false; | 
 |   bool sysinfo = false; | 
 |   bool list_cached = false; | 
 |   bool list_all_cached = false; | 
 |   bool list_help = false; | 
 |   bool view_only = false; | 
 |   cmake::WorkingMode workingMode = cmake::NORMAL_MODE; | 
 |   std::vector<std::string> parsedArgs; | 
 |  | 
 |   using CommandArgument = | 
 |     cmCommandLineArgument<bool(std::string const& value)>; | 
 |   std::vector<CommandArgument> arguments = { | 
 |     CommandArgument{ | 
 |       "-i", CommandArgument::Values::Zero, | 
 |       [&wizard_mode](std::string const&) -> bool { | 
 |         /* clang-format off */ | 
 |         std::cerr << | 
 |           "The \"cmake -i\" wizard mode is no longer supported.\n" | 
 |           "Use the -D option to set cache values on the command line.\n" | 
 |           "Use cmake-gui or ccmake for an interactive dialog.\n"; | 
 |         /* clang-format on */ | 
 |         wizard_mode = true; | 
 |         return true; | 
 |       } }, | 
 |     CommandArgument{ "--system-information", CommandArgument::Values::Zero, | 
 |                      CommandArgument::setToTrue(sysinfo) }, | 
 |     CommandArgument{ "-N", CommandArgument::Values::Zero, | 
 |                      CommandArgument::setToTrue(view_only) }, | 
 |     CommandArgument{ "-LAH", CommandArgument::Values::Zero, | 
 |                      CommandArgument::setToTrue(list_all_cached, list_help) }, | 
 |     CommandArgument{ "-LA", CommandArgument::Values::Zero, | 
 |                      CommandArgument::setToTrue(list_all_cached) }, | 
 |     CommandArgument{ "-LH", CommandArgument::Values::Zero, | 
 |                      CommandArgument::setToTrue(list_cached, list_help) }, | 
 |     CommandArgument{ "-L", CommandArgument::Values::Zero, | 
 |                      CommandArgument::setToTrue(list_cached) }, | 
 |     CommandArgument{ "-P", "No script specified for argument -P", | 
 |                      CommandArgument::Values::One, | 
 |                      CommandArgument::RequiresSeparator::No, | 
 |                      [&](std::string const& value) -> bool { | 
 |                        workingMode = cmake::SCRIPT_MODE; | 
 |                        parsedArgs.emplace_back("-P"); | 
 |                        parsedArgs.push_back(value); | 
 |                        return true; | 
 |                      } }, | 
 |     CommandArgument{ "--find-package", CommandArgument::Values::Zero, | 
 |                      [&](std::string const&) -> bool { | 
 |                        workingMode = cmake::FIND_PACKAGE_MODE; | 
 |                        parsedArgs.emplace_back("--find-package"); | 
 |                        return true; | 
 |                      } }, | 
 |     CommandArgument{ "--list-presets", CommandArgument::Values::ZeroOrOne, | 
 |                      [&](std::string const& value) -> bool { | 
 |                        workingMode = cmake::HELP_MODE; | 
 |                        parsedArgs.emplace_back("--list-presets"); | 
 |                        parsedArgs.emplace_back(value); | 
 |                        return true; | 
 |                      } }, | 
 |   }; | 
 |  | 
 |   std::vector<std::string> inputArgs; | 
 |   inputArgs.reserve(ac); | 
 |   cm::append(inputArgs, av, av + ac); | 
 |  | 
 |   for (decltype(inputArgs.size()) i = 0; i < inputArgs.size(); ++i) { | 
 |     std::string const& arg = inputArgs[i]; | 
 |     bool matched = false; | 
 |     for (auto const& m : arguments) { | 
 |       if (m.matches(arg)) { | 
 |         matched = true; | 
 |         if (m.parse(arg, i, inputArgs)) { | 
 |           break; | 
 |         } | 
 |         return 1; // failed to parse | 
 |       } | 
 |     } | 
 |     if (!matched) { | 
 |       parsedArgs.emplace_back(av[i]); | 
 |     } | 
 |   } | 
 |  | 
 |   if (wizard_mode) { | 
 |     return 1; | 
 |   } | 
 |  | 
 |   if (sysinfo) { | 
 |     cmake cm(cmake::RoleProject, cmState::Project); | 
 |     cm.SetHomeDirectory(""); | 
 |     cm.SetHomeOutputDirectory(""); | 
 |     int ret = cm.GetSystemInformation(parsedArgs); | 
 |     return ret; | 
 |   } | 
 |   cmake::Role const role = | 
 |     workingMode == cmake::SCRIPT_MODE ? cmake::RoleScript : cmake::RoleProject; | 
 |   cmState::Mode mode = cmState::Unknown; | 
 |   switch (workingMode) { | 
 |     case cmake::NORMAL_MODE: | 
 |     case cmake::HELP_MODE: | 
 |       mode = cmState::Project; | 
 |       break; | 
 |     case cmake::SCRIPT_MODE: | 
 |       mode = cmState::Script; | 
 |       break; | 
 |     case cmake::FIND_PACKAGE_MODE: | 
 |       mode = cmState::FindPackage; | 
 |       break; | 
 |   } | 
 |   cmake cm(role, mode); | 
 |   cm.SetHomeDirectory(""); | 
 |   cm.SetHomeOutputDirectory(""); | 
 |   cmSystemTools::SetMessageCallback( | 
 |     [&cm](const std::string& msg, const cmMessageMetadata& md) { | 
 |       cmakemainMessageCallback(msg, md, &cm); | 
 |     }); | 
 |   cm.SetProgressCallback([&cm](const std::string& msg, float prog) { | 
 |     cmakemainProgressCallback(msg, prog, &cm); | 
 |   }); | 
 |   cm.SetWorkingMode(workingMode); | 
 |  | 
 |   int res = cm.Run(parsedArgs, view_only); | 
 |   if (list_cached || list_all_cached) { | 
 |     std::cout << "-- Cache values" << std::endl; | 
 |     std::vector<std::string> keys = cm.GetState()->GetCacheEntryKeys(); | 
 |     for (std::string const& k : keys) { | 
 |       cmStateEnums::CacheEntryType t = cm.GetState()->GetCacheEntryType(k); | 
 |       if (t != cmStateEnums::INTERNAL && t != cmStateEnums::STATIC && | 
 |           t != cmStateEnums::UNINITIALIZED) { | 
 |         cmValue advancedProp = | 
 |           cm.GetState()->GetCacheEntryProperty(k, "ADVANCED"); | 
 |         if (list_all_cached || !advancedProp) { | 
 |           if (list_help) { | 
 |             cmValue help = | 
 |               cm.GetState()->GetCacheEntryProperty(k, "HELPSTRING"); | 
 |             std::cout << "// " << (help ? *help : "") << std::endl; | 
 |           } | 
 |           std::cout << k << ":" << cmState::CacheEntryTypeToString(t) << "=" | 
 |                     << cm.GetState()->GetSafeCacheEntryValue(k) << std::endl; | 
 |           if (list_help) { | 
 |             std::cout << std::endl; | 
 |           } | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   // Always return a non-negative value.  Windows tools do not always | 
 |   // interpret negative return values as errors. | 
 |   if (res != 0) { | 
 |     return 1; | 
 |   } | 
 |   return 0; | 
 | } | 
 |  | 
 | #ifndef CMAKE_BOOTSTRAP | 
 | int extract_job_number(std::string const& command, | 
 |                        std::string const& jobString) | 
 | { | 
 |   int jobs = -1; | 
 |   unsigned long numJobs = 0; | 
 |   if (jobString.empty()) { | 
 |     jobs = cmake::DEFAULT_BUILD_PARALLEL_LEVEL; | 
 |   } else if (cmStrToULong(jobString, &numJobs)) { | 
 |     if (numJobs == 0) { | 
 |       std::cerr | 
 |         << "The <jobs> value requires a positive integer argument.\n\n"; | 
 |     } else if (numJobs > INT_MAX) { | 
 |       std::cerr << "The <jobs> value is too large.\n\n"; | 
 |     } else { | 
 |       jobs = static_cast<int>(numJobs); | 
 |     } | 
 |   } else { | 
 |     std::cerr << "'" << command << "' invalid number '" << jobString | 
 |               << "' given.\n\n"; | 
 |   } | 
 |   return jobs; | 
 | } | 
 | #endif | 
 |  | 
 | int do_build(int ac, char const* const* av) | 
 | { | 
 | #ifdef CMAKE_BOOTSTRAP | 
 |   std::cerr << "This cmake does not support --build\n"; | 
 |   return -1; | 
 | #else | 
 |   int jobs = cmake::NO_BUILD_PARALLEL_LEVEL; | 
 |   std::vector<std::string> targets; | 
 |   std::string config; | 
 |   std::string dir; | 
 |   std::vector<std::string> nativeOptions; | 
 |   bool nativeOptionsPassed = false; | 
 |   bool cleanFirst = false; | 
 |   bool foundClean = false; | 
 |   bool foundNonClean = false; | 
 |   PackageResolveMode resolveMode = PackageResolveMode::Default; | 
 |   bool verbose = cmSystemTools::HasEnv("VERBOSE"); | 
 |   std::string presetName; | 
 |   bool listPresets = false; | 
 |  | 
 |   auto jLambda = [&](std::string const& value) -> bool { | 
 |     jobs = extract_job_number("-j", value); | 
 |     if (jobs < 0) { | 
 |       dir.clear(); | 
 |     } | 
 |     return true; | 
 |   }; | 
 |   auto parallelLambda = [&](std::string const& value) -> bool { | 
 |     jobs = extract_job_number("--parallel", value); | 
 |     if (jobs < 0) { | 
 |       dir.clear(); | 
 |     } | 
 |     return true; | 
 |   }; | 
 |   auto targetLambda = [&](std::string const& value) -> bool { | 
 |     if (!value.empty()) { | 
 |       std::vector<std::string> values = cmExpandedList(value); | 
 |       for (auto const& v : values) { | 
 |         targets.emplace_back(v); | 
 |         if (v == "clean") { | 
 |           foundClean = true; | 
 |         } else { | 
 |           foundNonClean = true; | 
 |         } | 
 |       } | 
 |       return true; | 
 |     } | 
 |     return false; | 
 |   }; | 
 |   auto resolvePackagesLambda = [&](std::string const& value) -> bool { | 
 |     std::string v = value; | 
 |     std::transform(v.begin(), v.end(), v.begin(), ::tolower); | 
 |  | 
 |     if (v == "on") { | 
 |       resolveMode = PackageResolveMode::Force; | 
 |     } else if (v == "only") { | 
 |       resolveMode = PackageResolveMode::OnlyResolve; | 
 |     } else if (v == "off") { | 
 |       resolveMode = PackageResolveMode::Disable; | 
 |     } else { | 
 |       return false; | 
 |     } | 
 |  | 
 |     return true; | 
 |   }; | 
 |   auto verboseLambda = [&](std::string const&) -> bool { | 
 |     verbose = true; | 
 |     return true; | 
 |   }; | 
 |  | 
 |   using CommandArgument = | 
 |     cmCommandLineArgument<bool(std::string const& value)>; | 
 |  | 
 |   std::vector<CommandArgument> arguments = { | 
 |     CommandArgument{ "--preset", CommandArgument::Values::One, | 
 |                      CommandArgument::setToValue(presetName) }, | 
 |     CommandArgument{ "--list-presets", CommandArgument::Values::Zero, | 
 |                      CommandArgument::setToTrue(listPresets) }, | 
 |     CommandArgument{ "-j", CommandArgument::Values::ZeroOrOne, | 
 |                      CommandArgument::RequiresSeparator::No, jLambda }, | 
 |     CommandArgument{ "--parallel", CommandArgument::Values::ZeroOrOne, | 
 |                      CommandArgument::RequiresSeparator::No, parallelLambda }, | 
 |     CommandArgument{ "-t", CommandArgument::Values::OneOrMore, targetLambda }, | 
 |     CommandArgument{ "--target", CommandArgument::Values::OneOrMore, | 
 |                      targetLambda }, | 
 |     CommandArgument{ "--config", CommandArgument::Values::One, | 
 |                      CommandArgument::setToValue(config) }, | 
 |     CommandArgument{ "--clean-first", CommandArgument::Values::Zero, | 
 |                      CommandArgument::setToTrue(cleanFirst) }, | 
 |     CommandArgument{ "--resolve-package-references", | 
 |                      CommandArgument::Values::One, resolvePackagesLambda }, | 
 |     CommandArgument{ "-v", CommandArgument::Values::Zero, verboseLambda }, | 
 |     CommandArgument{ "--verbose", CommandArgument::Values::Zero, | 
 |                      verboseLambda }, | 
 |     /* legacy option no-op*/ | 
 |     CommandArgument{ "--use-stderr", CommandArgument::Values::Zero, | 
 |                      [](std::string const&) -> bool { return true; } }, | 
 |     CommandArgument{ "--", CommandArgument::Values::Zero, | 
 |                      CommandArgument::setToTrue(nativeOptionsPassed) }, | 
 |   }; | 
 |  | 
 |   if (ac >= 3) { | 
 |     std::vector<std::string> inputArgs; | 
 |  | 
 |     bool hasPreset = false; | 
 |     for (int i = 2; i < ac; ++i) { | 
 |       if (strcmp(av[i], "--list-presets") == 0 || | 
 |           cmHasLiteralPrefix(av[i], "--preset=") || | 
 |           strcmp(av[i], "--preset") == 0) { | 
 |         hasPreset = true; | 
 |         break; | 
 |       } | 
 |     } | 
 |  | 
 |     if (hasPreset) { | 
 |       inputArgs.reserve(ac - 2); | 
 |       cm::append(inputArgs, av + 2, av + ac); | 
 |     } else { | 
 |       dir = cmSystemTools::CollapseFullPath(av[2]); | 
 |  | 
 |       inputArgs.reserve(ac - 3); | 
 |       cm::append(inputArgs, av + 3, av + ac); | 
 |     } | 
 |  | 
 |     decltype(inputArgs.size()) i = 0; | 
 |     for (; i < inputArgs.size() && !nativeOptionsPassed; ++i) { | 
 |  | 
 |       std::string const& arg = inputArgs[i]; | 
 |       bool matched = false; | 
 |       bool parsed = false; | 
 |       for (auto const& m : arguments) { | 
 |         matched = m.matches(arg); | 
 |         if (matched) { | 
 |           parsed = m.parse(arg, i, inputArgs); | 
 |           break; | 
 |         } | 
 |       } | 
 |       if (!(matched && parsed)) { | 
 |         dir.clear(); | 
 |         if (!matched) { | 
 |           std::cerr << "Unknown argument " << arg << std::endl; | 
 |         } | 
 |         break; | 
 |       } | 
 |     } | 
 |  | 
 |     if (nativeOptionsPassed) { | 
 |       cm::append(nativeOptions, inputArgs.begin() + i, inputArgs.end()); | 
 |     } | 
 |   } | 
 |  | 
 |   if (foundClean && foundNonClean) { | 
 |     std::cerr << "Error: Building 'clean' and other targets together " | 
 |                  "is not supported." | 
 |               << std::endl; | 
 |     dir.clear(); | 
 |   } | 
 |  | 
 |   if (jobs == cmake::NO_BUILD_PARALLEL_LEVEL) { | 
 |     std::string parallel; | 
 |     if (cmSystemTools::GetEnv("CMAKE_BUILD_PARALLEL_LEVEL", parallel)) { | 
 |       if (parallel.empty()) { | 
 |         jobs = cmake::DEFAULT_BUILD_PARALLEL_LEVEL; | 
 |       } else { | 
 |         unsigned long numJobs = 0; | 
 |         if (cmStrToULong(parallel, &numJobs)) { | 
 |           if (numJobs == 0) { | 
 |             std::cerr << "The CMAKE_BUILD_PARALLEL_LEVEL environment variable " | 
 |                          "requires a positive integer argument.\n\n"; | 
 |             dir.clear(); | 
 |           } else if (numJobs > INT_MAX) { | 
 |             std::cerr << "The CMAKE_BUILD_PARALLEL_LEVEL environment variable " | 
 |                          "is too large.\n\n"; | 
 |             dir.clear(); | 
 |           } else { | 
 |             jobs = static_cast<int>(numJobs); | 
 |           } | 
 |         } else { | 
 |           std::cerr << "'CMAKE_BUILD_PARALLEL_LEVEL' environment variable\n" | 
 |                     << "invalid number '" << parallel << "' given.\n\n"; | 
 |           dir.clear(); | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   if (dir.empty() && presetName.empty() && !listPresets) { | 
 |     /* clang-format off */ | 
 |     std::cerr << | 
 |       "Usage: cmake --build <dir>            " | 
 |       " [options] [-- [native-options]]\n" | 
 |       "       cmake --build --preset <preset>" | 
 |       " [options] [-- [native-options]]\n" | 
 |       "Options:\n" | 
 |       "  <dir>          = Project binary directory to be built.\n" | 
 |       "  --preset <preset>, --preset=<preset>\n" | 
 |       "                 = Specify a build preset.\n" | 
 |       "  --list-presets\n" | 
 |       "                 = List available build presets.\n" | 
 |       "  --parallel [<jobs>], -j [<jobs>]\n" | 
 |       "                 = Build in parallel using the given number of jobs. \n" | 
 |       "                   If <jobs> is omitted the native build tool's \n" | 
 |       "                   default number is used.\n" | 
 |       "                   The CMAKE_BUILD_PARALLEL_LEVEL environment " | 
 |       "variable\n" | 
 |       "                   specifies a default parallel level when this " | 
 |       "option\n" | 
 |       "                   is not given.\n" | 
 |       "  --target <tgt>..., -t <tgt>... \n" | 
 |       "                 = Build <tgt> instead of default targets.\n" | 
 |       "  --config <cfg> = For multi-configuration tools, choose <cfg>.\n" | 
 |       "  --clean-first  = Build target 'clean' first, then build.\n" | 
 |       "                   (To clean only, use --target 'clean'.)\n" | 
 |       "  --resolve-package-references={on|only|off}\n" | 
 |       "                 = Restore/resolve package references during build.\n" | 
 |       "  --verbose, -v  = Enable verbose output - if supported - including\n" | 
 |       "                   the build commands to be executed. \n" | 
 |       "  --             = Pass remaining options to the native tool.\n" | 
 |       ; | 
 |     /* clang-format on */ | 
 |     return 1; | 
 |   } | 
 |  | 
 |   cmake cm(cmake::RoleInternal, cmState::Project); | 
 |   cmSystemTools::SetMessageCallback( | 
 |     [&cm](const std::string& msg, const cmMessageMetadata& md) { | 
 |       cmakemainMessageCallback(msg, md, &cm); | 
 |     }); | 
 |   cm.SetProgressCallback([&cm](const std::string& msg, float prog) { | 
 |     cmakemainProgressCallback(msg, prog, &cm); | 
 |   }); | 
 |  | 
 |   cmBuildOptions buildOptions(cleanFirst, false, resolveMode); | 
 |  | 
 |   return cm.Build(jobs, std::move(dir), std::move(targets), std::move(config), | 
 |                   std::move(nativeOptions), buildOptions, verbose, presetName, | 
 |                   listPresets); | 
 | #endif | 
 | } | 
 |  | 
 | bool parse_default_directory_permissions(const std::string& permissions, | 
 |                                          std::string& parsedPermissionsVar) | 
 | { | 
 |   std::vector<std::string> parsedPermissions; | 
 |   enum Doing | 
 |   { | 
 |     DoingNone, | 
 |     DoingOwner, | 
 |     DoingGroup, | 
 |     DoingWorld, | 
 |     DoingOwnerAssignment, | 
 |     DoingGroupAssignment, | 
 |     DoingWorldAssignment, | 
 |   }; | 
 |   Doing doing = DoingNone; | 
 |  | 
 |   auto uniquePushBack = [&parsedPermissions](const std::string& e) { | 
 |     if (std::find(parsedPermissions.begin(), parsedPermissions.end(), e) == | 
 |         parsedPermissions.end()) { | 
 |       parsedPermissions.push_back(e); | 
 |     } | 
 |   }; | 
 |  | 
 |   for (auto const& e : permissions) { | 
 |     switch (doing) { | 
 |       case DoingNone: | 
 |         if (e == 'u') { | 
 |           doing = DoingOwner; | 
 |         } else if (e == 'g') { | 
 |           doing = DoingGroup; | 
 |         } else if (e == 'o') { | 
 |           doing = DoingWorld; | 
 |         } else { | 
 |           return false; | 
 |         } | 
 |         break; | 
 |       case DoingOwner: | 
 |         if (e == '=') { | 
 |           doing = DoingOwnerAssignment; | 
 |         } else { | 
 |           return false; | 
 |         } | 
 |         break; | 
 |       case DoingGroup: | 
 |         if (e == '=') { | 
 |           doing = DoingGroupAssignment; | 
 |         } else { | 
 |           return false; | 
 |         } | 
 |         break; | 
 |       case DoingWorld: | 
 |         if (e == '=') { | 
 |           doing = DoingWorldAssignment; | 
 |         } else { | 
 |           return false; | 
 |         } | 
 |         break; | 
 |       case DoingOwnerAssignment: | 
 |         if (e == 'r') { | 
 |           uniquePushBack("OWNER_READ"); | 
 |         } else if (e == 'w') { | 
 |           uniquePushBack("OWNER_WRITE"); | 
 |         } else if (e == 'x') { | 
 |           uniquePushBack("OWNER_EXECUTE"); | 
 |         } else if (e == ',') { | 
 |           doing = DoingNone; | 
 |         } else { | 
 |           return false; | 
 |         } | 
 |         break; | 
 |       case DoingGroupAssignment: | 
 |         if (e == 'r') { | 
 |           uniquePushBack("GROUP_READ"); | 
 |         } else if (e == 'w') { | 
 |           uniquePushBack("GROUP_WRITE"); | 
 |         } else if (e == 'x') { | 
 |           uniquePushBack("GROUP_EXECUTE"); | 
 |         } else if (e == ',') { | 
 |           doing = DoingNone; | 
 |         } else { | 
 |           return false; | 
 |         } | 
 |         break; | 
 |       case DoingWorldAssignment: | 
 |         if (e == 'r') { | 
 |           uniquePushBack("WORLD_READ"); | 
 |         } else if (e == 'w') { | 
 |           uniquePushBack("WORLD_WRITE"); | 
 |         } else if (e == 'x') { | 
 |           uniquePushBack("WORLD_EXECUTE"); | 
 |         } else if (e == ',') { | 
 |           doing = DoingNone; | 
 |         } else { | 
 |           return false; | 
 |         } | 
 |         break; | 
 |     } | 
 |   } | 
 |   if (doing != DoingOwnerAssignment && doing != DoingGroupAssignment && | 
 |       doing != DoingWorldAssignment) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   std::ostringstream oss; | 
 |   for (auto i = 0u; i < parsedPermissions.size(); i++) { | 
 |     if (i != 0) { | 
 |       oss << ";"; | 
 |     } | 
 |     oss << parsedPermissions[i]; | 
 |   } | 
 |  | 
 |   parsedPermissionsVar = oss.str(); | 
 |   return true; | 
 | } | 
 |  | 
 | int do_install(int ac, char const* const* av) | 
 | { | 
 | #ifdef CMAKE_BOOTSTRAP | 
 |   std::cerr << "This cmake does not support --install\n"; | 
 |   return -1; | 
 | #else | 
 |   assert(1 < ac); | 
 |  | 
 |   std::string config; | 
 |   std::string component; | 
 |   std::string defaultDirectoryPermissions; | 
 |   std::string prefix; | 
 |   std::string dir; | 
 |   bool strip = false; | 
 |   bool verbose = cmSystemTools::HasEnv("VERBOSE"); | 
 |  | 
 |   auto verboseLambda = [&](std::string const&) -> bool { | 
 |     verbose = true; | 
 |     return true; | 
 |   }; | 
 |  | 
 |   using CommandArgument = | 
 |     cmCommandLineArgument<bool(std::string const& value)>; | 
 |  | 
 |   std::vector<CommandArgument> arguments = { | 
 |     CommandArgument{ "--config", CommandArgument::Values::One, | 
 |                      CommandArgument::setToValue(config) }, | 
 |     CommandArgument{ "--component", CommandArgument::Values::One, | 
 |                      CommandArgument::setToValue(component) }, | 
 |     CommandArgument{ | 
 |       "--default-directory-permissions", CommandArgument::Values::One, | 
 |       CommandArgument::setToValue(defaultDirectoryPermissions) }, | 
 |     CommandArgument{ "--prefix", CommandArgument::Values::One, | 
 |                      CommandArgument::setToValue(prefix) }, | 
 |     CommandArgument{ "--strip", CommandArgument::Values::Zero, | 
 |                      CommandArgument::setToTrue(strip) }, | 
 |     CommandArgument{ "-v", CommandArgument::Values::Zero, verboseLambda }, | 
 |     CommandArgument{ "--verbose", CommandArgument::Values::Zero, | 
 |                      verboseLambda } | 
 |   }; | 
 |  | 
 |   if (ac >= 3) { | 
 |     dir = cmSystemTools::CollapseFullPath(av[2]); | 
 |  | 
 |     std::vector<std::string> inputArgs; | 
 |     inputArgs.reserve(ac - 3); | 
 |     cm::append(inputArgs, av + 3, av + ac); | 
 |     for (decltype(inputArgs.size()) i = 0; i < inputArgs.size(); ++i) { | 
 |  | 
 |       std::string const& arg = inputArgs[i]; | 
 |       bool matched = false; | 
 |       bool parsed = false; | 
 |       for (auto const& m : arguments) { | 
 |         matched = m.matches(arg); | 
 |         if (matched) { | 
 |           parsed = m.parse(arg, i, inputArgs); | 
 |           break; | 
 |         } | 
 |       } | 
 |       if (!(matched && parsed)) { | 
 |         dir.clear(); | 
 |         if (!matched) { | 
 |           std::cerr << "Unknown argument " << arg << std::endl; | 
 |         } | 
 |         break; | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   if (dir.empty()) { | 
 |     /* clang-format off */ | 
 |     std::cerr << | 
 |       "Usage: cmake --install <dir> [options]\n" | 
 |       "Options:\n" | 
 |       "  <dir>              = Project binary directory to install.\n" | 
 |       "  --config <cfg>     = For multi-configuration tools, choose <cfg>.\n" | 
 |       "  --component <comp> = Component-based install. Only install <comp>.\n" | 
 |       "  --default-directory-permissions <permission> \n" | 
 |       "     Default install permission. Use default permission <permission>.\n" | 
 |       "  --prefix <prefix>  = The installation prefix CMAKE_INSTALL_PREFIX.\n" | 
 |       "  --strip            = Performing install/strip.\n" | 
 |       "  -v --verbose       = Enable verbose output.\n" | 
 |       ; | 
 |     /* clang-format on */ | 
 |     return 1; | 
 |   } | 
 |  | 
 |   cmake cm(cmake::RoleScript, cmState::Script); | 
 |  | 
 |   cmSystemTools::SetMessageCallback( | 
 |     [&cm](const std::string& msg, const cmMessageMetadata& md) { | 
 |       cmakemainMessageCallback(msg, md, &cm); | 
 |     }); | 
 |   cm.SetProgressCallback([&cm](const std::string& msg, float prog) { | 
 |     cmakemainProgressCallback(msg, prog, &cm); | 
 |   }); | 
 |   cm.SetHomeDirectory(""); | 
 |   cm.SetHomeOutputDirectory(""); | 
 |   cm.SetDebugOutputOn(verbose); | 
 |   cm.SetWorkingMode(cmake::SCRIPT_MODE); | 
 |  | 
 |   std::vector<std::string> args{ av[0] }; | 
 |  | 
 |   if (!prefix.empty()) { | 
 |     args.emplace_back("-DCMAKE_INSTALL_PREFIX=" + prefix); | 
 |   } | 
 |  | 
 |   if (!component.empty()) { | 
 |     args.emplace_back("-DCMAKE_INSTALL_COMPONENT=" + component); | 
 |   } | 
 |  | 
 |   if (strip) { | 
 |     args.emplace_back("-DCMAKE_INSTALL_DO_STRIP=1"); | 
 |   } | 
 |  | 
 |   if (!config.empty()) { | 
 |     args.emplace_back("-DCMAKE_INSTALL_CONFIG_NAME=" + config); | 
 |   } | 
 |  | 
 |   if (!defaultDirectoryPermissions.empty()) { | 
 |     std::string parsedPermissionsVar; | 
 |     if (!parse_default_directory_permissions(defaultDirectoryPermissions, | 
 |                                              parsedPermissionsVar)) { | 
 |       std::cerr << "--default-directory-permissions is in incorrect format" | 
 |                 << std::endl; | 
 |       return 1; | 
 |     } | 
 |     args.emplace_back("-DCMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS=" + | 
 |                       parsedPermissionsVar); | 
 |   } | 
 |  | 
 |   args.emplace_back("-P"); | 
 |   args.emplace_back(dir + "/cmake_install.cmake"); | 
 |  | 
 |   return cm.Run(args) ? 1 : 0; | 
 | #endif | 
 | } | 
 |  | 
 | int do_open(int ac, char const* const* av) | 
 | { | 
 | #ifdef CMAKE_BOOTSTRAP | 
 |   std::cerr << "This cmake does not support --open\n"; | 
 |   return -1; | 
 | #else | 
 |   std::string dir; | 
 |  | 
 |   enum Doing | 
 |   { | 
 |     DoingNone, | 
 |     DoingDir, | 
 |   }; | 
 |   Doing doing = DoingDir; | 
 |   for (int i = 2; i < ac; ++i) { | 
 |     switch (doing) { | 
 |       case DoingDir: | 
 |         dir = cmSystemTools::CollapseFullPath(av[i]); | 
 |         doing = DoingNone; | 
 |         break; | 
 |       default: | 
 |         std::cerr << "Unknown argument " << av[i] << std::endl; | 
 |         dir.clear(); | 
 |         break; | 
 |     } | 
 |   } | 
 |   if (dir.empty()) { | 
 |     std::cerr << "Usage: cmake --open <dir>\n"; | 
 |     return 1; | 
 |   } | 
 |  | 
 |   cmake cm(cmake::RoleInternal, cmState::Unknown); | 
 |   cmSystemTools::SetMessageCallback( | 
 |     [&cm](const std::string& msg, const cmMessageMetadata& md) { | 
 |       cmakemainMessageCallback(msg, md, &cm); | 
 |     }); | 
 |   cm.SetProgressCallback([&cm](const std::string& msg, float prog) { | 
 |     cmakemainProgressCallback(msg, prog, &cm); | 
 |   }); | 
 |   return cm.Open(dir, false) ? 0 : 1; | 
 | #endif | 
 | } | 
 | } // namespace | 
 |  | 
 | int main(int ac, char const* const* av) | 
 | { | 
 |   cmSystemTools::EnsureStdPipes(); | 
 |  | 
 |   // Replace streambuf so we can output Unicode to console | 
 |   auto consoleBuf = cm::make_unique<cmConsoleBuf>(); | 
 |   consoleBuf->SetUTF8Pipes(); | 
 |  | 
 |   cmsys::Encoding::CommandLineArguments args = | 
 |     cmsys::Encoding::CommandLineArguments::Main(ac, av); | 
 |   ac = args.argc(); | 
 |   av = args.argv(); | 
 |  | 
 |   cmSystemTools::InitializeLibUV(); | 
 |   cmSystemTools::FindCMakeResources(av[0]); | 
 |   if (ac > 1) { | 
 |     if (strcmp(av[1], "--build") == 0) { | 
 |       return do_build(ac, av); | 
 |     } | 
 |     if (strcmp(av[1], "--install") == 0) { | 
 |       return do_install(ac, av); | 
 |     } | 
 |     if (strcmp(av[1], "--open") == 0) { | 
 |       return do_open(ac, av); | 
 |     } | 
 |     if (strcmp(av[1], "-E") == 0) { | 
 |       return do_command(ac, av, std::move(consoleBuf)); | 
 |     } | 
 |   } | 
 |   int ret = do_cmake(ac, av); | 
 | #ifndef CMAKE_BOOTSTRAP | 
 |   cmDynamicLoader::FlushCache(); | 
 | #endif | 
 |   if (uv_loop_t* loop = uv_default_loop()) { | 
 |     uv_loop_close(loop); | 
 |   } | 
 |   return ret; | 
 | } |