Browse Source

Merge topic 'fileapi-codemodel-directory'

049bf98f63 fileapi: Add installers to codemodel-v2 "directory" object
eae2256a52 fileapi: Add backtraceGraph to codemodel-v2 "directory" object
a12d7f70b1 fileapi: Add a "directory" object to codemodel-v2
fd30bd93e6 fileapi: Re-organize backtrace infrastructure
415ead8153 cmFileAPICodemodel: Build map from each target to its index
f73b6879e9 cmInstallTargetGenerator: Report namelink mode with list of files
ea430582f9 cmInstallTargetGenerator: Drop unused GetNamelinkMode method

Acked-by: Kitware Robot <[email protected]>
Merge-request: !5917
Brad King 4 years ago
parent
commit
f3d9cedc8b
26 changed files with 1647 additions and 143 deletions
  1. 229 36
      Help/manual/cmake-file-api.7.rst
  2. 10 0
      Help/release/dev/fileapi-codemodel-directory.rst
  3. 1 1
      Source/cmFileAPI.cxx
  4. 386 82
      Source/cmFileAPICodemodel.cxx
  5. 1 0
      Source/cmInstallTargetGenerator.cxx
  6. 1 1
      Source/cmInstallTargetGenerator.h
  7. 1 1
      Tests/RunCMake/CommandLine/E_capabilities-stdout.txt
  8. 1 0
      Tests/RunCMake/FileAPI/codemodel-v2-ClientStateful-check.cmake
  9. 1 0
      Tests/RunCMake/FileAPI/codemodel-v2-ClientStateless-check.cmake
  10. 1 0
      Tests/RunCMake/FileAPI/codemodel-v2-SharedStateless-check.cmake
  11. 115 5
      Tests/RunCMake/FileAPI/codemodel-v2-check.py
  12. 2 1
      Tests/RunCMake/FileAPI/codemodel-v2-data/directories/alias.json
  13. 2 1
      Tests/RunCMake/FileAPI/codemodel-v2-data/directories/custom.json
  14. 2 1
      Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.json
  15. 2 1
      Tests/RunCMake/FileAPI/codemodel-v2-data/directories/dir.json
  16. 2 1
      Tests/RunCMake/FileAPI/codemodel-v2-data/directories/dir_dir.json
  17. 65 1
      Tests/RunCMake/FileAPI/codemodel-v2-data/directories/external.json
  18. 2 1
      Tests/RunCMake/FileAPI/codemodel-v2-data/directories/imported.json
  19. 2 1
      Tests/RunCMake/FileAPI/codemodel-v2-data/directories/interface.json
  20. 65 1
      Tests/RunCMake/FileAPI/codemodel-v2-data/directories/object.json
  21. 545 1
      Tests/RunCMake/FileAPI/codemodel-v2-data/directories/top.json
  22. 97 4
      Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_shared_lib.json
  23. 94 1
      Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_shared_lib.json
  24. 16 1
      Tests/RunCMake/FileAPI/codemodel-v2.cmake
  25. 1 1
      Tests/RunCMake/FileAPI/cxx/CMakeLists.txt
  26. 3 0
      Tests/RunCMake/FileAPIExternalSource/CMakeLists.txt

+ 229 - 36
Help/manual/cmake-file-api.7.rst

