Prechádzať zdrojové kódy

fileapi: extend codemodel v2 with directory details

Issue: #18398
Co-Author: Kyle Edwards <[email protected]>
Brad King 7 rokov pred
rodič
commit
4b6b2a571c

+ 30 - 2
Help/manual/cmake-file-api.7.rst

@@ -433,14 +433,21 @@ Version 1 does not exist to avoid confusion with that from
             "build": ".",
             "childIndexes": [ 1 ],
             "projectIndex": 0,
-            "targetIndexes": [ 0 ]
+            "targetIndexes": [ 0 ],
+            "hasInstallRule": true,
+            "minimumCMakeVersion": {
+              "string": "3.14"
+            }
           },
           {
             "source": "sub",
             "build": "sub",
             "parentIndex": 0,
             "projectIndex": 0,
-            "targetIndexes": [ 1 ]
+            "targetIndexes": [ 1 ],
+            "minimumCMakeVersion": {
+              "string": "3.14"
+            }
           }
         ],
         "projects": [
@@ -535,6 +542,27 @@ The members specific to ``codemodel`` objects are:
       array of entries corresponding to the targets.  Each entry is an
       unsigned integer 0-based index into the main ``targets`` array.
 
+    ``minimumCMakeVersion``
+      Optional member present when a minimum required version of CMake is
+      known for the directory.  This is the ``<min>`` version given to the
+      most local call to the :command:`cmake_minimum_required(VERSION)`
+      command in the directory itself or one of its ancestors.
+      The value is a JSON object with one member:
+
+      ``string``
+        A string specifying the minimum required version in the format::
+
+          <major>.<minor>[.<patch>[.<tweak>]][<suffix>]
+
+        Each component is an unsigned integer and the suffix may be an
+        arbitrary string.
+
+    ``hasInstallRule``
+      Optional member that is present with boolean value ``true`` when
+      the directory or one of its subdirectories contains any
+      :command:`install` rules, i.e. whether a ``make install``
+      or equivalent rule is available.
+
   ``projects``
     A JSON array of entries corresponding to the top-level project
     and sub-projects defined in the build system.  Each (sub-)project

+ 50 - 0
Source/cmFileAPICodemodel.cxx

@@ -8,6 +8,7 @@
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
 #include "cmInstallGenerator.h"
+#include "cmInstallSubdirectoryGenerator.h"
 #include "cmInstallTargetGenerator.h"
 #include "cmLinkLineComputer.h"
 #include "cmListFileCache.h"
@@ -62,8 +63,10 @@ class CodemodelConfig
   struct Directory
   {
     cmStateSnapshot Snapshot;
+    cmLocalGenerator const* LocalGenerator = nullptr;
     Json::Value TargetIndexes = Json::arrayValue;
     Json::ArrayIndex ProjectIndex;
+    bool HasInstallRule = false;
   };
   std::map<cmStateSnapshot, Json::ArrayIndex, cmStateSnapshot::StrictWeakOrder>
     DirectoryMap;
@@ -99,6 +102,8 @@ class CodemodelConfig
   Json::Value DumpProjects();
   Json::Value DumpProject(Project& p);
 
+  Json::Value DumpMinimumCMakeVersion(cmStateSnapshot s);
+
 public:
   CodemodelConfig(cmFileAPI& fileAPI, unsigned long version,
                   std::string const& config);
@@ -396,11 +401,36 @@ void CodemodelConfig::ProcessDirectories()
     this->Directories.emplace_back();
     Directory& d = this->Directories[directoryIndex];
     d.Snapshot = lg->GetStateSnapshot().GetBuildsystemDirectory();
+    d.LocalGenerator = lg;
     this->DirectoryMap[d.Snapshot] = directoryIndex;
 
     d.ProjectIndex = this->AddProject(d.Snapshot);
     this->Projects[d.ProjectIndex].DirectoryIndexes.append(directoryIndex);
   }
+
+  // Update directories in reverse order to process children before parents.
+  for (auto di = this->Directories.rbegin(); di != this->Directories.rend();
+       ++di) {
+    Directory& d = *di;
+
+    // Accumulate the presence of install rules on the way up.
+    for (auto gen : d.LocalGenerator->GetMakefile()->GetInstallGenerators()) {
+      if (!dynamic_cast<cmInstallSubdirectoryGenerator*>(gen)) {
+        d.HasInstallRule = true;
+        break;
+      }
+    }
+    if (!d.HasInstallRule) {
+      for (cmStateSnapshot const& child : d.Snapshot.GetChildren()) {
+        cmStateSnapshot childDir = child.GetBuildsystemDirectory();
+        Json::ArrayIndex const childIndex = this->GetDirectoryIndex(childDir);
+        if (this->Directories[childIndex].HasInstallRule) {
+          d.HasInstallRule = true;
+          break;
+        }
+      }
+    }
+  }
 }
 
 Json::ArrayIndex CodemodelConfig::GetDirectoryIndex(cmLocalGenerator const* lg)
