浏览代码

fileapi: Add installers to codemodel-v2 "directory" object

Co-Authored-by: Kyle Edwards <[email protected]>
Brad King 4 年之前
父节点
当前提交
049bf98f63

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

@@ -666,6 +666,154 @@ with members:
     directory (with ``.`` for the top-level build directory itself).
     Otherwise the path is absolute.
 
+``installers``
+  A JSON array of entries corresponding to :command:`install` rules.
+  Each entry is a JSON object containing members:
+
+  ``component``
+    A string specifying the component selected by the corresponding
+    :command:`install` command invocation.
+
+  ``destination``
+    Optional member that is present for specific ``type`` values below.
+    The value is a string specifying the install destination path.
+    The path may be absolute or relative to the install prefix.
+
+  ``paths``
+    Optional member that is present for specific ``type`` values below.
+    The value is a JSON array of entries corresponding to the paths
+    (files or directories) to be installed.  Each entry is one of:
+
+    * A string specifying the path from which a file or directory
+      is to be installed.  The portion of the path not preceded by
+      a ``/`` also specifies the path (name) to which the file
+      or directory is to be installed under the destination.
+
+    * A JSON object with members:
+
+      ``from``
+        A string specifying the path from which a file or directory
+        is to be installed.
+
+      ``to``
+        A string specifying the path to which the file or directory
+        is to be installed under the destination.
+
+    In both cases the paths are represented with forward slashes.  If
+    the "from" path is inside the top-level directory documented by the
+    corresponding ``type`` value, then the path is specified relative
+    to that directory.  Otherwise the path is absolute.
+
+  ``type``
+    A string specifying the type of installation rule.  The value is one
+    of the following, with some variants providing additional members:
+
+    ``file``
+      An :command:`install(FILES)` or :command:`install(PROGRAMS)` call.
+      The ``destination`` and ``paths`` members are populated, with paths
+      under the top-level *source* directory expressed relative to it.
+      The ``isOptional`` member may exist.
+      This type has no additional members.
+
+    ``directory``
+      An :command:`install(DIRECTORY)` call.
+      The ``destination`` and ``paths`` members are populated, with paths
+      under the top-level *source* directory expressed relative to it.
+      The ``isOptional`` member may exist.
+      This type has no additional members.
+
+    ``target``
+      An :command:`install(TARGETS)` call.
+      The ``destination`` and ``paths`` members are populated, with paths
+      under the top-level *build* directory expressed relative to it.
+      The ``isOptional`` member may exist.
+      This type has additional members ``targetId``, ``targetIndex``,
+      ``targetIsImportLibrary``, and ``targetInstallNamelink``.
+
+    ``export``
+      An :command:`install(EXPORT)` call.
+      The ``destination`` and ``paths`` members are populated, with paths
+      under the top-level *build* directory expressed relative to it.
+      The ``paths`` entries refer to files generated automatically by
+      CMake for installation, and their actual values are considered
+      private implementation details.
+      This type has additional members ``exportName`` and ``exportTargets``.
+
+    ``script``
+      An :command:`install(SCRIPT)` call.
+      This type has additional member ``scriptFile``.
+
+    ``code``
+      An :command:`install(CODE)` call.
+      This type has no additional members.
+
+  ``isExcludeFromAll``
+    Optional member that is present with boolean value ``true`` when
+    :command:`install` is called with the ``EXCLUDE_FROM_ALL`` option.
+
+  ``isOptional``
+    Optional member that is present with boolean value ``true`` when
+    :command:`install` is called with the ``OPTIONAL`` option.
+    This is allowed when ``type`` is ``file``, ``directory``, or ``target``.
+
+  ``targetId``
+    Optional member that is present when ``type`` is ``target``.
+    The value is a string uniquely identifying the target to be installed.
+    This matches the ``id`` member of the target in the main
+    "codemodel" object's ``targets`` array.
+
+  ``targetIndex``
+    Optional member that is present when ``type`` is ``target``.
+    The value is an unsigned integer 0-based index into the main "codemodel"
+    object's ``targets`` array for the target to be installed.
+
+  ``targetIsImportLibrary``
+    Optional member that is present when ``type`` is ``target`` and
+    the installer is for a Windows DLL import library file or for an
+    AIX linker import file.  If present, it has boolean value ``true``.
+
+  ``targetInstallNamelink``
+    Optional member that is present when ``type`` is ``target`` and
+    the installer corresponds to a target that may use symbolic links
+    to implement the :prop_tgt:`VERSION` and :prop_tgt:`SOVERSION`
+    target properties.
+    The value is a string indicating how the installer is supposed to
+    handle the symlinks: ``skip`` means the installer should skip the
+    symlinks and install only the real file, and ``only`` means the
+    installer should install only the symlinks and not the real file.
+    In all cases the ``paths`` member lists what it actually installs.
+
+  ``exportName``
+    Optional member that is present when ``type`` is ``export``.
+    The value is a string specifying the name of the export.
+
+  ``exportTargets``
+    Optional member that is present when ``type`` is ``export``.
+    The value is a JSON array of entries corresponding to the targets
+    included in the export.  Each entry is a JSON object with members:
+
+    ``id``
+      A string uniquely identifying the target.  This matches
+      the ``id`` member of the target in the main "codemodel"
+      object's ``targets`` array.
+
+    ``index``
+      An unsigned integer 0-based index into the main "codemodel"
+      object's ``targets`` array for the target.
+
+  ``scriptFile``
+    Optional member that is present when ``type`` is ``script``.
+    The value is a string specifying the path to the script file on disk,
+    represented with forward slashes.  If the file is inside the top-level
+    source directory then the path is specified relative to that directory.
+    Otherwise the path is absolute.
+
+  ``backtrace``
+    Optional member that is present when a CMake language backtrace to
+    the :command:`install` or other command invocation that added this
+    installer is available.  The value is an unsigned integer 0-based
+    index into the ``backtraceGraph`` member's ``nodes`` array.
+
 ``backtraceGraph``
   A `"codemodel" version 2 "backtrace graph"`_ whose nodes are referenced
   from ``backtrace`` members elsewhere in this "directory" object.