@@ -443,7 +443,8 @@ Version 1 does not exist to avoid confusion with that from
             "hasInstallRule": true,
             "hasInstallRule": true,
             "minimumCMakeVersion": {
             "minimumCMakeVersion": {
               "string": "3.14"
               "string": "3.14"
-            }
+            },
+            "jsonFile": "<file>"
           },
           },
           {
           {
             "source": "sub",
             "source": "sub",
@@ -453,7 +454,8 @@ Version 1 does not exist to avoid confusion with that from
             "targetIndexes": [ 1 ],
             "targetIndexes": [ 1 ],
             "minimumCMakeVersion": {
             "minimumCMakeVersion": {
               "string": "3.14"
               "string": "3.14"
-            }
+            },
+            "jsonFile": "<file>"
           }
           }
         ],
         ],
         "projects": [
         "projects": [
@@ -569,6 +571,13 @@ The members specific to ``codemodel`` objects are:
       :command:`install` rules, i.e. whether a ``make install``
       :command:`install` rules, i.e. whether a ``make install``
       or equivalent rule is available.
       or equivalent rule is available.
 
 
+    ``jsonFile``
+      A JSON string specifying a path relative to the codemodel file
+      to another JSON file containing a
+      `"codemodel" version 2 "directory" object`_.
+
+      This field was added in codemodel version 2.3.
+
   ``projects``
   ``projects``
     A JSON array of entries corresponding to the top-level project
     A JSON array of entries corresponding to the top-level project
     and sub-projects defined in the build system.  Each (sub-)project
     and sub-projects defined in the build system.  Each (sub-)project
@@ -633,6 +642,182 @@ The members specific to ``codemodel`` objects are:
       to another JSON file containing a
       to another JSON file containing a
       `"codemodel" version 2 "target" object`_.
       `"codemodel" version 2 "target" object`_.
 
 
+"codemodel" version 2 "directory" object
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+A codemodel "directory" object is referenced by a `"codemodel" version 2`_
+object's ``directories`` array.  Each "directory" object is a JSON object
+with members:
+
+``paths``
+  A JSON object containing members:
+
+  ``source``
+    A string specifying the path to the source directory, represented
+    with forward slashes.  If the directory is inside the top-level
+    source directory then the path is specified relative to that
+    directory (with ``.`` for the top-level source directory itself).
+    Otherwise the path is absolute.
+
+  ``build``
+    A string specifying the path to the build directory, represented
+    with forward slashes.  If the directory is inside the top-level
+    build directory then the path is specified relative to that
+    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.
+
 "codemodel" version 2 "target" object
 "codemodel" version 2 "target" object
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 
@@ -964,40 +1149,48 @@ with members:
       with forward slashes.
       with forward slashes.
 
 
 ``backtraceGraph``
 ``backtraceGraph``
-  A JSON object describing the graph of backtraces whose nodes are
-  referenced from ``backtrace`` members elsewhere.  The members are:
-
-  ``nodes``
-    A JSON array listing nodes in the backtrace graph.  Each entry
-    is a JSON object with members:
-
-    ``file``
-      An unsigned integer 0-based index into the backtrace ``files`` array.
-
-    ``line``
-      An optional member present when the node represents a line within
-      the file.  The value is an unsigned integer 1-based line number.
-
-    ``command``
-      An optional member present when the node represents a command
-      invocation within the file.  The value is an unsigned integer
-      0-based index into the backtrace ``commands`` array.
-
-    ``parent``
-      An optional member present when the node is not the bottom of
-      the call stack.  The value is an unsigned integer 0-based index
-      of another entry in the backtrace ``nodes`` array.
-
-  ``commands``
-    A JSON array listing command names referenced by backtrace nodes.
-    Each entry is a string specifying a command name.
-
-  ``files``
-    A JSON array listing CMake language files referenced by backtrace nodes.
-    Each entry is a string specifying the path to a file, 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.
+  A `"codemodel" version 2 "backtrace graph"`_ whose nodes are referenced
+  from ``backtrace`` members elsewhere in this "target" object.
+
+"codemodel" version 2 "backtrace graph"
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The ``backtraceGraph`` member of a `"codemodel" version 2 "directory" object`_,
+or `"codemodel" version 2 "target" object`_ is a JSON object describing a
+graph of backtraces.  Its nodes are referenced from ``backtrace`` members
+elsewhere in the containing object.  The backtrace graph object members are:
+
+``nodes``
+  A JSON array listing nodes in the backtrace graph.  Each entry
+  is a JSON object with members:
+
+  ``file``
+    An unsigned integer 0-based index into the backtrace ``files`` array.
+
+  ``line``
+    An optional member present when the node represents a line within
+    the file.  The value is an unsigned integer 1-based line number.
+
+  ``command``
+    An optional member present when the node represents a command
+    invocation within the file.  The value is an unsigned integer
+    0-based index into the backtrace ``commands`` array.
+
+  ``parent``
+    An optional member present when the node is not the bottom of
+    the call stack.  The value is an unsigned integer 0-based index
+    of another entry in the backtrace ``nodes`` array.
+
+``commands``
+  A JSON array listing command names referenced by backtrace nodes.
+  Each entry is a string specifying a command name.
+
+``files``
+  A JSON array listing CMake language files referenced by backtrace nodes.
+  Each entry is a string specifying the path to a file, 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.
 
 
 Object Kind "cache"
 Object Kind "cache"
 -------------------
 -------------------

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

@@ -0,0 +1,10 @@
+fileapi-codemodel-directory
+---------------------------
+
+* The :manual:`cmake-file-api(7)` "codemodel" version 2 ``version`` field has
+  component been updated to 2.3.
+
+* 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.

+ 1 - 1
Source/cmFileAPI.cxx

@@ -686,7 +686,7 @@ std::string cmFileAPI::NoSupportedVersion(
 
 
 // The "codemodel" object kind.
 // The "codemodel" object kind.
 
 
-static unsigned int const CodeModelV2Minor = 2;
+static unsigned int const CodeModelV2Minor = 3;
 
 
 void cmFileAPI::BuildClientRequestCodeModel(
 void cmFileAPI::BuildClientRequestCodeModel(
   ClientRequest& r, std::vector<RequestVersion> const& versions)
   ClientRequest& r, std::vector<RequestVersion> const& versions)

+ 386 - 82
Source/cmFileAPICodemodel.cxx

@@ -20,11 +20,16 @@
 #include <cm3p/json/value.h>
 #include <cm3p/json/value.h>
 
 
 #include "cmCryptoHash.h"
 #include "cmCryptoHash.h"
+#include "cmExportSet.h"
 #include "cmFileAPI.h"
 #include "cmFileAPI.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
 #include "cmGlobalGenerator.h"
+#include "cmInstallDirectoryGenerator.h"
+#include "cmInstallExportGenerator.h"
+#include "cmInstallFilesGenerator.h"
 #include "cmInstallGenerator.h"
 #include "cmInstallGenerator.h"
+#include "cmInstallScriptGenerator.h"
 #include "cmInstallSubdirectoryGenerator.h"
 #include "cmInstallSubdirectoryGenerator.h"
 #include "cmInstallTargetGenerator.h"
 #include "cmInstallTargetGenerator.h"
 #include "cmLinkLineComputer.h"
 #include "cmLinkLineComputer.h"
@@ -42,81 +47,13 @@
 #include "cmSystemTools.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTarget.h"
 #include "cmTargetDepend.h"
 #include "cmTargetDepend.h"
+#include "cmTargetExport.h"
 #include "cmake.h"
 #include "cmake.h"
 
 
 namespace {
 namespace {
 
 
-class Codemodel
-{
-  cmFileAPI& FileAPI;
-  unsigned long Version;
-
-  Json::Value DumpPaths();
-  Json::Value DumpConfigurations();
-  Json::Value DumpConfiguration(std::string const& config);
-
-public:
-  Codemodel(cmFileAPI& fileAPI, unsigned long version);
-  Json::Value Dump();
-};
-
-class CodemodelConfig
-{
-  cmFileAPI& FileAPI;
-  unsigned long Version;
-  std::string const& Config;
-  std::string TopSource;
-  std::string TopBuild;
-
-  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;
-  std::vector<Directory> Directories;
-
-  struct Project
-  {
-    cmStateSnapshot Snapshot;
-    static const Json::ArrayIndex NoParentIndex =
-      static_cast<Json::ArrayIndex>(-1);
-    Json::ArrayIndex ParentIndex = NoParentIndex;
-    Json::Value ChildIndexes = Json::arrayValue;
-    Json::Value DirectoryIndexes = Json::arrayValue;
-    Json::Value TargetIndexes = Json::arrayValue;
-  };
-  std::map<cmStateSnapshot, Json::ArrayIndex, cmStateSnapshot::StrictWeakOrder>
-    ProjectMap;
-  std::vector<Project> Projects;
-
-  void ProcessDirectories();
-
-  Json::ArrayIndex GetDirectoryIndex(cmLocalGenerator const* lg);
-  Json::ArrayIndex GetDirectoryIndex(cmStateSnapshot s);
-
-  Json::ArrayIndex AddProject(cmStateSnapshot s);
-
-  Json::Value DumpTargets();
-  Json::Value DumpTarget(cmGeneratorTarget* gt, Json::ArrayIndex ti);
-
-  Json::Value DumpDirectories();
-  Json::Value DumpDirectory(Directory& d);
-
-  Json::Value DumpProjects();
-  Json::Value DumpProject(Project& p);
-
-  Json::Value DumpMinimumCMakeVersion(cmStateSnapshot s);
-
-public:
-  CodemodelConfig(cmFileAPI& fileAPI, unsigned long version,
-                  std::string const& config);
-  Json::Value Dump();
-};
+using TargetIndexMapType =
+  std::unordered_map<cmGeneratorTarget const*, Json::ArrayIndex>;
 
 
 std::string RelativeIfUnder(std::string const& top, std::string const& in)
 std::string RelativeIfUnder(std::string const& top, std::string const& in)
 {
 {
@@ -131,16 +68,6 @@ std::string RelativeIfUnder(std::string const& top, std::string const& in)
   return out;
   return out;
 }
 }
 
 
-std::string TargetId(cmGeneratorTarget const* gt, std::string const& topBuild)
-{
-  cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_256);
-  std::string path = RelativeIfUnder(
-    topBuild, gt->GetLocalGenerator()->GetCurrentBinaryDirectory());
-  std::string hash = hasher.HashString(path);
-  hash.resize(20, '0');
-  return gt->GetName() + CMAKE_DIRECTORY_ID_SEP + hash;
-}
-
 class JBTIndex
 class JBTIndex
 {
 {
 public:
 public:
@@ -290,6 +217,91 @@ Json::Value BacktraceData::Dump()
   return backtraceGraph;
   return backtraceGraph;
 }
 }
 
 
+class Codemodel
+{
+  cmFileAPI& FileAPI;
+  unsigned long Version;
+
+  Json::Value DumpPaths();
+  Json::Value DumpConfigurations();
+  Json::Value DumpConfiguration(std::string const& config);
+
+public:
+  Codemodel(cmFileAPI& fileAPI, unsigned long version);
+  Json::Value Dump();
+};
+
+class CodemodelConfig
+{
+  cmFileAPI& FileAPI;
+  unsigned long Version;
+  std::string const& Config;
+  std::string TopSource;
+  std::string TopBuild;
+
+  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;
+  std::vector<Directory> Directories;
+
+  struct Project
+  {
+    cmStateSnapshot Snapshot;
+    static const Json::ArrayIndex NoParentIndex =
+      static_cast<Json::ArrayIndex>(-1);
+    Json::ArrayIndex ParentIndex = NoParentIndex;
+    Json::Value ChildIndexes = Json::arrayValue;
+    Json::Value DirectoryIndexes = Json::arrayValue;
+    Json::Value TargetIndexes = Json::arrayValue;
+  };
+  std::map<cmStateSnapshot, Json::ArrayIndex, cmStateSnapshot::StrictWeakOrder>
+    ProjectMap;
+  std::vector<Project> Projects;
+
+  TargetIndexMapType TargetIndexMap;
+
+  void ProcessDirectories();
+
+  Json::ArrayIndex GetDirectoryIndex(cmLocalGenerator const* lg);
+  Json::ArrayIndex GetDirectoryIndex(cmStateSnapshot s);
+
+  Json::ArrayIndex AddProject(cmStateSnapshot s);
+
+  Json::Value DumpTargets();
+  Json::Value DumpTarget(cmGeneratorTarget* gt, Json::ArrayIndex ti);
+
+  Json::Value DumpDirectories();
+  Json::Value DumpDirectory(Directory& d);
+  Json::Value DumpDirectoryObject(Directory& d);
+
+  Json::Value DumpProjects();
+  Json::Value DumpProject(Project& p);
+
+  Json::Value DumpMinimumCMakeVersion(cmStateSnapshot s);
+
+public:
+  CodemodelConfig(cmFileAPI& fileAPI, unsigned long version,
+                  std::string const& config);
+  Json::Value Dump();
+};
+
+std::string TargetId(cmGeneratorTarget const* gt, std::string const& topBuild)
+{
+  cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_256);
+  std::string path = RelativeIfUnder(
+    topBuild, gt->GetLocalGenerator()->GetCurrentBinaryDirectory());
+  std::string hash = hasher.HashString(path);
+  hash.resize(20, '0');
+  return gt->GetName() + CMAKE_DIRECTORY_ID_SEP + hash;
+}
+
 struct CompileData
 struct CompileData
 {
 {
   struct IncludeEntry
   struct IncludeEntry
@@ -367,6 +379,31 @@ struct hash<CompileData>
 } // namespace std
 } // namespace std
 
 
 namespace {
 namespace {
+class DirectoryObject
+{
+  cmLocalGenerator const* LG = nullptr;
+  std::string const& Config;
+  TargetIndexMapType& TargetIndexMap;
+  std::string TopSource;
+  std::string TopBuild;
+  BacktraceData Backtraces;
+
+  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,
+                  TargetIndexMapType& targetIndexMap);
+  Json::Value Dump();
+};
+
 class Target
 class Target
 {
 {
   cmGeneratorTarget* GT;
   cmGeneratorTarget* GT;
@@ -663,6 +700,8 @@ Json::Value CodemodelConfig::DumpTarget(cmGeneratorTarget* gt,
   target["projectIndex"] = pi;
   target["projectIndex"] = pi;
   this->Projects[pi].TargetIndexes.append(ti);
   this->Projects[pi].TargetIndexes.append(ti);
 
 
+  this->TargetIndexMap[gt] = ti;
+
   return target;
   return target;
 }
 }
 
 
@@ -677,7 +716,7 @@ Json::Value CodemodelConfig::DumpDirectories()
 
 
 Json::Value CodemodelConfig::DumpDirectory(Directory& d)
 Json::Value CodemodelConfig::DumpDirectory(Directory& d)
 {
 {
-  Json::Value directory = Json::objectValue;
+  Json::Value directory = this->DumpDirectoryObject(d);
 
 
   std::string sourceDir = d.Snapshot.GetDirectory().GetCurrentSource();
   std::string sourceDir = d.Snapshot.GetDirectory().GetCurrentSource();
   directory["source"] = RelativeIfUnder(this->TopSource, sourceDir);
   directory["source"] = RelativeIfUnder(this->TopSource, sourceDir);
@@ -717,6 +756,31 @@ Json::Value CodemodelConfig::DumpDirectory(Directory& d)
   return directory;
   return directory;
 }
 }
 
 
+Json::Value CodemodelConfig::DumpDirectoryObject(Directory& d)
+{
+  std::string prefix = "directory";
+  std::string sourceDirRel = RelativeIfUnder(
+    this->TopSource, d.Snapshot.GetDirectory().GetCurrentSource());
+  std::string buildDirRel = RelativeIfUnder(
+    this->TopBuild, d.Snapshot.GetDirectory().GetCurrentBinary());
+  if (!cmSystemTools::FileIsFullPath(buildDirRel)) {
+    prefix = cmStrCat(prefix, '-', buildDirRel);
+  } else if (!cmSystemTools::FileIsFullPath(sourceDirRel)) {
+    prefix = cmStrCat(prefix, '-', sourceDirRel);
+  }
+  for (char& c : prefix) {
+    if (c == '/' || c == '\\') {
+      c = '.';
+    }
+  }
+  if (!this->Config.empty()) {
+    prefix += "-" + this->Config;
+  }
+
+  DirectoryObject dir(d.LocalGenerator, this->Config, this->TargetIndexMap);
+  return this->FileAPI.MaybeJsonFile(dir.Dump(), prefix);
+}
+
 Json::Value CodemodelConfig::DumpProjects()
 Json::Value CodemodelConfig::DumpProjects()
 {
 {
   Json::Value projects = Json::arrayValue;
   Json::Value projects = Json::arrayValue;
@@ -760,6 +824,246 @@ Json::Value CodemodelConfig::DumpMinimumCMakeVersion(cmStateSnapshot s)
   return minimumCMakeVersion;
   return minimumCMakeVersion;
 }
 }
 
 
+DirectoryObject::DirectoryObject(cmLocalGenerator const* lg,
+                                 std::string const& config,
+                                 TargetIndexMapType& targetIndexMap)
+  : LG(lg)
+  , Config(config)
+  , TargetIndexMap(targetIndexMap)
+  , TopSource(lg->GetGlobalGenerator()->GetCMakeInstance()->GetHomeDirectory())
+  , TopBuild(
+      lg->GetGlobalGenerator()->GetCMakeInstance()->GetHomeOutputDirectory())
+  , Backtraces(this->TopSource)
+{
+}
+
+Json::Value DirectoryObject::Dump()
+{
+  Json::Value directoryObject = Json::objectValue;
+  directoryObject["paths"] = this->DumpPaths();
+  directoryObject["installers"] = this->DumpInstallers();
+  directoryObject["backtraceGraph"] = this->Backtraces.Dump();
+  return directoryObject;
+}
+
+void DirectoryObject::AddBacktrace(Json::Value& object,
+                                   cmListFileBacktrace const& bt)
+{
+  if (JBTIndex backtrace = this->Backtraces.Add(bt)) {
+    object["backtrace"] = backtrace.Index;
+  }
+}
+
+Json::Value DirectoryObject::DumpPaths()
+{
+  Json::Value paths = Json::objectValue;
+
+  std::string const& sourceDir = this->LG->GetCurrentSourceDirectory();
+  paths["source"] = RelativeIfUnder(this->TopSource, sourceDir);
+
+  std::string const& buildDir = this->LG->GetCurrentBinaryDirectory();
+  paths["build"] = RelativeIfUnder(this->TopBuild, buildDir);
+
+  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)
 Target::Target(cmGeneratorTarget* gt, std::string const& config)
   : GT(gt)
   : GT(gt)
   , Config(config)
   , Config(config)

+ 1 - 0
Source/cmInstallTargetGenerator.cxx

@@ -338,6 +338,7 @@ cmInstallTargetGenerator::Files cmInstallTargetGenerator::GetFiles(
 
 
       // Add the names based on the current namelink mode.
       // Add the names based on the current namelink mode.
       if (haveNamelink) {
       if (haveNamelink) {
+        files.NamelinkMode = this->NamelinkMode;
         // With a namelink we need to check the mode.
         // With a namelink we need to check the mode.
         if (this->NamelinkMode == NamelinkModeOnly) {
         if (this->NamelinkMode == NamelinkModeOnly) {
           // Install the namelink only.
           // Install the namelink only.

+ 1 - 1
Source/cmInstallTargetGenerator.h

@@ -39,7 +39,6 @@ public:
     NamelinkModeSkip
     NamelinkModeSkip
   };
   };
   void SetNamelinkMode(NamelinkModeType mode) { this->NamelinkMode = mode; }
   void SetNamelinkMode(NamelinkModeType mode) { this->NamelinkMode = mode; }
-  NamelinkModeType GetNamelinkMode() const { return this->NamelinkMode; }
 
 
   std::string GetInstallFilename(const std::string& config) const;
   std::string GetInstallFilename(const std::string& config) const;
 
 
@@ -82,6 +81,7 @@ public:
     // Prefix for all files in To.
     // Prefix for all files in To.
     std::string ToDir;
     std::string ToDir;
 
 
+    NamelinkModeType NamelinkMode = NamelinkModeNone;
     bool NoTweak = false;
     bool NoTweak = false;
     bool UseSourcePermissions = false;
     bool UseSourcePermissions = false;
     cmInstallType Type = cmInstallType();
     cmInstallType Type = cmInstallType();

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

@@ -1 +1 @@
-^{"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":2}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":0}]},{"kind":"toolchains","version":\[{"major":1,"minor":0}]}]},"generators":\[.*\],"serverMode":false,"version":{.*}}$
+^{"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":3}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":0}]},{"kind":"toolchains","version":\[{"major":1,"minor":0}]}]},"generators":\[.*\],"serverMode":false,"version":{.*}}$

+ 1 - 0
Tests/RunCMake/FileAPI/codemodel-v2-ClientStateful-check.cmake

@@ -4,6 +4,7 @@ set(expect
   query/client-foo/query.json
   query/client-foo/query.json
   reply
   reply
   reply/codemodel-v2-[0-9a-f]+\\.json
   reply/codemodel-v2-[0-9a-f]+\\.json
+  .*
   reply/index-[0-9.T-]+\\.json
   reply/index-[0-9.T-]+\\.json
   .*
   .*
   )
   )

+ 1 - 0
Tests/RunCMake/FileAPI/codemodel-v2-ClientStateless-check.cmake

@@ -4,6 +4,7 @@ set(expect
   query/client-foo/codemodel-v2
   query/client-foo/codemodel-v2
   reply
   reply
   reply/codemodel-v2-[0-9a-f]+\\.json
   reply/codemodel-v2-[0-9a-f]+\\.json
+  .*
   reply/index-[0-9.T-]+\\.json
   reply/index-[0-9.T-]+\\.json
   .*
   .*
   )
   )

+ 1 - 0
Tests/RunCMake/FileAPI/codemodel-v2-SharedStateless-check.cmake

@@ -3,6 +3,7 @@ set(expect
   query/codemodel-v2
   query/codemodel-v2
   reply
   reply
   reply/codemodel-v2-[0-9a-f]+\\.json
   reply/codemodel-v2-[0-9a-f]+\\.json
+  .*
   reply/index-[0-9.T-]+\\.json
   reply/index-[0-9.T-]+\\.json
   .*
   .*
   )
   )

+ 115 - 5
Tests/RunCMake/FileAPI/codemodel-v2-check.py

@@ -12,7 +12,7 @@ def read_codemodel_json_data(filename):
 def check_objects(o, g):
 def check_objects(o, g):
     assert is_list(o)
     assert is_list(o)
     assert len(o) == 1
     assert len(o) == 1
-    check_index_object(o[0], "codemodel", 2, 2, check_object_codemodel(g))
+    check_index_object(o[0], "codemodel", 2, 3, check_object_codemodel(g))
 
 
 def check_backtrace(t, b, backtrace):
 def check_backtrace(t, b, backtrace):
     btg = t["backtraceGraph"]
     btg = t["backtraceGraph"]
@@ -55,7 +55,7 @@ def check_backtraces(t, actual, expected):
 def check_directory(c):
 def check_directory(c):
     def _check(actual, expected):
     def _check(actual, expected):
         assert is_dict(actual)
         assert is_dict(actual)
-        expected_keys = ["build", "source", "projectIndex"]
+        expected_keys = ["build", "jsonFile", "source", "projectIndex"]
         assert matches(actual["build"], expected["build"])
         assert matches(actual["build"], expected["build"])
 
 
         assert is_int(actual["projectIndex"])
         assert is_int(actual["projectIndex"])
@@ -92,10 +92,99 @@ def check_directory(c):
 
 
         assert sorted(actual.keys()) == sorted(expected_keys)
         assert sorted(actual.keys()) == sorted(expected_keys)
 
 
+        assert is_string(actual["jsonFile"])
+        filepath = os.path.join(reply_dir, actual["jsonFile"])
+        with open(filepath) as f:
+            d = json.load(f)
+
+        assert is_dict(d)
+        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
     return _check
 
 
-def check_target_backtrace_graph(t):
-    btg = t["backtraceGraph"]
+def check_backtrace_graph(btg):
     assert is_dict(btg)
     assert is_dict(btg)
     assert sorted(btg.keys()) == ["commands", "files", "nodes"]
     assert sorted(btg.keys()) == ["commands", "files", "nodes"]
     assert is_list(btg["commands"])
     assert is_list(btg["commands"])
@@ -148,7 +237,7 @@ def check_target(c):
         assert is_string(obj["name"], expected["name"])
         assert is_string(obj["name"], expected["name"])
         assert matches(obj["id"], expected["id"])
         assert matches(obj["id"], expected["id"])
         assert is_string(obj["type"], expected["type"])
         assert is_string(obj["type"], expected["type"])
-        check_target_backtrace_graph(obj)
+        check_backtrace_graph(obj["backtraceGraph"])
 
 
         assert is_dict(obj["paths"])
         assert is_dict(obj["paths"])
         assert sorted(obj["paths"].keys()) == ["build", "source"]
         assert sorted(obj["paths"].keys()) == ["build", "source"]
@@ -543,6 +632,20 @@ def gen_check_directories(c, g):
         for e in expected:
         for e in expected:
             e["targetIds"] = filter_list(lambda t: not matches(t, "^\\^(ALL_BUILD|ZERO_CHECK)"), e["targetIds"])
             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
     return expected
 
 
 def check_directories(c, g):
 def check_directories(c, g):
@@ -705,6 +808,13 @@ def gen_check_targets(c, g, inSource):
     if sys.platform not in ("win32", "cygwin", "msys"):
     if sys.platform not in ("win32", "cygwin", "msys"):
         for e in expected:
         for e in expected:
             e["artifacts"] = filter_list(lambda a: not a["_dllExtra"], e["artifacts"])
             e["artifacts"] = filter_list(lambda a: not a["_dllExtra"], e["artifacts"])
+            if e["install"] is not None:
+                e["install"]["destinations"] = filter_list(lambda d: "_dllExtra" not in d or not d["_dllExtra"], e["install"]["destinations"])
+
+    else:
+        for e in expected:
+            if e["install"] is not None:
+                e["install"]["destinations"] = filter_list(lambda d: "_namelink" not in d or not d["_namelink"], e["install"]["destinations"])
 
 
     if "aix" not in sys.platform:
     if "aix" not in sys.platform:
         for e in expected:
         for e in expected:

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

@@ -11,5 +11,6 @@
     ],
     ],
     "projectName": "Alias",
     "projectName": "Alias",
     "minimumCMakeVersion": "3.12",
     "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",
     "projectName": "Custom",
     "minimumCMakeVersion": "3.12",
     "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",
     "projectName": "Cxx",
     "minimumCMakeVersion": "3.12",
     "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,
     "targetIds": null,
     "projectName": "codemodel-v2",
     "projectName": "codemodel-v2",
     "minimumCMakeVersion": "3.12",
     "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,
     "targetIds": null,
     "projectName": "codemodel-v2",
     "projectName": "codemodel-v2",
     "minimumCMakeVersion": "3.12",
     "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",
     "projectName": "External",
     "minimumCMakeVersion": "3.12",
     "minimumCMakeVersion": "3.12",
-    "hasInstallRule": null
+    "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",
     "projectName": "Imported",
     "minimumCMakeVersion": "3.12",
     "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",
     "projectName": "Interface",
     "minimumCMakeVersion": "3.12",
     "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",
     "projectName": "Object",
     "minimumCMakeVersion": "3.13",
     "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",
     "projectName": "codemodel-v2",
     "minimumCMakeVersion": "3.12",
     "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
+                }
+            ]
+        }
+    ]
 }
 }

