cmFileAPICommand.cxx 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file LICENSE.rst or https://cmake.org/licensing for details. */
  3. #include "cmFileAPICommand.h"
  4. #include <algorithm>
  5. #include <array>
  6. #include <cctype>
  7. #include <cstdlib>
  8. #include <utility>
  9. #include <cm/string_view>
  10. #include <cmext/string_view>
  11. #include "cmArgumentParser.h"
  12. #include "cmArgumentParserTypes.h"
  13. #include "cmExecutionStatus.h"
  14. #include "cmFileAPI.h"
  15. #include "cmMakefile.h"
  16. #include "cmRange.h"
  17. #include "cmStringAlgorithms.h"
  18. #include "cmSubcommandTable.h"
  19. #include "cmake.h"
  20. namespace {
  21. bool isCharDigit(char ch)
  22. {
  23. return std::isdigit(static_cast<unsigned char>(ch));
  24. }
  25. std::string processObjectKindVersions(cmFileAPI& fileApi,
  26. cmFileAPI::ObjectKind objectKind,
  27. cm::string_view keyword,
  28. std::vector<std::string> const& versions)
  29. {
  30. // The "versions" vector is empty only when the keyword was not present.
  31. // It is an error to provide the keyword with no versions after it, and that
  32. // is enforced by the argument parser before we get here.
  33. if (versions.empty()) {
  34. return {};
  35. }
  36. // The first supported version listed is what we use
  37. for (std::string const& ver : versions) {
  38. char const* vStart = ver.c_str();
  39. int majorVersion = std::atoi(vStart);
  40. int minorVersion = 0;
  41. std::string::size_type pos = ver.find('.');
  42. if (pos != std::string::npos) {
  43. vStart += pos + 1;
  44. minorVersion = std::atoi(vStart);
  45. }
  46. if (majorVersion < 1 || minorVersion < 0) {
  47. return cmStrCat("Given a malformed version \"", ver, "\" for ", keyword,
  48. '.');
  49. }
  50. if (fileApi.AddProjectQuery(objectKind,
  51. static_cast<unsigned>(majorVersion),
  52. static_cast<unsigned>(minorVersion))) {
  53. return {};
  54. }
  55. }
  56. return cmStrCat("None of the specified ", keyword,
  57. " versions is supported by this version of CMake.");
  58. }
  59. bool handleQueryCommand(std::vector<std::string> const& args,
  60. cmExecutionStatus& status)
  61. {
  62. if (args.empty()) {
  63. status.SetError("QUERY subcommand called without required arguments.");
  64. return false;
  65. }
  66. struct Arguments : public ArgumentParser::ParseResult
  67. {
  68. ArgumentParser::NonEmpty<std::string> ApiVersion;
  69. ArgumentParser::NonEmpty<std::vector<std::string>> CodeModelVersions;
  70. ArgumentParser::NonEmpty<std::vector<std::string>> CacheVersions;
  71. ArgumentParser::NonEmpty<std::vector<std::string>> CMakeFilesVersions;
  72. ArgumentParser::NonEmpty<std::vector<std::string>> ToolchainsVersions;
  73. };
  74. static auto const parser =
  75. cmArgumentParser<Arguments>{}
  76. .Bind("API_VERSION"_s, &Arguments::ApiVersion)
  77. .Bind("CODEMODEL"_s, &Arguments::CodeModelVersions)
  78. .Bind("CACHE"_s, &Arguments::CacheVersions)
  79. .Bind("CMAKEFILES"_s, &Arguments::CMakeFilesVersions)
  80. .Bind("TOOLCHAINS"_s, &Arguments::ToolchainsVersions);
  81. std::vector<std::string> unparsedArguments;
  82. Arguments const arguments =
  83. parser.Parse(cmMakeRange(args).advance(1), &unparsedArguments);
  84. if (arguments.MaybeReportError(status.GetMakefile())) {
  85. return true;
  86. }
  87. if (!unparsedArguments.empty()) {
  88. status.SetError("QUERY subcommand given unknown argument \"" +
  89. unparsedArguments.front() + "\".");
  90. return false;
  91. }
  92. if (!std::all_of(arguments.ApiVersion.begin(), arguments.ApiVersion.end(),
  93. isCharDigit)) {
  94. status.SetError("QUERY subcommand given a non-integer API_VERSION.");
  95. return false;
  96. }
  97. int const apiVersion = std::atoi(arguments.ApiVersion.c_str());
  98. if (apiVersion != 1) {
  99. status.SetError(
  100. cmStrCat("QUERY subcommand given an unsupported API_VERSION \"",
  101. arguments.ApiVersion,
  102. "\" (the only currently supported version is 1)."));
  103. return false;
  104. }
  105. cmMakefile& mf = status.GetMakefile();
  106. cmake* cmi = mf.GetCMakeInstance();
  107. cmFileAPI* fileApi = cmi->GetFileAPI();
  108. // We want to check all keywords and report all errors, not just the first.
  109. // Record each result rather than short-circuiting on the first error.
  110. // NOTE: Double braces are needed here for compilers that don't implement the
  111. // CWG 1270 revision to C++11.
  112. std::array<std::string, 4> errors{
  113. { processObjectKindVersions(*fileApi, cmFileAPI::ObjectKind::CodeModel,
  114. "CODEMODEL"_s, arguments.CodeModelVersions),
  115. processObjectKindVersions(*fileApi, cmFileAPI::ObjectKind::Cache,
  116. "CACHE"_s, arguments.CacheVersions),
  117. processObjectKindVersions(*fileApi, cmFileAPI::ObjectKind::CMakeFiles,
  118. "CMAKEFILES"_s, arguments.CMakeFilesVersions),
  119. processObjectKindVersions(*fileApi, cmFileAPI::ObjectKind::Toolchains,
  120. "TOOLCHAINS"_s, arguments.ToolchainsVersions) }
  121. };
  122. if (!std::all_of(errors.begin(), errors.end(),
  123. [](std::string const& s) -> bool { return s.empty(); })) {
  124. std::string message("QUERY subcommand was given invalid arguments:");
  125. for (std::string const& s : errors) {
  126. if (!s.empty()) {
  127. message = cmStrCat(message, "\n ", s);
  128. }
  129. }
  130. status.SetError(message);
  131. return false;
  132. }
  133. return true;
  134. }
  135. }
  136. bool cmFileAPICommand(std::vector<std::string> const& args,
  137. cmExecutionStatus& status)
  138. {
  139. if (args.empty()) {
  140. status.SetError("must be called with arguments.");
  141. return false;
  142. }
  143. // clang-format off
  144. static cmSubcommandTable const subcommand{
  145. { "QUERY"_s, handleQueryCommand }
  146. };
  147. // clang-format on
  148. return subcommand(args[0], args, status);
  149. }