| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmConfigure.h" // IWYU pragma: keep | |
| #include <algorithm> | |
| #include <cassert> | |
| #include <chrono> | |
| #include <climits> | |
| #include <cstring> | |
| #include <functional> | |
| #include <iostream> | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| #include <cm/optional> | |
| #include <cmext/algorithm> | |
| #include <cm3p/uv.h> | |
| #include "cmsys/String.h" | |
| #include "cmBuildArgs.h" | |
| #include "cmBuildOptions.h" | |
| #include "cmCommandLineArgument.h" | |
| #include "cmDocumentationEntry.h" | |
| #include "cmGlobalGenerator.h" | |
| #include "cmInstallScriptHandler.h" | |
| #include "cmInstrumentation.h" | |
| #include "cmInstrumentationInterrupt.h" | |
| #include "cmInstrumentationQuery.h" | |
| #include "cmList.h" | |
| #include "cmMakefile.h" | |
| #include "cmMessageMetadata.h" | |
| #include "cmMessenger.h" | |
| #include "cmState.h" | |
| #include "cmStateTypes.h" | |
| #include "cmStdIoConsole.h" | |
| #include "cmStdIoStream.h" | |
| #include "cmStdIoTerminal.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmSystemTools.h" | |
| #include "cmValue.h" | |
| #include "cmake.h" | |
| #include "cmcmd.h" | |
| #ifndef CMAKE_BOOTSTRAP | |
| # include "cmCMakePresetsArgs.h" | |
| # include "cmCMakeSarifLogger.h" | |
| # include "cmDocumentation.h" | |
| #endif | |
| #include "cmsys/Encoding.hxx" | |
| #include "cmsys/RegularExpression.hxx" | |
| namespace { | |
| #ifndef CMAKE_BOOTSTRAP | |
| cmDocumentationEntry const cmDocumentationName = { | |
| {}, | |
| " cmake - Cross-Platform Makefile Generator." | |
| }; | |
| cmDocumentationEntry const cmDocumentationUsage[2] = { | |
| { {}, | |
| " cmake [options] <path-to-source>\n" | |
| " cmake [options] <path-to-existing-build>\n" | |
| " cmake [options] -S <path-to-source> -B <path-to-build>" }, | |
| { {}, | |
| "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." } | |
| }; | |
| cmDocumentationEntry const cmDocumentationUsageNote = { | |
| {}, | |
| "Run 'cmake --help' for more information." | |
| }; | |
| cmDocumentationEntry const cmDocumentationOptions[] = { | |
| { "--preset <preset>,--preset=<preset>", "Specify a configure preset." }, | |
| { "--presets-file <file>,--presets-file=<file>", | |
| "Specify the path to a presets file." }, | |
| { "--list-presets[=<type>]", "List available presets." }, | |
| { "--workflow [<options>]", "Run a workflow preset." }, | |
| { "-E", "CMake command mode. Run \"cmake -E\" for a summary of commands." }, | |
| { "-L[A][H]", "List non-advanced cached variables." }, | |
| { "-LR[A][H] <regex>", "Show cached variables that match the regex." }, | |
| { "--fresh", | |
| "Configure a fresh build tree, removing any existing cache file." }, | |
| { "--build <dir>", | |
| "Build a CMake-generated project binary tree. Run \"cmake --build\" to " | |
| "see compatible options and a quick help." }, | |
| { "--install <dir>", | |
| "Install a CMake-generated project binary tree. Run \"cmake --install\" " | |
| "to see compatible options and a quick help." }, | |
| { "--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." }, | |
| { "--print-config-dir", | |
| "Print CMake config directory for user-wide FileAPI queries." }, | |
| { "--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." }, | |
| { "--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." }, | |
| { "--link-no-warning-as-error", | |
| "Ignore LINK_WARNING_AS_ERROR property and " | |
| "CMAKE_LINK_WARNING_AS_ERROR variable." }, | |
| { "--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." } | |
| }; | |
| cmCMakeSarifLogger cmSarifLogger; | |
| #endif | |
| int do_command(int ac, char const* const* av, | |
| cm::optional<cm::StdIo::Console> console) | |
| { | |
| std::vector<std::string> args; | |
| args.reserve(ac - 1); | |
| args.emplace_back(av[0]); | |
| cm::append(args, av + 2, av + ac); | |
| return cmcmd::ExecuteCMakeCommand(std::move(args), std::move(console)); | |
| } | |
| 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(std::string const& m, | |
| cmMessageMetadata const& md, cmake* cm) | |
| { | |
| Print(cm::StdIo::Err(), md.attrs, m); | |
| std::cerr << cmakemainGetStack(cm) << std::endl; | |
| } | |
| void cmakemainProgressCallback(std::string const& 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; | |
| } | |
| } | |
| std::function<bool(std::string const& value)> getShowCachedCallback( | |
| bool& show_flag, bool* help_flag = nullptr, std::string* filter = nullptr) | |
| { | |
| return [=, &show_flag](std::string const& value) -> bool { | |
| show_flag = true; | |
| if (help_flag) { | |
| *help_flag = true; | |
| } | |
| if (filter) { | |
| *filter = value; | |
| } | |
| return true; | |
| }; | |
| } | |
| int do_cmake(int ac, char const* const* av) | |
| { | |
| if (cmSystemTools::GetLogicalWorkingDirectory().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(cmState::Role::Help); | |
| 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); | |
| doc.PrependSection("Options", cmake::CMAKE_STANDARD_OPTIONS_TABLE); | |
| return !doc.PrintRequestedDocumentation(std::cout); | |
| } | |
| #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; | |
| // (Regex) Filter on the cached variable(s) to print. | |
| std::string filter_var_name; | |
| bool view_only = false; | |
| #ifndef CMAKE_BOOTSTRAP | |
| cm::optional<std::string> sarif_output_path; | |
| #endif | |
| cmState::Role role = cmState::Role::Project; | |
| 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, | |
| getShowCachedCallback(list_all_cached, &list_help) }, | |
| CommandArgument{ "-LA", CommandArgument::Values::Zero, | |
| getShowCachedCallback(list_all_cached) }, | |
| CommandArgument{ "-LH", CommandArgument::Values::Zero, | |
| getShowCachedCallback(list_cached, &list_help) }, | |
| CommandArgument{ "-L", CommandArgument::Values::Zero, | |
| getShowCachedCallback(list_cached) }, | |
| CommandArgument{ | |
| "-LRAH", CommandArgument::Values::One, | |
| getShowCachedCallback(list_all_cached, &list_help, &filter_var_name) }, | |
| CommandArgument{ | |
| "-LRA", CommandArgument::Values::One, | |
| getShowCachedCallback(list_all_cached, nullptr, &filter_var_name) }, | |
| CommandArgument{ | |
| "-LRH", CommandArgument::Values::One, | |
| getShowCachedCallback(list_cached, &list_help, &filter_var_name) }, | |
| CommandArgument{ | |
| "-LR", CommandArgument::Values::One, | |
| getShowCachedCallback(list_cached, nullptr, &filter_var_name) }, | |
| CommandArgument{ "-P", "No script specified for argument -P", | |
| CommandArgument::Values::One, | |
| CommandArgument::RequiresSeparator::No, | |
| [&](std::string const& value) -> bool { | |
| role = cmState::Role::Script; | |
| parsedArgs.emplace_back("-P"); | |
| parsedArgs.push_back(value); | |
| return true; | |
| } }, | |
| CommandArgument{ "--find-package", CommandArgument::Values::Zero, | |
| [&](std::string const&) -> bool { | |
| role = cmState::Role::FindPackage; | |
| parsedArgs.emplace_back("--find-package"); | |
| return true; | |
| } }, | |
| CommandArgument{ "--list-presets", CommandArgument::Values::ZeroOrOne, | |
| [&](std::string const& value) -> bool { | |
| role = cmState::Role::Help; | |
| parsedArgs.emplace_back("--list-presets"); | |
| parsedArgs.emplace_back(value); | |
| return true; | |
| } }, | |
| }; | |
| #ifndef CMAKE_BOOTSTRAP | |
| arguments.emplace_back( | |
| "--sarif-output", "No file specified for --sarif-output", | |
| CommandArgument::Values::One, [&](std::string const& value) -> bool { | |
| sarif_output_path = cmSystemTools::ToNormalizedPathOnDisk(value); | |
| return true; | |
| }); | |
| #endif | |
| 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; | |
| // Only in script mode do we stop parsing instead | |
| // of preferring the last mode flag provided | |
| if (arg == "--" && role == cmState::Role::Script) { | |
| parsedArgs = inputArgs; | |
| break; | |
| } | |
| 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(cmState::Role::Project); | |
| int ret = cm.GetSystemInformation(parsedArgs); | |
| return ret; | |
| } | |
| cmake cm(role); | |
| cmSystemTools::SetMessageCallback( | |
| [&cm](std::string const& msg, cmMessageMetadata const& md) { | |
| cmakemainMessageCallback(msg, md, &cm); | |
| }); | |
| cm.SetProgressCallback([&cm](std::string const& msg, float prog) { | |
| cmakemainProgressCallback(msg, prog, &cm); | |
| }); | |
| 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(); | |
| cmsys::RegularExpression regex_var_name; | |
| if (!filter_var_name.empty()) { | |
| regex_var_name.compile(filter_var_name); | |
| } | |
| for (std::string const& k : keys) { | |
| if (regex_var_name.is_valid() && !regex_var_name.find(k)) { | |
| continue; | |
| } | |
| 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; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| #ifndef CMAKE_BOOTSTRAP | |
| // If SARIF wasn't enabled on the CLI, see if it is enabled for the project. | |
| if (!sarif_output_path && (sarif_output_path = cm.GetProjectSarifFile())) { | |
| // Create parent directories when writing to the default location | |
| cmSystemTools::MakeDirectory( | |
| cmSystemTools::GetFilenamePath(*sarif_output_path)); | |
| } | |
| if (sarif_output_path) { | |
| cmSarifLogger.SetOutputPath(*sarif_output_path); | |
| std::string const& binDir = cm.GetHomeOutputDirectory(); | |
| if (!binDir.empty()) { | |
| cmSarifLogger.AddBaseDirectory("CMAKE_BINARY_DIR", binDir); | |
| } | |
| std::string const& homeDir = cm.GetHomeDirectory(); | |
| if (!homeDir.empty()) { | |
| cmSarifLogger.AddBaseDirectory("CMAKE_SOURCE_DIR", homeDir); | |
| } | |
| cmSarifLogger.RecordDiagnostics(cm.GetMessenger()->GetDisplayedMessages()); | |
| } | |
| #endif | |
| // Always return a non-negative value (except exit code from SCRIPT_MODE). | |
| // Windows tools do not always interpret negative return values as errors. | |
| if (res != 0) { | |
| auto scriptModeExitCode = | |
| cm.HasScriptModeExitCode() ? cm.GetScriptModeExitCode() : 0; | |
| res = scriptModeExitCode ? scriptModeExitCode : 1; | |
| #ifdef CMake_ENABLE_DEBUGGER | |
| cm.StopDebuggerIfNeeded(res); | |
| #endif | |
| return res; | |
| } | |
| #ifdef CMake_ENABLE_DEBUGGER | |
| cm.StopDebuggerIfNeeded(0); | |
| #endif | |
| return 0; | |
| } | |
| #ifndef CMAKE_BOOTSTRAP | |
| int extract_job_number(std::string const& command, | |
| std::string const& jobString) | |
| { | |
| int jobs = cmake::NO_BUILD_PARALLEL_LEVEL; | |
| 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; | |
| } | |
| std::function<bool(std::string const&)> extract_job_number_lambda_builder( | |
| std::string& dir, int& jobs, std::string const& flag) | |
| { | |
| return [&dir, &jobs, flag](std::string const& value) -> bool { | |
| jobs = extract_job_number(flag, value); | |
| if (jobs < 0) { | |
| dir.clear(); | |
| } | |
| return true; | |
| }; | |
| }; | |
| #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 | |
| cmBuildArgs buildArgs; | |
| std::vector<std::string> targets; | |
| std::vector<std::string> nativeOptions; | |
| bool nativeOptionsPassed = false; | |
| bool cleanFirst = false; | |
| bool foundClean = false; | |
| bool foundNonClean = false; | |
| PackageResolveMode resolveMode = PackageResolveMode::Default; | |
| buildArgs.verbose = cmSystemTools::HasEnv("VERBOSE"); | |
| cmCMakePresetsArgs presetsArgs; | |
| auto jLambda = extract_job_number_lambda_builder(buildArgs.binaryDir, | |
| buildArgs.jobs, "-j"); | |
| auto parallelLambda = extract_job_number_lambda_builder( | |
| buildArgs.binaryDir, buildArgs.jobs, "--parallel"); | |
| auto targetLambda = [&](std::string const& value) -> bool { | |
| if (!value.empty()) { | |
| cmList values{ 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(), cmsysString_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 { | |
| buildArgs.verbose = true; | |
| return true; | |
| }; | |
| auto presetFileLambda = [&](std::string const& value) -> bool { | |
| presetsArgs.PresetsFile = cmSystemTools::ToNormalizedPathOnDisk(value); | |
| return true; | |
| }; | |
| using CommandArgument = | |
| cmCommandLineArgument<bool(std::string const& value)>; | |
| std::vector<CommandArgument> arguments = { | |
| CommandArgument{ "--preset", "No preset specified for --preset", | |
| CommandArgument::Values::One, | |
| CommandArgument::setToValue(presetsArgs.PresetName) }, | |
| CommandArgument{ "--presets-file", "No file specified for --presets-file", | |
| CommandArgument::Values::One, presetFileLambda }, | |
| CommandArgument{ "--list-presets", CommandArgument::Values::ZeroOrOne, | |
| [&presetsArgs](std::string const& value) -> bool { | |
| if (presetsArgs.SetListPresets(value)) { | |
| return true; | |
| } | |
| cmSystemTools::Error( | |
| "Invalid value specified for --list-presets.\n" | |
| "The only supported value is \"defined\"."); | |
| return false; | |
| } }, | |
| 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(buildArgs.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; | |
| inputArgs.reserve(ac - 2); | |
| cm::append(inputArgs, av + 2, 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 && i == 0) { | |
| buildArgs.binaryDir = cmSystemTools::ToNormalizedPathOnDisk(arg); | |
| matched = true; | |
| parsed = true; | |
| } | |
| if (!(matched && parsed)) { | |
| buildArgs.binaryDir.clear(); | |
| if (!matched) { | |
| std::string error = cmStrCat("Unknown argument ", arg); | |
| std::string const suggestion = | |
| cmFindClosestCommandLineArgument(arg, arguments); | |
| if (!suggestion.empty()) { | |
| error = | |
| cmStrCat(std::move(error), ". Did you mean: ", suggestion, '?'); | |
| } | |
| std::cerr << error << 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; | |
| buildArgs.binaryDir.clear(); | |
| } | |
| if (buildArgs.jobs == cmake::NO_BUILD_PARALLEL_LEVEL) { | |
| std::string parallel; | |
| if (cmSystemTools::GetEnv("CMAKE_BUILD_PARALLEL_LEVEL", parallel)) { | |
| if (parallel.empty()) { | |
| buildArgs.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"; | |
| buildArgs.binaryDir.clear(); | |
| } else if (numJobs > INT_MAX) { | |
| std::cerr << "The CMAKE_BUILD_PARALLEL_LEVEL environment variable " | |
| "is too large.\n\n"; | |
| buildArgs.binaryDir.clear(); | |
| } else { | |
| buildArgs.jobs = static_cast<int>(numJobs); | |
| } | |
| } else { | |
| std::cerr << "'CMAKE_BUILD_PARALLEL_LEVEL' environment variable\n" | |
| << "invalid number '" << parallel << "' given.\n\n"; | |
| buildArgs.binaryDir.clear(); | |
| } | |
| } | |
| } | |
| } | |
| if (buildArgs.binaryDir.empty() && !presetsArgs.HasPresetsArg()) { | |
| /* 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" | |
| " --presets-file <file>, --presets-file=<file>\n" | |
| " = Specify the path to a presets file.\n" | |
| " --list-presets[=defined]\n" | |
| " = List available or all defined 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" | |
| " -t <tgt>..., --target <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" | |
| " -v, --verbose = 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(cmState::Role::Internal); | |
| cmSystemTools::SetMessageCallback( | |
| [&cm](std::string const& msg, cmMessageMetadata const& md) { | |
| cmakemainMessageCallback(msg, md, &cm); | |
| }); | |
| cm.SetProgressCallback([&cm](std::string const& msg, float prog) { | |
| cmakemainProgressCallback(msg, prog, &cm); | |
| }); | |
| cmBuildOptions buildOptions(cleanFirst, false, resolveMode); | |
| std::vector<std::string> cmd; | |
| cm::append(cmd, av, av + ac); | |
| return cm.Build(buildArgs, std::move(targets), std::move(nativeOptions), | |
| buildOptions, presetsArgs, cmd); | |
| #endif | |
| } | |
| bool parse_default_directory_permissions(std::string const& permissions, | |
| std::string& parsedPermissionsVar) | |
| { | |
| std::vector<std::string> parsedPermissions; | |
| enum Doing | |
| { | |
| DoingNone, | |
| DoingOwner, | |
| DoingGroup, | |
| DoingWorld, | |
| DoingOwnerAssignment, | |
| DoingGroupAssignment, | |
| DoingWorldAssignment, | |
| }; | |
| Doing doing = DoingNone; | |
| auto uniquePushBack = [&parsedPermissions](std::string const& 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; | |
| } | |
| parsedPermissionsVar = cmJoin(parsedPermissions, ";"); | |
| 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::vector<std::string> components; | |
| std::string defaultDirectoryPermissions; | |
| std::string prefix; | |
| std::string dir; | |
| int jobs = 0; | |
| bool strip = false; | |
| bool verbose = cmSystemTools::HasEnv("VERBOSE"); | |
| auto componentLambda = [&components](std::string const& value) -> bool { | |
| if (!value.empty()) { | |
| cmList values{ value }; | |
| for (auto const& v : values) { | |
| components.emplace_back(v); | |
| } | |
| return true; | |
| } | |
| return false; | |
| }; | |
| auto jLambda = extract_job_number_lambda_builder(dir, jobs, "-j"); | |
| auto parallelLambda = | |
| extract_job_number_lambda_builder(dir, jobs, "--parallel"); | |
| 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::OneOrMore, | |
| componentLambda }, | |
| CommandArgument{ | |
| "--default-directory-permissions", CommandArgument::Values::One, | |
| CommandArgument::setToValue(defaultDirectoryPermissions) }, | |
| CommandArgument{ "-j", CommandArgument::Values::One, | |
| CommandArgument::RequiresSeparator::No, jLambda }, | |
| CommandArgument{ "--parallel", CommandArgument::Values::One, | |
| parallelLambda }, | |
| 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::ToNormalizedPathOnDisk(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::string error = cmStrCat("Unknown argument ", arg); | |
| std::string const suggestion = | |
| cmFindClosestCommandLineArgument(arg, arguments); | |
| if (!suggestion.empty()) { | |
| error = | |
| cmStrCat(std::move(error), ". Did you mean: ", suggestion, '?'); | |
| } | |
| std::cerr << error << 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>...\n" | |
| " Component-based install. Only install <comp>.\n" | |
| " --default-directory-permissions <permission> \n" | |
| " Default install permission. Use default permission <permission>.\n" | |
| " -j <jobs> --parallel <jobs>\n" | |
| " Build in parallel using the given number of jobs. \n" | |
| " The CMAKE_INSTALL_PARALLEL_LEVEL environment variable\n" | |
| " specifies a default parallel level when this option is not given.\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; | |
| } | |
| std::vector<std::string> args{ av[0] }; | |
| if (!prefix.empty()) { | |
| args.emplace_back("-DCMAKE_INSTALL_PREFIX=" + prefix); | |
| } | |
| if (strip) { | |
| args.emplace_back("-DCMAKE_INSTALL_DO_STRIP=1"); | |
| } | |
| 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"); | |
| cmInstrumentation instrumentation(dir); | |
| auto handler = cmInstallScriptHandler(dir, components, config, args); | |
| int ret = 0; | |
| if (!jobs && handler.IsParallel()) { | |
| jobs = 1; | |
| auto envvar = cmSystemTools::GetEnvVar("CMAKE_INSTALL_PARALLEL_LEVEL"); | |
| if (envvar.has_value()) { | |
| jobs = extract_job_number("", envvar.value()); | |
| if (jobs < 1) { | |
| std::cerr << "Value of CMAKE_INSTALL_PARALLEL_LEVEL environment" | |
| " variable must be a positive integer.\n"; | |
| return 1; | |
| } | |
| } | |
| } | |
| auto doInstall = [&handler, &verbose, &jobs, &instrumentation]() -> int { | |
| int ret_ = 0; | |
| if (handler.IsParallel()) { | |
| ret_ = handler.Install(jobs, instrumentation); | |
| } else { | |
| for (auto const& script : handler.GetScripts()) { | |
| if (cmInstrumentationInterrupt::PendingInterruptSignal() != 0) { | |
| // Interrupted (e.g. Ctrl+C): launch no further scripts. The script | |
| // currently running executes in-process and finishes on its own; we | |
| // simply stop starting new ones. | |
| break; | |
| } | |
| std::vector<std::string> cmd = script.command; | |
| cmake cm(cmState::Role::Script); | |
| cmSystemTools::SetMessageCallback( | |
| [&cm](std::string const& msg, cmMessageMetadata const& md) { | |
| cmakemainMessageCallback(msg, md, &cm); | |
| }); | |
| cm.SetProgressCallback([&cm](std::string const& msg, float prog) { | |
| cmakemainProgressCallback(msg, prog, &cm); | |
| }); | |
| cm.SetDebugOutputOn(verbose); | |
| ret_ = int(bool(cm.Run(cmd))); | |
| if (ret_ != 0) { | |
| // Serial install is fail-fast: stop at the first failed script | |
| // instead of attempting the remaining components or configs. | |
| break; | |
| } | |
| } | |
| } | |
| if (cmInstrumentationInterrupt::PendingInterruptSignal() != 0) { | |
| // Any caught interrupt makes the install unsuccessful even if the work | |
| // that did run happened to succeed. Windows has no signal to re-raise, | |
| // so this is what forces a non-zero exit status there; on POSIX it also | |
| // keeps the snippet `result` consistent with the re-raised signal. | |
| ret_ = 1; | |
| } | |
| return int(ret_ > 0); | |
| }; | |
| std::vector<std::string> cmd; | |
| cm::append(cmd, av, av + ac); | |
| // Run the install under an interrupt handler so that a user interrupt (e.g. | |
| // Ctrl+C) still writes the overall `cmakeInstall` snippet before we exit. | |
| cmInstrumentationInterrupt::InterruptOutcome installOutcome = | |
| cmInstrumentationInterrupt::HandleInterrupt( | |
| instrumentation.HasQuery(), | |
| [&instrumentation, &cmd, &doInstall]() -> int { | |
| return instrumentation.InstrumentCommand( | |
| "cmakeInstall", cmd, | |
| [&doInstall]() -> cmInstrumentation::CommandResult { | |
| return { doInstall(), cm::nullopt, cm::nullopt, cm::nullopt }; | |
| }); | |
| }); | |
| ret = installOutcome.ExitCode; | |
| if (installOutcome.Interrupted) { | |
| // The install was interrupted and its snippet has been written. Skip the | |
| // post-install indexing hook (which would run callbacks and delete data). | |
| // For a real OS interrupt, re-raise so the exit status reflects it; for a | |
| // test-injected interrupt, exit cleanly. | |
| if (installOutcome.ShouldRaise) { | |
| cmInstrumentationInterrupt::RaiseInterrupt(installOutcome.Signal); | |
| } | |
| return ret; | |
| } | |
| instrumentation.CollectTimingData( | |
| cmInstrumentationQuery::Hook::PostCMakeInstall); | |
| return ret; | |
| #endif | |
| } | |
| int do_workflow(int ac, char const* const* av) | |
| { | |
| #ifdef CMAKE_BOOTSTRAP | |
| std::cerr << "This cmake does not support --workflow\n"; | |
| return -1; | |
| #else | |
| cmCMakePresetsWorkflowArgs presetsArgs; | |
| using CommandArgument = | |
| cmCommandLineArgument<bool(std::string const& value)>; | |
| std::vector<CommandArgument> arguments = { | |
| CommandArgument{ "--preset", "No preset specified for --preset", | |
| CommandArgument::Values::One, | |
| CommandArgument::setToValue(presetsArgs.PresetName) }, | |
| CommandArgument{ "--presets-file", "No file specified for --presets-file", | |
| CommandArgument::Values::One, | |
| [&presetsArgs](std::string const& value) -> bool { | |
| presetsArgs.PresetsFile = | |
| cmSystemTools::ToNormalizedPathOnDisk(value); | |
| return true; | |
| } }, | |
| CommandArgument{ "--list-presets", CommandArgument::Values::ZeroOrOne, | |
| [&presetsArgs](std::string const& value) -> bool { | |
| if (presetsArgs.SetListPresets(value)) { | |
| return true; | |
| } | |
| std::cerr | |
| << "Invalid value specified for --list-presets.\n" | |
| "The only supported value in workflow mode is " | |
| "\"defined\".\n"; | |
| return false; | |
| } }, | |
| CommandArgument{ "--fresh", CommandArgument::Values::Zero, | |
| CommandArgument::setToTrue(presetsArgs.Fresh) } | |
| }; | |
| std::vector<std::string> inputArgs; | |
| inputArgs.reserve(ac - 2); | |
| cm::append(inputArgs, av + 2, av + ac); | |
| decltype(inputArgs.size()) i = 0; | |
| for (; 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 && i == 0) { | |
| inputArgs.insert(inputArgs.begin(), "--preset"); | |
| matched = true; | |
| parsed = arguments[0].parse("--preset", i, inputArgs); | |
| } | |
| if (!(matched && parsed)) { | |
| if (!matched) { | |
| presetsArgs.Clear(); | |
| std::string error = cmStrCat("Unknown argument ", arg); | |
| std::string const suggestion = | |
| cmFindClosestCommandLineArgument(arg, arguments); | |
| if (!suggestion.empty()) { | |
| error = | |
| cmStrCat(std::move(error), ". Did you mean: ", suggestion, '?'); | |
| } | |
| std::cerr << error << std::endl; | |
| } | |
| break; | |
| } | |
| } | |
| if (!presetsArgs.HasPresetsArg()) { | |
| /* clang-format off */ | |
| std::cerr << | |
| "Usage: cmake --workflow <options>\n" | |
| "Options:\n" | |
| " --preset <preset> = Workflow preset to execute.\n" | |
| " --presets-file <file> = Path to a presets file.\n" | |
| " --list-presets[=defined] = List available or all defined workflow " | |
| "presets.\n" | |
| " --fresh = Configure a fresh build tree, removing " | |
| "any existing cache file.\n" | |
| ; | |
| /* clang-format on */ | |
| return 1; | |
| } | |
| cmake cm(cmState::Role::Internal); | |
| cmSystemTools::SetMessageCallback( | |
| [&cm](std::string const& msg, cmMessageMetadata const& md) { | |
| cmakemainMessageCallback(msg, md, &cm); | |
| }); | |
| cm.SetProgressCallback([&cm](std::string const& msg, float prog) { | |
| cmakemainProgressCallback(msg, prog, &cm); | |
| }); | |
| return cm.Workflow(presetsArgs); | |
| #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::ToNormalizedPathOnDisk(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(cmState::Role::Internal); | |
| cmSystemTools::SetMessageCallback( | |
| [&cm](std::string const& msg, cmMessageMetadata const& md) { | |
| cmakemainMessageCallback(msg, md, &cm); | |
| }); | |
| cm.SetProgressCallback([&cm](std::string const& msg, float prog) { | |
| cmakemainProgressCallback(msg, prog, &cm); | |
| }); | |
| return cm.Open(dir, cmake::DryRun::No) ? 0 : 1; | |
| #endif | |
| } | |
| } // namespace | |
| int main(int ac, char const* const* av) | |
| { | |
| #ifndef CMAKE_BOOTSTRAP | |
| std::chrono::system_clock::time_point wall_start_time = | |
| std::chrono::system_clock::now(); | |
| #endif | |
| cm::optional<cm::StdIo::Console> console = cm::StdIo::Console(); | |
| cmsys::Encoding::CommandLineArguments args = | |
| cmsys::Encoding::CommandLineArguments::Main(ac, av); | |
| ac = args.argc(); | |
| av = args.argv(); | |
| // Dispatch "cmake -E" and "--print-config-dir" before the start-up | |
| // work below, which they do not need. cmcmd does that work itself | |
| // for the "-E" commands that do. | |
| if (ac > 1) { | |
| if (strcmp(av[1], "-E") == 0) { | |
| return do_command(ac, av, std::move(console)); | |
| } | |
| if (strcmp(av[1], "--print-config-dir") == 0) { | |
| std::cout << cmSystemTools::ConvertToOutputPath( | |
| cmSystemTools::GetCMakeConfigDirectory().value_or( | |
| std::string())) | |
| << std::endl; | |
| return 0; | |
| } | |
| } | |
| cmSystemTools::InitializeCMake(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], "--workflow") == 0) { | |
| return do_workflow(ac, av); | |
| } | |
| } | |
| int ret = do_cmake(ac, av); | |
| if (uv_loop_t* loop = uv_default_loop()) { | |
| uv_loop_close(loop); | |
| } | |
| #ifndef CMAKE_BOOTSTRAP | |
| cmSarifLogger.RecordInvocation(ac, av, ret, wall_start_time, | |
| std::chrono::system_clock::now()); | |
| #endif | |
| return ret; | |
| } |