+ 97 - 4
Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_shared_lib.json

@@ -90,10 +90,10 @@
         }
         }
     ],
     ],
     "folder": null,
     "folder": null,
-    "nameOnDisk": "^(lib|cyg)?c_shared_lib\\.(so|dylib|dll)$",
+    "nameOnDisk": "^(lib|cyg)?c_shared_lib(-1)?\\.(so|dylib|dll)$",
     "artifacts": [
     "artifacts": [
         {
         {
-            "path": "^lib/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib|cyg)?c_shared_lib\\.(so|dylib|dll)$",
+            "path": "^lib/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib|cyg)?c_shared_lib(-1)?\\.(so|dylib|dll)$",
             "_dllExtra": false
             "_dllExtra": false
         },
         },
         {
         {
@@ -101,13 +101,106 @@
             "_dllExtra": true
             "_dllExtra": true
         },
         },
         {
         {
-            "path": "^lib/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib|cyg)?c_shared_lib\\.pdb$",
+            "path": "^lib/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib|cyg)?c_shared_lib(-1)?\\.pdb$",
             "_dllExtra": true
             "_dllExtra": true
         }
         }
     ],
     ],
     "build": "^\\.$",
     "build": "^\\.$",
     "source": "^\\.$",
     "source": "^\\.$",
