diff options
Diffstat (limited to 'Source/cmakemain.cxx')
-rw-r--r-- | Source/cmakemain.cxx | 556 |
1 files changed, 556 insertions, 0 deletions
diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx new file mode 100644 index 0000000..09068f8 --- /dev/null +++ b/Source/cmakemain.cxx @@ -0,0 +1,556 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmAlgorithms.h" +#include "cmDocumentationEntry.h" // IWYU pragma: keep +#include "cmGlobalGenerator.h" +#include "cmMakefile.h" +#include "cmState.h" +#include "cmStateTypes.h" +#include "cmSystemTools.h" +#include "cmake.h" +#include "cmcmd.h" + +#ifdef CMAKE_BUILD_WITH_CMAKE +# include "cmDocumentation.h" +# include "cmDynamicLoader.h" +#endif + +#include "cm_uv.h" + +#include "cmsys/Encoding.hxx" +#if defined(_WIN32) && defined(CMAKE_BUILD_WITH_CMAKE) +# include "cmsys/ConsoleBuf.hxx" +#endif + +#include <ctype.h> +#include <iostream> +#include <string.h> +#include <string> +#include <vector> + +#ifdef CMAKE_BUILD_WITH_CMAKE +static const char* cmDocumentationName[][2] = { + { nullptr, " cmake - Cross-Platform Makefile Generator." }, + { nullptr, nullptr } +}; + +static 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 } +}; + +static const char* cmDocumentationUsageNote[][2] = { + { nullptr, "Run 'cmake --help' for more information." }, + { nullptr, nullptr } +}; + +# define CMAKE_BUILD_OPTIONS \ + " <dir> = Project binary directory to be built.\n" \ + " -j [<jobs>] --parallel [<jobs>] = Build in parallel using\n" \ + " the given number of jobs. If <jobs> is omitted\n" \ + " the native build tool's 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> = Build <tgt> instead of default targets.\n" \ + " May only be specified once.\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" \ + " --use-stderr = Ignored. Behavior is default in CMake >= 3.0.\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" + +static const char* cmDocumentationOptions[][2] = { + CMAKE_STANDARD_OPTIONS_TABLE, + { "-E", "CMake command mode." }, + { "-L[A][H]", "List non-advanced cached variables." }, + { "--build <dir>", "Build 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", "Run in pkg-config like mode." }, + { "--graphviz=[file]", + "Generate graphviz of dependencies, see " + "CMakeGraphVizOptions.cmake for more." }, + { "--system-information [file]", "Dump information about this system." }, + { "--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." }, + { "--trace", "Put cmake in trace mode." }, + { "--trace-expand", "Put cmake in trace mode with variable expansion." }, + { "--trace-source=<file>", + "Trace only this CMake file/module. Multiple options allowed." }, + { "--warn-uninitialized", "Warn about uninitialized values." }, + { "--warn-unused-vars", "Warn about unused variables." }, + { "--no-warn-unused-cli", "Don't warn about command line options." }, + { "--check-system-vars", + "Find problems with variable usage in system " + "files." }, + { nullptr, nullptr } +}; + +#endif + +static int do_command(int ac, char const* const* av) +{ + std::vector<std::string> args; + args.reserve(ac - 1); + args.emplace_back(av[0]); + args.insert(args.end(), av + 2, av + ac); + return cmcmd::ExecuteCMakeCommand(args); +} + +int do_cmake(int ac, char const* const* av); +static int do_build(int ac, char const* const* av); +static int do_open(int ac, char const* const* av); + +static cmMakefile* cmakemainGetMakefile(cmake* cm) +{ + if (cm && cm->GetDebugOutput()) { + cmGlobalGenerator* gg = cm->GetGlobalGenerator(); + if (gg) { + return gg->GetCurrentMakefile(); + } + } + return nullptr; +} + +static 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; +} + +static void cmakemainMessageCallback(const std::string& m, + const char* /*unused*/, cmake* cm) +{ + std::cerr << m << cmakemainGetStack(cm) << std::endl << std::flush; +} + +static 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 = " "; + dir += mf->GetCurrentSourceDirectory(); + } else if (mf && cmHasLiteralPrefix(m, "Generating")) { + dir = " "; + dir += mf->GetCurrentBinaryDirectory(); + } + + if ((prog < 0) || (!dir.empty())) { + std::cout << "-- " << m << dir << cmakemainGetStack(cm) << std::endl; + } + + std::cout.flush(); +} + +int main(int ac, char const* const* av) +{ +#if defined(_WIN32) && defined(CMAKE_BUILD_WITH_CMAKE) + // Replace streambuf so we can output Unicode to console + cmsys::ConsoleBuf::Manager consoleOut(std::cout); + consoleOut.SetUTF8Pipes(); + cmsys::ConsoleBuf::Manager consoleErr(std::cerr, true); + consoleErr.SetUTF8Pipes(); +#endif + cmsys::Encoding::CommandLineArguments args = + cmsys::Encoding::CommandLineArguments::Main(ac, av); + ac = args.argc(); + av = args.argv(); + + cmSystemTools::EnableMSVCDebugHook(); + cmSystemTools::InitializeLibUV(); + cmSystemTools::FindCMakeResources(av[0]); + if (ac > 1) { + if (strcmp(av[1], "--build") == 0) { + return do_build(ac, av); + } + if (strcmp(av[1], "--open") == 0) { + return do_open(ac, av); + } + if (strcmp(av[1], "-E") == 0) { + return do_command(ac, av); + } + } + int ret = do_cmake(ac, av); +#ifdef CMAKE_BUILD_WITH_CMAKE + cmDynamicLoader::FlushCache(); +#endif + uv_loop_close(uv_default_loop()); + return ret; +} + +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; + } + +#ifdef CMAKE_BUILD_WITH_CMAKE + 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 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> args; + for (int i = 0; i < ac; ++i) { + if (strcmp(av[i], "-i") == 0) { + /* 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 */ + return 1; + } + if (strcmp(av[i], "--system-information") == 0) { + sysinfo = true; + } else if (strcmp(av[i], "-N") == 0) { + view_only = true; + } else if (strcmp(av[i], "-L") == 0) { + list_cached = true; + } else if (strcmp(av[i], "-LA") == 0) { + list_all_cached = true; + } else if (strcmp(av[i], "-LH") == 0) { + list_cached = true; + list_help = true; + } else if (strcmp(av[i], "-LAH") == 0) { + list_all_cached = true; + list_help = true; + } else if (cmHasLiteralPrefix(av[i], "-P")) { + if (i == ac - 1) { + cmSystemTools::Error("No script specified for argument -P"); + return 1; + } + workingMode = cmake::SCRIPT_MODE; + args.emplace_back(av[i]); + i++; + args.emplace_back(av[i]); + } else if (cmHasLiteralPrefix(av[i], "--find-package")) { + workingMode = cmake::FIND_PACKAGE_MODE; + args.emplace_back(av[i]); + } else { + args.emplace_back(av[i]); + } + } + if (sysinfo) { + cmake cm(cmake::RoleProject, cmState::Project); + cm.SetHomeDirectory(""); + cm.SetHomeOutputDirectory(""); + int ret = cm.GetSystemInformation(args); + 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: + 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 char* title) { + cmakemainMessageCallback(msg, title, &cm); + }); + cm.SetProgressCallback([&cm](const std::string& msg, float prog) { + cmakemainProgressCallback(msg, prog, &cm); + }); + cm.SetWorkingMode(workingMode); + + int res = cm.Run(args, 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) { + const char* advancedProp = + cm.GetState()->GetCacheEntryProperty(k, "ADVANCED"); + if (list_all_cached || !advancedProp) { + if (list_help) { + std::cout << "// " + << cm.GetState()->GetCacheEntryProperty(k, "HELPSTRING") + << std::endl; + } + std::cout << k << ":" << cmState::CacheEntryTypeToString(t) << "=" + << cm.GetState()->GetCacheEntryValue(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; +} + +namespace { +int extract_job_number(int& index, char const* current, char const* next, + int len_of_flag) +{ + std::string command(current); + std::string jobString = command.substr(len_of_flag); + if (jobString.empty() && next && isdigit(next[0])) { + ++index; // skip parsing the job number + jobString = std::string(next); + } + + int jobs = -1; + unsigned long numJobs = 0; + if (jobString.empty()) { + jobs = cmake::DEFAULT_BUILD_PARALLEL_LEVEL; + } else if (cmSystemTools::StringToULong(jobString.c_str(), &numJobs)) { + jobs = int(numJobs); + } else { + std::cerr << "'" << command.substr(0, len_of_flag) << "' invalid number '" + << jobString << "' given.\n\n"; + } + return jobs; +} +} + +static int do_build(int ac, char const* const* av) +{ +#ifndef CMAKE_BUILD_WITH_CMAKE + std::cerr << "This cmake does not support --build\n"; + return -1; +#else + int jobs = cmake::NO_BUILD_PARALLEL_LEVEL; + std::string target; + std::string config = "Debug"; + std::string dir; + std::vector<std::string> nativeOptions; + bool clean = false; + bool verbose = cmSystemTools::HasEnv("VERBOSE"); + bool hasTarget = false; + + enum Doing + { + DoingNone, + DoingDir, + DoingTarget, + DoingConfig, + DoingNative + }; + Doing doing = DoingDir; + for (int i = 2; i < ac; ++i) { + if (doing == DoingNative) { + nativeOptions.emplace_back(av[i]); + } else if (cmHasLiteralPrefix(av[i], "-j")) { + const char* nextArg = ((i + 1 < ac) ? av[i + 1] : nullptr); + jobs = extract_job_number(i, av[i], nextArg, sizeof("-j") - 1); + if (jobs < 0) { + dir.clear(); + } + } else if (cmHasLiteralPrefix(av[i], "--parallel")) { + const char* nextArg = ((i + 1 < ac) ? av[i + 1] : nullptr); + jobs = extract_job_number(i, av[i], nextArg, sizeof("--parallel") - 1); + if (jobs < 0) { + dir.clear(); + } + } else if (strcmp(av[i], "--target") == 0) { + if (!hasTarget) { + doing = DoingTarget; + hasTarget = true; + } else { + std::cerr << "'--target' may not be specified more than once.\n\n"; + dir.clear(); + break; + } + } else if (strcmp(av[i], "--config") == 0) { + doing = DoingConfig; + } else if (strcmp(av[i], "--clean-first") == 0) { + clean = true; + doing = DoingNone; + } else if ((strcmp(av[i], "--verbose") == 0) || + (strcmp(av[i], "-v") == 0)) { + verbose = true; + doing = DoingNone; + } else if (strcmp(av[i], "--use-stderr") == 0) { + /* tolerate legacy option */ + } else if (strcmp(av[i], "--") == 0) { + doing = DoingNative; + } else { + switch (doing) { + case DoingDir: + dir = cmSystemTools::CollapseFullPath(av[i]); + doing = DoingNone; + break; + case DoingTarget: + target = av[i]; + doing = DoingNone; + break; + case DoingConfig: + config = av[i]; + doing = DoingNone; + break; + default: + std::cerr << "Unknown argument " << av[i] << std::endl; + dir.clear(); + break; + } + } + } + + 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 (cmSystemTools::StringToULong(parallel.c_str(), &numJobs)) { + jobs = int(numJobs); + } else { + std::cerr << "'CMAKE_BUILD_PARALLEL_LEVEL' environment variable\n" + << "invalid number '" << parallel << "' given.\n\n"; + dir.clear(); + } + } + } + } + + if (dir.empty()) { + /* clang-format off */ + std::cerr << + "Usage: cmake --build <dir> [options] [-- [native-options]]\n" + "Options:\n" + CMAKE_BUILD_OPTIONS + ; + /* clang-format on */ + return 1; + } + + cmake cm(cmake::RoleInternal, cmState::Unknown); + cmSystemTools::SetMessageCallback( + [&cm](const std::string& msg, const char* title) { + cmakemainMessageCallback(msg, title, &cm); + }); + cm.SetProgressCallback([&cm](const std::string& msg, float prog) { + cmakemainProgressCallback(msg, prog, &cm); + }); + return cm.Build(jobs, dir, target, config, nativeOptions, clean, verbose); +#endif +} + +static int do_open(int ac, char const* const* av) +{ +#ifndef CMAKE_BUILD_WITH_CMAKE + 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 char* title) { + cmakemainMessageCallback(msg, title, &cm); + }); + cm.SetProgressCallback([&cm](const std::string& msg, float prog) { + cmakemainProgressCallback(msg, prog, &cm); + }); + return cm.Open(dir, false) ? 0 : 1; +#endif +} |