+ 2 - 0
Help/release/dev/fileapi-codemodel-directory.rst

@@ -6,3 +6,5 @@ fileapi-codemodel-directory
 
 * The :manual:`cmake-file-api(7)` "codemodel" version 2 gained a
   new "directory" object containing directory-level information.
+  This includes a list of installers generated by the :command:`install`
+  command.

+ 217 - 3
Source/cmFileAPICodemodel.cxx

@@ -20,11 +20,16 @@
 #include <cm3p/json/value.h>
 
 #include "cmCryptoHash.h"
+#include "cmExportSet.h"
 #include "cmFileAPI.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
+#include "cmInstallDirectoryGenerator.h"
+#include "cmInstallExportGenerator.h"
+#include "cmInstallFilesGenerator.h"
 #include "cmInstallGenerator.h"
+#include "cmInstallScriptGenerator.h"
 #include "cmInstallSubdirectoryGenerator.h"
 #include "cmInstallTargetGenerator.h"
 #include "cmLinkLineComputer.h"
@@ -42,6 +47,7 @@
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetDepend.h"
+#include "cmTargetExport.h"
 #include "cmake.h"
 
 namespace {
@@ -377,6 +383,7 @@ class DirectoryObject
 {
   cmLocalGenerator const* LG = nullptr;
   std::string const& Config;
+  TargetIndexMapType& TargetIndexMap;
   std::string TopSource;
   std::string TopBuild;
   BacktraceData Backtraces;
@@ -384,9 +391,16 @@ class DirectoryObject
   void AddBacktrace(Json::Value& object, cmListFileBacktrace const& bt);
 
   Json::Value DumpPaths();
+  Json::Value DumpInstallers();
+  Json::Value DumpInstaller(cmInstallGenerator* gen);
+  Json::Value DumpInstallerExportTargets(cmExportSet* exp);
+  Json::Value DumpInstallerPath(std::string const& top,
+                                std::string const& fromPathIn,
+                                std::string const& toPath);
 
 public:
-  DirectoryObject(cmLocalGenerator const* lg, std::string const& config);
+  DirectoryObject(cmLocalGenerator const* lg, std::string const& config,
+                  TargetIndexMapType& targetIndexMap);
   Json::Value Dump();
 };
 
@@ -763,7 +777,7 @@ Json::Value CodemodelConfig::DumpDirectoryObject(Directory& d)
     prefix += "-" + this->Config;
   }
 
-  DirectoryObject dir(d.LocalGenerator, this->Config);
+  DirectoryObject dir(d.LocalGenerator, this->Config, this->TargetIndexMap);
   return this->FileAPI.MaybeJsonFile(dir.Dump(), prefix);
 }
 
@@ -811,9 +825,11 @@ Json::Value CodemodelConfig::DumpMinimumCMakeVersion(cmStateSnapshot s)
 }
 
 DirectoryObject::DirectoryObject(cmLocalGenerator const* lg,
-                                 std::string const& config)
+                                 std::string const& config,
+                                 TargetIndexMapType& targetIndexMap)
   : LG(lg)
   , Config(config)
+  , TargetIndexMap(targetIndexMap)
   , TopSource(lg->GetGlobalGenerator()->GetCMakeInstance()->GetHomeDirectory())
   , TopBuild(
       lg->GetGlobalGenerator()->GetCMakeInstance()->GetHomeOutputDirectory())
@@ -825,6 +841,7 @@ Json::Value DirectoryObject::Dump()
 {
   Json::Value directoryObject = Json::objectValue;
   directoryObject["paths"] = this->DumpPaths();
+  directoryObject["installers"] = this->DumpInstallers();
   directoryObject["backtraceGraph"] = this->Backtraces.Dump();
   return directoryObject;
 }
