diff options
author | Craig Scott <craig.scott@crascit.com> | 2023-06-02 07:16:57 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2023-06-05 10:20:50 (GMT) |
commit | 99b2ccf80dc87ccf6832508cc3f8889a70c2785f (patch) | |
tree | 6ff990a8878c0e90ee9c5b88ce876f7893e5c882 /Source | |
parent | 9a63aa8d57394fbddf913ce35c2d32bbf523f0e6 (diff) | |
download | CMake-99b2ccf80dc87ccf6832508cc3f8889a70c2785f.zip CMake-99b2ccf80dc87ccf6832508cc3f8889a70c2785f.tar.gz CMake-99b2ccf80dc87ccf6832508cc3f8889a70c2785f.tar.bz2 |
cmake_file_api: New project command
Projects can use the new command to request file API replies for the current
run. No query files are generated, the query is tracked internally. Replies are
created in the file system at generation time in the usual way.
Fixes: #24951
Diffstat (limited to 'Source')
-rw-r--r-- | Source/CMakeLists.txt | 2 | ||||
-rw-r--r-- | Source/cmCommands.cxx | 3 | ||||
-rw-r--r-- | Source/cmFileAPI.cxx | 42 | ||||
-rw-r--r-- | Source/cmFileAPI.h | 33 | ||||
-rw-r--r-- | Source/cmFileAPICommand.cxx | 170 | ||||
-rw-r--r-- | Source/cmFileAPICommand.h | 13 | ||||
-rw-r--r-- | Source/cmake.h | 4 |
7 files changed, 256 insertions, 11 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index bcaf890..b01e1e7 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -232,6 +232,8 @@ add_library( cmFileAPIConfigureLog.h cmFileAPICMakeFiles.cxx cmFileAPICMakeFiles.h + cmFileAPICommand.cxx + cmFileAPICommand.h cmFileAPIToolchains.cxx cmFileAPIToolchains.h cmFileCopier.cxx diff --git a/Source/cmCommands.cxx b/Source/cmCommands.cxx index 27f2156..ae83b2e 100644 --- a/Source/cmCommands.cxx +++ b/Source/cmCommands.cxx @@ -97,6 +97,7 @@ # include "cmExportCommand.h" # include "cmExportLibraryDependenciesCommand.h" # include "cmFLTKWrapUICommand.h" +# include "cmFileAPICommand.h" # include "cmIncludeExternalMSProjectCommand.h" # include "cmInstallProgramsCommand.h" # include "cmLinkLibrariesCommand.h" @@ -293,6 +294,7 @@ void GetProjectCommands(cmState* state) state->AddBuiltinCommand("qt_wrap_ui", cmQTWrapUICommand); state->AddBuiltinCommand("remove_definitions", cmRemoveDefinitionsCommand); state->AddBuiltinCommand("source_group", cmSourceGroupCommand); + state->AddBuiltinCommand("cmake_file_api", cmFileAPICommand); state->AddDisallowedCommand( "export_library_dependencies", cmExportLibraryDependenciesCommand, @@ -333,6 +335,7 @@ void GetProjectCommandsInScriptMode(cmState* state) CM_UNEXPECTED_PROJECT_COMMAND("add_test"); CM_UNEXPECTED_PROJECT_COMMAND("aux_source_directory"); CM_UNEXPECTED_PROJECT_COMMAND("build_command"); + CM_UNEXPECTED_PROJECT_COMMAND("cmake_file_api"); CM_UNEXPECTED_PROJECT_COMMAND("create_test_sourcelist"); CM_UNEXPECTED_PROJECT_COMMAND("define_property"); CM_UNEXPECTED_PROJECT_COMMAND("enable_language"); diff --git a/Source/cmFileAPI.cxx b/Source/cmFileAPI.cxx index 8b98916..8abb5a8 100644 --- a/Source/cmFileAPI.cxx +++ b/Source/cmFileAPI.cxx @@ -978,3 +978,45 @@ Json::Value cmFileAPI::ReportCapabilities() return capabilities; } + +bool cmFileAPI::AddProjectQuery(cmFileAPI::ObjectKind kind, + unsigned majorVersion, unsigned minorVersion) +{ + switch (kind) { + case ObjectKind::CodeModel: + if (majorVersion != 2 || minorVersion > CodeModelV2Minor) { + return false; + } + break; + case ObjectKind::Cache: + if (majorVersion != 2 || minorVersion > CacheV2Minor) { + return false; + } + break; + case ObjectKind::CMakeFiles: + if (majorVersion != 1 || minorVersion > CMakeFilesV1Minor) { + return false; + } + break; + case ObjectKind::Toolchains: + if (majorVersion != 1 || minorVersion > ToolchainsV1Minor) { + return false; + } + break; + // These cannot be requested by the project + case ObjectKind::ConfigureLog: + case ObjectKind::InternalTest: + return false; + } + + Object query; + query.Kind = kind; + query.Version = majorVersion; + if (std::find(this->TopQuery.Known.begin(), this->TopQuery.Known.end(), + query) == this->TopQuery.Known.end()) { + this->TopQuery.Known.emplace_back(query); + this->QueryExists = true; + } + + return true; +} diff --git a/Source/cmFileAPI.h b/Source/cmFileAPI.h index 6d7678f..1c13d7b 100644 --- a/Source/cmFileAPI.h +++ b/Source/cmFileAPI.h @@ -41,6 +41,20 @@ public: /** Report file-api capabilities for cmake -E capabilities. */ static Json::Value ReportCapabilities(); + // Keep in sync with ObjectKindName. + enum class ObjectKind + { + CodeModel, + ConfigureLog, + Cache, + CMakeFiles, + Toolchains, + InternalTest + }; + + bool AddProjectQuery(ObjectKind kind, unsigned majorVersion, + unsigned minorVersion); + private: cmake* CMakeInstance; @@ -53,17 +67,6 @@ private: static std::vector<std::string> LoadDir(std::string const& dir); void RemoveOldReplyFiles(); - // Keep in sync with ObjectKindName. - enum class ObjectKind - { - CodeModel, - ConfigureLog, - Cache, - CMakeFiles, - Toolchains, - InternalTest - }; - /** Identify one object kind and major version. */ struct Object { @@ -76,6 +79,14 @@ private: } return l.Version < r.Version; } + friend bool operator==(Object const& l, Object const& r) + { + return l.Kind == r.Kind && l.Version == r.Version; + } + friend bool operator!=(Object const& l, Object const& r) + { + return !(l == r); + } }; /** Represent content of a query directory. */ diff --git a/Source/cmFileAPICommand.cxx b/Source/cmFileAPICommand.cxx new file mode 100644 index 0000000..d051c9c --- /dev/null +++ b/Source/cmFileAPICommand.cxx @@ -0,0 +1,170 @@ +/* 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 <algorithm> +#include <array> +#include <cctype> +#include <cstdlib> + +#include <cm/string_view> +#include <cmext/string_view> + +#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<unsigned char>(ch)); +} + +std::string processObjectKindVersions(cmFileAPI& fileApi, + cmFileAPI::ObjectKind objectKind, + cm::string_view keyword, + const std::vector<std::string>& 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<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 subcommand 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("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<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(), + [](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<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); +} diff --git a/Source/cmFileAPICommand.h b/Source/cmFileAPICommand.h new file mode 100644 index 0000000..28a4571 --- /dev/null +++ b/Source/cmFileAPICommand.h @@ -0,0 +1,13 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying +file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <string> +#include <vector> + +class cmExecutionStatus; + +bool cmFileAPICommand(std::vector<std::string> const& args, + cmExecutionStatus& status); diff --git a/Source/cmake.h b/Source/cmake.h index 2f5ea24..156d061 100644 --- a/Source/cmake.h +++ b/Source/cmake.h @@ -635,6 +635,10 @@ public: void UnwatchUnusedCli(const std::string& var); void WatchUnusedCli(const std::string& var); +#if !defined(CMAKE_BOOTSTRAP) + cmFileAPI* GetFileAPI() const { return this->FileAPI.get(); } +#endif + cmState* GetState() const { return this->State.get(); } void SetCurrentSnapshot(cmStateSnapshot const& snapshot) { |