Bläddra i källkod

fileapi: Add codemodelVersion fields to target and directory objects

This will allow JSON schemas for these two types of files to describe the
version-specific content without requiring any outside information.

Fixes: #27031
Craig Scott 4 månader sedan
förälder
incheckning
4315076f2e

+ 26 - 0
Help/manual/cmake-file-api.7.rst

@@ -704,6 +704,19 @@ A codemodel "directory" object is referenced by a `"codemodel" version 2`_
 object's ``directories`` array.  Each "directory" object is a JSON object
 with members:
 
+``codemodelVersion``
+  This specifies the codemodel version this file is part of.  It will match
+  the ``version`` field of the codemodel object kind that references this file.
+  It is a JSON object with the following members:
+
+  ``major``
+    The codemodel major version.
+
+  ``minor``
+    The codemodel minor version.
+
+  This field was added in codemodel version 2.9.
+
 ``paths``
   A JSON object containing members:
 
@@ -980,6 +993,19 @@ A codemodel "target" object is referenced by a `"codemodel" version 2`_
 object's ``targets`` array.  Each "target" object is a JSON object
 with members:
 
+``codemodelVersion``
+  This specifies the codemodel version this file is part of.  It will match
+  the ``version`` field of the codemodel object kind that references this file.
+  It is a JSON object with the following members:
+
+  ``major``
+    The codemodel major version.
+
+  ``minor``
+    The codemodel minor version.
+
+  This field was added in codemodel version 2.9.
+
 ``name``
   A string specifying the logical name of the target.
 

+ 8 - 0
Help/release/dev/codemodel-version-directory-target-objects.rst

@@ -0,0 +1,8 @@
+codemodel-version-directory-target-objects
+------------------------------------------
+
+* The :manual:`cmake-file-api(7)` "codemodel" version 2 ``version`` field has
+  been updated to 2.9.
+
+* The :manual:`cmake-file-api(7)` "codemodel" version 2 "target" and
+  "directory" objects gained a new ``codemodelVersion`` field.

+ 7 - 3
Source/cmFileAPI.cxx