@@ -850,6 +867,203 @@ Json::Value DirectoryObject::DumpPaths()
   return paths;
 }
 
+Json::Value DirectoryObject::DumpInstallers()
+{
+  Json::Value installers = Json::arrayValue;
+  for (const auto& gen : this->LG->GetMakefile()->GetInstallGenerators()) {
+    Json::Value installer = this->DumpInstaller(gen.get());
+    if (!installer.empty()) {
+      installers.append(std::move(installer)); // NOLINT(*)
+    }
+  }
+  return installers;
+}
+
+Json::Value DirectoryObject::DumpInstaller(cmInstallGenerator* gen)
+{
+  Json::Value installer = Json::objectValue;
+
+  // Exclude subdirectory installers.  They are implementation details.
+  if (dynamic_cast<cmInstallSubdirectoryGenerator*>(gen)) {
+    return installer;
+  }
+
+  // Exclude installers not used in this configuration.
+  if (!gen->InstallsForConfig(this->Config)) {
+    return installer;
+  }
+
+  // Add fields specific to each kind of install generator.
+  if (auto* installTarget = dynamic_cast<cmInstallTargetGenerator*>(gen)) {
+    cmInstallTargetGenerator::Files const& files =
+      installTarget->GetFiles(this->Config);
+    if (files.From.empty()) {
+      return installer;
+    }
+
+    installer["type"] = "target";
+    installer["destination"] = installTarget->GetDestination(this->Config);
+    installer["targetId"] =
+      TargetId(installTarget->GetTarget(), this->TopBuild);
+    installer["targetIndex"] =
+      this->TargetIndexMap[installTarget->GetTarget()];
+
+    std::string fromDir = files.FromDir;
+    if (!fromDir.empty()) {
+      fromDir.push_back('/');
+    }
+
+    std::string toDir = files.ToDir;
+    if (!toDir.empty()) {
+      toDir.push_back('/');
+    }
+
+    Json::Value paths = Json::arrayValue;
+    for (size_t i = 0; i < files.From.size(); ++i) {
+      std::string const& fromPath = cmStrCat(fromDir, files.From[i]);
+      std::string const& toPath = cmStrCat(toDir, files.To[i]);
+      paths.append(this->DumpInstallerPath(this->TopBuild, fromPath, toPath));
+    }
+    installer["paths"] = std::move(paths);
+
+    if (installTarget->GetOptional()) {
+      installer["isOptional"] = true;
+    }
+
+    if (installTarget->IsImportLibrary()) {
+      installer["targetIsImportLibrary"] = true;
+    }
+
+    switch (files.NamelinkMode) {
+      case cmInstallTargetGenerator::NamelinkModeNone:
+        break;
+      case cmInstallTargetGenerator::NamelinkModeOnly:
+        installer["targetInstallNamelink"] = "only";
+        break;
+      case cmInstallTargetGenerator::NamelinkModeSkip:
+        installer["targetInstallNamelink"] = "skip";
+        break;
+    }
+
+    // FIXME: Parse FilePermissions to provide structured information.
+    // FIXME: Thread EXPORT name through from install() call.
+  } else if (auto* installFiles =
+               dynamic_cast<cmInstallFilesGenerator*>(gen)) {
+    std::vector<std::string> const& files =
+      installFiles->GetFiles(this->Config);
+    if (files.empty()) {
+      return installer;
+    }
+
+    installer["type"] = "file";
+    installer["destination"] = installFiles->GetDestination(this->Config);
+    Json::Value paths = Json::arrayValue;
+    std::string const& rename = installFiles->GetRename(this->Config);
+    if (!rename.empty() && files.size() == 1) {
+      paths.append(this->DumpInstallerPath(this->TopSource, files[0], rename));
+    } else {
+      for (std::string const& file : installFiles->GetFiles(this->Config)) {
+        paths.append(RelativeIfUnder(this->TopSource, file));
+      }
+    }
+    installer["paths"] = std::move(paths);
+    if (installFiles->GetOptional()) {
+      installer["isOptional"] = true;
+    }
+    // FIXME: Parse FilePermissions to provide structured information.
+  } else if (auto* installDir =
+               dynamic_cast<cmInstallDirectoryGenerator*>(gen)) {
+    std::vector<std::string> const& dirs =
+      installDir->GetDirectories(this->Config);
+    if (dirs.empty()) {
+      return installer;
+    }
+
+    installer["type"] = "directory";
+    installer["destination"] = installDir->GetDestination(this->Config);
+    Json::Value paths = Json::arrayValue;
+    for (std::string const& dir : dirs) {
+      if (cmHasLiteralSuffix(dir, "/")) {
+        paths.append(this->DumpInstallerPath(
+          this->TopSource, dir.substr(0, dir.size() - 1), "."));
+      } else {
+        paths.append(this->DumpInstallerPath(
+          this->TopSource, dir, cmSystemTools::GetFilenameName(dir)));
+      }
+    }
+    installer["paths"] = std::move(paths);
+    if (installDir->GetOptional()) {
+      installer["isOptional"] = true;
+    }
+    // FIXME: Parse FilePermissions, DirPermissions, and LiteralArguments.
+    // to provide structured information.
+  } else if (auto* installExport =
+               dynamic_cast<cmInstallExportGenerator*>(gen)) {
+    installer["type"] = "export";
+    installer["destination"] = installExport->GetDestination();
+    cmExportSet* exportSet = installExport->GetExportSet();
+    installer["exportName"] = exportSet->GetName();
+    installer["exportTargets"] = this->DumpInstallerExportTargets(exportSet);
+    Json::Value paths = Json::arrayValue;
+    paths.append(
+      RelativeIfUnder(this->TopBuild, installExport->GetMainImportFile()));
+    installer["paths"] = std::move(paths);
+  } else if (auto* installScript =
+               dynamic_cast<cmInstallScriptGenerator*>(gen)) {
+    if (installScript->IsCode()) {
+      installer["type"] = "code";
+    } else {
+      installer["type"] = "script";
+      installer["scriptFile"] = RelativeIfUnder(
+        this->TopSource, installScript->GetScript(this->Config));
+    }
+  }
+
+  // Add fields common to all install generators.
+  installer["component"] = gen->GetComponent();
+  if (gen->GetExcludeFromAll()) {
+    installer["isExcludeFromAll"] = true;
+  }
+  this->AddBacktrace(installer, gen->GetBacktrace());
+
+  return installer;
+}
+
+Json::Value DirectoryObject::DumpInstallerExportTargets(cmExportSet* exp)
+{
+  Json::Value targets = Json::arrayValue;
+  for (auto const& targetExport : exp->GetTargetExports()) {
+    Json::Value target = Json::objectValue;
+    target["id"] = TargetId(targetExport->Target, this->TopBuild);
+    target["index"] = this->TargetIndexMap[targetExport->Target];
+    targets.append(std::move(target)); // NOLINT(*)
+  }
+  return targets;
+}
+
+Json::Value DirectoryObject::DumpInstallerPath(std::string const& top,
+                                               std::string const& fromPathIn,
+                                               std::string const& toPath)
+{
+  Json::Value installPath;
+
+  std::string fromPath = RelativeIfUnder(top, fromPathIn);
+
+  // If toPath is the last component of fromPath, use just fromPath.
+  if (toPath.find_first_of('/') == std::string::npos &&
+      cmHasSuffix(fromPath, toPath) &&
+      (fromPath.size() == toPath.size() ||
+       fromPath[fromPath.size() - toPath.size() - 1] == '/')) {
+    installPath = fromPath;
+  } else {
+    installPath = Json::objectValue;
+    installPath["from"] = fromPath;
+    installPath["to"] = toPath;
+  }
+
+  return installPath;
+}
+
 Target::Target(cmGeneratorTarget* gt, std::string const& config)
   : GT(gt)
   , Config(config)