-    "install": null,
+    "install": {
+        "prefix": "^(/usr/local|[A-Za-z]:.*/codemodel-v2)$",
+        "destinations": [
+            {
+                "path": "lib",
+                "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
+                    }
+                ]
+            },
+            {
+                "path": "lib",
+                "_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
+                    }
+                ]
+            },
+            {
+                "path": "lib",
+                "_namelink": true,
+                "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
+                    }
+                ]
+            }
+        ]
+    },
     "link": {
     "link": {
         "language": "C",
         "language": "C",
         "lto": true,
         "lto": true,

+ 94 - 1
Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_shared_lib.json

@@ -83,7 +83,100 @@
     ],
     ],
     "build": "^cxx$",
     "build": "^cxx$",
     "source": "^cxx$",
     "source": "^cxx$",
-    "install": null,
+    "install": {
+        "prefix": "^(/usr/local|[A-Za-z]:.*/codemodel-v2)$",
+        "destinations": [
+            {
+                "path": "lib",
+                "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
+                    }
+                ]
+            },
+            {
+                "path": "lib",
+                "_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
+                    }
+                ]
+            },
+            {
+                "path": "lib",
+                "_namelink": true,
+                "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
+                    }
+                ]
+            }
+        ]
+    },
     "link": {
     "link": {
         "language": "CXX",
         "language": "CXX",
         "lto": null,
         "lto": null,

+ 16 - 1
Tests/RunCMake/FileAPI/codemodel-v2.cmake

@@ -35,4 +35,19 @@ if(_ipo)
   file(WRITE "${CMAKE_BINARY_DIR}/ipo_enabled.txt" "")
   file(WRITE "${CMAKE_BINARY_DIR}/ipo_enabled.txt" "")
 endif()
 endif()
 
 
-install(TARGETS cxx_exe)
+install(TARGETS cxx_exe COMPONENT Tools EXPORT FooTargets)
+
+set_target_properties(c_shared_lib PROPERTIES VERSION 1.2.3 SOVERSION 1)
+install(TARGETS c_shared_lib cxx_shared_lib
+  ARCHIVE DESTINATION lib
+  RUNTIME DESTINATION lib
+  LIBRARY DESTINATION lib NAMELINK_SKIP
+  )
+install(TARGETS c_shared_lib cxx_shared_lib LIBRARY NAMELINK_ONLY)
+
+install(FILES empty.h TYPE INCLUDE RENAME empty-renamed.h OPTIONAL)
+install(FILES codemodel-v2.cmake empty.h DESTINATION include)
+install(DIRECTORY . dir cxx/ OPTIONAL DESTINATION dir1)
+install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/dir" "${CMAKE_CURRENT_SOURCE_DIR}/cxx/" DESTINATION dir2)
+install(EXPORT FooTargets DESTINATION lib/cmake/foo)
+install(SCRIPT InstallScript.cmake)

+ 1 - 1
Tests/RunCMake/FileAPI/cxx/CMakeLists.txt

@@ -16,7 +16,7 @@ target_link_libraries(cxx_static_exe PRIVATE cxx_static_lib)
 
 
 target_compile_options(cxx_exe PUBLIC TargetCompileOptions)
 target_compile_options(cxx_exe PUBLIC TargetCompileOptions)
 target_link_options(cxx_exe PUBLIC TargetLinkOptions)
 target_link_options(cxx_exe PUBLIC TargetLinkOptions)
-target_link_directories(cxx_exe PUBLIC "${CMAKE_BINARY_DIR}/TargetLinkDir")
+target_link_directories(cxx_exe PUBLIC "$<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/TargetLinkDir>")
 
 
 target_precompile_headers(cxx_exe PUBLIC ../empty.h)
 target_precompile_headers(cxx_exe PUBLIC ../empty.h)
 
 

+ 3 - 0
Tests/RunCMake/FileAPIExternalSource/CMakeLists.txt

@@ -11,3 +11,6 @@ set_property(SOURCE empty.c PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY
 target_include_directories(generated_exe SYSTEM PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
 target_include_directories(generated_exe SYSTEM PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
 target_compile_definitions(generated_exe PRIVATE GENERATED_EXE=1 -DTGT_DUMMY)
 target_compile_definitions(generated_exe PRIVATE GENERATED_EXE=1 -DTGT_DUMMY)
 set_source_files_properties(empty.c PROPERTIES COMPILE_OPTIONS SRC_COMPILE_OPTIONS_DUMMY)
 set_source_files_properties(empty.c PROPERTIES COMPILE_OPTIONS SRC_COMPILE_OPTIONS_DUMMY)
+
+install(DIRECTORY . DESTINATION dir3)
+install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} EXCLUDE_FROM_ALL DESTINATION dir4)