| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmFileAPICommand.h" | |
| #include <algorithm> | |
| #include <array> | |
| #include <cstdlib> | |
| #include <utility> | |
| #include <cm/string_view> | |
| #include <cmext/string_view> | |
| #include "cmsys/String.h" | |
| #include "cmArgumentParser.h" | |
| #include "cmArgumentParserTypes.h" | |
| #include "cmExecutionStatus.h" | |
| #include "cmFileAPI.h" | |
| #include "cmMakefile.h" | |
| #include "cmRange.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmSubcommandTable.h" | |
| #include "cmake.h" | |
| namespace { | |
| std::string processObjectKindVersions(cmFileAPI& fileApi, | |
| cmFileAPI::ObjectKind objectKind, | |
| cm::string_view keyword, | |
| std::vector<std::string> const& versions) | |
| { | |
| // The "versions" vector is empty only when the keyword was not present. | |
| // It is an error to provide the keyword with no versions after it, and that | |
| // is enforced by the argument parser before we get here. | |
| if (versions.empty()) { | |
| return {}; | |
| } | |
| // The first supported version listed is what we use | |
| for (std::string const& ver : versions) { | |
| char const* vStart = ver.c_str(); | |
| int majorVersion = std::atoi(vStart); | |
| int minorVersion = 0; | |
| std::string::size_type pos = ver.find('.'); | |
| if (pos != std::string::npos) { | |
| vStart += pos + 1; | |
| minorVersion = std::atoi(vStart); | |
| } | |
| if (majorVersion < 1 || minorVersion < 0) { | |
| return cmStrCat("Given a malformed version \"", ver, "\" for ", keyword, | |
| '.'); | |
| } | |
| if (fileApi.AddProjectQuery(objectKind, | |
| static_cast<unsigned>(majorVersion), | |
| static_cast<unsigned>(minorVersion))) { | |
| return {}; | |
| } | |
| } | |
| return cmStrCat("None of the specified ", keyword, | |
| " versions is supported by this version of CMake."); | |
| } | |
| bool handleQueryCommand(std::vector<std::string> const& args, | |
| cmExecutionStatus& status) | |
| { | |
| if (args.empty()) { | |
| status.SetError("QUERY called without required arguments."); | |
| return false; | |
| } | |
| struct Arguments : public ArgumentParser::ParseResult | |
| { | |
| ArgumentParser::NonEmpty<std::string> ApiVersion; | |
| ArgumentParser::NonEmpty<std::vector<std::string>> CodeModelVersions; | |
| ArgumentParser::NonEmpty<std::vector<std::string>> CacheVersions; | |
| ArgumentParser::NonEmpty<std::vector<std::string>> CMakeFilesVersions; | |
| ArgumentParser::NonEmpty<std::vector<std::string>> ToolchainsVersions; | |
| }; | |
| static auto const parser = | |
| cmArgumentParser<Arguments>{} | |
| .Bind("API_VERSION"_s, &Arguments::ApiVersion) | |
| .Bind("CODEMODEL"_s, &Arguments::CodeModelVersions) | |
| .Bind("CACHE"_s, &Arguments::CacheVersions) | |
| .Bind("CMAKEFILES"_s, &Arguments::CMakeFilesVersions) | |
| .Bind("TOOLCHAINS"_s, &Arguments::ToolchainsVersions); | |
| std::vector<std::string> unparsedArguments; | |
| Arguments const arguments = | |
| parser.Parse(cmMakeRange(args).advance(1), &unparsedArguments); | |
| if (arguments.MaybeReportError(status.GetMakefile())) { | |
| return true; | |
| } | |
| if (!unparsedArguments.empty()) { | |
| status.SetError(cmStrCat("QUERY given unknown argument \"", | |
| unparsedArguments.front(), "\".")); | |
| return false; | |
| } | |
| if (!std::all_of(arguments.ApiVersion.begin(), arguments.ApiVersion.end(), | |
| cmsysString_isdigit)) { | |
| status.SetError("QUERY given non-integer API_VERSION."); | |
| return false; | |
| } | |
| int const apiVersion = std::atoi(arguments.ApiVersion.c_str()); | |
| if (apiVersion != 1) { | |
| status.SetError( | |
| cmStrCat("QUERY given unsupported API_VERSION \"", arguments.ApiVersion, | |
| "\" (the only currently supported version is 1).")); | |
| return false; | |
| } | |
| cmMakefile& mf = status.GetMakefile(); | |
| cmake* cmi = mf.GetCMakeInstance(); | |
| cmFileAPI* fileApi = cmi->GetFileAPI(); | |
| // We want to check all keywords and report all errors, not just the first. | |
| // Record each result rather than short-circuiting on the first error. | |
| // NOTE: Double braces are needed here for compilers that don't implement the | |
| // CWG 1270 revision to C++11. | |
| std::array<std::string, 4> errors{ | |
| { processObjectKindVersions(*fileApi, cmFileAPI::ObjectKind::CodeModel, | |
| "CODEMODEL"_s, arguments.CodeModelVersions), | |
| processObjectKindVersions(*fileApi, cmFileAPI::ObjectKind::Cache, | |
| "CACHE"_s, arguments.CacheVersions), | |
| processObjectKindVersions(*fileApi, cmFileAPI::ObjectKind::CMakeFiles, | |
| "CMAKEFILES"_s, arguments.CMakeFilesVersions), | |
| processObjectKindVersions(*fileApi, cmFileAPI::ObjectKind::Toolchains, | |
| "TOOLCHAINS"_s, arguments.ToolchainsVersions) } | |
| }; | |
| if (!std::all_of(errors.begin(), errors.end(), | |
| [](std::string const& s) -> bool { return s.empty(); })) { | |
| std::string message("QUERY given invalid arguments:"); | |
| for (std::string const& s : errors) { | |
| if (!s.empty()) { | |
| message = cmStrCat(std::move(message), "\n ", s); | |
| } | |
| } | |
| status.SetError(message); | |
| return false; | |
| } | |
| return true; | |
| } | |
| } | |
| bool cmFileAPICommand(std::vector<std::string> const& args, | |
| cmExecutionStatus& status) | |
| { | |
| if (args.empty()) { | |
| status.SetError("must be called with arguments."); | |
| return false; | |
| } | |
| // clang-format off | |
| static cmSubcommandTable const subcommand{ | |
| { "QUERY"_s, handleQueryCommand } | |
| }; | |
| // clang-format on | |
| return subcommand(args[0], args, status); | |
| } |