/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmFileAPICommand.h" #include #include #include #include #include #include #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 { bool isCharDigit(char ch) { return std::isdigit(static_cast(ch)); } std::string processObjectKindVersions(cmFileAPI& fileApi, cmFileAPI::ObjectKind objectKind, cm::string_view keyword, const std::vector& 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 (const std::string& ver : versions) { const char* 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(majorVersion), static_cast(minorVersion))) { return {}; } } return cmStrCat("None of the specified ", keyword, " versions is supported by this version of CMake."); } bool handleQueryCommand(std::vector const& args, cmExecutionStatus& status) { if (args.empty()) { status.SetError("QUERY subcommand called without required arguments."); return false; } struct Arguments : public ArgumentParser::ParseResult { ArgumentParser::NonEmpty ApiVersion; ArgumentParser::NonEmpty> CodeModelVersions; ArgumentParser::NonEmpty> CacheVersions; ArgumentParser::NonEmpty> CMakeFilesVersions; ArgumentParser::NonEmpty> ToolchainsVersions; }; static auto const parser = cmArgumentParser{} .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 unparsedArguments; Arguments const arguments = parser.Parse(cmMakeRange(args).advance(1), &unparsedArguments); if (arguments.MaybeReportError(status.GetMakefile())) { return true; } if (!unparsedArguments.empty()) { status.SetError("QUERY subcommand given unknown argument \"" + unparsedArguments.front() + "\"."); return false; } if (!std::all_of(arguments.ApiVersion.begin(), arguments.ApiVersion.end(), isCharDigit)) { status.SetError("QUERY subcommand given a non-integer API_VERSION."); return false; } const int apiVersion = std::atoi(arguments.ApiVersion.c_str()); if (apiVersion != 1) { status.SetError( cmStrCat("QUERY subcommand given an 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 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(), [](const std::string& s) -> bool { return s.empty(); })) { std::string message("QUERY subcommand was given invalid arguments:"); for (const std::string& s : errors) { if (!s.empty()) { message = cmStrCat(message, "\n ", s); } } status.SetError(message); return false; } return true; } } bool cmFileAPICommand(std::vector 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); }