@@ -789,8 +789,10 @@ std::string cmFileAPI::NoSupportedVersion(
 
 // The "codemodel" object kind.
 
-// Update Help/manual/cmake-file-api.7.rst when updating this constant.
-static unsigned int const CodeModelV2Minor = 8;
+// Update the following files as well when updating this constant:
+//   Help/manual/cmake-file-api.7.rst
+//   Tests/RunCMake/FileAPI/codemodel-v2-check.py (check_objects())
+static unsigned int const CodeModelV2Minor = 9;
 
 void cmFileAPI::BuildClientRequestCodeModel(
   ClientRequest& r, std::vector<RequestVersion> const& versions)
@@ -809,7 +811,9 @@ void cmFileAPI::BuildClientRequestCodeModel(
 
 Json::Value cmFileAPI::BuildCodeModel(Object const& object)
 {
-  Json::Value codemodel = cmFileAPICodemodelDump(*this, object.Version);
+  assert(object.Version == 2);
+  Json::Value codemodel =
+    cmFileAPICodemodelDump(*this, object.Version, CodeModelV2Minor);
   codemodel["kind"] = this->ObjectKindName(object.Kind);
 
   Json::Value& version = codemodel["version"];

+ 3 - 2
Source/cmFileAPI.h

@@ -64,6 +64,9 @@ public:
   bool AddProjectQuery(ObjectKind kind, unsigned majorVersion,
                        unsigned minorVersion);
 
+  /** Build a JSON object with major and minor fields.  */
+  static Json::Value BuildVersion(unsigned int major, unsigned int minor);
+
 private:
   cmake* CMakeInstance;
 
@@ -196,8 +199,6 @@ private:
   static char const* ObjectKindName(ObjectKind kind);
   static std::string ObjectName(Object const& o);
 
-  static Json::Value BuildVersion(unsigned int major, unsigned int minor);
-
   Json::Value BuildObject(Object const& object);
 
   ClientRequests BuildClientRequests(Json::Value const& requests);

+ 45 - 18
Source/cmFileAPICodemodel.cxx

@@ -223,21 +223,24 @@ Json::Value BacktraceData::Dump()
 class Codemodel
 {
   cmFileAPI& FileAPI;
-  unsigned int Version;
+  unsigned int VersionMajor;
+  unsigned int VersionMinor;
 
   Json::Value DumpPaths();
   Json::Value DumpConfigurations();
   Json::Value DumpConfiguration(std::string const& config);
 
 public:
-  Codemodel(cmFileAPI& fileAPI, unsigned int version);
+  Codemodel(cmFileAPI& fileAPI, unsigned int versionMajor,
+            unsigned int versionMinor);
   Json::Value Dump();
 };
 
 class CodemodelConfig
 {
   cmFileAPI& FileAPI;
-  unsigned int Version;
+  unsigned int VersionMajor;
+  unsigned int VersionMinor;
   std::string const& Config;
   std::string TopSource;
   std::string TopBuild;
@@ -290,8 +293,8 @@ class CodemodelConfig
   Json::Value DumpMinimumCMakeVersion(cmStateSnapshot s);
 
 public:
-  CodemodelConfig(cmFileAPI& fileAPI, unsigned int version,
-                  std::string const& config);
+  CodemodelConfig(cmFileAPI& fileAPI, unsigned int versionMajor,
+                  unsigned int versionMinor, std::string const& config);
   Json::Value Dump();
 };
 
@@ -392,6 +395,8 @@ namespace {
 class DirectoryObject
 {
   cmLocalGenerator const* LG = nullptr;
+  unsigned int VersionMajor;
+  unsigned int VersionMinor;
   std::string const& Config;
   TargetIndexMapType& TargetIndexMap;
   std::string TopSource;
@@ -409,7 +414,8 @@ class DirectoryObject
                                 std::string const& toPath);
 
 public:
-  DirectoryObject(cmLocalGenerator const* lg, std::string const& config,
+  DirectoryObject(cmLocalGenerator const* lg, unsigned int versionMajor,
+                  unsigned int versionMinor, std::string const& config,
                   TargetIndexMapType& targetIndexMap);
   Json::Value Dump();
 };
@@ -417,6 +423,8 @@ public:
 class Target
 {
   cmGeneratorTarget* GT;
+  unsigned int VersionMajor;
+  unsigned int VersionMinor;
   std::string const& Config;
   std::string TopSource;
   std::string TopBuild;
@@ -511,13 +519,16 @@ class Target
   Json::Value DumpDebugger();
 
 public:
-  Target(cmGeneratorTarget* gt, std::string const& config);
+  Target(cmGeneratorTarget* gt, unsigned int versionMajor,
+         unsigned int versionMinor, std::string const& config);
   Json::Value Dump();
 };
 
-Codemodel::Codemodel(cmFileAPI& fileAPI, unsigned int version)
+Codemodel::Codemodel(cmFileAPI& fileAPI, unsigned int versionMajor,
+                     unsigned int versionMinor)
   : FileAPI(fileAPI)
-  , Version(version)
+  , VersionMajor(versionMajor)
+  , VersionMinor(versionMinor)
 {
 }
 
@@ -557,19 +568,21 @@ Json::Value Codemodel::DumpConfigurations()
 
 Json::Value Codemodel::DumpConfiguration(std::string const& config)
 {
-  CodemodelConfig configuration(this->FileAPI, this->Version, config);
+  CodemodelConfig configuration(this->FileAPI, this->VersionMajor,
+                                this->VersionMinor, config);
   return configuration.Dump();
 }
 
-CodemodelConfig::CodemodelConfig(cmFileAPI& fileAPI, unsigned int version,
+CodemodelConfig::CodemodelConfig(cmFileAPI& fileAPI, unsigned int versionMajor,
+                                 unsigned int versionMinor,
                                  std::string const& config)
   : FileAPI(fileAPI)
-  , Version(version)
+  , VersionMajor(versionMajor)
+  , VersionMinor(versionMinor)
   , Config(config)
   , TopSource(this->FileAPI.GetCMakeInstance()->GetHomeDirectory())
   , TopBuild(this->FileAPI.GetCMakeInstance()->GetHomeOutputDirectory())
 {
-  static_cast<void>(this->Version);
 }
 
 Json::Value CodemodelConfig::Dump()
@@ -701,7 +714,7 @@ Json::Value CodemodelConfig::DumpTargets()
 Json::Value CodemodelConfig::DumpTarget(cmGeneratorTarget* gt,
                                         Json::ArrayIndex ti)
 {
-  Target t(gt, this->Config);
+  Target t(gt, this->VersionMajor, this->VersionMinor, this->Config);
   std::string prefix = "target-" + gt->GetName();
   if (!this->Config.empty()) {
     prefix += "-" + this->Config;
@@ -797,7 +810,8 @@ Json::Value CodemodelConfig::DumpDirectoryObject(Directory& d)
     prefix += "-" + this->Config;
   }
 
-  DirectoryObject dir(d.LocalGenerator, this->Config, this->TargetIndexMap);
+  DirectoryObject dir(d.LocalGenerator, this->VersionMajor, this->VersionMinor,
+                      this->Config, this->TargetIndexMap);
   return this->FileAPI.MaybeJsonFile(dir.Dump(), prefix);
 }
 
@@ -844,9 +858,13 @@ Json::Value CodemodelConfig::DumpMinimumCMakeVersion(cmStateSnapshot s)
 }
 
 DirectoryObject::DirectoryObject(cmLocalGenerator const* lg,
+                                 unsigned int versionMajor,
+                                 unsigned int versionMinor,
                                  std::string const& config,
                                  TargetIndexMapType& targetIndexMap)
   : LG(lg)
+  , VersionMajor(versionMajor)
+  , VersionMinor(versionMinor)
   , Config(config)
   , TargetIndexMap(targetIndexMap)
   , TopSource(lg->GetGlobalGenerator()->GetCMakeInstance()->GetHomeDirectory())
@@ -859,6 +877,8 @@ DirectoryObject::DirectoryObject(cmLocalGenerator const* lg,
 Json::Value DirectoryObject::Dump()
 {
   Json::Value directoryObject = Json::objectValue;
+  directoryObject["codemodelVersion"] =
+    cmFileAPI::BuildVersion(this->VersionMajor, this->VersionMinor);
   directoryObject["paths"] = this->DumpPaths();
   directoryObject["installers"] = this->DumpInstallers();
   directoryObject["backtraceGraph"] = this->Backtraces.Dump();
@@ -1186,8 +1206,11 @@ Json::Value DirectoryObject::DumpInstallerPath(std::string const& top,
   return installPath;
 }
 
-Target::Target(cmGeneratorTarget* gt, std::string const& config)
+Target::Target(cmGeneratorTarget* gt, unsigned int versionMajor,
+               unsigned int versionMinor, std::string const& config)
   : GT(gt)
+  , VersionMajor(versionMajor)
+  , VersionMinor(versionMinor)
   , Config(config)
   , TopSource(gt->GetGlobalGenerator()->GetCMakeInstance()->GetHomeDirectory())
   , TopBuild(
@@ -1203,6 +1226,8 @@ Json::Value Target::Dump()
 
   cmStateEnums::TargetType const type = this->GT->GetType();
 
+  target["codemodelVersion"] =
+    cmFileAPI::BuildVersion(this->VersionMajor, this->VersionMinor);
   target["name"] = this->GT->GetName();
   target["type"] = cmState::GetTargetTypeName(type);
   target["id"] = TargetId(this->GT, this->TopBuild);
@@ -2154,8 +2179,10 @@ Json::Value Target::DumpDebugger()
   return debuggerInformation;
 }
 
-Json::Value cmFileAPICodemodelDump(cmFileAPI& fileAPI, unsigned int version)
+Json::Value cmFileAPICodemodelDump(cmFileAPI& fileAPI,
+                                   unsigned int versionMajor,
+                                   unsigned int versionMinor)
 {
-  Codemodel codemodel(fileAPI, version);
+  Codemodel codemodel(fileAPI, versionMajor, versionMinor);
   return codemodel.Dump();
 }

+ 2 - 1
Source/cmFileAPICodemodel.h

@@ -9,4 +9,5 @@
 class cmFileAPI;
 
 extern Json::Value cmFileAPICodemodelDump(cmFileAPI& fileAPI,
-                                          unsigned int version);
+                                          unsigned int majorVersion,
+                                          unsigned int minorVersion);

+ 1 - 1
Tests/RunCMake/CommandLine/E_capabilities-stdout.txt

@@ -1 +1 @@
-^{"debugger":(true|false),"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":8}]},{"kind":"configureLog","version":\[{"major":1,"minor":0}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":1}]},{"kind":"toolchains","version":\[{"major":1,"minor":0}]}]},"generators":\[.*\],"serverMode":false,"tls":(true|false),"version":{.*}}$
+^{"debugger":(true|false),"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":9}]},{"kind":"configureLog","version":\[{"major":1,"minor":0}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":1}]},{"kind":"toolchains","version":\[{"major":1,"minor":0}]}]},"generators":\[.*\],"serverMode":false,"tls":(true|false),"version":{.*}}$

+ 28 - 14
Tests/RunCMake/FileAPI/codemodel-v2-check.py

@@ -12,7 +12,9 @@ def read_codemodel_json_data(filename):
 def check_objects(o, g):
     assert is_list(o)
     assert len(o) == 1
-    check_index_object(o[0], "codemodel", 2, 8, check_object_codemodel(g))
+    major = 2
+    minor = 9
+    check_index_object(o[0], "codemodel", major, minor, check_object_codemodel(g, major, minor))
 
 def check_backtrace(t, b, backtrace):
     btg = t["backtraceGraph"]
@@ -52,7 +54,7 @@ def check_backtraces(t, actual, expected):
         check_backtrace(t, actual[i], expected[i])
         i += 1
 
-def check_directory(c):
+def check_directory(c, major, minor):
     def _check(actual, expected):
         assert is_dict(actual)
         expected_keys = ["build", "jsonFile", "source", "projectIndex"]
@@ -100,7 +102,12 @@ def check_directory(c):
             d = json.load(f)
 
         assert is_dict(d)
-        assert sorted(d.keys()) == ["backtraceGraph", "installers", "paths"]
+        assert sorted(d.keys()) == ["backtraceGraph", "codemodelVersion", "installers", "paths"]
+
+        # We get the values for major and minor directly rather than from the "expected" data.
+        # This avoids having to update every data file any time the major or minor version changes.
+        assert is_int(d["codemodelVersion"]["major"], major)
+        assert is_int(d["codemodelVersion"]["minor"], minor)
 
         assert is_string(d["paths"]["source"], actual["source"])
         assert is_string(d["paths"]["build"], actual["build"])
@@ -266,7 +273,7 @@ def check_backtrace_graph(btg):
 
         assert sorted(n.keys()) == sorted(expected_keys)
 
-def check_target(c):
+def check_target(c, major, minor):
     def _check(actual, expected):
         assert is_dict(actual)
         assert sorted(actual.keys()) == ["directoryIndex", "id", "jsonFile", "name", "projectIndex"]
@@ -281,7 +288,7 @@ def check_target(c):
         with open(filepath) as f:
             obj = json.load(f)
 
-        expected_keys = ["name", "id", "type", "backtraceGraph", "paths", "sources"]
+        expected_keys = ["codemodelVersion", "name", "id", "type", "backtraceGraph", "paths", "sources"]
         assert is_dict(obj)
         assert is_string(obj["name"], expected["name"])
         assert matches(obj["id"], expected["id"])
@@ -293,6 +300,13 @@ def check_target(c):
         assert matches(obj["paths"]["build"], expected["build"])
         assert matches(obj["paths"]["source"], expected["source"])
 
+        # We get the values for major and minor directly rather than from the "expected" data.
+        # This avoids having to update every data file any time the major or minor version changes.
+        assert is_dict(obj["codemodelVersion"])
+        assert sorted(obj["codemodelVersion"].keys()) == ["major", "minor"]
+        assert is_int(obj["codemodelVersion"]["major"], major)
+        assert is_int(obj["codemodelVersion"]["minor"], minor)
+
         def check_file_set(actual, expected):
             assert is_dict(actual)
             expected_keys = ["name", "type", "visibility", "baseDirectories"]
@@ -794,9 +808,9 @@ def gen_check_directories(c, g):
 
     return expected
 
-def check_directories(c, g):
+def check_directories(c, g, major, minor):
     check_list_match(lambda a, e: matches(a["source"], e["source"]), c["directories"], gen_check_directories(c, g),
-                     check=check_directory(c),
+                     check=check_directory(c, major, minor),
                      check_exception=lambda a, e: "Directory source: %s" % a["source"],
                      missing_exception=lambda e: "Directory source: %s" % e["source"],
                      extra_exception=lambda a: "Directory source: %s" % a["source"])
@@ -1001,10 +1015,10 @@ def gen_check_targets(c, g, inSource):
 
     return expected
 
-def check_targets(c, g, inSource):
+def check_targets(c, g, major, minor, inSource):
     check_list_match(lambda a, e: matches(a["id"], e["id"]),
                      c["targets"], gen_check_targets(c, g, inSource),
-                     check=check_target(c),
+                     check=check_target(c, major, minor),
                      check_exception=lambda a, e: "Target ID: %s" % a["id"],
                      missing_exception=lambda e: "Target ID: %s" % e["id"],
                      extra_exception=lambda a: "Target ID: %s" % a["id"])
@@ -1045,14 +1059,14 @@ def check_projects(c, g):
                      missing_exception=lambda e: "Project name: %s" % e["name"],
                      extra_exception=lambda a: "Project name: %s" % a["name"])
 
-def check_object_codemodel_configuration(c, g, inSource):
+def check_object_codemodel_configuration(c, g, major, minor, inSource):
     assert sorted(c.keys()) == ["directories", "name", "projects", "targets"]
     assert is_string(c["name"])
-    check_directories(c, g)
-    check_targets(c, g, inSource)
+    check_directories(c, g, major, minor)
+    check_targets(c, g, major, minor, inSource)
     check_projects(c, g)
 
-def check_object_codemodel(g):
+def check_object_codemodel(g, major, minor):
     def _check(o):
         assert sorted(o.keys()) == ["configurations", "kind", "paths", "version"]
         # The "kind" and "version" members are handled by check_index_object.
@@ -1070,7 +1084,7 @@ def check_object_codemodel(g):
             assert o["configurations"][0]["name"] in ("", "Debug", "Release", "RelWithDebInfo", "MinSizeRel")
 
         for c in o["configurations"]:
-            check_object_codemodel_configuration(c, g, inSource)
+            check_object_codemodel_configuration(c, g, major, minor, inSource)
     return _check
 
 cxx_compiler_id = sys.argv[2]