@@ -531,6 +561,15 @@ Json::Value CodemodelConfig::DumpDirectory(Directory& d)
     directory["targetIndexes"] = std::move(d.TargetIndexes);
   }
 
+  Json::Value minimumCMakeVersion = this->DumpMinimumCMakeVersion(d.Snapshot);
+  if (!minimumCMakeVersion.isNull()) {
+    directory["minimumCMakeVersion"] = std::move(minimumCMakeVersion);
+  }
+
+  if (d.HasInstallRule) {
+    directory["hasInstallRule"] = true;
+  }
+
   return directory;
 }
 
@@ -566,6 +605,17 @@ Json::Value CodemodelConfig::DumpProject(Project& p)
   return project;
 }
 
+Json::Value CodemodelConfig::DumpMinimumCMakeVersion(cmStateSnapshot s)
+{
+  Json::Value minimumCMakeVersion;
+  if (std::string const* def =
+        s.GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION")) {
+    minimumCMakeVersion = Json::objectValue;
+    minimumCMakeVersion["string"] = *def;
+  }
+  return minimumCMakeVersion;
+}
+
 Target::Target(cmGeneratorTarget* gt, std::string const& config)
   : GT(gt)
   , Config(config)

+ 26 - 0
Tests/RunCMake/FileAPI/codemodel-v2-check.py

@@ -64,6 +64,16 @@ def check_directory(c):
                              missing_exception=lambda e: "Target ID: %s" % e,
                              extra_exception=lambda a: "Target ID: %s" % c["targets"][a]["id"])
 
+        if expected["minimumCMakeVersion"] is not None:
+            expected_keys.append("minimumCMakeVersion")
+            assert is_dict(actual["minimumCMakeVersion"])
+            assert sorted(actual["minimumCMakeVersion"].keys()) == ["string"]
+            assert is_string(actual["minimumCMakeVersion"]["string"], expected["minimumCMakeVersion"])
+
+        if expected["hasInstallRule"] is not None:
+            expected_keys.append("hasInstallRule")
+            assert is_bool(actual["hasInstallRule"], expected["hasInstallRule"])
+
         assert sorted(actual.keys()) == sorted(expected_keys)
 
     return _check
@@ -448,6 +458,8 @@ def gen_check_directories(c, g):
                 "^interface_exe::@6890427a1f51a3e7e1df$",
             ],
             "projectName": "codemodel-v2",
+            "minimumCMakeVersion": "3.12",
+            "hasInstallRule": True,
         },
         {
             "source": "^alias$",
@@ -461,6 +473,8 @@ def gen_check_directories(c, g):
                 "^cxx_alias_exe::@53632cba2752272bb008$",
             ],
             "projectName": "Alias",
+            "minimumCMakeVersion": "3.12",
+            "hasInstallRule": None,
         },
         {
             "source": "^custom$",
@@ -474,6 +488,8 @@ def gen_check_directories(c, g):
                 "^custom_tgt::@c11385ffed57b860da63$",
             ],
             "projectName": "Custom",
+            "minimumCMakeVersion": "3.12",
+            "hasInstallRule": None,
         },
         {
             "source": "^cxx$",
@@ -491,6 +507,8 @@ def gen_check_directories(c, g):
                 "^cxx_static_lib::@a56b12a3f5c0529fb296$",
             ],
             "projectName": "Cxx",
+            "minimumCMakeVersion": "3.12",
+            "hasInstallRule": None,
         },
         {
             "source": "^imported$",
@@ -507,6 +525,8 @@ def gen_check_directories(c, g):
                 "^link_imported_static_exe::@ba7eb709d0b48779c6c8$",
             ],
             "projectName": "Imported",
+            "minimumCMakeVersion": "3.12",
+            "hasInstallRule": None,
         },
         {
             "source": "^object$",
@@ -522,6 +542,8 @@ def gen_check_directories(c, g):
                 "^cxx_object_lib::@5ed5358f70faf8d8af7a$",
             ],
             "projectName": "Object",
+            "minimumCMakeVersion": "3.13",
+            "hasInstallRule": True,
         },
         {
             "source": "^dir$",
@@ -542,6 +564,8 @@ def gen_check_directories(c, g):
             "childSources": None,
             "targetIds": None,
             "projectName": "codemodel-v2",
+            "minimumCMakeVersion": "3.12",
+            "hasInstallRule": None,
         },
         {
             "source": "^.*/Tests/RunCMake/FileAPIExternalSource$",
@@ -554,6 +578,8 @@ def gen_check_directories(c, g):
                 "^generated_exe::@[0-9a-f]+$",
             ],
             "projectName": "External",
+            "minimumCMakeVersion": "3.12",
+            "hasInstallRule": None,
         },
     ]