/* 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cmBuildOptions.h" #include "cmCommandLineArgument.h" #include "cmConsoleBuf.h" #include "cmDocumentationEntry.h" #include "cmGlobalGenerator.h" #include "cmInstallScriptHandler.h" #include "cmList.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/RegularExpression.hxx" #include "cmsys/Terminal.h" namespace { #ifndef CMAKE_BOOTSTRAP const cmDocumentationEntry cmDocumentationName = { {}, " cmake - Cross-Platform Makefile Generator." }; const cmDocumentationEntry cmDocumentationUsage[2] = { { {}, " cmake [options] \n" " cmake [options] \n" " cmake [options] -S -B " }, { {}, "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." } }; const cmDocumentationEntry cmDocumentationUsageNote = { {}, "Run 'cmake --help' for more information." }; const cmDocumentationEntry cmDocumentationOptions[34] = { { "--preset ,--preset=", "Specify a configure preset." }, { "--list-presets[=]", "List available presets." }, { "--workflow []", "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] ", "Show cached variables that match the regex." }, { "--fresh", "Configure a fresh build tree, removing any existing cache file." }, { "--build ", "Build a CMake-generated project binary tree. Run \"cmake --build\" to " "see compatible options and a quick help." }, { "--install ", "Install a CMake-generated project binary tree. Run \"cmake --install\" " "to see compatible options and a quick help." }, { "--open ", "Open generated project in the associated application." }, { "-N", "View mode only." }, { "-P ", "Process script mode." }, { "--find-package", "Legacy pkg-config like mode. Do not use." }, { "--graphviz=", "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=", "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=[,...]", "Limit cmake debug-find to the comma-separated list of packages" }, { "--debug-find-var=[,...]", "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=", "Set the output format of the trace." }, { "--trace-source=", "Trace only this CMake file/module. Multiple options allowed." }, { "--trace-redirect=", "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." }, { "--profiling-format=", "Output data for profiling CMake scripts. Supported formats: " "google-trace" }, { "--profiling-output=", "Select an output path for the profiling data enabled through " "--profiling-format." } }; #endif int do_command(int ac, char const* const* av, std::unique_ptr consoleBuf) { std::vector 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(md); std::cerr << m << cmakemainGetStack(cm) << std::endl; #else cmsysTerminal_cfprintf(md.desiredColor, stderr, "%s", m.c_str()); fflush(stderr); // stderr is buffered in some cases. std::cerr << cmakemainGetStack(cm) << std::endl; #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; } } std::function 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::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::Help); 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 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; cmake::WorkingMode workingMode = cmake::NORMAL_MODE; std::vector parsedArgs; using CommandArgument = cmCommandLineArgument; std::vector 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 { 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 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 == "--" && workingMode == cmake::SCRIPT_MODE) { 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(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 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; } } } } } // 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 = -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 value requires a positive integer argument.\n\n"; } else if (numJobs > INT_MAX) { std::cerr << "The value is too large.\n\n"; } else { jobs = static_cast(numJobs); } } else { std::cerr << "'" << command << "' invalid number '" << jobString << "' given.\n\n"; } return jobs; } std::function extract_job_number_lambda_builder( std::string& dir, int& jobs, const std::string& 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 int jobs = cmake::NO_BUILD_PARALLEL_LEVEL; std::vector targets; std::string config; std::string dir; std::vector 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 = extract_job_number_lambda_builder(dir, jobs, "-j"); auto parallelLambda = extract_job_number_lambda_builder(dir, 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(), ::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; std::vector 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 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) { dir = cmSystemTools::CollapseFullPath(arg); matched = true; parsed = true; } 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(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 " " [options] [-- [native-options]]\n" " cmake --build --preset " " [options] [-- [native-options]]\n" "Options:\n" " = Project binary directory to be built.\n" " --preset , --preset=\n" " = Specify a build preset.\n" " --list-presets[=]\n" " = List available build presets.\n" " --parallel [], -j []\n" " = Build in parallel using the given number of jobs. \n" " If 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 ..., --target ...\n" " = Build instead of default targets.\n" " --config = For multi-configuration tools, choose .\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(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 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; int jobs = 0; bool strip = false; bool verbose = cmSystemTools::HasEnv("VERBOSE"); 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; std::vector 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{ "-j", CommandArgument::Values::One, 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::CollapseFullPath(av[2]); std::vector 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 [options]\n" "Options:\n" " = Project binary directory to install.\n" " --config = For multi-configuration tools, choose .\n" " --component = Component-based install. Only install .\n" " --default-directory-permissions \n" " Default install permission. Use default permission .\n" " -j --parallel \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 = 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 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"); auto handler = cmInstallScriptHandler(dir, component, args); int ret = 0; if (!handler.isParallel()) { args.emplace_back(cmStrCat(dir, "/cmake_install.cmake")); ret = int(bool(cm.Run(args))); } else { if (!jobs) { 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; } } } ret = handler.install(jobs); } return int(ret > 0); #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 using WorkflowListPresets = cmake::WorkflowListPresets; using WorkflowFresh = cmake::WorkflowFresh; std::string presetName; auto listPresets = WorkflowListPresets::No; auto fresh = WorkflowFresh::No; using CommandArgument = cmCommandLineArgument; std::vector arguments = { CommandArgument{ "--preset", CommandArgument::Values::One, CommandArgument::setToValue(presetName) }, CommandArgument{ "--list-presets", CommandArgument::Values::Zero, [&listPresets](const std::string&) -> bool { listPresets = WorkflowListPresets::Yes; return true; } }, CommandArgument{ "--fresh", CommandArgument::Values::Zero, [&fresh](const std::string&) -> bool { fresh = WorkflowFresh::Yes; return true; } }, }; std::vector 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) { presetName.clear(); listPresets = WorkflowListPresets::No; std::cerr << "Unknown argument " << arg << std::endl; } break; } } if (presetName.empty() && listPresets == WorkflowListPresets::No) { /* clang-format off */ std::cerr << "Usage: cmake --workflow \n" "Options:\n" " --preset = Workflow preset to execute.\n" " --list-presets = List available workflow presets.\n" " --fresh = Configure a fresh build tree, removing any " "existing cache file.\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); }); return cm.Workflow(presetName, listPresets, fresh); #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 \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(); 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], "--workflow") == 0) { return do_workflow(ac, av); } if (strcmp(av[1], "-E") == 0) { return do_command(ac, av, std::move(consoleBuf)); } if (strcmp(av[1], "--print-config-dir") == 0) { std::cout << cmSystemTools::ConvertToOutputPath( cmSystemTools::GetCMakeConfigDirectory().value_or( std::string())) << std::endl; return 0; } } 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; }