+ 92 - 1
Tests/RunCMake/FileAPI/codemodel-v2-check.py

@@ -98,13 +98,90 @@ def check_directory(c):
             d = json.load(f)
 
         assert is_dict(d)
-        assert sorted(d.keys()) == ["backtraceGraph", "paths"]
+        assert sorted(d.keys()) == ["backtraceGraph", "installers", "paths"]
 
         assert is_string(d["paths"]["source"], actual["source"])
         assert is_string(d["paths"]["build"], actual["build"])
 
         check_backtrace_graph(d["backtraceGraph"])
 
+        assert is_list(d["installers"])
+        assert len(d["installers"]) == len(expected["installers"])
+        for a, e in zip(d["installers"], expected["installers"]):
+            assert is_dict(a)
+            expected_keys = ["component", "type"]
+
+            assert is_string(a["component"], e["component"])
+            assert is_string(a["type"], e["type"])
+
+            if e["destination"] is not None:
+                expected_keys.append("destination")
+                assert is_string(a["destination"], e["destination"])
+
+            if e["paths"] is not None:
+                expected_keys.append("paths")
+                assert is_list(a["paths"])
+                assert len(a["paths"]) == len(e["paths"])
+
+                for ap, ep in zip(a["paths"], e["paths"]):
+                    if is_string(ep):
+                        assert matches(ap, ep)
+                    else:
+                        assert is_dict(ap)
+                        assert sorted(ap.keys()) == ["from", "to"]
+                        assert matches(ap["from"], ep["from"])
+                        assert matches(ap["to"], ep["to"])
+
+            if e["isExcludeFromAll"] is not None:
+                expected_keys.append("isExcludeFromAll")
+                assert is_bool(a["isExcludeFromAll"], e["isExcludeFromAll"])
+
+            if e["isOptional"] is not None:
+                expected_keys.append("isOptional")
+                assert is_bool(a["isOptional"], e["isOptional"])
+
+            if e["targetId"] is not None:
+                expected_keys.append("targetId")
+                assert matches(a["targetId"], e["targetId"])
+
+            if e["targetIndex"] is not None:
+                expected_keys.append("targetIndex")
+                assert is_int(a["targetIndex"])
+                assert c["targets"][a["targetIndex"]]["name"] == e["targetIndex"]
+
+            if e["targetIsImportLibrary"] is not None:
+                expected_keys.append("targetIsImportLibrary")
+                assert is_bool(a["targetIsImportLibrary"], e["targetIsImportLibrary"])
+
+            if e["targetInstallNamelink"] is not None:
+                expected_keys.append("targetInstallNamelink")
+                assert is_string(a["targetInstallNamelink"], e["targetInstallNamelink"])
+
+            if e["exportName"] is not None:
+                expected_keys.append("exportName")
+                assert is_string(a["exportName"], e["exportName"])
+
+            if e["exportTargets"] is not None:
+                expected_keys.append("exportTargets")
+                assert is_list(a["exportTargets"])
+                assert len(a["exportTargets"]) == len(e["exportTargets"])
+                for at, et in zip(a["exportTargets"], e["exportTargets"]):
+                    assert is_dict(at)
+                    assert sorted(at.keys()) == ["id", "index"]
+                    assert matches(at["id"], et["id"])
+                    assert is_int(at["index"])
+                    assert c["targets"][at["index"]]["name"] == et["index"]
+
+            if e["scriptFile"] is not None:
+                expected_keys.append("scriptFile")
+                assert is_string(a["scriptFile"], e["scriptFile"])
+
+            if e["backtrace"] is not None:
+                expected_keys.append("backtrace")
+                check_backtrace(d, a["backtrace"], e["backtrace"])
+
+            assert sorted(a.keys()) == sorted(expected_keys)
+
     return _check
 
 def check_backtrace_graph(btg):
@@ -555,6 +632,20 @@ def gen_check_directories(c, g):
         for e in expected:
             e["targetIds"] = filter_list(lambda t: not matches(t, "^\\^(ALL_BUILD|ZERO_CHECK)"), e["targetIds"])
 
+    if sys.platform in ("win32", "cygwin", "msys") or "aix" in sys.platform:
+        for e in expected:
+            e["installers"] = list(filter(lambda i: i["targetInstallNamelink"] is None or i["targetInstallNamelink"] == "skip", e["installers"]))
+            for i in e["installers"]:
+                i["targetInstallNamelink"] = None
+
+    if sys.platform not in ("win32", "cygwin", "msys"):
+        for e in expected:
+            e["installers"] = list(filter(lambda i: "_dllExtra" not in i or not i["_dllExtra"], e["installers"]))
+            if "aix" not in sys.platform:
+                for i in e["installers"]:
+                    if "pathsNamelink" in i:
+                        i["paths"] = i["pathsNamelink"]
+
     return expected
 
 def check_directories(c, g):

+ 2 - 1
Tests/RunCMake/FileAPI/codemodel-v2-data/directories/alias.json

@@ -11,5 +11,6 @@
     ],
     "projectName": "Alias",
     "minimumCMakeVersion": "3.12",
-    "hasInstallRule": null
+    "hasInstallRule": null,
+    "installers": []
 }

+ 2 - 1
Tests/RunCMake/FileAPI/codemodel-v2-data/directories/custom.json

@@ -11,5 +11,6 @@
     ],
     "projectName": "Custom",
     "minimumCMakeVersion": "3.12",
-    "hasInstallRule": null
+    "hasInstallRule": null,
+    "installers": []
 }

+ 2 - 1
Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.json

@@ -17,5 +17,6 @@
     ],
     "projectName": "Cxx",
     "minimumCMakeVersion": "3.12",
-    "hasInstallRule": null
+    "hasInstallRule": null,
+    "installers": []
 }

+ 2 - 1
Tests/RunCMake/FileAPI/codemodel-v2-data/directories/dir.json

@@ -8,5 +8,6 @@
     "targetIds": null,
     "projectName": "codemodel-v2",
     "minimumCMakeVersion": "3.12",
-    "hasInstallRule": null
+    "hasInstallRule": null,
+    "installers": []
 }

+ 2 - 1
Tests/RunCMake/FileAPI/codemodel-v2-data/directories/dir_dir.json

@@ -6,5 +6,6 @@
     "targetIds": null,
     "projectName": "codemodel-v2",
     "minimumCMakeVersion": "3.12",
-    "hasInstallRule": null
+    "hasInstallRule": null,
+    "installers": []
 }

+ 65 - 1
Tests/RunCMake/FileAPI/codemodel-v2-data/directories/external.json

@@ -10,5 +10,69 @@
     ],
     "projectName": "External",
     "minimumCMakeVersion": "3.12",
-    "hasInstallRule": true
+    "hasInstallRule": true,
+    "installers": [
+        {
+            "component": "Unspecified",
+            "type": "directory",
+            "destination": "dir3",
+            "paths": [
+                "^.*/Tests/RunCMake/FileAPIExternalSource/\\.$"
+            ],
+            "isExcludeFromAll": null,
+            "isOptional": null,
+            "targetId": null,
+            "targetIndex": null,
+            "targetIsImportLibrary": null,
+            "targetInstallNamelink": null,
+            "exportName": null,
+            "exportTargets": null,
+            "scriptFile": null,
+            "backtrace": [
+                {
+                    "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                    "line": 15,
+                    "command": "install",
+                    "hasParent": true
+                },
+                {
+                    "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                }
+            ]
+        },
+        {
+            "component": "Unspecified",
+            "type": "directory",
+            "destination": "dir4",
+            "paths": [
+                "^.*/Tests/RunCMake/FileAPIExternalSource$"
+            ],
+            "isExcludeFromAll": true,
+            "isOptional": null,
+            "targetId": null,
+            "targetIndex": null,
+            "targetIsImportLibrary": null,
+            "targetInstallNamelink": null,
+            "exportName": null,
+            "exportTargets": null,
+            "scriptFile": null,
+            "backtrace": [
+                {
+                    "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                    "line": 16,
+                    "command": "install",
+                    "hasParent": true
+                },
+                {
+                    "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                }
+            ]
+        }
+    ]
 }

+ 2 - 1
Tests/RunCMake/FileAPI/codemodel-v2-data/directories/imported.json

@@ -14,5 +14,6 @@
     ],
     "projectName": "Imported",
     "minimumCMakeVersion": "3.12",
-    "hasInstallRule": null
+    "hasInstallRule": null,
+    "installers": []
 }

+ 2 - 1
Tests/RunCMake/FileAPI/codemodel-v2-data/directories/interface.json

@@ -10,5 +10,6 @@
     ],
     "projectName": "Interface",
     "minimumCMakeVersion": "3.12",
-    "hasInstallRule": null
+    "hasInstallRule": null,
+    "installers": []
 }

+ 65 - 1
Tests/RunCMake/FileAPI/codemodel-v2-data/directories/object.json

@@ -13,5 +13,69 @@
     ],
     "projectName": "Object",
     "minimumCMakeVersion": "3.13",
-    "hasInstallRule": true
+    "hasInstallRule": true,
+    "installers": [
+        {
+            "component": "Unspecified",
+            "type": "target",
+            "destination": "bin",
+            "paths": [
+                "^object/((Debug|Release|MinSizeRel|RelWithDebInfo)/)?c_object_exe(\\.exe)?$"
+            ],
+            "isExcludeFromAll": null,
+            "isOptional": null,
+            "targetId": "^c_object_exe::@5ed5358f70faf8d8af7a$",
+            "targetIndex": "c_object_exe",
+            "targetIsImportLibrary": null,
+            "targetInstallNamelink": null,
+            "exportName": null,
+            "exportTargets": null,
+            "scriptFile": null,
+            "backtrace": [
+                {
+                    "file": "^object/CMakeLists\\.txt$",
+                    "line": 13,
+                    "command": "install",
+                    "hasParent": true
+                },
+                {
+                    "file": "^object/CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                }
+            ]
+        },
+        {
+            "component": "Unspecified",
+            "type": "target",
+            "destination": "bin",
+            "paths": [
+                "^object/((Debug|Release|MinSizeRel|RelWithDebInfo)/)?cxx_object_exe(\\.exe)?$"
+            ],
+            "isExcludeFromAll": null,
+            "isOptional": null,
+            "targetId": "^cxx_object_exe::@5ed5358f70faf8d8af7a$",
+            "targetIndex": "cxx_object_exe",
+            "targetIsImportLibrary": null,
+            "targetInstallNamelink": null,
+            "exportName": null,
+            "exportTargets": null,
+            "scriptFile": null,
+            "backtrace": [
+                {
+                    "file": "^object/CMakeLists\\.txt$",
+                    "line": 13,
+                    "command": "install",
+                    "hasParent": true
+                },
+                {
+                    "file": "^object/CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                }
+            ]
+        }
+    ]
 }

+ 545 - 1
Tests/RunCMake/FileAPI/codemodel-v2-data/directories/top.json

@@ -25,5 +25,549 @@
     ],
     "projectName": "codemodel-v2",
     "minimumCMakeVersion": "3.12",
-    "hasInstallRule": true
+    "hasInstallRule": true,
+    "installers": [
+        {
+            "component": "Tools",
+            "type": "target",
+            "destination": "bin",
+            "paths": [
+                "^cxx/((Debug|Release|MinSizeRel|RelWithDebInfo)/)?cxx_exe(\\.exe)?$"
+            ],
+            "isExcludeFromAll": null,
+            "isOptional": null,
+            "targetId": "^cxx_exe::@a56b12a3f5c0529fb296$",
+            "targetIndex": "cxx_exe",
+            "targetIsImportLibrary": null,
+            "targetInstallNamelink": null,
+            "exportName": null,
+            "exportTargets": null,
+            "scriptFile": null,
+            "backtrace": [
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": 38,
+                    "command": "install",
+                    "hasParent": true
+                },
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": true
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": 3,
+                    "command": "include",
+                    "hasParent": true
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                }
+            ]
+        },
+        {
+            "component": "Unspecified",
+            "type": "target",
+            "destination": "lib",
+            "paths": [
+                "^((Debug|Release|MinSizeRel|RelWithDebInfo)/)?(lib)?c_shared_lib\\.(lib|dll\\.a)$"
+            ],
+            "isExcludeFromAll": null,
+            "isOptional": null,
+            "targetId": "^c_shared_lib::@6890427a1f51a3e7e1df$",
+            "targetIndex": "c_shared_lib",
+            "targetIsImportLibrary": true,
+            "targetInstallNamelink": null,
+            "exportName": null,
+            "exportTargets": null,
+            "scriptFile": null,
+            "_dllExtra": true,
+            "backtrace": [
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": 41,
+                    "command": "install",
+                    "hasParent": true
+                },
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": true
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": 3,
+                    "command": "include",
+                    "hasParent": true
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                }
+            ]
+        },
+        {
+            "component": "Unspecified",
+            "type": "target",
+            "destination": "lib",
+            "paths": [
+                "^lib/((Debug|Release|MinSizeRel|RelWithDebInfo)/)?(lib|cyg)?c_shared_lib(-1)?\\.(dll|so)$"
+            ],
+            "pathsNamelink": [
+                "^lib/((Debug|Release|MinSizeRel|RelWithDebInfo)/)?(lib)?c_shared_lib\\.(so\\.1\\.2\\.3|1\\.2\\.3\\.dylib)$",
+                "^lib/((Debug|Release|MinSizeRel|RelWithDebInfo)/)?(lib)?c_shared_lib\\.(so\\.1|1\\.dylib)$"
+            ],
+            "isExcludeFromAll": null,
+            "isOptional": null,
+            "targetId": "^c_shared_lib::@6890427a1f51a3e7e1df$",
+            "targetIndex": "c_shared_lib",
+            "targetIsImportLibrary": null,
+            "targetInstallNamelink": "skip",
+            "exportName": null,
+            "exportTargets": null,
+            "scriptFile": null,
+            "backtrace": [
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": 41,
+                    "command": "install",
+                    "hasParent": true
+                },
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": true
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": 3,
+                    "command": "include",
+                    "hasParent": true
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                }
+            ]
+        },
+        {
+            "component": "Unspecified",
+            "type": "target",
+            "destination": "lib",
+            "paths": [
+                "^cxx/((Debug|Release|MinSizeRel|RelWithDebInfo)/)?(lib)?cxx_shared_lib\\.(lib|dll\\.a)$"
+            ],
+            "isExcludeFromAll": null,
+            "isOptional": null,
+            "targetId": "^cxx_shared_lib::@a56b12a3f5c0529fb296$",
+            "targetIndex": "cxx_shared_lib",
+            "targetIsImportLibrary": true,
+            "targetInstallNamelink": null,
+            "exportName": null,
+            "exportTargets": null,
+            "scriptFile": null,
+            "_dllExtra": true,
+            "backtrace": [
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": 41,
+                    "command": "install",
+                    "hasParent": true
+                },
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": true
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": 3,
+                    "command": "include",
+                    "hasParent": true
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                }
+            ]
+        },
+        {
+            "component": "Unspecified",
+            "type": "target",
+            "destination": "lib",
+            "paths": [
+                "^cxx/((Debug|Release|MinSizeRel|RelWithDebInfo)/)?(lib|cyg)?cxx_shared_lib\\.(dll|so|dylib)$"
+            ],
+            "isExcludeFromAll": null,
+            "isOptional": null,
+            "targetId": "^cxx_shared_lib::@a56b12a3f5c0529fb296$",
+            "targetIndex": "cxx_shared_lib",
+            "targetIsImportLibrary": null,
+            "targetInstallNamelink": null,
+            "exportName": null,
+            "exportTargets": null,
+            "scriptFile": null,
+            "backtrace": [
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": 41,
+                    "command": "install",
+                    "hasParent": true
+                },
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": true
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": 3,
+                    "command": "include",
+                    "hasParent": true
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                }
+            ]
+        },
+        {
+            "component": "Unspecified",
+            "type": "target",
+            "destination": "lib",
+            "paths": [
+                "^lib/((Debug|Release|MinSizeRel|RelWithDebInfo)/)?(lib)?c_shared_lib\\.(dll|so|dylib)$"
+            ],
+            "isExcludeFromAll": null,
+            "isOptional": null,
+            "targetId": "^c_shared_lib::@6890427a1f51a3e7e1df$",
+            "targetIndex": "c_shared_lib",
+            "targetIsImportLibrary": null,
+            "targetInstallNamelink": "only",
+            "exportName": null,
+            "exportTargets": null,
+            "scriptFile": null,
+            "backtrace": [
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": 46,
+                    "command": "install",
+                    "hasParent": true
+                },
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": true
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": 3,
+                    "command": "include",
+                    "hasParent": true
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                }
+            ]
+        },
+        {
+            "component": "Unspecified",
+            "type": "file",
+            "destination": "include",
+            "paths": [
+                {
+                    "from": "^empty\\.h$",
+                    "to": "^empty-renamed\\.h$"
+                }
+            ],
+            "isExcludeFromAll": null,
+            "isOptional": true,
+            "targetId": null,
+            "targetIndex": null,
+            "targetIsImportLibrary": null,
+            "targetInstallNamelink": null,
+            "exportName": null,
+            "exportTargets": null,
+            "scriptFile": null,
+            "backtrace": [
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": 48,
+                    "command": "install",
+                    "hasParent": true
+                },
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": true
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": 3,
+                    "command": "include",
+                    "hasParent": true
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                }
+            ]
+        },
+        {
+            "component": "Unspecified",
+            "type": "file",
+            "destination": "include",
+            "paths": [
+                "^codemodel-v2\\.cmake$",
+                "^empty\\.h$"
+            ],
+            "isExcludeFromAll": null,
+            "isOptional": null,
+            "targetId": null,
+            "targetIndex": null,
+            "targetIsImportLibrary": null,
+            "targetInstallNamelink": null,
+            "exportName": null,
+            "exportTargets": null,
+            "scriptFile": null,
+            "backtrace": [
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": 49,
+                    "command": "install",
+                    "hasParent": true
+                },
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": true
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": 3,
+                    "command": "include",
+                    "hasParent": true
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                }
+            ]
+        },
+        {
+            "component": "Unspecified",
+            "type": "directory",
+            "destination": "dir1",
+            "paths": [
+                "^\\.$",
+                "^dir$",
+                {
+                    "from": "^cxx$",
+                    "to": "^\\.$"
+                }
+            ],
+            "isExcludeFromAll": null,
+            "isOptional": true,
+            "targetId": null,
+            "targetIndex": null,
+            "targetIsImportLibrary": null,
+            "targetInstallNamelink": null,
+            "exportName": null,
+            "exportTargets": null,
+            "scriptFile": null,
+            "backtrace": [
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": 50,
+                    "command": "install",
+                    "hasParent": true
+                },
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": true
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": 3,
+                    "command": "include",
+                    "hasParent": true
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                }
+            ]
+        },
+        {
+            "component": "Unspecified",
+            "type": "directory",
+            "destination": "dir2",
+            "paths": [
+                {
+                    "from": "^\\.$",
+                    "to": "^FileAPI$"
+                },
+                "^dir$",
+                {
+                    "from": "^cxx$",
+                    "to": "^\\.$"
+                }
+            ],
+            "isExcludeFromAll": null,
+            "isOptional": null,
+            "targetId": null,
+            "targetIndex": null,
+            "targetIsImportLibrary": null,
+            "targetInstallNamelink": null,
+            "exportName": null,
+            "exportTargets": null,
+            "scriptFile": null,
+            "backtrace": [
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": 51,
+                    "command": "install",
+                    "hasParent": true
+                },
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": true
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": 3,
+                    "command": "include",
+                    "hasParent": true
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                }
+            ]
+        },
+        {
+            "component": "Unspecified",
+            "type": "export",
+            "destination": "lib/cmake/foo",
+            "paths": [
+                "^CMakeFiles/Export/lib/cmake/foo/FooTargets\\.cmake$"
+            ],
+            "isExcludeFromAll": null,
+            "isOptional": null,
+            "targetId": null,
+            "targetIndex": null,
+            "targetIsImportLibrary": null,
+            "targetInstallNamelink": null,
+            "exportName": "FooTargets",
+            "exportTargets": [
+                {
+                    "id": "^cxx_exe::@a56b12a3f5c0529fb296$",
+                    "index": "cxx_exe"
+                }
+            ],
+            "scriptFile": null,
+            "backtrace": [
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": 52,
+                    "command": "install",
+                    "hasParent": true
+                },
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": true
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": 3,
+                    "command": "include",
+                    "hasParent": true
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                }
+            ]
+        },
+        {
+            "component": "Unspecified",
+            "type": "script",
+            "destination": null,
+            "paths": null,
+            "isExcludeFromAll": null,
+            "isOptional": null,
+            "targetId": null,
+            "targetIndex": null,
+            "targetIsImportLibrary": null,
+            "targetInstallNamelink": null,
+            "exportName": null,
+            "exportTargets": null,
+            "scriptFile": "InstallScript.cmake",
+            "backtrace": [
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": 53,
+                    "command": "install",
+                    "hasParent": true
+                },
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": true
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": 3,
+                    "command": "include",
+                    "hasParent": true
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                }
+            ]
+        }
+    ]
 }