[Cmake-commits] CMake branch, master, updated. v3.13.2-731-g6589f67

Kitware Robot kwrobot at kitware.com
Thu Dec 13 10:03:05 EST 2018


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "CMake".

The branch, master has been updated
       via  6589f67fb725df68836646ab1e30f496d58f9e36 (commit)
       via  eef87a37cdfe6493dd292e1278771a8c6c819242 (commit)
       via  04a9145af5a16ea1329e63b7708886744f50a981 (commit)
       via  0be5ed7128b59c6ac3b508fa89fe793f5cfdf07c (commit)
       via  dc90cd68778b374122162cd3a87a7b6d27ed6866 (commit)
       via  b9c6f08276951e815a1fcaef78f4e894471d4195 (commit)
       via  4b6b2a571c39439f3b07d56f36e6a927ed6d1dd8 (commit)
       via  eb8c7676a4723a44245e47630f12d4868e8e182c (commit)
       via  42f0125ceb6c34ce5a67e9954d1831b2aeb263ba (commit)
       via  c150bf087c9ba3425d91bb1103589a928fc87098 (commit)
       via  6615408193aa542833ef34c902a35831bd85d25a (commit)
       via  3f6ee75a66042370fe48833bcd5c712448ec59c0 (commit)
       via  7489e95b8ec35c7faa1f9dcfc3a6962a56969531 (commit)
       via  ea0a0601682af00706cf57f6d3e27d4f0895ecbb (commit)
       via  b29d96701b6df7d32f36ab47618ba1b7af1d9167 (commit)
       via  8ca8daeb475fdcb2d15e508fe911bbc7ede7773a (commit)
       via  2d68b2c593b0bc19273e95f98ff29b3d3cff90c7 (commit)
       via  a0841b59bdacc1e550e6607d9e44e79ae456cd19 (commit)
       via  9d5fe8e96a074b6e112d981302c77e31a1bcde00 (commit)
       via  80802a002c16249e99c24f62adb8ffbe006b97e0 (commit)
       via  ff69763ca08fbd3a9831ec0c8adb32a7111b1a46 (commit)
       via  3e922ceb5e8cc4c4d72ddcbd8b485803c49d84f1 (commit)
       via  555fa77a35b15cf1ed5163bf9fa1782079534ef1 (commit)
       via  b83fe27d8d3f25ed6e229270966b8323f5fc3ae5 (commit)
       via  7ee0abbde1656b07eb28ddacca3d22bb7aafdbd8 (commit)
       via  276fdf299306f83b12bd1cc886f5e37d61f25c59 (commit)
       via  8fce59848b52f71ae310fcd64fcf943fb2c42bf6 (commit)
       via  eb2ec41a0422e9acd4961e32f6f28c20846a292a (commit)
       via  410a3e4b22c72794d4f96e41c1d37d84d6e7e54d (commit)
       via  c3635e502c9804fc0ab781b9f20e07449d4e6c23 (commit)
       via  1c03c12b1dfe56be5f303aef022da0fed8dee609 (commit)
       via  4a8898cd6d4ab86d04f64a699a2dc95d35846744 (commit)
       via  03d40110dc791a803345150806ef3a13ba3a0957 (commit)
       via  5751a09092a35554ebdd75ea0aa05c63ec414734 (commit)
       via  5aa88f0e026c004524b88fd8d2be13ab85f17e71 (commit)
      from  a9d9e8cafccab60c209a23a7ff71fad572dd7399 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=6589f67fb725df68836646ab1e30f496d58f9e36
commit 6589f67fb725df68836646ab1e30f496d58f9e36
Merge: eef87a3 5aa88f0
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Thu Dec 13 15:01:45 2018 +0000
Commit:     Kitware Robot <kwrobot at kitware.com>
CommitDate: Thu Dec 13 10:02:14 2018 -0500

    Merge topic 'UseJava-jar-depends'
    
    5aa88f0e02 UseJava: add_jar: rebuild jar on dependencies changes
    
    Acked-by: Kitware Robot <kwrobot at kitware.com>
    Merge-request: !2727


https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=eef87a37cdfe6493dd292e1278771a8c6c819242
commit eef87a37cdfe6493dd292e1278771a8c6c819242
Merge: 04a9145 c150bf0
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Thu Dec 13 15:01:27 2018 +0000
Commit:     Kitware Robot <kwrobot at kitware.com>
CommitDate: Thu Dec 13 10:01:34 2018 -0500

    Merge topic 'server-mode-install-fix'
    
    c150bf087c Server mode: Fix regression of hasInstallRule
    
    Acked-by: Kitware Robot <kwrobot at kitware.com>
    Merge-request: !2735


https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=04a9145af5a16ea1329e63b7708886744f50a981
commit 04a9145af5a16ea1329e63b7708886744f50a981
Merge: 0be5ed7 b29d967
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Thu Dec 13 15:00:22 2018 +0000
Commit:     Kitware Robot <kwrobot at kitware.com>
CommitDate: Thu Dec 13 10:00:27 2018 -0500

    Merge topic 'update-kwsys'
    
    b29d96701b Merge branch 'upstream-KWSys' into update-kwsys
    8ca8daeb47 KWSys 2018-12-12 (abb8c680)
    
    Acked-by: Kitware Robot <kwrobot at kitware.com>
    Merge-request: !2733


https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=0be5ed7128b59c6ac3b508fa89fe793f5cfdf07c
commit 0be5ed7128b59c6ac3b508fa89fe793f5cfdf07c
Merge: dc90cd6 2d68b2c
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Thu Dec 13 14:58:30 2018 +0000
Commit:     Kitware Robot <kwrobot at kitware.com>
CommitDate: Thu Dec 13 09:58:50 2018 -0500

    Merge topic 'custom-string'
    
    2d68b2c593 String: Add str_if_stable() as a const alternative to str()
    a0841b59bd String: Add support for a ""_s string literal syntax
    9d5fe8e96a String: Add 'borrow' member to construct borrowing instances
    80802a002c String: Add support for concatenation by operator+
    ff69763ca0 String: Add a custom string type
    410a3e4b22 Add support for using C++17 string_view or a fallback
    
    Acked-by: Kitware Robot <kwrobot at kitware.com>
    Acked-by: Daniel Pfeifer <daniel at pfeifer-mail.de>
    Acked-by: Pavel Solodovnikov <hellyeahdominate at gmail.com>
    Merge-request: !2578


https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=dc90cd68778b374122162cd3a87a7b6d27ed6866
commit dc90cd68778b374122162cd3a87a7b6d27ed6866
Merge: a9d9e8c b9c6f08
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Thu Dec 13 14:57:49 2018 +0000
Commit:     Kitware Robot <kwrobot at kitware.com>
CommitDate: Thu Dec 13 09:57:59 2018 -0500

    Merge topic 'fileapi'
    
    b9c6f08276 Help: Add release note for fileapi feature
    4b6b2a571c fileapi: extend codemodel v2 with directory details
    eb8c7676a4 fileapi: extend codemodel v2 with a project model
    42f0125ceb fileapi: Add test for cmakeFiles v1
    6615408193 fileapi: add cmakeFiles v1
    3f6ee75a66 fileapi: Add test for cache v2
    7489e95b8e fileapi: add cache v2
    ea0a060168 fileapi: Add test for codemodel v2
    ...
    
    Acked-by: Kitware Robot <kwrobot at kitware.com>
    Merge-request: !2706


https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=b9c6f08276951e815a1fcaef78f4e894471d4195
commit b9c6f08276951e815a1fcaef78f4e894471d4195
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Thu Dec 6 08:08:11 2018 -0500
Commit:     Kyle Edwards <kyle.edwards at kitware.com>
CommitDate: Wed Dec 12 15:12:26 2018 -0500

    Help: Add release note for fileapi feature
    
    Fixes: #18398

diff --git a/Help/release/dev/fileapi.rst b/Help/release/dev/fileapi.rst
new file mode 100644
index 0000000..c3f03ef
--- /dev/null
+++ b/Help/release/dev/fileapi.rst
@@ -0,0 +1,5 @@
+fileapi
+-------
+
+* A file-based api for clients to get semantic buildsystem information
+  has been added.  See the :manual:`cmake-file-api(7)` manual.

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=4b6b2a571c39439f3b07d56f36e6a927ed6d1dd8
commit 4b6b2a571c39439f3b07d56f36e6a927ed6d1dd8
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Tue Nov 13 09:34:10 2018 -0500
Commit:     Kyle Edwards <kyle.edwards at kitware.com>
CommitDate: Wed Dec 12 15:12:26 2018 -0500

    fileapi: extend codemodel v2 with directory details
    
    Issue: #18398
    Co-Author: Kyle Edwards <kyle.edwards at kitware.com>

diff --git a/Help/manual/cmake-file-api.7.rst b/Help/manual/cmake-file-api.7.rst
index f35e351..f3e0208 100644
--- a/Help/manual/cmake-file-api.7.rst
+++ b/Help/manual/cmake-file-api.7.rst
@@ -433,14 +433,21 @@ Version 1 does not exist to avoid confusion with that from
             "build": ".",
             "childIndexes": [ 1 ],
             "projectIndex": 0,
-            "targetIndexes": [ 0 ]
+            "targetIndexes": [ 0 ],
+            "hasInstallRule": true,
+            "minimumCMakeVersion": {
+              "string": "3.14"
+            }
           },
           {
             "source": "sub",
             "build": "sub",
             "parentIndex": 0,
             "projectIndex": 0,
-            "targetIndexes": [ 1 ]
+            "targetIndexes": [ 1 ],
+            "minimumCMakeVersion": {
+              "string": "3.14"
+            }
           }
         ],
         "projects": [
@@ -535,6 +542,27 @@ The members specific to ``codemodel`` objects are:
       array of entries corresponding to the targets.  Each entry is an
       unsigned integer 0-based index into the main ``targets`` array.
 
+    ``minimumCMakeVersion``
+      Optional member present when a minimum required version of CMake is
+      known for the directory.  This is the ``<min>`` version given to the
+      most local call to the :command:`cmake_minimum_required(VERSION)`
+      command in the directory itself or one of its ancestors.
+      The value is a JSON object with one member:
+
+      ``string``
+        A string specifying the minimum required version in the format::
+
+          <major>.<minor>[.<patch>[.<tweak>]][<suffix>]
+
+        Each component is an unsigned integer and the suffix may be an
+        arbitrary string.
+
+    ``hasInstallRule``
+      Optional member that is present with boolean value ``true`` when
+      the directory or one of its subdirectories contains any
+      :command:`install` rules, i.e. whether a ``make install``
+      or equivalent rule is available.
+
   ``projects``
     A JSON array of entries corresponding to the top-level project
     and sub-projects defined in the build system.  Each (sub-)project
diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx
index d432c1e..078d1d5 100644
--- a/Source/cmFileAPICodemodel.cxx
+++ b/Source/cmFileAPICodemodel.cxx
@@ -8,6 +8,7 @@
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
 #include "cmInstallGenerator.h"
+#include "cmInstallSubdirectoryGenerator.h"
 #include "cmInstallTargetGenerator.h"
 #include "cmLinkLineComputer.h"
 #include "cmListFileCache.h"
@@ -62,8 +63,10 @@ class CodemodelConfig
   struct Directory
   {
     cmStateSnapshot Snapshot;
+    cmLocalGenerator const* LocalGenerator = nullptr;
     Json::Value TargetIndexes = Json::arrayValue;
     Json::ArrayIndex ProjectIndex;
+    bool HasInstallRule = false;
   };
   std::map<cmStateSnapshot, Json::ArrayIndex, cmStateSnapshot::StrictWeakOrder>
     DirectoryMap;
@@ -99,6 +102,8 @@ class CodemodelConfig
   Json::Value DumpProjects();
   Json::Value DumpProject(Project& p);
 
+  Json::Value DumpMinimumCMakeVersion(cmStateSnapshot s);
+
 public:
   CodemodelConfig(cmFileAPI& fileAPI, unsigned long version,
                   std::string const& config);
@@ -396,11 +401,36 @@ void CodemodelConfig::ProcessDirectories()
     this->Directories.emplace_back();
     Directory& d = this->Directories[directoryIndex];
     d.Snapshot = lg->GetStateSnapshot().GetBuildsystemDirectory();
+    d.LocalGenerator = lg;
     this->DirectoryMap[d.Snapshot] = directoryIndex;
 
     d.ProjectIndex = this->AddProject(d.Snapshot);
     this->Projects[d.ProjectIndex].DirectoryIndexes.append(directoryIndex);
   }
+
+  // Update directories in reverse order to process children before parents.
+  for (auto di = this->Directories.rbegin(); di != this->Directories.rend();
+       ++di) {
+    Directory& d = *di;
+
+    // Accumulate the presence of install rules on the way up.
+    for (auto gen : d.LocalGenerator->GetMakefile()->GetInstallGenerators()) {
+      if (!dynamic_cast<cmInstallSubdirectoryGenerator*>(gen)) {
+        d.HasInstallRule = true;
+        break;
+      }
+    }
+    if (!d.HasInstallRule) {
+      for (cmStateSnapshot const& child : d.Snapshot.GetChildren()) {
+        cmStateSnapshot childDir = child.GetBuildsystemDirectory();
+        Json::ArrayIndex const childIndex = this->GetDirectoryIndex(childDir);
+        if (this->Directories[childIndex].HasInstallRule) {
+          d.HasInstallRule = true;
+          break;
+        }
+      }
+    }
+  }
 }
 
 Json::ArrayIndex CodemodelConfig::GetDirectoryIndex(cmLocalGenerator const* lg)
@@ -531,6 +561,15 @@ Json::Value CodemodelConfig::DumpDirectory(Directory& d)
     directory["targetIndexes"] = std::move(d.TargetIndexes);
   }
 
+  Json::Value minimumCMakeVersion = this->DumpMinimumCMakeVersion(d.Snapshot);
+  if (!minimumCMakeVersion.isNull()) {
+    directory["minimumCMakeVersion"] = std::move(minimumCMakeVersion);
+  }
+
+  if (d.HasInstallRule) {
+    directory["hasInstallRule"] = true;
+  }
+
   return directory;
 }
 
@@ -566,6 +605,17 @@ Json::Value CodemodelConfig::DumpProject(Project& p)
   return project;
 }
 
+Json::Value CodemodelConfig::DumpMinimumCMakeVersion(cmStateSnapshot s)
+{
+  Json::Value minimumCMakeVersion;
+  if (std::string const* def =
+        s.GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION")) {
+    minimumCMakeVersion = Json::objectValue;
+    minimumCMakeVersion["string"] = *def;
+  }
+  return minimumCMakeVersion;
+}
+
 Target::Target(cmGeneratorTarget* gt, std::string const& config)
   : GT(gt)
   , Config(config)
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-check.py b/Tests/RunCMake/FileAPI/codemodel-v2-check.py
index 8111c79..18b9347 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-check.py
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-check.py
@@ -64,6 +64,16 @@ def check_directory(c):
                              missing_exception=lambda e: "Target ID: %s" % e,
                              extra_exception=lambda a: "Target ID: %s" % c["targets"][a]["id"])
 
+        if expected["minimumCMakeVersion"] is not None:
+            expected_keys.append("minimumCMakeVersion")
+            assert is_dict(actual["minimumCMakeVersion"])
+            assert sorted(actual["minimumCMakeVersion"].keys()) == ["string"]
+            assert is_string(actual["minimumCMakeVersion"]["string"], expected["minimumCMakeVersion"])
+
+        if expected["hasInstallRule"] is not None:
+            expected_keys.append("hasInstallRule")
+            assert is_bool(actual["hasInstallRule"], expected["hasInstallRule"])
+
         assert sorted(actual.keys()) == sorted(expected_keys)
 
     return _check
@@ -448,6 +458,8 @@ def gen_check_directories(c, g):
                 "^interface_exe::@6890427a1f51a3e7e1df$",
             ],
             "projectName": "codemodel-v2",
+            "minimumCMakeVersion": "3.12",
+            "hasInstallRule": True,
         },
         {
             "source": "^alias$",
@@ -461,6 +473,8 @@ def gen_check_directories(c, g):
                 "^cxx_alias_exe::@53632cba2752272bb008$",
             ],
             "projectName": "Alias",
+            "minimumCMakeVersion": "3.12",
+            "hasInstallRule": None,
         },
         {
             "source": "^custom$",
@@ -474,6 +488,8 @@ def gen_check_directories(c, g):
                 "^custom_tgt::@c11385ffed57b860da63$",
             ],
             "projectName": "Custom",
+            "minimumCMakeVersion": "3.12",
+            "hasInstallRule": None,
         },
         {
             "source": "^cxx$",
@@ -491,6 +507,8 @@ def gen_check_directories(c, g):
                 "^cxx_static_lib::@a56b12a3f5c0529fb296$",
             ],
             "projectName": "Cxx",
+            "minimumCMakeVersion": "3.12",
+            "hasInstallRule": None,
         },
         {
             "source": "^imported$",
@@ -507,6 +525,8 @@ def gen_check_directories(c, g):
                 "^link_imported_static_exe::@ba7eb709d0b48779c6c8$",
             ],
             "projectName": "Imported",
+            "minimumCMakeVersion": "3.12",
+            "hasInstallRule": None,
         },
         {
             "source": "^object$",
@@ -522,6 +542,8 @@ def gen_check_directories(c, g):
                 "^cxx_object_lib::@5ed5358f70faf8d8af7a$",
             ],
             "projectName": "Object",
+            "minimumCMakeVersion": "3.13",
+            "hasInstallRule": True,
         },
         {
             "source": "^dir$",
@@ -542,6 +564,8 @@ def gen_check_directories(c, g):
             "childSources": None,
             "targetIds": None,
             "projectName": "codemodel-v2",
+            "minimumCMakeVersion": "3.12",
+            "hasInstallRule": None,
         },
         {
             "source": "^.*/Tests/RunCMake/FileAPIExternalSource$",
@@ -554,6 +578,8 @@ def gen_check_directories(c, g):
                 "^generated_exe::@[0-9a-f]+$",
             ],
             "projectName": "External",
+            "minimumCMakeVersion": "3.12",
+            "hasInstallRule": None,
         },
     ]
 

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=eb8c7676a4723a44245e47630f12d4868e8e182c
commit eb8c7676a4723a44245e47630f12d4868e8e182c
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Tue Nov 13 07:00:33 2018 -0500
Commit:     Kyle Edwards <kyle.edwards at kitware.com>
CommitDate: Wed Dec 12 15:12:26 2018 -0500

    fileapi: extend codemodel v2 with a project model
    
    Offer clients a `project()`-centric view of the build system.  This is
    similar to the directory-centric view but consolidates subdirectories
    that do not call `project()` with a new project name.
    
    Issue: #18398
    Co-Author: Kyle Edwards <kyle.edwards at kitware.com>

diff --git a/Help/manual/cmake-file-api.7.rst b/Help/manual/cmake-file-api.7.rst
index 0dbdfd7..f35e351 100644
--- a/Help/manual/cmake-file-api.7.rst
+++ b/Help/manual/cmake-file-api.7.rst
@@ -432,24 +432,35 @@ Version 1 does not exist to avoid confusion with that from
             "source": ".",
             "build": ".",
             "childIndexes": [ 1 ],
+            "projectIndex": 0,
             "targetIndexes": [ 0 ]
           },
           {
             "source": "sub",
             "build": "sub",
             "parentIndex": 0,
+            "projectIndex": 0,
             "targetIndexes": [ 1 ]
           }
         ],
+        "projects": [
+          {
+            "name": "MyProject",
+            "directoryIndexes": [ 0, 1 ],
+            "targetIndexes": [ 0, 1 ]
+          }
+        ],
         "targets": [
           {
             "name": "MyExecutable",
             "directoryIndex": 0,
+            "projectIndex": 0,
             "jsonFile": "<file>"
           },
           {
             "name": "MyLibrary",
             "directoryIndex": 1,
+            "projectIndex": 0,
             "jsonFile": "<file>"
           }
         ]
@@ -514,12 +525,53 @@ The members specific to ``codemodel`` objects are:
       command.  Each entry is an unsigned integer 0-based index of another
       entry in the main ``directories`` array.
 
+    ``projectIndex``
+      An unsigned integer 0-based index into the main ``projects`` array
+      indicating the build system project to which the this directory belongs.
+
     ``targetIndexes``
       Optional member that is present when the directory itself has targets,
       excluding those belonging to subdirectories.  The value is a JSON
       array of entries corresponding to the targets.  Each entry is an
       unsigned integer 0-based index into the main ``targets`` array.
 
+  ``projects``
+    A JSON array of entries corresponding to the top-level project
+    and sub-projects defined in the build system.  Each (sub-)project
+    corresponds to a source directory whose ``CMakeLists.txt`` file
+    calls the :command:`project` command with a project name different
+    from its parent directory.  The first entry corresponds to the
+    top-level project.
+
+    Each entry is a JSON object containing members:
+
+    ``name``
+      A string specifying the name given to the :command:`project` command.
+
+    ``parentIndex``
+      Optional member that is present when the project is not top-level.
+      The value is an unsigned integer 0-based index of another entry in
+      the main ``projects`` array that corresponds to the parent project
+      that added this project as a sub-project.
+
+    ``childIndexes``
+      Optional member that is present when the project has sub-projects.
+      The value is a JSON array of entries corresponding to the sub-projects.
+      Each entry is an unsigned integer 0-based index of another
+      entry in the main ``projects`` array.
+
+    ``directoryIndexes``
+      A JSON array of entries corresponding to build system directories
+      that are part of the project.  The first entry corresponds to the
+      top-level directory of the project.  Each entry is an unsigned
+      integer 0-based index into the main ``directories`` array.
+
+    ``targetIndexes``
+      Optional member that is present when the project itself has targets,
+      excluding those belonging to sub-projects.  The value is a JSON
+      array of entries corresponding to the targets.  Each entry is an
+      unsigned integer 0-based index into the main ``targets`` array.
+
   ``targets``
     A JSON array of entries corresponding to the build system targets.
     Such targets are created by calls to :command:`add_executable`,
@@ -538,6 +590,10 @@ The members specific to ``codemodel`` objects are:
       An unsigned integer 0-based index into the main ``directories`` array
       indicating the build system directory in which the target is defined.
 
+    ``projectIndex``
+      An unsigned integer 0-based index into the main ``projects`` array
+      indicating the build system project in which the target is defined.
+
     ``jsonFile``
       A JSON string specifying a path relative to the codemodel file
       to another JSON file containing a
diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx
index 3d4a7cf..d432c1e 100644
--- a/Source/cmFileAPICodemodel.cxx
+++ b/Source/cmFileAPICodemodel.cxx
@@ -63,22 +63,42 @@ class CodemodelConfig
   {
     cmStateSnapshot Snapshot;
     Json::Value TargetIndexes = Json::arrayValue;
+    Json::ArrayIndex ProjectIndex;
   };
   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);
+
 public:
   CodemodelConfig(cmFileAPI& fileAPI, unsigned long version,
                   std::string const& config);
@@ -358,6 +378,7 @@ Json::Value CodemodelConfig::Dump()
   this->ProcessDirectories();
   configuration["targets"] = this->DumpTargets();
   configuration["directories"] = this->DumpDirectories();
+  configuration["projects"] = this->DumpProjects();
   return configuration;
 }
 
@@ -376,6 +397,9 @@ void CodemodelConfig::ProcessDirectories()
     Directory& d = this->Directories[directoryIndex];
     d.Snapshot = lg->GetStateSnapshot().GetBuildsystemDirectory();
     this->DirectoryMap[d.Snapshot] = directoryIndex;
+
+    d.ProjectIndex = this->AddProject(d.Snapshot);
+    this->Projects[d.ProjectIndex].DirectoryIndexes.append(directoryIndex);
   }
 }
 
@@ -392,6 +416,29 @@ Json::ArrayIndex CodemodelConfig::GetDirectoryIndex(cmStateSnapshot s)
   return i->second;
 }
 
+Json::ArrayIndex CodemodelConfig::AddProject(cmStateSnapshot s)
+{
+  cmStateSnapshot ps = s.GetBuildsystemDirectoryParent();
+  if (ps.IsValid() && ps.GetProjectName() == s.GetProjectName()) {
+    // This directory is part of its parent directory project.
+    Json::ArrayIndex const parentDirIndex = this->GetDirectoryIndex(ps);
+    return this->Directories[parentDirIndex].ProjectIndex;
+  }
+
+  // This directory starts a new project.
+  auto projectIndex = static_cast<Json::ArrayIndex>(this->Projects.size());
+  this->Projects.emplace_back();
+  Project& p = this->Projects[projectIndex];
+  p.Snapshot = s;
+  this->ProjectMap[s] = projectIndex;
+  if (ps.IsValid()) {
+    Json::ArrayIndex const parentDirIndex = this->GetDirectoryIndex(ps);
+    p.ParentIndex = this->Directories[parentDirIndex].ProjectIndex;
+    this->Projects[p.ParentIndex].ChildIndexes.append(projectIndex);
+  }
+  return projectIndex;
+}
+
 Json::Value CodemodelConfig::DumpTargets()
 {
   Json::Value targets = Json::arrayValue;
@@ -437,6 +484,11 @@ Json::Value CodemodelConfig::DumpTarget(cmGeneratorTarget* gt,
   target["directoryIndex"] = di;
   this->Directories[di].TargetIndexes.append(ti);
 
+  // Cross-reference project containing target.
+  Json::ArrayIndex pi = this->Directories[di].ProjectIndex;
+  target["projectIndex"] = pi;
+  this->Projects[pi].TargetIndexes.append(ti);
+
   return target;
 }
 
@@ -473,6 +525,8 @@ Json::Value CodemodelConfig::DumpDirectory(Directory& d)
     directory["childIndexes"] = std::move(childIndexes);
   }
 
+  directory["projectIndex"] = d.ProjectIndex;
+
   if (!d.TargetIndexes.empty()) {
     directory["targetIndexes"] = std::move(d.TargetIndexes);
   }
@@ -480,6 +534,38 @@ Json::Value CodemodelConfig::DumpDirectory(Directory& d)
   return directory;
 }
 
+Json::Value CodemodelConfig::DumpProjects()
+{
+  Json::Value projects = Json::arrayValue;
+  for (Project& p : this->Projects) {
+    projects.append(this->DumpProject(p));
+  }
+  return projects;
+}
+
+Json::Value CodemodelConfig::DumpProject(Project& p)
+{
+  Json::Value project = Json::objectValue;
+
+  project["name"] = p.Snapshot.GetProjectName();
+
+  if (p.ParentIndex != Project::NoParentIndex) {
+    project["parentIndex"] = p.ParentIndex;
+  }
+
+  if (!p.ChildIndexes.empty()) {
+    project["childIndexes"] = std::move(p.ChildIndexes);
+  }
+
+  project["directoryIndexes"] = std::move(p.DirectoryIndexes);
+
+  if (!p.TargetIndexes.empty()) {
+    project["targetIndexes"] = std::move(p.TargetIndexes);
+  }
+
+  return project;
+}
+
 Target::Target(cmGeneratorTarget* gt, std::string const& config)
   : GT(gt)
   , Config(config)
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-check.py b/Tests/RunCMake/FileAPI/codemodel-v2-check.py
index e82bddd..8111c79 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-check.py
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-check.py
@@ -39,9 +39,12 @@ def check_backtrace(t, b, backtrace):
 def check_directory(c):
     def _check(actual, expected):
         assert is_dict(actual)
-        expected_keys = ["build", "source"]
+        expected_keys = ["build", "source", "projectIndex"]
         assert matches(actual["build"], expected["build"])
 
+        assert is_int(actual["projectIndex"])
+        assert is_string(c["projects"][actual["projectIndex"]]["name"], expected["projectName"])
+
         if expected["parentSource"] is not None:
             expected_keys.append("parentIndex")
             assert is_int(actual["parentIndex"])
@@ -102,11 +105,13 @@ def check_target_backtrace_graph(t):
 def check_target(c):
     def _check(actual, expected):
         assert is_dict(actual)
-        assert sorted(actual.keys()) == ["directoryIndex", "id", "jsonFile", "name"]
+        assert sorted(actual.keys()) == ["directoryIndex", "id", "jsonFile", "name", "projectIndex"]
         assert is_int(actual["directoryIndex"])
         assert matches(c["directories"][actual["directoryIndex"]]["source"], expected["directorySource"])
         assert is_string(actual["name"], expected["name"])
         assert is_string(actual["jsonFile"])
+        assert is_int(actual["projectIndex"])
+        assert is_string(c["projects"][actual["projectIndex"]]["name"], expected["projectName"])
 
         filepath = os.path.join(reply_dir, actual["jsonFile"])
         with open(filepath) as f:
@@ -383,6 +388,39 @@ def check_target(c):
 
     return _check
 
+def check_project(c):
+    def _check(actual, expected):
+        assert is_dict(actual)
+        expected_keys = ["name", "directoryIndexes"]
+
+        check_list_match(lambda a, e: matches(c["directories"][a]["source"], e),
+                         actual["directoryIndexes"], expected["directorySources"],
+                         missing_exception=lambda e: "Directory source: %s" % e,
+                         extra_exception=lambda a: "Directory source: %s" % c["directories"][a]["source"])
+
+        if expected["parentName"] is not None:
+            expected_keys.append("parentIndex")
+            assert is_int(actual["parentIndex"])
+            assert is_string(c["projects"][actual["parentIndex"]]["name"], expected["parentName"])
+
+        if expected["childNames"] is not None:
+            expected_keys.append("childIndexes")
+            check_list_match(lambda a, e: is_string(c["projects"][a]["name"], e),
+                             actual["childIndexes"], expected["childNames"],
+                             missing_exception=lambda e: "Child name: %s" % e,
+                             extra_exception=lambda a: "Child name: %s" % c["projects"][a]["name"])
+
+        if expected["targetIds"] is not None:
+            expected_keys.append("targetIndexes")
+            check_list_match(lambda a, e: matches(c["targets"][a]["id"], e),
+                             actual["targetIndexes"], expected["targetIds"],
+                             missing_exception=lambda e: "Target ID: %s" % e,
+                             extra_exception=lambda a: "Target ID: %s" % c["targets"][a]["id"])
+
+        assert sorted(actual.keys()) == sorted(expected_keys)
+
+    return _check
+
 def gen_check_directories(c, g):
     expected = [
         {
@@ -396,6 +434,7 @@ def gen_check_directories(c, g):
                 "^imported$",
                 "^object$",
                 "^.*/Tests/RunCMake/FileAPIExternalSource$",
+                "^dir$",
             ],
             "targetIds": [
                 "^ALL_BUILD::@6890427a1f51a3e7e1df$",
@@ -408,6 +447,7 @@ def gen_check_directories(c, g):
                 "^c_static_lib::@6890427a1f51a3e7e1df$",
                 "^interface_exe::@6890427a1f51a3e7e1df$",
             ],
+            "projectName": "codemodel-v2",
         },
         {
             "source": "^alias$",
@@ -420,6 +460,7 @@ def gen_check_directories(c, g):
                 "^c_alias_exe::@53632cba2752272bb008$",
                 "^cxx_alias_exe::@53632cba2752272bb008$",
             ],
+            "projectName": "Alias",
         },
         {
             "source": "^custom$",
@@ -432,6 +473,7 @@ def gen_check_directories(c, g):
                 "^custom_exe::@c11385ffed57b860da63$",
                 "^custom_tgt::@c11385ffed57b860da63$",
             ],
+            "projectName": "Custom",
         },
         {
             "source": "^cxx$",
@@ -448,6 +490,7 @@ def gen_check_directories(c, g):
                 "^cxx_static_exe::@a56b12a3f5c0529fb296$",
                 "^cxx_static_lib::@a56b12a3f5c0529fb296$",
             ],
+            "projectName": "Cxx",
         },
         {
             "source": "^imported$",
@@ -463,6 +506,7 @@ def gen_check_directories(c, g):
                 "^link_imported_shared_exe::@ba7eb709d0b48779c6c8$",
                 "^link_imported_static_exe::@ba7eb709d0b48779c6c8$",
             ],
+            "projectName": "Imported",
         },
         {
             "source": "^object$",
@@ -477,6 +521,27 @@ def gen_check_directories(c, g):
                 "^cxx_object_exe::@5ed5358f70faf8d8af7a$",
                 "^cxx_object_lib::@5ed5358f70faf8d8af7a$",
             ],
+            "projectName": "Object",
+        },
+        {
+            "source": "^dir$",
+            "build": "^dir$",
+            "parentSource": "^\\.$",
+            "childSources": [
+                "^dir/dir$",
+            ],
+            "targetIds": None,
+            "projectName": "codemodel-v2",
+            "minimumCMakeVersion": "3.12",
+            "hasInstallRule": None,
+        },
+        {
+            "source": "^dir/dir$",
+            "build": "^dir/dir$",
+            "parentSource": "^dir$",
+            "childSources": None,
+            "targetIds": None,
+            "projectName": "codemodel-v2",
         },
         {
             "source": "^.*/Tests/RunCMake/FileAPIExternalSource$",
@@ -488,6 +553,7 @@ def gen_check_directories(c, g):
                 "^ZERO_CHECK::@[0-9a-f]+$",
                 "^generated_exe::@[0-9a-f]+$",
             ],
+            "projectName": "External",
         },
     ]
 
@@ -520,6 +586,7 @@ def gen_check_targets(c, g, inSource):
             "name": "ALL_BUILD",
             "id": "^ALL_BUILD::@6890427a1f51a3e7e1df$",
             "directorySource": "^\\.$",
+            "projectName": "codemodel-v2",
             "type": "UTILITY",
             "isGeneratorProvided": True,
             "sources": [
@@ -698,6 +765,7 @@ def gen_check_targets(c, g, inSource):
             "name": "ZERO_CHECK",
             "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$",
             "directorySource": "^\\.$",
+            "projectName": "codemodel-v2",
             "type": "UTILITY",
             "isGeneratorProvided": True,
             "sources": [
@@ -767,6 +835,7 @@ def gen_check_targets(c, g, inSource):
             "name": "interface_exe",
             "id": "^interface_exe::@6890427a1f51a3e7e1df$",
             "directorySource": "^\\.$",
+            "projectName": "codemodel-v2",
             "type": "EXECUTABLE",
             "isGeneratorProvided": None,
             "sources": [
@@ -911,6 +980,7 @@ def gen_check_targets(c, g, inSource):
             "name": "c_lib",
             "id": "^c_lib::@6890427a1f51a3e7e1df$",
             "directorySource": "^\\.$",
+            "projectName": "codemodel-v2",
             "type": "STATIC_LIBRARY",
             "isGeneratorProvided": None,
             "sources": [
@@ -1017,6 +1087,7 @@ def gen_check_targets(c, g, inSource):
             "name": "c_exe",
             "id": "^c_exe::@6890427a1f51a3e7e1df$",
             "directorySource": "^\\.$",
+            "projectName": "codemodel-v2",
             "type": "EXECUTABLE",
             "isGeneratorProvided": None,
             "sources": [
@@ -1157,6 +1228,7 @@ def gen_check_targets(c, g, inSource):
             "name": "c_shared_lib",
             "id": "^c_shared_lib::@6890427a1f51a3e7e1df$",
             "directorySource": "^\\.$",
+            "projectName": "codemodel-v2",
             "type": "SHARED_LIBRARY",
             "isGeneratorProvided": None,
             "sources": [
@@ -1277,6 +1349,7 @@ def gen_check_targets(c, g, inSource):
             "name": "c_shared_exe",
             "id": "^c_shared_exe::@6890427a1f51a3e7e1df$",
             "directorySource": "^\\.$",
+            "projectName": "codemodel-v2",
             "type": "EXECUTABLE",
             "isGeneratorProvided": None,
             "sources": [
@@ -1417,6 +1490,7 @@ def gen_check_targets(c, g, inSource):
             "name": "c_static_lib",
             "id": "^c_static_lib::@6890427a1f51a3e7e1df$",
             "directorySource": "^\\.$",
+            "projectName": "codemodel-v2",
             "type": "STATIC_LIBRARY",
             "isGeneratorProvided": None,
             "sources": [
@@ -1523,6 +1597,7 @@ def gen_check_targets(c, g, inSource):
             "name": "c_static_exe",
             "id": "^c_static_exe::@6890427a1f51a3e7e1df$",
             "directorySource": "^\\.$",
+            "projectName": "codemodel-v2",
             "type": "EXECUTABLE",
             "isGeneratorProvided": None,
             "sources": [
@@ -1663,6 +1738,7 @@ def gen_check_targets(c, g, inSource):
             "name": "ALL_BUILD",
             "id": "^ALL_BUILD::@a56b12a3f5c0529fb296$",
             "directorySource": "^cxx$",
+            "projectName": "Cxx",
             "type": "UTILITY",
             "isGeneratorProvided": True,
             "sources": [
@@ -1761,6 +1837,7 @@ def gen_check_targets(c, g, inSource):
             "name": "ZERO_CHECK",
             "id": "^ZERO_CHECK::@a56b12a3f5c0529fb296$",
             "directorySource": "^cxx$",
+            "projectName": "Cxx",
             "type": "UTILITY",
             "isGeneratorProvided": True,
             "sources": [
@@ -1830,6 +1907,7 @@ def gen_check_targets(c, g, inSource):
             "name": "cxx_lib",
             "id": "^cxx_lib::@a56b12a3f5c0529fb296$",
             "directorySource": "^cxx$",
+            "projectName": "Cxx",
             "type": "STATIC_LIBRARY",
             "isGeneratorProvided": None,
             "sources": [
@@ -1912,6 +1990,7 @@ def gen_check_targets(c, g, inSource):
             "name": "cxx_exe",
             "id": "^cxx_exe::@a56b12a3f5c0529fb296$",
             "directorySource": "^cxx$",
+            "projectName": "Cxx",
             "type": "EXECUTABLE",
             "isGeneratorProvided": None,
             "sources": [
@@ -2016,6 +2095,7 @@ def gen_check_targets(c, g, inSource):
             "name": "cxx_shared_lib",
             "id": "^cxx_shared_lib::@a56b12a3f5c0529fb296$",
             "directorySource": "^cxx$",
+            "projectName": "Cxx",
             "type": "SHARED_LIBRARY",
             "isGeneratorProvided": None,
             "sources": [
@@ -2112,6 +2192,7 @@ def gen_check_targets(c, g, inSource):
             "name": "cxx_shared_exe",
             "id": "^cxx_shared_exe::@a56b12a3f5c0529fb296$",
             "directorySource": "^cxx$",
+            "projectName": "Cxx",
             "type": "EXECUTABLE",
             "isGeneratorProvided": None,
             "sources": [
@@ -2216,6 +2297,7 @@ def gen_check_targets(c, g, inSource):
             "name": "cxx_static_lib",
             "id": "^cxx_static_lib::@a56b12a3f5c0529fb296$",
             "directorySource": "^cxx$",
+            "projectName": "Cxx",
             "type": "STATIC_LIBRARY",
             "isGeneratorProvided": None,
             "sources": [
@@ -2298,6 +2380,7 @@ def gen_check_targets(c, g, inSource):
             "name": "cxx_static_exe",
             "id": "^cxx_static_exe::@a56b12a3f5c0529fb296$",
             "directorySource": "^cxx$",
+            "projectName": "Cxx",
             "type": "EXECUTABLE",
             "isGeneratorProvided": None,
             "sources": [
@@ -2402,6 +2485,7 @@ def gen_check_targets(c, g, inSource):
             "name": "ALL_BUILD",
             "id": "^ALL_BUILD::@53632cba2752272bb008$",
             "directorySource": "^alias$",
+            "projectName": "Alias",
             "type": "UTILITY",
             "isGeneratorProvided": True,
             "sources": [
@@ -2484,6 +2568,7 @@ def gen_check_targets(c, g, inSource):
             "name": "ZERO_CHECK",
             "id": "^ZERO_CHECK::@53632cba2752272bb008$",
             "directorySource": "^alias$",
+            "projectName": "Alias",
             "type": "UTILITY",
             "isGeneratorProvided": True,
             "sources": [
@@ -2553,6 +2638,7 @@ def gen_check_targets(c, g, inSource):
             "name": "c_alias_exe",
             "id": "^c_alias_exe::@53632cba2752272bb008$",
             "directorySource": "^alias$",
+            "projectName": "Alias",
             "type": "EXECUTABLE",
             "isGeneratorProvided": None,
             "sources": [
@@ -2657,6 +2743,7 @@ def gen_check_targets(c, g, inSource):
             "name": "cxx_alias_exe",
             "id": "^cxx_alias_exe::@53632cba2752272bb008$",
             "directorySource": "^alias$",
+            "projectName": "Alias",
             "type": "EXECUTABLE",
             "isGeneratorProvided": None,
             "sources": [
@@ -2761,6 +2848,7 @@ def gen_check_targets(c, g, inSource):
             "name": "ALL_BUILD",
             "id": "^ALL_BUILD::@5ed5358f70faf8d8af7a$",
             "directorySource": "^object$",
+            "projectName": "Object",
             "type": "UTILITY",
             "isGeneratorProvided": True,
             "sources": [
@@ -2851,6 +2939,7 @@ def gen_check_targets(c, g, inSource):
             "name": "ZERO_CHECK",
             "id": "^ZERO_CHECK::@5ed5358f70faf8d8af7a$",
             "directorySource": "^object$",
+            "projectName": "Object",
             "type": "UTILITY",
             "isGeneratorProvided": True,
             "sources": [
@@ -2920,6 +3009,7 @@ def gen_check_targets(c, g, inSource):
             "name": "c_object_lib",
             "id": "^c_object_lib::@5ed5358f70faf8d8af7a$",
             "directorySource": "^object$",
+            "projectName": "Object",
             "type": "OBJECT_LIBRARY",
             "isGeneratorProvided": None,
             "sources": [
@@ -3000,6 +3090,7 @@ def gen_check_targets(c, g, inSource):
             "name": "c_object_exe",
             "id": "^c_object_exe::@5ed5358f70faf8d8af7a$",
             "directorySource": "^object$",
+            "projectName": "Object",
             "type": "EXECUTABLE",
             "isGeneratorProvided": None,
             "sources": [
@@ -3141,6 +3232,7 @@ def gen_check_targets(c, g, inSource):
             "name": "cxx_object_lib",
             "id": "^cxx_object_lib::@5ed5358f70faf8d8af7a$",
             "directorySource": "^object$",
+            "projectName": "Object",
             "type": "OBJECT_LIBRARY",
             "isGeneratorProvided": None,
             "sources": [
@@ -3221,6 +3313,7 @@ def gen_check_targets(c, g, inSource):
             "name": "cxx_object_exe",
             "id": "^cxx_object_exe::@5ed5358f70faf8d8af7a$",
             "directorySource": "^object$",
+            "projectName": "Object",
             "type": "EXECUTABLE",
             "isGeneratorProvided": None,
             "sources": [
@@ -3362,6 +3455,7 @@ def gen_check_targets(c, g, inSource):
             "name": "ALL_BUILD",
             "id": "^ALL_BUILD::@ba7eb709d0b48779c6c8$",
             "directorySource": "^imported$",
+            "projectName": "Imported",
             "type": "UTILITY",
             "isGeneratorProvided": True,
             "sources": [
@@ -3456,6 +3550,7 @@ def gen_check_targets(c, g, inSource):
             "name": "ZERO_CHECK",
             "id": "^ZERO_CHECK::@ba7eb709d0b48779c6c8$",
             "directorySource": "^imported$",
+            "projectName": "Imported",
             "type": "UTILITY",
             "isGeneratorProvided": True,
             "sources": [
@@ -3525,6 +3620,7 @@ def gen_check_targets(c, g, inSource):
             "name": "link_imported_exe",
             "id": "^link_imported_exe::@ba7eb709d0b48779c6c8$",
             "directorySource": "^imported$",
+            "projectName": "Imported",
             "type": "EXECUTABLE",
             "isGeneratorProvided": None,
             "sources": [
@@ -3612,6 +3708,7 @@ def gen_check_targets(c, g, inSource):
             "name": "link_imported_shared_exe",
             "id": "^link_imported_shared_exe::@ba7eb709d0b48779c6c8$",
             "directorySource": "^imported$",
+            "projectName": "Imported",
             "type": "EXECUTABLE",
             "isGeneratorProvided": None,
             "sources": [
@@ -3699,6 +3796,7 @@ def gen_check_targets(c, g, inSource):
             "name": "link_imported_static_exe",
             "id": "^link_imported_static_exe::@ba7eb709d0b48779c6c8$",
             "directorySource": "^imported$",
+            "projectName": "Imported",
             "type": "EXECUTABLE",
             "isGeneratorProvided": None,
             "sources": [
@@ -3786,6 +3884,7 @@ def gen_check_targets(c, g, inSource):
             "name": "link_imported_object_exe",
             "id": "^link_imported_object_exe::@ba7eb709d0b48779c6c8$",
             "directorySource": "^imported$",
+            "projectName": "Imported",
             "type": "EXECUTABLE",
             "isGeneratorProvided": None,
             "sources": [
@@ -3873,6 +3972,7 @@ def gen_check_targets(c, g, inSource):
             "name": "link_imported_interface_exe",
             "id": "^link_imported_interface_exe::@ba7eb709d0b48779c6c8$",
             "directorySource": "^imported$",
+            "projectName": "Imported",
             "type": "EXECUTABLE",
             "isGeneratorProvided": None,
             "sources": [
@@ -3960,6 +4060,7 @@ def gen_check_targets(c, g, inSource):
             "name": "ALL_BUILD",
             "id": "^ALL_BUILD::@c11385ffed57b860da63$",
             "directorySource": "^custom$",
+            "projectName": "Custom",
             "type": "UTILITY",
             "isGeneratorProvided": True,
             "sources": [
@@ -4038,6 +4139,7 @@ def gen_check_targets(c, g, inSource):
             "name": "ZERO_CHECK",
             "id": "^ZERO_CHECK::@c11385ffed57b860da63$",
             "directorySource": "^custom$",
+            "projectName": "Custom",
             "type": "UTILITY",
             "isGeneratorProvided": True,
             "sources": [
@@ -4107,6 +4209,7 @@ def gen_check_targets(c, g, inSource):
             "name": "custom_tgt",
             "id": "^custom_tgt::@c11385ffed57b860da63$",
             "directorySource": "^custom$",
+            "projectName": "Custom",
             "type": "UTILITY",
             "isGeneratorProvided": None,
             "sources": [
@@ -4193,6 +4296,7 @@ def gen_check_targets(c, g, inSource):
             "name": "custom_exe",
             "id": "^custom_exe::@c11385ffed57b860da63$",
             "directorySource": "^custom$",
+            "projectName": "Custom",
             "type": "EXECUTABLE",
             "isGeneratorProvided": None,
             "sources": [
@@ -4297,6 +4401,7 @@ def gen_check_targets(c, g, inSource):
             "name": "ALL_BUILD",
             "id": "^ALL_BUILD::@[0-9a-f]+$",
             "directorySource": "^.*/Tests/RunCMake/FileAPIExternalSource$",
+            "projectName": "External",
             "type": "UTILITY",
             "isGeneratorProvided": True,
             "sources": [
@@ -4375,6 +4480,7 @@ def gen_check_targets(c, g, inSource):
             "name": "ZERO_CHECK",
             "id": "^ZERO_CHECK::@[0-9a-f]+$",
             "directorySource": "^.*/Tests/RunCMake/FileAPIExternalSource$",
+            "projectName": "External",
             "type": "UTILITY",
             "isGeneratorProvided": True,
             "sources": [
@@ -4444,6 +4550,7 @@ def gen_check_targets(c, g, inSource):
             "name": "generated_exe",
             "id": "^generated_exe::@[0-9a-f]+$",
             "directorySource": "^.*/Tests/RunCMake/FileAPIExternalSource$",
+            "projectName": "External",
             "type": "EXECUTABLE",
             "isGeneratorProvided": None,
             "sources": [
@@ -4772,11 +4879,159 @@ def check_targets(c, g, inSource):
                      missing_exception=lambda e: "Target ID: %s" % e["id"],
                      extra_exception=lambda a: "Target ID: %s" % a["id"])
 
+def gen_check_projects(c, g):
+    expected = [
+        {
+            "name": "codemodel-v2",
+            "parentName": None,
+            "childNames": [
+                "Alias",
+                "Custom",
+                "Cxx",
+                "Imported",
+                "Object",
+                "External",
+            ],
+            "directorySources": [
+                "^\\.$",
+                "^dir$",
+                "^dir/dir$",
+            ],
+            "targetIds": [
+                "^ALL_BUILD::@6890427a1f51a3e7e1df$",
+                "^ZERO_CHECK::@6890427a1f51a3e7e1df$",
+                "^interface_exe::@6890427a1f51a3e7e1df$",
+                "^c_lib::@6890427a1f51a3e7e1df$",
+                "^c_exe::@6890427a1f51a3e7e1df$",
+                "^c_shared_lib::@6890427a1f51a3e7e1df$",
+                "^c_shared_exe::@6890427a1f51a3e7e1df$",
+                "^c_static_lib::@6890427a1f51a3e7e1df$",
+                "^c_static_exe::@6890427a1f51a3e7e1df$",
+            ],
+        },
+        {
+            "name": "Cxx",
+            "parentName": "codemodel-v2",
+            "childNames": None,
+            "directorySources": [
+                "^cxx$",
+            ],
+            "targetIds": [
+                "^ALL_BUILD::@a56b12a3f5c0529fb296$",
+                "^ZERO_CHECK::@a56b12a3f5c0529fb296$",
+                "^cxx_lib::@a56b12a3f5c0529fb296$",
+                "^cxx_exe::@a56b12a3f5c0529fb296$",
+                "^cxx_shared_lib::@a56b12a3f5c0529fb296$",
+                "^cxx_shared_exe::@a56b12a3f5c0529fb296$",
+                "^cxx_static_lib::@a56b12a3f5c0529fb296$",
+                "^cxx_static_exe::@a56b12a3f5c0529fb296$",
+            ],
+        },
+        {
+            "name": "Alias",
+            "parentName": "codemodel-v2",
+            "childNames": None,
+            "directorySources": [
+                "^alias$",
+            ],
+            "targetIds": [
+                "^ALL_BUILD::@53632cba2752272bb008$",
+                "^ZERO_CHECK::@53632cba2752272bb008$",
+                "^c_alias_exe::@53632cba2752272bb008$",
+                "^cxx_alias_exe::@53632cba2752272bb008$",
+            ],
+        },
+        {
+            "name": "Object",
+            "parentName": "codemodel-v2",
+            "childNames": None,
+            "directorySources": [
+                "^object$",
+            ],
+            "targetIds": [
+                "^ALL_BUILD::@5ed5358f70faf8d8af7a$",
+                "^ZERO_CHECK::@5ed5358f70faf8d8af7a$",
+                "^c_object_lib::@5ed5358f70faf8d8af7a$",
+                "^c_object_exe::@5ed5358f70faf8d8af7a$",
+                "^cxx_object_lib::@5ed5358f70faf8d8af7a$",
+                "^cxx_object_exe::@5ed5358f70faf8d8af7a$",
+            ],
+        },
+        {
+            "name": "Imported",
+            "parentName": "codemodel-v2",
+            "childNames": None,
+            "directorySources": [
+                "^imported$",
+            ],
+            "targetIds": [
+                "^ALL_BUILD::@ba7eb709d0b48779c6c8$",
+                "^ZERO_CHECK::@ba7eb709d0b48779c6c8$",
+                "^link_imported_exe::@ba7eb709d0b48779c6c8$",
+                "^link_imported_shared_exe::@ba7eb709d0b48779c6c8$",
+                "^link_imported_static_exe::@ba7eb709d0b48779c6c8$",
+                "^link_imported_object_exe::@ba7eb709d0b48779c6c8$",
+                "^link_imported_interface_exe::@ba7eb709d0b48779c6c8$",
+            ],
+        },
+        {
+            "name": "Custom",
+            "parentName": "codemodel-v2",
+            "childNames": None,
+            "directorySources": [
+                "^custom$",
+            ],
+            "targetIds": [
+                "^ALL_BUILD::@c11385ffed57b860da63$",
+                "^ZERO_CHECK::@c11385ffed57b860da63$",
+                "^custom_tgt::@c11385ffed57b860da63$",
+                "^custom_exe::@c11385ffed57b860da63$",
+            ],
+        },
+        {
+            "name": "External",
+            "parentName": "codemodel-v2",
+            "childNames": None,
+            "directorySources": [
+                "^.*/Tests/RunCMake/FileAPIExternalSource$",
+            ],
+            "targetIds": [
+                "^ALL_BUILD::@[0-9a-f]+$",
+                "^ZERO_CHECK::@[0-9a-f]+$",
+                "^generated_exe::@[0-9a-f]+$",
+            ],
+        },
+    ]
+
+    if matches(g, "^Visual Studio "):
+        for e in expected:
+            if e["parentName"] is not None:
+                e["targetIds"] = filter_list(lambda t: not matches(t, "^\\^ZERO_CHECK"), e["targetIds"])
+
+    elif g == "Xcode":
+        if ';' in os.environ.get("CMAKE_OSX_ARCHITECTURES", ""):
+            for e in expected:
+                e["targetIds"] = filter_list(lambda t: not matches(t, "^\\^(link_imported_object_exe)"), e["targetIds"])
+
+    else:
+        for e in expected:
+            e["targetIds"] = filter_list(lambda t: not matches(t, "^\\^(ALL_BUILD|ZERO_CHECK)"), e["targetIds"])
+
+    return expected
+
+def check_projects(c, g):
+    check_list_match(lambda a, e: is_string(a["name"], e["name"]), c["projects"], gen_check_projects(c, g),
+                     check=check_project(c),
+                     check_exception=lambda a, e: "Project name: %s" % a["name"],
+                     missing_exception=lambda e: "Project name: %s" % e["name"],
+                     extra_exception=lambda a: "Project name: %s" % a["name"])
+
 def check_object_codemodel_configuration(c, g, inSource):
-    assert sorted(c.keys()) == ["directories", "name", "targets"]
+    assert sorted(c.keys()) == ["directories", "name", "projects", "targets"]
     assert is_string(c["name"])
     check_directories(c, g)
     check_targets(c, g, inSource)
+    check_projects(c, g)
 
 def check_object_codemodel(g):
     def _check(o):
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2.cmake b/Tests/RunCMake/FileAPI/codemodel-v2.cmake
index dca1dd1..72073d5 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2.cmake
+++ b/Tests/RunCMake/FileAPI/codemodel-v2.cmake
@@ -20,6 +20,7 @@ add_subdirectory(object)
 add_subdirectory(imported)
 add_subdirectory(custom)
 add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../FileAPIExternalSource" "${CMAKE_CURRENT_BINARY_DIR}/../FileAPIExternalBuild")
+add_subdirectory(dir)
 
 set_property(TARGET c_shared_lib PROPERTY LIBRARY_OUTPUT_DIRECTORY lib)
 set_property(TARGET c_shared_lib PROPERTY RUNTIME_OUTPUT_DIRECTORY lib)

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=42f0125ceb6c34ce5a67e9954d1831b2aeb263ba
commit 42f0125ceb6c34ce5a67e9954d1831b2aeb263ba
Author:     Kyle Edwards <kyle.edwards at kitware.com>
AuthorDate: Mon Nov 12 15:23:17 2018 -0500
Commit:     Kyle Edwards <kyle.edwards at kitware.com>
CommitDate: Wed Dec 12 13:02:31 2018 -0500

    fileapi: Add test for cmakeFiles v1

diff --git a/Tests/RunCMake/FileAPI/cmakeFiles-v1-check.py b/Tests/RunCMake/FileAPI/cmakeFiles-v1-check.py
index 25dabf8..49dfe87 100644
--- a/Tests/RunCMake/FileAPI/cmakeFiles-v1-check.py
+++ b/Tests/RunCMake/FileAPI/cmakeFiles-v1-check.py
@@ -5,10 +5,89 @@ def check_objects(o):
     assert len(o) == 1
     check_index_object(o[0], "cmakeFiles", 1, 0, check_object_cmakeFiles)
 
+def check_input(actual, expected):
+    assert is_dict(actual)
+    expected_keys = ["path"]
+
+    if expected["isGenerated"] is not None:
+        expected_keys.append("isGenerated")
+        assert is_bool(actual["isGenerated"], expected["isGenerated"])
+
+    if expected["isExternal"] is not None:
+        expected_keys.append("isExternal")
+        assert is_bool(actual["isExternal"], expected["isExternal"])
+
+    if expected["isCMake"] is not None:
+        expected_keys.append("isCMake")
+        assert is_bool(actual["isCMake"], expected["isCMake"])
+
+    assert sorted(actual.keys()) == sorted(expected_keys)
+
 def check_object_cmakeFiles(o):
     assert sorted(o.keys()) == ["inputs", "kind", "paths", "version"]
     # The "kind" and "version" members are handled by check_index_object.
-    # FIXME: Check "paths" and "inputs" members.
+    assert is_dict(o["paths"])
+    assert sorted(o["paths"].keys()) == ["build", "source"]
+    assert matches(o["paths"]["build"], "^.*/Tests/RunCMake/FileAPI/cmakeFiles-v1-build$")
+    assert matches(o["paths"]["source"], "^.*/Tests/RunCMake/FileAPI$")
+
+    expected = [
+        {
+            "path": "^CMakeLists\\.txt$",
+            "isGenerated": None,
+            "isExternal": None,
+            "isCMake": None,
+        },
+        {
+            "path": "^cmakeFiles-v1\\.cmake$",
+            "isGenerated": None,
+            "isExternal": None,
+            "isCMake": None,
+        },
+        {
+            "path": "^dir/CMakeLists\\.txt$",
+            "isGenerated": None,
+            "isExternal": None,
+            "isCMake": None,
+        },
+        {
+            "path": "^dir/dir/CMakeLists\\.txt$",
+            "isGenerated": None,
+            "isExternal": None,
+            "isCMake": None,
+        },
+        {
+            "path": "^dir/dirtest\\.cmake$",
+            "isGenerated": None,
+            "isExternal": None,
+            "isCMake": None,
+        },
+        {
+            "path": "^.*/Tests/RunCMake/FileAPIDummyFile\\.cmake$",
+            "isGenerated": None,
+            "isExternal": True,
+            "isCMake": None,
+        },
+        {
+            "path": "^.*/Tests/RunCMake/FileAPI/cmakeFiles-v1-build/generated\\.cmake",
+            "isGenerated": True,
+            "isExternal": None,
+            "isCMake": None,
+        },
+        {
+            "path": "^.*/Modules/CMakeParseArguments\\.cmake$",
+            "isGenerated": None,
+            "isExternal": True,
+            "isCMake": True,
+        },
+    ]
+
+    inSource = os.path.dirname(o["paths"]["build"]) == o["paths"]["source"]
+    if inSource:
+        for e in expected:
+            e["path"] = e["path"].replace("^.*/Tests/RunCMake/FileAPI/", "^", 1)
+
+    check_list_match(lambda a, e: matches(a["path"], e["path"]), o["inputs"], expected, check=check_input, allow_extra=True)
 
 assert is_dict(index)
 assert sorted(index.keys()) == ["cmake", "objects", "reply"]
diff --git a/Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake b/Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake
index e69de29..4d4d757 100644
--- a/Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake
+++ b/Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake
@@ -0,0 +1,8 @@
+include("${CMAKE_CURRENT_LIST_DIR}/dir/dirtest.cmake")
+include(CMakeParseArguments)
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/generated.cmake" "")
+include("${CMAKE_CURRENT_BINARY_DIR}/generated.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/../FileAPIDummyFile.cmake")
+
+add_subdirectory(dir)
diff --git a/Tests/RunCMake/FileAPI/dir/CMakeLists.txt b/Tests/RunCMake/FileAPI/dir/CMakeLists.txt
new file mode 100644
index 0000000..780445d
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/dir/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(dir)
diff --git a/Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake b/Tests/RunCMake/FileAPI/dir/dir/CMakeLists.txt
similarity index 100%
copy from Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake
copy to Tests/RunCMake/FileAPI/dir/dir/CMakeLists.txt
diff --git a/Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake b/Tests/RunCMake/FileAPI/dir/dirtest.cmake
similarity index 100%
copy from Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake
copy to Tests/RunCMake/FileAPI/dir/dirtest.cmake
diff --git a/Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake b/Tests/RunCMake/FileAPIDummyFile.cmake
similarity index 100%
copy from Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake
copy to Tests/RunCMake/FileAPIDummyFile.cmake

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=c150bf087c9ba3425d91bb1103589a928fc87098
commit c150bf087c9ba3425d91bb1103589a928fc87098
Author:     Kyle Edwards <kyle.edwards at kitware.com>
AuthorDate: Wed Dec 12 10:29:50 2018 -0500
Commit:     Kyle Edwards <kyle.edwards at kitware.com>
CommitDate: Wed Dec 12 10:29:50 2018 -0500

    Server mode: Fix regression of hasInstallRule
    
    The introduction of policy CMP0082, to run subdirectory install
    rules in the correct order, caused a regression in server mode
    where any directory with a subdirectory is marked as having an
    install rule even if no "real" install rules are present. This
    change fixes this regression for server mode.

diff --git a/Source/cmJsonObjects.cxx b/Source/cmJsonObjects.cxx
index bc50f9f..0da6f4f 100644
--- a/Source/cmJsonObjects.cxx
+++ b/Source/cmJsonObjects.cxx
@@ -6,6 +6,7 @@
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
 #include "cmInstallGenerator.h"
+#include "cmInstallSubdirectoryGenerator.h"
 #include "cmInstallTargetGenerator.h"
 #include "cmJsonObjectDictionary.h"
 #include "cmJsonObjects.h"
@@ -640,8 +641,13 @@ static Json::Value DumpProjectList(const cmake* cm, std::string const& config)
     // associated generators.
     bool hasInstallRule = false;
     for (const auto generator : projectIt.second) {
-      hasInstallRule =
-        generator->GetMakefile()->GetInstallGenerators().empty() == false;
+      for (const auto installGen :
+           generator->GetMakefile()->GetInstallGenerators()) {
+        if (!dynamic_cast<cmInstallSubdirectoryGenerator*>(installGen)) {
+          hasInstallRule = true;
+          break;
+        }
+      }
 
       if (hasInstallRule) {
         break;

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=6615408193aa542833ef34c902a35831bd85d25a
commit 6615408193aa542833ef34c902a35831bd85d25a
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Thu Nov 1 09:45:54 2018 -0400
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Wed Dec 12 09:46:13 2018 -0500

    fileapi: add cmakeFiles v1
    
    Issue: #18398

diff --git a/Help/manual/cmake-file-api.7.rst b/Help/manual/cmake-file-api.7.rst
index e30ad10..0dbdfd7 100644
--- a/Help/manual/cmake-file-api.7.rst
+++ b/Help/manual/cmake-file-api.7.rst
@@ -943,3 +943,85 @@ The members specific to ``cache`` objects are:
 
     ``value``
       A string specifying the value of the cache entry property.
+
+Object Kind "cmakeFiles"
+------------------------
+
+The ``cmakeFiles`` object kind lists files used by CMake while
+configuring and generating the build system.  These include the
+``CMakeLists.txt`` files as well as included ``.cmake`` files.
+
+There is only one ``cmakeFiles`` object major version, version 1.
+
+"cmakeFiles" version 1
+^^^^^^^^^^^^^^^^^^^^^^
+
+``cmakeFiles`` object version 1 is a JSON object:
+
+.. code-block:: json
+
+  {
+    "kind": "cmakeFiles",
+    "version": { "major": 1, "minor": 0 },
+    "paths": {
+      "build": "/path/to/top-level-build-dir",
+      "source": "/path/to/top-level-source-dir"
+    },
+    "inputs": [
+      {
+        "path": "CMakeLists.txt"
+      },
+      {
+        "isGenerated": true,
+        "path": "/path/to/top-level-build-dir/.../CMakeSystem.cmake"
+      },
+      {
+        "isExternal": true,
+        "path": "/path/to/external/third-party/module.cmake"
+      },
+      {
+        "isCMake": true,
+        "isExternal": true,
+        "path": "/path/to/cmake/Modules/CMakeGenericSystem.cmake"
+      }
+    ]
+  }
+
+The members specific to ``cmakeFiles`` objects are:
+
+``paths``
+  A JSON object containing members:
+
+  ``source``
+    A string specifying the absolute path to the top-level source directory,
+    represented with forward slashes.
+
+  ``build``
+    A string specifying the absolute path to the top-level build directory,
+    represented with forward slashes.
+
+``inputs``
+  A JSON array whose entries are each a JSON object specifying an input
+  file used by CMake when configuring and generating the build system.
+  The members of each entry are:
+
+  ``path``
+    A string specifying the path to an input file to CMake, 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.
+
+  ``isGenerated``
+    Optional member that is present with boolean value ``true``
+    if the path specifies a file that is under the top-level
+    build directory and the build is out-of-source.
+    This member is not available on in-source builds.
+
+  ``isExternal``
+    Optional member that is present with boolean value ``true``
+    if the path specifies a file that is not under the top-level
+    source or build directories.
+
+  ``isCMake``
+    Optional member that is present with boolean value ``true``
+    if the path specifies a file in the CMake installation.
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 82fad1c..035b7a0 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -213,6 +213,8 @@ set(SRCS
   cmFileAPICache.h
   cmFileAPICodemodel.cxx
   cmFileAPICodemodel.h
+  cmFileAPICMakeFiles.cxx
+  cmFileAPICMakeFiles.h
   cmFileLock.cxx
   cmFileLock.h
   cmFileLockPool.cxx
diff --git a/Source/cmFileAPI.cxx b/Source/cmFileAPI.cxx
index ec26268..89bd258 100644
--- a/Source/cmFileAPI.cxx
+++ b/Source/cmFileAPI.cxx
@@ -4,6 +4,7 @@
 
 #include "cmAlgorithms.h"
 #include "cmCryptoHash.h"
+#include "cmFileAPICMakeFiles.h"
 #include "cmFileAPICache.h"
 #include "cmFileAPICodemodel.h"
 #include "cmGlobalGenerator.h"
@@ -248,6 +249,17 @@ bool cmFileAPI::ReadQuery(std::string const& query,
     objects.push_back(o);
     return true;
   }
+  if (kindName == ObjectKindName(ObjectKind::CMakeFiles)) {
+    Object o;
+    o.Kind = ObjectKind::CMakeFiles;
+    if (verStr == "v1") {
+      o.Version = 1;
+    } else {
+      return false;
+    }
+    objects.push_back(o);
+    return true;
+  }
   if (kindName == ObjectKindName(ObjectKind::InternalTest)) {
     Object o;
     o.Kind = ObjectKind::InternalTest;
@@ -385,9 +397,10 @@ const char* cmFileAPI::ObjectKindName(ObjectKind kind)
 {
   // Keep in sync with ObjectKind enum.
   static const char* objectKindNames[] = {
-    "codemodel", //
-    "cache",     //
-    "__test"     //
+    "codemodel",  //
+    "cache",      //
+    "cmakeFiles", //
+    "__test"      //
   };
   return objectKindNames[size_t(kind)];
 }
@@ -411,6 +424,9 @@ Json::Value cmFileAPI::BuildObject(Object const& object)
     case ObjectKind::Cache:
       value = this->BuildCache(object);
       break;
+    case ObjectKind::CMakeFiles:
+      value = this->BuildCMakeFiles(object);
+      break;
     case ObjectKind::InternalTest:
       value = this->BuildInternalTest(object);
       break;
@@ -465,6 +481,8 @@ cmFileAPI::ClientRequest cmFileAPI::BuildClientRequest(
     r.Kind = ObjectKind::CodeModel;
   } else if (kindName == this->ObjectKindName(ObjectKind::Cache)) {
     r.Kind = ObjectKind::Cache;
+  } else if (kindName == this->ObjectKindName(ObjectKind::CMakeFiles)) {
+    r.Kind = ObjectKind::CMakeFiles;
   } else if (kindName == this->ObjectKindName(ObjectKind::InternalTest)) {
     r.Kind = ObjectKind::InternalTest;
   } else {
@@ -489,6 +507,9 @@ cmFileAPI::ClientRequest cmFileAPI::BuildClientRequest(
     case ObjectKind::Cache:
       this->BuildClientRequestCache(r, versions);
       break;
+    case ObjectKind::CMakeFiles:
+      this->BuildClientRequestCMakeFiles(r, versions);
+      break;
     case ObjectKind::InternalTest:
       this->BuildClientRequestInternalTest(r, versions);
       break;
@@ -706,6 +727,42 @@ Json::Value cmFileAPI::BuildCache(Object const& object)
   return cache;
 }
 
+// The "cmakeFiles" object kind.
+
+static unsigned int const CMakeFilesV1Minor = 0;
+
+void cmFileAPI::BuildClientRequestCMakeFiles(
+  ClientRequest& r, std::vector<RequestVersion> const& versions)
+{
+  // Select a known version from those requested.
+  for (RequestVersion const& v : versions) {
+    if ((v.Major == 1 && v.Minor <= CMakeFilesV1Minor)) {
+      r.Version = v.Major;
+      break;
+    }
+  }
+  if (!r.Version) {
+    r.Error = NoSupportedVersion(versions);
+  }
+}
+
+Json::Value cmFileAPI::BuildCMakeFiles(Object const& object)
+{
+  using namespace std::placeholders;
+  Json::Value cmakeFiles = cmFileAPICMakeFilesDump(*this, object.Version);
+  cmakeFiles["kind"] = this->ObjectKindName(object.Kind);
+
+  Json::Value& version = cmakeFiles["version"] = Json::objectValue;
+  if (object.Version == 1) {
+    version["major"] = 1;
+    version["minor"] = CMakeFilesV1Minor;
+  } else {
+    return cmakeFiles; // should be unreachable
+  }
+
+  return cmakeFiles;
+}
+
 // The "__test" object kind is for internal testing of CMake.
 
 static unsigned int const InternalTestV1Minor = 3;
diff --git a/Source/cmFileAPI.h b/Source/cmFileAPI.h
index 20aa011..341b072 100644
--- a/Source/cmFileAPI.h
+++ b/Source/cmFileAPI.h
@@ -53,6 +53,7 @@ private:
   {
     CodeModel,
     Cache,
+    CMakeFiles,
     InternalTest
   };
 
@@ -191,6 +192,10 @@ private:
                                std::vector<RequestVersion> const& versions);
   Json::Value BuildCache(Object const& object);
 
+  void BuildClientRequestCMakeFiles(
+    ClientRequest& r, std::vector<RequestVersion> const& versions);
+  Json::Value BuildCMakeFiles(Object const& object);
+
   void BuildClientRequestInternalTest(
     ClientRequest& r, std::vector<RequestVersion> const& versions);
   Json::Value BuildInternalTest(Object const& object);
diff --git a/Source/cmFileAPICMakeFiles.cxx b/Source/cmFileAPICMakeFiles.cxx
new file mode 100644
index 0000000..799a047
--- /dev/null
+++ b/Source/cmFileAPICMakeFiles.cxx
@@ -0,0 +1,113 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmFileAPICMakeFiles.h"
+
+#include "cmFileAPI.h"
+#include "cmGlobalGenerator.h"
+#include "cmLocalGenerator.h"
+#include "cmMakefile.h"
+#include "cmSystemTools.h"
+#include "cmake.h"
+
+#include "cm_jsoncpp_value.h"
+
+#include <string>
+
+namespace {
+
+class CMakeFiles
+{
+  cmFileAPI& FileAPI;
+  unsigned long Version;
+  std::string CMakeModules;
+  std::string const& TopSource;
+  std::string const& TopBuild;
+  bool OutOfSource;
+
+  Json::Value DumpPaths();
+  Json::Value DumpInputs();
+  Json::Value DumpInput(std::string const& file);
+
+public:
+  CMakeFiles(cmFileAPI& fileAPI, unsigned long version);
+  Json::Value Dump();
+};
+
+CMakeFiles::CMakeFiles(cmFileAPI& fileAPI, unsigned long version)
+  : FileAPI(fileAPI)
+  , Version(version)
+  , CMakeModules(cmSystemTools::GetCMakeRoot() + "/Modules")
+  , TopSource(this->FileAPI.GetCMakeInstance()->GetHomeDirectory())
+  , TopBuild(this->FileAPI.GetCMakeInstance()->GetHomeOutputDirectory())
+  , OutOfSource(TopBuild != TopSource)
+{
+  static_cast<void>(this->Version);
+}
+
+Json::Value CMakeFiles::Dump()
+{
+  Json::Value cmakeFiles = Json::objectValue;
+  cmakeFiles["paths"] = this->DumpPaths();
+  cmakeFiles["inputs"] = DumpInputs();
+  return cmakeFiles;
+}
+
+Json::Value CMakeFiles::DumpPaths()
+{
+  Json::Value paths = Json::objectValue;
+  paths["source"] = this->TopSource;
+  paths["build"] = this->TopBuild;
+  return paths;
+}
+
+Json::Value CMakeFiles::DumpInputs()
+{
+  Json::Value inputs = Json::arrayValue;
+
+  cmGlobalGenerator* gg =
+    this->FileAPI.GetCMakeInstance()->GetGlobalGenerator();
+  for (cmLocalGenerator const* lg : gg->GetLocalGenerators()) {
+    cmMakefile const* mf = lg->GetMakefile();
+    for (std::string const& file : mf->GetListFiles()) {
+      inputs.append(this->DumpInput(file));
+    }
+  }
+
+  return inputs;
+}
+
+Json::Value CMakeFiles::DumpInput(std::string const& file)
+{
+  Json::Value input = Json::objectValue;
+
+  bool const isCMake = cmSystemTools::IsSubDirectory(file, this->CMakeModules);
+  if (isCMake) {
+    input["isCMake"] = true;
+  }
+
+  if (!cmSystemTools::IsSubDirectory(file, this->TopSource) &&
+      !cmSystemTools::IsSubDirectory(file, this->TopBuild)) {
+    input["isExternal"] = true;
+  }
+
+  if (this->OutOfSource &&
+      cmSystemTools::IsSubDirectory(file, this->TopBuild)) {
+    input["isGenerated"] = true;
+  }
+
+  std::string path = file;
+  if (!isCMake && cmSystemTools::IsSubDirectory(path, this->TopSource)) {
+    // Use a relative path within the source directory.
+    path = cmSystemTools::RelativePath(this->TopSource, path);
+  }
+  input["path"] = path;
+
+  return input;
+}
+}
+
+Json::Value cmFileAPICMakeFilesDump(cmFileAPI& fileAPI, unsigned long version)
+{
+  CMakeFiles cmakeFiles(fileAPI, version);
+  return cmakeFiles.Dump();
+}
diff --git a/Source/cmFileAPICMakeFiles.h b/Source/cmFileAPICMakeFiles.h
new file mode 100644
index 0000000..a851c32
--- /dev/null
+++ b/Source/cmFileAPICMakeFiles.h
@@ -0,0 +1,15 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmFileAPICMakeFiles_h
+#define cmFileAPICMakeFiles_h
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include "cm_jsoncpp_value.h"
+
+class cmFileAPI;
+
+extern Json::Value cmFileAPICMakeFilesDump(cmFileAPI& fileAPI,
+                                           unsigned long version);
+
+#endif
diff --git a/Tests/RunCMake/FileAPI/RunCMakeTest.cmake b/Tests/RunCMake/FileAPI/RunCMakeTest.cmake
index 57c9cc9..f8adb64 100644
--- a/Tests/RunCMake/FileAPI/RunCMakeTest.cmake
+++ b/Tests/RunCMake/FileAPI/RunCMakeTest.cmake
@@ -55,3 +55,4 @@ endfunction()
 
 run_object(codemodel-v2)
 run_object(cache-v2)
+run_object(cmakeFiles-v1)
diff --git a/Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateful-check.cmake b/Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateful-check.cmake
new file mode 100644
index 0000000..21e931e
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateful-check.cmake
@@ -0,0 +1,11 @@
+set(expect
+  query
+  query/client-foo
+  query/client-foo/query.json
+  reply
+  reply/cmakeFiles-v1-[0-9a-f]+.json
+  reply/index-[0-9.T-]+.json
+  )
+check_api("^${expect}$")
+
+check_python(cmakeFiles-v1)
diff --git a/Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateful-prep.cmake b/Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateful-prep.cmake
new file mode 100644
index 0000000..7a72696
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateful-prep.cmake
@@ -0,0 +1,4 @@
+file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query)
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/query.json" [[
+{ "requests": [ { "kind": "cmakeFiles", "version" : 1 } ] }
+]])
diff --git a/Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateless-check.cmake b/Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateless-check.cmake
new file mode 100644
index 0000000..2ce2e79
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateless-check.cmake
@@ -0,0 +1,11 @@
+set(expect
+  query
+  query/client-foo
+  query/client-foo/cmakeFiles-v1
+  reply
+  reply/cmakeFiles-v1-[0-9a-f]+.json
+  reply/index-[0-9.T-]+.json
+  )
+check_api("^${expect}$")
+
+check_python(cmakeFiles-v1)
diff --git a/Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateless-prep.cmake b/Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateless-prep.cmake
new file mode 100644
index 0000000..eb96491
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateless-prep.cmake
@@ -0,0 +1,2 @@
+file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query)
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/cmakeFiles-v1" "")
diff --git a/Tests/RunCMake/FileAPI/cmakeFiles-v1-SharedStateless-check.cmake b/Tests/RunCMake/FileAPI/cmakeFiles-v1-SharedStateless-check.cmake
new file mode 100644
index 0000000..6e3b49a
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/cmakeFiles-v1-SharedStateless-check.cmake
@@ -0,0 +1,10 @@
+set(expect
+  query
+  query/cmakeFiles-v1
+  reply
+  reply/cmakeFiles-v1-[0-9a-f]+.json
+  reply/index-[0-9.T-]+.json
+  )
+check_api("^${expect}$")
+
+check_python(cmakeFiles-v1)
diff --git a/Tests/RunCMake/FileAPI/cmakeFiles-v1-SharedStateless-prep.cmake b/Tests/RunCMake/FileAPI/cmakeFiles-v1-SharedStateless-prep.cmake
new file mode 100644
index 0000000..8c8bdef
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/cmakeFiles-v1-SharedStateless-prep.cmake
@@ -0,0 +1,2 @@
+file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query)
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/cmakeFiles-v1" "")
diff --git a/Tests/RunCMake/FileAPI/cmakeFiles-v1-check.py b/Tests/RunCMake/FileAPI/cmakeFiles-v1-check.py
new file mode 100644
index 0000000..25dabf8
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/cmakeFiles-v1-check.py
@@ -0,0 +1,15 @@
+from check_index import *
+
+def check_objects(o):
+    assert is_list(o)
+    assert len(o) == 1
+    check_index_object(o[0], "cmakeFiles", 1, 0, check_object_cmakeFiles)
+
+def check_object_cmakeFiles(o):
+    assert sorted(o.keys()) == ["inputs", "kind", "paths", "version"]
+    # The "kind" and "version" members are handled by check_index_object.
+    # FIXME: Check "paths" and "inputs" members.
+
+assert is_dict(index)
+assert sorted(index.keys()) == ["cmake", "objects", "reply"]
+check_objects(index["objects"])
diff --git a/Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake b/Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake
new file mode 100644
index 0000000..e69de29

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=3f6ee75a66042370fe48833bcd5c712448ec59c0
commit 3f6ee75a66042370fe48833bcd5c712448ec59c0
Author:     Kyle Edwards <kyle.edwards at kitware.com>
AuthorDate: Mon Nov 12 15:01:49 2018 -0500
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Wed Dec 12 09:46:13 2018 -0500

    fileapi: Add test for cache v2

diff --git a/Tests/RunCMake/FileAPI/cache-v2-check.py b/Tests/RunCMake/FileAPI/cache-v2-check.py
index 13553c3..756ef80 100644
--- a/Tests/RunCMake/FileAPI/cache-v2-check.py
+++ b/Tests/RunCMake/FileAPI/cache-v2-check.py
@@ -5,10 +5,129 @@ def check_objects(o):
     assert len(o) == 1
     check_index_object(o[0], "cache", 2, 0, check_object_cache)
 
+def check_cache_entry(actual, expected):
+    assert is_dict(actual)
+    assert sorted(actual.keys()) == ["name", "properties", "type", "value"]
+
+    assert is_string(actual["type"], expected["type"])
+    assert is_string(actual["value"], expected["value"])
+
+    def check_property(actual, expected):
+        assert is_dict(actual)
+        assert sorted(actual.keys()) == ["name", "value"]
+        assert is_string(actual["value"], expected["value"])
+
+    check_list_match(lambda a, e: is_string(a["name"], e["name"]), actual["properties"], expected["properties"], check=check_property)
+
 def check_object_cache(o):
     assert sorted(o.keys()) == ["entries", "kind", "version"]
     # The "kind" and "version" members are handled by check_index_object.
-    # FIXME: Check "entries" member
+    check_list_match(lambda a, e: is_string(a["name"], e["name"]), o["entries"], [
+        {
+            "name": "CM_OPTION_BOOL",
+            "type": "BOOL",
+            "value": "OFF",
+            "properties": [
+                {
+                    "name": "HELPSTRING",
+                    "value": "Testing option()",
+                },
+            ],
+        },
+        {
+            "name": "CM_SET_BOOL",
+            "type": "BOOL",
+            "value": "ON",
+            "properties": [
+                {
+                    "name": "HELPSTRING",
+                    "value": "Testing set(CACHE BOOL)",
+                },
+                {
+                    "name": "ADVANCED",
+                    "value": "1",
+                },
+            ],
+        },
+        {
+            "name": "CM_SET_FILEPATH",
+            "type": "FILEPATH",
+            "value": "dir1/dir2/empty.txt",
+            "properties": [
+                {
+                    "name": "HELPSTRING",
+                    "value": "Testing set(CACHE FILEPATH)",
+                },
+            ],
+        },
+        {
+            "name": "CM_SET_PATH",
+            "type": "PATH",
+            "value": "dir1/dir2",
+            "properties": [
+                {
+                    "name": "HELPSTRING",
+                    "value": "Testing set(CACHE PATH)",
+                },
+                {
+                    "name": "ADVANCED",
+                    "value": "ON",
+                },
+            ],
+        },
+        {
+            "name": "CM_SET_STRING",
+            "type": "STRING",
+            "value": "test",
+            "properties": [
+                {
+                    "name": "HELPSTRING",
+                    "value": "Testing set(CACHE STRING)",
+                },
+            ],
+        },
+        {
+            "name": "CM_SET_STRINGS",
+            "type": "STRING",
+            "value": "1",
+            "properties": [
+                {
+                    "name": "HELPSTRING",
+                    "value": "Testing set(CACHE STRING) with STRINGS",
+                },
+                {
+                    "name": "STRINGS",
+                    "value": "1;2;3;4",
+                },
+            ],
+        },
+        {
+            "name": "CM_SET_INTERNAL",
+            "type": "INTERNAL",
+            "value": "int2",
+            "properties": [
+                {
+                    "name": "HELPSTRING",
+                    "value": "Testing set(CACHE INTERNAL)",
+                },
+            ],
+        },
+        {
+            "name": "CM_SET_TYPE",
+            "type": "STRING",
+            "value": "1",
+            "properties": [
+                {
+                    "name": "HELPSTRING",
+                    "value": "Testing set(CACHE INTERNAL) with set_property(TYPE)",
+                },
+                {
+                    "name": "ADVANCED",
+                    "value": "0",
+                },
+            ],
+        },
+    ], check=check_cache_entry, allow_extra=True)
 
 assert is_dict(index)
 assert sorted(index.keys()) == ["cmake", "objects", "reply"]
diff --git a/Tests/RunCMake/FileAPI/cache-v2.cmake b/Tests/RunCMake/FileAPI/cache-v2.cmake
index 7b98869..45b402d 100644
--- a/Tests/RunCMake/FileAPI/cache-v2.cmake
+++ b/Tests/RunCMake/FileAPI/cache-v2.cmake
@@ -1 +1,14 @@
-# FIXME: add some specific cache entries to cover in test, with properties
+option(CM_OPTION_BOOL "Testing option()" "OFF")
+set(CM_SET_BOOL "ON" CACHE BOOL "Testing set(CACHE BOOL)")
+mark_as_advanced(CM_SET_BOOL)
+set(CM_SET_FILEPATH "dir1/dir2/empty.txt" CACHE FILEPATH "Testing set(CACHE FILEPATH)")
+set(CM_SET_PATH "dir1/dir2" CACHE PATH "Testing set(CACHE PATH)")
+set_property(CACHE CM_SET_PATH PROPERTY ADVANCED ON)
+set(CM_SET_STRING "test" CACHE STRING "Testing set(CACHE STRING)")
+set(CM_SET_STRINGS "1" CACHE STRING "Testing set(CACHE STRING) with STRINGS")
+set_property(CACHE CM_SET_STRINGS PROPERTY STRINGS "1;2;3;4")
+set(CM_SET_INTERNAL "int" CACHE INTERNAL "Testing set(CACHE INTERNAL)")
+set_property(CACHE CM_SET_INTERNAL PROPERTY VALUE "int2")
+set(CM_SET_TYPE "1" CACHE INTERNAL "Testing set(CACHE INTERNAL) with set_property(TYPE)")
+set_property(CACHE CM_SET_TYPE PROPERTY TYPE "STRING")
+set_property(CACHE CM_SET_TYPE PROPERTY ADVANCED "0")

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=7489e95b8ec35c7faa1f9dcfc3a6962a56969531
commit 7489e95b8ec35c7faa1f9dcfc3a6962a56969531
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Thu Nov 1 09:37:17 2018 -0400
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Wed Dec 12 09:46:13 2018 -0500

    fileapi: add cache v2
    
    Start with v2 to distinguish it from server-mode v1.
    
    Issue: #18398

diff --git a/Help/manual/cmake-file-api.7.rst b/Help/manual/cmake-file-api.7.rst
index f87d7f0..e30ad10 100644
--- a/Help/manual/cmake-file-api.7.rst
+++ b/Help/manual/cmake-file-api.7.rst
@@ -869,3 +869,77 @@ with members:
     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"
+-------------------
+
+The ``cache`` object kind lists cache entries.  These are the
+:ref:`CMake Language Variables` stored in the persistent cache
+(``CMakeCache.txt``) for the build tree.
+
+There is only one ``cache`` object major version, version 2.
+Version 1 does not exist to avoid confusion with that from
+:manual:`cmake-server(7)` mode.
+
+"cache" version 2
+^^^^^^^^^^^^^^^^^
+
+``cache`` object version 2 is a JSON object:
+
+.. code-block:: json
+
+  {
+    "kind": "cache",
+    "version": { "major": 2, "minor": 0 },
+    "entries": [
+      {
+        "name": "BUILD_SHARED_LIBS",
+        "value": "ON",
+        "type": "BOOL",
+        "properties": [
+          {
+            "name": "HELPSTRING",
+            "value": "Build shared libraries"
+          }
+        ]
+      },
+      {
+        "name": "CMAKE_GENERATOR",
+        "value": "Unix Makefiles",
+        "type": "INTERNAL",
+        "properties": [
+          {
+            "name": "HELPSTRING",
+            "value": "Name of generator."
+          }
+        ]
+      }
+    ]
+  }
+
+The members specific to ``cache`` objects are:
+
+``entries``
+  A JSON array whose entries are each a JSON object specifying a
+  cache entry.  The members of each entry are:
+
+  ``name``
+    A string specifying the name of the entry.
+
+  ``value``
+    A string specifying the value of the entry.
+
+  ``type``
+    A string specifying the type of the entry used by
+    :manual:`cmake-gui(1)` to choose a widget for editing.
+
+  ``properties``
+    A JSON array of entries specifying associated
+    :ref:`cache entry properties <Cache Entry Properties>`.
+    Each entry is a JSON object containing members:
+
+    ``name``
+      A string specifying the name of the cache entry property.
+
+    ``value``
+      A string specifying the value of the cache entry property.
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index e672eab..82fad1c 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -209,6 +209,8 @@ set(SRCS
   cmExtraSublimeTextGenerator.h
   cmFileAPI.cxx
   cmFileAPI.h
+  cmFileAPICache.cxx
+  cmFileAPICache.h
   cmFileAPICodemodel.cxx
   cmFileAPICodemodel.h
   cmFileLock.cxx
diff --git a/Source/cmFileAPI.cxx b/Source/cmFileAPI.cxx
index b63349a..ec26268 100644
--- a/Source/cmFileAPI.cxx
+++ b/Source/cmFileAPI.cxx
@@ -4,6 +4,7 @@
 
 #include "cmAlgorithms.h"
 #include "cmCryptoHash.h"
+#include "cmFileAPICache.h"
 #include "cmFileAPICodemodel.h"
 #include "cmGlobalGenerator.h"
 #include "cmSystemTools.h"
@@ -236,6 +237,17 @@ bool cmFileAPI::ReadQuery(std::string const& query,
     objects.push_back(o);
     return true;
   }
+  if (kindName == ObjectKindName(ObjectKind::Cache)) {
+    Object o;
+    o.Kind = ObjectKind::Cache;
+    if (verStr == "v2") {
+      o.Version = 2;
+    } else {
+      return false;
+    }
+    objects.push_back(o);
+    return true;
+  }
   if (kindName == ObjectKindName(ObjectKind::InternalTest)) {
     Object o;
     o.Kind = ObjectKind::InternalTest;
@@ -374,6 +386,7 @@ const char* cmFileAPI::ObjectKindName(ObjectKind kind)
   // Keep in sync with ObjectKind enum.
   static const char* objectKindNames[] = {
     "codemodel", //
+    "cache",     //
     "__test"     //
   };
   return objectKindNames[size_t(kind)];
@@ -395,6 +408,9 @@ Json::Value cmFileAPI::BuildObject(Object const& object)
     case ObjectKind::CodeModel:
       value = this->BuildCodeModel(object);
       break;
+    case ObjectKind::Cache:
+      value = this->BuildCache(object);
+      break;
     case ObjectKind::InternalTest:
       value = this->BuildInternalTest(object);
       break;
@@ -447,6 +463,8 @@ cmFileAPI::ClientRequest cmFileAPI::BuildClientRequest(
 
   if (kindName == this->ObjectKindName(ObjectKind::CodeModel)) {
     r.Kind = ObjectKind::CodeModel;
+  } else if (kindName == this->ObjectKindName(ObjectKind::Cache)) {
+    r.Kind = ObjectKind::Cache;
   } else if (kindName == this->ObjectKindName(ObjectKind::InternalTest)) {
     r.Kind = ObjectKind::InternalTest;
   } else {
@@ -468,6 +486,9 @@ cmFileAPI::ClientRequest cmFileAPI::BuildClientRequest(
     case ObjectKind::CodeModel:
       this->BuildClientRequestCodeModel(r, versions);
       break;
+    case ObjectKind::Cache:
+      this->BuildClientRequestCache(r, versions);
+      break;
     case ObjectKind::InternalTest:
       this->BuildClientRequestInternalTest(r, versions);
       break;
@@ -649,6 +670,42 @@ Json::Value cmFileAPI::BuildCodeModel(Object const& object)
   return codemodel;
 }
 
+// The "cache" object kind.
+
+static unsigned int const CacheV2Minor = 0;
+
+void cmFileAPI::BuildClientRequestCache(
+  ClientRequest& r, std::vector<RequestVersion> const& versions)
+{
+  // Select a known version from those requested.
+  for (RequestVersion const& v : versions) {
+    if ((v.Major == 2 && v.Minor <= CacheV2Minor)) {
+      r.Version = v.Major;
+      break;
+    }
+  }
+  if (!r.Version) {
+    r.Error = NoSupportedVersion(versions);
+  }
+}
+
+Json::Value cmFileAPI::BuildCache(Object const& object)
+{
+  using namespace std::placeholders;
+  Json::Value cache = cmFileAPICacheDump(*this, object.Version);
+  cache["kind"] = this->ObjectKindName(object.Kind);
+
+  Json::Value& version = cache["version"] = Json::objectValue;
+  if (object.Version == 2) {
+    version["major"] = 2;
+    version["minor"] = CacheV2Minor;
+  } else {
+    return cache; // should be unreachable
+  }
+
+  return cache;
+}
+
 // The "__test" object kind is for internal testing of CMake.
 
 static unsigned int const InternalTestV1Minor = 3;
diff --git a/Source/cmFileAPI.h b/Source/cmFileAPI.h
index 5339ba7..20aa011 100644
--- a/Source/cmFileAPI.h
+++ b/Source/cmFileAPI.h
@@ -52,6 +52,7 @@ private:
   enum class ObjectKind
   {
     CodeModel,
+    Cache,
     InternalTest
   };
 
@@ -186,6 +187,10 @@ private:
     ClientRequest& r, std::vector<RequestVersion> const& versions);
   Json::Value BuildCodeModel(Object const& object);
 
+  void BuildClientRequestCache(ClientRequest& r,
+                               std::vector<RequestVersion> const& versions);
+  Json::Value BuildCache(Object const& object);
+
   void BuildClientRequestInternalTest(
     ClientRequest& r, std::vector<RequestVersion> const& versions);
   Json::Value BuildInternalTest(Object const& object);
diff --git a/Source/cmFileAPICache.cxx b/Source/cmFileAPICache.cxx
new file mode 100644
index 0000000..074994a
--- /dev/null
+++ b/Source/cmFileAPICache.cxx
@@ -0,0 +1,105 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmFileAPICache.h"
+
+#include "cmFileAPI.h"
+#include "cmState.h"
+#include "cmake.h"
+
+#include "cm_jsoncpp_value.h"
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+namespace {
+
+class Cache
+{
+  cmFileAPI& FileAPI;
+  unsigned long Version;
+  cmState* State;
+
+  Json::Value DumpEntries();
+  Json::Value DumpEntry(std::string const& name);
+  Json::Value DumpEntryProperties(std::string const& name);
+  Json::Value DumpEntryProperty(std::string const& name,
+                                std::string const& prop);
+
+public:
+  Cache(cmFileAPI& fileAPI, unsigned long version);
+  Json::Value Dump();
+};
+
+Cache::Cache(cmFileAPI& fileAPI, unsigned long version)
+  : FileAPI(fileAPI)
+  , Version(version)
+  , State(this->FileAPI.GetCMakeInstance()->GetState())
+{
+  static_cast<void>(this->Version);
+}
+
+Json::Value Cache::Dump()
+{
+  Json::Value cache = Json::objectValue;
+  cache["entries"] = DumpEntries();
+  return cache;
+}
+
+Json::Value Cache::DumpEntries()
+{
+  Json::Value entries = Json::arrayValue;
+
+  std::vector<std::string> names = this->State->GetCacheEntryKeys();
+  std::sort(names.begin(), names.end());
+
+  for (std::string const& name : names) {
+    entries.append(this->DumpEntry(name));
+  }
+
+  return entries;
+}
+
+Json::Value Cache::DumpEntry(std::string const& name)
+{
+  Json::Value entry = Json::objectValue;
+  entry["name"] = name;
+  entry["type"] =
+    cmState::CacheEntryTypeToString(this->State->GetCacheEntryType(name));
+  entry["value"] = this->State->GetCacheEntryValue(name);
+
+  Json::Value properties = this->DumpEntryProperties(name);
+  if (!properties.empty()) {
+    entry["properties"] = std::move(properties);
+  }
+
+  return entry;
+}
+
+Json::Value Cache::DumpEntryProperties(std::string const& name)
+{
+  Json::Value properties = Json::arrayValue;
+  std::vector<std::string> props =
+    this->State->GetCacheEntryPropertyList(name);
+  std::sort(props.begin(), props.end());
+  for (std::string const& prop : props) {
+    properties.append(this->DumpEntryProperty(name, prop));
+  }
+  return properties;
+}
+
+Json::Value Cache::DumpEntryProperty(std::string const& name,
+                                     std::string const& prop)
+{
+  Json::Value property = Json::objectValue;
+  property["name"] = prop;
+  property["value"] = this->State->GetCacheEntryProperty(name, prop);
+  return property;
+}
+}
+
+Json::Value cmFileAPICacheDump(cmFileAPI& fileAPI, unsigned long version)
+{
+  Cache cache(fileAPI, version);
+  return cache.Dump();
+}
diff --git a/Source/cmFileAPICache.h b/Source/cmFileAPICache.h
new file mode 100644
index 0000000..09d9e1c
--- /dev/null
+++ b/Source/cmFileAPICache.h
@@ -0,0 +1,15 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmFileAPICache_h
+#define cmFileAPICache_h
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include "cm_jsoncpp_value.h"
+
+class cmFileAPI;
+
+extern Json::Value cmFileAPICacheDump(cmFileAPI& fileAPI,
+                                      unsigned long version);
+
+#endif
diff --git a/Tests/RunCMake/FileAPI/RunCMakeTest.cmake b/Tests/RunCMake/FileAPI/RunCMakeTest.cmake
index 515a4bd..57c9cc9 100644
--- a/Tests/RunCMake/FileAPI/RunCMakeTest.cmake
+++ b/Tests/RunCMake/FileAPI/RunCMakeTest.cmake
@@ -54,3 +54,4 @@ function(run_object object)
 endfunction()
 
 run_object(codemodel-v2)
+run_object(cache-v2)
diff --git a/Tests/RunCMake/FileAPI/cache-v2-ClientStateful-check.cmake b/Tests/RunCMake/FileAPI/cache-v2-ClientStateful-check.cmake
new file mode 100644
index 0000000..0f5ef28
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/cache-v2-ClientStateful-check.cmake
@@ -0,0 +1,11 @@
+set(expect
+  query
+  query/client-foo
+  query/client-foo/query.json
+  reply
+  reply/cache-v2-[0-9a-f]+.json
+  reply/index-[0-9.T-]+.json
+  )
+check_api("^${expect}$")
+
+check_python(cache-v2)
diff --git a/Tests/RunCMake/FileAPI/cache-v2-ClientStateful-prep.cmake b/Tests/RunCMake/FileAPI/cache-v2-ClientStateful-prep.cmake
new file mode 100644
index 0000000..9329280
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/cache-v2-ClientStateful-prep.cmake
@@ -0,0 +1,4 @@
+file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query)
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/query.json" [[
+{ "requests": [ { "kind": "cache", "version" : 2 } ] }
+]])
diff --git a/Tests/RunCMake/FileAPI/cache-v2-ClientStateless-check.cmake b/Tests/RunCMake/FileAPI/cache-v2-ClientStateless-check.cmake
new file mode 100644
index 0000000..c406ec8
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/cache-v2-ClientStateless-check.cmake
@@ -0,0 +1,11 @@
+set(expect
+  query
+  query/client-foo
+  query/client-foo/cache-v2
+  reply
+  reply/cache-v2-[0-9a-f]+.json
+  reply/index-[0-9.T-]+.json
+  )
+check_api("^${expect}$")
+
+check_python(cache-v2)
diff --git a/Tests/RunCMake/FileAPI/cache-v2-ClientStateless-prep.cmake b/Tests/RunCMake/FileAPI/cache-v2-ClientStateless-prep.cmake
new file mode 100644
index 0000000..dccafa5
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/cache-v2-ClientStateless-prep.cmake
@@ -0,0 +1,2 @@
+file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query)
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/cache-v2" "")
diff --git a/Tests/RunCMake/FileAPI/cache-v2-SharedStateless-check.cmake b/Tests/RunCMake/FileAPI/cache-v2-SharedStateless-check.cmake
new file mode 100644
index 0000000..f8337eb
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/cache-v2-SharedStateless-check.cmake
@@ -0,0 +1,10 @@
+set(expect
+  query
+  query/cache-v2
+  reply
+  reply/cache-v2-[0-9a-f]+.json
+  reply/index-[0-9.T-]+.json
+  )
+check_api("^${expect}$")
+
+check_python(cache-v2)
diff --git a/Tests/RunCMake/FileAPI/cache-v2-SharedStateless-prep.cmake b/Tests/RunCMake/FileAPI/cache-v2-SharedStateless-prep.cmake
new file mode 100644
index 0000000..ee5ac57
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/cache-v2-SharedStateless-prep.cmake
@@ -0,0 +1,2 @@
+file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query)
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/cache-v2" "")
diff --git a/Tests/RunCMake/FileAPI/cache-v2-check.py b/Tests/RunCMake/FileAPI/cache-v2-check.py
new file mode 100644
index 0000000..13553c3
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/cache-v2-check.py
@@ -0,0 +1,15 @@
+from check_index import *
+
+def check_objects(o):
+    assert is_list(o)
+    assert len(o) == 1
+    check_index_object(o[0], "cache", 2, 0, check_object_cache)
+
+def check_object_cache(o):
+    assert sorted(o.keys()) == ["entries", "kind", "version"]
+    # The "kind" and "version" members are handled by check_index_object.
+    # FIXME: Check "entries" member
+
+assert is_dict(index)
+assert sorted(index.keys()) == ["cmake", "objects", "reply"]
+check_objects(index["objects"])
diff --git a/Tests/RunCMake/FileAPI/cache-v2.cmake b/Tests/RunCMake/FileAPI/cache-v2.cmake
new file mode 100644
index 0000000..7b98869
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/cache-v2.cmake
@@ -0,0 +1 @@
+# FIXME: add some specific cache entries to cover in test, with properties

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=ea0a0601682af00706cf57f6d3e27d4f0895ecbb
commit ea0a0601682af00706cf57f6d3e27d4f0895ecbb
Author:     Kyle Edwards <kyle.edwards at kitware.com>
AuthorDate: Fri Nov 9 13:54:41 2018 -0500
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Wed Dec 12 09:45:49 2018 -0500

    fileapi: Add test for codemodel v2

diff --git a/Tests/RunCMake/FileAPI/alias/CMakeLists.txt b/Tests/RunCMake/FileAPI/alias/CMakeLists.txt
new file mode 100644
index 0000000..549307d
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/alias/CMakeLists.txt
@@ -0,0 +1,10 @@
+project(Alias)
+enable_language(CXX)
+
+add_library(c_alias_lib ALIAS c_lib)
+add_executable(c_alias_exe ../empty.c)
+target_link_libraries(c_alias_exe PRIVATE c_alias_lib)
+
+add_library(cxx_alias_lib ALIAS cxx_lib)
+add_executable(cxx_alias_exe ../empty.cxx)
+target_link_libraries(cxx_alias_exe PRIVATE cxx_alias_lib)
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateful-check.cmake b/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateful-check.cmake
index cdbfe7c..fb78e87 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateful-check.cmake
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateful-check.cmake
@@ -3,8 +3,9 @@ set(expect
   query/client-foo
   query/client-foo/query.json
   reply
-  reply/codemodel-v2-[0-9a-f]+.json
-  reply/index-[0-9.T-]+.json
+  reply/codemodel-v2-[0-9a-f]+\\.json
+  reply/index-[0-9.T-]+\\.json
+  .*
   )
 check_api("^${expect}$")
 
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateless-check.cmake b/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateless-check.cmake
index 612120b..7c6a35a 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateless-check.cmake
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateless-check.cmake
@@ -3,8 +3,9 @@ set(expect
   query/client-foo
   query/client-foo/codemodel-v2
   reply
-  reply/codemodel-v2-[0-9a-f]+.json
-  reply/index-[0-9.T-]+.json
+  reply/codemodel-v2-[0-9a-f]+\\.json
+  reply/index-[0-9.T-]+\\.json
+  .*
   )
 check_api("^${expect}$")
 
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-SharedStateless-check.cmake b/Tests/RunCMake/FileAPI/codemodel-v2-SharedStateless-check.cmake
index 2afaeeb..cc2f31b 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-SharedStateless-check.cmake
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-SharedStateless-check.cmake
@@ -2,8 +2,9 @@ set(expect
   query
   query/codemodel-v2
   reply
-  reply/codemodel-v2-[0-9a-f]+.json
-  reply/index-[0-9.T-]+.json
+  reply/codemodel-v2-[0-9a-f]+\\.json
+  reply/index-[0-9.T-]+\\.json
+  .*
   )
 check_api("^${expect}$")
 
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-check.py b/Tests/RunCMake/FileAPI/codemodel-v2-check.py
index 8e65acb..e82bddd 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-check.py
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-check.py
@@ -1,15 +1,4804 @@
 from check_index import *
 
-def check_objects(o):
+import sys
+import os
+
+def check_objects(o, g):
     assert is_list(o)
     assert len(o) == 1
-    check_index_object(o[0], "codemodel", 2, 0, check_object_codemodel)
+    check_index_object(o[0], "codemodel", 2, 0, check_object_codemodel(g))
+
+def check_backtrace(t, b, backtrace):
+    btg = t["backtraceGraph"]
+    for expected in backtrace:
+        assert is_int(b)
+        node = btg["nodes"][b]
+        expected_keys = ["file"]
+        assert matches(btg["files"][node["file"]], expected["file"])
+
+        if expected["line"] is not None:
+            expected_keys.append("line")
+            assert is_int(node["line"], expected["line"])
+
+        if expected["command"] is not None:
+            expected_keys.append("command")
+            assert is_int(node["command"])
+            assert is_string(btg["commands"][node["command"]], expected["command"])
+
+        if expected["hasParent"]:
+            expected_keys.append("parent")
+            assert is_int(node["parent"])
+            b = node["parent"]
+        else:
+            b = None
+
+        assert sorted(node.keys()) == sorted(expected_keys)
+
+    assert b is None
+
+def check_directory(c):
+    def _check(actual, expected):
+        assert is_dict(actual)
+        expected_keys = ["build", "source"]
+        assert matches(actual["build"], expected["build"])
+
+        if expected["parentSource"] is not None:
+            expected_keys.append("parentIndex")
+            assert is_int(actual["parentIndex"])
+            assert matches(c["directories"][actual["parentIndex"]]["source"], expected["parentSource"])
+
+        if expected["childSources"] is not None:
+            expected_keys.append("childIndexes")
+            check_list_match(lambda a, e: matches(c["directories"][a]["source"], e),
+                             actual["childIndexes"], expected["childSources"],
+                             missing_exception=lambda e: "Child source: %s" % e,
+                             extra_exception=lambda a: "Child source: %s" % a["source"])
+
+        if expected["targetIds"] is not None:
+            expected_keys.append("targetIndexes")
+            check_list_match(lambda a, e: matches(c["targets"][a]["id"], e),
+                             actual["targetIndexes"], expected["targetIds"],
+                             missing_exception=lambda e: "Target ID: %s" % e,
+                             extra_exception=lambda a: "Target ID: %s" % c["targets"][a]["id"])
+
+        assert sorted(actual.keys()) == sorted(expected_keys)
+
+    return _check
+
+def check_target_backtrace_graph(t):
+    btg = t["backtraceGraph"]
+    assert is_dict(btg)
+    assert sorted(btg.keys()) == ["commands", "files", "nodes"]
+    assert is_list(btg["commands"])
+
+    for c in btg["commands"]:
+        assert is_string(c)
+
+    for f in btg["files"]:
+        assert is_string(f)
+
+    for n in btg["nodes"]:
+        expected_keys = ["file"]
+        assert is_dict(n)
+        assert is_int(n["file"])
+        assert 0 <= n["file"] < len(btg["files"])
+
+        if "line" in n:
+            expected_keys.append("line")
+            assert is_int(n["line"])
+
+        if "command" in n:
+            expected_keys.append("command")
+            assert is_int(n["command"])
+            assert 0 <= n["command"] < len(btg["commands"])
+
+        if "parent" in n:
+            expected_keys.append("parent")
+            assert is_int(n["parent"])
+            assert 0 <= n["parent"] < len(btg["nodes"])
+
+        assert sorted(n.keys()) == sorted(expected_keys)
+
+def check_target(c):
+    def _check(actual, expected):
+        assert is_dict(actual)
+        assert sorted(actual.keys()) == ["directoryIndex", "id", "jsonFile", "name"]
+        assert is_int(actual["directoryIndex"])
+        assert matches(c["directories"][actual["directoryIndex"]]["source"], expected["directorySource"])
+        assert is_string(actual["name"], expected["name"])
+        assert is_string(actual["jsonFile"])
+
+        filepath = os.path.join(reply_dir, actual["jsonFile"])
+        with open(filepath) as f:
+            obj = json.load(f)
+
+        expected_keys = ["name", "id", "type", "backtraceGraph", "paths", "sources"]
+        assert is_dict(obj)
+        assert is_string(obj["name"], expected["name"])
+        assert matches(obj["id"], expected["id"])
+        assert is_string(obj["type"], expected["type"])
+        check_target_backtrace_graph(obj)
+
+        assert is_dict(obj["paths"])
+        assert sorted(obj["paths"].keys()) == ["build", "source"]
+        assert matches(obj["paths"]["build"], expected["build"])
+        assert matches(obj["paths"]["source"], expected["source"])
+
+        def check_source(actual, expected):
+            assert is_dict(actual)
+            expected_keys = ["path"]
+
+            if expected["compileGroupLanguage"] is not None:
+                expected_keys.append("compileGroupIndex")
+                assert is_string(obj["compileGroups"][actual["compileGroupIndex"]]["language"], expected["compileGroupLanguage"])
+
+            if expected["sourceGroupName"] is not None:
+                expected_keys.append("sourceGroupIndex")
+                assert is_string(obj["sourceGroups"][actual["sourceGroupIndex"]]["name"], expected["sourceGroupName"])
+
+            if expected["isGenerated"] is not None:
+                expected_keys.append("isGenerated")
+                assert is_bool(actual["isGenerated"], expected["isGenerated"])
+
+            if expected["backtrace"] is not None:
+                expected_keys.append("backtrace")
+                check_backtrace(obj, actual["backtrace"], expected["backtrace"])
+
+            assert sorted(actual.keys()) == sorted(expected_keys)
+
+        check_list_match(lambda a, e: matches(a["path"], e["path"]), obj["sources"],
+                         expected["sources"], check=check_source,
+                         check_exception=lambda a, e: "Source file: %s" % a["path"],
+                         missing_exception=lambda e: "Source file: %s" % e["path"],
+                         extra_exception=lambda a: "Source file: %s" % a["path"])
+
+        if expected["backtrace"] is not None:
+            expected_keys.append("backtrace")
+            check_backtrace(obj, obj["backtrace"], expected["backtrace"])
+
+        if expected["folder"] is not None:
+            expected_keys.append("folder")
+            assert is_dict(obj["folder"])
+            assert sorted(obj["folder"].keys()) == ["name"]
+            assert is_string(obj["folder"]["name"], expected["folder"])
+
+        if expected["nameOnDisk"] is not None:
+            expected_keys.append("nameOnDisk")
+            assert matches(obj["nameOnDisk"], expected["nameOnDisk"])
+
+        if expected["artifacts"] is not None:
+            expected_keys.append("artifacts")
+
+            def check_artifact(actual, expected):
+                assert is_dict(actual)
+                assert sorted(actual.keys()) == ["path"]
+
+            check_list_match(lambda a, e: matches(a["path"], e["path"]),
+                             obj["artifacts"], expected["artifacts"],
+                             check=check_artifact,
+                             check_exception=lambda a, e: "Artifact: %s" % a["path"],
+                             missing_exception=lambda e: "Artifact: %s" % e["path"],
+                             extra_exception=lambda a: "Artifact: %s" % a["path"])
+
+        if expected["isGeneratorProvided"] is not None:
+            expected_keys.append("isGeneratorProvided")
+            assert is_bool(obj["isGeneratorProvided"], expected["isGeneratorProvided"])
+
+        if expected["install"] is not None:
+            expected_keys.append("install")
+            assert is_dict(obj["install"])
+            assert sorted(obj["install"].keys()) == ["destinations", "prefix"]
+
+            assert is_dict(obj["install"]["prefix"])
+            assert sorted(obj["install"]["prefix"].keys()) == ["path"]
+            assert matches(obj["install"]["prefix"]["path"], expected["install"]["prefix"])
+
+            def check_install_destination(actual, expected):
+                assert is_dict(actual)
+                expected_keys = ["path"]
+
+                if expected["backtrace"] is not None:
+                    expected_keys.append("backtrace")
+                    check_backtrace(obj, actual["backtrace"], expected["backtrace"])
+
+                assert sorted(actual.keys()) == sorted(expected_keys)
+
+            check_list_match(lambda a, e: matches(a["path"], e["path"]),
+                             obj["install"]["destinations"], expected["install"]["destinations"],
+                             check=check_install_destination,
+                             check_exception=lambda a, e: "Install path: %s" % a["path"],
+                             missing_exception=lambda e: "Install path: %s" % e["path"],
+                             extra_exception=lambda a: "Install path: %s" % a["path"])
+
+        if expected["link"] is not None:
+            expected_keys.append("link")
+            assert is_dict(obj["link"])
+            link_keys = ["language"]
+
+            assert is_string(obj["link"]["language"], expected["link"]["language"])
+
+            # FIXME: Properly test commandFragments
+            if "commandFragments" in obj["link"]:
+                link_keys.append("commandFragments")
+                assert is_list(obj["link"]["commandFragments"])
+                for f in obj["link"]["commandFragments"]:
+                    assert is_dict(f)
+                    assert sorted(f.keys()) == ["fragment", "role"]
+                    assert is_string(f["fragment"])
+                    assert is_string(f["role"])
+                    assert f["role"] in ("flags", "libraries", "libraryPath", "frameworkPath")
+
+            if expected["link"]["lto"] is not None:
+                link_keys.append("lto")
+                assert is_bool(obj["link"]["lto"], expected["link"]["lto"])
+
+            # FIXME: Properly test sysroot
+            if "sysroot" in obj["link"]:
+                link_keys.append("sysroot")
+                assert is_string(obj["link"]["sysroot"])
+
+            assert sorted(obj["link"].keys()) == sorted(link_keys)
+
+        if expected["archive"] is not None:
+            expected_keys.append("archive")
+            assert is_dict(obj["archive"])
+            archive_keys = []
+
+            # FIXME: Properly test commandFragments
+            if "commandFragments" in obj["archive"]:
+                archive_keys.append("commandFragments")
+                assert is_list(obj["archive"]["commandFragments"])
+                for f in obj["archive"]["commandFragments"]:
+                    assert is_dict(f)
+                    assert sorted(f.keys()) == ["fragment", "role"]
+                    assert is_string(f["fragment"])
+                    assert is_string(f["role"])
+                    assert f["role"] in ("flags")
+
+            if expected["archive"]["lto"] is not None:
+                archive_keys.append("lto")
+                assert is_bool(obj["archive"]["lto"], expected["archive"]["lto"])
+
+            assert sorted(obj["archive"].keys()) == sorted(archive_keys)
+
+        if expected["dependencies"] is not None:
+            expected_keys.append("dependencies")
+
+            def check_dependency(actual, expected):
+                assert is_dict(actual)
+                expected_keys = ["id"]
+
+                if expected["backtrace"] is not None:
+                    expected_keys.append("backtrace")
+                    check_backtrace(obj, actual["backtrace"], expected["backtrace"])
+
+                assert sorted(actual.keys()) == sorted(expected_keys)
+
+            check_list_match(lambda a, e: matches(a["id"], e["id"]),
+                             obj["dependencies"], expected["dependencies"],
+                             check=check_dependency,
+                             check_exception=lambda a, e: "Dependency ID: %s" % a["id"],
+                             missing_exception=lambda e: "Dependency ID: %s" % e["id"],
+                             extra_exception=lambda a: "Dependency ID: %s" % a["id"])
+
+        if expected["sourceGroups"] is not None:
+            expected_keys.append("sourceGroups")
+
+            def check_source_group(actual, expected):
+                assert is_dict(actual)
+                assert sorted(actual.keys()) == ["name", "sourceIndexes"]
+
+                check_list_match(lambda a, e: matches(obj["sources"][a]["path"], e),
+                                 actual["sourceIndexes"], expected["sourcePaths"],
+                                 missing_exception=lambda e: "Source path: %s" % e,
+                                 extra_exception=lambda a: "Source path: %s" % obj["sources"][a]["path"])
+
+            check_list_match(lambda a, e: is_string(a["name"], e["name"]),
+                             obj["sourceGroups"], expected["sourceGroups"],
+                             check=check_source_group,
+                             check_exception=lambda a, e: "Source group: %s" % a["name"],
+                             missing_exception=lambda e: "Source group: %s" % e["name"],
+                             extra_exception=lambda a: "Source group: %s" % a["name"])
+
+        if expected["compileGroups"] is not None:
+            expected_keys.append("compileGroups")
+
+            def check_compile_group(actual, expected):
+                assert is_dict(actual)
+                expected_keys = ["sourceIndexes", "language"]
+
+                check_list_match(lambda a, e: matches(obj["sources"][a]["path"], e),
+                                 actual["sourceIndexes"], expected["sourcePaths"],
+                                 missing_exception=lambda e: "Source path: %s" % e,
+                                 extra_exception=lambda a: "Source path: %s" % obj["sources"][a]["path"])
+
+                # FIXME: Properly test compileCommandFragments
+                if "compileCommandFragments" in actual:
+                    expected_keys.append("compileCommandFragments")
+                    assert is_list(actual["compileCommandFragments"])
+                    for f in actual["compileCommandFragments"]:
+                        assert is_dict(f)
+                        assert sorted(f.keys()) == ["fragment"]
+                        assert is_string(f["fragment"])
+
+                if expected["includes"] is not None:
+                    expected_keys.append("includes")
+
+                    def check_include(actual, expected):
+                        assert is_dict(actual)
+                        expected_keys = ["path"]
+
+                        if expected["isSystem"] is not None:
+                            expected_keys.append("isSystem")
+                            assert is_bool(actual["isSystem"], expected["isSystem"])
+
+                        if expected["backtrace"] is not None:
+                            expected_keys.append("backtrace")
+                            check_backtrace(obj, actual["backtrace"], expected["backtrace"])
+
+                        assert sorted(actual.keys()) == sorted(expected_keys)
+
+                    check_list_match(lambda a, e: matches(a["path"], e["path"]),
+                                     actual["includes"], expected["includes"],
+                                     check=check_include,
+                                     check_exception=lambda a, e: "Include path: %s" % a["path"],
+                                     missing_exception=lambda e: "Include path: %s" % e["path"],
+                                     extra_exception=lambda a: "Include path: %s" % a["path"])
+
+                if expected["defines"] is not None:
+                    expected_keys.append("defines")
+
+                    def check_define(actual, expected):
+                        assert is_dict(actual)
+                        expected_keys = ["define"]
+
+                        if expected["backtrace"] is not None:
+                            expected_keys.append("backtrace")
+                            check_backtrace(obj, actual["backtrace"], expected["backtrace"])
+
+                        assert sorted(actual.keys()) == sorted(expected_keys)
+
+                    check_list_match(lambda a, e: is_string(a["define"], e["define"]),
+                                     actual["defines"], expected["defines"],
+                                     check=check_define,
+                                     check_exception=lambda a, e: "Define: %s" % a["define"],
+                                     missing_exception=lambda e: "Define: %s" % e["define"],
+                                     extra_exception=lambda a: "Define: %s" % a["define"])
+
+                # FIXME: Properly test sysroot
+                if "sysroot" in actual:
+                    expected_keys.append("sysroot")
+                    assert is_string(actual["sysroot"])
+
+                assert sorted(actual.keys()) == sorted(expected_keys)
+
+            check_list_match(lambda a, e: is_string(a["language"], e["language"]),
+                             obj["compileGroups"], expected["compileGroups"],
+                             check=check_compile_group,
+                             check_exception=lambda a, e: "Compile group: %s" % a["language"],
+                             missing_exception=lambda e: "Compile group: %s" % e["language"],
+                             extra_exception=lambda a: "Compile group: %s" % a["language"])
+
+        assert sorted(obj.keys()) == sorted(expected_keys)
+
+    return _check
+
+def gen_check_directories(c, g):
+    expected = [
+        {
+            "source": "^\\.$",
+            "build": "^\\.$",
+            "parentSource": None,
+            "childSources": [
+                "^alias$",
+                "^custom$",
+                "^cxx$",
+                "^imported$",
+                "^object$",
+                "^.*/Tests/RunCMake/FileAPIExternalSource$",
+            ],
+            "targetIds": [
+                "^ALL_BUILD::@6890427a1f51a3e7e1df$",
+                "^ZERO_CHECK::@6890427a1f51a3e7e1df$",
+                "^c_exe::@6890427a1f51a3e7e1df$",
+                "^c_lib::@6890427a1f51a3e7e1df$",
+                "^c_shared_exe::@6890427a1f51a3e7e1df$",
+                "^c_shared_lib::@6890427a1f51a3e7e1df$",
+                "^c_static_exe::@6890427a1f51a3e7e1df$",
+                "^c_static_lib::@6890427a1f51a3e7e1df$",
+                "^interface_exe::@6890427a1f51a3e7e1df$",
+            ],
+        },
+        {
+            "source": "^alias$",
+            "build": "^alias$",
+            "parentSource": "^\\.$",
+            "childSources": None,
+            "targetIds": [
+                "^ALL_BUILD::@53632cba2752272bb008$",
+                "^ZERO_CHECK::@53632cba2752272bb008$",
+                "^c_alias_exe::@53632cba2752272bb008$",
+                "^cxx_alias_exe::@53632cba2752272bb008$",
+            ],
+        },
+        {
+            "source": "^custom$",
+            "build": "^custom$",
+            "parentSource": "^\\.$",
+            "childSources": None,
+            "targetIds": [
+                "^ALL_BUILD::@c11385ffed57b860da63$",
+                "^ZERO_CHECK::@c11385ffed57b860da63$",
+                "^custom_exe::@c11385ffed57b860da63$",
+                "^custom_tgt::@c11385ffed57b860da63$",
+            ],
+        },
+        {
+            "source": "^cxx$",
+            "build": "^cxx$",
+            "parentSource": "^\\.$",
+            "childSources": None,
+            "targetIds": [
+                "^ALL_BUILD::@a56b12a3f5c0529fb296$",
+                "^ZERO_CHECK::@a56b12a3f5c0529fb296$",
+                "^cxx_exe::@a56b12a3f5c0529fb296$",
+                "^cxx_lib::@a56b12a3f5c0529fb296$",
+                "^cxx_shared_exe::@a56b12a3f5c0529fb296$",
+                "^cxx_shared_lib::@a56b12a3f5c0529fb296$",
+                "^cxx_static_exe::@a56b12a3f5c0529fb296$",
+                "^cxx_static_lib::@a56b12a3f5c0529fb296$",
+            ],
+        },
+        {
+            "source": "^imported$",
+            "build": "^imported$",
+            "parentSource": "^\\.$",
+            "childSources": None,
+            "targetIds": [
+                "^ALL_BUILD::@ba7eb709d0b48779c6c8$",
+                "^ZERO_CHECK::@ba7eb709d0b48779c6c8$",
+                "^link_imported_exe::@ba7eb709d0b48779c6c8$",
+                "^link_imported_interface_exe::@ba7eb709d0b48779c6c8$",
+                "^link_imported_object_exe::@ba7eb709d0b48779c6c8$",
+                "^link_imported_shared_exe::@ba7eb709d0b48779c6c8$",
+                "^link_imported_static_exe::@ba7eb709d0b48779c6c8$",
+            ],
+        },
+        {
+            "source": "^object$",
+            "build": "^object$",
+            "parentSource": "^\\.$",
+            "childSources": None,
+            "targetIds": [
+                "^ALL_BUILD::@5ed5358f70faf8d8af7a$",
+                "^ZERO_CHECK::@5ed5358f70faf8d8af7a$",
+                "^c_object_exe::@5ed5358f70faf8d8af7a$",
+                "^c_object_lib::@5ed5358f70faf8d8af7a$",
+                "^cxx_object_exe::@5ed5358f70faf8d8af7a$",
+                "^cxx_object_lib::@5ed5358f70faf8d8af7a$",
+            ],
+        },
+        {
+            "source": "^.*/Tests/RunCMake/FileAPIExternalSource$",
+            "build": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild$",
+            "parentSource": "^\\.$",
+            "childSources": None,
+            "targetIds": [
+                "^ALL_BUILD::@[0-9a-f]+$",
+                "^ZERO_CHECK::@[0-9a-f]+$",
+                "^generated_exe::@[0-9a-f]+$",
+            ],
+        },
+    ]
+
+    if matches(g, "^Visual Studio "):
+        for e in expected:
+            if e["parentSource"] is not None:
+                e["targetIds"] = filter_list(lambda t: not matches(t, "^\\^ZERO_CHECK"), e["targetIds"])
+
+    elif g == "Xcode":
+        if ';' in os.environ.get("CMAKE_OSX_ARCHITECTURES", ""):
+            for e in expected:
+                e["targetIds"] = filter_list(lambda t: not matches(t, "^\\^(link_imported_object_exe)"), e["targetIds"])
+
+    else:
+        for e in expected:
+            e["targetIds"] = filter_list(lambda t: not matches(t, "^\\^(ALL_BUILD|ZERO_CHECK)"), e["targetIds"])
+
+    return expected
+
+def check_directories(c, g):
+    check_list_match(lambda a, e: matches(a["source"], e["source"]), c["directories"], gen_check_directories(c, g),
+                     check=check_directory(c),
+                     check_exception=lambda a, e: "Directory source: %s" % a["source"],
+                     missing_exception=lambda e: "Directory source: %s" % e["source"],
+                     extra_exception=lambda a: "Directory source: %s" % a["source"])
+
+def gen_check_targets(c, g, inSource):
+    expected = [
+        {
+            "name": "ALL_BUILD",
+            "id": "^ALL_BUILD::@6890427a1f51a3e7e1df$",
+            "directorySource": "^\\.$",
+            "type": "UTILITY",
+            "isGeneratorProvided": True,
+            "sources": [
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/CMakeFiles/ALL_BUILD$",
+                    "isGenerated": True,
+                    "sourceGroupName": "",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/CMakeFiles/ALL_BUILD\\.rule$",
+                    "isGenerated": True,
+                    "sourceGroupName": "CMake Rules",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/CMakeFiles/ALL_BUILD$",
+                    ],
+                },
+                {
+                    "name": "CMake Rules",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/CMakeFiles/ALL_BUILD\\.rule$",
+                    ],
+                },
+            ],
+            "compileGroups": None,
+            "backtrace": [
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": None,
+            "artifacts": None,
+            "build": "^\\.$",
+            "source": "^\\.$",
+            "install": None,
+            "link": None,
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^interface_exe::@6890427a1f51a3e7e1df$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^c_lib::@6890427a1f51a3e7e1df$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^c_exe::@6890427a1f51a3e7e1df$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^c_shared_lib::@6890427a1f51a3e7e1df$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^c_shared_exe::@6890427a1f51a3e7e1df$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^c_static_lib::@6890427a1f51a3e7e1df$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^c_static_exe::@6890427a1f51a3e7e1df$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^c_alias_exe::@53632cba2752272bb008$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^cxx_alias_exe::@53632cba2752272bb008$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^cxx_lib::@a56b12a3f5c0529fb296$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^cxx_exe::@a56b12a3f5c0529fb296$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^cxx_shared_lib::@a56b12a3f5c0529fb296$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^cxx_shared_exe::@a56b12a3f5c0529fb296$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^cxx_static_lib::@a56b12a3f5c0529fb296$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^cxx_static_exe::@a56b12a3f5c0529fb296$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^c_object_lib::@5ed5358f70faf8d8af7a$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^c_object_exe::@5ed5358f70faf8d8af7a$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^cxx_object_lib::@5ed5358f70faf8d8af7a$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^cxx_object_exe::@5ed5358f70faf8d8af7a$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^link_imported_exe::@ba7eb709d0b48779c6c8$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^link_imported_shared_exe::@ba7eb709d0b48779c6c8$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^link_imported_static_exe::@ba7eb709d0b48779c6c8$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^link_imported_object_exe::@ba7eb709d0b48779c6c8$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^link_imported_interface_exe::@ba7eb709d0b48779c6c8$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^custom_exe::@c11385ffed57b860da63$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^generated_exe::@[0-9a-f]+$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "ZERO_CHECK",
+            "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$",
+            "directorySource": "^\\.$",
+            "type": "UTILITY",
+            "isGeneratorProvided": True,
+            "sources": [
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/CMakeFiles/ZERO_CHECK$",
+                    "isGenerated": True,
+                    "sourceGroupName": "",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/CMakeFiles/ZERO_CHECK\\.rule$",
+                    "isGenerated": True,
+                    "sourceGroupName": "CMake Rules",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/CMakeFiles/ZERO_CHECK$",
+                    ],
+                },
+                {
+                    "name": "CMake Rules",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/CMakeFiles/ZERO_CHECK\\.rule$",
+                    ],
+                },
+            ],
+            "compileGroups": None,
+            "backtrace": [
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": None,
+            "artifacts": None,
+            "build": "^\\.$",
+            "source": "^\\.$",
+            "install": None,
+            "link": None,
+            "archive": None,
+            "dependencies": None,
+        },
+        {
+            "name": "interface_exe",
+            "id": "^interface_exe::@6890427a1f51a3e7e1df$",
+            "directorySource": "^\\.$",
+            "type": "EXECUTABLE",
+            "isGeneratorProvided": None,
+            "sources": [
+                {
+                    "path": "^empty\\.c$",
+                    "isGenerated": None,
+                    "sourceGroupName": "Source Files",
+                    "compileGroupLanguage": "C",
+                    "backtrace": [
+                        {
+                            "file": "^include_test\\.cmake$",
+                            "line": 3,
+                            "command": "add_executable",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^include_test\\.cmake$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^codemodel-v2\\.cmake$",
+                            "line": 3,
+                            "command": "include",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^codemodel-v2\\.cmake$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^CMakeLists\\.txt$",
+                            "line": 3,
+                            "command": "include",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "Source Files",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                },
+            ],
+            "compileGroups": [
+                {
+                    "language": "C",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                    "includes": None,
+                    "defines": [
+                        {
+                            "define": "interface_exe_EXPORTS",
+                            "backtrace": None,
+                        },
+                    ],
+                },
+            ],
+            "backtrace": [
+                {
+                    "file": "^include_test\\.cmake$",
+                    "line": 3,
+                    "command": "add_executable",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^include_test\\.cmake$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": True,
+                },
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": 3,
+                    "command": "include",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": True,
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": 3,
+                    "command": "include",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": "^my_interface_exe\\.myexe$",
+            "artifacts": [
+                {
+                    "path": "^bin/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?my_interface_exe\\.myexe$",
+                    "_dllExtra": False,
+                },
+                {
+                    "path": "^lib/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?my_interface_exe\\.(dll\\.a|lib)$",
+                    "_dllExtra": True,
+                },
+                {
+                    "path": "^bin/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?my_interface_exe\\.pdb$",
+                    "_dllExtra": True,
+                },
+            ],
+            "build": "^\\.$",
+            "source": "^\\.$",
+            "install": None,
+            "link": {
+                "language": "C",
+                "lto": None,
+            },
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "c_lib",
+            "id": "^c_lib::@6890427a1f51a3e7e1df$",
+            "directorySource": "^\\.$",
+            "type": "STATIC_LIBRARY",
+            "isGeneratorProvided": None,
+            "sources": [
+                {
+                    "path": "^empty\\.c$",
+                    "isGenerated": None,
+                    "sourceGroupName": "Source Files",
+                    "compileGroupLanguage": "C",
+                    "backtrace": [
+                        {
+                            "file": "^codemodel-v2\\.cmake$",
+                            "line": 5,
+                            "command": "add_library",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^codemodel-v2\\.cmake$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^CMakeLists\\.txt$",
+                            "line": 3,
+                            "command": "include",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "Source Files",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                },
+            ],
+            "compileGroups": [
+                {
+                    "language": "C",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                    "includes": None,
+                    "defines": None,
+                },
+            ],
+            "backtrace": [
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": 5,
+                    "command": "add_library",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": True,
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": 3,
+                    "command": "include",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": "^(lib)?c_lib\\.(a|lib)$",
+            "artifacts": [
+                {
+                    "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_lib\\.(a|lib)$",
+                    "_dllExtra": False,
+                },
+            ],
+            "build": "^\\.$",
+            "source": "^\\.$",
+            "install": None,
+            "link": None,
+            "archive": {
+                "lto": None,
+            },
+            "dependencies": [
+                {
+                    "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "c_exe",
+            "id": "^c_exe::@6890427a1f51a3e7e1df$",
+            "directorySource": "^\\.$",
+            "type": "EXECUTABLE",
+            "isGeneratorProvided": None,
+            "sources": [
+                {
+                    "path": "^empty\\.c$",
+                    "isGenerated": None,
+                    "sourceGroupName": "Source Files",
+                    "compileGroupLanguage": "C",
+                    "backtrace": [
+                        {
+                            "file": "^codemodel-v2\\.cmake$",
+                            "line": 6,
+                            "command": "add_executable",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^codemodel-v2\\.cmake$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^CMakeLists\\.txt$",
+                            "line": 3,
+                            "command": "include",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "Source Files",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                },
+            ],
+            "compileGroups": [
+                {
+                    "language": "C",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                    "includes": None,
+                    "defines": None,
+                },
+            ],
+            "backtrace": [
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": 6,
+                    "command": "add_executable",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": True,
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": 3,
+                    "command": "include",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": "^c_exe(\\.exe)?$",
+            "artifacts": [
+                {
+                    "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?c_exe(\\.exe)?$",
+                    "_dllExtra": False,
+                },
+                {
+                    "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?c_exe\\.pdb$",
+                    "_dllExtra": True,
+                },
+            ],
+            "build": "^\\.$",
+            "source": "^\\.$",
+            "install": None,
+            "link": {
+                "language": "C",
+                "lto": None,
+            },
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^c_lib::@6890427a1f51a3e7e1df$",
+                    "backtrace": [
+                        {
+                            "file": "^codemodel-v2\\.cmake$",
+                            "line": 7,
+                            "command": "target_link_libraries",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^codemodel-v2\\.cmake$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^CMakeLists\\.txt$",
+                            "line": 3,
+                            "command": "include",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+                {
+                    "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "c_shared_lib",
+            "id": "^c_shared_lib::@6890427a1f51a3e7e1df$",
+            "directorySource": "^\\.$",
+            "type": "SHARED_LIBRARY",
+            "isGeneratorProvided": None,
+            "sources": [
+                {
+                    "path": "^empty\\.c$",
+                    "isGenerated": None,
+                    "sourceGroupName": "Source Files",
+                    "compileGroupLanguage": "C",
+                    "backtrace": [
+                        {
+                            "file": "^codemodel-v2\\.cmake$",
+                            "line": 9,
+                            "command": "add_library",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^codemodel-v2\\.cmake$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^CMakeLists\\.txt$",
+                            "line": 3,
+                            "command": "include",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "Source Files",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                },
+            ],
+            "compileGroups": [
+                {
+                    "language": "C",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                    "includes": None,
+                    "defines": [
+                        {
+                            "define": "c_shared_lib_EXPORTS",
+                            "backtrace": None,
+                        },
+                    ],
+                },
+            ],
+            "backtrace": [
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": 9,
+                    "command": "add_library",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": True,
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": 3,
+                    "command": "include",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": "^(lib|cyg)?c_shared_lib\\.(so|dylib|dll)$",
+            "artifacts": [
+                {
+                    "path": "^lib/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib|cyg)?c_shared_lib\\.(so|dylib|dll)$",
+                    "_dllExtra": False,
+                },
+                {
+                    "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_shared_lib\\.(dll\\.a|lib)$",
+                    "_dllExtra": True,
+                },
+                {
+                    "path": "^lib/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib|cyg)?c_shared_lib\\.pdb$",
+                    "_dllExtra": True,
+                },
+            ],
+            "build": "^\\.$",
+            "source": "^\\.$",
+            "install": None,
+            "link": {
+                "language": "C",
+                "lto": True,
+            },
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "c_shared_exe",
+            "id": "^c_shared_exe::@6890427a1f51a3e7e1df$",
+            "directorySource": "^\\.$",
+            "type": "EXECUTABLE",
+            "isGeneratorProvided": None,
+            "sources": [
+                {
+                    "path": "^empty\\.c$",
+                    "isGenerated": None,
+                    "sourceGroupName": "Source Files",
+                    "compileGroupLanguage": "C",
+                    "backtrace": [
+                        {
+                            "file": "^codemodel-v2\\.cmake$",
+                            "line": 10,
+                            "command": "add_executable",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^codemodel-v2\\.cmake$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^CMakeLists\\.txt$",
+                            "line": 3,
+                            "command": "include",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "Source Files",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                },
+            ],
+            "compileGroups": [
+                {
+                    "language": "C",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                    "includes": None,
+                    "defines": None,
+                },
+            ],
+            "backtrace": [
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": 10,
+                    "command": "add_executable",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": True,
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": 3,
+                    "command": "include",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": "^c_shared_exe(\\.exe)?$",
+            "artifacts": [
+                {
+                    "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?c_shared_exe(\\.exe)?$",
+                    "_dllExtra": False,
+                },
+                {
+                    "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?c_shared_exe\\.pdb$",
+                    "_dllExtra": True,
+                },
+            ],
+            "build": "^\\.$",
+            "source": "^\\.$",
+            "install": None,
+            "link": {
+                "language": "C",
+                "lto": True,
+            },
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^c_shared_lib::@6890427a1f51a3e7e1df$",
+                    "backtrace": [
+                        {
+                            "file": "^codemodel-v2\\.cmake$",
+                            "line": 11,
+                            "command": "target_link_libraries",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^codemodel-v2\\.cmake$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^CMakeLists\\.txt$",
+                            "line": 3,
+                            "command": "include",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+                {
+                    "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "c_static_lib",
+            "id": "^c_static_lib::@6890427a1f51a3e7e1df$",
+            "directorySource": "^\\.$",
+            "type": "STATIC_LIBRARY",
+            "isGeneratorProvided": None,
+            "sources": [
+                {
+                    "path": "^empty\\.c$",
+                    "isGenerated": None,
+                    "sourceGroupName": "Source Files",
+                    "compileGroupLanguage": "C",
+                    "backtrace": [
+                        {
+                            "file": "^codemodel-v2\\.cmake$",
+                            "line": 13,
+                            "command": "add_library",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^codemodel-v2\\.cmake$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^CMakeLists\\.txt$",
+                            "line": 3,
+                            "command": "include",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "Source Files",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                },
+            ],
+            "compileGroups": [
+                {
+                    "language": "C",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                    "includes": None,
+                    "defines": None,
+                },
+            ],
+            "backtrace": [
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": 13,
+                    "command": "add_library",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": True,
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": 3,
+                    "command": "include",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": "^(lib)?c_static_lib\\.(a|lib)$",
+            "artifacts": [
+                {
+                    "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_static_lib\\.(a|lib)$",
+                    "_dllExtra": False,
+                },
+            ],
+            "build": "^\\.$",
+            "source": "^\\.$",
+            "install": None,
+            "link": None,
+            "archive": {
+                "lto": True,
+            },
+            "dependencies": [
+                {
+                    "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "c_static_exe",
+            "id": "^c_static_exe::@6890427a1f51a3e7e1df$",
+            "directorySource": "^\\.$",
+            "type": "EXECUTABLE",
+            "isGeneratorProvided": None,
+            "sources": [
+                {
+                    "path": "^empty\\.c$",
+                    "isGenerated": None,
+                    "sourceGroupName": "Source Files",
+                    "compileGroupLanguage": "C",
+                    "backtrace": [
+                        {
+                            "file": "^codemodel-v2\\.cmake$",
+                            "line": 14,
+                            "command": "add_executable",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^codemodel-v2\\.cmake$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^CMakeLists\\.txt$",
+                            "line": 3,
+                            "command": "include",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "Source Files",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                },
+            ],
+            "compileGroups": [
+                {
+                    "language": "C",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                    "includes": None,
+                    "defines": None,
+                },
+            ],
+            "backtrace": [
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": 14,
+                    "command": "add_executable",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^codemodel-v2\\.cmake$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": True,
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": 3,
+                    "command": "include",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": "^c_static_exe(\\.exe)?$",
+            "artifacts": [
+                {
+                    "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?c_static_exe(\\.exe)?$",
+                    "_dllExtra": False,
+                },
+                {
+                    "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?c_static_exe\\.pdb$",
+                    "_dllExtra": True,
+                },
+            ],
+            "build": "^\\.$",
+            "source": "^\\.$",
+            "install": None,
+            "link": {
+                "language": "C",
+                "lto": None,
+            },
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^c_static_lib::@6890427a1f51a3e7e1df$",
+                    "backtrace": [
+                        {
+                            "file": "^codemodel-v2\\.cmake$",
+                            "line": 15,
+                            "command": "target_link_libraries",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^codemodel-v2\\.cmake$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^CMakeLists\\.txt$",
+                            "line": 3,
+                            "command": "include",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+                {
+                    "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "ALL_BUILD",
+            "id": "^ALL_BUILD::@a56b12a3f5c0529fb296$",
+            "directorySource": "^cxx$",
+            "type": "UTILITY",
+            "isGeneratorProvided": True,
+            "sources": [
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/cxx/CMakeFiles/ALL_BUILD$",
+                    "isGenerated": True,
+                    "sourceGroupName": "",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^cxx/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/cxx/CMakeFiles/ALL_BUILD\\.rule$",
+                    "isGenerated": True,
+                    "sourceGroupName": "CMake Rules",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^cxx/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/cxx/CMakeFiles/ALL_BUILD$",
+                    ],
+                },
+                {
+                    "name": "CMake Rules",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/cxx/CMakeFiles/ALL_BUILD\\.rule$",
+                    ],
+                },
+            ],
+            "compileGroups": None,
+            "backtrace": [
+                {
+                    "file": "^cxx/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": None,
+            "artifacts": None,
+            "build": "^cxx$",
+            "source": "^cxx$",
+            "install": None,
+            "link": None,
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^ZERO_CHECK::@a56b12a3f5c0529fb296$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^cxx_lib::@a56b12a3f5c0529fb296$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^cxx_exe::@a56b12a3f5c0529fb296$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^cxx_shared_lib::@a56b12a3f5c0529fb296$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^cxx_shared_exe::@a56b12a3f5c0529fb296$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^cxx_static_lib::@a56b12a3f5c0529fb296$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^cxx_static_exe::@a56b12a3f5c0529fb296$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "ZERO_CHECK",
+            "id": "^ZERO_CHECK::@a56b12a3f5c0529fb296$",
+            "directorySource": "^cxx$",
+            "type": "UTILITY",
+            "isGeneratorProvided": True,
+            "sources": [
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/cxx/CMakeFiles/ZERO_CHECK$",
+                    "isGenerated": True,
+                    "sourceGroupName": "",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^cxx/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/cxx/CMakeFiles/ZERO_CHECK\\.rule$",
+                    "isGenerated": True,
+                    "sourceGroupName": "CMake Rules",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^cxx/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/cxx/CMakeFiles/ZERO_CHECK$",
+                    ],
+                },
+                {
+                    "name": "CMake Rules",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/cxx/CMakeFiles/ZERO_CHECK\\.rule$",
+                    ],
+                },
+            ],
+            "compileGroups": None,
+            "backtrace": [
+                {
+                    "file": "^cxx/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": None,
+            "artifacts": None,
+            "build": "^cxx$",
+            "source": "^cxx$",
+            "install": None,
+            "link": None,
+            "archive": None,
+            "dependencies": None,
+        },
+        {
+            "name": "cxx_lib",
+            "id": "^cxx_lib::@a56b12a3f5c0529fb296$",
+            "directorySource": "^cxx$",
+            "type": "STATIC_LIBRARY",
+            "isGeneratorProvided": None,
+            "sources": [
+                {
+                    "path": "^empty\\.cxx$",
+                    "isGenerated": None,
+                    "sourceGroupName": "Source Files",
+                    "compileGroupLanguage": "CXX",
+                    "backtrace": [
+                        {
+                            "file": "^cxx/CMakeLists\\.txt$",
+                            "line": 4,
+                            "command": "add_library",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^cxx/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "Source Files",
+                    "sourcePaths": [
+                        "^empty\\.cxx$",
+                    ],
+                },
+            ],
+            "compileGroups": [
+                {
+                    "language": "CXX",
+                    "sourcePaths": [
+                        "^empty\\.cxx$",
+                    ],
+                    "includes": None,
+                    "defines": None,
+                },
+            ],
+            "backtrace": [
+                {
+                    "file": "^cxx/CMakeLists\\.txt$",
+                    "line": 4,
+                    "command": "add_library",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^cxx/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": "^(lib)?cxx_lib\\.(a|lib)$",
+            "artifacts": [
+                {
+                    "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?cxx_lib\\.(a|lib)$",
+                    "_dllExtra": False,
+                },
+            ],
+            "build": "^cxx$",
+            "source": "^cxx$",
+            "install": None,
+            "link": None,
+            "archive": {
+                "lto": None,
+            },
+            "dependencies": [
+                {
+                    "id": "^ZERO_CHECK::@a56b12a3f5c0529fb296$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "cxx_exe",
+            "id": "^cxx_exe::@a56b12a3f5c0529fb296$",
+            "directorySource": "^cxx$",
+            "type": "EXECUTABLE",
+            "isGeneratorProvided": None,
+            "sources": [
+                {
+                    "path": "^empty\\.cxx$",
+                    "isGenerated": None,
+                    "sourceGroupName": "Source Files",
+                    "compileGroupLanguage": "CXX",
+                    "backtrace": [
+                        {
+                            "file": "^cxx/CMakeLists\\.txt$",
+                            "line": 5,
+                            "command": "add_executable",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^cxx/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "Source Files",
+                    "sourcePaths": [
+                        "^empty\\.cxx$",
+                    ],
+                },
+            ],
+            "compileGroups": [
+                {
+                    "language": "CXX",
+                    "sourcePaths": [
+                        "^empty\\.cxx$",
+                    ],
+                    "includes": None,
+                    "defines": None,
+                },
+            ],
+            "backtrace": [
+                {
+                    "file": "^cxx/CMakeLists\\.txt$",
+                    "line": 5,
+                    "command": "add_executable",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^cxx/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": "bin",
+            "nameOnDisk": "^cxx_exe(\\.exe)?$",
+            "artifacts": [
+                {
+                    "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_exe(\\.exe)?$",
+                    "_dllExtra": False,
+                },
+                {
+                    "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_exe\\.pdb$",
+                    "_dllExtra": True,
+                },
+            ],
+            "build": "^cxx$",
+            "source": "^cxx$",
+            "install": None,
+            "link": {
+                "language": "CXX",
+                "lto": None,
+            },
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^cxx_lib::@a56b12a3f5c0529fb296$",
+                    "backtrace": [
+                        {
+                            "file": "^cxx/CMakeLists\\.txt$",
+                            "line": 6,
+                            "command": "target_link_libraries",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^cxx/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+                {
+                    "id": "^ZERO_CHECK::@a56b12a3f5c0529fb296$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "cxx_shared_lib",
+            "id": "^cxx_shared_lib::@a56b12a3f5c0529fb296$",
+            "directorySource": "^cxx$",
+            "type": "SHARED_LIBRARY",
+            "isGeneratorProvided": None,
+            "sources": [
+                {
+                    "path": "^empty\\.cxx$",
+                    "isGenerated": None,
+                    "sourceGroupName": "Source Files",
+                    "compileGroupLanguage": "CXX",
+                    "backtrace": [
+                        {
+                            "file": "^cxx/CMakeLists\\.txt$",
+                            "line": 9,
+                            "command": "add_library",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^cxx/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "Source Files",
+                    "sourcePaths": [
+                        "^empty\\.cxx$",
+                    ],
+                },
+            ],
+            "compileGroups": [
+                {
+                    "language": "CXX",
+                    "sourcePaths": [
+                        "^empty\\.cxx$",
+                    ],
+                    "includes": None,
+                    "defines": [
+                        {
+                            "define": "cxx_shared_lib_EXPORTS",
+                            "backtrace": None,
+                        },
+                    ],
+                },
+            ],
+            "backtrace": [
+                {
+                    "file": "^cxx/CMakeLists\\.txt$",
+                    "line": 9,
+                    "command": "add_library",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^cxx/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": "^(lib|cyg)?cxx_shared_lib\\.(so|dylib|dll)$",
+            "artifacts": [
+                {
+                    "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib|cyg)?cxx_shared_lib\\.(so|dylib|dll)$",
+                    "_dllExtra": False,
+                },
+                {
+                    "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?cxx_shared_lib\\.(dll\\.a|lib)$",
+                    "_dllExtra": True,
+                },
+                {
+                    "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib|cyg)?cxx_shared_lib\\.pdb$",
+                    "_dllExtra": True,
+                },
+            ],
+            "build": "^cxx$",
+            "source": "^cxx$",
+            "install": None,
+            "link": {
+                "language": "CXX",
+                "lto": None,
+            },
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^ZERO_CHECK::@a56b12a3f5c0529fb296$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "cxx_shared_exe",
+            "id": "^cxx_shared_exe::@a56b12a3f5c0529fb296$",
+            "directorySource": "^cxx$",
+            "type": "EXECUTABLE",
+            "isGeneratorProvided": None,
+            "sources": [
+                {
+                    "path": "^empty\\.cxx$",
+                    "isGenerated": None,
+                    "sourceGroupName": "Source Files",
+                    "compileGroupLanguage": "CXX",
+                    "backtrace": [
+                        {
+                            "file": "^cxx/CMakeLists\\.txt$",
+                            "line": 10,
+                            "command": "add_executable",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^cxx/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "Source Files",
+                    "sourcePaths": [
+                        "^empty\\.cxx$",
+                    ],
+                },
+            ],
+            "compileGroups": [
+                {
+                    "language": "CXX",
+                    "sourcePaths": [
+                        "^empty\\.cxx$",
+                    ],
+                    "includes": None,
+                    "defines": None,
+                },
+            ],
+            "backtrace": [
+                {
+                    "file": "^cxx/CMakeLists\\.txt$",
+                    "line": 10,
+                    "command": "add_executable",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^cxx/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": "^cxx_shared_exe(\\.exe)?$",
+            "artifacts": [
+                {
+                    "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_shared_exe(\\.exe)?$",
+                    "_dllExtra": False,
+                },
+                {
+                    "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_shared_exe\\.pdb$",
+                    "_dllExtra": True,
+                },
+            ],
+            "build": "^cxx$",
+            "source": "^cxx$",
+            "install": None,
+            "link": {
+                "language": "CXX",
+                "lto": None,
+            },
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^cxx_shared_lib::@a56b12a3f5c0529fb296$",
+                    "backtrace": [
+                        {
+                            "file": "^cxx/CMakeLists\\.txt$",
+                            "line": 11,
+                            "command": "target_link_libraries",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^cxx/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+                {
+                    "id": "^ZERO_CHECK::@a56b12a3f5c0529fb296$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "cxx_static_lib",
+            "id": "^cxx_static_lib::@a56b12a3f5c0529fb296$",
+            "directorySource": "^cxx$",
+            "type": "STATIC_LIBRARY",
+            "isGeneratorProvided": None,
+            "sources": [
+                {
+                    "path": "^empty\\.cxx$",
+                    "isGenerated": None,
+                    "sourceGroupName": "Source Files",
+                    "compileGroupLanguage": "CXX",
+                    "backtrace": [
+                        {
+                            "file": "^cxx/CMakeLists\\.txt$",
+                            "line": 13,
+                            "command": "add_library",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^cxx/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "Source Files",
+                    "sourcePaths": [
+                        "^empty\\.cxx$",
+                    ],
+                },
+            ],
+            "compileGroups": [
+                {
+                    "language": "CXX",
+                    "sourcePaths": [
+                        "^empty\\.cxx$",
+                    ],
+                    "includes": None,
+                    "defines": None,
+                },
+            ],
+            "backtrace": [
+                {
+                    "file": "^cxx/CMakeLists\\.txt$",
+                    "line": 13,
+                    "command": "add_library",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^cxx/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": "^(lib)?cxx_static_lib\\.(a|lib)$",
+            "artifacts": [
+                {
+                    "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?cxx_static_lib\\.(a|lib)$",
+                    "_dllExtra": False,
+                },
+            ],
+            "build": "^cxx$",
+            "source": "^cxx$",
+            "install": None,
+            "link": None,
+            "archive": {
+                "lto": None,
+            },
+            "dependencies": [
+                {
+                    "id": "^ZERO_CHECK::@a56b12a3f5c0529fb296$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "cxx_static_exe",
+            "id": "^cxx_static_exe::@a56b12a3f5c0529fb296$",
+            "directorySource": "^cxx$",
+            "type": "EXECUTABLE",
+            "isGeneratorProvided": None,
+            "sources": [
+                {
+                    "path": "^empty\\.cxx$",
+                    "isGenerated": None,
+                    "sourceGroupName": "Source Files",
+                    "compileGroupLanguage": "CXX",
+                    "backtrace": [
+                        {
+                            "file": "^cxx/CMakeLists\\.txt$",
+                            "line": 14,
+                            "command": "add_executable",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^cxx/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "Source Files",
+                    "sourcePaths": [
+                        "^empty\\.cxx$",
+                    ],
+                },
+            ],
+            "compileGroups": [
+                {
+                    "language": "CXX",
+                    "sourcePaths": [
+                        "^empty\\.cxx$",
+                    ],
+                    "includes": None,
+                    "defines": None,
+                },
+            ],
+            "backtrace": [
+                {
+                    "file": "^cxx/CMakeLists\\.txt$",
+                    "line": 14,
+                    "command": "add_executable",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^cxx/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": "^cxx_static_exe(\\.exe)?$",
+            "artifacts": [
+                {
+                    "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_static_exe(\\.exe)?$",
+                    "_dllExtra": False,
+                },
+                {
+                    "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_static_exe\\.pdb$",
+                    "_dllExtra": True,
+                },
+            ],
+            "build": "^cxx$",
+            "source": "^cxx$",
+            "install": None,
+            "link": {
+                "language": "CXX",
+                "lto": None,
+            },
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^cxx_static_lib::@a56b12a3f5c0529fb296$",
+                    "backtrace": [
+                        {
+                            "file": "^cxx/CMakeLists\\.txt$",
+                            "line": 15,
+                            "command": "target_link_libraries",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^cxx/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+                {
+                    "id": "^ZERO_CHECK::@a56b12a3f5c0529fb296$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "ALL_BUILD",
+            "id": "^ALL_BUILD::@53632cba2752272bb008$",
+            "directorySource": "^alias$",
+            "type": "UTILITY",
+            "isGeneratorProvided": True,
+            "sources": [
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/alias/CMakeFiles/ALL_BUILD$",
+                    "isGenerated": True,
+                    "sourceGroupName": "",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^alias/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/alias/CMakeFiles/ALL_BUILD\\.rule$",
+                    "isGenerated": True,
+                    "sourceGroupName": "CMake Rules",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^alias/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/alias/CMakeFiles/ALL_BUILD$",
+                    ],
+                },
+                {
+                    "name": "CMake Rules",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/alias/CMakeFiles/ALL_BUILD\\.rule$",
+                    ],
+                },
+            ],
+            "compileGroups": None,
+            "backtrace": [
+                {
+                    "file": "^alias/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": None,
+            "artifacts": None,
+            "build": "^alias$",
+            "source": "^alias$",
+            "install": None,
+            "link": None,
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^ZERO_CHECK::@53632cba2752272bb008$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^c_alias_exe::@53632cba2752272bb008$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^cxx_alias_exe::@53632cba2752272bb008$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "ZERO_CHECK",
+            "id": "^ZERO_CHECK::@53632cba2752272bb008$",
+            "directorySource": "^alias$",
+            "type": "UTILITY",
+            "isGeneratorProvided": True,
+            "sources": [
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/alias/CMakeFiles/ZERO_CHECK$",
+                    "isGenerated": True,
+                    "sourceGroupName": "",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^alias/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/alias/CMakeFiles/ZERO_CHECK\\.rule$",
+                    "isGenerated": True,
+                    "sourceGroupName": "CMake Rules",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^alias/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/alias/CMakeFiles/ZERO_CHECK$",
+                    ],
+                },
+                {
+                    "name": "CMake Rules",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/alias/CMakeFiles/ZERO_CHECK\\.rule$",
+                    ],
+                },
+            ],
+            "compileGroups": None,
+            "backtrace": [
+                {
+                    "file": "^alias/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": None,
+            "artifacts": None,
+            "build": "^alias$",
+            "source": "^alias$",
+            "install": None,
+            "link": None,
+            "archive": None,
+            "dependencies": None,
+        },
+        {
+            "name": "c_alias_exe",
+            "id": "^c_alias_exe::@53632cba2752272bb008$",
+            "directorySource": "^alias$",
+            "type": "EXECUTABLE",
+            "isGeneratorProvided": None,
+            "sources": [
+                {
+                    "path": "^empty\\.c$",
+                    "isGenerated": None,
+                    "sourceGroupName": "Source Files",
+                    "compileGroupLanguage": "C",
+                    "backtrace": [
+                        {
+                            "file": "^alias/CMakeLists\\.txt$",
+                            "line": 5,
+                            "command": "add_executable",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^alias/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "Source Files",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                },
+            ],
+            "compileGroups": [
+                {
+                    "language": "C",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                    "includes": None,
+                    "defines": None,
+                },
+            ],
+            "backtrace": [
+                {
+                    "file": "^alias/CMakeLists\\.txt$",
+                    "line": 5,
+                    "command": "add_executable",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^alias/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": "^c_alias_exe(\\.exe)?$",
+            "artifacts": [
+                {
+                    "path": "^alias/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?c_alias_exe(\\.exe)?$",
+                    "_dllExtra": False,
+                },
+                {
+                    "path": "^alias/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?c_alias_exe\\.pdb$",
+                    "_dllExtra": True,
+                },
+            ],
+            "build": "^alias$",
+            "source": "^alias$",
+            "install": None,
+            "link": {
+                "language": "C",
+                "lto": None,
+            },
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^c_lib::@6890427a1f51a3e7e1df$",
+                    "backtrace": [
+                        {
+                            "file": "^alias/CMakeLists\\.txt$",
+                            "line": 6,
+                            "command": "target_link_libraries",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^alias/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+                {
+                    "id": "^ZERO_CHECK::@53632cba2752272bb008$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "cxx_alias_exe",
+            "id": "^cxx_alias_exe::@53632cba2752272bb008$",
+            "directorySource": "^alias$",
+            "type": "EXECUTABLE",
+            "isGeneratorProvided": None,
+            "sources": [
+                {
+                    "path": "^empty\\.cxx$",
+                    "isGenerated": None,
+                    "sourceGroupName": "Source Files",
+                    "compileGroupLanguage": "CXX",
+                    "backtrace": [
+                        {
+                            "file": "^alias/CMakeLists\\.txt$",
+                            "line": 9,
+                            "command": "add_executable",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^alias/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "Source Files",
+                    "sourcePaths": [
+                        "^empty\\.cxx$",
+                    ],
+                },
+            ],
+            "compileGroups": [
+                {
+                    "language": "CXX",
+                    "sourcePaths": [
+                        "^empty\\.cxx$",
+                    ],
+                    "includes": None,
+                    "defines": None,
+                },
+            ],
+            "backtrace": [
+                {
+                    "file": "^alias/CMakeLists\\.txt$",
+                    "line": 9,
+                    "command": "add_executable",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^alias/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": "^cxx_alias_exe(\\.exe)?$",
+            "artifacts": [
+                {
+                    "path": "^alias/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_alias_exe(\\.exe)?$",
+                    "_dllExtra": False,
+                },
+                {
+                    "path": "^alias/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_alias_exe\\.pdb$",
+                    "_dllExtra": True,
+                },
+            ],
+            "build": "^alias$",
+            "source": "^alias$",
+            "install": None,
+            "link": {
+                "language": "CXX",
+                "lto": None,
+            },
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^cxx_lib::@a56b12a3f5c0529fb296$",
+                    "backtrace": [
+                        {
+                            "file": "^alias/CMakeLists\\.txt$",
+                            "line": 10,
+                            "command": "target_link_libraries",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^alias/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+                {
+                    "id": "^ZERO_CHECK::@53632cba2752272bb008$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "ALL_BUILD",
+            "id": "^ALL_BUILD::@5ed5358f70faf8d8af7a$",
+            "directorySource": "^object$",
+            "type": "UTILITY",
+            "isGeneratorProvided": True,
+            "sources": [
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/CMakeFiles/ALL_BUILD$",
+                    "isGenerated": True,
+                    "sourceGroupName": "",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^object/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/CMakeFiles/ALL_BUILD\\.rule$",
+                    "isGenerated": True,
+                    "sourceGroupName": "CMake Rules",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^object/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/CMakeFiles/ALL_BUILD$",
+                    ],
+                },
+                {
+                    "name": "CMake Rules",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/CMakeFiles/ALL_BUILD\\.rule$",
+                    ],
+                },
+            ],
+            "compileGroups": None,
+            "backtrace": [
+                {
+                    "file": "^object/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": None,
+            "artifacts": None,
+            "build": "^object$",
+            "source": "^object$",
+            "install": None,
+            "link": None,
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^ZERO_CHECK::@5ed5358f70faf8d8af7a$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^c_object_lib::@5ed5358f70faf8d8af7a$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^c_object_exe::@5ed5358f70faf8d8af7a$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^cxx_object_lib::@5ed5358f70faf8d8af7a$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^cxx_object_exe::@5ed5358f70faf8d8af7a$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "ZERO_CHECK",
+            "id": "^ZERO_CHECK::@5ed5358f70faf8d8af7a$",
+            "directorySource": "^object$",
+            "type": "UTILITY",
+            "isGeneratorProvided": True,
+            "sources": [
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/CMakeFiles/ZERO_CHECK$",
+                    "isGenerated": True,
+                    "sourceGroupName": "",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^object/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/CMakeFiles/ZERO_CHECK\\.rule$",
+                    "isGenerated": True,
+                    "sourceGroupName": "CMake Rules",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^object/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/CMakeFiles/ZERO_CHECK$",
+                    ],
+                },
+                {
+                    "name": "CMake Rules",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/CMakeFiles/ZERO_CHECK\\.rule$",
+                    ],
+                },
+            ],
+            "compileGroups": None,
+            "backtrace": [
+                {
+                    "file": "^object/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": None,
+            "artifacts": None,
+            "build": "^object$",
+            "source": "^object$",
+            "install": None,
+            "link": None,
+            "archive": None,
+            "dependencies": None,
+        },
+        {
+            "name": "c_object_lib",
+            "id": "^c_object_lib::@5ed5358f70faf8d8af7a$",
+            "directorySource": "^object$",
+            "type": "OBJECT_LIBRARY",
+            "isGeneratorProvided": None,
+            "sources": [
+                {
+                    "path": "^empty\\.c$",
+                    "isGenerated": None,
+                    "sourceGroupName": "Source Files",
+                    "compileGroupLanguage": "C",
+                    "backtrace": [
+                        {
+                            "file": "^object/CMakeLists\\.txt$",
+                            "line": 5,
+                            "command": "add_library",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^object/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "Source Files",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                },
+            ],
+            "compileGroups": [
+                {
+                    "language": "C",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                    "includes": None,
+                    "defines": None,
+                },
+            ],
+            "backtrace": [
+                {
+                    "file": "^object/CMakeLists\\.txt$",
+                    "line": 5,
+                    "command": "add_library",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^object/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": None,
+            "artifacts": [
+                {
+                    "path": "^object/.*/empty(\\.c)?\\.o(bj)?$",
+                    "_dllExtra": False,
+                },
+            ],
+            "build": "^object$",
+            "source": "^object$",
+            "install": None,
+            "link": None,
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^ZERO_CHECK::@5ed5358f70faf8d8af7a$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "c_object_exe",
+            "id": "^c_object_exe::@5ed5358f70faf8d8af7a$",
+            "directorySource": "^object$",
+            "type": "EXECUTABLE",
+            "isGeneratorProvided": None,
+            "sources": [
+                {
+                    "path": "^empty\\.c$",
+                    "isGenerated": None,
+                    "sourceGroupName": "Source Files",
+                    "compileGroupLanguage": "C",
+                    "backtrace": [
+                        {
+                            "file": "^object/CMakeLists\\.txt$",
+                            "line": 6,
+                            "command": "add_executable",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^object/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/.*/empty(\\.c)?\\.o(bj)?$",
+                    "isGenerated": True,
+                    "sourceGroupName": "Object Libraries",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^object/CMakeLists\\.txt$",
+                            "line": 7,
+                            "command": "target_link_libraries",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^object/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "Source Files",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                },
+                {
+                    "name": "Object Libraries",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/.*/empty(\\.c)?\\.o(bj)?$",
+                    ],
+                },
+            ],
+            "compileGroups": [
+                {
+                    "language": "C",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                    "includes": None,
+                    "defines": None,
+                },
+            ],
+            "backtrace": [
+                {
+                    "file": "^object/CMakeLists\\.txt$",
+                    "line": 6,
+                    "command": "add_executable",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^object/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": "^c_object_exe(\\.exe)?$",
+            "artifacts": [
+                {
+                    "path": "^object/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?c_object_exe(\\.exe)?$",
+                    "_dllExtra": False,
+                },
+                {
+                    "path": "^object/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?c_object_exe\\.pdb$",
+                    "_dllExtra": True,
+                },
+            ],
+            "build": "^object$",
+            "source": "^object$",
+            "install": {
+                "prefix": "^(/usr/local|[A-Za-z]:.*/codemodel-v2)$",
+                "destinations": [
+                    {
+                        "path": "bin",
+                        "backtrace": [
+                            {
+                                "file": "^object/CMakeLists\\.txt$",
+                                "line": 13,
+                                "command": "install",
+                                "hasParent": True,
+                            },
+                            {
+                                "file": "^object/CMakeLists\\.txt$",
+                                "line": None,
+                                "command": None,
+                                "hasParent": False,
+                            },
+                        ],
+                    },
+                ],
+            },
+            "link": {
+                "language": "C",
+                "lto": None,
+            },
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^c_object_lib::@5ed5358f70faf8d8af7a$",
+                    # FIXME: Add a backtrace here when it becomes available.
+                    # You'll know when it's available, because this test will
+                    # fail.
+                    "backtrace": None,
+                },
+                {
+                    "id": "^ZERO_CHECK::@5ed5358f70faf8d8af7a$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "cxx_object_lib",
+            "id": "^cxx_object_lib::@5ed5358f70faf8d8af7a$",
+            "directorySource": "^object$",
+            "type": "OBJECT_LIBRARY",
+            "isGeneratorProvided": None,
+            "sources": [
+                {
+                    "path": "^empty\\.cxx$",
+                    "isGenerated": None,
+                    "sourceGroupName": "Source Files",
+                    "compileGroupLanguage": "CXX",
+                    "backtrace": [
+                        {
+                            "file": "^object/CMakeLists\\.txt$",
+                            "line": 9,
+                            "command": "add_library",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^object/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "Source Files",
+                    "sourcePaths": [
+                        "^empty\\.cxx$",
+                    ],
+                },
+            ],
+            "compileGroups": [
+                {
+                    "language": "CXX",
+                    "sourcePaths": [
+                        "^empty\\.cxx$",
+                    ],
+                    "includes": None,
+                    "defines": None,
+                },
+            ],
+            "backtrace": [
+                {
+                    "file": "^object/CMakeLists\\.txt$",
+                    "line": 9,
+                    "command": "add_library",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^object/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": None,
+            "artifacts": [
+                {
+                    "path": "^object/.*/empty(\\.cxx)?\\.o(bj)?$",
+                    "_dllExtra": False,
+                },
+            ],
+            "build": "^object$",
+            "source": "^object$",
+            "install": None,
+            "link": None,
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^ZERO_CHECK::@5ed5358f70faf8d8af7a$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "cxx_object_exe",
+            "id": "^cxx_object_exe::@5ed5358f70faf8d8af7a$",
+            "directorySource": "^object$",
+            "type": "EXECUTABLE",
+            "isGeneratorProvided": None,
+            "sources": [
+                {
+                    "path": "^empty\\.cxx$",
+                    "isGenerated": None,
+                    "sourceGroupName": "Source Files",
+                    "compileGroupLanguage": "CXX",
+                    "backtrace": [
+                        {
+                            "file": "^object/CMakeLists\\.txt$",
+                            "line": 10,
+                            "command": "add_executable",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^object/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/.*/empty(\\.cxx)?\\.o(bj)?$",
+                    "isGenerated": True,
+                    "sourceGroupName": "Object Libraries",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^object/CMakeLists\\.txt$",
+                            "line": 11,
+                            "command": "target_link_libraries",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^object/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "Source Files",
+                    "sourcePaths": [
+                        "^empty\\.cxx$",
+                    ],
+                },
+                {
+                    "name": "Object Libraries",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/.*/empty(\\.cxx)?\\.o(bj)?$",
+                    ],
+                },
+            ],
+            "compileGroups": [
+                {
+                    "language": "CXX",
+                    "sourcePaths": [
+                        "^empty\\.cxx$",
+                    ],
+                    "includes": None,
+                    "defines": None,
+                },
+            ],
+            "backtrace": [
+                {
+                    "file": "^object/CMakeLists\\.txt$",
+                    "line": 10,
+                    "command": "add_executable",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^object/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": "^cxx_object_exe(\\.exe)?$",
+            "artifacts": [
+                {
+                    "path": "^object/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_object_exe(\\.exe)?$",
+                    "_dllExtra": False,
+                },
+                {
+                    "path": "^object/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_object_exe\\.pdb$",
+                    "_dllExtra": True,
+                },
+            ],
+            "build": "^object$",
+            "source": "^object$",
+            "install": {
+                "prefix": "^(/usr/local|[A-Za-z]:.*/codemodel-v2)$",
+                "destinations": [
+                    {
+                        "path": "bin",
+                        "backtrace": [
+                            {
+                                "file": "^object/CMakeLists\\.txt$",
+                                "line": 13,
+                                "command": "install",
+                                "hasParent": True,
+                            },
+                            {
+                                "file": "^object/CMakeLists\\.txt$",
+                                "line": None,
+                                "command": None,
+                                "hasParent": False,
+                            },
+                        ],
+                    },
+                ],
+            },
+            "link": {
+                "language": "CXX",
+                "lto": None,
+            },
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^cxx_object_lib::@5ed5358f70faf8d8af7a$",
+                    # FIXME: Add a backtrace here when it becomes available.
+                    # You'll know when it's available, because this test will
+                    # fail.
+                    "backtrace": None,
+                },
+                {
+                    "id": "^ZERO_CHECK::@5ed5358f70faf8d8af7a$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "ALL_BUILD",
+            "id": "^ALL_BUILD::@ba7eb709d0b48779c6c8$",
+            "directorySource": "^imported$",
+            "type": "UTILITY",
+            "isGeneratorProvided": True,
+            "sources": [
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/imported/CMakeFiles/ALL_BUILD$",
+                    "isGenerated": True,
+                    "sourceGroupName": "",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^imported/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/imported/CMakeFiles/ALL_BUILD\\.rule$",
+                    "isGenerated": True,
+                    "sourceGroupName": "CMake Rules",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^imported/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/imported/CMakeFiles/ALL_BUILD$",
+                    ],
+                },
+                {
+                    "name": "CMake Rules",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/imported/CMakeFiles/ALL_BUILD\\.rule$",
+                    ],
+                },
+            ],
+            "compileGroups": None,
+            "backtrace": [
+                {
+                    "file": "^imported/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": None,
+            "artifacts": None,
+            "build": "^imported$",
+            "source": "^imported$",
+            "install": None,
+            "link": None,
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^ZERO_CHECK::@ba7eb709d0b48779c6c8$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^link_imported_exe::@ba7eb709d0b48779c6c8$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^link_imported_shared_exe::@ba7eb709d0b48779c6c8$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^link_imported_static_exe::@ba7eb709d0b48779c6c8$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^link_imported_object_exe::@ba7eb709d0b48779c6c8$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^link_imported_interface_exe::@ba7eb709d0b48779c6c8$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "ZERO_CHECK",
+            "id": "^ZERO_CHECK::@ba7eb709d0b48779c6c8$",
+            "directorySource": "^imported$",
+            "type": "UTILITY",
+            "isGeneratorProvided": True,
+            "sources": [
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/imported/CMakeFiles/ZERO_CHECK$",
+                    "isGenerated": True,
+                    "sourceGroupName": "",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^imported/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/imported/CMakeFiles/ZERO_CHECK\\.rule$",
+                    "isGenerated": True,
+                    "sourceGroupName": "CMake Rules",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^imported/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/imported/CMakeFiles/ZERO_CHECK$",
+                    ],
+                },
+                {
+                    "name": "CMake Rules",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/imported/CMakeFiles/ZERO_CHECK\\.rule$",
+                    ],
+                },
+            ],
+            "compileGroups": None,
+            "backtrace": [
+                {
+                    "file": "^imported/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": None,
+            "artifacts": None,
+            "build": "^imported$",
+            "source": "^imported$",
+            "install": None,
+            "link": None,
+            "archive": None,
+            "dependencies": None,
+        },
+        {
+            "name": "link_imported_exe",
+            "id": "^link_imported_exe::@ba7eb709d0b48779c6c8$",
+            "directorySource": "^imported$",
+            "type": "EXECUTABLE",
+            "isGeneratorProvided": None,
+            "sources": [
+                {
+                    "path": "^empty\\.c$",
+                    "isGenerated": None,
+                    "sourceGroupName": "Source Files",
+                    "compileGroupLanguage": "C",
+                    "backtrace": [
+                        {
+                            "file": "^imported/CMakeLists\\.txt$",
+                            "line": 5,
+                            "command": "add_executable",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^imported/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "Source Files",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                },
+            ],
+            "compileGroups": [
+                {
+                    "language": "C",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                    "includes": None,
+                    "defines": None,
+                },
+            ],
+            "backtrace": [
+                {
+                    "file": "^imported/CMakeLists\\.txt$",
+                    "line": 5,
+                    "command": "add_executable",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^imported/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": "^link_imported_exe(\\.exe)?$",
+            "artifacts": [
+                {
+                    "path": "^imported/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?link_imported_exe(\\.exe)?$",
+                    "_dllExtra": False,
+                },
+                {
+                    "path": "^imported/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?link_imported_exe\\.pdb$",
+                    "_dllExtra": True,
+                },
+            ],
+            "build": "^imported$",
+            "source": "^imported$",
+            "install": None,
+            "link": {
+                "language": "C",
+                "lto": None,
+            },
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^ZERO_CHECK::@ba7eb709d0b48779c6c8$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "link_imported_shared_exe",
+            "id": "^link_imported_shared_exe::@ba7eb709d0b48779c6c8$",
+            "directorySource": "^imported$",
+            "type": "EXECUTABLE",
+            "isGeneratorProvided": None,
+            "sources": [
+                {
+                    "path": "^empty\\.c$",
+                    "isGenerated": None,
+                    "sourceGroupName": "Source Files",
+                    "compileGroupLanguage": "C",
+                    "backtrace": [
+                        {
+                            "file": "^imported/CMakeLists\\.txt$",
+                            "line": 9,
+                            "command": "add_executable",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^imported/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "Source Files",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                },
+            ],
+            "compileGroups": [
+                {
+                    "language": "C",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                    "includes": None,
+                    "defines": None,
+                },
+            ],
+            "backtrace": [
+                {
+                    "file": "^imported/CMakeLists\\.txt$",
+                    "line": 9,
+                    "command": "add_executable",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^imported/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": "^link_imported_shared_exe(\\.exe)?$",
+            "artifacts": [
+                {
+                    "path": "^imported/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?link_imported_shared_exe(\\.exe)?$",
+                    "_dllExtra": False,
+                },
+                {
+                    "path": "^imported/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?link_imported_shared_exe\\.pdb$",
+                    "_dllExtra": True,
+                },
+            ],
+            "build": "^imported$",
+            "source": "^imported$",
+            "install": None,
+            "link": {
+                "language": "C",
+                "lto": None,
+            },
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^ZERO_CHECK::@ba7eb709d0b48779c6c8$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "link_imported_static_exe",
+            "id": "^link_imported_static_exe::@ba7eb709d0b48779c6c8$",
+            "directorySource": "^imported$",
+            "type": "EXECUTABLE",
+            "isGeneratorProvided": None,
+            "sources": [
+                {
+                    "path": "^empty\\.c$",
+                    "isGenerated": None,
+                    "sourceGroupName": "Source Files",
+                    "compileGroupLanguage": "C",
+                    "backtrace": [
+                        {
+                            "file": "^imported/CMakeLists\\.txt$",
+                            "line": 13,
+                            "command": "add_executable",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^imported/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "Source Files",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                },
+            ],
+            "compileGroups": [
+                {
+                    "language": "C",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                    "includes": None,
+                    "defines": None,
+                },
+            ],
+            "backtrace": [
+                {
+                    "file": "^imported/CMakeLists\\.txt$",
+                    "line": 13,
+                    "command": "add_executable",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^imported/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": "^link_imported_static_exe(\\.exe)?$",
+            "artifacts": [
+                {
+                    "path": "^imported/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?link_imported_static_exe(\\.exe)?$",
+                    "_dllExtra": False,
+                },
+                {
+                    "path": "^imported/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?link_imported_static_exe\\.pdb$",
+                    "_dllExtra": True,
+                },
+            ],
+            "build": "^imported$",
+            "source": "^imported$",
+            "install": None,
+            "link": {
+                "language": "C",
+                "lto": None,
+            },
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^ZERO_CHECK::@ba7eb709d0b48779c6c8$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "link_imported_object_exe",
+            "id": "^link_imported_object_exe::@ba7eb709d0b48779c6c8$",
+            "directorySource": "^imported$",
+            "type": "EXECUTABLE",
+            "isGeneratorProvided": None,
+            "sources": [
+                {
+                    "path": "^empty\\.c$",
+                    "isGenerated": None,
+                    "sourceGroupName": "Source Files",
+                    "compileGroupLanguage": "C",
+                    "backtrace": [
+                        {
+                            "file": "^imported/CMakeLists\\.txt$",
+                            "line": 18,
+                            "command": "add_executable",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^imported/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "Source Files",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                },
+            ],
+            "compileGroups": [
+                {
+                    "language": "C",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                    "includes": None,
+                    "defines": None,
+                },
+            ],
+            "backtrace": [
+                {
+                    "file": "^imported/CMakeLists\\.txt$",
+                    "line": 18,
+                    "command": "add_executable",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^imported/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": "^link_imported_object_exe(\\.exe)?$",
+            "artifacts": [
+                {
+                    "path": "^imported/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?link_imported_object_exe(\\.exe)?$",
+                    "_dllExtra": False,
+                },
+                {
+                    "path": "^imported/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?link_imported_object_exe\\.pdb$",
+                    "_dllExtra": True,
+                },
+            ],
+            "build": "^imported$",
+            "source": "^imported$",
+            "install": None,
+            "link": {
+                "language": "C",
+                "lto": None,
+            },
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^ZERO_CHECK::@ba7eb709d0b48779c6c8$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "link_imported_interface_exe",
+            "id": "^link_imported_interface_exe::@ba7eb709d0b48779c6c8$",
+            "directorySource": "^imported$",
+            "type": "EXECUTABLE",
+            "isGeneratorProvided": None,
+            "sources": [
+                {
+                    "path": "^empty\\.c$",
+                    "isGenerated": None,
+                    "sourceGroupName": "Source Files",
+                    "compileGroupLanguage": "C",
+                    "backtrace": [
+                        {
+                            "file": "^imported/CMakeLists\\.txt$",
+                            "line": 23,
+                            "command": "add_executable",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^imported/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "Source Files",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                },
+            ],
+            "compileGroups": [
+                {
+                    "language": "C",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                    "includes": None,
+                    "defines": None,
+                },
+            ],
+            "backtrace": [
+                {
+                    "file": "^imported/CMakeLists\\.txt$",
+                    "line": 23,
+                    "command": "add_executable",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^imported/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": "^link_imported_interface_exe(\\.exe)?$",
+            "artifacts": [
+                {
+                    "path": "^imported/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?link_imported_interface_exe(\\.exe)?$",
+                    "_dllExtra": False,
+                },
+                {
+                    "path": "^imported/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?link_imported_interface_exe\\.pdb$",
+                    "_dllExtra": True,
+                },
+            ],
+            "build": "^imported$",
+            "source": "^imported$",
+            "install": None,
+            "link": {
+                "language": "C",
+                "lto": None,
+            },
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^ZERO_CHECK::@ba7eb709d0b48779c6c8$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "ALL_BUILD",
+            "id": "^ALL_BUILD::@c11385ffed57b860da63$",
+            "directorySource": "^custom$",
+            "type": "UTILITY",
+            "isGeneratorProvided": True,
+            "sources": [
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/custom/CMakeFiles/ALL_BUILD$",
+                    "isGenerated": True,
+                    "sourceGroupName": "",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^custom/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/custom/CMakeFiles/ALL_BUILD\\.rule$",
+                    "isGenerated": True,
+                    "sourceGroupName": "CMake Rules",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^custom/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/custom/CMakeFiles/ALL_BUILD$",
+                    ],
+                },
+                {
+                    "name": "CMake Rules",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/custom/CMakeFiles/ALL_BUILD\\.rule$",
+                    ],
+                },
+            ],
+            "compileGroups": None,
+            "backtrace": [
+                {
+                    "file": "^custom/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": None,
+            "artifacts": None,
+            "build": "^custom$",
+            "source": "^custom$",
+            "install": None,
+            "link": None,
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^ZERO_CHECK::@c11385ffed57b860da63$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^custom_exe::@c11385ffed57b860da63$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "ZERO_CHECK",
+            "id": "^ZERO_CHECK::@c11385ffed57b860da63$",
+            "directorySource": "^custom$",
+            "type": "UTILITY",
+            "isGeneratorProvided": True,
+            "sources": [
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/custom/CMakeFiles/ZERO_CHECK$",
+                    "isGenerated": True,
+                    "sourceGroupName": "",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^custom/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/custom/CMakeFiles/ZERO_CHECK\\.rule$",
+                    "isGenerated": True,
+                    "sourceGroupName": "CMake Rules",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^custom/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/custom/CMakeFiles/ZERO_CHECK$",
+                    ],
+                },
+                {
+                    "name": "CMake Rules",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/custom/CMakeFiles/ZERO_CHECK\\.rule$",
+                    ],
+                },
+            ],
+            "compileGroups": None,
+            "backtrace": [
+                {
+                    "file": "^custom/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": None,
+            "artifacts": None,
+            "build": "^custom$",
+            "source": "^custom$",
+            "install": None,
+            "link": None,
+            "archive": None,
+            "dependencies": None,
+        },
+        {
+            "name": "custom_tgt",
+            "id": "^custom_tgt::@c11385ffed57b860da63$",
+            "directorySource": "^custom$",
+            "type": "UTILITY",
+            "isGeneratorProvided": None,
+            "sources": [
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/custom/CMakeFiles/custom_tgt$",
+                    "isGenerated": True,
+                    "sourceGroupName": "",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^custom/CMakeLists\\.txt$",
+                            "line": 3,
+                            "command": "add_custom_target",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^custom/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/(custom/)?CMakeFiles/([0-9a-f]+/)?custom_tgt\\.rule$",
+                    "isGenerated": True,
+                    "sourceGroupName": "CMake Rules",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^custom/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/custom/CMakeFiles/custom_tgt$",
+                    ],
+                },
+                {
+                    "name": "CMake Rules",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/(custom/)?CMakeFiles/([0-9a-f]+/)?custom_tgt\\.rule$",
+                    ],
+                },
+            ],
+            "compileGroups": None,
+            "backtrace": [
+                {
+                    "file": "^custom/CMakeLists\\.txt$",
+                    "line": 3,
+                    "command": "add_custom_target",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^custom/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": None,
+            "artifacts": None,
+            "build": "^custom$",
+            "source": "^custom$",
+            "install": None,
+            "link": None,
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^ZERO_CHECK::@c11385ffed57b860da63$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "custom_exe",
+            "id": "^custom_exe::@c11385ffed57b860da63$",
+            "directorySource": "^custom$",
+            "type": "EXECUTABLE",
+            "isGeneratorProvided": None,
+            "sources": [
+                {
+                    "path": "^empty\\.c$",
+                    "isGenerated": None,
+                    "sourceGroupName": "Source Files",
+                    "compileGroupLanguage": "C",
+                    "backtrace": [
+                        {
+                            "file": "^custom/CMakeLists\\.txt$",
+                            "line": 4,
+                            "command": "add_executable",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^custom/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "Source Files",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                },
+            ],
+            "compileGroups": [
+                {
+                    "language": "C",
+                    "sourcePaths": [
+                        "^empty\\.c$",
+                    ],
+                    "includes": None,
+                    "defines": None,
+                },
+            ],
+            "backtrace": [
+                {
+                    "file": "^custom/CMakeLists\\.txt$",
+                    "line": 4,
+                    "command": "add_executable",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^custom/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": "^custom_exe(\\.exe)?$",
+            "artifacts": [
+                {
+                    "path": "^custom/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?custom_exe(\\.exe)?$",
+                    "_dllExtra": False,
+                },
+                {
+                    "path": "^custom/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?custom_exe\\.pdb$",
+                    "_dllExtra": True,
+                },
+            ],
+            "build": "^custom$",
+            "source": "^custom$",
+            "install": None,
+            "link": {
+                "language": "C",
+                "lto": None,
+            },
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^custom_tgt::@c11385ffed57b860da63$",
+                    "backtrace": [
+                        {
+                            "file": "^custom/CMakeLists\\.txt$",
+                            "line": 5,
+                            "command": "add_dependencies",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^custom/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+                {
+                    "id": "^ZERO_CHECK::@c11385ffed57b860da63$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "ALL_BUILD",
+            "id": "^ALL_BUILD::@[0-9a-f]+$",
+            "directorySource": "^.*/Tests/RunCMake/FileAPIExternalSource$",
+            "type": "UTILITY",
+            "isGeneratorProvided": True,
+            "sources": [
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/CMakeFiles/ALL_BUILD$",
+                    "isGenerated": True,
+                    "sourceGroupName": "",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/CMakeFiles/ALL_BUILD\\.rule$",
+                    "isGenerated": True,
+                    "sourceGroupName": "CMake Rules",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/CMakeFiles/ALL_BUILD$",
+                    ],
+                },
+                {
+                    "name": "CMake Rules",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/CMakeFiles/ALL_BUILD\\.rule$",
+                    ],
+                },
+            ],
+            "compileGroups": None,
+            "backtrace": [
+                {
+                    "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": None,
+            "artifacts": None,
+            "build": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild$",
+            "source": "^.*/Tests/RunCMake/FileAPIExternalSource$",
+            "install": None,
+            "link": None,
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^ZERO_CHECK::@[0-9a-f]+$",
+                    "backtrace": None,
+                },
+                {
+                    "id": "^generated_exe::@[0-9a-f]+$",
+                    "backtrace": None,
+                },
+            ],
+        },
+        {
+            "name": "ZERO_CHECK",
+            "id": "^ZERO_CHECK::@[0-9a-f]+$",
+            "directorySource": "^.*/Tests/RunCMake/FileAPIExternalSource$",
+            "type": "UTILITY",
+            "isGeneratorProvided": True,
+            "sources": [
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/CMakeFiles/ZERO_CHECK$",
+                    "isGenerated": True,
+                    "sourceGroupName": "",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/CMakeFiles/ZERO_CHECK\\.rule$",
+                    "isGenerated": True,
+                    "sourceGroupName": "CMake Rules",
+                    "compileGroupLanguage": None,
+                    "backtrace": [
+                        {
+                            "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/CMakeFiles/ZERO_CHECK$",
+                    ],
+                },
+                {
+                    "name": "CMake Rules",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/CMakeFiles/ZERO_CHECK\\.rule$",
+                    ],
+                },
+            ],
+            "compileGroups": None,
+            "backtrace": [
+                {
+                    "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": None,
+            "artifacts": None,
+            "build": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild$",
+            "source": "^.*/Tests/RunCMake/FileAPIExternalSource$",
+            "install": None,
+            "link": None,
+            "archive": None,
+            "dependencies": None,
+        },
+        {
+            "name": "generated_exe",
+            "id": "^generated_exe::@[0-9a-f]+$",
+            "directorySource": "^.*/Tests/RunCMake/FileAPIExternalSource$",
+            "type": "EXECUTABLE",
+            "isGeneratorProvided": None,
+            "sources": [
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPIExternalSource/empty\\.c$",
+                    "isGenerated": None,
+                    "sourceGroupName": "Source Files",
+                    "compileGroupLanguage": "C",
+                    "backtrace": [
+                        {
+                            "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                            "line": 5,
+                            "command": "add_executable",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/generated\\.cxx$",
+                    "isGenerated": True,
+                    "sourceGroupName": "Generated Source Files",
+                    "compileGroupLanguage": "CXX",
+                    "backtrace": [
+                        {
+                            "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                            "line": 6,
+                            "command": "target_sources",
+                            "hasParent": True,
+                        },
+                        {
+                            "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                            "line": None,
+                            "command": None,
+                            "hasParent": False,
+                        },
+                    ],
+                },
+            ],
+            "sourceGroups": [
+                {
+                    "name": "Source Files",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPIExternalSource/empty\\.c$",
+                    ],
+                },
+                {
+                    "name": "Generated Source Files",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/generated\\.cxx$",
+                    ],
+                },
+            ],
+            "compileGroups": [
+                {
+                    "language": "C",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPIExternalSource/empty\\.c$",
+                    ],
+                    "includes": [
+                        {
+                            "path": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild$",
+                            "isSystem": None,
+                            "backtrace": None,
+                        },
+                        {
+                            "path": "^.*/Tests/RunCMake/FileAPIExternalSource$",
+                            "isSystem": True,
+                            "backtrace": [
+                                {
+                                    "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                                    "line": 11,
+                                    "command": "target_include_directories",
+                                    "hasParent": True,
+                                },
+                                {
+                                    "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                                    "line": None,
+                                    "command": None,
+                                    "hasParent": False,
+                                },
+                            ],
+                        },
+                    ],
+                    "defines": [
+                        {
+                            "define": "EMPTY_C=1",
+                            "backtrace": None,
+                        },
+                        {
+                            "define": "SRC_DUMMY",
+                            "backtrace": None,
+                        },
+                        {
+                            "define": "GENERATED_EXE=1",
+                            "backtrace": [
+                                {
+                                    "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                                    "line": 12,
+                                    "command": "target_compile_definitions",
+                                    "hasParent": True,
+                                },
+                                {
+                                    "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                                    "line": None,
+                                    "command": None,
+                                    "hasParent": False,
+                                },
+                            ],
+                        },
+                        {
+                            "define": "TGT_DUMMY",
+                            "backtrace": [
+                                {
+                                    "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                                    "line": 12,
+                                    "command": "target_compile_definitions",
+                                    "hasParent": True,
+                                },
+                                {
+                                    "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                                    "line": None,
+                                    "command": None,
+                                    "hasParent": False,
+                                },
+                            ],
+                        },
+                    ],
+                },
+                {
+                    "language": "CXX",
+                    "sourcePaths": [
+                        "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/generated\\.cxx$",
+                    ],
+                    "includes": [
+                        {
+                            "path": "^.*/Tests/RunCMake/FileAPIExternalSource$",
+                            "isSystem": True,
+                            "backtrace": [
+                                {
+                                    "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                                    "line": 11,
+                                    "command": "target_include_directories",
+                                    "hasParent": True,
+                                },
+                                {
+                                    "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                                    "line": None,
+                                    "command": None,
+                                    "hasParent": False,
+                                },
+                            ],
+                        },
+                    ],
+                    "defines": [
+                        {
+                            "define": "GENERATED_EXE=1",
+                            "backtrace": [
+                                {
+                                    "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                                    "line": 12,
+                                    "command": "target_compile_definitions",
+                                    "hasParent": True,
+                                },
+                                {
+                                    "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                                    "line": None,
+                                    "command": None,
+                                    "hasParent": False,
+                                },
+                            ],
+                        },
+                        {
+                            "define": "TGT_DUMMY",
+                            "backtrace": [
+                                {
+                                    "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                                    "line": 12,
+                                    "command": "target_compile_definitions",
+                                    "hasParent": True,
+                                },
+                                {
+                                    "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                                    "line": None,
+                                    "command": None,
+                                    "hasParent": False,
+                                },
+                            ],
+                        },
+                    ],
+                },
+            ],
+            "backtrace": [
+                {
+                    "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                    "line": 5,
+                    "command": "add_executable",
+                    "hasParent": True,
+                },
+                {
+                    "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                    "line": None,
+                    "command": None,
+                    "hasParent": False,
+                },
+            ],
+            "folder": None,
+            "nameOnDisk": "^generated_exe(\\.exe)?$",
+            "artifacts": [
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?generated_exe(\\.exe)?$",
+                    "_dllExtra": False,
+                },
+                {
+                    "path": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?generated_exe\\.pdb$",
+                    "_dllExtra": True,
+                },
+            ],
+            "build": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild$",
+            "source": "^.*/Tests/RunCMake/FileAPIExternalSource$",
+            "install": None,
+            "link": {
+                "language": "CXX",
+                "lto": None,
+            },
+            "archive": None,
+            "dependencies": [
+                {
+                    "id": "^ZERO_CHECK::@[0-9a-f]+$",
+                    "backtrace": None,
+                },
+            ],
+        },
+    ]
+
+    if not os.path.exists(os.path.join(reply_dir, "..", "..", "..", "..", "ipo_enabled.txt")):
+        for e in expected:
+            try:
+                e["link"]["lto"] = None
+            except TypeError:  # "link" is not a dict, no problem.
+                pass
+            try:
+                e["archive"]["lto"] = None
+            except TypeError:  # "archive" is not a dict, no problem.
+                pass
+
+    if inSource:
+        for e in expected:
+            if e["sources"] is not None:
+                for s in e["sources"]:
+                    s["path"] = s["path"].replace("^.*/Tests/RunCMake/FileAPI/", "^", 1)
+            if e["sourceGroups"] is not None:
+                for g in e["sourceGroups"]:
+                    g["sourcePaths"] = [p.replace("^.*/Tests/RunCMake/FileAPI/", "^", 1) for p in g["sourcePaths"]]
+            if e["compileGroups"] is not None:
+                for g in e["compileGroups"]:
+                    g["sourcePaths"] = [p.replace("^.*/Tests/RunCMake/FileAPI/", "^", 1) for p in g["sourcePaths"]]
+
+    if matches(g, "^Visual Studio "):
+        expected = filter_list(lambda e: e["name"] not in ("ZERO_CHECK") or e["id"] == "^ZERO_CHECK::@6890427a1f51a3e7e1df$", expected)
+        for e in expected:
+            if e["type"] == "UTILITY":
+                if e["id"] == "^ZERO_CHECK::@6890427a1f51a3e7e1df$":
+                    e["sources"] = [
+                        {
+                            "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/CMakeFiles/([0-9a-f]+/)?generate\\.stamp\\.rule$",
+                            "isGenerated": True,
+                            "sourceGroupName": "CMake Rules",
+                            "compileGroupLanguage": None,
+                            "backtrace": [
+                                {
+                                    "file": "^CMakeLists\\.txt$",
+                                    "line": None,
+                                    "command": None,
+                                    "hasParent": False,
+                                },
+                            ],
+                        },
+                    ]
+                    e["sourceGroups"] = [
+                        {
+                            "name": "CMake Rules",
+                            "sourcePaths": [
+                                "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/CMakeFiles/([0-9a-f]+/)?generate\\.stamp\\.rule$",
+                            ],
+                        },
+                    ]
+                elif e["name"] in ("ALL_BUILD"):
+                    e["sources"] = []
+                    e["sourceGroups"] = None
+            if e["dependencies"] is not None:
+                for d in e["dependencies"]:
+                    if matches(d["id"], "^\\^ZERO_CHECK::@"):
+                        d["id"] = "^ZERO_CHECK::@6890427a1f51a3e7e1df$"
+
+    elif g == "Xcode":
+        if ';' in os.environ.get("CMAKE_OSX_ARCHITECTURES", ""):
+            expected = filter_list(lambda e: e["name"] not in ("link_imported_object_exe"), expected)
+            for e in expected:
+                e["dependencies"] = filter_list(lambda d: not matches(d["id"], "^\\^link_imported_object_exe::@"), e["dependencies"])
+                if e["name"] in ("c_object_lib", "cxx_object_lib"):
+                    e["artifacts"] = None
+
+    else:
+        for e in expected:
+            e["dependencies"] = filter_list(lambda d: not matches(d["id"], "^\\^ZERO_CHECK::@"), e["dependencies"])
+
+        expected = filter_list(lambda t: t["name"] not in ("ALL_BUILD", "ZERO_CHECK"), expected)
+
+    if sys.platform not in ("win32", "cygwin", "msys"):
+        for e in expected:
+            e["artifacts"] = filter_list(lambda a: not a["_dllExtra"], e["artifacts"])
+
+    return expected
+
+def check_targets(c, g, inSource):
+    check_list_match(lambda a, e: matches(a["id"], e["id"]),
+                     c["targets"], gen_check_targets(c, g, inSource),
+                     check=check_target(c),
+                     check_exception=lambda a, e: "Target ID: %s" % a["id"],
+                     missing_exception=lambda e: "Target ID: %s" % e["id"],
+                     extra_exception=lambda a: "Target ID: %s" % a["id"])
+
+def check_object_codemodel_configuration(c, g, inSource):
+    assert sorted(c.keys()) == ["directories", "name", "targets"]
+    assert is_string(c["name"])
+    check_directories(c, g)
+    check_targets(c, g, inSource)
+
+def check_object_codemodel(g):
+    def _check(o):
+        assert sorted(o.keys()) == ["configurations", "kind", "paths", "version"]
+        # The "kind" and "version" members are handled by check_index_object.
+        assert is_dict(o["paths"])
+        assert sorted(o["paths"].keys()) == ["build", "source"]
+        assert matches(o["paths"]["build"], "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build$")
+        assert matches(o["paths"]["source"], "^.*/Tests/RunCMake/FileAPI$")
+
+        inSource = os.path.dirname(o["paths"]["build"]) == o["paths"]["source"]
+
+        if matches(g, "^(Visual Studio |Xcode$)"):
+            assert sorted([c["name"] for c in o["configurations"]]) == ["Debug", "MinSizeRel", "RelWithDebInfo", "Release"]
+        else:
+            assert len(o["configurations"]) == 1
+            assert o["configurations"][0]["name"] in ("", "Debug", "Release", "RelWithDebInfo", "MinSizeRel")
 
-def check_object_codemodel(o):
-    assert sorted(o.keys()) == ["configurations", "kind", "paths", "version"]
-    # The "kind" and "version" members are handled by check_index_object.
-    # FIXME: Check "configurations"  and "paths" members
+        for c in o["configurations"]:
+            check_object_codemodel_configuration(c, g, inSource)
+    return _check
 
 assert is_dict(index)
 assert sorted(index.keys()) == ["cmake", "objects", "reply"]
-check_objects(index["objects"])
+check_objects(index["objects"], index["cmake"]["generator"]["name"])
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2.cmake b/Tests/RunCMake/FileAPI/codemodel-v2.cmake
index 8c782b9..dca1dd1 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2.cmake
+++ b/Tests/RunCMake/FileAPI/codemodel-v2.cmake
@@ -1 +1,34 @@
-# FIXME: enable_language(C) and add targets to dump
+enable_language(C)
+
+include("${CMAKE_CURRENT_LIST_DIR}/include_test.cmake")
+
+add_library(c_lib empty.c)
+add_executable(c_exe empty.c)
+target_link_libraries(c_exe PRIVATE c_lib)
+
+add_library(c_shared_lib SHARED empty.c)
+add_executable(c_shared_exe empty.c)
+target_link_libraries(c_shared_exe PRIVATE c_shared_lib)
+
+add_library(c_static_lib STATIC empty.c)
+add_executable(c_static_exe empty.c)
+target_link_libraries(c_static_exe PRIVATE c_static_lib)
+
+add_subdirectory(cxx)
+add_subdirectory(alias)
+add_subdirectory(object)
+add_subdirectory(imported)
+add_subdirectory(custom)
+add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../FileAPIExternalSource" "${CMAKE_CURRENT_BINARY_DIR}/../FileAPIExternalBuild")
+
+set_property(TARGET c_shared_lib PROPERTY LIBRARY_OUTPUT_DIRECTORY lib)
+set_property(TARGET c_shared_lib PROPERTY RUNTIME_OUTPUT_DIRECTORY lib)
+
+include(CheckIPOSupported)
+check_ipo_supported(RESULT _ipo LANGUAGES C)
+if(_ipo)
+  set_property(TARGET c_shared_lib PROPERTY INTERPROCEDURAL_OPTIMIZATION ON)
+  set_property(TARGET c_shared_exe PROPERTY INTERPROCEDURAL_OPTIMIZATION ON)
+  set_property(TARGET c_static_lib PROPERTY INTERPROCEDURAL_OPTIMIZATION ON)
+  file(WRITE "${CMAKE_BINARY_DIR}/ipo_enabled.txt" "")
+endif()
diff --git a/Tests/RunCMake/FileAPI/custom/CMakeLists.txt b/Tests/RunCMake/FileAPI/custom/CMakeLists.txt
new file mode 100644
index 0000000..1cdf5c2
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/custom/CMakeLists.txt
@@ -0,0 +1,5 @@
+project(Custom)
+
+add_custom_target(custom_tgt COMMAND ${CMAKE_COMMAND} -E echo "Building custom_tgt")
+add_executable(custom_exe ../empty.c)
+add_dependencies(custom_exe custom_tgt)
diff --git a/Tests/RunCMake/FileAPI/cxx/CMakeLists.txt b/Tests/RunCMake/FileAPI/cxx/CMakeLists.txt
new file mode 100644
index 0000000..29b61b8
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/cxx/CMakeLists.txt
@@ -0,0 +1,15 @@
+project(Cxx)
+enable_language(CXX)
+
+add_library(cxx_lib ../empty.cxx)
+add_executable(cxx_exe ../empty.cxx)
+target_link_libraries(cxx_exe PRIVATE cxx_lib)
+set_property(TARGET cxx_exe PROPERTY FOLDER bin)
+
+add_library(cxx_shared_lib SHARED ../empty.cxx)
+add_executable(cxx_shared_exe ../empty.cxx)
+target_link_libraries(cxx_shared_exe PRIVATE cxx_shared_lib)
+
+add_library(cxx_static_lib STATIC ../empty.cxx)
+add_executable(cxx_static_exe ../empty.cxx)
+target_link_libraries(cxx_static_exe PRIVATE cxx_static_lib)
diff --git a/Tests/RunCMake/FileAPI/empty.c b/Tests/RunCMake/FileAPI/empty.c
new file mode 100644
index 0000000..e69de29
diff --git a/Tests/RunCMake/FileAPI/empty.cxx b/Tests/RunCMake/FileAPI/empty.cxx
new file mode 100644
index 0000000..e69de29
diff --git a/Tests/RunCMake/FileAPI/imported/CMakeLists.txt b/Tests/RunCMake/FileAPI/imported/CMakeLists.txt
new file mode 100644
index 0000000..d36d88b
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/imported/CMakeLists.txt
@@ -0,0 +1,24 @@
+project(Imported)
+
+add_library(imported_lib UNKNOWN IMPORTED)
+add_executable(imported_exe IMPORTED)
+add_executable(link_imported_exe ../empty.c)
+target_link_libraries(link_imported_exe PRIVATE imported_lib)
+
+add_library(imported_shared_lib SHARED IMPORTED)
+add_executable(link_imported_shared_exe ../empty.c)
+target_link_libraries(link_imported_shared_exe PRIVATE imported_shared_lib)
+
+add_library(imported_static_lib STATIC IMPORTED)
+add_executable(link_imported_static_exe ../empty.c)
+target_link_libraries(link_imported_static_exe PRIVATE imported_static_lib)
+
+if(NOT CMAKE_GENERATOR STREQUAL "Xcode" OR NOT CMAKE_OSX_ARCHITECTURES MATCHES "[;$]")
+  add_library(imported_object_lib OBJECT IMPORTED)
+  add_executable(link_imported_object_exe ../empty.c)
+  target_link_libraries(link_imported_object_exe PRIVATE imported_object_lib)
+endif()
+
+add_library(imported_interface_lib INTERFACE IMPORTED)
+add_executable(link_imported_interface_exe ../empty.c)
+target_link_libraries(link_imported_interface_exe PRIVATE imported_interface_lib)
diff --git a/Tests/RunCMake/FileAPI/include_test.cmake b/Tests/RunCMake/FileAPI/include_test.cmake
new file mode 100644
index 0000000..c74d264
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/include_test.cmake
@@ -0,0 +1,9 @@
+add_library(interface_lib INTERFACE)
+target_compile_definitions(interface_lib INTERFACE COMPILED_WITH_INTERFACE_LIB)
+add_executable(interface_exe empty.c)
+target_link_libraries(interface_exe PRIVATE inteface_lib)
+set_property(TARGET interface_exe PROPERTY ENABLE_EXPORTS ON)
+set_property(TARGET interface_exe PROPERTY RUNTIME_OUTPUT_DIRECTORY bin)
+set_property(TARGET interface_exe PROPERTY ARCHIVE_OUTPUT_DIRECTORY lib)
+set_property(TARGET interface_exe PROPERTY OUTPUT_NAME my_interface_exe)
+set_property(TARGET interface_exe PROPERTY SUFFIX .myexe)
diff --git a/Tests/RunCMake/FileAPI/object/CMakeLists.txt b/Tests/RunCMake/FileAPI/object/CMakeLists.txt
new file mode 100644
index 0000000..9773b81
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/object/CMakeLists.txt
@@ -0,0 +1,13 @@
+cmake_minimum_required(VERSION 3.13)
+project(Object)
+enable_language(CXX)
+
+add_library(c_object_lib OBJECT ../empty.c)
+add_executable(c_object_exe ../empty.c)
+target_link_libraries(c_object_exe PRIVATE c_object_lib)
+
+add_library(cxx_object_lib OBJECT ../empty.cxx)
+add_executable(cxx_object_exe ../empty.cxx)
+target_link_libraries(cxx_object_exe PRIVATE cxx_object_lib)
+
+install(TARGETS c_object_exe cxx_object_exe DESTINATION bin)
diff --git a/Tests/RunCMake/FileAPIExternalSource/CMakeLists.txt b/Tests/RunCMake/FileAPIExternalSource/CMakeLists.txt
new file mode 100644
index 0000000..f5670a7
--- /dev/null
+++ b/Tests/RunCMake/FileAPIExternalSource/CMakeLists.txt
@@ -0,0 +1,12 @@
+project(External)
+enable_language(CXX)
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/generated.cxx" "")
+add_executable(generated_exe empty.c)
+target_sources(generated_exe PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/generated.cxx")
+source_group("Generated Source Files" FILES "${CMAKE_CURRENT_BINARY_DIR}/generated.cxx")
+set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/generated.cxx" PROPERTY GENERATED ON)
+set_property(SOURCE empty.c PROPERTY COMPILE_DEFINITIONS EMPTY_C=1 SRC_DUMMY)
+set_property(SOURCE empty.c PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}")
+target_include_directories(generated_exe SYSTEM PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
+target_compile_definitions(generated_exe PRIVATE GENERATED_EXE=1 -DTGT_DUMMY)
diff --git a/Tests/RunCMake/FileAPIExternalSource/empty.c b/Tests/RunCMake/FileAPIExternalSource/empty.c
new file mode 100644
index 0000000..e69de29

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=b29d96701b6df7d32f36ab47618ba1b7af1d9167
commit b29d96701b6df7d32f36ab47618ba1b7af1d9167
Merge: c337b2a 8ca8dae
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Wed Dec 12 08:14:53 2018 -0500
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Wed Dec 12 08:14:53 2018 -0500

    Merge branch 'upstream-KWSys' into update-kwsys
    
    * upstream-KWSys:
      KWSys 2018-12-12 (abb8c680)


https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=8ca8daeb475fdcb2d15e508fe911bbc7ede7773a
commit 8ca8daeb475fdcb2d15e508fe911bbc7ede7773a
Author:     KWSys Upstream <kwrobot at kitware.com>
AuthorDate: Wed Dec 12 08:11:37 2018 -0500
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Wed Dec 12 08:14:52 2018 -0500

    KWSys 2018-12-12 (abb8c680)
    
    Code extracted from:
    
        https://gitlab.kitware.com/utils/kwsys.git
    
    at commit abb8c680927708b161f87fb596cefb454ef5e97d (master).
    
    Upstream Shortlog
    -----------------
    
    Gregor Jasny (1):
          228c60b8 SystemTools: CopyFileAlways: try to create a cheap CoW clone first

diff --git a/SystemTools.cxx b/SystemTools.cxx
index 331f16e..c5160a9 100644
--- a/SystemTools.cxx
+++ b/SystemTools.cxx
@@ -82,6 +82,10 @@
 #  include <signal.h> /* sigprocmask */
 #endif
 
+#ifdef __linux
+#  include <linux/fs.h>
+#endif
+
 // Windows API.
 #if defined(_WIN32)
 #  include <windows.h>
@@ -2158,6 +2162,120 @@ bool SystemTools::FilesDiffer(const std::string& source,
 }
 
 /**
+ * Blockwise copy source to destination file
+ */
+static bool CopyFileContentBlockwise(const std::string& source,
+                                     const std::string& destination)
+{
+// Open files
+#if defined(_WIN32)
+  kwsys::ifstream fin(
+    Encoding::ToNarrow(Encoding::ToWindowsExtendedPath(source)).c_str(),
+    std::ios::in | std::ios::binary);
+#else
+  kwsys::ifstream fin(source.c_str(), std::ios::in | std::ios::binary);
+#endif
+  if (!fin) {
+    return false;
+  }
+
+  // try and remove the destination file so that read only destination files
+  // can be written to.
+  // If the remove fails continue so that files in read only directories
+  // that do not allow file removal can be modified.
+  SystemTools::RemoveFile(destination);
+
+#if defined(_WIN32)
+  kwsys::ofstream fout(
+    Encoding::ToNarrow(Encoding::ToWindowsExtendedPath(destination)).c_str(),
+    std::ios::out | std::ios::trunc | std::ios::binary);
+#else
+  kwsys::ofstream fout(destination.c_str(),
+                       std::ios::out | std::ios::trunc | std::ios::binary);
+#endif
+  if (!fout) {
+    return false;
+  }
+
+  // This copy loop is very sensitive on certain platforms with
+  // slightly broken stream libraries (like HPUX).  Normally, it is
+  // incorrect to not check the error condition on the fin.read()
+  // before using the data, but the fin.gcount() will be zero if an
+  // error occurred.  Therefore, the loop should be safe everywhere.
+  while (fin) {
+    const int bufferSize = 4096;
+    char buffer[bufferSize];
+
+    fin.read(buffer, bufferSize);
+    if (fin.gcount()) {
+      fout.write(buffer, fin.gcount());
+    } else {
+      break;
+    }
+  }
+
+  // Make sure the operating system has finished writing the file
+  // before closing it.  This will ensure the file is finished before
+  // the check below.
+  fout.flush();
+
+  fin.close();
+  fout.close();
+
+  if (!fout) {
+    return false;
+  }
+
+  return true;
+}
+
+/**
+ * Clone the source file to the destination file
+ *
+ * If available, the Linux FICLONE ioctl is used to create a check
+ * copy-on-write clone of the source file.
+ *
+ * The method returns false for the following cases:
+ * - The code has not been compiled on Linux or the ioctl was unknown
+ * - The source and destination is on different file systems
+ * - The underlying filesystem does not support file cloning
+ * - An unspecified error occurred
+ */
+static bool CloneFileContent(const std::string& source,
+                             const std::string& destination)
+{
+#if defined(__linux) && defined(FICLONE)
+  int in = open(source.c_str(), O_RDONLY);
+  if (in < 0) {
+    return false;
+  }
+
+  SystemTools::RemoveFile(destination);
+
+  int out =
+    open(destination.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+  if (out < 0) {
+    close(in);
+    return false;
+  }
+
+  int result = ioctl(out, FICLONE, in);
+  close(in);
+  close(out);
+
+  if (result < 0) {
+    return false;
+  }
+
+  return true;
+#else
+  (void)source;
+  (void)destination;
+  return false;
+#endif
+}
+
+/**
  * Copy a file named by "source" to the file named by "destination".
  */
 bool SystemTools::CopyFileAlways(const std::string& source,
@@ -2174,9 +2292,6 @@ bool SystemTools::CopyFileAlways(const std::string& source,
   if (SystemTools::FileIsDirectory(source)) {
     SystemTools::MakeDirectory(destination);
   } else {
-    const int bufferSize = 4096;
-    char buffer[bufferSize];
-
     // If destination is a directory, try to create a file with the same
     // name as the source in that directory.
 
@@ -2195,62 +2310,12 @@ bool SystemTools::CopyFileAlways(const std::string& source,
 
     SystemTools::MakeDirectory(destination_dir);
 
-// Open files
-#if defined(_WIN32)
-    kwsys::ifstream fin(
-      Encoding::ToNarrow(Encoding::ToWindowsExtendedPath(source)).c_str(),
-      std::ios::in | std::ios::binary);
-#else
-    kwsys::ifstream fin(source.c_str(), std::ios::in | std::ios::binary);
-#endif
-    if (!fin) {
-      return false;
-    }
-
-    // try and remove the destination file so that read only destination files
-    // can be written to.
-    // If the remove fails continue so that files in read only directories
-    // that do not allow file removal can be modified.
-    SystemTools::RemoveFile(real_destination);
-
-#if defined(_WIN32)
-    kwsys::ofstream fout(
-      Encoding::ToNarrow(Encoding::ToWindowsExtendedPath(real_destination))
-        .c_str(),
-      std::ios::out | std::ios::trunc | std::ios::binary);
-#else
-    kwsys::ofstream fout(real_destination.c_str(),
-                         std::ios::out | std::ios::trunc | std::ios::binary);
-#endif
-    if (!fout) {
-      return false;
-    }
-
-    // This copy loop is very sensitive on certain platforms with
-    // slightly broken stream libraries (like HPUX).  Normally, it is
-    // incorrect to not check the error condition on the fin.read()
-    // before using the data, but the fin.gcount() will be zero if an
-    // error occurred.  Therefore, the loop should be safe everywhere.
-    while (fin) {
-      fin.read(buffer, bufferSize);
-      if (fin.gcount()) {
-        fout.write(buffer, fin.gcount());
-      } else {
-        break;
+    if (!CloneFileContent(source, real_destination)) {
+      // if cloning did not succeed, fall back to blockwise copy
+      if (!CopyFileContentBlockwise(source, real_destination)) {
+        return false;
       }
     }
-
-    // Make sure the operating system has finished writing the file
-    // before closing it.  This will ensure the file is finished before
-    // the check below.
-    fout.flush();
-
-    fin.close();
-    fout.close();
-
-    if (!fout) {
-      return false;
-    }
   }
   if (perms) {
     if (!SystemTools::SetPermissions(real_destination, perm)) {

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=2d68b2c593b0bc19273e95f98ff29b3d3cff90c7
commit 2d68b2c593b0bc19273e95f98ff29b3d3cff90c7
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Thu Nov 8 08:12:02 2018 -0500
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Wed Dec 12 08:10:16 2018 -0500

    String: Add str_if_stable() as a const alternative to str()
    
    The `str()` method must be non-const because it may need to internally
    mutate the representation of the string in order to have an owned
    `std::string` instance holding the exact string (not a superstring).
    This is inconvenient in contexts where we can ensure that no mutation
    is needed to get a `std::string const&`.
    
    Add a `str_if_stable() const` method that returns `std::string const*`
    so we can return `nullptr` if if mutation would be necessary to get a
    `std::string const&`.  Add supporting `is_stable() const` and
    `stabilize()` methods to check and enforce stable availability of
    `std::string const&`.  These can be used to create `String const`
    instances from which we can still get a `std::string const&` via
    `*str_if_stable()` by maintaining the stability invariant at runtime.

diff --git a/Source/cmString.cxx b/Source/cmString.cxx
index e965bfb..2a0c125 100644
--- a/Source/cmString.cxx
+++ b/Source/cmString.cxx
@@ -22,20 +22,41 @@ void String::internally_mutate_to_stable_string()
   *this = String(data(), size());
 }
 
-std::string const& String::str()
+bool String::is_stable() const
+{
+  return str_if_stable() != nullptr;
+}
+
+void String::stabilize()
+{
+  if (is_stable()) {
+    return;
+  }
+  this->internally_mutate_to_stable_string();
+}
+
+std::string const* String::str_if_stable() const
 {
   if (!data()) {
     // We view no string.
     // This is stable for the lifetime of our current value.
-    return empty_string_;
+    return &empty_string_;
   }
 
   if (string_ && data() == string_->data() && size() == string_->size()) {
     // We view an entire string.
     // This is stable for the lifetime of our current value.
-    return *string_;
+    return string_.get();
   }
 
+  return nullptr;
+}
+
+std::string const& String::str()
+{
+  if (std::string const* s = str_if_stable()) {
+    return *s;
+  }
   // Mutate to hold a std::string that is stable for the lifetime
   // of our current value.
   this->internally_mutate_to_stable_string();
diff --git a/Source/cmString.hxx b/Source/cmString.hxx
index 52e891c..1623a43 100644
--- a/Source/cmString.hxx
+++ b/Source/cmString.hxx
@@ -348,6 +348,20 @@ public:
 
   char back() const noexcept { return view_.back(); }
 
+  /** Return true if this instance is stable and otherwise false.
+      An instance is stable if it is in the 'null' state or if it is
+      an 'owned' state not produced by substring operations, or
+      after a call to 'stabilize()' or 'str()'.  */
+  bool is_stable() const;
+
+  /** If 'is_stable()' does not return true, mutate so it does.  */
+  void stabilize();
+
+  /** Get a pointer to a normal std::string if 'is_stable()' returns
+      true and otherwise nullptr.  The pointer is valid until this
+      instance is mutated or destroyed.  */
+  std::string const* str_if_stable() const;
+
   /** Get a refernce to a normal std::string.  The reference
       is valid until this instance is mutated or destroyed.  */
   std::string const& str();
diff --git a/Tests/CMakeLib/testString.cxx b/Tests/CMakeLib/testString.cxx
index 8bdcb15..2aa1459 100644
--- a/Tests/CMakeLib/testString.cxx
+++ b/Tests/CMakeLib/testString.cxx
@@ -30,8 +30,10 @@ static bool testConstructDefault()
   ASSERT_TRUE(str_const.data() == nullptr);
   ASSERT_TRUE(str_const.size() == 0);
   ASSERT_TRUE(str_const.empty());
+  ASSERT_TRUE(str_const.is_stable());
   ASSERT_TRUE(str.c_str() == nullptr);
   ASSERT_TRUE(str.str().empty());
+  ASSERT_TRUE(str.is_stable());
   return true;
 }
 
@@ -42,8 +44,10 @@ static bool testFromNullPtr(cm::String str)
   ASSERT_TRUE(str_const.data() == nullptr);
   ASSERT_TRUE(str_const.size() == 0);
   ASSERT_TRUE(str_const.empty());
+  ASSERT_TRUE(str_const.is_stable());
   ASSERT_TRUE(str.c_str() == nullptr);
   ASSERT_TRUE(str.str().empty());
+  ASSERT_TRUE(str.is_stable());
   return true;
 }
 
@@ -68,8 +72,10 @@ static bool testFromCStrNull(cm::String str)
   ASSERT_TRUE(str_const.data() == nullptr);
   ASSERT_TRUE(str_const.size() == 0);
   ASSERT_TRUE(str_const.empty());
+  ASSERT_TRUE(str_const.is_stable());
   ASSERT_TRUE(str.c_str() == nullptr);
   ASSERT_TRUE(str.str().empty());
+  ASSERT_TRUE(str.is_stable());
   return true;
 }
 
@@ -96,12 +102,16 @@ static bool testFromCharArray(cm::String str)
   cm::String const& str_const = str;
   ASSERT_TRUE(str_const.data() != charArray);
   ASSERT_TRUE(str_const.size() == sizeof(charArray) - 1);
+  ASSERT_TRUE(str_const.is_stable());
   ASSERT_TRUE(str.c_str() != charArray);
+  ASSERT_TRUE(str.is_stable());
   cm::String substr = str.substr(1);
   cm::String const& substr_const = substr;
   ASSERT_TRUE(substr_const.data() != &charArray[1]);
   ASSERT_TRUE(substr_const.size() == 2);
+  ASSERT_TRUE(!substr_const.is_stable());
   ASSERT_TRUE(substr.c_str() != &charArray[1]);
+  ASSERT_TRUE(!substr.is_stable());
   return true;
 }
 
@@ -126,6 +136,7 @@ static bool testFromCStr(cm::String const& str)
   ASSERT_TRUE(str.data() != cstr);
   ASSERT_TRUE(str.size() == 3);
   ASSERT_TRUE(std::strncmp(str.data(), cstr, 3) == 0);
+  ASSERT_TRUE(str.is_stable());
   return true;
 }
 
@@ -156,6 +167,7 @@ static bool testFromStdString(cm::String const& str)
 #endif
   ASSERT_TRUE(str.size() == 3);
   ASSERT_TRUE(std::strncmp(str.data(), stdstr.data(), 3) == 0);
+  ASSERT_TRUE(str.is_stable());
   return true;
 }
 
@@ -193,6 +205,7 @@ static bool testFromChar(cm::String const& str)
 {
   ASSERT_TRUE(str.size() == 1);
   ASSERT_TRUE(std::strncmp(str.data(), "a", 1) == 0);
+  ASSERT_TRUE(str.is_stable());
   return true;
 }
 
@@ -216,6 +229,7 @@ static bool testConstructFromInitList()
   cm::String const str{ 'a', 'b', 'c' };
   ASSERT_TRUE(str.size() == 3);
   ASSERT_TRUE(std::strncmp(str.data(), "abc", 3) == 0);
+  ASSERT_TRUE(str.is_stable());
   return true;
 }
 
@@ -226,6 +240,7 @@ static bool testAssignFromInitList()
   str = { 'a', 'b', 'c' };
   ASSERT_TRUE(str.size() == 3);
   ASSERT_TRUE(std::strncmp(str.data(), "abc", 3) == 0);
+  ASSERT_TRUE(str.is_stable());
   return true;
 }
 
@@ -243,6 +258,7 @@ static bool testConstructFromInputIterator()
   ASSERT_TRUE(str.data() != cstr);
   ASSERT_TRUE(str.size() == 3);
   ASSERT_TRUE(std::strncmp(str.data(), cstr, 3) == 0);
+  ASSERT_TRUE(str.is_stable());
   return true;
 }
 
@@ -252,6 +268,7 @@ static bool testConstructFromN()
   cm::String const str(3, 'a');
   ASSERT_TRUE(str.size() == 3);
   ASSERT_TRUE(std::strncmp(str.data(), "aaa", 3) == 0);
+  ASSERT_TRUE(str.is_stable());
   return true;
 }
 
@@ -262,12 +279,16 @@ static bool testFromStaticStringView(cm::String str)
   cm::String const& str_const = str;
   ASSERT_TRUE(str_const.data() == staticStringView.data());
   ASSERT_TRUE(str_const.size() == staticStringView.size());
+  ASSERT_TRUE(!str_const.is_stable());
   ASSERT_TRUE(str.c_str() == staticStringView);
+  ASSERT_TRUE(!str.is_stable());
   cm::String substr = str.substr(1);
   cm::String const& substr_const = substr;
   ASSERT_TRUE(substr_const.data() == &staticStringView[1]);
   ASSERT_TRUE(substr_const.size() == 2);
+  ASSERT_TRUE(!substr_const.is_stable());
   ASSERT_TRUE(substr.c_str() == &staticStringView[1]);
+  ASSERT_TRUE(!substr.is_stable());
   return true;
 }
 
@@ -294,6 +315,8 @@ static bool testConstructCopy()
   ASSERT_TRUE(s1.size() == 3);
   ASSERT_TRUE(s2.size() == 3);
   ASSERT_TRUE(std::strncmp(s2.data(), "abc", 3) == 0);
+  ASSERT_TRUE(s1.is_stable());
+  ASSERT_TRUE(s2.is_stable());
   return true;
 }
 
@@ -306,6 +329,8 @@ static bool testConstructMove()
   ASSERT_TRUE(s1.size() == 0);
   ASSERT_TRUE(s2.size() == 3);
   ASSERT_TRUE(std::strncmp(s2.data(), "abc", 3) == 0);
+  ASSERT_TRUE(s1.is_stable());
+  ASSERT_TRUE(s2.is_stable());
   return true;
 }
 
@@ -319,6 +344,8 @@ static bool testAssignCopy()
   ASSERT_TRUE(s1.size() == 3);
   ASSERT_TRUE(s2.size() == 3);
   ASSERT_TRUE(std::strncmp(s2.data(), "abc", 3) == 0);
+  ASSERT_TRUE(s1.is_stable());
+  ASSERT_TRUE(s2.is_stable());
   return true;
 }
 
@@ -332,6 +359,8 @@ static bool testAssignMove()
   ASSERT_TRUE(s1.size() == 0);
   ASSERT_TRUE(s2.size() == 3);
   ASSERT_TRUE(std::strncmp(s2.data(), "abc", 3) == 0);
+  ASSERT_TRUE(s1.is_stable());
+  ASSERT_TRUE(s2.is_stable());
   return true;
 }
 
@@ -376,6 +405,7 @@ static bool testOperatorPlusEqual()
   str += cm::String("g");
   ASSERT_TRUE(str.size() == 7);
   ASSERT_TRUE(std::strncmp(str.data(), "abcdefg", 7) == 0);
+  ASSERT_TRUE(str.is_stable());
   return true;
 }
 
@@ -742,15 +772,18 @@ static bool testMethod_substr_AtEnd(cm::String str)
   cm::String substr = str.substr(1);
   ASSERT_TRUE(substr.data() == str.data() + 1);
   ASSERT_TRUE(substr.size() == 2);
+  ASSERT_TRUE(!substr.is_stable());
 
   // c_str() at the end of the buffer does not internally mutate.
   ASSERT_TRUE(std::strcmp(substr.c_str(), "bc") == 0);
   ASSERT_TRUE(substr.c_str() == str.data() + 1);
   ASSERT_TRUE(substr.data() == str.data() + 1);
   ASSERT_TRUE(substr.size() == 2);
+  ASSERT_TRUE(!substr.is_stable());
 
   // str() internally mutates.
   ASSERT_TRUE(substr.str() == "bc");
+  ASSERT_TRUE(substr.is_stable());
   ASSERT_TRUE(substr.data() != str.data() + 1);
   ASSERT_TRUE(substr.size() == 2);
   ASSERT_TRUE(substr.c_str() != str.data() + 1);
@@ -783,9 +816,11 @@ static bool testMethod_substr_AtStart(cm::String str)
     ASSERT_TRUE(substr_c != str.data());
     ASSERT_TRUE(substr.data() != str.data());
     ASSERT_TRUE(substr.size() == 2);
+    ASSERT_TRUE(substr.is_stable());
 
     // str() does not need to internally mutate after c_str() did so
     ASSERT_TRUE(substr.str() == "ab");
+    ASSERT_TRUE(substr.is_stable());
     ASSERT_TRUE(substr.data() == substr_c);
     ASSERT_TRUE(substr.size() == 2);
     ASSERT_TRUE(substr.c_str() == substr_c);
@@ -795,9 +830,11 @@ static bool testMethod_substr_AtStart(cm::String str)
     cm::String substr = str.substr(0, 2);
     ASSERT_TRUE(substr.data() == str.data());
     ASSERT_TRUE(substr.size() == 2);
+    ASSERT_TRUE(!substr.is_stable());
 
     // str() internally mutates.
     ASSERT_TRUE(substr.str() == "ab");
+    ASSERT_TRUE(substr.is_stable());
     ASSERT_TRUE(substr.data() != str.data());
     ASSERT_TRUE(substr.size() == 2);
     ASSERT_TRUE(substr.c_str() != str.data());
@@ -807,6 +844,7 @@ static bool testMethod_substr_AtStart(cm::String str)
     ASSERT_TRUE(std::strcmp(substr_c, "ab") == 0);
     ASSERT_TRUE(substr_c == substr.data());
     ASSERT_TRUE(substr.size() == 2);
+    ASSERT_TRUE(substr.is_stable());
   }
 
   return true;
@@ -1088,6 +1126,7 @@ static bool testAddition()
     cm::String str;
     str += "a" + cm::String("b") + 'c';
     ASSERT_TRUE(str == "abc");
+    ASSERT_TRUE(str.is_stable());
   }
   {
     std::string s;
@@ -1102,6 +1141,23 @@ static bool testAddition()
   return true;
 }
 
+static bool testStability()
+{
+  std::cout << "testStability()\n";
+  cm::String str = "abc"_s;
+  ASSERT_TRUE(!str.is_stable());
+  ASSERT_TRUE(str.str_if_stable() == nullptr);
+  str.stabilize();
+  ASSERT_TRUE(str.is_stable());
+  std::string const* str_if_stable = str.str_if_stable();
+  ASSERT_TRUE(str_if_stable != nullptr);
+  ASSERT_TRUE(*str_if_stable == "abc");
+  str.stabilize();
+  ASSERT_TRUE(str.is_stable());
+  ASSERT_TRUE(str.str_if_stable() == str_if_stable);
+  return true;
+}
+
 int testString(int /*unused*/, char* /*unused*/ [])
 {
   if (!testConstructDefault()) {
@@ -1284,5 +1340,8 @@ int testString(int /*unused*/, char* /*unused*/ [])
   if (!testAddition()) {
     return 1;
   }
+  if (!testStability()) {
+    return 1;
+  }
   return 0;
 }

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=a0841b59bdacc1e550e6607d9e44e79ae456cd19
commit a0841b59bdacc1e550e6607d9e44e79ae456cd19
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Fri Dec 7 21:08:17 2018 -0500
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Wed Dec 12 08:10:15 2018 -0500

    String: Add support for a ""_s string literal syntax
    
    Create a `static_string_view` type that binds only to the static storage
    of string literals.  Teach `cm::String` to borrow from these implicitly.

diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 0ce09d4..ab3e835 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -377,6 +377,7 @@ set(SRCS
   cmake.h
   cm_string_view.cxx
   cm_string_view.hxx
+  cm_static_string_view.hxx
 
   cmCommand.cxx
   cmCommand.h
diff --git a/Source/cmString.hxx b/Source/cmString.hxx
index 3b59353..52e891c 100644
--- a/Source/cmString.hxx
+++ b/Source/cmString.hxx
@@ -5,6 +5,7 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include "cm_static_string_view.hxx"
 #include "cm_string_view.hxx"
 
 #include <algorithm>
@@ -90,6 +91,12 @@ struct IntoString<string_view> : std::true_type
 };
 
 template <>
+struct IntoString<static_string_view> : std::true_type
+{
+  static string_view into_string(static_string_view s) { return s; }
+};
+
+template <>
 struct IntoString<char> : std::true_type
 {
   static std::string into_string(char const& c) { return std::string(1, c); }
@@ -157,6 +164,12 @@ struct AsStringView<string_view> : std::true_type
 };
 
 template <>
+struct AsStringView<static_string_view> : std::true_type
+{
+  static string_view view(static_string_view const& s) { return s; }
+};
+
+template <>
 struct AsStringView<String> : std::true_type
 {
   static string_view view(String const& s);
@@ -370,7 +383,7 @@ public:
   }
 
   /** Assign to an empty string.  */
-  void clear() { *this = String(string_view("", 0), Private()); }
+  void clear() { *this = ""_s; }
 
   /** Insert 'count' copies of 'ch' at position 'index'.  */
   String& insert(size_type index, size_type count, char ch);
diff --git a/Source/cm_static_string_view.hxx b/Source/cm_static_string_view.hxx
new file mode 100644
index 0000000..1bef0c6
--- /dev/null
+++ b/Source/cm_static_string_view.hxx
@@ -0,0 +1,41 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cm_static_string_view_hxx
+#define cm_static_string_view_hxx
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include "cm_string_view.hxx"
+
+#include <cstddef>
+
+namespace cm {
+
+/** A string_view that only binds to static storage.
+ *
+ * This is used together with the `""_s` user-defined literal operator
+ * to construct a type-safe abstraction of a string_view that only views
+ * statically allocated strings.  These strings are const and available
+ * for the entire lifetime of the program.
+ */
+class static_string_view : public string_view
+{
+  static_string_view(string_view v)
+    : string_view(v)
+  {
+  }
+
+  friend static_string_view operator"" _s(const char* data, size_t size);
+};
+
+/** Create a static_string_view using `""_s` literal syntax.  */
+inline static_string_view operator"" _s(const char* data, size_t size)
+{
+  return string_view(data, size);
+}
+
+} // namespace cm
+
+using cm::operator"" _s;
+
+#endif
diff --git a/Tests/CMakeLib/testString.cxx b/Tests/CMakeLib/testString.cxx
index 411a61a..8bdcb15 100644
--- a/Tests/CMakeLib/testString.cxx
+++ b/Tests/CMakeLib/testString.cxx
@@ -3,6 +3,7 @@
 
 #include "cmString.hxx"
 
+#include "cm_static_string_view.hxx"
 #include "cm_string_view.hxx"
 
 #include <cstring>
@@ -254,6 +255,36 @@ static bool testConstructFromN()
   return true;
 }
 
+static const auto staticStringView = "abc"_s;
+
+static bool testFromStaticStringView(cm::String str)
+{
+  cm::String const& str_const = str;
+  ASSERT_TRUE(str_const.data() == staticStringView.data());
+  ASSERT_TRUE(str_const.size() == staticStringView.size());
+  ASSERT_TRUE(str.c_str() == staticStringView);
+  cm::String substr = str.substr(1);
+  cm::String const& substr_const = substr;
+  ASSERT_TRUE(substr_const.data() == &staticStringView[1]);
+  ASSERT_TRUE(substr_const.size() == 2);
+  ASSERT_TRUE(substr.c_str() == &staticStringView[1]);
+  return true;
+}
+
+static bool testConstructFromStaticStringView()
+{
+  std::cout << "testConstructFromStaticStringView()\n";
+  return testFromStaticStringView(staticStringView);
+}
+
+static bool testAssignFromStaticStringView()
+{
+  std::cout << "testAssignFromStaticStringView()\n";
+  cm::String str;
+  str = staticStringView;
+  return testFromStaticStringView(str);
+}
+
 static bool testConstructCopy()
 {
   std::cout << "testConstructCopy()\n";
@@ -730,7 +761,7 @@ static bool testMethod_substr_AtEnd(cm::String str)
 static bool testMethod_substr_AtEndBorrowed()
 {
   std::cout << "testMethod_substr_AtEndBorrowed()\n";
-  return testMethod_substr_AtEnd(cm::String::borrow("abc"));
+  return testMethod_substr_AtEnd("abc"_s);
 }
 
 static bool testMethod_substr_AtEndOwned()
@@ -784,7 +815,7 @@ static bool testMethod_substr_AtStart(cm::String str)
 static bool testMethod_substr_AtStartBorrowed()
 {
   std::cout << "testMethod_substr_AtStartBorrowed()\n";
-  return testMethod_substr_AtStart(cm::String::borrow("abc"));
+  return testMethod_substr_AtStart("abc"_s);
 }
 
 static bool testMethod_substr_AtStartOwned()
@@ -978,6 +1009,14 @@ static bool testAddition()
     ASSERT_TRUE("abcd" == "a" + (cm::String("b") + "c") + "d");
   }
   {
+    ASSERT_TRUE(cm::String("a"_s) + "b"_s == "ab"_s);
+    ASSERT_TRUE("ab"_s == "a"_s + cm::String("b"_s));
+    ASSERT_TRUE("a"_s + cm::String("b"_s) + "c"_s == "abc"_s);
+    ASSERT_TRUE("abc"_s == "a"_s + cm::String("b"_s) + "c"_s);
+    ASSERT_TRUE("a"_s + (cm::String("b"_s) + "c"_s) + "d"_s == "abcd"_s);
+    ASSERT_TRUE("abcd"_s == "a"_s + (cm::String("b"_s) + "c"_s) + "d"_s);
+  }
+  {
     const char* a = "a";
     const char* b = "b";
     const char* ab = "ab";
@@ -1101,6 +1140,9 @@ int testString(int /*unused*/, char* /*unused*/ [])
   if (!testConstructFromN()) {
     return 1;
   }
+  if (!testConstructFromStaticStringView()) {
+    return 1;
+  }
   if (!testConstructCopy()) {
     return 1;
   }
@@ -1137,6 +1179,9 @@ int testString(int /*unused*/, char* /*unused*/ [])
   if (!testAssignFromInitList()) {
     return 1;
   }
+  if (!testAssignFromStaticStringView()) {
+    return 1;
+  }
   if (!testOperatorBool()) {
     return 1;
   }

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=9d5fe8e96a074b6e112d981302c77e31a1bcde00
commit 9d5fe8e96a074b6e112d981302c77e31a1bcde00
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Wed Oct 3 20:08:18 2018 -0400
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Wed Dec 12 08:10:15 2018 -0500

    String: Add 'borrow' member to construct borrowing instances
    
    This will allow creation of `cm::String` instances that borrow from
    non-owned storage.  It is the caller's responsibility to ensure that
    no copy of the instance outlives the borrowed buffer.

diff --git a/Source/cmString.hxx b/Source/cmString.hxx
index ddafd56..3b59353 100644
--- a/Source/cmString.hxx
+++ b/Source/cmString.hxx
@@ -272,6 +272,10 @@ public:
 
   ~String() = default;
 
+  /** Construct by borrowing an externally-owned buffer.  The buffer
+      must outlive the returned instance and all copies of it.  */
+  static String borrow(string_view v) { return String(v, Private()); }
+
   /** Assign by moving from another String instance.
       The other instance is left as a null string.  */
   String& operator=(String&& s) noexcept
diff --git a/Tests/CMakeLib/testString.cxx b/Tests/CMakeLib/testString.cxx
index 63688af..411a61a 100644
--- a/Tests/CMakeLib/testString.cxx
+++ b/Tests/CMakeLib/testString.cxx
@@ -469,6 +469,17 @@ static bool testOperatorStdStringPlusEqual()
   return true;
 }
 
+static bool testMethod_borrow()
+{
+  std::cout << "testMethod_borrow()\n";
+  std::string s = "abc";
+  cm::String str = cm::String::borrow(s);
+  ASSERT_TRUE(str.data() == s.data());
+  ASSERT_TRUE(str.size() == s.size());
+  ASSERT_TRUE(str == s);
+  return true;
+}
+
 static bool testMethod_view()
 {
   std::cout << "testMethod_view()\n";
@@ -716,6 +727,12 @@ static bool testMethod_substr_AtEnd(cm::String str)
   return true;
 }
 
+static bool testMethod_substr_AtEndBorrowed()
+{
+  std::cout << "testMethod_substr_AtEndBorrowed()\n";
+  return testMethod_substr_AtEnd(cm::String::borrow("abc"));
+}
+
 static bool testMethod_substr_AtEndOwned()
 {
   std::cout << "testMethod_substr_AtEndOwned()\n";
@@ -764,6 +781,12 @@ static bool testMethod_substr_AtStart(cm::String str)
   return true;
 }
 
+static bool testMethod_substr_AtStartBorrowed()
+{
+  std::cout << "testMethod_substr_AtStartBorrowed()\n";
+  return testMethod_substr_AtStart(cm::String::borrow("abc"));
+}
+
 static bool testMethod_substr_AtStartOwned()
 {
   std::cout << "testMethod_substr_AtStartOwned()\n";
@@ -1132,6 +1155,9 @@ int testString(int /*unused*/, char* /*unused*/ [])
   if (!testOperatorStdStringPlusEqual()) {
     return 1;
   }
+  if (!testMethod_borrow()) {
+    return 1;
+  }
   if (!testMethod_view()) {
     return 1;
   }
@@ -1177,9 +1203,15 @@ int testString(int /*unused*/, char* /*unused*/ [])
   if (!testMethodIterators()) {
     return 1;
   }
+  if (!testMethod_substr_AtEndBorrowed()) {
+    return 1;
+  }
   if (!testMethod_substr_AtEndOwned()) {
     return 1;
   }
+  if (!testMethod_substr_AtStartBorrowed()) {
+    return 1;
+  }
   if (!testMethod_substr_AtStartOwned()) {
     return 1;
   }

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=80802a002c16249e99c24f62adb8ffbe006b97e0
commit 80802a002c16249e99c24f62adb8ffbe006b97e0
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Mon Oct 1 15:45:02 2018 -0400
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Wed Dec 12 08:10:15 2018 -0500

    String: Add support for concatenation by operator+
    
    Use expression templates to collect the entire expression and
    pre-allocate a string with the final length before concatenating
    the pieces.

diff --git a/Source/cmString.hxx b/Source/cmString.hxx
index 833234c..ddafd56 100644
--- a/Source/cmString.hxx
+++ b/Source/cmString.hxx
@@ -662,6 +662,107 @@ operator>=(L&& l, R&& r)
 std::ostream& operator<<(std::ostream& os, String const& s);
 std::string& operator+=(std::string& self, String const& s);
 
+template <typename L, typename R>
+struct StringOpPlus
+{
+  L l;
+  R r;
+#if defined(__SUNPRO_CC)
+  StringOpPlus(L in_l, R in_r)
+    : l(in_l)
+    , r(in_r)
+  {
+  }
+#endif
+  operator std::string() const;
+  std::string::size_type size() const { return l.size() + r.size(); }
+};
+
+template <typename T>
+struct StringAdd
+{
+  static const bool value = AsStringView<T>::value;
+  typedef string_view temp_type;
+  template <typename S>
+  static temp_type temp(S&& s)
+  {
+    return AsStringView<T>::view(std::forward<S>(s));
+  }
+};
+
+template <typename L, typename R>
+struct StringAdd<StringOpPlus<L, R>> : std::true_type
+{
+  typedef StringOpPlus<L, R> const& temp_type;
+  static temp_type temp(temp_type s) { return s; }
+};
+
+template <typename L, typename R>
+StringOpPlus<L, R>::operator std::string() const
+{
+  std::string s;
+  s.reserve(size());
+  s += *this;
+  return s;
+}
+
+template <typename L, typename R>
+std::string& operator+=(std::string& s, StringOpPlus<L, R> const& a)
+{
+  s.reserve(s.size() + a.size());
+  s += a.l;
+  s += a.r;
+  return s;
+}
+
+template <typename L, typename R>
+String& operator+=(String& s, StringOpPlus<L, R> const& a)
+{
+  std::string r;
+  r.reserve(s.size() + a.size());
+  r.assign(s.data(), s.size());
+  r += a.l;
+  r += a.r;
+  s = std::move(r);
+  return s;
+}
+
+template <typename L, typename R>
+std::ostream& operator<<(std::ostream& os, StringOpPlus<L, R> const& a)
+{
+  return os << a.l << a.r;
+}
+
+template <typename L, typename R>
+struct IntoString<StringOpPlus<L, R>> : std::true_type
+{
+  static std::string into_string(StringOpPlus<L, R> const& a) { return a; }
+};
+
+template <typename L, typename R>
+typename std::enable_if<StringAdd<L>::value && StringAdd<R>::value,
+                        StringOpPlus<typename StringAdd<L>::temp_type,
+                                     typename StringAdd<R>::temp_type>>::type
+operator+(L&& l, R&& r)
+{
+  return { StringAdd<L>::temp(std::forward<L>(l)),
+           StringAdd<R>::temp(std::forward<R>(r)) };
+}
+
+template <typename LL, typename LR, typename R>
+typename std::enable_if<AsStringView<R>::value, bool>::type operator==(
+  StringOpPlus<LL, LR> const& l, R&& r)
+{
+  return std::string(l) == AsStringView<R>::view(std::forward<R>(r));
+}
+
+template <typename L, typename RL, typename RR>
+typename std::enable_if<AsStringView<L>::value, bool>::type operator==(
+  L&& l, StringOpPlus<RL, RR> const& r)
+{
+  return AsStringView<L>::view(std::forward<L>(l)) == std::string(r);
+}
+
 } // namespace cm
 
 namespace std {
diff --git a/Tests/CMakeLib/testString.cxx b/Tests/CMakeLib/testString.cxx
index 9fe2e39..63688af 100644
--- a/Tests/CMakeLib/testString.cxx
+++ b/Tests/CMakeLib/testString.cxx
@@ -11,6 +11,7 @@
 #include <sstream>
 #include <stdexcept>
 #include <string>
+#include <type_traits>
 #include <utility>
 
 #define ASSERT_TRUE(x)                                                        \
@@ -942,6 +943,103 @@ static bool testMethod_find_last_not_of()
   return true;
 }
 
+static bool testAddition()
+{
+  std::cout << "testAddition()\n";
+  {
+    ASSERT_TRUE(cm::String("a") + "b" == "ab");
+    ASSERT_TRUE("ab" == "a" + cm::String("b"));
+    ASSERT_TRUE("a" + cm::String("b") + "c" == "abc");
+    ASSERT_TRUE("abc" == "a" + cm::String("b") + "c");
+    ASSERT_TRUE("a" + (cm::String("b") + "c") + "d" == "abcd");
+    ASSERT_TRUE("abcd" == "a" + (cm::String("b") + "c") + "d");
+  }
+  {
+    const char* a = "a";
+    const char* b = "b";
+    const char* ab = "ab";
+    ASSERT_TRUE(cm::String(a) + b == ab);
+    ASSERT_TRUE(ab == a + cm::String(b));
+    const char* c = "c";
+    const char* abc = "abc";
+    ASSERT_TRUE(a + cm::String(b) + c == abc);
+    ASSERT_TRUE(abc == a + cm::String(b) + c);
+    const char* d = "d";
+    const char* abcd = "abcd";
+    ASSERT_TRUE(a + (cm::String(b) + c) + d == abcd);
+    ASSERT_TRUE(abcd == a + (cm::String(b) + c) + d);
+  }
+  {
+    ASSERT_TRUE(cm::String('a') + 'b' == "ab");
+    ASSERT_TRUE("ab" == 'a' + cm::String('b'));
+    ASSERT_TRUE('a' + cm::String('b') + 'c' == "abc");
+    ASSERT_TRUE("abc" == 'a' + cm::String('b') + 'c');
+    ASSERT_TRUE('a' + (cm::String('b') + 'c') + 'd' == "abcd");
+    ASSERT_TRUE("abcd" == 'a' + (cm::String('b') + 'c') + 'd');
+  }
+  {
+    std::string a = "a";
+    std::string b = "b";
+    std::string ab = "ab";
+    ASSERT_TRUE(cm::String(a) + b == ab);
+    ASSERT_TRUE(ab == a + cm::String(b));
+    std::string c = "c";
+    std::string abc = "abc";
+    ASSERT_TRUE(a + cm::String(b) + c == abc);
+    ASSERT_TRUE(abc == a + cm::String(b) + c);
+    std::string d = "d";
+    std::string abcd = "abcd";
+    ASSERT_TRUE(a + (cm::String(b) + c) + d == abcd);
+    ASSERT_TRUE(abcd == a + (cm::String(b) + c) + d);
+  }
+  {
+    cm::string_view a("a", 1);
+    cm::string_view b("b", 1);
+    cm::string_view ab("ab", 2);
+    ASSERT_TRUE(cm::String(a) + b == ab);
+    ASSERT_TRUE(ab == a + cm::String(b));
+    cm::string_view c("c", 1);
+    cm::string_view abc("abc", 3);
+    ASSERT_TRUE(a + cm::String(b) + c == abc);
+    ASSERT_TRUE(abc == a + cm::String(b) + c);
+    cm::string_view d("d", 1);
+    cm::string_view abcd("abcd", 4);
+    ASSERT_TRUE(a + (cm::String(b) + c) + d == abcd);
+    ASSERT_TRUE(abcd == a + (cm::String(b) + c) + d);
+  }
+  {
+    cm::String a = "a";
+    cm::String b = "b";
+    cm::String ab = "ab";
+    ASSERT_TRUE(a + b == ab);
+    ASSERT_TRUE(ab == a + b);
+    cm::String c = "c";
+    cm::String abc = "abc";
+    ASSERT_TRUE(a + cm::String(b) + c == abc);
+    ASSERT_TRUE(abc == a + cm::String(b) + c);
+    cm::String d = "d";
+    cm::String abcd = "abcd";
+    ASSERT_TRUE(a + (cm::String(b) + c) + d == abcd);
+    ASSERT_TRUE(abcd == a + (cm::String(b) + c) + d);
+  }
+  {
+    cm::String str;
+    str += "a" + cm::String("b") + 'c';
+    ASSERT_TRUE(str == "abc");
+  }
+  {
+    std::string s;
+    s += "a" + cm::String("b") + 'c';
+    ASSERT_TRUE(s == "abc");
+  }
+  {
+    std::ostringstream ss;
+    ss << ("a" + cm::String("b") + 'c');
+    ASSERT_TRUE(ss.str() == "abc");
+  }
+  return true;
+}
+
 int testString(int /*unused*/, char* /*unused*/ [])
 {
   if (!testConstructDefault()) {
@@ -1106,5 +1204,8 @@ int testString(int /*unused*/, char* /*unused*/ [])
   if (!testMethod_find_last_not_of()) {
     return 1;
   }
+  if (!testAddition()) {
+    return 1;
+  }
   return 0;
 }

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=ff69763ca08fbd3a9831ec0c8adb32a7111b1a46
commit ff69763ca08fbd3a9831ec0c8adb32a7111b1a46
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Tue Sep 25 19:43:41 2018 -0400
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Wed Dec 12 08:10:15 2018 -0500

    String: Add a custom string type
    
    Create a `cm::String` type that holds a view of a string buffer and
    optionally shares ownership of the buffer.  Instances can either
    borrow longer-lived storage (e.g. static storage of string literals)
    or internally own a `std::string` instance.  In the latter case,
    share ownership with copies and substrings.  Allocate a new internal
    string only on operations that require mutation.
    
    This will allow us to recover string sharing semantics that we
    used to get from C++98 std::string copy-on-write implementations.
    Such implementations are not allowed by C++11 so code our own in
    a custom string type instead.

diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 4bf2e73..0ce09d4 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -568,6 +568,8 @@ set(SRCS
   cmSiteNameCommand.h
   cmSourceGroupCommand.cxx
   cmSourceGroupCommand.h
+  cmString.cxx
+  cmString.hxx
   cmStringReplaceHelper.cxx
   cmStringCommand.cxx
   cmStringCommand.h
diff --git a/Source/cmString.cxx b/Source/cmString.cxx
new file mode 100644
index 0000000..e965bfb
--- /dev/null
+++ b/Source/cmString.cxx
@@ -0,0 +1,131 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#define _SCL_SECURE_NO_WARNINGS
+
+#include "cmString.hxx"
+
+#include <memory>
+#include <ostream>
+#include <stdexcept>
+#include <string>
+#include <type_traits>
+
+namespace cm {
+
+static std::string const empty_string_;
+
+void String::internally_mutate_to_stable_string()
+{
+  // We assume that only one thread mutates this instance at
+  // a time even if we point to a shared string buffer refernced
+  // by other threads.
+  *this = String(data(), size());
+}
+
+std::string const& String::str()
+{
+  if (!data()) {
+    // We view no string.
+    // This is stable for the lifetime of our current value.
+    return empty_string_;
+  }
+
+  if (string_ && data() == string_->data() && size() == string_->size()) {
+    // We view an entire string.
+    // This is stable for the lifetime of our current value.
+    return *string_;
+  }
+
+  // Mutate to hold a std::string that is stable for the lifetime
+  // of our current value.
+  this->internally_mutate_to_stable_string();
+  return *string_;
+}
+
+const char* String::c_str()
+{
+  const char* c = data();
+  if (c == nullptr) {
+    return c;
+  }
+
+  // We always point into a null-terminated string so it is safe to
+  // access one past the end.  If it is a null byte then we can use
+  // the pointer directly.
+  if (c[size()] == '\0') {
+    return c;
+  }
+
+  // Mutate to hold a std::string so we can get a null terminator.
+  this->internally_mutate_to_stable_string();
+  c = string_->c_str();
+  return c;
+}
+
+String& String::insert(size_type index, size_type count, char ch)
+{
+  std::string s;
+  s.reserve(size() + count);
+  s.assign(data(), size());
+  s.insert(index, count, ch);
+  return *this = std::move(s);
+}
+
+String& String::erase(size_type index, size_type count)
+{
+  if (index > size()) {
+    throw std::out_of_range("Index out of range in String::erase");
+  }
+  size_type const rcount = std::min(count, size() - index);
+  size_type const rindex = index + rcount;
+  std::string s;
+  s.reserve(size() - rcount);
+  s.assign(data(), index);
+  s.append(data() + rindex, size() - rindex);
+  return *this = std::move(s);
+}
+
+String String::substr(size_type pos, size_type count) const
+{
+  if (pos > size()) {
+    throw std::out_of_range("Index out of range in String::substr");
+  }
+  return String(*this, pos, count);
+}
+
+String::String(std::string&& s, Private)
+  : string_(std::make_shared<std::string>(std::move(s)))
+  , view_(string_->data(), string_->size())
+{
+}
+
+String::size_type String::copy(char* dest, size_type count,
+                               size_type pos) const
+{
+  return view_.copy(dest, count, pos);
+}
+
+std::ostream& operator<<(std::ostream& os, String const& s)
+{
+  return os.write(s.data(), s.size());
+}
+
+std::string& operator+=(std::string& self, String const& s)
+{
+  return self += s.view();
+}
+
+String IntoString<char*>::into_string(const char* s)
+{
+  if (!s) {
+    return String();
+  }
+  return std::string(s);
+}
+
+string_view AsStringView<String>::view(String const& s)
+{
+  return s.view();
+}
+
+} // namespace cm
diff --git a/Source/cmString.hxx b/Source/cmString.hxx
new file mode 100644
index 0000000..833234c
--- /dev/null
+++ b/Source/cmString.hxx
@@ -0,0 +1,683 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmString_hxx
+#define cmString_hxx
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include "cm_string_view.hxx"
+
+#include <algorithm>
+#include <functional>
+#include <initializer_list>
+#include <memory>
+#include <ostream>
+#include <string>
+#include <type_traits>
+
+namespace cm {
+
+class String;
+
+/**
+ * Trait to convert type T into a String.
+ * Implementations must derive from 'std::true_type'
+ * and define an 'into_string' member that accepts
+ * type T (by value or reference) and returns one of:
+ *
+ * - 'std::string' to construct an owned instance.
+ * - 'cm::string_view' to construct a borrowed or null instances.
+ *   The buffer from which the view is borrowed must outlive
+ *   all copies of the resulting String, e.g. static storage.
+ * - 'cm::String' for already-constructed instances.
+ */
+template <typename T>
+struct IntoString : std::false_type
+{
+};
+
+template <typename T>
+struct IntoString<T&> : IntoString<T>
+{
+};
+
+template <typename T>
+struct IntoString<T const> : IntoString<T>
+{
+};
+
+template <typename T>
+struct IntoString<T const*> : IntoString<T*>
+{
+};
+
+template <typename T, std::string::size_type N>
+struct IntoString<T const[N]> : IntoString<T[N]>
+{
+};
+
+template <>
+struct IntoString<char*> : std::true_type
+{
+  static String into_string(const char* s);
+};
+
+template <>
+struct IntoString<std::nullptr_t> : std::true_type
+{
+  static string_view into_string(std::nullptr_t) { return string_view(); }
+};
+
+template <std::string::size_type N>
+struct IntoString<char[N]> : std::true_type
+{
+  static std::string into_string(char const (&s)[N])
+  {
+    return std::string(s, N - 1);
+  }
+};
+
+template <>
+struct IntoString<std::string> : std::true_type
+{
+  static std::string into_string(std::string s) { return s; }
+};
+
+template <>
+struct IntoString<string_view> : std::true_type
+{
+  static std::string into_string(string_view s) { return std::string(s); }
+};
+
+template <>
+struct IntoString<char> : std::true_type
+{
+  static std::string into_string(char const& c) { return std::string(1, c); }
+};
+
+/**
+ * Trait to convert type T into a 'cm::string_view'.
+ * Implementations must derive from 'std::true_type' and
+ * define a 'view' member that accepts type T (by reference)
+ * and returns a 'cm::string_view'.
+ */
+template <typename T>
+struct AsStringView : std::false_type
+{
+};
+
+template <typename T>
+struct AsStringView<T&> : AsStringView<T>
+{
+};
+
+template <typename T>
+struct AsStringView<T const> : AsStringView<T>
+{
+};
+
+template <typename T>
+struct AsStringView<T const*> : AsStringView<T*>
+{
+};
+
+template <typename T, std::string::size_type N>
+struct AsStringView<T const[N]> : AsStringView<T[N]>
+{
+};
+
+template <>
+struct AsStringView<char*> : std::true_type
+{
+  static string_view view(const char* s) { return s; }
+};
+
+template <std::string::size_type N>
+struct AsStringView<char[N]> : std::true_type
+{
+  static string_view view(char const (&s)[N]) { return string_view(s, N - 1); }
+};
+
+template <>
+struct AsStringView<std::string> : std::true_type
+{
+  static string_view view(std::string const& s) { return s; }
+};
+
+template <>
+struct AsStringView<char> : std::true_type
+{
+  static string_view view(const char& s) { return string_view(&s, 1); }
+};
+
+template <>
+struct AsStringView<string_view> : std::true_type
+{
+  static string_view view(string_view const& s) { return s; }
+};
+
+template <>
+struct AsStringView<String> : std::true_type
+{
+  static string_view view(String const& s);
+};
+
+/**
+ * \class String
+ *
+ * A custom string type that holds a view of a string buffer
+ * and optionally shares ownership of the buffer.  Instances
+ * may have one of the following states:
+ *
+ * - null: views and owns nothing.
+ *   Conversion to 'bool' is 'false'.
+ *   'data()' and 'c_str()' return nullptr.
+ *   'size()' returns 0.
+ *   'str()' returns an empty string.
+ *
+ * - borrowed: views a string but does not own it.  This is used
+ *   to bind to static storage (e.g. string literals) or for
+ *   temporary instances that do not outlive the borrowed buffer.
+ *   Copies and substrings still borrow the original buffer.
+ *   Mutation allocates a new internal string and converts to
+ *   the 'owned' state.
+ *   Conversion to 'bool' is 'true'.
+ *   'c_str()' may internally mutate to the 'owned' state.
+ *   'str()' internally mutates to the 'owned' state.
+ *
+ * - owned: views an immutable 'std::string' instance owned internally.
+ *   Copies and substrings share ownership of the internal string.
+ *   Mutation allocates a new internal string.
+ *   Conversion to 'bool' is 'true'.
+ */
+class String
+{
+  enum class Private
+  {
+  };
+
+public:
+  using traits_type = std::string::traits_type;
+  using value_type = string_view::value_type;
+  using pointer = string_view::pointer;
+  using const_pointer = string_view::const_pointer;
+  using reference = string_view::reference;
+  using const_reference = string_view::const_reference;
+  using const_iterator = string_view::const_iterator;
+  using iterator = string_view::const_iterator;
+  using const_reverse_iterator = string_view::const_reverse_iterator;
+  using reverse_iterator = string_view::const_reverse_iterator;
+  using difference_type = string_view::difference_type;
+  using size_type = string_view::size_type;
+
+  static size_type const npos = string_view::npos;
+
+  /** Construct a null string.  */
+  String() = default;
+
+  /** Construct from any type implementing the IntoString trait.  */
+  template <typename T,
+            typename = typename std::enable_if<IntoString<T>::value>::type>
+  String(T&& s)
+    : String(IntoString<T>::into_string(std::forward<T>(s)), Private())
+  {
+  }
+
+  /** Construct via std::string initializer list constructor.  */
+  String(std::initializer_list<char> il)
+    : String(std::string(il))
+  {
+  }
+
+  /** Construct by copying the specified buffer.  */
+  String(const char* d, size_type s)
+    : String(std::string(d, s))
+  {
+  }
+
+  /** Construct by copying from input iterator range.  */
+  template <typename InputIterator>
+  String(InputIterator first, InputIterator last)
+    : String(std::string(first, last))
+  {
+  }
+
+  /** Construct a string with 'n' copies of character 'c'.  */
+  String(size_type n, char c)
+    : String(std::string(n, c))
+  {
+  }
+
+  /** Construct from a substring of another String instance.
+      This shares ownership of the other string's buffer
+      but views only a substring.  */
+  String(String const& s, size_type pos, size_type count = npos)
+    : string_(s.string_)
+    , view_(s.data() + pos, std::min(count, s.size() - pos))
+  {
+  }
+
+  /** Construct by moving from another String instance.
+      The other instance is left as a null string.  */
+  String(String&& s) noexcept
+    : string_(std::move(s.string_))
+    , view_(s.view_)
+  {
+    s.view_ = string_view();
+  }
+
+  /** Construct by copying from another String instance.
+      This shares ownership of the other string's buffer.  */
+  String(String const&) noexcept = default;
+
+  ~String() = default;
+
+  /** Assign by moving from another String instance.
+      The other instance is left as a null string.  */
+  String& operator=(String&& s) noexcept
+  {
+    string_ = std::move(s.string_);
+    view_ = s.view_;
+    s.view_ = string_view();
+    return *this;
+  }
+
+  /** Assign by copying from another String instance.
+      This shares ownership of the other string's buffer.  */
+  String& operator=(String const&) noexcept = default;
+
+  /** Assign from any type implementing the IntoString trait.  */
+  template <typename T>
+  typename // NOLINT(*)
+    std::enable_if<IntoString<T>::value, String&>::type
+    operator=(T&& s)
+  {
+    *this = String(std::forward<T>(s));
+    return *this;
+  }
+
+  /** Assign via std::string initializer list constructor.  */
+  String& operator=(std::initializer_list<char> il)
+  {
+    *this = String(il);
+    return *this;
+  }
+
+  /** Return true if the instance is not a null string.  */
+  explicit operator bool() const noexcept { return data() != nullptr; }
+
+  /** Return a view of the string.  */
+  string_view view() const noexcept { return view_; }
+
+  /** Return true if the instance is an empty stringn or null string.  */
+  bool empty() const noexcept { return view_.empty(); }
+
+  /** Return a pointer to the start of the string.  */
+  const char* data() const noexcept { return view_.data(); }
+
+  /** Return the length of the string in bytes.  */
+  size_type size() const noexcept { return view_.size(); }
+  size_type length() const noexcept { return view_.length(); }
+
+  /** Return the character at the given position.
+      No bounds checking is performed.  */
+  char operator[](size_type pos) const noexcept { return view_[pos]; }
+
+  /** Return the character at the given position.
+      If the position is out of bounds, throws std::out_of_range.  */
+  char at(size_type pos) const { return view_.at(pos); }
+
+  char front() const noexcept { return view_.front(); }
+
+  char back() const noexcept { return view_.back(); }
+
+  /** Get a refernce to a normal std::string.  The reference
+      is valid until this instance is mutated or destroyed.  */
+  std::string const& str();
+
+  /** Get a pointer to a C-style null-terminated string
+      containing the same value as this instance.  The pointer
+      is valid until this instance is mutated, destroyed,
+      or str() is called.  */
+  const char* c_str();
+
+  const_iterator begin() const noexcept { return view_.begin(); }
+  const_iterator end() const noexcept { return view_.end(); }
+  const_iterator cbegin() const noexcept { return begin(); }
+  const_iterator cend() const noexcept { return end(); }
+
+  const_reverse_iterator rbegin() const noexcept { return view_.rbegin(); }
+  const_reverse_iterator rend() const noexcept { return view_.rend(); }
+  const_reverse_iterator crbegin() const noexcept { return rbegin(); }
+  const_reverse_iterator crend() const noexcept { return rend(); }
+
+  /** Append to the string using any type that implements the
+      AsStringView trait.  */
+  template <typename T>
+  typename std::enable_if<AsStringView<T>::value, String&>::type operator+=(
+    T&& s)
+  {
+    string_view v = AsStringView<T>::view(std::forward<T>(s));
+    std::string r;
+    r.reserve(size() + v.size());
+    r.assign(data(), size());
+    r.append(v.data(), v.size());
+    return *this = std::move(r);
+  }
+
+  /** Assign to an empty string.  */
+  void clear() { *this = String(string_view("", 0), Private()); }
+
+  /** Insert 'count' copies of 'ch' at position 'index'.  */
+  String& insert(size_type index, size_type count, char ch);
+
+  /** Erase 'count' characters starting at position 'index'.  */
+  String& erase(size_type index = 0, size_type count = npos);
+
+  void push_back(char ch)
+  {
+    std::string s;
+    s.reserve(size() + 1);
+    s.assign(data(), size());
+    s.push_back(ch);
+    *this = std::move(s);
+  }
+
+  void pop_back() { *this = String(*this, 0, size() - 1); }
+
+  template <typename T>
+  typename std::enable_if<AsStringView<T>::value, String&>::type replace(
+    size_type pos, size_type count, T&& s)
+  {
+    const_iterator first = begin() + pos;
+    const_iterator last = first + count;
+    return replace(first, last, std::forward<T>(s));
+  }
+
+  template <typename InputIterator>
+  String& replace(const_iterator first, const_iterator last,
+                  InputIterator first2, InputIterator last2)
+  {
+    std::string out;
+    out.append(view_.begin(), first);
+    out.append(first2, last2);
+    out.append(last, view_.end());
+    return *this = std::move(out);
+  }
+
+  template <typename T>
+  typename std::enable_if<AsStringView<T>::value, String&>::type replace(
+    const_iterator first, const_iterator last, T&& s)
+  {
+    string_view v = AsStringView<T>::view(std::forward<T>(s));
+    std::string out;
+    out.reserve((first - view_.begin()) + v.size() + (view_.end() - last));
+    out.append(view_.begin(), first);
+    out.append(v.data(), v.size());
+    out.append(last, view_.end());
+    return *this = std::move(out);
+  }
+
+  template <typename T>
+  typename std::enable_if<AsStringView<T>::value, String&>::type replace(
+    size_type pos, size_type count, T&& s, size_type pos2,
+    size_type count2 = npos)
+  {
+    string_view v = AsStringView<T>::view(std::forward<T>(s));
+    v = v.substr(pos2, count2);
+    return replace(pos, count, v);
+  }
+
+  String& replace(size_type pos, size_type count, size_type count2, char ch)
+  {
+    const_iterator first = begin() + pos;
+    const_iterator last = first + count;
+    return replace(first, last, count2, ch);
+  }
+
+  String& replace(const_iterator first, const_iterator last, size_type count2,
+                  char ch)
+  {
+    std::string out;
+    out.reserve((first - view_.begin()) + count2 + (view_.end() - last));
+    out.append(view_.begin(), first);
+    out.append(count2, ch);
+    out.append(last, view_.end());
+    return *this = std::move(out);
+  }
+
+  size_type copy(char* dest, size_type count, size_type pos = 0) const;
+
+  void resize(size_type count) { resize(count, char()); }
+
+  void resize(size_type count, char ch)
+  {
+    std::string s;
+    s.reserve(count);
+    if (count <= size()) {
+      s.assign(data(), count);
+    } else {
+      s.assign(data(), size());
+      s.resize(count, ch);
+    }
+    *this = std::move(s);
+  }
+
+  void swap(String& other)
+  {
+    std::swap(string_, other.string_);
+    std::swap(view_, other.view_);
+  }
+
+  /** Return a substring starting at position 'pos' and
+      consisting of at most 'count' characters.  */
+  String substr(size_type pos = 0, size_type count = npos) const;
+
+  template <typename T>
+  typename std::enable_if<AsStringView<T>::value, int>::type compare(
+    T&& s) const
+  {
+    return view_.compare(AsStringView<T>::view(std::forward<T>(s)));
+  }
+
+  int compare(size_type pos1, size_type count1, string_view v) const
+  {
+    return view_.compare(pos1, count1, v);
+  }
+
+  int compare(size_type pos1, size_type count1, string_view v, size_type pos2,
+              size_type count2) const
+  {
+    return view_.compare(pos1, count1, v, pos2, count2);
+  }
+
+  int compare(size_type pos1, size_type count1, const char* s) const
+  {
+    return view_.compare(pos1, count1, s);
+  }
+
+  int compare(size_type pos1, size_type count1, const char* s,
+              size_type count2) const
+  {
+    return view_.compare(pos1, count1, s, count2);
+  }
+
+  template <typename T>
+  typename std::enable_if<AsStringView<T>::value, size_type>::type find(
+    T&& s, size_type pos = 0) const
+  {
+    string_view v = AsStringView<T>::view(std::forward<T>(s));
+    return view_.find(v, pos);
+  }
+
+  size_type find(const char* s, size_type pos, size_type count) const
+  {
+    return view_.find(s, pos, count);
+  }
+
+  template <typename T>
+  typename std::enable_if<AsStringView<T>::value, size_type>::type rfind(
+    T&& s, size_type pos = npos) const
+  {
+    string_view v = AsStringView<T>::view(std::forward<T>(s));
+    return view_.rfind(v, pos);
+  }
+
+  size_type rfind(const char* s, size_type pos, size_type count) const
+  {
+    return view_.rfind(s, pos, count);
+  }
+
+  template <typename T>
+  typename std::enable_if<AsStringView<T>::value, size_type>::type
+  find_first_of(T&& s, size_type pos = 0) const
+  {
+    string_view v = AsStringView<T>::view(std::forward<T>(s));
+    return view_.find_first_of(v, pos);
+  }
+
+  size_type find_first_of(const char* s, size_type pos, size_type count) const
+  {
+    return view_.find_first_of(s, pos, count);
+  }
+
+  template <typename T>
+  typename std::enable_if<AsStringView<T>::value, size_type>::type
+  find_first_not_of(T&& s, size_type pos = 0) const
+  {
+    string_view v = AsStringView<T>::view(std::forward<T>(s));
+    return view_.find_first_not_of(v, pos);
+  }
+
+  size_type find_first_not_of(const char* s, size_type pos,
+                              size_type count) const
+  {
+    return view_.find_first_not_of(s, pos, count);
+  }
+
+  template <typename T>
+  typename std::enable_if<AsStringView<T>::value, size_type>::type
+  find_last_of(T&& s, size_type pos = npos) const
+  {
+    string_view v = AsStringView<T>::view(std::forward<T>(s));
+    return view_.find_last_of(v, pos);
+  }
+
+  size_type find_last_of(const char* s, size_type pos, size_type count) const
+  {
+    return view_.find_last_of(s, pos, count);
+  }
+
+  template <typename T>
+  typename std::enable_if<AsStringView<T>::value, size_type>::type
+  find_last_not_of(T&& s, size_type pos = npos) const
+  {
+    string_view v = AsStringView<T>::view(std::forward<T>(s));
+    return view_.find_last_not_of(v, pos);
+  }
+
+  size_type find_last_not_of(const char* s, size_type pos,
+                             size_type count) const
+  {
+    return view_.find_last_not_of(s, pos, count);
+  }
+
+private:
+  // Internal constructor to move from existing String.
+  String(String&& s, Private) noexcept
+    : String(std::move(s))
+  {
+  }
+
+  // Internal constructor for dynamically allocated string.
+  String(std::string&& s, Private);
+
+  // Internal constructor for view of statically allocated string.
+  String(string_view v, Private)
+    : string_()
+    , view_(v)
+  {
+  }
+
+  void internally_mutate_to_stable_string();
+
+  std::shared_ptr<std::string const> string_;
+  string_view view_;
+};
+
+template <typename L, typename R>
+typename std::enable_if<AsStringView<L>::value && AsStringView<R>::value,
+                        bool>::type
+operator==(L&& l, R&& r)
+{
+  return (AsStringView<L>::view(std::forward<L>(l)) ==
+          AsStringView<R>::view(std::forward<R>(r)));
+}
+
+template <typename L, typename R>
+typename std::enable_if<AsStringView<L>::value && AsStringView<R>::value,
+                        bool>::type
+operator!=(L&& l, R&& r)
+{
+  return (AsStringView<L>::view(std::forward<L>(l)) !=
+          AsStringView<R>::view(std::forward<R>(r)));
+}
+
+template <typename L, typename R>
+typename std::enable_if<AsStringView<L>::value && AsStringView<R>::value,
+                        bool>::type
+operator<(L&& l, R&& r)
+{
+  return (AsStringView<L>::view(std::forward<L>(l)) <
+          AsStringView<R>::view(std::forward<R>(r)));
+}
+
+template <typename L, typename R>
+typename std::enable_if<AsStringView<L>::value && AsStringView<R>::value,
+                        bool>::type
+operator<=(L&& l, R&& r)
+{
+  return (AsStringView<L>::view(std::forward<L>(l)) <=
+          AsStringView<R>::view(std::forward<R>(r)));
+}
+
+template <typename L, typename R>
+typename std::enable_if<AsStringView<L>::value && AsStringView<R>::value,
+                        bool>::type
+operator>(L&& l, R&& r)
+{
+  return (AsStringView<L>::view(std::forward<L>(l)) >
+          AsStringView<R>::view(std::forward<R>(r)));
+}
+
+template <typename L, typename R>
+typename std::enable_if<AsStringView<L>::value && AsStringView<R>::value,
+                        bool>::type
+operator>=(L&& l, R&& r)
+{
+  return (AsStringView<L>::view(std::forward<L>(l)) >=
+          AsStringView<R>::view(std::forward<R>(r)));
+}
+
+std::ostream& operator<<(std::ostream& os, String const& s);
+std::string& operator+=(std::string& self, String const& s);
+
+} // namespace cm
+
+namespace std {
+
+template <>
+struct hash<cm::String>
+{
+  typedef cm::String argument_type;
+  typedef size_t result_type;
+
+  result_type operator()(argument_type const& s) const noexcept
+  {
+    result_type const h(std::hash<cm::string_view>{}(s.view()));
+    return h;
+  }
+};
+}
+
+#endif
diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt
index 126076d..f6a9153 100644
--- a/Tests/CMakeLib/CMakeLists.txt
+++ b/Tests/CMakeLib/CMakeLists.txt
@@ -7,6 +7,7 @@ include_directories(
 set(CMakeLib_TESTS
   testGeneratedFileStream.cxx
   testRST.cxx
+  testString.cxx
   testSystemTools.cxx
   testUTF8.cxx
   testXMLParser.cxx
diff --git a/Tests/CMakeLib/testString.cxx b/Tests/CMakeLib/testString.cxx
new file mode 100644
index 0000000..9fe2e39
--- /dev/null
+++ b/Tests/CMakeLib/testString.cxx
@@ -0,0 +1,1110 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmString.hxx"
+
+#include "cm_string_view.hxx"
+
+#include <cstring>
+#include <iostream>
+#include <iterator>
+#include <sstream>
+#include <stdexcept>
+#include <string>
+#include <utility>
+
+#define ASSERT_TRUE(x)                                                        \
+  if (!(x)) {                                                                 \
+    std::cout << "ASSERT_TRUE(" #x ") failed on line " << __LINE__ << "\n";   \
+    return false;                                                             \
+  }
+
+static bool testConstructDefault()
+{
+  std::cout << "testConstructDefault()\n";
+  cm::String str;
+  cm::String const& str_const = str;
+  ASSERT_TRUE(bool(str_const) == false);
+  ASSERT_TRUE(str_const.data() == nullptr);
+  ASSERT_TRUE(str_const.size() == 0);
+  ASSERT_TRUE(str_const.empty());
+  ASSERT_TRUE(str.c_str() == nullptr);
+  ASSERT_TRUE(str.str().empty());
+  return true;
+}
+
+static bool testFromNullPtr(cm::String str)
+{
+  cm::String const& str_const = str;
+  ASSERT_TRUE(bool(str_const) == false);
+  ASSERT_TRUE(str_const.data() == nullptr);
+  ASSERT_TRUE(str_const.size() == 0);
+  ASSERT_TRUE(str_const.empty());
+  ASSERT_TRUE(str.c_str() == nullptr);
+  ASSERT_TRUE(str.str().empty());
+  return true;
+}
+
+static bool testConstructFromNullPtr()
+{
+  std::cout << "testConstructFromNullPtr()\n";
+  return testFromNullPtr(nullptr);
+}
+
+static bool testAssignFromNullPtr()
+{
+  std::cout << "testAssignFromNullPtr()\n";
+  cm::String str;
+  str = nullptr;
+  return testFromNullPtr(str);
+}
+
+static bool testFromCStrNull(cm::String str)
+{
+  cm::String const& str_const = str;
+  ASSERT_TRUE(bool(str_const) == false);
+  ASSERT_TRUE(str_const.data() == nullptr);
+  ASSERT_TRUE(str_const.size() == 0);
+  ASSERT_TRUE(str_const.empty());
+  ASSERT_TRUE(str.c_str() == nullptr);
+  ASSERT_TRUE(str.str().empty());
+  return true;
+}
+
+static bool testConstructFromCStrNull()
+{
+  std::cout << "testConstructFromCStrNull()\n";
+  const char* null = nullptr;
+  return testFromCStrNull(null);
+}
+
+static bool testAssignFromCStrNull()
+{
+  std::cout << "testAssignFromCStrNull()\n";
+  const char* null = nullptr;
+  cm::String str;
+  str = null;
+  return testFromCStrNull(str);
+}
+
+static char const charArray[] = "abc";
+
+static bool testFromCharArray(cm::String str)
+{
+  cm::String const& str_const = str;
+  ASSERT_TRUE(str_const.data() != charArray);
+  ASSERT_TRUE(str_const.size() == sizeof(charArray) - 1);
+  ASSERT_TRUE(str.c_str() != charArray);
+  cm::String substr = str.substr(1);
+  cm::String const& substr_const = substr;
+  ASSERT_TRUE(substr_const.data() != &charArray[1]);
+  ASSERT_TRUE(substr_const.size() == 2);
+  ASSERT_TRUE(substr.c_str() != &charArray[1]);
+  return true;
+}
+
+static bool testConstructFromCharArray()
+{
+  std::cout << "testConstructFromCharArray()\n";
+  return testFromCharArray(charArray);
+}
+
+static bool testAssignFromCharArray()
+{
+  std::cout << "testAssignFromCharArray()\n";
+  cm::String str;
+  str = charArray;
+  return testFromCharArray(str);
+}
+
+static const char* cstr = "abc";
+
+static bool testFromCStr(cm::String const& str)
+{
+  ASSERT_TRUE(str.data() != cstr);
+  ASSERT_TRUE(str.size() == 3);
+  ASSERT_TRUE(std::strncmp(str.data(), cstr, 3) == 0);
+  return true;
+}
+
+static bool testConstructFromCStr()
+{
+  std::cout << "testConstructFromCStr()\n";
+  return testFromCStr(cstr);
+  ;
+}
+
+static bool testAssignFromCStr()
+{
+  std::cout << "testAssignFromCStr()\n";
+  cm::String str;
+  str = cstr;
+  return testFromCStr(str);
+  ;
+}
+
+static const std::string stdstr = "abc";
+
+static bool testFromStdString(cm::String const& str)
+{
+#if defined(_GLIBCXX_USE_CXX11_ABI) && _GLIBCXX_USE_CXX11_ABI
+  // It would be nice to check this everywhere, but several platforms
+  // still use a CoW implementation even in C++11.
+  ASSERT_TRUE(str.data() != stdstr.data());
+#endif
+  ASSERT_TRUE(str.size() == 3);
+  ASSERT_TRUE(std::strncmp(str.data(), stdstr.data(), 3) == 0);
+  return true;
+}
+
+static bool testConstructFromStdString()
+{
+  std::cout << "testConstructFromStdString()\n";
+  return testFromStdString(stdstr);
+}
+
+static bool testAssignFromStdString()
+{
+  std::cout << "testAssignFromStdString()\n";
+  cm::String str;
+  str = stdstr;
+  return testFromStdString(str);
+}
+
+static bool testConstructFromView()
+{
+  std::cout << "testConstructFromView()\n";
+  cm::string_view view = cstr;
+  return testFromCStr(view);
+}
+
+static bool testAssignFromView()
+{
+  std::cout << "testAssignFromView()\n";
+  cm::string_view view = cstr;
+  cm::String str;
+  str = view;
+  return testFromCStr(str);
+}
+
+static bool testFromChar(cm::String const& str)
+{
+  ASSERT_TRUE(str.size() == 1);
+  ASSERT_TRUE(std::strncmp(str.data(), "a", 1) == 0);
+  return true;
+}
+
+static bool testConstructFromChar()
+{
+  std::cout << "testConstructFromChar()\n";
+  return testFromChar('a');
+}
+
+static bool testAssignFromChar()
+{
+  std::cout << "testAssignFromChar()\n";
+  cm::String str;
+  str = 'a';
+  return testFromChar(str);
+}
+
+static bool testConstructFromInitList()
+{
+  std::cout << "testConstructFromInitList()\n";
+  cm::String const str{ 'a', 'b', 'c' };
+  ASSERT_TRUE(str.size() == 3);
+  ASSERT_TRUE(std::strncmp(str.data(), "abc", 3) == 0);
+  return true;
+}
+
+static bool testAssignFromInitList()
+{
+  std::cout << "testAssignFromInitList()\n";
+  cm::String str;
+  str = { 'a', 'b', 'c' };
+  ASSERT_TRUE(str.size() == 3);
+  ASSERT_TRUE(std::strncmp(str.data(), "abc", 3) == 0);
+  return true;
+}
+
+static bool testConstructFromBuffer()
+{
+  std::cout << "testConstructFromBuffer()\n";
+  cm::String const str(cstr, 3);
+  return testFromCStr(str);
+}
+
+static bool testConstructFromInputIterator()
+{
+  std::cout << "testConstructFromInputIterator()\n";
+  cm::String const str(cstr, cstr + 3);
+  ASSERT_TRUE(str.data() != cstr);
+  ASSERT_TRUE(str.size() == 3);
+  ASSERT_TRUE(std::strncmp(str.data(), cstr, 3) == 0);
+  return true;
+}
+
+static bool testConstructFromN()
+{
+  std::cout << "testConstructFromN()\n";
+  cm::String const str(3, 'a');
+  ASSERT_TRUE(str.size() == 3);
+  ASSERT_TRUE(std::strncmp(str.data(), "aaa", 3) == 0);
+  return true;
+}
+
+static bool testConstructCopy()
+{
+  std::cout << "testConstructCopy()\n";
+  cm::String s1 = std::string("abc");
+  cm::String s2 = s1;
+  ASSERT_TRUE(s1.data() == s2.data());
+  ASSERT_TRUE(s1.size() == 3);
+  ASSERT_TRUE(s2.size() == 3);
+  ASSERT_TRUE(std::strncmp(s2.data(), "abc", 3) == 0);
+  return true;
+}
+
+static bool testConstructMove()
+{
+  std::cout << "testConstructMove()\n";
+  cm::String s1 = std::string("abc");
+  cm::String s2 = std::move(s1);
+  ASSERT_TRUE(s1.data() == nullptr);
+  ASSERT_TRUE(s1.size() == 0);
+  ASSERT_TRUE(s2.size() == 3);
+  ASSERT_TRUE(std::strncmp(s2.data(), "abc", 3) == 0);
+  return true;
+}
+
+static bool testAssignCopy()
+{
+  std::cout << "testAssignCopy()\n";
+  cm::String s1 = std::string("abc");
+  cm::String s2;
+  s2 = s1;
+  ASSERT_TRUE(s1.data() == s2.data());
+  ASSERT_TRUE(s1.size() == 3);
+  ASSERT_TRUE(s2.size() == 3);
+  ASSERT_TRUE(std::strncmp(s2.data(), "abc", 3) == 0);
+  return true;
+}
+
+static bool testAssignMove()
+{
+  std::cout << "testAssignMove()\n";
+  cm::String s1 = std::string("abc");
+  cm::String s2;
+  s2 = std::move(s1);
+  ASSERT_TRUE(s1.data() == nullptr);
+  ASSERT_TRUE(s1.size() == 0);
+  ASSERT_TRUE(s2.size() == 3);
+  ASSERT_TRUE(std::strncmp(s2.data(), "abc", 3) == 0);
+  return true;
+}
+
+static bool testOperatorBool()
+{
+  std::cout << "testOperatorBool()\n";
+  cm::String str;
+  ASSERT_TRUE(!str);
+  str = "";
+  ASSERT_TRUE(str);
+  str = static_cast<const char*>(nullptr);
+  ASSERT_TRUE(!str);
+  str = std::string();
+  ASSERT_TRUE(str);
+  str = nullptr;
+  ASSERT_TRUE(!str);
+  return true;
+}
+
+static bool testOperatorIndex()
+{
+  std::cout << "testOperatorIndex()\n";
+  cm::String str = "abc";
+  ASSERT_TRUE(str[0] == 'a');
+  ASSERT_TRUE(str[1] == 'b');
+  ASSERT_TRUE(str[2] == 'c');
+  return true;
+}
+
+static bool testOperatorPlusEqual()
+{
+  std::cout << "testOperatorPlusEqual()\n";
+  cm::String str = "a";
+  str += "b";
+  {
+    const char* c = "c";
+    str += c;
+  }
+  str += 'd';
+  str += std::string("e");
+  str += cm::string_view("f", 1);
+  str += cm::String("g");
+  ASSERT_TRUE(str.size() == 7);
+  ASSERT_TRUE(std::strncmp(str.data(), "abcdefg", 7) == 0);
+  return true;
+}
+
+static bool testOperatorCompare()
+{
+  std::cout << "testOperatorCompare()\n";
+  cm::String str = "b";
+  {
+    ASSERT_TRUE(str == "b");
+    ASSERT_TRUE("b" == str);
+    ASSERT_TRUE(str != "a");
+    ASSERT_TRUE("a" != str);
+    ASSERT_TRUE(str < "c");
+    ASSERT_TRUE("a" < str);
+    ASSERT_TRUE(str > "a");
+    ASSERT_TRUE("c" > str);
+    ASSERT_TRUE(str <= "b");
+    ASSERT_TRUE("b" <= str);
+    ASSERT_TRUE(str >= "b");
+    ASSERT_TRUE("b" >= str);
+  }
+  {
+    const char* a = "a";
+    const char* b = "b";
+    const char* c = "c";
+    ASSERT_TRUE(str == b);
+    ASSERT_TRUE(b == str);
+    ASSERT_TRUE(str != a);
+    ASSERT_TRUE(a != str);
+    ASSERT_TRUE(str < c);
+    ASSERT_TRUE(a < str);
+    ASSERT_TRUE(str > a);
+    ASSERT_TRUE(c > str);
+    ASSERT_TRUE(str <= b);
+    ASSERT_TRUE(b <= str);
+    ASSERT_TRUE(str >= b);
+    ASSERT_TRUE(b >= str);
+  }
+  {
+    ASSERT_TRUE(str == 'b');
+    ASSERT_TRUE('b' == str);
+    ASSERT_TRUE(str != 'a');
+    ASSERT_TRUE('a' != str);
+    ASSERT_TRUE(str < 'c');
+    ASSERT_TRUE('a' < str);
+    ASSERT_TRUE(str > 'a');
+    ASSERT_TRUE('c' > str);
+    ASSERT_TRUE(str <= 'b');
+    ASSERT_TRUE('b' <= str);
+    ASSERT_TRUE(str >= 'b');
+    ASSERT_TRUE('b' >= str);
+  }
+  {
+    std::string const a = "a";
+    std::string const b = "b";
+    std::string const c = "c";
+    ASSERT_TRUE(str == b);
+    ASSERT_TRUE(b == str);
+    ASSERT_TRUE(str != a);
+    ASSERT_TRUE(a != str);
+    ASSERT_TRUE(str < c);
+    ASSERT_TRUE(a < str);
+    ASSERT_TRUE(str > a);
+    ASSERT_TRUE(c > str);
+    ASSERT_TRUE(str <= b);
+    ASSERT_TRUE(b <= str);
+    ASSERT_TRUE(str >= b);
+    ASSERT_TRUE(b >= str);
+  }
+  {
+    cm::string_view const a("a", 1);
+    cm::string_view const b("b", 1);
+    cm::string_view const c("c", 1);
+    ASSERT_TRUE(str == b);
+    ASSERT_TRUE(b == str);
+    ASSERT_TRUE(str != a);
+    ASSERT_TRUE(a != str);
+    ASSERT_TRUE(str < c);
+    ASSERT_TRUE(a < str);
+    ASSERT_TRUE(str > a);
+    ASSERT_TRUE(c > str);
+    ASSERT_TRUE(str <= b);
+    ASSERT_TRUE(b <= str);
+    ASSERT_TRUE(str >= b);
+    ASSERT_TRUE(b >= str);
+  }
+  {
+    cm::String const a("a");
+    cm::String const b("b");
+    cm::String const c("c");
+    ASSERT_TRUE(str == b);
+    ASSERT_TRUE(b == str);
+    ASSERT_TRUE(str != a);
+    ASSERT_TRUE(a != str);
+    ASSERT_TRUE(str < c);
+    ASSERT_TRUE(a < str);
+    ASSERT_TRUE(str > a);
+    ASSERT_TRUE(c > str);
+    ASSERT_TRUE(str <= b);
+    ASSERT_TRUE(b <= str);
+    ASSERT_TRUE(str >= b);
+    ASSERT_TRUE(b >= str);
+  }
+  return true;
+}
+
+static bool testOperatorStream()
+{
+  std::cout << "testOperatorStream()\n";
+  std::ostringstream ss;
+  ss << "a" << cm::String("b") << 'c';
+  ASSERT_TRUE(ss.str() == "abc");
+  return true;
+}
+
+static bool testOperatorStdStringPlusEqual()
+{
+  std::cout << "testOperatorStdStringPlusEqual()\n";
+  std::string s = "a";
+  s += cm::String("b");
+  ASSERT_TRUE(s == "ab");
+  return true;
+}
+
+static bool testMethod_view()
+{
+  std::cout << "testMethod_view()\n";
+  cm::String str;
+  ASSERT_TRUE(str.view().data() == nullptr);
+  ASSERT_TRUE(str.view().size() == 0);
+  str = charArray;
+  ASSERT_TRUE(str.view().data() != charArray);
+  ASSERT_TRUE(str.view().size() == sizeof(charArray) - 1);
+  str = std::string("abc");
+  ASSERT_TRUE(str.view().size() == 3);
+  ASSERT_TRUE(strncmp(str.view().data(), "abc", 3) == 0);
+  return true;
+}
+
+static bool testMethod_empty()
+{
+  std::cout << "testMethod_empty()\n";
+  cm::String str;
+  ASSERT_TRUE(str.empty());
+  str = "";
+  ASSERT_TRUE(str.empty());
+  str = "abc";
+  ASSERT_TRUE(!str.empty());
+  str = std::string();
+  ASSERT_TRUE(str.empty());
+  str = std::string("abc");
+  ASSERT_TRUE(!str.empty());
+  return true;
+}
+
+static bool testMethod_length()
+{
+  std::cout << "testMethod_length()\n";
+  cm::String str;
+  ASSERT_TRUE(str.length() == 0);
+  str = "";
+  ASSERT_TRUE(str.length() == 0);
+  str = "abc";
+  ASSERT_TRUE(str.length() == 3);
+  str = std::string();
+  ASSERT_TRUE(str.length() == 0);
+  str = std::string("abc");
+  ASSERT_TRUE(str.length() == 3);
+  return true;
+}
+
+static bool testMethod_at()
+{
+  std::cout << "testMethod_at()\n";
+  cm::String str = "abc";
+  ASSERT_TRUE(str.at(0) == 'a');
+  ASSERT_TRUE(str.at(1) == 'b');
+  ASSERT_TRUE(str.at(2) == 'c');
+  bool except_out_of_range = false;
+  try {
+    str.at(3);
+  } catch (std::out_of_range&) {
+    except_out_of_range = true;
+  }
+  ASSERT_TRUE(except_out_of_range);
+  return true;
+}
+
+static bool testMethod_front_back()
+{
+  std::cout << "testMethod_front_back()\n";
+  cm::String str = "abc";
+  ASSERT_TRUE(str.front() == 'a');
+  ASSERT_TRUE(str.back() == 'c');
+  return true;
+}
+
+static bool testMethodIterators()
+{
+  std::cout << "testMethodIterators()\n";
+  cm::String str = "abc";
+  ASSERT_TRUE(*str.begin() == 'a');
+  ASSERT_TRUE(*(str.end() - 1) == 'c');
+  ASSERT_TRUE(str.end() - str.begin() == 3);
+  ASSERT_TRUE(*str.cbegin() == 'a');
+  ASSERT_TRUE(*(str.cend() - 1) == 'c');
+  ASSERT_TRUE(str.cend() - str.cbegin() == 3);
+  ASSERT_TRUE(*str.rbegin() == 'c');
+  ASSERT_TRUE(*(str.rend() - 1) == 'a');
+  ASSERT_TRUE(str.rend() - str.rbegin() == 3);
+  ASSERT_TRUE(*str.crbegin() == 'c');
+  ASSERT_TRUE(*(str.crend() - 1) == 'a');
+  ASSERT_TRUE(str.crend() - str.crbegin() == 3);
+  return true;
+}
+
+static bool testMethod_clear()
+{
+  std::cout << "testMethod_clear()\n";
+  cm::String str = "abc";
+  ASSERT_TRUE(!str.empty());
+  str.clear();
+  ASSERT_TRUE(str.empty());
+  return true;
+}
+
+static bool testMethod_insert()
+{
+  std::cout << "testMethod_insert()\n";
+  cm::String str = "abc";
+  str.insert(1, 2, 'd').insert(0, 1, '_');
+  ASSERT_TRUE(str.size() == 6);
+  ASSERT_TRUE(std::strncmp(str.data(), "_addbc", 6) == 0);
+  return true;
+}
+
+static bool testMethod_erase()
+{
+  std::cout << "testMethod_erase()\n";
+  cm::String str = "abcdefg";
+  str.erase(5).erase(1, 2);
+  ASSERT_TRUE(str.size() == 3);
+  ASSERT_TRUE(std::strncmp(str.data(), "ade", 3) == 0);
+  return true;
+}
+
+static bool testMethod_push_back()
+{
+  std::cout << "testMethod_push_back()\n";
+  cm::String str = "abc";
+  str.push_back('d');
+  ASSERT_TRUE(str == "abcd");
+  return true;
+}
+
+static bool testMethod_pop_back()
+{
+  std::cout << "testMethod_pop_back()\n";
+  cm::String str = "abc";
+  str.pop_back();
+  ASSERT_TRUE(str == "ab");
+  return true;
+}
+
+static bool testMethod_replace()
+{
+  std::cout << "testMethod_replace()\n";
+  {
+    cm::String str = "abcd";
+    const char* bc = "bc";
+    ASSERT_TRUE(str.replace(1, 2, "BC") == "aBCd");
+    ASSERT_TRUE(str.replace(1, 2, bc) == "abcd");
+    ASSERT_TRUE(str.replace(1, 2, 'x') == "axd");
+    ASSERT_TRUE(str.replace(1, 1, std::string("bc")) == "abcd");
+    ASSERT_TRUE(str.replace(1, 2, cm::string_view("BC", 2)) == "aBCd");
+    ASSERT_TRUE(str.replace(1, 2, cm::String("bc")) == "abcd");
+  }
+  {
+    cm::String str = "abcd";
+    const char* bc = "bc";
+    ASSERT_TRUE(str.replace(str.begin() + 1, str.begin() + 3, "BC") == "aBCd");
+    ASSERT_TRUE(str.replace(str.begin() + 1, str.begin() + 3, bc) == "abcd");
+    ASSERT_TRUE(str.replace(str.begin() + 1, str.begin() + 3, 'x') == "axd");
+    ASSERT_TRUE(str.replace(str.begin() + 1, str.begin() + 2,
+                            std::string("bc")) == "abcd");
+    ASSERT_TRUE(str.replace(str.begin() + 1, str.begin() + 3,
+                            cm::string_view("BC", 2)) == "aBCd");
+    ASSERT_TRUE(str.replace(str.begin() + 1, str.begin() + 3,
+                            cm::String("bc")) == "abcd");
+  }
+  {
+    cm::String str = "abcd";
+    const char* bc = "_bc";
+    ASSERT_TRUE(str.replace(1, 2, "_BC_", 1, 2) == "aBCd");
+    ASSERT_TRUE(str.replace(1, 2, bc, 1) == "abcd");
+    ASSERT_TRUE(str.replace(1, 2, 'x', 0) == "axd");
+    ASSERT_TRUE(str.replace(1, 1, std::string("_bc_"), 1, 2) == "abcd");
+    ASSERT_TRUE(str.replace(1, 2, cm::string_view("_BC", 3), 1) == "aBCd");
+    ASSERT_TRUE(str.replace(1, 2, cm::String("_bc_"), 1, 2) == "abcd");
+  }
+  {
+    cm::String str = "abcd";
+    const char* bc = "_bc_";
+    ASSERT_TRUE(str.replace(1, 2, 2, 'x') == "axxd");
+    ASSERT_TRUE(str.replace(str.begin() + 1, str.begin() + 3, 2, 'y') ==
+                "ayyd");
+    ASSERT_TRUE(
+      str.replace(str.begin() + 1, str.begin() + 3, bc + 1, bc + 3) == "abcd");
+  }
+  return true;
+}
+
+static bool testMethod_copy()
+{
+  std::cout << "testMethod_copy()\n";
+  cm::String str = "abc";
+  char dest[2];
+  cm::String::size_type n = str.copy(dest, 2, 1);
+  ASSERT_TRUE(n == 2);
+  ASSERT_TRUE(std::strncmp(dest, "bc", 2) == 0);
+  n = str.copy(dest, 2);
+  ASSERT_TRUE(n == 2);
+  ASSERT_TRUE(std::strncmp(dest, "ab", 2) == 0);
+  return true;
+}
+
+static bool testMethod_resize()
+{
+  std::cout << "testMethod_resize()\n";
+  cm::String str = "abc";
+  str.resize(3);
+  ASSERT_TRUE(str == "abc");
+  str.resize(2);
+  ASSERT_TRUE(str == "ab");
+  str.resize(3, 'c');
+  ASSERT_TRUE(str == "abc");
+  return true;
+}
+
+static bool testMethod_swap()
+{
+  std::cout << "testMethod_swap()\n";
+  cm::String str1 = std::string("1");
+  cm::String str2 = std::string("2");
+  str1.swap(str2);
+  ASSERT_TRUE(str1 == "2");
+  ASSERT_TRUE(str2 == "1");
+  return true;
+}
+
+static bool testMethod_substr_AtEnd(cm::String str)
+{
+  cm::String substr = str.substr(1);
+  ASSERT_TRUE(substr.data() == str.data() + 1);
+  ASSERT_TRUE(substr.size() == 2);
+
+  // c_str() at the end of the buffer does not internally mutate.
+  ASSERT_TRUE(std::strcmp(substr.c_str(), "bc") == 0);
+  ASSERT_TRUE(substr.c_str() == str.data() + 1);
+  ASSERT_TRUE(substr.data() == str.data() + 1);
+  ASSERT_TRUE(substr.size() == 2);
+
+  // str() internally mutates.
+  ASSERT_TRUE(substr.str() == "bc");
+  ASSERT_TRUE(substr.data() != str.data() + 1);
+  ASSERT_TRUE(substr.size() == 2);
+  ASSERT_TRUE(substr.c_str() != str.data() + 1);
+  ASSERT_TRUE(std::strcmp(substr.c_str(), "bc") == 0);
+  return true;
+}
+
+static bool testMethod_substr_AtEndOwned()
+{
+  std::cout << "testMethod_substr_AtEndOwned()\n";
+  return testMethod_substr_AtEnd(std::string("abc"));
+}
+
+static bool testMethod_substr_AtStart(cm::String str)
+{
+  {
+    cm::String substr = str.substr(0, 2);
+    ASSERT_TRUE(substr.data() == str.data());
+    ASSERT_TRUE(substr.size() == 2);
+
+    // c_str() not at the end of the buffer internally mutates.
+    const char* substr_c = substr.c_str();
+    ASSERT_TRUE(std::strcmp(substr_c, "ab") == 0);
+    ASSERT_TRUE(substr_c != str.data());
+    ASSERT_TRUE(substr.data() != str.data());
+    ASSERT_TRUE(substr.size() == 2);
+
+    // str() does not need to internally mutate after c_str() did so
+    ASSERT_TRUE(substr.str() == "ab");
+    ASSERT_TRUE(substr.data() == substr_c);
+    ASSERT_TRUE(substr.size() == 2);
+    ASSERT_TRUE(substr.c_str() == substr_c);
+  }
+
+  {
+    cm::String substr = str.substr(0, 2);
+    ASSERT_TRUE(substr.data() == str.data());
+    ASSERT_TRUE(substr.size() == 2);
+
+    // str() internally mutates.
+    ASSERT_TRUE(substr.str() == "ab");
+    ASSERT_TRUE(substr.data() != str.data());
+    ASSERT_TRUE(substr.size() == 2);
+    ASSERT_TRUE(substr.c_str() != str.data());
+
+    // c_str() does not internally after str() did so
+    const char* substr_c = substr.c_str();
+    ASSERT_TRUE(std::strcmp(substr_c, "ab") == 0);
+    ASSERT_TRUE(substr_c == substr.data());
+    ASSERT_TRUE(substr.size() == 2);
+  }
+
+  return true;
+}
+
+static bool testMethod_substr_AtStartOwned()
+{
+  std::cout << "testMethod_substr_AtStartOwned()\n";
+  return testMethod_substr_AtStart(std::string("abc"));
+}
+
+static bool testMethod_compare()
+{
+  std::cout << "testMethod_compare()\n";
+  cm::String str = "b";
+  ASSERT_TRUE(str.compare("a") > 0);
+  ASSERT_TRUE(str.compare("b") == 0);
+  ASSERT_TRUE(str.compare("c") < 0);
+  {
+    const char* a = "a";
+    const char* b = "b";
+    const char* c = "c";
+    ASSERT_TRUE(str.compare(a) > 0);
+    ASSERT_TRUE(str.compare(b) == 0);
+    ASSERT_TRUE(str.compare(c) < 0);
+  }
+  ASSERT_TRUE(str.compare('a') > 0);
+  ASSERT_TRUE(str.compare('b') == 0);
+  ASSERT_TRUE(str.compare('c') < 0);
+  ASSERT_TRUE(str.compare(std::string("a")) > 0);
+  ASSERT_TRUE(str.compare(std::string("b")) == 0);
+  ASSERT_TRUE(str.compare(std::string("c")) < 0);
+  ASSERT_TRUE(str.compare(cm::string_view("a_", 1)) > 0);
+  ASSERT_TRUE(str.compare(cm::string_view("b_", 1)) == 0);
+  ASSERT_TRUE(str.compare(cm::string_view("c_", 1)) < 0);
+  ASSERT_TRUE(str.compare(cm::String("a")) > 0);
+  ASSERT_TRUE(str.compare(cm::String("b")) == 0);
+  ASSERT_TRUE(str.compare(cm::String("c")) < 0);
+  ASSERT_TRUE(str.compare(0, 1, cm::string_view("a", 1)) > 0);
+  ASSERT_TRUE(str.compare(1, 0, cm::string_view("", 0)) == 0);
+  ASSERT_TRUE(str.compare(0, 1, cm::string_view("ac", 2), 1, 1) < 0);
+  ASSERT_TRUE(str.compare(1, 0, "") == 0);
+  ASSERT_TRUE(str.compare(1, 0, "_", 0) == 0);
+  return true;
+}
+
+static bool testMethod_find()
+{
+  std::cout << "testMethod_find()\n";
+  cm::String str = "abcabc";
+  ASSERT_TRUE(str.find("a") == 0);
+  ASSERT_TRUE(str.find("a", 1) == 3);
+  {
+    const char* a = "a";
+    ASSERT_TRUE(str.find(a) == 0);
+    ASSERT_TRUE(str.find(a, 1) == 3);
+  }
+  ASSERT_TRUE(str.find('a') == 0);
+  ASSERT_TRUE(str.find('a', 1) == 3);
+  ASSERT_TRUE(str.find(std::string("a")) == 0);
+  ASSERT_TRUE(str.find(std::string("a"), 1) == 3);
+  ASSERT_TRUE(str.find(cm::string_view("a_", 1)) == 0);
+  ASSERT_TRUE(str.find(cm::string_view("a_", 1), 1) == 3);
+  ASSERT_TRUE(str.find(cm::String("a")) == 0);
+  ASSERT_TRUE(str.find(cm::String("a"), 1) == 3);
+  ASSERT_TRUE(str.find("ab_", 1, 2) == 3);
+  return true;
+}
+
+static bool testMethod_rfind()
+{
+  std::cout << "testMethod_rfind()\n";
+  cm::String str = "abcabc";
+  ASSERT_TRUE(str.rfind("a") == 3);
+  ASSERT_TRUE(str.rfind("a", 1) == 0);
+  {
+    const char* a = "a";
+    ASSERT_TRUE(str.rfind(a) == 3);
+    ASSERT_TRUE(str.rfind(a, 1) == 0);
+  }
+  ASSERT_TRUE(str.rfind('a') == 3);
+  ASSERT_TRUE(str.rfind('a', 1) == 0);
+  ASSERT_TRUE(str.rfind(std::string("a")) == 3);
+  ASSERT_TRUE(str.rfind(std::string("a"), 1) == 0);
+  ASSERT_TRUE(str.rfind(cm::string_view("a_", 1)) == 3);
+  ASSERT_TRUE(str.rfind(cm::string_view("a_", 1), 1) == 0);
+  ASSERT_TRUE(str.rfind(cm::String("a")) == 3);
+  ASSERT_TRUE(str.rfind(cm::String("a"), 1) == 0);
+  ASSERT_TRUE(str.rfind("ab_", 1, 2) == 0);
+  return true;
+}
+
+static bool testMethod_find_first_of()
+{
+  std::cout << "testMethod_find_first_of()\n";
+  cm::String str = "abcabc";
+  ASSERT_TRUE(str.find_first_of("_a") == 0);
+  ASSERT_TRUE(str.find_first_of("_a", 1) == 3);
+  {
+    const char* a = "_a";
+    ASSERT_TRUE(str.find_first_of(a) == 0);
+    ASSERT_TRUE(str.find_first_of(a, 1) == 3);
+  }
+  ASSERT_TRUE(str.find_first_of('a') == 0);
+  ASSERT_TRUE(str.find_first_of('a', 1) == 3);
+  ASSERT_TRUE(str.find_first_of(std::string("_a")) == 0);
+  ASSERT_TRUE(str.find_first_of(std::string("_a"), 1) == 3);
+  ASSERT_TRUE(str.find_first_of(cm::string_view("ba_", 1)) == 1);
+  ASSERT_TRUE(str.find_first_of(cm::string_view("ba_", 1), 2) == 4);
+  ASSERT_TRUE(str.find_first_of(cm::String("ab")) == 0);
+  ASSERT_TRUE(str.find_first_of(cm::String("ab"), 2) == 3);
+  ASSERT_TRUE(str.find_first_of("_ab", 1, 2) == 3);
+  return true;
+}
+
+static bool testMethod_find_first_not_of()
+{
+  std::cout << "testMethod_find_first_not_of()\n";
+  cm::String str = "abcabc";
+  ASSERT_TRUE(str.find_first_not_of("_a") == 1);
+  ASSERT_TRUE(str.find_first_not_of("_a", 2) == 2);
+  {
+    const char* a = "_a";
+    ASSERT_TRUE(str.find_first_not_of(a) == 1);
+    ASSERT_TRUE(str.find_first_not_of(a, 2) == 2);
+  }
+  ASSERT_TRUE(str.find_first_not_of('a') == 1);
+  ASSERT_TRUE(str.find_first_not_of('a', 2) == 2);
+  ASSERT_TRUE(str.find_first_not_of(std::string("_a")) == 1);
+  ASSERT_TRUE(str.find_first_not_of(std::string("_a"), 2) == 2);
+  ASSERT_TRUE(str.find_first_not_of(cm::string_view("ba_", 1)) == 0);
+  ASSERT_TRUE(str.find_first_not_of(cm::string_view("ba_", 1), 1) == 2);
+  ASSERT_TRUE(str.find_first_not_of(cm::String("_a")) == 1);
+  ASSERT_TRUE(str.find_first_not_of(cm::String("_a"), 2) == 2);
+  ASSERT_TRUE(str.find_first_not_of("_bca", 1, 3) == 3);
+  return true;
+}
+
+static bool testMethod_find_last_of()
+{
+  std::cout << "testMethod_find_last_of()\n";
+  cm::String str = "abcabc";
+  ASSERT_TRUE(str.find_last_of("_a") == 3);
+  ASSERT_TRUE(str.find_last_of("_a", 1) == 0);
+  {
+    const char* a = "_a";
+    ASSERT_TRUE(str.find_last_of(a) == 3);
+    ASSERT_TRUE(str.find_last_of(a, 1) == 0);
+  }
+  ASSERT_TRUE(str.find_last_of('a') == 3);
+  ASSERT_TRUE(str.find_last_of('a', 1) == 0);
+  ASSERT_TRUE(str.find_last_of(std::string("_a")) == 3);
+  ASSERT_TRUE(str.find_last_of(std::string("_a"), 1) == 0);
+  ASSERT_TRUE(str.find_last_of(cm::string_view("ba_", 1)) == 4);
+  ASSERT_TRUE(str.find_last_of(cm::string_view("ba_", 1), 2) == 1);
+  ASSERT_TRUE(str.find_last_of(cm::String("ab")) == 4);
+  ASSERT_TRUE(str.find_last_of(cm::String("ab"), 2) == 1);
+  ASSERT_TRUE(str.find_last_of("_ab", 1, 2) == 0);
+  return true;
+}
+
+static bool testMethod_find_last_not_of()
+{
+  std::cout << "testMethod_find_last_not_of()\n";
+  cm::String str = "abcabc";
+  ASSERT_TRUE(str.find_last_not_of("_a") == 5);
+  ASSERT_TRUE(str.find_last_not_of("_a", 1) == 1);
+  {
+    const char* a = "_a";
+    ASSERT_TRUE(str.find_last_not_of(a) == 5);
+    ASSERT_TRUE(str.find_last_not_of(a, 1) == 1);
+  }
+  ASSERT_TRUE(str.find_last_not_of('a') == 5);
+  ASSERT_TRUE(str.find_last_not_of('a', 1) == 1);
+  ASSERT_TRUE(str.find_last_not_of(std::string("_a")) == 5);
+  ASSERT_TRUE(str.find_last_not_of(std::string("_a"), 1) == 1);
+  ASSERT_TRUE(str.find_last_not_of(cm::string_view("cb_", 1)) == 4);
+  ASSERT_TRUE(str.find_last_not_of(cm::string_view("cb_", 1), 2) == 1);
+  ASSERT_TRUE(str.find_last_not_of(cm::String("_a")) == 5);
+  ASSERT_TRUE(str.find_last_not_of(cm::String("_a"), 1) == 1);
+  ASSERT_TRUE(str.find_last_not_of("cb_", 2, 2) == 0);
+  return true;
+}
+
+int testString(int /*unused*/, char* /*unused*/ [])
+{
+  if (!testConstructDefault()) {
+    return 1;
+  }
+  if (!testConstructFromNullPtr()) {
+    return 1;
+  }
+  if (!testConstructFromCStrNull()) {
+    return 1;
+  }
+  if (!testConstructFromCharArray()) {
+    return 1;
+  }
+  if (!testConstructFromCStr()) {
+    return 1;
+  }
+  if (!testConstructFromStdString()) {
+    return 1;
+  }
+  if (!testConstructFromView()) {
+    return 1;
+  }
+  if (!testConstructFromChar()) {
+    return 1;
+  }
+  if (!testConstructFromInitList()) {
+    return 1;
+  }
+  if (!testConstructFromBuffer()) {
+    return 1;
+  }
+  if (!testConstructFromInputIterator()) {
+    return 1;
+  }
+  if (!testConstructFromN()) {
+    return 1;
+  }
+  if (!testConstructCopy()) {
+    return 1;
+  }
+  if (!testConstructMove()) {
+    return 1;
+  }
+  if (!testAssignCopy()) {
+    return 1;
+  }
+  if (!testAssignMove()) {
+    return 1;
+  }
+  if (!testAssignFromChar()) {
+    return 1;
+  }
+  if (!testAssignFromView()) {
+    return 1;
+  }
+  if (!testAssignFromStdString()) {
+    return 1;
+  }
+  if (!testAssignFromCStr()) {
+    return 1;
+  }
+  if (!testAssignFromCharArray()) {
+    return 1;
+  }
+  if (!testAssignFromCStrNull()) {
+    return 1;
+  }
+  if (!testAssignFromNullPtr()) {
+    return 1;
+  }
+  if (!testAssignFromInitList()) {
+    return 1;
+  }
+  if (!testOperatorBool()) {
+    return 1;
+  }
+  if (!testOperatorIndex()) {
+    return 1;
+  }
+  if (!testOperatorPlusEqual()) {
+    return 1;
+  }
+  if (!testOperatorCompare()) {
+    return 1;
+  }
+  if (!testOperatorStream()) {
+    return 1;
+  }
+  if (!testOperatorStdStringPlusEqual()) {
+    return 1;
+  }
+  if (!testMethod_view()) {
+    return 1;
+  }
+  if (!testMethod_empty()) {
+    return 1;
+  }
+  if (!testMethod_length()) {
+    return 1;
+  }
+  if (!testMethod_at()) {
+    return 1;
+  }
+  if (!testMethod_front_back()) {
+    return 1;
+  }
+  if (!testMethod_clear()) {
+    return 1;
+  }
+  if (!testMethod_insert()) {
+    return 1;
+  }
+  if (!testMethod_erase()) {
+    return 1;
+  }
+  if (!testMethod_push_back()) {
+    return 1;
+  }
+  if (!testMethod_pop_back()) {
+    return 1;
+  }
+  if (!testMethod_replace()) {
+    return 1;
+  }
+  if (!testMethod_copy()) {
+    return 1;
+  }
+  if (!testMethod_resize()) {
+    return 1;
+  }
+  if (!testMethod_swap()) {
+    return 1;
+  }
+  if (!testMethodIterators()) {
+    return 1;
+  }
+  if (!testMethod_substr_AtEndOwned()) {
+    return 1;
+  }
+  if (!testMethod_substr_AtStartOwned()) {
+    return 1;
+  }
+  if (!testMethod_compare()) {
+    return 1;
+  }
+  if (!testMethod_find()) {
+    return 1;
+  }
+  if (!testMethod_rfind()) {
+    return 1;
+  }
+  if (!testMethod_find_first_of()) {
+    return 1;
+  }
+  if (!testMethod_find_first_not_of()) {
+    return 1;
+  }
+  if (!testMethod_find_last_of()) {
+    return 1;
+  }
+  if (!testMethod_find_last_not_of()) {
+    return 1;
+  }
+  return 0;
+}

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=3e922ceb5e8cc4c4d72ddcbd8b485803c49d84f1
commit 3e922ceb5e8cc4c4d72ddcbd8b485803c49d84f1
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Thu Oct 18 09:39:25 2018 -0400
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Wed Dec 12 06:40:10 2018 -0500

    fileapi: add codemodel v2
    
    Start with v2 to distinguish it from server-mode v1.
    
    Issue: #18398

diff --git a/Help/manual/cmake-file-api.7.rst b/Help/manual/cmake-file-api.7.rst
index a1b4830..f87d7f0 100644
--- a/Help/manual/cmake-file-api.7.rst
+++ b/Help/manual/cmake-file-api.7.rst
@@ -399,3 +399,473 @@ The ``kind`` member is a string specifying the object kind name.
 The ``version`` member is a JSON object with ``major`` and ``minor``
 members specifying integer components of the object kind's version.
 Additional top-level members are specific to each object kind.
+
+Object Kind "codemodel"
+-----------------------
+
+The ``codemodel`` object kind describes the build system structure as
+modeled by CMake.
+
+There is only one ``codemodel`` object major version, version 2.
+Version 1 does not exist to avoid confusion with that from
+:manual:`cmake-server(7)` mode.
+
+"codemodel" version 2
+^^^^^^^^^^^^^^^^^^^^^
+
+``codemodel`` object version 2 is a JSON object:
+
+.. code-block:: json
+
+  {
+    "kind": "codemodel",
+    "version": { "major": 2, "minor": 0 },
+    "paths": {
+      "source": "/path/to/top-level-source-dir",
+      "build": "/path/to/top-level-build-dir"
+    },
+    "configurations": [
+      {
+        "name": "Debug",
+        "directories": [
+          {
+            "source": ".",
+            "build": ".",
+            "childIndexes": [ 1 ],
+            "targetIndexes": [ 0 ]
+          },
+          {
+            "source": "sub",
+            "build": "sub",
+            "parentIndex": 0,
+            "targetIndexes": [ 1 ]
+          }
+        ],
+        "targets": [
+          {
+            "name": "MyExecutable",
+            "directoryIndex": 0,
+            "jsonFile": "<file>"
+          },
+          {
+            "name": "MyLibrary",
+            "directoryIndex": 1,
+            "jsonFile": "<file>"
+          }
+        ]
+      }
+    ]
+  }
+
+The members specific to ``codemodel`` objects are:
+
+``paths``
+  A JSON object containing members:
+
+  ``source``
+    A string specifying the absolute path to the top-level source directory,
+    represented with forward slashes.
+
+  ``build``
+    A string specifying the absolute path to the top-level build directory,
+    represented with forward slashes.
+
+``configurations``
+  A JSON array of entries corresponding to available build configurations.
+  On single-configuration generators there is one entry for the value
+  of the :variable:`CMAKE_BUILD_TYPE` variable.  For multi-configuration
+  generators there is an entry for each configuration listed in the
+  :variable:`CMAKE_CONFIGURATION_TYPES` variable.
+  Each entry is a JSON object containing members:
+
+  ``name``
+    A string specifying the name of the configuration, e.g. ``Debug``.
+
+  ``directories``
+    A JSON array of entries each corresponding to a build system directory
+    whose source directory contains a ``CMakeLists.txt`` file.  The first
+    entry corresponds to the top-level directory.  Each entry is 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.
+
+    ``parentIndex``
+      Optional member that is present when the directory is not top-level.
+      The value is an unsigned integer 0-based index of another entry in
+      the main ``directories`` array that corresponds to the parent
+      directory that added this directory as a subdirectory.
+
+    ``childIndexes``
+      Optional member that is present when the directory has subdirectories.
+      The value is a JSON array of entries corresponding to child directories
+      created by the :command:`add_subdirectory` or :command:`subdirs`
+      command.  Each entry is an unsigned integer 0-based index of another
+      entry in the main ``directories`` array.
+
+    ``targetIndexes``
+      Optional member that is present when the directory itself has targets,
+      excluding those belonging to subdirectories.  The value is a JSON
+      array of entries corresponding to the targets.  Each entry is an
+      unsigned integer 0-based index into the main ``targets`` array.
+
+  ``targets``
+    A JSON array of entries corresponding to the build system targets.
+    Such targets are created by calls to :command:`add_executable`,
+    :command:`add_library`, and :command:`add_custom_target`, excluding
+    imported targets and interface libraries (which do not generate any
+    build rules).  Each entry is a JSON object containing members:
+
+    ``name``
+      A string specifying the target name.
+
+    ``id``
+      A string uniquely identifying the target.  This matches the ``id``
+      field in the file referenced by ``jsonFile``.
+
+    ``directoryIndex``
+      An unsigned integer 0-based index into the main ``directories`` array
+      indicating the build system directory in which the target is defined.
+
+    ``jsonFile``
+      A JSON string specifying a path relative to the codemodel file
+      to another JSON file containing a
+      `"codemodel" version 2 "target" object`_.
+
+"codemodel" version 2 "target" object
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+A codemodel "target" object is referenced by a `"codemodel" version 2`_
+object's ``targets`` array.  Each "target" object is a JSON object
+with members:
+
+``name``
+  A string specifying the logical name of the target.
+
+``id``
+  A string uniquely identifying the target.  The format is unspecified
+  and should not be interpreted by clients.
+
+``type``
+  A string specifying the type of the target.  The value is one of
+  ``EXECUTABLE``, ``STATIC_LIBRARY``, ``SHARED_LIBRARY``,
+  ``MODULE_LIBRARY``, ``OBJECT_LIBRARY``, or ``UTILITY``.
+
+``backtrace``
+  Optional member that is present when a CMake language backtrace to
+  the command in the source code that created the target is available.
+  The value is an unsigned integer 0-based index into the
+  ``backtraceGraph`` member's ``nodes`` array.
+
+``folder``
+  Optional member that is present when the :prop_tgt:`FOLDER` target
+  property is set.  The value is a JSON object with one member:
+
+  ``name``
+    A string specifying the name of the target folder.
+
+``paths``
+  A JSON object containing members:
+
+  ``source``
+    A string specifying the path to the target's 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 target's 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.
+
+``nameOnDisk``
+  Optional member that is present for executable and library targets
+  that are linked or archived into a single primary artifact.
+  The value is a string specifying the file name of that artifact on disk.
+
+``artifacts``
+  Optional member that is present for executable and library targets
+  that produce artifacts on disk meant for consumption by dependents.
+  The value is a JSON array of entries corresponding to the artifacts.
+  Each entry is a JSON object containing one member:
+
+  ``path``
+    A string specifying the path to the file on disk, represented with
+    forward slashes.  If the file is inside the top-level build directory
+    then the path is specified relative to that directory.
+    Otherwise the path is absolute.
+
+``isGeneratorProvided``
+  Optional member that is present with boolean value ``true`` if the
+  target is provided by CMake's build system generator rather than by
+  a command in the source code.
+
+``install``
+  Optional member that is present when the target has an :command:`install`
+  rule.  The value is a JSON object with members:
+
+  ``prefix``
+    A JSON object specifying the installation prefix.  It has one member:
+
+    ``path``
+      A string specifying the value of :variable:`CMAKE_INSTALL_PREFIX`.
+
+  ``destinations``
+    A JSON array of entries specifying an install destination path.
+    Each entry is a JSON object with members:
+
+    ``path``
+      A string specifying the install destination path.  The path may
+      be absolute or relative to the install prefix.
+
+    ``backtrace``
+      Optional member that is present when a CMake language backtrace to
+      the :command:`install` command invocation that specified this
+      destination is available.  The value is an unsigned integer 0-based
+      index into the ``backtraceGraph`` member's ``nodes`` array.
+
+``link``
+  Optional member that is present for executables and shared library
+  targets that link into a runtime binary.  The value is a JSON object
+  with members describing the link step:
+
+  ``language``
+    A string specifying the language (e.g. ``C``, ``CXX``, ``Fortran``)
+    of the toolchain is used to invoke the linker.
+
+  ``commandFragments``
+    Optional member that is present when fragments of the link command
+    line invocation are available.  The value is a JSON array of entries
+    specifying ordered fragments.  Each entry is a JSON object with members:
+
+    ``fragment``
+      A string specifying a fragment of the link command line invocation.
+      The value is encoded in the build system's native shell format.
+
+    ``role``
+      A string specifying the role of the fragment's content:
+
+      * ``flags``: link flags.
+      * ``libraries``: link library file paths or flags.
+      * ``libraryPath``: library search path flags.
+      * ``frameworkPath``: macOS framework search path flags.
+
+  ``lto``
+    Optional member that is present with boolean value ``true``
+    when link-time optimization (a.k.a. interprocedural optimization
+    or link-time code generation) is enabled.
+
+  ``sysroot``
+    Optional member that is present when the :variable:`CMAKE_SYSROOT_LINK`
+    or :variable:`CMAKE_SYSROOT` variable is defined.  The value is a
+    JSON object with one member:
+
+    ``path``
+      A string specifying the absolute path to the sysroot, represented
+      with forward slashes.
+
+``archive``
+  Optional member that is present for static library targets.  The value
+  is a JSON object with members describing the archive step:
+
+  ``commandFragments``
+    Optional member that is present when fragments of the archiver command
+    line invocation are available.  The value is a JSON array of entries
+    specifying the fragments.  Each entry is a JSON object with members:
+
+    ``fragment``
+      A string specifying a fragment of the archiver command line invocation.
+      The value is encoded in the build system's native shell format.
+
+    ``role``
+      A string specifying the role of the fragment's content:
+
+      * ``flags``: archiver flags.
+
+  ``lto``
+    Optional member that is present with boolean value ``true``
+    when link-time optimization (a.k.a. interprocedural optimization
+    or link-time code generation) is enabled.
+
+``dependencies``
+  Optional member that is present when the target depends on other targets.
+  The value is a JSON array of entries corresponding to the dependencies.
+  Each entry is a JSON object with members:
+
+  ``id``
+    A string uniquely identifying the target on which this target depends.
+    This matches the main ``id`` member of the other target.
+
+  ``backtrace``
+    Optional member that is present when a CMake language backtrace to
+    the :command:`add_dependencies`, :command:`target_link_libraries`,
+    or other command invocation that created this dependency is
+    available.  The value is an unsigned integer 0-based index into
+    the ``backtraceGraph`` member's ``nodes`` array.
+
+``sources``
+  A JSON array of entries corresponding to the target's source files.
+  Each entry is a JSON object with members:
+
+  ``path``
+    A string specifying the path to the source 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.
+
+  ``compileGroupIndex``
+    Optional member that is present when the source is compiled.
+    The value is an unsigned integer 0-based index into the
+    ``compileGroups`` array.
+
+  ``sourceGroupIndex``
+    Optional member that is present when the source is part of a source
+    group either via the :command:`source_group` command or by default.
+    The value is an unsigned integer 0-based index into the
+    ``sourceGroups`` array.
+
+  ``isGenerated``
+    Optional member that is present with boolean value ``true`` if
+    the source is :prop_sf:`GENERATED`.
+
+  ``backtrace``
+    Optional member that is present when a CMake language backtrace to
+    the :command:`target_sources`, :command:`add_executable`,
+    :command:`add_library`, :command:`add_custom_target`, or other
+    command invocation that added this source to the target is
+    available.  The value is an unsigned integer 0-based index into
+    the ``backtraceGraph`` member's ``nodes`` array.
+
+``sourceGroups``
+  Optional member that is present when sources are grouped together by
+  the :command:`source_group` command or by default.  The value is a
+  JSON array of entries corresponding to the groups.  Each entry is
+  a JSON object with members:
+
+  ``name``
+    A string specifying the name of the source group.
+
+  ``sourceIndexes``
+    A JSON array listing the sources belonging to the group.
+    Each entry is an unsigned integer 0-based index into the
+    main ``sources`` array for the target.
+
+``compileGroups``
+  Optional member that is present when the target has sources that compile.
+  The value is a JSON array of entries corresponding to groups of sources
+  that all compile with the same settings.  Each entry is a JSON object
+  with members:
+
+  ``sourceIndexes``
+    A JSON array listing the sources belonging to the group.
+    Each entry is an unsigned integer 0-based index into the
+    main ``sources`` array for the target.
+
+  ``language``
+    A string specifying the language (e.g. ``C``, ``CXX``, ``Fortran``)
+    of the toolchain is used to compile the source file.
+
+  ``compileCommandFragments``
+    Optional member that is present when fragments of the compiler command
+    line invocation are available.  The value is a JSON array of entries
+    specifying ordered fragments.  Each entry is a JSON object with
+    one member:
+
+    ``fragment``
+      A string specifying a fragment of the compile command line invocation.
+      The value is encoded in the build system's native shell format.
+
+  ``includes``
+    Optional member that is present when there are include directories.
+    The value is a JSON array with an entry for each directory.  Each
+    entry is a JSON object with members:
+
+    ``path``
+      A string specifying the path to the include directory,
+      represented with forward slashes.
+
+    ``isSystem``
+      Optional member that is present with boolean value ``true`` if
+      the include directory is marked as a system include directory.
+
+    ``backtrace``
+      Optional member that is present when a CMake language backtrace to
+      the :command:`target_include_directories` or other command invocation
+      that added this include directory is available.  The value is
+      an unsigned integer 0-based index into the ``backtraceGraph``
+      member's ``nodes`` array.
+
+  ``defines``
+    Optional member that is present when there are preprocessor definitions.
+    The value is a JSON array with an entry for each definition.  Each
+    entry is a JSON object with members:
+
+    ``define``
+      A string specifying the preprocessor definition in the format
+      ``<name>[=<value>]``, e.g. ``DEF`` or ``DEF=1``.
+
+    ``backtrace``
+      Optional member that is present when a CMake language backtrace to
+      the :command:`target_compile_definitions` or other command invocation
+      that added this preprocessor definition is available.  The value is
+      an unsigned integer 0-based index into the ``backtraceGraph``
+      member's ``nodes`` array.
+
+  ``sysroot``
+    Optional member that is present when the
+    :variable:`CMAKE_SYSROOT_COMPILE` or :variable:`CMAKE_SYSROOT`
+    variable is defined.  The value is a JSON object with one member:
+
+    ``path``
+      A string specifying the absolute path to the sysroot, represented
+      with forward slashes.
+
+``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.
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index ec71fe0..e672eab 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -209,6 +209,8 @@ set(SRCS
   cmExtraSublimeTextGenerator.h
   cmFileAPI.cxx
   cmFileAPI.h
+  cmFileAPICodemodel.cxx
+  cmFileAPICodemodel.h
   cmFileLock.cxx
   cmFileLock.h
   cmFileLockPool.cxx
diff --git a/Source/cmFileAPI.cxx b/Source/cmFileAPI.cxx
index 1a41370..b63349a 100644
--- a/Source/cmFileAPI.cxx
+++ b/Source/cmFileAPI.cxx
@@ -4,6 +4,7 @@
 
 #include "cmAlgorithms.h"
 #include "cmCryptoHash.h"
+#include "cmFileAPICodemodel.h"
 #include "cmGlobalGenerator.h"
 #include "cmSystemTools.h"
 #include "cmTimestamp.h"
@@ -224,6 +225,17 @@ bool cmFileAPI::ReadQuery(std::string const& query,
   }
   std::string kindName = query.substr(0, sep_pos);
   std::string verStr = query.substr(sep_pos + 1);
+  if (kindName == ObjectKindName(ObjectKind::CodeModel)) {
+    Object o;
+    o.Kind = ObjectKind::CodeModel;
+    if (verStr == "v2") {
+      o.Version = 2;
+    } else {
+      return false;
+    }
+    objects.push_back(o);
+    return true;
+  }
   if (kindName == ObjectKindName(ObjectKind::InternalTest)) {
     Object o;
     o.Kind = ObjectKind::InternalTest;
@@ -361,7 +373,8 @@ const char* cmFileAPI::ObjectKindName(ObjectKind kind)
 {
   // Keep in sync with ObjectKind enum.
   static const char* objectKindNames[] = {
-    "__test" //
+    "codemodel", //
+    "__test"     //
   };
   return objectKindNames[size_t(kind)];
 }
@@ -379,6 +392,9 @@ Json::Value cmFileAPI::BuildObject(Object const& object)
   Json::Value value;
 
   switch (object.Kind) {
+    case ObjectKind::CodeModel:
+      value = this->BuildCodeModel(object);
+      break;
     case ObjectKind::InternalTest:
       value = this->BuildInternalTest(object);
       break;
@@ -429,7 +445,9 @@ cmFileAPI::ClientRequest cmFileAPI::BuildClientRequest(
   }
   std::string const& kindName = kind.asString();
 
-  if (kindName == this->ObjectKindName(ObjectKind::InternalTest)) {
+  if (kindName == this->ObjectKindName(ObjectKind::CodeModel)) {
+    r.Kind = ObjectKind::CodeModel;
+  } else if (kindName == this->ObjectKindName(ObjectKind::InternalTest)) {
     r.Kind = ObjectKind::InternalTest;
   } else {
     r.Error = "unknown request kind '" + kindName + "'";
@@ -447,6 +465,9 @@ cmFileAPI::ClientRequest cmFileAPI::BuildClientRequest(
   }
 
   switch (r.Kind) {
+    case ObjectKind::CodeModel:
+      this->BuildClientRequestCodeModel(r, versions);
+      break;
     case ObjectKind::InternalTest:
       this->BuildClientRequestInternalTest(r, versions);
       break;
@@ -592,6 +613,42 @@ std::string cmFileAPI::NoSupportedVersion(
   return msg.str();
 }
 
+// The "codemodel" object kind.
+
+static unsigned int const CodeModelV2Minor = 0;
+
+void cmFileAPI::BuildClientRequestCodeModel(
+  ClientRequest& r, std::vector<RequestVersion> const& versions)
+{
+  // Select a known version from those requested.
+  for (RequestVersion const& v : versions) {
+    if ((v.Major == 2 && v.Minor <= CodeModelV2Minor)) {
+      r.Version = v.Major;
+      break;
+    }
+  }
+  if (!r.Version) {
+    r.Error = NoSupportedVersion(versions);
+  }
+}
+
+Json::Value cmFileAPI::BuildCodeModel(Object const& object)
+{
+  using namespace std::placeholders;
+  Json::Value codemodel = cmFileAPICodemodelDump(*this, object.Version);
+  codemodel["kind"] = this->ObjectKindName(object.Kind);
+
+  Json::Value& version = codemodel["version"] = Json::objectValue;
+  if (object.Version == 2) {
+    version["major"] = 2;
+    version["minor"] = CodeModelV2Minor;
+  } else {
+    return codemodel; // should be unreachable
+  }
+
+  return codemodel;
+}
+
 // The "__test" object kind is for internal testing of CMake.
 
 static unsigned int const InternalTestV1Minor = 3;
diff --git a/Source/cmFileAPI.h b/Source/cmFileAPI.h
index 8bfae31..5339ba7 100644
--- a/Source/cmFileAPI.h
+++ b/Source/cmFileAPI.h
@@ -51,6 +51,7 @@ private:
   // Keep in sync with ObjectKindName.
   enum class ObjectKind
   {
+    CodeModel,
     InternalTest
   };
 
@@ -181,6 +182,10 @@ private:
   static std::string NoSupportedVersion(
     std::vector<RequestVersion> const& versions);
 
+  void BuildClientRequestCodeModel(
+    ClientRequest& r, std::vector<RequestVersion> const& versions);
+  Json::Value BuildCodeModel(Object const& object);
+
   void BuildClientRequestInternalTest(
     ClientRequest& r, std::vector<RequestVersion> const& versions);
   Json::Value BuildInternalTest(Object const& object);
diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx
new file mode 100644
index 0000000..3d4a7cf
--- /dev/null
+++ b/Source/cmFileAPICodemodel.cxx
@@ -0,0 +1,1111 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmFileAPICodemodel.h"
+
+#include "cmCryptoHash.h"
+#include "cmFileAPI.h"
+#include "cmGeneratorExpression.h"
+#include "cmGeneratorTarget.h"
+#include "cmGlobalGenerator.h"
+#include "cmInstallGenerator.h"
+#include "cmInstallTargetGenerator.h"
+#include "cmLinkLineComputer.h"
+#include "cmListFileCache.h"
+#include "cmLocalGenerator.h"
+#include "cmMakefile.h"
+#include "cmSourceFile.h"
+#include "cmSourceGroup.h"
+#include "cmState.h"
+#include "cmStateDirectory.h"
+#include "cmStateSnapshot.h"
+#include "cmStateTypes.h"
+#include "cmSystemTools.h"
+#include "cmTarget.h"
+#include "cmTargetDepend.h"
+#include "cmake.h"
+
+#include "cm_jsoncpp_value.h"
+
+#include <algorithm>
+#include <cassert>
+#include <map>
+#include <set>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+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;
+    Json::Value TargetIndexes = Json::arrayValue;
+  };
+  std::map<cmStateSnapshot, Json::ArrayIndex, cmStateSnapshot::StrictWeakOrder>
+    DirectoryMap;
+  std::vector<Directory> Directories;
+
+  void ProcessDirectories();
+
+  Json::ArrayIndex GetDirectoryIndex(cmLocalGenerator const* lg);
+  Json::ArrayIndex GetDirectoryIndex(cmStateSnapshot s);
+
+  Json::Value DumpTargets();
+  Json::Value DumpTarget(cmGeneratorTarget* gt, Json::ArrayIndex ti);
+
+  Json::Value DumpDirectories();
+  Json::Value DumpDirectory(Directory& d);
+
+public:
+  CodemodelConfig(cmFileAPI& fileAPI, unsigned long version,
+                  std::string const& config);
+  Json::Value Dump();
+};
+
+std::string RelativeIfUnder(std::string const& top, std::string const& in)
+{
+  std::string out;
+  if (in == top) {
+    out = ".";
+  } else if (cmSystemTools::IsSubDirectory(in, top)) {
+    out = in.substr(top.size() + 1);
+  } else {
+    out = in;
+  }
+  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 BacktraceData
+{
+  std::string TopSource;
+  std::unordered_map<std::string, Json::ArrayIndex> CommandMap;
+  std::unordered_map<std::string, Json::ArrayIndex> FileMap;
+  std::unordered_map<cmListFileContext const*, Json::ArrayIndex> NodeMap;
+  Json::Value Commands = Json::arrayValue;
+  Json::Value Files = Json::arrayValue;
+  Json::Value Nodes = Json::arrayValue;
+
+  Json::ArrayIndex AddCommand(std::string const& command)
+  {
+    auto i = this->CommandMap.find(command);
+    if (i == this->CommandMap.end()) {
+      auto cmdIndex = static_cast<Json::ArrayIndex>(this->Commands.size());
+      i = this->CommandMap.emplace(command, cmdIndex).first;
+      this->Commands.append(command);
+    }
+    return i->second;
+  }
+
+  Json::ArrayIndex AddFile(std::string const& file)
+  {
+    auto i = this->FileMap.find(file);
+    if (i == this->FileMap.end()) {
+      auto fileIndex = static_cast<Json::ArrayIndex>(this->Files.size());
+      i = this->FileMap.emplace(file, fileIndex).first;
+      this->Files.append(RelativeIfUnder(this->TopSource, file));
+    }
+    return i->second;
+  }
+
+public:
+  BacktraceData(std::string const& topSource);
+  bool Add(cmListFileBacktrace const& bt, Json::ArrayIndex& index);
+  Json::Value Dump();
+};
+
+BacktraceData::BacktraceData(std::string const& topSource)
+  : TopSource(topSource)
+{
+}
+
+bool BacktraceData::Add(cmListFileBacktrace const& bt, Json::ArrayIndex& index)
+{
+  if (bt.Empty()) {
+    return false;
+  }
+  cmListFileContext const* top = &bt.Top();
+  auto found = this->NodeMap.find(top);
+  if (found != this->NodeMap.end()) {
+    index = found->second;
+    return true;
+  }
+  Json::Value entry = Json::objectValue;
+  entry["file"] = this->AddFile(top->FilePath);
+  if (top->Line) {
+    entry["line"] = static_cast<int>(top->Line);
+  }
+  if (!top->Name.empty()) {
+    entry["command"] = this->AddCommand(top->Name);
+  }
+  Json::ArrayIndex parent;
+  if (this->Add(bt.Pop(), parent)) {
+    entry["parent"] = parent;
+  }
+  index = this->NodeMap[top] = this->Nodes.size();
+  this->Nodes.append(std::move(entry)); // NOLINT(*)
+  return true;
+}
+
+Json::Value BacktraceData::Dump()
+{
+  Json::Value backtraceGraph;
+  this->CommandMap.clear();
+  this->FileMap.clear();
+  this->NodeMap.clear();
+  backtraceGraph["commands"] = std::move(this->Commands);
+  backtraceGraph["files"] = std::move(this->Files);
+  backtraceGraph["nodes"] = std::move(this->Nodes);
+  return backtraceGraph;
+}
+
+struct CompileData
+{
+  struct IncludeEntry
+  {
+    BT<std::string> Path;
+    bool IsSystem = false;
+    IncludeEntry(BT<std::string> path, bool isSystem)
+      : Path(std::move(path))
+      , IsSystem(isSystem)
+    {
+    }
+  };
+
+  void SetDefines(std::set<BT<std::string>> const& defines);
+
+  std::string Language;
+  std::string Sysroot;
+  std::vector<BT<std::string>> Flags;
+  std::vector<BT<std::string>> Defines;
+  std::vector<IncludeEntry> Includes;
+};
+
+void CompileData::SetDefines(std::set<BT<std::string>> const& defines)
+{
+  this->Defines.reserve(defines.size());
+  for (BT<std::string> const& d : defines) {
+    this->Defines.push_back(d);
+  }
+}
+
+class Target
+{
+  cmGeneratorTarget* GT;
+  std::string const& Config;
+  std::string TopSource;
+  std::string TopBuild;
+  std::vector<cmSourceGroup> SourceGroupsLocal;
+  BacktraceData Backtraces;
+
+  std::map<std::string, CompileData> CompileDataMap;
+
+  std::unordered_map<cmSourceFile const*, Json::ArrayIndex> SourceMap;
+  Json::Value Sources = Json::arrayValue;
+
+  struct SourceGroup
+  {
+    std::string Name;
+    Json::Value SourceIndexes = Json::arrayValue;
+  };
+  std::unordered_map<cmSourceGroup const*, Json::ArrayIndex> SourceGroupsMap;
+  std::vector<SourceGroup> SourceGroups;
+
+  struct CompileGroup
+  {
+    std::map<Json::Value, Json::ArrayIndex>::iterator Entry;
+    Json::Value SourceIndexes = Json::arrayValue;
+  };
+  std::map<Json::Value, Json::ArrayIndex> CompileGroupMap;
+  std::vector<CompileGroup> CompileGroups;
+
+  void ProcessLanguages();
+  void ProcessLanguage(std::string const& lang);
+
+  Json::ArrayIndex AddSourceGroup(cmSourceGroup* sg, Json::ArrayIndex si);
+  CompileData BuildCompileData(cmSourceFile* sf);
+  Json::ArrayIndex AddSourceCompileGroup(cmSourceFile* sf,
+                                         Json::ArrayIndex si);
+  void AddBacktrace(Json::Value& object, cmListFileBacktrace const& bt);
+  Json::Value DumpPaths();
+  Json::Value DumpCompileData(CompileData cd);
+  Json::Value DumpInclude(CompileData::IncludeEntry const& inc);
+  Json::Value DumpDefine(BT<std::string> const& def);
+  Json::Value DumpSources();
+  Json::Value DumpSource(cmGeneratorTarget::SourceAndKind const& sk,
+                         Json::ArrayIndex si);
+  Json::Value DumpSourceGroups();
+  Json::Value DumpSourceGroup(SourceGroup& sg);
+  Json::Value DumpCompileGroups();
+  Json::Value DumpCompileGroup(CompileGroup& cg);
+  Json::Value DumpSysroot(std::string const& path);
+  Json::Value DumpInstall();
+  Json::Value DumpInstallPrefix();
+  Json::Value DumpInstallDestinations();
+  Json::Value DumpInstallDestination(cmInstallTargetGenerator* itGen);
+  Json::Value DumpArtifacts();
+  Json::Value DumpLink();
+  Json::Value DumpArchive();
+  Json::Value DumpLinkCommandFragments();
+  Json::Value DumpCommandFragments(std::vector<BT<std::string>> const& frags);
+  Json::Value DumpCommandFragment(BT<std::string> const& frag,
+                                  std::string const& role = std::string());
+  Json::Value DumpDependencies();
+  Json::Value DumpDependency(cmTargetDepend const& td);
+  Json::Value DumpFolder();
+
+public:
+  Target(cmGeneratorTarget* gt, std::string const& config);
+  Json::Value Dump();
+};
+
+Codemodel::Codemodel(cmFileAPI& fileAPI, unsigned long version)
+  : FileAPI(fileAPI)
+  , Version(version)
+{
+}
+
+Json::Value Codemodel::Dump()
+{
+  Json::Value codemodel = Json::objectValue;
+
+  codemodel["paths"] = this->DumpPaths();
+  codemodel["configurations"] = this->DumpConfigurations();
+
+  return codemodel;
+}
+
+Json::Value Codemodel::DumpPaths()
+{
+  Json::Value paths = Json::objectValue;
+  paths["source"] = this->FileAPI.GetCMakeInstance()->GetHomeDirectory();
+  paths["build"] = this->FileAPI.GetCMakeInstance()->GetHomeOutputDirectory();
+  return paths;
+}
+
+Json::Value Codemodel::DumpConfigurations()
+{
+  std::vector<std::string> configs;
+  cmGlobalGenerator* gg =
+    this->FileAPI.GetCMakeInstance()->GetGlobalGenerator();
+  auto makefiles = gg->GetMakefiles();
+  if (!makefiles.empty()) {
+    makefiles[0]->GetConfigurations(configs);
+    if (configs.empty()) {
+      configs.emplace_back();
+    }
+  }
+  Json::Value configurations = Json::arrayValue;
+  for (std::string const& config : configs) {
+    configurations.append(this->DumpConfiguration(config));
+  }
+  return configurations;
+}
+
+Json::Value Codemodel::DumpConfiguration(std::string const& config)
+{
+  CodemodelConfig configuration(this->FileAPI, this->Version, config);
+  return configuration.Dump();
+}
+
+CodemodelConfig::CodemodelConfig(cmFileAPI& fileAPI, unsigned long version,
+                                 std::string const& config)
+  : FileAPI(fileAPI)
+  , Version(version)
+  , Config(config)
+  , TopSource(this->FileAPI.GetCMakeInstance()->GetHomeDirectory())
+  , TopBuild(this->FileAPI.GetCMakeInstance()->GetHomeOutputDirectory())
+{
+  static_cast<void>(this->Version);
+}
+
+Json::Value CodemodelConfig::Dump()
+{
+  Json::Value configuration = Json::objectValue;
+  configuration["name"] = this->Config;
+  this->ProcessDirectories();
+  configuration["targets"] = this->DumpTargets();
+  configuration["directories"] = this->DumpDirectories();
+  return configuration;
+}
+
+void CodemodelConfig::ProcessDirectories()
+{
+  cmGlobalGenerator* gg =
+    this->FileAPI.GetCMakeInstance()->GetGlobalGenerator();
+  std::vector<cmLocalGenerator*> const& localGens = gg->GetLocalGenerators();
+
+  // Add directories in forward order to process parents before children.
+  this->Directories.reserve(localGens.size());
+  for (cmLocalGenerator* lg : localGens) {
+    auto directoryIndex =
+      static_cast<Json::ArrayIndex>(this->Directories.size());
+    this->Directories.emplace_back();
+    Directory& d = this->Directories[directoryIndex];
+    d.Snapshot = lg->GetStateSnapshot().GetBuildsystemDirectory();
+    this->DirectoryMap[d.Snapshot] = directoryIndex;
+  }
+}
+
+Json::ArrayIndex CodemodelConfig::GetDirectoryIndex(cmLocalGenerator const* lg)
+{
+  return this->GetDirectoryIndex(
+    lg->GetStateSnapshot().GetBuildsystemDirectory());
+}
+
+Json::ArrayIndex CodemodelConfig::GetDirectoryIndex(cmStateSnapshot s)
+{
+  auto i = this->DirectoryMap.find(s);
+  assert(i != this->DirectoryMap.end());
+  return i->second;
+}
+
+Json::Value CodemodelConfig::DumpTargets()
+{
+  Json::Value targets = Json::arrayValue;
+
+  std::vector<cmGeneratorTarget*> targetList;
+  cmGlobalGenerator* gg =
+    this->FileAPI.GetCMakeInstance()->GetGlobalGenerator();
+  for (cmLocalGenerator const* lg : gg->GetLocalGenerators()) {
+    std::vector<cmGeneratorTarget*> const& list = lg->GetGeneratorTargets();
+    targetList.insert(targetList.end(), list.begin(), list.end());
+  }
+  std::sort(targetList.begin(), targetList.end(),
+            [](cmGeneratorTarget* l, cmGeneratorTarget* r) {
+              return l->GetName() < r->GetName();
+            });
+
+  for (cmGeneratorTarget* gt : targetList) {
+    if (gt->GetType() == cmStateEnums::GLOBAL_TARGET ||
+        gt->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
+      continue;
+    }
+
+    targets.append(this->DumpTarget(gt, targets.size()));
+  }
+
+  return targets;
+}
+
+Json::Value CodemodelConfig::DumpTarget(cmGeneratorTarget* gt,
+                                        Json::ArrayIndex ti)
+{
+  Target t(gt, this->Config);
+  std::string prefix = "target-" + gt->GetName();
+  if (!this->Config.empty()) {
+    prefix += "-" + this->Config;
+  }
+  Json::Value target = this->FileAPI.MaybeJsonFile(t.Dump(), prefix);
+  target["name"] = gt->GetName();
+  target["id"] = TargetId(gt, this->TopBuild);
+
+  // Cross-reference directory containing target.
+  Json::ArrayIndex di = this->GetDirectoryIndex(gt->GetLocalGenerator());
+  target["directoryIndex"] = di;
+  this->Directories[di].TargetIndexes.append(ti);
+
+  return target;
+}
+
+Json::Value CodemodelConfig::DumpDirectories()
+{
+  Json::Value directories = Json::arrayValue;
+  for (Directory& d : this->Directories) {
+    directories.append(this->DumpDirectory(d));
+  }
+  return directories;
+}
+
+Json::Value CodemodelConfig::DumpDirectory(Directory& d)
+{
+  Json::Value directory = Json::objectValue;
+
+  std::string sourceDir = d.Snapshot.GetDirectory().GetCurrentSource();
+  directory["source"] = RelativeIfUnder(this->TopSource, sourceDir);
+
+  std::string buildDir = d.Snapshot.GetDirectory().GetCurrentBinary();
+  directory["build"] = RelativeIfUnder(this->TopBuild, buildDir);
+
+  cmStateSnapshot parentDir = d.Snapshot.GetBuildsystemDirectoryParent();
+  if (parentDir.IsValid()) {
+    directory["parentIndex"] = this->GetDirectoryIndex(parentDir);
+  }
+
+  Json::Value childIndexes = Json::arrayValue;
+  for (cmStateSnapshot const& child : d.Snapshot.GetChildren()) {
+    childIndexes.append(
+      this->GetDirectoryIndex(child.GetBuildsystemDirectory()));
+  }
+  if (!childIndexes.empty()) {
+    directory["childIndexes"] = std::move(childIndexes);
+  }
+
+  if (!d.TargetIndexes.empty()) {
+    directory["targetIndexes"] = std::move(d.TargetIndexes);
+  }
+
+  return directory;
+}
+
+Target::Target(cmGeneratorTarget* gt, std::string const& config)
+  : GT(gt)
+  , Config(config)
+  , TopSource(gt->GetGlobalGenerator()->GetCMakeInstance()->GetHomeDirectory())
+  , TopBuild(
+      gt->GetGlobalGenerator()->GetCMakeInstance()->GetHomeOutputDirectory())
+  , SourceGroupsLocal(this->GT->Makefile->GetSourceGroups())
+  , Backtraces(this->TopSource)
+{
+}
+
+Json::Value Target::Dump()
+{
+  Json::Value target = Json::objectValue;
+
+  cmStateEnums::TargetType const type = this->GT->GetType();
+
+  target["name"] = this->GT->GetName();
+  target["type"] = cmState::GetTargetTypeName(type);
+  target["id"] = TargetId(this->GT, this->TopBuild);
+  target["paths"] = this->DumpPaths();
+  if (this->GT->Target->GetIsGeneratorProvided()) {
+    target["isGeneratorProvided"] = true;
+  }
+
+  this->AddBacktrace(target, this->GT->GetBacktrace());
+
+  if (this->GT->Target->GetHaveInstallRule()) {
+    target["install"] = this->DumpInstall();
+  }
+
+  if (this->GT->HaveWellDefinedOutputFiles()) {
+    Json::Value artifacts = this->DumpArtifacts();
+    if (!artifacts.empty()) {
+      target["artifacts"] = std::move(artifacts);
+    }
+  }
+
+  if (type == cmStateEnums::EXECUTABLE ||
+      type == cmStateEnums::SHARED_LIBRARY ||
+      type == cmStateEnums::MODULE_LIBRARY) {
+    target["nameOnDisk"] = this->GT->GetFullName(this->Config);
+    target["link"] = this->DumpLink();
+  } else if (type == cmStateEnums::STATIC_LIBRARY) {
+    target["nameOnDisk"] = this->GT->GetFullName(this->Config);
+    target["archive"] = this->DumpArchive();
+  }
+
+  Json::Value dependencies = this->DumpDependencies();
+  if (!dependencies.empty()) {
+    target["dependencies"] = dependencies;
+  }
+
+  {
+    this->ProcessLanguages();
+
+    target["sources"] = this->DumpSources();
+
+    Json::Value folder = this->DumpFolder();
+    if (!folder.isNull()) {
+      target["folder"] = std::move(folder);
+    }
+
+    Json::Value sourceGroups = this->DumpSourceGroups();
+    if (!sourceGroups.empty()) {
+      target["sourceGroups"] = std::move(sourceGroups);
+    }
+
+    Json::Value compileGroups = this->DumpCompileGroups();
+    if (!compileGroups.empty()) {
+      target["compileGroups"] = std::move(compileGroups);
+    }
+  }
+
+  target["backtraceGraph"] = this->Backtraces.Dump();
+
+  return target;
+}
+
+void Target::ProcessLanguages()
+{
+  std::set<std::string> languages;
+  this->GT->GetLanguages(languages, this->Config);
+  for (std::string const& lang : languages) {
+    this->ProcessLanguage(lang);
+  }
+}
+
+void Target::ProcessLanguage(std::string const& lang)
+{
+  CompileData& cd = this->CompileDataMap[lang];
+  cd.Language = lang;
+  if (const char* sysrootCompile =
+        this->GT->Makefile->GetDefinition("CMAKE_SYSROOT_COMPILE")) {
+    cd.Sysroot = sysrootCompile;
+  } else if (const char* sysroot =
+               this->GT->Makefile->GetDefinition("CMAKE_SYSROOT")) {
+    cd.Sysroot = sysroot;
+  }
+  cmLocalGenerator* lg = this->GT->GetLocalGenerator();
+  {
+    // FIXME: Add flags from end section of ExpandRuleVariable,
+    // which may need to be factored out.
+    std::string flags;
+    lg->GetTargetCompileFlags(this->GT, this->Config, lang, flags);
+    cd.Flags.emplace_back(std::move(flags), cmListFileBacktrace());
+  }
+  std::set<BT<std::string>> defines =
+    lg->GetTargetDefines(this->GT, this->Config, lang);
+  cd.SetDefines(defines);
+  std::vector<BT<std::string>> includePathList =
+    lg->GetIncludeDirectories(this->GT, lang, this->Config, true);
+  for (BT<std::string> const& i : includePathList) {
+    cd.Includes.emplace_back(
+      i, this->GT->IsSystemIncludeDirectory(i.Value, this->Config, lang));
+  }
+}
+
+Json::ArrayIndex Target::AddSourceGroup(cmSourceGroup* sg, Json::ArrayIndex si)
+{
+  std::unordered_map<cmSourceGroup const*, Json::ArrayIndex>::iterator i =
+    this->SourceGroupsMap.find(sg);
+  if (i == this->SourceGroupsMap.end()) {
+    auto sgIndex = static_cast<Json::ArrayIndex>(this->SourceGroups.size());
+    i = this->SourceGroupsMap.emplace(sg, sgIndex).first;
+    SourceGroup g;
+    g.Name = sg->GetFullName();
+    this->SourceGroups.push_back(std::move(g));
+  }
+  this->SourceGroups[i->second].SourceIndexes.append(si);
+  return i->second;
+}
+
+CompileData Target::BuildCompileData(cmSourceFile* sf)
+{
+  CompileData fd;
+
+  fd.Language = sf->GetLanguage();
+  if (fd.Language.empty()) {
+    return fd;
+  }
+  CompileData const& cd = this->CompileDataMap.at(fd.Language);
+
+  fd.Sysroot = cd.Sysroot;
+
+  cmLocalGenerator* lg = this->GT->GetLocalGenerator();
+  cmGeneratorExpressionInterpreter genexInterpreter(lg, this->Config, this->GT,
+                                                    fd.Language);
+
+  fd.Flags = cd.Flags;
+  const std::string COMPILE_FLAGS("COMPILE_FLAGS");
+  if (const char* cflags = sf->GetProperty(COMPILE_FLAGS)) {
+    std::string flags = genexInterpreter.Evaluate(cflags, COMPILE_FLAGS);
+    fd.Flags.emplace_back(std::move(flags), cmListFileBacktrace());
+  }
+  const std::string COMPILE_OPTIONS("COMPILE_OPTIONS");
+  if (const char* coptions = sf->GetProperty(COMPILE_OPTIONS)) {
+    std::string flags;
+    lg->AppendCompileOptions(
+      flags, genexInterpreter.Evaluate(coptions, COMPILE_OPTIONS));
+    fd.Flags.emplace_back(std::move(flags), cmListFileBacktrace());
+  }
+
+  // Add include directories from source file properties.
+  {
+    std::vector<std::string> includes;
+    const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES");
+    if (const char* cincludes = sf->GetProperty(INCLUDE_DIRECTORIES)) {
+      const std::string& evaluatedIncludes =
+        genexInterpreter.Evaluate(cincludes, INCLUDE_DIRECTORIES);
+      lg->AppendIncludeDirectories(includes, evaluatedIncludes, *sf);
+
+      for (std::string const& include : includes) {
+        bool const isSystemInclude = this->GT->IsSystemIncludeDirectory(
+          include, this->Config, fd.Language);
+        fd.Includes.emplace_back(include, isSystemInclude);
+      }
+    }
+  }
+  fd.Includes.insert(fd.Includes.end(), cd.Includes.begin(),
+                     cd.Includes.end());
+
+  const std::string COMPILE_DEFINITIONS("COMPILE_DEFINITIONS");
+  std::set<std::string> fileDefines;
+  if (const char* defs = sf->GetProperty(COMPILE_DEFINITIONS)) {
+    lg->AppendDefines(fileDefines,
+                      genexInterpreter.Evaluate(defs, COMPILE_DEFINITIONS));
+  }
+
+  const std::string defPropName =
+    "COMPILE_DEFINITIONS_" + cmSystemTools::UpperCase(this->Config);
+  if (const char* config_defs = sf->GetProperty(defPropName)) {
+    lg->AppendDefines(
+      fileDefines,
+      genexInterpreter.Evaluate(config_defs, COMPILE_DEFINITIONS));
+  }
+
+  std::set<BT<std::string>> defines;
+  defines.insert(fileDefines.begin(), fileDefines.end());
+  defines.insert(cd.Defines.begin(), cd.Defines.end());
+
+  fd.SetDefines(defines);
+
+  return fd;
+}
+
+Json::ArrayIndex Target::AddSourceCompileGroup(cmSourceFile* sf,
+                                               Json::ArrayIndex si)
+{
+  Json::Value compileDataJson =
+    this->DumpCompileData(this->BuildCompileData(sf));
+  std::map<Json::Value, Json::ArrayIndex>::iterator i =
+    this->CompileGroupMap.find(compileDataJson);
+  if (i == this->CompileGroupMap.end()) {
+    Json::ArrayIndex cgIndex =
+      static_cast<Json::ArrayIndex>(this->CompileGroups.size());
+    i =
+      this->CompileGroupMap.emplace(std::move(compileDataJson), cgIndex).first;
+    CompileGroup g;
+    g.Entry = i;
+    this->CompileGroups.push_back(std::move(g));
+  }
+  this->CompileGroups[i->second].SourceIndexes.append(si);
+  return i->second;
+}
+
+void Target::AddBacktrace(Json::Value& object, cmListFileBacktrace const& bt)
+{
+  Json::ArrayIndex backtrace;
+  if (this->Backtraces.Add(bt, backtrace)) {
+    object["backtrace"] = backtrace;
+  }
+}
+
+Json::Value Target::DumpPaths()
+{
+  Json::Value paths = Json::objectValue;
+  cmLocalGenerator* lg = this->GT->GetLocalGenerator();
+
+  std::string const& sourceDir = lg->GetCurrentSourceDirectory();
+  paths["source"] = RelativeIfUnder(this->TopSource, sourceDir);
+
+  std::string const& buildDir = lg->GetCurrentBinaryDirectory();
+  paths["build"] = RelativeIfUnder(this->TopBuild, buildDir);
+
+  return paths;
+}
+
+Json::Value Target::DumpSources()
+{
+  Json::Value sources = Json::arrayValue;
+  cmGeneratorTarget::KindedSources const& kinded =
+    this->GT->GetKindedSources(this->Config);
+  for (cmGeneratorTarget::SourceAndKind const& sk : kinded.Sources) {
+    sources.append(this->DumpSource(sk, sources.size()));
+  }
+  return sources;
+}
+
+Json::Value Target::DumpSource(cmGeneratorTarget::SourceAndKind const& sk,
+                               Json::ArrayIndex si)
+{
+  Json::Value source = Json::objectValue;
+
+  std::string const path = sk.Source.Value->GetFullPath();
+  source["path"] = RelativeIfUnder(this->TopSource, path);
+  if (sk.Source.Value->GetPropertyAsBool("GENERATED")) {
+    source["isGenerated"] = true;
+  }
+  this->AddBacktrace(source, sk.Source.Backtrace);
+
+  if (cmSourceGroup* sg =
+        this->GT->Makefile->FindSourceGroup(path, this->SourceGroupsLocal)) {
+    source["sourceGroupIndex"] = this->AddSourceGroup(sg, si);
+  }
+
+  switch (sk.Kind) {
+    case cmGeneratorTarget::SourceKindObjectSource: {
+      source["compileGroupIndex"] =
+        this->AddSourceCompileGroup(sk.Source.Value, si);
+    } break;
+    case cmGeneratorTarget::SourceKindAppManifest:
+    case cmGeneratorTarget::SourceKindCertificate:
+    case cmGeneratorTarget::SourceKindCustomCommand:
+    case cmGeneratorTarget::SourceKindExternalObject:
+    case cmGeneratorTarget::SourceKindExtra:
+    case cmGeneratorTarget::SourceKindHeader:
+    case cmGeneratorTarget::SourceKindIDL:
+    case cmGeneratorTarget::SourceKindManifest:
+    case cmGeneratorTarget::SourceKindModuleDefinition:
+    case cmGeneratorTarget::SourceKindResx:
+    case cmGeneratorTarget::SourceKindXaml:
+      break;
+  }
+
+  return source;
+}
+
+Json::Value Target::DumpCompileData(CompileData cd)
+{
+  Json::Value result = Json::objectValue;
+
+  if (!cd.Language.empty()) {
+    result["language"] = cd.Language;
+  }
+  if (!cd.Sysroot.empty()) {
+    result["sysroot"] = this->DumpSysroot(cd.Sysroot);
+  }
+  if (!cd.Flags.empty()) {
+    result["compileCommandFragments"] = this->DumpCommandFragments(cd.Flags);
+  }
+  if (!cd.Includes.empty()) {
+    Json::Value includes = Json::arrayValue;
+    for (auto const& i : cd.Includes) {
+      includes.append(this->DumpInclude(i));
+    }
+    result["includes"] = includes;
+  }
+  if (!cd.Defines.empty()) {
+    Json::Value defines = Json::arrayValue;
+    for (BT<std::string> const& d : cd.Defines) {
+      defines.append(this->DumpDefine(d));
+    }
+    result["defines"] = std::move(defines);
+  }
+
+  return result;
+}
+
+Json::Value Target::DumpInclude(CompileData::IncludeEntry const& inc)
+{
+  Json::Value include = Json::objectValue;
+  include["path"] = inc.Path.Value;
+  if (inc.IsSystem) {
+    include["isSystem"] = true;
+  }
+  this->AddBacktrace(include, inc.Path.Backtrace);
+  return include;
+}
+
+Json::Value Target::DumpDefine(BT<std::string> const& def)
+{
+  Json::Value define = Json::objectValue;
+  define["define"] = def.Value;
+  this->AddBacktrace(define, def.Backtrace);
+  return define;
+}
+
+Json::Value Target::DumpSourceGroups()
+{
+  Json::Value sourceGroups = Json::arrayValue;
+  for (auto& sg : this->SourceGroups) {
+    sourceGroups.append(this->DumpSourceGroup(sg));
+  }
+  return sourceGroups;
+}
+
+Json::Value Target::DumpSourceGroup(SourceGroup& sg)
+{
+  Json::Value group = Json::objectValue;
+  group["name"] = sg.Name;
+  group["sourceIndexes"] = std::move(sg.SourceIndexes);
+  return group;
+}
+
+Json::Value Target::DumpCompileGroups()
+{
+  Json::Value compileGroups = Json::arrayValue;
+  for (auto& cg : this->CompileGroups) {
+    compileGroups.append(this->DumpCompileGroup(cg));
+  }
+  return compileGroups;
+}
+
+Json::Value Target::DumpCompileGroup(CompileGroup& cg)
+{
+  Json::Value group = cg.Entry->first;
+  group["sourceIndexes"] = std::move(cg.SourceIndexes);
+  return group;
+}
+
+Json::Value Target::DumpSysroot(std::string const& path)
+{
+  Json::Value sysroot = Json::objectValue;
+  sysroot["path"] = path;
+  return sysroot;
+}
+
+Json::Value Target::DumpInstall()
+{
+  Json::Value install = Json::objectValue;
+  install["prefix"] = this->DumpInstallPrefix();
+  install["destinations"] = this->DumpInstallDestinations();
+  return install;
+}
+
+Json::Value Target::DumpInstallPrefix()
+{
+  Json::Value prefix = Json::objectValue;
+  std::string p =
+    this->GT->Makefile->GetSafeDefinition("CMAKE_INSTALL_PREFIX");
+  cmSystemTools::ConvertToUnixSlashes(p);
+  prefix["path"] = p;
+  return prefix;
+}
+
+Json::Value Target::DumpInstallDestinations()
+{
+  Json::Value destinations = Json::arrayValue;
+  auto installGens = this->GT->Makefile->GetInstallGenerators();
+  for (auto iGen : installGens) {
+    auto itGen = dynamic_cast<cmInstallTargetGenerator*>(iGen);
+    if (itGen != nullptr && itGen->GetTarget() == this->GT) {
+      destinations.append(this->DumpInstallDestination(itGen));
+    }
+  }
+  return destinations;
+}
+
+Json::Value Target::DumpInstallDestination(cmInstallTargetGenerator* itGen)
+{
+  Json::Value destination = Json::objectValue;
+  destination["path"] = itGen->GetDestination(this->Config);
+  this->AddBacktrace(destination, itGen->GetBacktrace());
+  return destination;
+}
+
+Json::Value Target::DumpArtifacts()
+{
+  Json::Value artifacts = Json::arrayValue;
+
+  // Object libraries have only object files as artifacts.
+  if (this->GT->GetType() == cmStateEnums::OBJECT_LIBRARY) {
+    if (!this->GT->GetGlobalGenerator()->HasKnownObjectFileLocation(nullptr)) {
+      return artifacts;
+    }
+    std::vector<cmSourceFile const*> objectSources;
+    this->GT->GetObjectSources(objectSources, this->Config);
+    std::string const obj_dir = this->GT->GetObjectDirectory(this->Config);
+    for (cmSourceFile const* sf : objectSources) {
+      const std::string& obj = this->GT->GetObjectName(sf);
+      Json::Value artifact = Json::objectValue;
+      artifact["path"] = RelativeIfUnder(this->TopBuild, obj_dir + obj);
+      artifacts.append(std::move(artifact)); // NOLINT(*)
+    }
+    return artifacts;
+  }
+
+  // Other target types always have a "main" artifact.
+  {
+    Json::Value artifact = Json::objectValue;
+    artifact["path"] =
+      RelativeIfUnder(this->TopBuild,
+                      this->GT->GetFullPath(
+                        this->Config, cmStateEnums::RuntimeBinaryArtifact));
+    artifacts.append(std::move(artifact)); // NOLINT(*)
+  }
+
+  // Add Windows-specific artifacts produced by the linker.
+  if (this->GT->IsDLLPlatform() &&
+      this->GT->GetType() != cmStateEnums::STATIC_LIBRARY) {
+    if (this->GT->GetType() == cmStateEnums::SHARED_LIBRARY ||
+        this->GT->IsExecutableWithExports()) {
+      Json::Value artifact = Json::objectValue;
+      artifact["path"] =
+        RelativeIfUnder(this->TopBuild,
+                        this->GT->GetFullPath(
+                          this->Config, cmStateEnums::ImportLibraryArtifact));
+      artifacts.append(std::move(artifact)); // NOLINT(*)
+    }
+    cmGeneratorTarget::OutputInfo const* output =
+      this->GT->GetOutputInfo(this->Config);
+    if (output && !output->PdbDir.empty()) {
+      Json::Value artifact = Json::objectValue;
+      artifact["path"] = RelativeIfUnder(this->TopBuild,
+                                         output->PdbDir + '/' +
+                                           this->GT->GetPDBName(this->Config));
+      artifacts.append(std::move(artifact)); // NOLINT(*)
+    }
+  }
+  return artifacts;
+}
+
+Json::Value Target::DumpLink()
+{
+  Json::Value link = Json::objectValue;
+  std::string lang = this->GT->GetLinkerLanguage(this->Config);
+  link["language"] = lang;
+  {
+    Json::Value commandFragments = this->DumpLinkCommandFragments();
+    if (!commandFragments.empty()) {
+      link["commandFragments"] = std::move(commandFragments);
+    }
+  }
+  if (const char* sysrootLink =
+        this->GT->Makefile->GetDefinition("CMAKE_SYSROOT_LINK")) {
+    link["sysroot"] = this->DumpSysroot(sysrootLink);
+  } else if (const char* sysroot =
+               this->GT->Makefile->GetDefinition("CMAKE_SYSROOT")) {
+    link["sysroot"] = this->DumpSysroot(sysroot);
+  }
+  if (this->GT->IsIPOEnabled(lang, this->Config)) {
+    link["lto"] = true;
+  }
+  return link;
+}
+
+Json::Value Target::DumpArchive()
+{
+  Json::Value archive = Json::objectValue;
+  {
+    // The "link" fragments not relevant to static libraries are empty.
+    Json::Value commandFragments = this->DumpLinkCommandFragments();
+    if (!commandFragments.empty()) {
+      archive["commandFragments"] = std::move(commandFragments);
+    }
+  }
+  std::string lang = this->GT->GetLinkerLanguage(this->Config);
+  if (this->GT->IsIPOEnabled(lang, this->Config)) {
+    archive["lto"] = true;
+  }
+  return archive;
+}
+
+Json::Value Target::DumpLinkCommandFragments()
+{
+  Json::Value linkFragments = Json::arrayValue;
+
+  std::string linkLanguageFlags;
+  std::string linkFlags;
+  std::string frameworkPath;
+  std::string linkPath;
+  std::string linkLibs;
+  cmLocalGenerator* lg = this->GT->GetLocalGenerator();
+  cmLinkLineComputer linkLineComputer(lg,
+                                      lg->GetStateSnapshot().GetDirectory());
+  lg->GetTargetFlags(&linkLineComputer, this->Config, linkLibs,
+                     linkLanguageFlags, linkFlags, frameworkPath, linkPath,
+                     this->GT);
+  linkLanguageFlags = cmSystemTools::TrimWhitespace(linkLanguageFlags);
+  linkFlags = cmSystemTools::TrimWhitespace(linkFlags);
+  frameworkPath = cmSystemTools::TrimWhitespace(frameworkPath);
+  linkPath = cmSystemTools::TrimWhitespace(linkPath);
+  linkLibs = cmSystemTools::TrimWhitespace(linkLibs);
+
+  if (!linkLanguageFlags.empty()) {
+    linkFragments.append(
+      this->DumpCommandFragment(std::move(linkLanguageFlags), "flags"));
+  }
+
+  if (!linkFlags.empty()) {
+    linkFragments.append(
+      this->DumpCommandFragment(std::move(linkFlags), "flags"));
+  }
+
+  if (!frameworkPath.empty()) {
+    linkFragments.append(
+      this->DumpCommandFragment(std::move(frameworkPath), "frameworkPath"));
+  }
+
+  if (!linkPath.empty()) {
+    linkFragments.append(
+      this->DumpCommandFragment(std::move(linkPath), "libraryPath"));
+  }
+
+  if (!linkLibs.empty()) {
+    linkFragments.append(
+      this->DumpCommandFragment(std::move(linkLibs), "libraries"));
+  }
+
+  return linkFragments;
+}
+
+Json::Value Target::DumpCommandFragments(
+  std::vector<BT<std::string>> const& frags)
+{
+  Json::Value commandFragments = Json::arrayValue;
+  for (BT<std::string> const& f : frags) {
+    commandFragments.append(this->DumpCommandFragment(f));
+  }
+  return commandFragments;
+}
+
+Json::Value Target::DumpCommandFragment(BT<std::string> const& frag,
+                                        std::string const& role)
+{
+  Json::Value fragment = Json::objectValue;
+  fragment["fragment"] = frag.Value;
+  if (!role.empty()) {
+    fragment["role"] = role;
+  }
+  this->AddBacktrace(fragment, frag.Backtrace);
+  return fragment;
+}
+
+Json::Value Target::DumpDependencies()
+{
+  Json::Value dependencies = Json::arrayValue;
+  cmGlobalGenerator* gg = this->GT->GetGlobalGenerator();
+  for (cmTargetDepend const& td : gg->GetTargetDirectDepends(this->GT)) {
+    dependencies.append(this->DumpDependency(td));
+  }
+  return dependencies;
+}
+
+Json::Value Target::DumpDependency(cmTargetDepend const& td)
+{
+  Json::Value dependency = Json::objectValue;
+  dependency["id"] = TargetId(td, this->TopBuild);
+  this->AddBacktrace(dependency, td.GetBacktrace());
+  return dependency;
+}
+
+Json::Value Target::DumpFolder()
+{
+  Json::Value folder;
+  if (const char* f = this->GT->GetProperty("FOLDER")) {
+    folder = Json::objectValue;
+    folder["name"] = f;
+  }
+  return folder;
+}
+}
+
+Json::Value cmFileAPICodemodelDump(cmFileAPI& fileAPI, unsigned long version)
+{
+  Codemodel codemodel(fileAPI, version);
+  return codemodel.Dump();
+}
diff --git a/Source/cmFileAPICodemodel.h b/Source/cmFileAPICodemodel.h
new file mode 100644
index 0000000..ffbd928
--- /dev/null
+++ b/Source/cmFileAPICodemodel.h
@@ -0,0 +1,15 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmFileAPICodemodel_h
+#define cmFileAPICodemodel_h
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include "cm_jsoncpp_value.h"
+
+class cmFileAPI;
+
+extern Json::Value cmFileAPICodemodelDump(cmFileAPI& fileAPI,
+                                          unsigned long version);
+
+#endif
diff --git a/Tests/RunCMake/FileAPI/RunCMakeTest.cmake b/Tests/RunCMake/FileAPI/RunCMakeTest.cmake
index 1cc10ee..515a4bd 100644
--- a/Tests/RunCMake/FileAPI/RunCMakeTest.cmake
+++ b/Tests/RunCMake/FileAPI/RunCMakeTest.cmake
@@ -43,3 +43,14 @@ run_cmake(ClientStateless)
 run_cmake(MixedStateless)
 run_cmake(DuplicateStateless)
 run_cmake(ClientStateful)
+
+function(run_object object)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${object}-build)
+  run_cmake(${object})
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(${object}-SharedStateless ${CMAKE_COMMAND} .)
+  run_cmake_command(${object}-ClientStateless ${CMAKE_COMMAND} .)
+  run_cmake_command(${object}-ClientStateful ${CMAKE_COMMAND} .)
+endfunction()
+
+run_object(codemodel-v2)
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateful-check.cmake b/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateful-check.cmake
new file mode 100644
index 0000000..cdbfe7c
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateful-check.cmake
@@ -0,0 +1,11 @@
+set(expect
+  query
+  query/client-foo
+  query/client-foo/query.json
+  reply
+  reply/codemodel-v2-[0-9a-f]+.json
+  reply/index-[0-9.T-]+.json
+  )
+check_api("^${expect}$")
+
+check_python(codemodel-v2)
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateful-prep.cmake b/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateful-prep.cmake
new file mode 100644
index 0000000..e2b38ff
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateful-prep.cmake
@@ -0,0 +1,4 @@
+file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query)
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/query.json" [[
+{ "requests": [ { "kind": "codemodel", "version" : 2 } ] }
+]])
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateless-check.cmake b/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateless-check.cmake
new file mode 100644
index 0000000..612120b
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateless-check.cmake
@@ -0,0 +1,11 @@
+set(expect
+  query
+  query/client-foo
+  query/client-foo/codemodel-v2
+  reply
+  reply/codemodel-v2-[0-9a-f]+.json
+  reply/index-[0-9.T-]+.json
+  )
+check_api("^${expect}$")
+
+check_python(codemodel-v2)
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateless-prep.cmake b/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateless-prep.cmake
new file mode 100644
index 0000000..d1ce292
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateless-prep.cmake
@@ -0,0 +1,2 @@
+file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query)
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/codemodel-v2" "")
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-SharedStateless-check.cmake b/Tests/RunCMake/FileAPI/codemodel-v2-SharedStateless-check.cmake
new file mode 100644
index 0000000..2afaeeb
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-SharedStateless-check.cmake
@@ -0,0 +1,10 @@
+set(expect
+  query
+  query/codemodel-v2
+  reply
+  reply/codemodel-v2-[0-9a-f]+.json
+  reply/index-[0-9.T-]+.json
+  )
+check_api("^${expect}$")
+
+check_python(codemodel-v2)
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-SharedStateless-prep.cmake b/Tests/RunCMake/FileAPI/codemodel-v2-SharedStateless-prep.cmake
new file mode 100644
index 0000000..8a519d5
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-SharedStateless-prep.cmake
@@ -0,0 +1,2 @@
+file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query)
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/codemodel-v2" "")
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-check.py b/Tests/RunCMake/FileAPI/codemodel-v2-check.py
new file mode 100644
index 0000000..8e65acb
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-check.py
@@ -0,0 +1,15 @@
+from check_index import *
+
+def check_objects(o):
+    assert is_list(o)
+    assert len(o) == 1
+    check_index_object(o[0], "codemodel", 2, 0, check_object_codemodel)
+
+def check_object_codemodel(o):
+    assert sorted(o.keys()) == ["configurations", "kind", "paths", "version"]
+    # The "kind" and "version" members are handled by check_index_object.
+    # FIXME: Check "configurations"  and "paths" members
+
+assert is_dict(index)
+assert sorted(index.keys()) == ["cmake", "objects", "reply"]
+check_objects(index["objects"])
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2.cmake b/Tests/RunCMake/FileAPI/codemodel-v2.cmake
new file mode 100644
index 0000000..8c782b9
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/codemodel-v2.cmake
@@ -0,0 +1 @@
+# FIXME: enable_language(C) and add targets to dump
diff --git a/Utilities/IWYU/mapping.imp b/Utilities/IWYU/mapping.imp
index 5af8b85..4f26ce1 100644
--- a/Utilities/IWYU/mapping.imp
+++ b/Utilities/IWYU/mapping.imp
@@ -126,6 +126,8 @@
   { symbol: [ "SIGINT", private, "\"cm_uv.h\"", public ] },
   { symbol: [ "ssize_t", private, "\"cm_uv.h\"", public ] },
 
+  { symbol: [ "Json::ArrayIndex", private, "\"cm_jsoncpp_value.h\"", public ] },
+
   { symbol: [ "std::ifstream", private, "\"cmsys/FStream.hxx\"", public ] },
   { symbol: [ "std::ofstream", private, "\"cmsys/FStream.hxx\"", public ] },
   { symbol: [ "cmsys::ifstream", private, "\"cmsys/FStream.hxx\"", public ] },

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=555fa77a35b15cf1ed5163bf9fa1782079534ef1
commit 555fa77a35b15cf1ed5163bf9fa1782079534ef1
Author:     Kyle Edwards <kyle.edwards at kitware.com>
AuthorDate: Tue Nov 13 18:46:17 2018 -0500
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Wed Dec 12 06:40:10 2018 -0500

    fileapi: Add more infrastructure to FileAPI test

diff --git a/Tests/RunCMake/FileAPI/check_index.py b/Tests/RunCMake/FileAPI/check_index.py
index d8eaecb..cda7234 100644
--- a/Tests/RunCMake/FileAPI/check_index.py
+++ b/Tests/RunCMake/FileAPI/check_index.py
@@ -6,8 +6,8 @@ import re
 if sys.version_info[0] >= 3:
     unicode = str
 
-def is_bool(x):
-    return isinstance(x, bool)
+def is_bool(x, val=None):
+    return isinstance(x, bool) and (val is None or x == val)
 
 def is_dict(x):
     return isinstance(x, dict)
@@ -15,11 +15,69 @@ def is_dict(x):
 def is_list(x):
     return isinstance(x, list)
 
-def is_int(x):
-    return isinstance(x, int) or isinstance(x, long)
-
-def is_string(x):
-    return isinstance(x, str) or isinstance(x, unicode)
+def is_int(x, val=None):
+    return (isinstance(x, int) or isinstance(x, long)) and (val is None or x == val)
+
+def is_string(x, val=None):
+    return (isinstance(x, str) or isinstance(x, unicode)) and (val is None or x == val)
+
+def matches(s, pattern):
+    return is_string(s) and bool(re.search(pattern, s))
+
+def check_list_match(match, actual, expected, check=None, check_exception=None, missing_exception=None, extra_exception=None, allow_extra=False):
+    """
+    Handle the common pattern of making sure every actual item "matches" some
+    item in the expected list, and that neither list has extra items after
+    matching is completed.
+
+    @param match: Callback to check if an actual item matches an expected
+    item. Return True if the item matches, return False if the item doesn't
+    match.
+    @param actual: List of actual items to search.
+    @param expected: List of expected items to match.
+    @param check: Optional function to check that the actual item is valid by
+    comparing it to the expected item.
+    @param check_exception: Optional function that returns an argument to
+    append to any exception thrown by the check function.
+    @param missing_exception: Optional function that returns an argument to
+    append to the exception thrown when an item is not found.
+    @param extra_exception: Optional function that returns an argument to
+    append to the exception thrown when an extra item is found.
+    @param allow_extra: Optional parameter allowing there to be extra actual
+    items after all the expected items have been found.
+    """
+    assert is_list(actual)
+    _actual = actual[:]
+    for expected_item in expected:
+        found = False
+        for i, actual_item in enumerate(_actual):
+            if match(actual_item, expected_item):
+                if check:
+                    try:
+                        check(actual_item, expected_item)
+                    except BaseException as e:
+                        if check_exception:
+                            e.args += (check_exception(actual_item, expected_item),)
+                        raise
+                found = True
+                del _actual[i]
+                break
+        if missing_exception:
+            assert found, missing_exception(expected_item)
+        else:
+            assert found
+    if not allow_extra:
+        if extra_exception:
+            assert len(_actual) == 0, [extra_exception(a) for a in _actual]
+        else:
+            assert len(_actual) == 0
+
+def filter_list(f, l):
+    if l is not None:
+        l = list(filter(f, l))
+    if l == []:
+        l = None
+    return l
 
 def check_cmake(cmake):
     assert is_dict(cmake)

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=b83fe27d8d3f25ed6e229270966b8323f5fc3ae5
commit b83fe27d8d3f25ed6e229270966b8323f5fc3ae5
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Thu Nov 1 10:37:53 2018 -0400
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Wed Dec 12 06:40:10 2018 -0500

    fileapi: Report cmake generator in reply index file

diff --git a/Help/manual/cmake-file-api.7.rst b/Help/manual/cmake-file-api.7.rst
index f5e4eeb..a1b4830 100644
--- a/Help/manual/cmake-file-api.7.rst
+++ b/Help/manual/cmake-file-api.7.rst
@@ -197,6 +197,9 @@ The reply index file contains a JSON object:
         "ctest": "/prefix/bin/ctest",
         "cpack": "/prefix/bin/cpack",
         "root": "/prefix/share/cmake-3.14"
+      },
+      "generator": {
+        "name": "Unix Makefiles"
       }
     },
     "objects": [
@@ -260,6 +263,16 @@ The members are:
     the absolute path to the directory containing CMake resources like the
     ``Modules/`` directory (see :variable:`CMAKE_ROOT`).
 
+  ``generator``
+    A JSON object describing the CMake generator used for the build.
+    It has members:
+
+    ``name``
+      A string specifying the name of the generator.
+    ``platform``
+      If the generator supports :variable:`CMAKE_GENERATOR_PLATFORM`,
+      this is a string specifying the generator platform name.
+
 ``objects``
   A JSON array listing all versions of all `Object Kinds`_ generated
   as part of the reply.  Each array entry is a
diff --git a/Source/cmFileAPI.cxx b/Source/cmFileAPI.cxx
index 2fa5495..1a41370 100644
--- a/Source/cmFileAPI.cxx
+++ b/Source/cmFileAPI.cxx
@@ -4,6 +4,7 @@
 
 #include "cmAlgorithms.h"
 #include "cmCryptoHash.h"
+#include "cmGlobalGenerator.h"
 #include "cmSystemTools.h"
 #include "cmTimestamp.h"
 #include "cmake.h"
@@ -311,6 +312,7 @@ Json::Value cmFileAPI::BuildCMake()
   cmake_paths["ctest"] = cmSystemTools::GetCTestCommand();
   cmake_paths["cpack"] = cmSystemTools::GetCPackCommand();
   cmake_paths["root"] = cmSystemTools::GetCMakeRoot();
+  cmake["generator"] = this->CMakeInstance->GetGlobalGenerator()->GetJson();
   return cmake;
 }
 
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx
index 8b20db6..35d716c 100644
--- a/Source/cmGlobalGenerator.cxx
+++ b/Source/cmGlobalGenerator.cxx
@@ -112,6 +112,15 @@ cmGlobalGenerator::~cmGlobalGenerator()
   delete this->ExtraGenerator;
 }
 
+#if defined(CMAKE_BUILD_WITH_CMAKE)
+Json::Value cmGlobalGenerator::GetJson() const
+{
+  Json::Value generator = Json::objectValue;
+  generator["name"] = this->GetName();
+  return generator;
+}
+#endif
+
 bool cmGlobalGenerator::SetGeneratorInstance(std::string const& i,
                                              cmMakefile* mf)
 {
diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h
index 1ea2d24..4e6b6de 100644
--- a/Source/cmGlobalGenerator.h
+++ b/Source/cmGlobalGenerator.h
@@ -24,6 +24,7 @@
 
 #if defined(CMAKE_BUILD_WITH_CMAKE)
 #  include "cmFileLockPool.h"
+#  include "cm_jsoncpp_value.h"
 #endif
 
 #define CMAKE_DIRECTORY_ID_SEP "::@"
@@ -70,6 +71,11 @@ public:
     return codecvt::None;
   }
 
+#if defined(CMAKE_BUILD_WITH_CMAKE)
+  /** Get a JSON object describing the generator.  */
+  virtual Json::Value GetJson() const;
+#endif
+
   /** Tell the generator about the target system.  */
   virtual bool SetSystemName(std::string const&, cmMakefile*) { return true; }
 
diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx
index 21121f2..1be80ac 100644
--- a/Source/cmGlobalVisualStudio7Generator.cxx
+++ b/Source/cmGlobalVisualStudio7Generator.cxx
@@ -254,6 +254,15 @@ cmLocalGenerator* cmGlobalVisualStudio7Generator::CreateLocalGenerator(
   return lg;
 }
 
+#if defined(CMAKE_BUILD_WITH_CMAKE)
+Json::Value cmGlobalVisualStudio7Generator::GetJson() const
+{
+  Json::Value generator = this->cmGlobalVisualStudioGenerator::GetJson();
+  generator["platform"] = this->GetPlatformName();
+  return generator;
+}
+#endif
+
 std::string const& cmGlobalVisualStudio7Generator::GetPlatformName() const
 {
   if (!this->GeneratorPlatform.empty()) {
diff --git a/Source/cmGlobalVisualStudio7Generator.h b/Source/cmGlobalVisualStudio7Generator.h
index 251478d..59e7a98 100644
--- a/Source/cmGlobalVisualStudio7Generator.h
+++ b/Source/cmGlobalVisualStudio7Generator.h
@@ -28,6 +28,10 @@ public:
   ///! Create a local generator appropriate to this Global Generator
   cmLocalGenerator* CreateLocalGenerator(cmMakefile* mf) override;
 
+#if defined(CMAKE_BUILD_WITH_CMAKE)
+  Json::Value GetJson() const override;
+#endif
+
   bool SetSystemName(std::string const& s, cmMakefile* mf) override;
 
   bool SetGeneratorPlatform(std::string const& p, cmMakefile* mf) override;
diff --git a/Tests/RunCMake/FileAPI/check_index.py b/Tests/RunCMake/FileAPI/check_index.py
index 4d90d59..d8eaecb 100644
--- a/Tests/RunCMake/FileAPI/check_index.py
+++ b/Tests/RunCMake/FileAPI/check_index.py
@@ -23,9 +23,10 @@ def is_string(x):
 
 def check_cmake(cmake):
     assert is_dict(cmake)
-    assert sorted(cmake.keys()) == ["paths", "version"]
+    assert sorted(cmake.keys()) == ["generator", "paths", "version"]
     check_cmake_version(cmake["version"])
     check_cmake_paths(cmake["paths"])
+    check_cmake_generator(cmake["generator"])
 
 def check_cmake_version(v):
     assert is_dict(v)
@@ -45,6 +46,16 @@ def check_cmake_paths(v):
     assert is_string(v["ctest"])
     assert is_string(v["root"])
 
+def check_cmake_generator(g):
+    assert is_dict(g)
+    name = g.get("name", None)
+    assert is_string(name)
+    if name.startswith("Visual Studio"):
+        assert sorted(g.keys()) == ["name", "platform"]
+        assert is_string(g["platform"])
+    else:
+        assert sorted(g.keys()) == ["name"]
+
 def check_index_object(indexEntry, kind, major, minor, check):
     assert is_dict(indexEntry)
     assert sorted(indexEntry.keys()) == ["jsonFile", "kind", "version"]

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=7ee0abbde1656b07eb28ddacca3d22bb7aafdbd8
commit 7ee0abbde1656b07eb28ddacca3d22bb7aafdbd8
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Wed Oct 24 15:22:54 2018 -0400
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Wed Dec 12 06:40:10 2018 -0500

    fileapi: Add helper to create and reference a json reply file

diff --git a/Source/cmFileAPI.cxx b/Source/cmFileAPI.cxx
index cf772d9..2fa5495 100644
--- a/Source/cmFileAPI.cxx
+++ b/Source/cmFileAPI.cxx
@@ -175,6 +175,18 @@ std::string cmFileAPI::WriteJsonFile(
   return fileName;
 }
 
+Json::Value cmFileAPI::MaybeJsonFile(Json::Value in, std::string const& prefix)
+{
+  Json::Value out;
+  if (in.isObject() || in.isArray()) {
+    out = Json::objectValue;
+    out["jsonFile"] = this->WriteJsonFile(in, prefix);
+  } else {
+    out = std::move(in);
+  }
+  return out;
+}
+
 std::string cmFileAPI::ComputeSuffixHash(std::string const& file)
 {
   cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_256);
diff --git a/Source/cmFileAPI.h b/Source/cmFileAPI.h
index 4f4506f..8bfae31 100644
--- a/Source/cmFileAPI.h
+++ b/Source/cmFileAPI.h
@@ -31,6 +31,11 @@ public:
   /** Get the "cmake" instance with which this was constructed.  */
   cmake* GetCMakeInstance() const { return this->CMakeInstance; }
 
+  /** Convert a JSON object or array into an object with a single
+      "jsonFile" member specifying a file named with the given prefix
+      and holding the original object.  Other JSON types are unchanged.  */
+  Json::Value MaybeJsonFile(Json::Value in, std::string const& prefix);
+
 private:
   cmake* CMakeInstance;
 

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=276fdf299306f83b12bd1cc886f5e37d61f25c59
commit 276fdf299306f83b12bd1cc886f5e37d61f25c59
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Thu Sep 13 10:32:14 2018 -0400
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Wed Dec 12 06:40:10 2018 -0500

    fileapi: Add protocol v1 support for stateful per-client queries
    
    Add support for client-owned *stateful* query files.  These allow
    clients to request a list of versions of each object kind and get only
    the first-listed version that CMake recognizes.  Since clients own their
    stateful query files they can mutate them over time.  As a client
    installation is updated it may update the queries that it writes to
    build trees to get newer object versions without paying the cost of
    continuing to generate older versions.
    
    Issue: #18398

diff --git a/Help/manual/cmake-file-api.7.rst b/Help/manual/cmake-file-api.7.rst
index 51655b2..f5e4eeb 100644
--- a/Help/manual/cmake-file-api.7.rst
+++ b/Help/manual/cmake-file-api.7.rst
@@ -31,8 +31,8 @@ It has the following subdirectories:
 
 ``query/``
   Holds query files written by clients.
-  These may be `v1 Shared Stateless Query Files`_ or
-  `v1 Client Stateless Query Files`_.
+  These may be `v1 Shared Stateless Query Files`_,
+  `v1 Client Stateless Query Files`_, or `v1 Client Stateful Query Files`_.
 
 ``reply/``
   Holds reply files written by CMake whenever it runs to generate a build
@@ -84,6 +84,87 @@ own means.
 Files of this form are stateless queries owned by the client ``<client>``.
 The owning client may remove them at any time.
 
+v1 Client Stateful Query Files
+------------------------------
+
+Stateful query files allow clients to request a list of versions of
+each of the `Object Kinds`_ and get only the most recent version
+recognized by the CMake that runs.
+
+Clients may create owned stateful queries by creating ``query.json``
+files in client-specific query subdirectories.  The form is::
+
+  <build>/.cmake/api/v1/query/client-<client>/query.json
+
+where ``client-`` is literal, ``<client>`` is a string uniquely
+identifying the client, and ``query.json`` is literal.  Each client
+must choose a unique ``<client>`` identifier via its own means.
+
+``query.json`` files are stateful queries owned by the client ``<client>``.
+The owning client may update or remove them at any time.  When a
+given client installation is updated it may then update the stateful
+query it writes to build trees to request newer object versions.
+This can be used to avoid asking CMake to generate multiple object
+versions unnecessarily.
+
+A ``query.json`` file must contain a JSON object:
+
+.. code-block:: json
+
+  {
+    "requests": [
+      { "kind": "<kind>" , "version": 1 },
+      { "kind": "<kind>" , "version": { "major": 1, "minor": 2 } },
+      { "kind": "<kind>" , "version": [2, 1] },
+      { "kind": "<kind>" , "version": [2, { "major": 1, "minor": 2 }] },
+      { "kind": "<kind>" , "version": 1, "client": {} },
+      { "kind": "..." }
+    ],
+    "client": {}
+  }
+
+The members are:
+
+``requests``
+  A JSON array containing zero or more requests.  Each request is
+  a JSON object with members:
+
+  ``kind``
+    Specifies one of the `Object Kinds`_ to be included in the reply.
+
+  ``version``
+    Indicates the version(s) of the object kind that the client
+    understands.  Versions have major and minor components following
+    semantic version conventions.  The value must be
+
+    * a JSON integer specifying a (non-negative) major version number, or
+    * a JSON object containing ``major`` and (optionally) ``minor``
+      members specifying non-negative integer version components, or
+    * a JSON array whose elements are each one of the above.
+
+  ``client``
+    Optional member reserved for use by the client.  This value is
+    preserved in the reply written for the client in the
+    `v1 Reply Index File`_ but is otherwise ignored.  Clients may use
+    this to pass custom information with a request through to its reply.
+
+  For each requested object kind CMake will choose the *first* version
+  that it recognizes for that kind among those listed in the request.
+  The response will use the selected *major* version with the highest
+  *minor* version known to the running CMake for that major version.
+  Therefore clients should list all supported major versions in
+  preferred order along with the minimal minor version required
+  for each major version.
+
+``client``
+  Optional member reserved for use by the client.  This value is
+  preserved in the reply written for the client in the
+  `v1 Reply Index File`_ but is otherwise ignored.  Clients may use
+  this to pass custom information with a query through to its reply.
+
+Other ``query.json`` top-level members are reserved for future use.
+If present they are ignored for forward compatibility.
+
 v1 Reply Index File
 -------------------
 
@@ -135,7 +216,18 @@ The reply index file contains a JSON object:
                              "version": { "major": 1, "minor": 0 },
                              "jsonFile": "<file>" },
         "<unknown>": { "error": "unknown query file" },
-        "...": {}
+        "...": {},
+        "query.json": {
+          "requests": [ {}, {}, {} ],
+          "responses": [
+            { "kind": "<kind>",
+              "version": { "major": 1, "minor": 0 },
+              "jsonFile": "<file>" },
+            { "error": "unknown query file" },
+            { "...": {} }
+          ],
+          "client": {}
+        }
       }
     }
   }
@@ -211,6 +303,33 @@ The members are:
       containing a string with an error message indicating that the
       query file is unknown.
 
+    ``query.json``
+      This member appears for clients using
+      `v1 Client Stateful Query Files`_.
+      If the ``query.json`` file failed to read or parse as a JSON object,
+      this member is a JSON object with a single ``error`` member
+      containing a string with an error message.  Otherwise, this member
+      is a JSON object mirroring the content of the ``query.json`` file.
+      The members are:
+
+      ``client``
+        A copy of the ``query.json`` file ``client`` member, if it exists.
+
+      ``requests``
+        A copy of the ``query.json`` file ``requests`` member, if it exists.
+
+      ``responses``
+        If the ``query.json`` file ``requests`` member is missing or invalid,
+        this member is a JSON object with a single ``error`` member
+        containing a string with an error message.  Otherwise, this member
+        contains a JSON array with a response for each entry of the
+        ``requests`` array, in the same order.  Each response is
+
+        * a JSON object with a single ``error`` member containing a string
+          with an error message, or
+        * a `v1 Reply File Reference`_ to the corresponding reply file for
+          the requested object kind and selected version.
+
 After reading the reply index file, clients may read the other
 `v1 Reply Files`_ it references.
 
diff --git a/Source/cmFileAPI.cxx b/Source/cmFileAPI.cxx
index e401b58..cf772d9 100644
--- a/Source/cmFileAPI.cxx
+++ b/Source/cmFileAPI.cxx
@@ -24,6 +24,14 @@ cmFileAPI::cmFileAPI(cmake* cm)
   this->APIv1 =
     this->CMakeInstance->GetHomeOutputDirectory() + "/.cmake/api/v1";
 
+  Json::CharReaderBuilder rbuilder;
+  rbuilder["collectComments"] = false;
+  rbuilder["failIfExtra"] = true;
+  rbuilder["rejectDupKeys"] = false;
+  rbuilder["strictRoot"] = true;
+  this->JsonReader =
+    std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
+
   Json::StreamWriterBuilder wbuilder;
   wbuilder["indentation"] = "\t";
   this->JsonWriter =
@@ -88,6 +96,46 @@ void cmFileAPI::RemoveOldReplyFiles()
   }
 }
 
+bool cmFileAPI::ReadJsonFile(std::string const& file, Json::Value& value,
+                             std::string& error)
+{
+  std::vector<char> content;
+
+  cmsys::ifstream fin;
+  if (!cmSystemTools::FileIsDirectory(file)) {
+    fin.open(file.c_str(), std::ios::binary);
+  }
+  auto finEnd = fin.rdbuf()->pubseekoff(0, std::ios::end);
+  if (finEnd > 0) {
+    size_t finSize = finEnd;
+    try {
+      // Allocate a buffer to read the whole file.
+      content.resize(finSize);
+
+      // Now read the file from the beginning.
+      fin.seekg(0, std::ios::beg);
+      fin.read(content.data(), finSize);
+    } catch (...) {
+      fin.setstate(std::ios::failbit);
+    }
+  }
+  fin.close();
+  if (!fin) {
+    value = Json::Value();
+    error = "failed to read from file";
+    return false;
+  }
+
+  // Parse our buffer as json.
+  if (!this->JsonReader->parse(content.data(), content.data() + content.size(),
+                               &value, &error)) {
+    value = Json::Value();
+    return false;
+  }
+
+  return true;
+}
+
 std::string cmFileAPI::WriteJsonFile(
   Json::Value const& value, std::string const& prefix,
   std::string (*computeSuffix)(std::string const&))
@@ -186,14 +234,38 @@ void cmFileAPI::ReadClient(std::string const& client)
   std::vector<std::string> queries = this->LoadDir(clientDir);
 
   // Read the queries and save for later.
-  Query& clientQuery = this->ClientQueries[client];
+  ClientQuery& clientQuery = this->ClientQueries[client];
   for (std::string& query : queries) {
-    if (!this->ReadQuery(query, clientQuery.Known)) {
-      clientQuery.Unknown.push_back(std::move(query));
+    if (query == "query.json") {
+      clientQuery.HaveQueryJson = true;
+      this->ReadClientQuery(client, clientQuery.QueryJson);
+    } else if (!this->ReadQuery(query, clientQuery.DirQuery.Known)) {
+      clientQuery.DirQuery.Unknown.push_back(std::move(query));
     }
   }
 }
 
+void cmFileAPI::ReadClientQuery(std::string const& client, ClientQueryJson& q)
+{
+  // Read the query.json file.
+  std::string queryFile = this->APIv1 + "/query/" + client + "/query.json";
+  Json::Value query;
+  if (!this->ReadJsonFile(queryFile, query, q.Error)) {
+    return;
+  }
+  if (!query.isObject()) {
+    q.Error = "query root is not an object";
+    return;
+  }
+
+  Json::Value const& clientValue = query["client"];
+  if (!clientValue.isNull()) {
+    q.ClientValue = clientValue;
+  }
+  q.RequestsValue = std::move(query["requests"]);
+  q.Requests = this->BuildClientRequests(q.RequestsValue);
+}
+
 Json::Value cmFileAPI::BuildReplyIndex()
 {
   Json::Value index(Json::objectValue);
@@ -205,8 +277,8 @@ Json::Value cmFileAPI::BuildReplyIndex()
   Json::Value& reply = index["reply"] = this->BuildReply(this->TopQuery);
   for (auto const& client : this->ClientQueries) {
     std::string const& clientName = client.first;
-    Query const& clientQuery = client.second;
-    reply[clientName] = this->BuildReply(clientQuery);
+    ClientQuery const& clientQuery = client.second;
+    reply[clientName] = this->BuildClientReply(clientQuery);
   }
 
   // Move our index of generated objects into its field.
@@ -301,11 +373,232 @@ Json::Value cmFileAPI::BuildObject(Object const& object)
   return value;
 }
 
+cmFileAPI::ClientRequests cmFileAPI::BuildClientRequests(
+  Json::Value const& requests)
+{
+  ClientRequests result;
+  if (requests.isNull()) {
+    result.Error = "'requests' member missing";
+    return result;
+  }
+  if (!requests.isArray()) {
+    result.Error = "'requests' member is not an array";
+    return result;
+  }
+
+  result.reserve(requests.size());
+  for (Json::Value const& request : requests) {
+    result.emplace_back(this->BuildClientRequest(request));
+  }
+
+  return result;
+}
+
+cmFileAPI::ClientRequest cmFileAPI::BuildClientRequest(
+  Json::Value const& request)
+{
+  ClientRequest r;
+
+  if (!request.isObject()) {
+    r.Error = "request is not an object";
+    return r;
+  }
+
+  Json::Value const& kind = request["kind"];
+  if (kind.isNull()) {
+    r.Error = "'kind' member missing";
+    return r;
+  }
+  if (!kind.isString()) {
+    r.Error = "'kind' member is not a string";
+    return r;
+  }
+  std::string const& kindName = kind.asString();
+
+  if (kindName == this->ObjectKindName(ObjectKind::InternalTest)) {
+    r.Kind = ObjectKind::InternalTest;
+  } else {
+    r.Error = "unknown request kind '" + kindName + "'";
+    return r;
+  }
+
+  Json::Value const& version = request["version"];
+  if (version.isNull()) {
+    r.Error = "'version' member missing";
+    return r;
+  }
+  std::vector<RequestVersion> versions;
+  if (!cmFileAPI::ReadRequestVersions(version, versions, r.Error)) {
+    return r;
+  }
+
+  switch (r.Kind) {
+    case ObjectKind::InternalTest:
+      this->BuildClientRequestInternalTest(r, versions);
+      break;
+  }
+
+  return r;
+}
+
+Json::Value cmFileAPI::BuildClientReply(ClientQuery const& q)
+{
+  Json::Value reply = this->BuildReply(q.DirQuery);
+
+  if (!q.HaveQueryJson) {
+    return reply;
+  }
+
+  Json::Value& reply_query_json = reply["query.json"];
+  ClientQueryJson const& qj = q.QueryJson;
+
+  if (!qj.Error.empty()) {
+    reply_query_json = this->BuildReplyError(qj.Error);
+    return reply;
+  }
+
+  if (!qj.ClientValue.isNull()) {
+    reply_query_json["client"] = qj.ClientValue;
+  }
+
+  if (!qj.RequestsValue.isNull()) {
+    reply_query_json["requests"] = qj.RequestsValue;
+  }
+
+  reply_query_json["responses"] = this->BuildClientReplyResponses(qj.Requests);
+
+  return reply;
+}
+
+Json::Value cmFileAPI::BuildClientReplyResponses(
+  ClientRequests const& requests)
+{
+  Json::Value responses;
+
+  if (!requests.Error.empty()) {
+    responses = this->BuildReplyError(requests.Error);
+    return responses;
+  }
+
+  responses = Json::arrayValue;
+  for (ClientRequest const& request : requests) {
+    responses.append(this->BuildClientReplyResponse(request));
+  }
+
+  return responses;
+}
+
+Json::Value cmFileAPI::BuildClientReplyResponse(ClientRequest const& request)
+{
+  Json::Value response;
+  if (!request.Error.empty()) {
+    response = this->BuildReplyError(request.Error);
+    return response;
+  }
+  response = this->AddReplyIndexObject(request);
+  return response;
+}
+
+bool cmFileAPI::ReadRequestVersions(Json::Value const& version,
+                                    std::vector<RequestVersion>& versions,
+                                    std::string& error)
+{
+  if (version.isArray()) {
+    for (Json::Value const& v : version) {
+      if (!ReadRequestVersion(v, /*inArray=*/true, versions, error)) {
+        return false;
+      }
+    }
+  } else {
+    if (!ReadRequestVersion(version, /*inArray=*/false, versions, error)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool cmFileAPI::ReadRequestVersion(Json::Value const& version, bool inArray,
+                                   std::vector<RequestVersion>& result,
+                                   std::string& error)
+{
+  if (version.isUInt()) {
+    RequestVersion v;
+    v.Major = version.asUInt();
+    result.push_back(v);
+    return true;
+  }
+
+  if (!version.isObject()) {
+    if (inArray) {
+      error = "'version' array entry is not a non-negative integer or object";
+    } else {
+      error =
+        "'version' member is not a non-negative integer, object, or array";
+    }
+    return false;
+  }
+
+  Json::Value const& major = version["major"];
+  if (major.isNull()) {
+    error = "'version' object 'major' member missing";
+    return false;
+  }
+  if (!major.isUInt()) {
+    error = "'version' object 'major' member is not a non-negative integer";
+    return false;
+  }
+
+  RequestVersion v;
+  v.Major = major.asUInt();
+
+  Json::Value const& minor = version["minor"];
+  if (minor.isUInt()) {
+    v.Minor = minor.asUInt();
+  } else if (!minor.isNull()) {
+    error = "'version' object 'minor' member is not a non-negative integer";
+    return false;
+  }
+
+  result.push_back(v);
+
+  return true;
+}
+
+std::string cmFileAPI::NoSupportedVersion(
+  std::vector<RequestVersion> const& versions)
+{
+  std::ostringstream msg;
+  msg << "no supported version specified";
+  if (!versions.empty()) {
+    msg << " among:";
+    for (RequestVersion const& v : versions) {
+      msg << " " << v.Major << "." << v.Minor;
+    }
+  }
+  return msg.str();
+}
+
 // The "__test" object kind is for internal testing of CMake.
 
 static unsigned int const InternalTestV1Minor = 3;
 static unsigned int const InternalTestV2Minor = 0;
 
+void cmFileAPI::BuildClientRequestInternalTest(
+  ClientRequest& r, std::vector<RequestVersion> const& versions)
+{
+  // Select a known version from those requested.
+  for (RequestVersion const& v : versions) {
+    if ((v.Major == 1 && v.Minor <= InternalTestV1Minor) || //
+        (v.Major == 2 && v.Minor <= InternalTestV2Minor)) {
+      r.Version = v.Major;
+      break;
+    }
+  }
+  if (!r.Version) {
+    r.Error = NoSupportedVersion(versions);
+  }
+}
+
 Json::Value cmFileAPI::BuildInternalTest(Object const& object)
 {
   Json::Value test = Json::objectValue;
diff --git a/Source/cmFileAPI.h b/Source/cmFileAPI.h
index 589b837..4f4506f 100644
--- a/Source/cmFileAPI.h
+++ b/Source/cmFileAPI.h
@@ -5,6 +5,7 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include "cm_jsoncpp_reader.h"
 #include "cm_jsoncpp_value.h"
 #include "cm_jsoncpp_writer.h"
 
@@ -71,6 +72,49 @@ private:
     std::vector<std::string> Unknown;
   };
 
+  /** Represent one request in a client 'query.json'.  */
+  struct ClientRequest : public Object
+  {
+    /** Empty if request is valid, else the error string.  */
+    std::string Error;
+  };
+
+  /** Represent the "requests" in a client 'query.json'.  */
+  struct ClientRequests : public std::vector<ClientRequest>
+  {
+    /** Empty if requests field is valid, else the error string.  */
+    std::string Error;
+  };
+
+  /** Represent the content of a client query.json file.  */
+  struct ClientQueryJson
+  {
+    /** The error string if parsing failed, else empty.  */
+    std::string Error;
+
+    /** The 'query.json' object "client" member if it exists, else null.  */
+    Json::Value ClientValue;
+
+    /** The 'query.json' object "requests" member if it exists, else null.  */
+    Json::Value RequestsValue;
+
+    /** Requests extracted from 'query.json'.  */
+    ClientRequests Requests;
+  };
+
+  /** Represent content of a client query directory.  */
+  struct ClientQuery
+  {
+    /** The content of the client query directory except 'query.json'.  */
+    Query DirQuery;
+
+    /** True if 'query.json' exists.  */
+    bool HaveQueryJson = false;
+
+    /** The 'query.json' content.  */
+    ClientQueryJson QueryJson;
+  };
+
   /** Whether the top-level query directory exists at all.  */
   bool QueryExists = false;
 
@@ -78,14 +122,18 @@ private:
   Query TopQuery;
 
   /** The content of each "client-$client" query directory.  */
-  std::map<std::string, Query> ClientQueries;
+  std::map<std::string, ClientQuery> ClientQueries;
 
   /** Reply index object generated for object kind/version.
       This populates the "objects" field of the reply index.  */
   std::map<Object, Json::Value> ReplyIndexObjects;
 
+  std::unique_ptr<Json::CharReader> JsonReader;
   std::unique_ptr<Json::StreamWriter> JsonWriter;
 
+  bool ReadJsonFile(std::string const& file, Json::Value& value,
+                    std::string& error);
+
   std::string WriteJsonFile(
     Json::Value const& value, std::string const& prefix,
     std::string (*computeSuffix)(std::string const&) = ComputeSuffixHash);
@@ -95,6 +143,7 @@ private:
   static bool ReadQuery(std::string const& query,
                         std::vector<Object>& objects);
   void ReadClient(std::string const& client);
+  void ReadClientQuery(std::string const& client, ClientQueryJson& q);
 
   Json::Value BuildReplyIndex();
   Json::Value BuildCMake();
@@ -107,6 +156,28 @@ private:
 
   Json::Value BuildObject(Object const& object);
 
+  ClientRequests BuildClientRequests(Json::Value const& requests);
+  ClientRequest BuildClientRequest(Json::Value const& request);
+  Json::Value BuildClientReply(ClientQuery const& q);
+  Json::Value BuildClientReplyResponses(ClientRequests const& requests);
+  Json::Value BuildClientReplyResponse(ClientRequest const& request);
+
+  struct RequestVersion
+  {
+    unsigned int Major = 0;
+    unsigned int Minor = 0;
+  };
+  static bool ReadRequestVersions(Json::Value const& version,
+                                  std::vector<RequestVersion>& versions,
+                                  std::string& error);
+  static bool ReadRequestVersion(Json::Value const& version, bool inArray,
+                                 std::vector<RequestVersion>& result,
+                                 std::string& error);
+  static std::string NoSupportedVersion(
+    std::vector<RequestVersion> const& versions);
+
+  void BuildClientRequestInternalTest(
+    ClientRequest& r, std::vector<RequestVersion> const& versions);
   Json::Value BuildInternalTest(Object const& object);
 };
 
diff --git a/Tests/RunCMake/FileAPI/ClientStateful-check.cmake b/Tests/RunCMake/FileAPI/ClientStateful-check.cmake
new file mode 100644
index 0000000..1e9aab6
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/ClientStateful-check.cmake
@@ -0,0 +1,68 @@
+set(expect
+  query
+  query/client-client-member
+  query/client-client-member/query.json
+  query/client-empty-array
+  query/client-empty-array/query.json
+  query/client-empty-object
+  query/client-empty-object/query.json
+  query/client-json-bad-root
+  query/client-json-bad-root/query.json
+  query/client-json-empty
+  query/client-json-empty/query.json
+  query/client-json-extra
+  query/client-json-extra/query.json
+  query/client-not-file
+  query/client-not-file/query.json
+  query/client-request-array-negative-major-version
+  query/client-request-array-negative-major-version/query.json
+  query/client-request-array-negative-minor-version
+  query/client-request-array-negative-minor-version/query.json
+  query/client-request-array-negative-version
+  query/client-request-array-negative-version/query.json
+  query/client-request-array-no-major-version
+  query/client-request-array-no-major-version/query.json
+  query/client-request-array-no-supported-version
+  query/client-request-array-no-supported-version-among
+  query/client-request-array-no-supported-version-among/query.json
+  query/client-request-array-no-supported-version/query.json
+  query/client-request-array-version-1
+  query/client-request-array-version-1-1
+  query/client-request-array-version-1-1/query.json
+  query/client-request-array-version-1/query.json
+  query/client-request-array-version-2
+  query/client-request-array-version-2/query.json
+  query/client-request-negative-major-version
+  query/client-request-negative-major-version/query.json
+  query/client-request-negative-minor-version
+  query/client-request-negative-minor-version/query.json
+  query/client-request-negative-version
+  query/client-request-negative-version/query.json
+  query/client-request-no-major-version
+  query/client-request-no-major-version/query.json
+  query/client-request-no-version
+  query/client-request-no-version/query.json
+  query/client-request-version-1
+  query/client-request-version-1-1
+  query/client-request-version-1-1/query.json
+  query/client-request-version-1/query.json
+  query/client-request-version-2
+  query/client-request-version-2/query.json
+  query/client-requests-bad
+  query/client-requests-bad/query.json
+  query/client-requests-empty
+  query/client-requests-empty/query.json
+  query/client-requests-not-kinded
+  query/client-requests-not-kinded/query.json
+  query/client-requests-not-objects
+  query/client-requests-not-objects/query.json
+  query/client-requests-unknown
+  query/client-requests-unknown/query.json
+  reply
+  reply/__test-v1-[0-9a-f]+.json
+  reply/__test-v2-[0-9a-f]+.json
+  reply/index-[0-9.T-]+.json
+  )
+check_api("^${expect}$")
+
+check_python(ClientStateful)
diff --git a/Tests/RunCMake/FileAPI/ClientStateful-check.py b/Tests/RunCMake/FileAPI/ClientStateful-check.py
new file mode 100644
index 0000000..f3d20d1
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/ClientStateful-check.py
@@ -0,0 +1,258 @@
+from check_index import *
+
+def check_reply(q):
+    assert is_dict(q)
+    assert sorted(q.keys()) == [
+        "client-client-member",
+        "client-empty-array",
+        "client-empty-object",
+        "client-json-bad-root",
+        "client-json-empty",
+        "client-json-extra",
+        "client-not-file",
+        "client-request-array-negative-major-version",
+        "client-request-array-negative-minor-version",
+        "client-request-array-negative-version",
+        "client-request-array-no-major-version",
+        "client-request-array-no-supported-version",
+        "client-request-array-no-supported-version-among",
+        "client-request-array-version-1",
+        "client-request-array-version-1-1",
+        "client-request-array-version-2",
+        "client-request-negative-major-version",
+        "client-request-negative-minor-version",
+        "client-request-negative-version",
+        "client-request-no-major-version",
+        "client-request-no-version",
+        "client-request-version-1",
+        "client-request-version-1-1",
+        "client-request-version-2",
+        "client-requests-bad",
+        "client-requests-empty",
+        "client-requests-not-kinded",
+        "client-requests-not-objects",
+        "client-requests-unknown",
+    ]
+    expected = [
+        (check_query_client_member, "client-client-member"),
+        (check_query_empty_array, "client-empty-array"),
+        (check_query_empty_object, "client-empty-object"),
+        (check_query_json_bad_root, "client-json-bad-root"),
+        (check_query_json_empty, "client-json-empty"),
+        (check_query_json_extra, "client-json-extra"),
+        (check_query_not_file, "client-not-file"),
+        (check_query_requests_bad, "client-requests-bad"),
+        (check_query_requests_empty, "client-requests-empty"),
+        (check_query_requests_not_kinded, "client-requests-not-kinded"),
+        (check_query_requests_not_objects, "client-requests-not-objects"),
+        (check_query_requests_unknown, "client-requests-unknown"),
+    ]
+    for (f, k) in expected:
+        assert is_dict(q[k])
+        assert sorted(q[k].keys()) == ["query.json"]
+        f(q[k]["query.json"])
+    expected = [
+        (check_query_response_array_negative_major_version,     "client-request-array-negative-major-version"),
+        (check_query_response_array_negative_minor_version,     "client-request-array-negative-minor-version"),
+        (check_query_response_array_negative_version,           "client-request-array-negative-version"),
+        (check_query_response_array_no_major_version,           "client-request-array-no-major-version"),
+        (check_query_response_array_no_supported_version,       "client-request-array-no-supported-version"),
+        (check_query_response_array_no_supported_version_among, "client-request-array-no-supported-version-among"),
+        (check_query_response_array_version_1,                  "client-request-array-version-1"),
+        (check_query_response_array_version_1_1,                "client-request-array-version-1-1"),
+        (check_query_response_array_version_2,                  "client-request-array-version-2"),
+        (check_query_response_negative_major_version,           "client-request-negative-major-version"),
+        (check_query_response_negative_minor_version,           "client-request-negative-minor-version"),
+        (check_query_response_negative_version,                 "client-request-negative-version"),
+        (check_query_response_no_major_version,                 "client-request-no-major-version"),
+        (check_query_response_no_version,                       "client-request-no-version"),
+        (check_query_response_version_1,                        "client-request-version-1"),
+        (check_query_response_version_1_1,                      "client-request-version-1-1"),
+        (check_query_response_version_2,                        "client-request-version-2"),
+    ]
+    for (f, k) in expected:
+        assert is_dict(q[k])
+        assert sorted(q[k].keys()) == ["query.json"]
+        assert is_dict(q[k]["query.json"])
+        assert sorted(q[k]["query.json"].keys()) == ["requests", "responses"]
+        r = q[k]["query.json"]["requests"]
+        assert is_list(r)
+        assert len(r) == 1
+        assert is_dict(r[0])
+        assert r[0]["kind"] == "__test"
+        r = q[k]["query.json"]["responses"]
+        assert is_list(r)
+        assert len(r) == 1
+        assert is_dict(r[0])
+        f(r[0])
+
+def check_query_client_member(q):
+    assert is_dict(q)
+    assert sorted(q.keys()) == ["client", "responses"]
+    assert is_dict(q["client"])
+    assert sorted(q["client"].keys()) == []
+    check_error(q["responses"], "'requests' member missing")
+
+def check_query_empty_array(q):
+    check_error(q, "query root is not an object")
+
+def check_query_empty_object(q):
+    assert is_dict(q)
+    assert sorted(q.keys()) == ["responses"]
+    check_error(q["responses"], "'requests' member missing")
+
+def check_query_json_bad_root(q):
+    check_error_re(q, "A valid JSON document must be either an array or an object value")
+
+def check_query_json_empty(q):
+    check_error_re(q, "value, object or array expected")
+
+def check_query_json_extra(q):
+    check_error_re(q, "Extra non-whitespace after JSON value")
+
+def check_query_not_file(q):
+    check_error_re(q, "failed to read from file")
+
+def check_query_requests_bad(q):
+    assert is_dict(q)
+    assert sorted(q.keys()) == ["requests", "responses"]
+    r = q["requests"]
+    assert is_dict(r)
+    assert sorted(r.keys()) == []
+    check_error(q["responses"], "'requests' member is not an array")
+
+def check_query_requests_empty(q):
+    assert is_dict(q)
+    assert sorted(q.keys()) == ["requests", "responses"]
+    r = q["requests"]
+    assert is_list(r)
+    assert len(r) == 0
+    r = q["responses"]
+    assert is_list(r)
+    assert len(r) == 0
+
+def check_query_requests_not_kinded(q):
+    assert is_dict(q)
+    assert sorted(q.keys()) == ["requests", "responses"]
+    r = q["requests"]
+    assert is_list(r)
+    assert len(r) == 4
+    assert is_dict(r[0])
+    assert sorted(r[0].keys()) == []
+    assert is_dict(r[1])
+    assert sorted(r[1].keys()) == ["kind"]
+    assert is_dict(r[1]["kind"])
+    assert is_dict(r[2])
+    assert sorted(r[2].keys()) == ["kind"]
+    assert is_list(r[2]["kind"])
+    assert is_dict(r[3])
+    assert sorted(r[3].keys()) == ["kind"]
+    assert is_int(r[3]["kind"])
+    r = q["responses"]
+    assert is_list(r)
+    assert len(r) == 4
+    check_error(r[0], "'kind' member missing")
+    check_error(r[1], "'kind' member is not a string")
+    check_error(r[2], "'kind' member is not a string")
+    check_error(r[3], "'kind' member is not a string")
+
+def check_query_requests_not_objects(q):
+    assert is_dict(q)
+    assert sorted(q.keys()) == ["requests", "responses"]
+    r = q["requests"]
+    assert is_list(r)
+    assert len(r) == 3
+    assert is_int(r[0])
+    assert is_string(r[1])
+    assert is_list(r[2])
+    r = q["responses"]
+    assert is_list(r)
+    assert len(r) == 3
+    check_error(r[0], "request is not an object")
+    check_error(r[1], "request is not an object")
+    check_error(r[2], "request is not an object")
+
+def check_query_requests_unknown(q):
+    assert is_dict(q)
+    assert sorted(q.keys()) == ["requests", "responses"]
+    r = q["requests"]
+    assert is_list(r)
+    assert len(r) == 3
+    assert is_dict(r[0])
+    assert sorted(r[0].keys()) == ["kind"]
+    assert r[0]["kind"] == "unknownC"
+    assert is_dict(r[1])
+    assert sorted(r[1].keys()) == ["kind"]
+    assert r[1]["kind"] == "unknownB"
+    assert is_dict(r[2])
+    assert sorted(r[2].keys()) == ["kind"]
+    assert r[2]["kind"] == "unknownA"
+    r = q["responses"]
+    assert is_list(r)
+    assert len(r) == 3
+    check_error(r[0], "unknown request kind 'unknownC'")
+    check_error(r[1], "unknown request kind 'unknownB'")
+    check_error(r[2], "unknown request kind 'unknownA'")
+
+def check_query_response_array_negative_major_version(r):
+    check_error(r, "'version' object 'major' member is not a non-negative integer")
+
+def check_query_response_array_negative_minor_version(r):
+    check_error(r, "'version' object 'minor' member is not a non-negative integer")
+
+def check_query_response_array_negative_version(r):
+    check_error(r, "'version' array entry is not a non-negative integer or object")
+
+def check_query_response_array_no_major_version(r):
+    check_error(r, "'version' object 'major' member missing")
+
+def check_query_response_array_no_supported_version(r):
+    check_error(r, "no supported version specified")
+
+def check_query_response_array_no_supported_version_among(r):
+    check_error(r, "no supported version specified among: 4.0 3.0")
+
+def check_query_response_array_version_1(r):
+    check_index__test(r, 1, 3)
+
+def check_query_response_array_version_1_1(r):
+    check_index__test(r, 1, 3) # always uses latest minor version
+
+def check_query_response_array_version_2(r):
+    check_index__test(r, 2, 0)
+
+def check_query_response_negative_major_version(r):
+    check_error(r, "'version' object 'major' member is not a non-negative integer")
+
+def check_query_response_negative_minor_version(r):
+    check_error(r, "'version' object 'minor' member is not a non-negative integer")
+
+def check_query_response_negative_version(r):
+    check_error(r, "'version' member is not a non-negative integer, object, or array")
+
+def check_query_response_no_major_version(r):
+    check_error(r, "'version' object 'major' member missing")
+
+def check_query_response_no_version(r):
+    check_error(r, "'version' member missing")
+
+def check_query_response_version_1(r):
+    check_index__test(r, 1, 3)
+
+def check_query_response_version_1_1(r):
+    check_index__test(r, 1, 3) # always uses latest minor version
+
+def check_query_response_version_2(r):
+    check_index__test(r, 2, 0)
+
+def check_objects(o):
+    assert is_list(o)
+    assert len(o) == 2
+    check_index__test(o[0], 1, 3)
+    check_index__test(o[1], 2, 0)
+
+assert is_dict(index)
+assert sorted(index.keys()) == ["cmake", "objects", "reply"]
+check_cmake(index["cmake"])
+check_reply(index["reply"])
+check_objects(index["objects"])
diff --git a/Tests/RunCMake/FileAPI/ClientStateful-prep.cmake b/Tests/RunCMake/FileAPI/ClientStateful-prep.cmake
new file mode 100644
index 0000000..5b41d7a
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/ClientStateful-prep.cmake
@@ -0,0 +1,73 @@
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-client-member/query.json" [[{ "client": {} }]])
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-empty-array/query.json" "[]")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-empty-object/query.json" "{}")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-json-bad-root/query.json" [["invalid root"]])
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-json-empty/query.json" "")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-json-extra/query.json" "{}x")
+file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-not-file/query.json")
+
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-requests-bad/query.json" [[{ "requests": {} }]])
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-requests-empty/query.json" [[{ "requests": [] }]])
+
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-requests-not-objects/query.json" [[
+{ "requests": [ 0, "", [] ] }
+]])
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-requests-not-kinded/query.json" [[
+{ "requests": [ {}, { "kind": {} }, { "kind": [] }, { "kind": 0 } ] }
+]])
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-requests-unknown/query.json" [[
+{ "requests": [ { "kind": "unknownC" }, { "kind": "unknownB" }, { "kind": "unknownA" } ] }
+]])
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-no-version/query.json" [[
+{ "requests": [ { "kind": "__test" } ] }
+]])
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-negative-version/query.json" [[
+{ "requests": [ { "kind": "__test", "version" : -1 } ] }
+]])
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-no-major-version/query.json" [[
+{ "requests": [ { "kind": "__test", "version" : {} } ] }
+]])
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-negative-major-version/query.json" [[
+{ "requests": [ { "kind": "__test", "version" : { "major": -1 } } ] }
+]])
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-negative-minor-version/query.json" [[
+{ "requests": [ { "kind": "__test", "version" : { "major": 0, "minor": -1 } } ] }
+]])
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-array-negative-version/query.json" [[
+{ "requests": [ { "kind": "__test", "version" : [ 1, -1 ] } ] }
+]])
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-array-no-major-version/query.json" [[
+{ "requests": [ { "kind": "__test", "version" : [ 1, {} ] } ] }
+]])
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-array-negative-major-version/query.json" [[
+{ "requests": [ { "kind": "__test", "version" : [ 1, { "major": -1 } ] } ] }
+]])
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-array-negative-minor-version/query.json" [[
+{ "requests": [ { "kind": "__test", "version" : [ 1, { "major": 0, "minor": -1 } ] } ] }
+]])
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-array-no-supported-version/query.json" [[
+{ "requests": [ { "kind": "__test", "version" : [] } ] }
+]])
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-array-no-supported-version-among/query.json" [[
+{ "requests": [ { "kind": "__test", "version" : [4, 3] } ] }
+]])
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-version-1/query.json" [[
+{ "requests": [ { "kind": "__test", "version" : 1 } ] }
+]])
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-version-1-1/query.json" [[
+{ "requests": [ { "kind": "__test", "version" : { "major": 1, "minor": 1 } } ] }
+]])
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-version-2/query.json" [[
+{ "requests": [ { "kind": "__test", "version" : { "major": 2 } } ] }
+]])
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-array-version-1/query.json" [[
+{ "requests": [ { "kind": "__test", "version" : [3, 1] } ] }
+]])
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-array-version-1-1/query.json" [[
+{ "requests": [ { "kind": "__test", "version" : [3, { "major": 1, "minor": 1 }, 2 ] } ] }
+]])
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-array-version-2/query.json" [[
+{ "requests": [ { "kind": "__test", "version" : [3, { "major": 2 } ]  } ] }
+]])
+
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/reply/object-to-be-deleted.json" "")
diff --git a/Tests/RunCMake/FileAPI/ClientStateful.cmake b/Tests/RunCMake/FileAPI/ClientStateful.cmake
new file mode 100644
index 0000000..e69de29
diff --git a/Tests/RunCMake/FileAPI/RunCMakeTest.cmake b/Tests/RunCMake/FileAPI/RunCMakeTest.cmake
index a7351b3..1cc10ee 100644
--- a/Tests/RunCMake/FileAPI/RunCMakeTest.cmake
+++ b/Tests/RunCMake/FileAPI/RunCMakeTest.cmake
@@ -42,3 +42,4 @@ run_cmake(SharedStateless)
 run_cmake(ClientStateless)
 run_cmake(MixedStateless)
 run_cmake(DuplicateStateless)
+run_cmake(ClientStateful)
diff --git a/Tests/RunCMake/FileAPI/check_index.py b/Tests/RunCMake/FileAPI/check_index.py
index 6cc16a5..4d90d59 100644
--- a/Tests/RunCMake/FileAPI/check_index.py
+++ b/Tests/RunCMake/FileAPI/check_index.py
@@ -81,6 +81,12 @@ def check_error(value, error):
     assert is_string(value["error"])
     assert value["error"] == error
 
+def check_error_re(value, error):
+    assert is_dict(value)
+    assert sorted(value.keys()) == ["error"]
+    assert is_string(value["error"])
+    assert re.search(error, value["error"])
+
 reply_index = sys.argv[1]
 reply_dir = os.path.dirname(reply_index)
 

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=8fce59848b52f71ae310fcd64fcf943fb2c42bf6
commit 8fce59848b52f71ae310fcd64fcf943fb2c42bf6
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Thu Sep 13 08:48:29 2018 -0400
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Wed Dec 12 06:40:10 2018 -0500

    fileapi: Add protocol v1 support for client-specific query files
    
    Add support for client-owned stateless query files.  These allow clients
    to *own* requests for major object versions and get all those recognized
    by CMake.
    
    Issue: #18398

diff --git a/Help/manual/cmake-file-api.7.rst b/Help/manual/cmake-file-api.7.rst
index dd2ef83..51655b2 100644
--- a/Help/manual/cmake-file-api.7.rst
+++ b/Help/manual/cmake-file-api.7.rst
@@ -31,7 +31,8 @@ It has the following subdirectories:
 
 ``query/``
   Holds query files written by clients.
-  These may be `v1 Shared Stateless Query Files`_.
+  These may be `v1 Shared Stateless Query Files`_ or
+  `v1 Client Stateless Query Files`_.
 
 ``reply/``
   Holds reply files written by CMake whenever it runs to generate a build
@@ -62,6 +63,27 @@ Files of this form are stateless shared queries not owned by any specific
 client.  Once created they should not be removed without external client
 coordination or human intervention.
 
+v1 Client Stateless Query Files
+-------------------------------
+
+Client stateless query files allow clients to create owned requests for
+major versions of the `Object Kinds`_ and get all requested versions
+recognized by the CMake that runs.
+
+Clients may create owned requests by creating empty files in
+client-specific query subdirectories.  The form is::
+
+  <build>/.cmake/api/v1/query/client-<client>/<kind>-v<major>
+
+where ``client-`` is literal, ``<client>`` is a string uniquely
+identifying the client, ``<kind>`` is one of the `Object Kinds`_,
+``-v`` is literal, and ``<major>`` is the major version number.
+Each client must choose a unique ``<client>`` identifier via its
+own means.
+
+Files of this form are stateless queries owned by the client ``<client>``.
+The owning client may remove them at any time.
+
 v1 Reply Index File
 -------------------
 
@@ -107,7 +129,14 @@ The reply index file contains a JSON object:
                            "version": { "major": 1, "minor": 0 },
                            "jsonFile": "<file>" },
       "<unknown>": { "error": "unknown query file" },
-      "...": {}
+      "...": {},
+      "client-<client>": {
+        "<kind>-v<major>": { "kind": "<kind>",
+                             "version": { "major": 1, "minor": 0 },
+                             "jsonFile": "<file>" },
+        "<unknown>": { "error": "unknown query file" },
+        "...": {}
+      }
     }
   }
 
@@ -162,6 +191,26 @@ The members are:
     containing a string with an error message indicating that the
     query file is unknown.
 
+  ``client-<client>``
+    A member of this form appears for each client-owned directory
+    holding `v1 Client Stateless Query Files`_.
+    The value is a JSON object mirroring the content of the
+    ``query/client-<client>/`` directory.  The members are of the form:
+
+    ``<kind>-v<major>``
+      A member of this form appears for each of the
+      `v1 Client Stateless Query Files`_ that CMake recognized as a
+      request for object kind ``<kind>`` with major version ``<major>``.
+      The value is a `v1 Reply File Reference`_ to the corresponding
+      reply file for that object kind and version.
+
+    ``<unknown>``
+      A member of this form appears for each of the
+      `v1 Client Stateless Query Files`_ that CMake did not recognize.
+      The value is a JSON object with a single ``error`` member
+      containing a string with an error message indicating that the
+      query file is unknown.
+
 After reading the reply index file, clients may read the other
 `v1 Reply Files`_ it references.
 
diff --git a/Source/cmFileAPI.cxx b/Source/cmFileAPI.cxx
index 23e0ced..e401b58 100644
--- a/Source/cmFileAPI.cxx
+++ b/Source/cmFileAPI.cxx
@@ -2,6 +2,7 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmFileAPI.h"
 
+#include "cmAlgorithms.h"
 #include "cmCryptoHash.h"
 #include "cmSystemTools.h"
 #include "cmTimestamp.h"
@@ -42,7 +43,9 @@ void cmFileAPI::ReadQueries()
 
   // Read the queries and save for later.
   for (std::string& query : queries) {
-    if (!cmFileAPI::ReadQuery(query, this->TopQuery.Known)) {
+    if (cmHasLiteralPrefix(query, "client-")) {
+      this->ReadClient(query);
+    } else if (!cmFileAPI::ReadQuery(query, this->TopQuery.Known)) {
       this->TopQuery.Unknown.push_back(std::move(query));
     }
   }
@@ -176,6 +179,21 @@ bool cmFileAPI::ReadQuery(std::string const& query,
   return false;
 }
 
+void cmFileAPI::ReadClient(std::string const& client)
+{
+  // Load queries for the client.
+  std::string clientDir = this->APIv1 + "/query/" + client;
+  std::vector<std::string> queries = this->LoadDir(clientDir);
+
+  // Read the queries and save for later.
+  Query& clientQuery = this->ClientQueries[client];
+  for (std::string& query : queries) {
+    if (!this->ReadQuery(query, clientQuery.Known)) {
+      clientQuery.Unknown.push_back(std::move(query));
+    }
+  }
+}
+
 Json::Value cmFileAPI::BuildReplyIndex()
 {
   Json::Value index(Json::objectValue);
@@ -184,7 +202,12 @@ Json::Value cmFileAPI::BuildReplyIndex()
   index["cmake"] = this->BuildCMake();
 
   // Reply to all queries that we loaded.
-  index["reply"] = this->BuildReply(this->TopQuery);
+  Json::Value& reply = index["reply"] = this->BuildReply(this->TopQuery);
+  for (auto const& client : this->ClientQueries) {
+    std::string const& clientName = client.first;
+    Query const& clientQuery = client.second;
+    reply[clientName] = this->BuildReply(clientQuery);
+  }
 
   // Move our index of generated objects into its field.
   Json::Value& objects = index["objects"] = Json::arrayValue;
diff --git a/Source/cmFileAPI.h b/Source/cmFileAPI.h
index 39b054d..589b837 100644
--- a/Source/cmFileAPI.h
+++ b/Source/cmFileAPI.h
@@ -77,6 +77,9 @@ private:
   /** The content of the top-level query directory.  */
   Query TopQuery;
 
+  /** The content of each "client-$client" query directory.  */
+  std::map<std::string, Query> ClientQueries;
+
   /** Reply index object generated for object kind/version.
       This populates the "objects" field of the reply index.  */
   std::map<Object, Json::Value> ReplyIndexObjects;
@@ -91,6 +94,7 @@ private:
 
   static bool ReadQuery(std::string const& query,
                         std::vector<Object>& objects);
+  void ReadClient(std::string const& client);
 
   Json::Value BuildReplyIndex();
   Json::Value BuildCMake();
diff --git a/Tests/RunCMake/FileAPI/ClientStateless-check.cmake b/Tests/RunCMake/FileAPI/ClientStateless-check.cmake
new file mode 100644
index 0000000..955d9be
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/ClientStateless-check.cmake
@@ -0,0 +1,15 @@
+set(expect
+  query
+  query/client-foo
+  query/client-foo/__test-v1
+  query/client-foo/__test-v2
+  query/client-foo/__test-v3
+  query/client-foo/unknown
+  reply
+  reply/__test-v1-[0-9a-f]+.json
+  reply/__test-v2-[0-9a-f]+.json
+  reply/index-[0-9.T-]+.json
+  )
+check_api("^${expect}$")
+
+check_python(ClientStateless)
diff --git a/Tests/RunCMake/FileAPI/ClientStateless-check.py b/Tests/RunCMake/FileAPI/ClientStateless-check.py
new file mode 100644
index 0000000..b7da314
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/ClientStateless-check.py
@@ -0,0 +1,26 @@
+from check_index import *
+
+def check_reply(r):
+    assert is_dict(r)
+    assert sorted(r.keys()) == ["client-foo"]
+    check_reply_client_foo(r["client-foo"])
+
+def check_reply_client_foo(r):
+    assert is_dict(r)
+    assert sorted(r.keys()) == ["__test-v1", "__test-v2", "__test-v3", "unknown"]
+    check_index__test(r["__test-v1"], 1, 3)
+    check_index__test(r["__test-v2"], 2, 0)
+    check_error(r["__test-v3"], "unknown query file")
+    check_error(r["unknown"], "unknown query file")
+
+def check_objects(o):
+    assert is_list(o)
+    assert len(o) == 2
+    check_index__test(o[0], 1, 3)
+    check_index__test(o[1], 2, 0)
+
+assert is_dict(index)
+assert sorted(index.keys()) == ["cmake", "objects", "reply"]
+check_cmake(index["cmake"])
+check_reply(index["reply"])
+check_objects(index["objects"])
diff --git a/Tests/RunCMake/FileAPI/ClientStateless-prep.cmake b/Tests/RunCMake/FileAPI/ClientStateless-prep.cmake
new file mode 100644
index 0000000..1b8d772
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/ClientStateless-prep.cmake
@@ -0,0 +1,5 @@
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/__test-v1" "")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/__test-v2" "")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/__test-v3" "")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/unknown" "")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/reply/object-to-be-deleted.json" "")
diff --git a/Tests/RunCMake/FileAPI/ClientStateless.cmake b/Tests/RunCMake/FileAPI/ClientStateless.cmake
new file mode 100644
index 0000000..e69de29
diff --git a/Tests/RunCMake/FileAPI/DuplicateStateless-check.cmake b/Tests/RunCMake/FileAPI/DuplicateStateless-check.cmake
new file mode 100644
index 0000000..4959c1e
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/DuplicateStateless-check.cmake
@@ -0,0 +1,20 @@
+set(expect
+  query
+  query/__test-v1
+  query/__test-v2
+  query/__test-v3
+  query/client-foo
+  query/client-foo/__test-v1
+  query/client-foo/__test-v2
+  query/client-foo/__test-v3
+  query/client-foo/unknown
+  query/query.json
+  query/unknown
+  reply
+  reply/__test-v1-[0-9a-f]+.json
+  reply/__test-v2-[0-9a-f]+.json
+  reply/index-[0-9.T-]+.json
+  )
+check_api("^${expect}$")
+
+check_python(DuplicateStateless)
diff --git a/Tests/RunCMake/FileAPI/DuplicateStateless-check.py b/Tests/RunCMake/FileAPI/DuplicateStateless-check.py
new file mode 100644
index 0000000..3335479
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/DuplicateStateless-check.py
@@ -0,0 +1,31 @@
+from check_index import *
+
+def check_reply(r):
+    assert is_dict(r)
+    assert sorted(r.keys()) == ["__test-v1", "__test-v2", "__test-v3", "client-foo", "query.json", "unknown"]
+    check_index__test(r["__test-v1"], 1, 3)
+    check_index__test(r["__test-v2"], 2, 0)
+    check_error(r["__test-v3"], "unknown query file")
+    check_reply_client_foo(r["client-foo"])
+    check_error(r["query.json"], "unknown query file")
+    check_error(r["unknown"], "unknown query file")
+
+def check_reply_client_foo(r):
+    assert is_dict(r)
+    assert sorted(r.keys()) == ["__test-v1", "__test-v2", "__test-v3", "unknown"]
+    check_index__test(r["__test-v1"], 1, 3)
+    check_index__test(r["__test-v2"], 2, 0)
+    check_error(r["__test-v3"], "unknown query file")
+    check_error(r["unknown"], "unknown query file")
+
+def check_objects(o):
+    assert is_list(o)
+    assert len(o) == 2
+    check_index__test(o[0], 1, 3)
+    check_index__test(o[1], 2, 0)
+
+assert is_dict(index)
+assert sorted(index.keys()) == ["cmake", "objects", "reply"]
+check_cmake(index["cmake"])
+check_reply(index["reply"])
+check_objects(index["objects"])
diff --git a/Tests/RunCMake/FileAPI/DuplicateStateless-prep.cmake b/Tests/RunCMake/FileAPI/DuplicateStateless-prep.cmake
new file mode 100644
index 0000000..51b9852
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/DuplicateStateless-prep.cmake
@@ -0,0 +1,10 @@
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/__test-v1" "")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/__test-v2" "")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/__test-v3" "")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/query.json" "")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/unknown" "")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/__test-v1" "")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/__test-v2" "")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/__test-v3" "")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/unknown" "")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/reply/object-to-be-deleted.json" "")
diff --git a/Tests/RunCMake/FileAPI/DuplicateStateless.cmake b/Tests/RunCMake/FileAPI/DuplicateStateless.cmake
new file mode 100644
index 0000000..e69de29
diff --git a/Tests/RunCMake/FileAPI/EmptyClient-check.cmake b/Tests/RunCMake/FileAPI/EmptyClient-check.cmake
new file mode 100644
index 0000000..4e5745c
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/EmptyClient-check.cmake
@@ -0,0 +1,9 @@
+set(expect
+  query
+  query/client-foo
+  reply
+  reply/index-[0-9.T-]+.json
+  )
+check_api("^${expect}$")
+
+check_python(EmptyClient)
diff --git a/Tests/RunCMake/FileAPI/EmptyClient-check.py b/Tests/RunCMake/FileAPI/EmptyClient-check.py
new file mode 100644
index 0000000..f887908
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/EmptyClient-check.py
@@ -0,0 +1,20 @@
+from check_index import *
+
+def check_reply(r):
+    assert is_dict(r)
+    assert sorted(r.keys()) == ["client-foo"]
+    check_reply_client_foo(r["client-foo"])
+
+def check_reply_client_foo(r):
+    assert is_dict(r)
+    assert sorted(r.keys()) == []
+
+def check_objects(o):
+    assert is_list(o)
+    assert len(o) == 0
+
+assert is_dict(index)
+assert sorted(index.keys()) == ["cmake", "objects", "reply"]
+check_cmake(index["cmake"])
+check_reply(index["reply"])
+check_objects(index["objects"])
diff --git a/Tests/RunCMake/FileAPI/EmptyClient-prep.cmake b/Tests/RunCMake/FileAPI/EmptyClient-prep.cmake
new file mode 100644
index 0000000..31512fd
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/EmptyClient-prep.cmake
@@ -0,0 +1,2 @@
+file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo" "")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/reply/object-to-be-deleted.json" "")
diff --git a/Tests/RunCMake/FileAPI/EmptyClient.cmake b/Tests/RunCMake/FileAPI/EmptyClient.cmake
new file mode 100644
index 0000000..e69de29
diff --git a/Tests/RunCMake/FileAPI/MixedStateless-check.cmake b/Tests/RunCMake/FileAPI/MixedStateless-check.cmake
new file mode 100644
index 0000000..5795614
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/MixedStateless-check.cmake
@@ -0,0 +1,16 @@
+set(expect
+  query
+  query/__test-v1
+  query/__test-v3
+  query/client-foo
+  query/client-foo/__test-v2
+  query/client-foo/unknown
+  query/query.json
+  reply
+  reply/__test-v1-[0-9a-f]+.json
+  reply/__test-v2-[0-9a-f]+.json
+  reply/index-[0-9.T-]+.json
+  )
+check_api("^${expect}$")
+
+check_python(MixedStateless)
diff --git a/Tests/RunCMake/FileAPI/MixedStateless-check.py b/Tests/RunCMake/FileAPI/MixedStateless-check.py
new file mode 100644
index 0000000..be019ab
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/MixedStateless-check.py
@@ -0,0 +1,27 @@
+from check_index import *
+
+def check_reply(r):
+    assert is_dict(r)
+    assert sorted(r.keys()) == ["__test-v1", "__test-v3", "client-foo", "query.json"]
+    check_index__test(r["__test-v1"], 1, 3)
+    check_error(r["__test-v3"], "unknown query file")
+    check_reply_client_foo(r["client-foo"])
+    check_error(r["query.json"], "unknown query file")
+
+def check_reply_client_foo(r):
+    assert is_dict(r)
+    assert sorted(r.keys()) == ["__test-v2", "unknown"]
+    check_index__test(r["__test-v2"], 2, 0)
+    check_error(r["unknown"], "unknown query file")
+
+def check_objects(o):
+    assert is_list(o)
+    assert len(o) == 2
+    check_index__test(o[0], 1, 3)
+    check_index__test(o[1], 2, 0)
+
+assert is_dict(index)
+assert sorted(index.keys()) == ["cmake", "objects", "reply"]
+check_cmake(index["cmake"])
+check_reply(index["reply"])
+check_objects(index["objects"])
diff --git a/Tests/RunCMake/FileAPI/MixedStateless-prep.cmake b/Tests/RunCMake/FileAPI/MixedStateless-prep.cmake
new file mode 100644
index 0000000..030baac
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/MixedStateless-prep.cmake
@@ -0,0 +1,6 @@
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/__test-v1" "")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/__test-v2" "")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/__test-v3" "")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/query.json" "")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/unknown" "")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/reply/object-to-be-deleted.json" "")
diff --git a/Tests/RunCMake/FileAPI/MixedStateless.cmake b/Tests/RunCMake/FileAPI/MixedStateless.cmake
new file mode 100644
index 0000000..e69de29
diff --git a/Tests/RunCMake/FileAPI/RunCMakeTest.cmake b/Tests/RunCMake/FileAPI/RunCMakeTest.cmake
index bb016ca..a7351b3 100644
--- a/Tests/RunCMake/FileAPI/RunCMakeTest.cmake
+++ b/Tests/RunCMake/FileAPI/RunCMakeTest.cmake
@@ -36,5 +36,9 @@ endfunction()
 
 run_cmake(Nothing)
 run_cmake(Empty)
+run_cmake(EmptyClient)
 run_cmake(Stale)
 run_cmake(SharedStateless)
+run_cmake(ClientStateless)
+run_cmake(MixedStateless)
+run_cmake(DuplicateStateless)

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=eb2ec41a0422e9acd4961e32f6f28c20846a292a
commit eb2ec41a0422e9acd4961e32f6f28c20846a292a
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Thu Sep 13 08:18:22 2018 -0400
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Wed Dec 12 06:39:30 2018 -0500

    fileapi: Add protocol v1 infrastructure with support for shared query files
    
    Add a file-based API that clients may use to get semantic information
    about the buildsystem that CMake generates.  Clients will write query
    files under a designated location in the build tree, and CMake will
    write reply files for clients to read.
    
    Start with support for shared stateless query files.  These allow
    clients to share requests for major object versions and get all those
    recognized by CMake.  Once any client has written a shared request to a
    build tree it will persist.  Other clients will not need to overwrite
    the request (since it is stateless) and should not remove it either.
    
    For now we add only an undocumented object kind to use for testing the
    query and reply infrastructure.  Object kinds providing real semantic
    information will be added later.
    
    Issue: #18398

diff --git a/Help/index.rst b/Help/index.rst
index fe1b73c..a948939 100644
--- a/Help/index.rst
+++ b/Help/index.rst
@@ -30,6 +30,7 @@ Reference Manuals
    /manual/cmake-compile-features.7
    /manual/cmake-developer.7
    /manual/cmake-env-variables.7
+   /manual/cmake-file-api.7
    /manual/cmake-generator-expressions.7
    /manual/cmake-generators.7
    /manual/cmake-language.7
diff --git a/Help/manual/cmake-file-api.7.rst b/Help/manual/cmake-file-api.7.rst
new file mode 100644
index 0000000..dd2ef83
--- /dev/null
+++ b/Help/manual/cmake-file-api.7.rst
@@ -0,0 +1,220 @@
+.. cmake-manual-description: CMake File-Based API
+
+cmake-file-api(7)
+*****************
+
+.. only:: html
+
+   .. contents::
+
+Introduction
+============
+
+CMake provides a file-based API that clients may use to get semantic
+information about the buildsystems CMake generates.  Clients may use
+the API by writing query files to a specific location in a build tree
+to request zero or more `Object Kinds`_.  When CMake generates the
+buildsystem in that build tree it will read the query files and write
+reply files for the client to read.
+
+The file-based API uses a ``<build>/.cmake/api/`` directory at the top
+of a build tree.  The API is versioned to support changes to the layout
+of files within the API directory.  API file layout versioning is
+orthogonal to the versioning of `Object Kinds`_ used in replies.
+This version of CMake supports only one API version, `API v1`_.
+
+API v1
+======
+
+API v1 is housed in the ``<build>/.cmake/api/v1/`` directory.
+It has the following subdirectories:
+
+``query/``
+  Holds query files written by clients.
+  These may be `v1 Shared Stateless Query Files`_.
+
+``reply/``
+  Holds reply files written by CMake whenever it runs to generate a build
+  system.  These are indexed by a `v1 Reply Index File`_ file that may
+  reference additional `v1 Reply Files`_.  CMake owns all reply files.
+  Clients must never remove them.
+
+  Clients may look for and read a reply index file at any time.
+  Clients may optionally create the ``reply/`` directory at any time
+  and monitor it for the appearance of a new reply index file.
+
+v1 Shared Stateless Query Files
+-------------------------------
+
+Shared stateless query files allow clients to share requests for
+major versions of the `Object Kinds`_ and get all requested versions
+recognized by the CMake that runs.
+
+Clients may create shared requests by creating empty files in the
+``v1/query/`` directory.  The form is::
+
+  <build>/.cmake/api/v1/query/<kind>-v<major>
+
+where ``<kind>`` is one of the `Object Kinds`_, ``-v`` is literal,
+and ``<major>`` is the major version number.
+
+Files of this form are stateless shared queries not owned by any specific
+client.  Once created they should not be removed without external client
+coordination or human intervention.
+
+v1 Reply Index File
+-------------------
+
+CMake writes an ``index-*.json`` file to the ``v1/reply/`` directory
+whenever it runs to generate a build system.  Clients must read the
+reply index file first and may read other `v1 Reply Files`_ only by
+following references.  The form of the reply index file name is::
+
+  <build>/.cmake/api/v1/reply/index-<unspecified>.json
+
+where ``index-`` is literal and ``<unspecified>`` is an unspecified
+name selected by CMake.  Whenever a new index file is generated it
+is given a new name and any old one is deleted.  During the short
+time between these steps there may be multiple index files present;
+the one with the largest name in lexicographic order is the current
+index file.
+
+The reply index file contains a JSON object:
+
+.. code-block:: json
+
+  {
+    "cmake": {
+      "version": {
+        "major": 3, "minor": 14, "patch": 0, "suffix": "",
+        "string": "3.14.0", "isDirty": false
+      },
+      "paths": {
+        "cmake": "/prefix/bin/cmake",
+        "ctest": "/prefix/bin/ctest",
+        "cpack": "/prefix/bin/cpack",
+        "root": "/prefix/share/cmake-3.14"
+      }
+    },
+    "objects": [
+      { "kind": "<kind>",
+        "version": { "major": 1, "minor": 0 },
+        "jsonFile": "<file>" },
+      { "...": "..." }
+    ],
+    "reply": {
+      "<kind>-v<major>": { "kind": "<kind>",
+                           "version": { "major": 1, "minor": 0 },
+                           "jsonFile": "<file>" },
+      "<unknown>": { "error": "unknown query file" },
+      "...": {}
+    }
+  }
+
+The members are:
+
+``cmake``
+  A JSON object containing information about the instance of CMake that
+  generated the reply.  It contains members:
+
+  ``version``
+    A JSON object specifying the version of CMake with members:
+
+    ``major``, ``minor``, ``patch``
+      Integer values specifying the major, minor, and patch version components.
+    ``suffix``
+      A string specifying the version suffix, if any, e.g. ``g0abc3``.
+    ``string``
+      A string specifying the full version in the format
+      ``<major>.<minor>.<patch>[-<suffix>]``.
+    ``isDirty``
+      A boolean indicating whether the version was built from a version
+      controlled source tree with local modifications.
+
+  ``paths``
+    A JSON object specifying paths to things that come with CMake.
+    It has members for ``cmake``, ``ctest``, and ``cpack`` whose values
+    are JSON strings specifying the absolute path to each tool,
+    represented with forward slashes.  It also has a ``root`` member for
+    the absolute path to the directory containing CMake resources like the
+    ``Modules/`` directory (see :variable:`CMAKE_ROOT`).
+
+``objects``
+  A JSON array listing all versions of all `Object Kinds`_ generated
+  as part of the reply.  Each array entry is a
+  `v1 Reply File Reference`_.
+
+``reply``
+  A JSON object mirroring the content of the ``query/`` directory
+  that CMake loaded to produce the reply.  The members are of the form
+
+  ``<kind>-v<major>``
+    A member of this form appears for each of the
+    `v1 Shared Stateless Query Files`_ that CMake recognized as a
+    request for object kind ``<kind>`` with major version ``<major>``.
+    The value is a `v1 Reply File Reference`_ to the corresponding
+    reply file for that object kind and version.
+
+  ``<unknown>``
+    A member of this form appears for each of the
+    `v1 Shared Stateless Query Files`_ that CMake did not recognize.
+    The value is a JSON object with a single ``error`` member
+    containing a string with an error message indicating that the
+    query file is unknown.
+
+After reading the reply index file, clients may read the other
+`v1 Reply Files`_ it references.
+
+v1 Reply File Reference
+^^^^^^^^^^^^^^^^^^^^^^^
+
+The reply index file represents each reference to another reply file
+using a JSON object with members:
+
+``kind``
+  A string specifying one of the `Object Kinds`_.
+``version``
+  A JSON object with members ``major`` and ``minor`` specifying
+  integer version components of the object kind.
+``jsonFile``
+  A JSON string specifying a path relative to the reply index file
+  to another JSON file containing the object.
+
+v1 Reply Files
+--------------
+
+Reply files containing specific `Object Kinds`_ are written by CMake.
+The names of these files are unspecified and must not be interpreted
+by clients.  Clients must first read the `v1 Reply Index File`_ and
+and follow references to the names of the desired response objects.
+
+Reply files (including the index file) will never be replaced by
+files of the same name but different content.  This allows a client
+to read the files concurrently with a running CMake that may generate
+a new reply.  However, after generating a new reply CMake will attempt
+to remove reply files from previous runs that it did not just write.
+If a client attempts to read a reply file referenced by the index but
+finds the file missing, that means a concurrent CMake has generated
+a new reply.  The client may simply start again by reading the new
+reply index file.
+
+Object Kinds
+============
+
+The CMake file-based API reports semantic information about the build
+system using the following kinds of JSON objects.  Each kind of object
+is versioned independently using semantic versioning with major and
+minor components.  Every kind of object has the form:
+
+.. code-block:: json
+
+  {
+    "kind": "<kind>",
+    "version": { "major": 1, "minor": 0 },
+    "...": {}
+  }
+
+The ``kind`` member is a string specifying the object kind name.
+The ``version`` member is a JSON object with ``major`` and ``minor``
+members specifying integer components of the object kind's version.
+Additional top-level members are specific to each object kind.
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 9aebfa7..ec71fe0 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -207,6 +207,8 @@ set(SRCS
   cmExtraKateGenerator.h
   cmExtraSublimeTextGenerator.cxx
   cmExtraSublimeTextGenerator.h
+  cmFileAPI.cxx
+  cmFileAPI.h
   cmFileLock.cxx
   cmFileLock.h
   cmFileLockPool.cxx
diff --git a/Source/cmFileAPI.cxx b/Source/cmFileAPI.cxx
new file mode 100644
index 0000000..23e0ced
--- /dev/null
+++ b/Source/cmFileAPI.cxx
@@ -0,0 +1,299 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmFileAPI.h"
+
+#include "cmCryptoHash.h"
+#include "cmSystemTools.h"
+#include "cmTimestamp.h"
+#include "cmake.h"
+#include "cmsys/Directory.hxx"
+#include "cmsys/FStream.hxx"
+
+#include <algorithm>
+#include <cassert>
+#include <chrono>
+#include <ctime>
+#include <iomanip>
+#include <sstream>
+#include <utility>
+
+cmFileAPI::cmFileAPI(cmake* cm)
+  : CMakeInstance(cm)
+{
+  this->APIv1 =
+    this->CMakeInstance->GetHomeOutputDirectory() + "/.cmake/api/v1";
+
+  Json::StreamWriterBuilder wbuilder;
+  wbuilder["indentation"] = "\t";
+  this->JsonWriter =
+    std::unique_ptr<Json::StreamWriter>(wbuilder.newStreamWriter());
+}
+
+void cmFileAPI::ReadQueries()
+{
+  std::string const query_dir = this->APIv1 + "/query";
+  this->QueryExists = cmSystemTools::FileIsDirectory(query_dir);
+  if (!this->QueryExists) {
+    return;
+  }
+
+  // Load queries at the top level.
+  std::vector<std::string> queries = cmFileAPI::LoadDir(query_dir);
+
+  // Read the queries and save for later.
+  for (std::string& query : queries) {
+    if (!cmFileAPI::ReadQuery(query, this->TopQuery.Known)) {
+      this->TopQuery.Unknown.push_back(std::move(query));
+    }
+  }
+}
+
+void cmFileAPI::WriteReplies()
+{
+  if (this->QueryExists) {
+    cmSystemTools::MakeDirectory(this->APIv1 + "/reply");
+    this->WriteJsonFile(this->BuildReplyIndex(), "index", ComputeSuffixTime);
+  }
+
+  this->RemoveOldReplyFiles();
+}
+
+std::vector<std::string> cmFileAPI::LoadDir(std::string const& dir)
+{
+  std::vector<std::string> files;
+  cmsys::Directory d;
+  d.Load(dir);
+  for (unsigned long i = 0; i < d.GetNumberOfFiles(); ++i) {
+    std::string f = d.GetFile(i);
+    if (f != "." && f != "..") {
+      files.push_back(std::move(f));
+    }
+  }
+  std::sort(files.begin(), files.end());
+  return files;
+}
+
+void cmFileAPI::RemoveOldReplyFiles()
+{
+  std::string const reply_dir = this->APIv1 + "/reply";
+  std::vector<std::string> files = this->LoadDir(reply_dir);
+  for (std::string const& f : files) {
+    if (this->ReplyFiles.find(f) == this->ReplyFiles.end()) {
+      std::string file = reply_dir + "/" + f;
+      cmSystemTools::RemoveFile(file);
+    }
+  }
+}
+
+std::string cmFileAPI::WriteJsonFile(
+  Json::Value const& value, std::string const& prefix,
+  std::string (*computeSuffix)(std::string const&))
+{
+  std::string fileName;
+
+  // Write the json file with a temporary name.
+  std::string const& tmpFile = this->APIv1 + "/tmp.json";
+  cmsys::ofstream ftmp(tmpFile.c_str());
+  this->JsonWriter->write(value, &ftmp);
+  ftmp << "\n";
+  ftmp.close();
+  if (!ftmp) {
+    cmSystemTools::RemoveFile(tmpFile);
+    return fileName;
+  }
+
+  // Compute the final name for the file.
+  fileName = prefix + "-" + computeSuffix(tmpFile) + ".json";
+
+  // Create the destination.
+  std::string file = this->APIv1 + "/reply";
+  cmSystemTools::MakeDirectory(file);
+  file += "/";
+  file += fileName;
+
+  // If the final name already exists then assume it has proper content.
+  // Otherwise, atomically place the reply file at its final name
+  if (cmSystemTools::FileExists(file, true) ||
+      !cmSystemTools::RenameFile(tmpFile.c_str(), file.c_str())) {
+    cmSystemTools::RemoveFile(tmpFile);
+  }
+
+  // Record this among files we have just written.
+  this->ReplyFiles.insert(fileName);
+
+  return fileName;
+}
+
+std::string cmFileAPI::ComputeSuffixHash(std::string const& file)
+{
+  cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_256);
+  std::string hash = hasher.HashFile(file);
+  hash.resize(20, '0');
+  return hash;
+}
+
+std::string cmFileAPI::ComputeSuffixTime(std::string const&)
+{
+  std::chrono::milliseconds ms =
+    std::chrono::duration_cast<std::chrono::milliseconds>(
+      std::chrono::system_clock::now().time_since_epoch());
+  std::chrono::seconds s =
+    std::chrono::duration_cast<std::chrono::seconds>(ms);
+
+  std::time_t ts = s.count();
+  std::size_t tms = ms.count() % 1000;
+
+  cmTimestamp cmts;
+  std::ostringstream ss;
+  ss << cmts.CreateTimestampFromTimeT(ts, "%Y-%m-%dT%H-%M-%S", true) << '-'
+     << std::setfill('0') << std::setw(4) << tms;
+  return ss.str();
+}
+
+bool cmFileAPI::ReadQuery(std::string const& query,
+                          std::vector<Object>& objects)
+{
+  // Parse the "<kind>-" syntax.
+  std::string::size_type sep_pos = query.find('-');
+  if (sep_pos == std::string::npos) {
+    return false;
+  }
+  std::string kindName = query.substr(0, sep_pos);
+  std::string verStr = query.substr(sep_pos + 1);
+  if (kindName == ObjectKindName(ObjectKind::InternalTest)) {
+    Object o;
+    o.Kind = ObjectKind::InternalTest;
+    if (verStr == "v1") {
+      o.Version = 1;
+    } else if (verStr == "v2") {
+      o.Version = 2;
+    } else {
+      return false;
+    }
+    objects.push_back(o);
+    return true;
+  }
+  return false;
+}
+
+Json::Value cmFileAPI::BuildReplyIndex()
+{
+  Json::Value index(Json::objectValue);
+
+  // Report information about this version of CMake.
+  index["cmake"] = this->BuildCMake();
+
+  // Reply to all queries that we loaded.
+  index["reply"] = this->BuildReply(this->TopQuery);
+
+  // Move our index of generated objects into its field.
+  Json::Value& objects = index["objects"] = Json::arrayValue;
+  for (auto& entry : this->ReplyIndexObjects) {
+    objects.append(std::move(entry.second)); // NOLINT(*)
+  }
+
+  return index;
+}
+
+Json::Value cmFileAPI::BuildCMake()
+{
+  Json::Value cmake = Json::objectValue;
+  cmake["version"] = this->CMakeInstance->ReportVersionJson();
+  Json::Value& cmake_paths = cmake["paths"] = Json::objectValue;
+  cmake_paths["cmake"] = cmSystemTools::GetCMakeCommand();
+  cmake_paths["ctest"] = cmSystemTools::GetCTestCommand();
+  cmake_paths["cpack"] = cmSystemTools::GetCPackCommand();
+  cmake_paths["root"] = cmSystemTools::GetCMakeRoot();
+  return cmake;
+}
+
+Json::Value cmFileAPI::BuildReply(Query const& q)
+{
+  Json::Value reply = Json::objectValue;
+  for (Object const& o : q.Known) {
+    std::string const& name = ObjectName(o);
+    reply[name] = this->AddReplyIndexObject(o);
+  }
+
+  for (std::string const& name : q.Unknown) {
+    reply[name] = cmFileAPI::BuildReplyError("unknown query file");
+  }
+  return reply;
+}
+
+Json::Value cmFileAPI::BuildReplyError(std::string const& error)
+{
+  Json::Value e = Json::objectValue;
+  e["error"] = error;
+  return e;
+}
+
+Json::Value const& cmFileAPI::AddReplyIndexObject(Object const& o)
+{
+  Json::Value& indexEntry = this->ReplyIndexObjects[o];
+  if (!indexEntry.isNull()) {
+    // The reply object has already been generated.
+    return indexEntry;
+  }
+
+  // Generate this reply object.
+  Json::Value const& object = this->BuildObject(o);
+  assert(object.isObject());
+
+  // Populate this index entry.
+  indexEntry = Json::objectValue;
+  indexEntry["kind"] = object["kind"];
+  indexEntry["version"] = object["version"];
+  indexEntry["jsonFile"] = this->WriteJsonFile(object, ObjectName(o));
+  return indexEntry;
+}
+
+const char* cmFileAPI::ObjectKindName(ObjectKind kind)
+{
+  // Keep in sync with ObjectKind enum.
+  static const char* objectKindNames[] = {
+    "__test" //
+  };
+  return objectKindNames[size_t(kind)];
+}
+
+std::string cmFileAPI::ObjectName(Object const& o)
+{
+  std::string name = ObjectKindName(o.Kind);
+  name += "-v";
+  name += std::to_string(o.Version);
+  return name;
+}
+
+Json::Value cmFileAPI::BuildObject(Object const& object)
+{
+  Json::Value value;
+
+  switch (object.Kind) {
+    case ObjectKind::InternalTest:
+      value = this->BuildInternalTest(object);
+      break;
+  }
+
+  return value;
+}
+
+// The "__test" object kind is for internal testing of CMake.
+
+static unsigned int const InternalTestV1Minor = 3;
+static unsigned int const InternalTestV2Minor = 0;
+
+Json::Value cmFileAPI::BuildInternalTest(Object const& object)
+{
+  Json::Value test = Json::objectValue;
+  test["kind"] = this->ObjectKindName(object.Kind);
+  Json::Value& version = test["version"] = Json::objectValue;
+  if (object.Version == 2) {
+    version["major"] = 2;
+    version["minor"] = InternalTestV2Minor;
+  } else {
+    version["major"] = 1;
+    version["minor"] = InternalTestV1Minor;
+  }
+  return test;
+}
diff --git a/Source/cmFileAPI.h b/Source/cmFileAPI.h
new file mode 100644
index 0000000..39b054d
--- /dev/null
+++ b/Source/cmFileAPI.h
@@ -0,0 +1,109 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmFileAPI_h
+#define cmFileAPI_h
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include "cm_jsoncpp_value.h"
+#include "cm_jsoncpp_writer.h"
+
+#include <map>
+#include <memory> // IWYU pragma: keep
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+class cmake;
+
+class cmFileAPI
+{
+public:
+  cmFileAPI(cmake* cm);
+
+  /** Read fileapi queries from disk.  */
+  void ReadQueries();
+
+  /** Write fileapi replies to disk.  */
+  void WriteReplies();
+
+  /** Get the "cmake" instance with which this was constructed.  */
+  cmake* GetCMakeInstance() const { return this->CMakeInstance; }
+
+private:
+  cmake* CMakeInstance;
+
+  /** The api/v1 directory location.  */
+  std::string APIv1;
+
+  /** The set of files we have just written to the reply directory.  */
+  std::unordered_set<std::string> ReplyFiles;
+
+  static std::vector<std::string> LoadDir(std::string const& dir);
+  void RemoveOldReplyFiles();
+
+  // Keep in sync with ObjectKindName.
+  enum class ObjectKind
+  {
+    InternalTest
+  };
+
+  /** Identify one object kind and major version.  */
+  struct Object
+  {
+    ObjectKind Kind;
+    unsigned long Version = 0;
+    friend bool operator<(Object const& l, Object const& r)
+    {
+      if (l.Kind != r.Kind) {
+        return l.Kind < r.Kind;
+      }
+      return l.Version < r.Version;
+    }
+  };
+
+  /** Represent content of a query directory.  */
+  struct Query
+  {
+    /** Known object kind-version pairs.  */
+    std::vector<Object> Known;
+    /** Unknown object kind names.  */
+    std::vector<std::string> Unknown;
+  };
+
+  /** Whether the top-level query directory exists at all.  */
+  bool QueryExists = false;
+
+  /** The content of the top-level query directory.  */
+  Query TopQuery;
+
+  /** Reply index object generated for object kind/version.
+      This populates the "objects" field of the reply index.  */
+  std::map<Object, Json::Value> ReplyIndexObjects;
+
+  std::unique_ptr<Json::StreamWriter> JsonWriter;
+
+  std::string WriteJsonFile(
+    Json::Value const& value, std::string const& prefix,
+    std::string (*computeSuffix)(std::string const&) = ComputeSuffixHash);
+  static std::string ComputeSuffixHash(std::string const&);
+  static std::string ComputeSuffixTime(std::string const&);
+
+  static bool ReadQuery(std::string const& query,
+                        std::vector<Object>& objects);
+
+  Json::Value BuildReplyIndex();
+  Json::Value BuildCMake();
+  Json::Value BuildReply(Query const& q);
+  static Json::Value BuildReplyError(std::string const& error);
+  Json::Value const& AddReplyIndexObject(Object const& o);
+
+  static const char* ObjectKindName(ObjectKind kind);
+  static std::string ObjectName(Object const& o);
+
+  Json::Value BuildObject(Object const& object);
+
+  Json::Value BuildInternalTest(Object const& object);
+};
+
+#endif
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index 2ac7f4d..e81d14b 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -30,6 +30,7 @@
 #if defined(CMAKE_BUILD_WITH_CMAKE)
 #  include "cm_jsoncpp_writer.h"
 
+#  include "cmFileAPI.h"
 #  include "cmGraphVizWriter.h"
 #  include "cmVariableWatch.h"
 #  include <unordered_map>
@@ -1443,6 +1444,11 @@ int cmake::ActualConfigure()
     this->TruncateOutputLog("CMakeError.log");
   }
 
+#if defined(CMAKE_BUILD_WITH_CMAKE)
+  this->FileAPI = cm::make_unique<cmFileAPI>(this);
+  this->FileAPI->ReadQueries();
+#endif
+
   // actually do the configure
   this->GlobalGenerator->Configure();
   // Before saving the cache
@@ -1682,6 +1688,10 @@ int cmake::Generate()
   // for the Visual Studio and Xcode generators.)
   this->SaveCache(this->GetHomeOutputDirectory());
 
+#if defined(CMAKE_BUILD_WITH_CMAKE)
+  this->FileAPI->WriteReplies();
+#endif
+
   return 0;
 }
 
diff --git a/Source/cmake.h b/Source/cmake.h
index d3d0e80..d00acc7 100644
--- a/Source/cmake.h
+++ b/Source/cmake.h
@@ -6,6 +6,7 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <map>
+#include <memory> // IWYU pragma: keep
 #include <set>
 #include <string>
 #include <unordered_set>
@@ -21,6 +22,7 @@
 #endif
 
 class cmExternalMakefileProjectGeneratorFactory;
+class cmFileAPI;
 class cmFileTimeComparison;
 class cmGlobalGenerator;
 class cmGlobalGeneratorFactory;
@@ -528,6 +530,7 @@ private:
 
 #if defined(CMAKE_BUILD_WITH_CMAKE)
   cmVariableWatch* VariableWatch;
+  std::unique_ptr<cmFileAPI> FileAPI;
 #endif
 
   cmState* State;
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index a4d829b..d5aa0c3 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -66,6 +66,9 @@ function(add_RunCMake_test_group test types)
   endforeach()
 endfunction()
 
+# Some tests use python for extra checks.
+find_package(PythonInterp QUIET)
+
 if(XCODE_VERSION AND "${XCODE_VERSION}" VERSION_LESS 6.1)
   set(Swift_ARGS -DXCODE_BELOW_6_1=1)
 endif()
@@ -155,6 +158,7 @@ add_RunCMake_test(DisallowedCommands)
 add_RunCMake_test(ExternalData)
 add_RunCMake_test(FeatureSummary)
 add_RunCMake_test(FPHSA)
+add_RunCMake_test(FileAPI -DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE})
 add_RunCMake_test(FindBoost)
 add_RunCMake_test(FindLua)
 add_RunCMake_test(FindOpenGL)
diff --git a/Tests/RunCMake/FileAPI/CMakeLists.txt b/Tests/RunCMake/FileAPI/CMakeLists.txt
new file mode 100644
index 0000000..44025d3
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.12)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/FileAPI/Empty-check.cmake b/Tests/RunCMake/FileAPI/Empty-check.cmake
new file mode 100644
index 0000000..2764b42
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/Empty-check.cmake
@@ -0,0 +1,8 @@
+set(expect
+  query
+  reply
+  reply/index-[0-9.T-]+.json
+  )
+check_api("^${expect}$")
+
+check_python(Empty)
diff --git a/Tests/RunCMake/FileAPI/Empty-check.py b/Tests/RunCMake/FileAPI/Empty-check.py
new file mode 100644
index 0000000..75bf096
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/Empty-check.py
@@ -0,0 +1,15 @@
+from check_index import *
+
+def check_reply(r):
+    assert is_dict(r)
+    assert sorted(r.keys()) == []
+
+def check_objects(o):
+    assert is_list(o)
+    assert len(o) == 0
+
+assert is_dict(index)
+assert sorted(index.keys()) == ["cmake", "objects", "reply"]
+check_cmake(index["cmake"])
+check_reply(index["reply"])
+check_objects(index["objects"])
diff --git a/Tests/RunCMake/FileAPI/Empty-prep.cmake b/Tests/RunCMake/FileAPI/Empty-prep.cmake
new file mode 100644
index 0000000..1d1f69e
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/Empty-prep.cmake
@@ -0,0 +1 @@
+file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query")
diff --git a/Tests/RunCMake/FileAPI/Empty.cmake b/Tests/RunCMake/FileAPI/Empty.cmake
new file mode 100644
index 0000000..e69de29
diff --git a/Tests/RunCMake/FileAPI/Nothing-check.cmake b/Tests/RunCMake/FileAPI/Nothing-check.cmake
new file mode 100644
index 0000000..cd4f42e
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/Nothing-check.cmake
@@ -0,0 +1 @@
+check_api("^$")
diff --git a/Tests/RunCMake/FileAPI/Nothing-prep.cmake b/Tests/RunCMake/FileAPI/Nothing-prep.cmake
new file mode 100644
index 0000000..b850d47
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/Nothing-prep.cmake
@@ -0,0 +1 @@
+file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1")
diff --git a/Tests/RunCMake/FileAPI/Nothing.cmake b/Tests/RunCMake/FileAPI/Nothing.cmake
new file mode 100644
index 0000000..e69de29
diff --git a/Tests/RunCMake/FileAPI/RunCMakeTest.cmake b/Tests/RunCMake/FileAPI/RunCMakeTest.cmake
new file mode 100644
index 0000000..bb016ca
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/RunCMakeTest.cmake
@@ -0,0 +1,40 @@
+include(RunCMake)
+
+# Function called in *-check.cmake scripts to check api files.
+function(check_api expect)
+  file(GLOB_RECURSE actual
+    LIST_DIRECTORIES TRUE
+    RELATIVE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1
+    ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/*
+    )
+  if(NOT "${actual}" MATCHES "${expect}")
+    set(RunCMake_TEST_FAILED "API files:
+  ${actual}
+do not match what we expected:
+  ${expect}
+in directory:
+  ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1" PARENT_SCOPE)
+  endif()
+endfunction()
+
+function(check_python case)
+  if(RunCMake_TEST_FAILED OR NOT PYTHON_EXECUTABLE)
+    return()
+  endif()
+  file(GLOB index ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/reply/index-*.json)
+  execute_process(
+    COMMAND ${PYTHON_EXECUTABLE} "${RunCMake_SOURCE_DIR}/${case}-check.py" "${index}"
+    RESULT_VARIABLE result
+    OUTPUT_VARIABLE output
+    ERROR_VARIABLE output
+    )
+  if(NOT result EQUAL 0)
+    string(REPLACE "\n" "\n  " output "  ${output}")
+    set(RunCMake_TEST_FAILED "Unexpected index:\n${output}" PARENT_SCOPE)
+  endif()
+endfunction()
+
+run_cmake(Nothing)
+run_cmake(Empty)
+run_cmake(Stale)
+run_cmake(SharedStateless)
diff --git a/Tests/RunCMake/FileAPI/SharedStateless-check.cmake b/Tests/RunCMake/FileAPI/SharedStateless-check.cmake
new file mode 100644
index 0000000..7f3bb23
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/SharedStateless-check.cmake
@@ -0,0 +1,15 @@
+set(expect
+  query
+  query/__test-v1
+  query/__test-v2
+  query/__test-v3
+  query/query.json
+  query/unknown
+  reply
+  reply/__test-v1-[0-9a-f]+.json
+  reply/__test-v2-[0-9a-f]+.json
+  reply/index-[0-9.T-]+.json
+  )
+check_api("^${expect}$")
+
+check_python(SharedStateless)
diff --git a/Tests/RunCMake/FileAPI/SharedStateless-check.py b/Tests/RunCMake/FileAPI/SharedStateless-check.py
new file mode 100644
index 0000000..79f52d7
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/SharedStateless-check.py
@@ -0,0 +1,22 @@
+from check_index import *
+
+def check_reply(r):
+    assert is_dict(r)
+    assert sorted(r.keys()) == ["__test-v1", "__test-v2", "__test-v3", "query.json", "unknown"]
+    check_index__test(r["__test-v1"], 1, 3)
+    check_index__test(r["__test-v2"], 2, 0)
+    check_error(r["__test-v3"], "unknown query file")
+    check_error(r["query.json"], "unknown query file")
+    check_error(r["unknown"], "unknown query file")
+
+def check_objects(o):
+    assert is_list(o)
+    assert len(o) == 2
+    check_index__test(o[0], 1, 3)
+    check_index__test(o[1], 2, 0)
+
+assert is_dict(index)
+assert sorted(index.keys()) == ["cmake", "objects", "reply"]
+check_cmake(index["cmake"])
+check_reply(index["reply"])
+check_objects(index["objects"])
diff --git a/Tests/RunCMake/FileAPI/SharedStateless-prep.cmake b/Tests/RunCMake/FileAPI/SharedStateless-prep.cmake
new file mode 100644
index 0000000..b280414
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/SharedStateless-prep.cmake
@@ -0,0 +1,6 @@
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/__test-v1" "")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/__test-v2" "")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/__test-v3" "")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/query.json" "")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/unknown" "")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/reply/object-to-be-deleted.json" "")
diff --git a/Tests/RunCMake/FileAPI/SharedStateless.cmake b/Tests/RunCMake/FileAPI/SharedStateless.cmake
new file mode 100644
index 0000000..e69de29
diff --git a/Tests/RunCMake/FileAPI/Stale-check.cmake b/Tests/RunCMake/FileAPI/Stale-check.cmake
new file mode 100644
index 0000000..7ee2c9e
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/Stale-check.cmake
@@ -0,0 +1,4 @@
+set(expect
+  reply
+  )
+check_api("^${expect}$")
diff --git a/Tests/RunCMake/FileAPI/Stale-prep.cmake b/Tests/RunCMake/FileAPI/Stale-prep.cmake
new file mode 100644
index 0000000..e920925
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/Stale-prep.cmake
@@ -0,0 +1 @@
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/reply/object-to-be-deleted.json" "")
diff --git a/Tests/RunCMake/FileAPI/Stale.cmake b/Tests/RunCMake/FileAPI/Stale.cmake
new file mode 100644
index 0000000..e69de29
diff --git a/Tests/RunCMake/FileAPI/check_index.py b/Tests/RunCMake/FileAPI/check_index.py
new file mode 100644
index 0000000..6cc16a5
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/check_index.py
@@ -0,0 +1,88 @@
+import sys
+import os
+import json
+import re
+
+if sys.version_info[0] >= 3:
+    unicode = str
+
+def is_bool(x):
+    return isinstance(x, bool)
+
+def is_dict(x):
+    return isinstance(x, dict)
+
+def is_list(x):
+    return isinstance(x, list)
+
+def is_int(x):
+    return isinstance(x, int) or isinstance(x, long)
+
+def is_string(x):
+    return isinstance(x, str) or isinstance(x, unicode)
+
+def check_cmake(cmake):
+    assert is_dict(cmake)
+    assert sorted(cmake.keys()) == ["paths", "version"]
+    check_cmake_version(cmake["version"])
+    check_cmake_paths(cmake["paths"])
+
+def check_cmake_version(v):
+    assert is_dict(v)
+    assert sorted(v.keys()) == ["isDirty", "major", "minor", "patch", "string", "suffix"]
+    assert is_string(v["string"])
+    assert is_int(v["major"])
+    assert is_int(v["minor"])
+    assert is_int(v["patch"])
+    assert is_string(v["suffix"])
+    assert is_bool(v["isDirty"])
+
+def check_cmake_paths(v):
+    assert is_dict(v)
+    assert sorted(v.keys()) == ["cmake", "cpack", "ctest", "root"]
+    assert is_string(v["cmake"])
+    assert is_string(v["cpack"])
+    assert is_string(v["ctest"])
+    assert is_string(v["root"])
+
+def check_index_object(indexEntry, kind, major, minor, check):
+    assert is_dict(indexEntry)
+    assert sorted(indexEntry.keys()) == ["jsonFile", "kind", "version"]
+    assert is_string(indexEntry["kind"])
+    assert indexEntry["kind"] == kind
+    assert is_dict(indexEntry["version"])
+    assert sorted(indexEntry["version"].keys()) == ["major", "minor"]
+    assert indexEntry["version"]["major"] == major
+    assert indexEntry["version"]["minor"] == minor
+    assert is_string(indexEntry["jsonFile"])
+    filepath = os.path.join(reply_dir, indexEntry["jsonFile"])
+    with open(filepath) as f:
+        object = json.load(f)
+    assert is_dict(object)
+    assert "kind" in object
+    assert is_string(object["kind"])
+    assert object["kind"] == kind
+    assert "version" in object
+    assert is_dict(object["version"])
+    assert sorted(object["version"].keys()) == ["major", "minor"]
+    assert object["version"]["major"] == major
+    assert object["version"]["minor"] == minor
+    if check:
+        check(object)
+
+def check_index__test(indexEntry, major, minor):
+    def check(object):
+        assert sorted(object.keys()) == ["kind", "version"]
+    check_index_object(indexEntry, "__test", major, minor, check)
+
+def check_error(value, error):
+    assert is_dict(value)
+    assert sorted(value.keys()) == ["error"]
+    assert is_string(value["error"])
+    assert value["error"] == error
+
+reply_index = sys.argv[1]
+reply_dir = os.path.dirname(reply_index)
+
+with open(reply_index) as f:
+    index = json.load(f)

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=410a3e4b22c72794d4f96e41c1d37d84d6e7e54d
commit 410a3e4b22c72794d4f96e41c1d37d84d6e7e54d
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Tue Sep 25 19:25:34 2018 -0400
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Tue Dec 11 13:19:39 2018 -0500

    Add support for using C++17 string_view or a fallback
    
    Define a `cm::string_view` type implemented via C++17 `std::string_view`
    when available.  Provide a fallback implementation for C++11 and C++14
    compilers.
    
    The fallback implementation was written by reading documentation of the
    standard spec.  We have no dedicated tests for it, but it will be
    covered by tests of its clients later.

diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 9aebfa7..4bf2e73 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -375,6 +375,8 @@ set(SRCS
   cmXMLWriter.h
   cmake.cxx
   cmake.h
+  cm_string_view.cxx
+  cm_string_view.hxx
 
   cmCommand.cxx
   cmCommand.h
diff --git a/Source/cm_string_view.cxx b/Source/cm_string_view.cxx
new file mode 100644
index 0000000..61fa80e
--- /dev/null
+++ b/Source/cm_string_view.cxx
@@ -0,0 +1,301 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cm_string_view.hxx"
+
+#ifndef CMake_HAVE_CXX_STRING_VIEW
+
+#  include "cm_kwiml.h"
+
+#  include <algorithm>
+#  include <ostream>
+#  include <stdexcept>
+
+namespace cm {
+
+string_view::const_reference string_view::at(size_type pos) const
+{
+  if (pos >= size_) {
+    throw std::out_of_range("Index out of range in string_view::at");
+  }
+  return data_[pos];
+}
+
+string_view::size_type string_view::copy(char* dest, size_type count,
+                                         size_type pos) const
+{
+  if (pos > size_) {
+    throw std::out_of_range("Index out of range in string_view::copy");
+  }
+  size_type const rcount = std::min(count, size_ - pos);
+  traits_type::copy(dest, data_ + pos, rcount);
+  return rcount;
+}
+
+string_view string_view::substr(size_type pos, size_type count) const
+{
+  if (pos > size_) {
+    throw std::out_of_range("Index out of range in string_view::substr");
+  }
+  size_type const rcount = std::min(count, size_ - pos);
+  return string_view(data_ + pos, rcount);
+}
+
+int string_view::compare(string_view v) const noexcept
+{
+  size_type const rlen = std::min(size_, v.size_);
+  int c = traits_type::compare(data_, v.data_, rlen);
+  if (c == 0) {
+    if (size_ < v.size_) {
+      c = -1;
+    } else if (size_ > v.size_) {
+      c = 1;
+    }
+  }
+  return c;
+}
+
+int string_view::compare(size_type pos1, size_type count1, string_view v) const
+{
+  return substr(pos1, count1).compare(v);
+}
+
+int string_view::compare(size_type pos1, size_type count1, string_view v,
+                         size_type pos2, size_type count2) const
+{
+  return substr(pos1, count1).compare(v.substr(pos2, count2));
+}
+
+int string_view::compare(const char* s) const
+{
+  return compare(string_view(s));
+}
+
+int string_view::compare(size_type pos1, size_type count1, const char* s) const
+{
+  return substr(pos1, count1).compare(string_view(s));
+}
+
+int string_view::compare(size_type pos1, size_type count1, const char* s,
+                         size_type count2) const
+{
+  return substr(pos1, count1).compare(string_view(s, count2));
+}
+
+string_view::size_type string_view::find(string_view v, size_type pos) const
+  noexcept
+{
+  for (; pos + v.size_ <= size_; ++pos) {
+    if (std::char_traits<char>::compare(data_ + pos, v.data_, v.size_) == 0) {
+      return pos;
+    }
+  }
+  return npos;
+}
+
+string_view::size_type string_view::find(char c, size_type pos) const noexcept
+{
+  return find(string_view(&c, 1), pos);
+}
+
+string_view::size_type string_view::find(const char* s, size_type pos,
+                                         size_type count) const
+{
+  return find(string_view(s, count), pos);
+}
+
+string_view::size_type string_view::find(const char* s, size_type pos) const
+{
+  return find(string_view(s), pos);
+}
+
+string_view::size_type string_view::rfind(string_view v, size_type pos) const
+  noexcept
+{
+  if (size_ >= v.size_) {
+    for (pos = std::min(pos, size_ - v.size_) + 1; pos > 0;) {
+      --pos;
+      if (std::char_traits<char>::compare(data_ + pos, v.data_, v.size_) ==
+          0) {
+        return pos;
+      }
+    }
+  }
+  return npos;
+}
+
+string_view::size_type string_view::rfind(char c, size_type pos) const noexcept
+{
+  return rfind(string_view(&c, 1), pos);
+}
+
+string_view::size_type string_view::rfind(const char* s, size_type pos,
+                                          size_type count) const
+{
+  return rfind(string_view(s, count), pos);
+}
+
+string_view::size_type string_view::rfind(const char* s, size_type pos) const
+{
+  return rfind(string_view(s), pos);
+}
+
+string_view::size_type string_view::find_first_of(string_view v,
+                                                  size_type pos) const noexcept
+{
+  for (; pos < size_; ++pos) {
+    if (traits_type::find(v.data_, v.size_, data_[pos])) {
+      return pos;
+    }
+  }
+  return npos;
+}
+
+string_view::size_type string_view::find_first_of(char c, size_type pos) const
+  noexcept
+{
+  return find_first_of(string_view(&c, 1), pos);
+}
+
+string_view::size_type string_view::find_first_of(const char* s, size_type pos,
+                                                  size_type count) const
+{
+  return find_first_of(string_view(s, count), pos);
+}
+
+string_view::size_type string_view::find_first_of(const char* s,
+                                                  size_type pos) const
+{
+  return find_first_of(string_view(s), pos);
+}
+
+string_view::size_type string_view::find_last_of(string_view v,
+                                                 size_type pos) const noexcept
+{
+  if (size_ > 0) {
+    for (pos = std::min(pos, size_ - 1) + 1; pos > 0;) {
+      --pos;
+      if (traits_type::find(v.data_, v.size_, data_[pos])) {
+        return pos;
+      }
+    }
+  }
+  return npos;
+}
+
+string_view::size_type string_view::find_last_of(char c, size_type pos) const
+  noexcept
+{
+  return find_last_of(string_view(&c, 1), pos);
+}
+
+string_view::size_type string_view::find_last_of(const char* s, size_type pos,
+                                                 size_type count) const
+{
+  return find_last_of(string_view(s, count), pos);
+}
+
+string_view::size_type string_view::find_last_of(const char* s,
+                                                 size_type pos) const
+{
+  return find_last_of(string_view(s), pos);
+}
+
+string_view::size_type string_view::find_first_not_of(string_view v,
+                                                      size_type pos) const
+  noexcept
+{
+  for (; pos < size_; ++pos) {
+    if (!traits_type::find(v.data_, v.size_, data_[pos])) {
+      return pos;
+    }
+  }
+  return npos;
+}
+
+string_view::size_type string_view::find_first_not_of(char c,
+                                                      size_type pos) const
+  noexcept
+{
+  return find_first_not_of(string_view(&c, 1), pos);
+}
+
+string_view::size_type string_view::find_first_not_of(const char* s,
+                                                      size_type pos,
+                                                      size_type count) const
+{
+  return find_first_not_of(string_view(s, count), pos);
+}
+
+string_view::size_type string_view::find_first_not_of(const char* s,
+                                                      size_type pos) const
+{
+  return find_first_not_of(string_view(s), pos);
+}
+
+string_view::size_type string_view::find_last_not_of(string_view v,
+                                                     size_type pos) const
+  noexcept
+{
+  if (size_ > 0) {
+    for (pos = std::min(pos, size_ - 1) + 1; pos > 0;) {
+      --pos;
+      if (!traits_type::find(v.data_, v.size_, data_[pos])) {
+        return pos;
+      }
+    }
+  }
+  return npos;
+}
+
+string_view::size_type string_view::find_last_not_of(char c,
+                                                     size_type pos) const
+  noexcept
+{
+  return find_last_not_of(string_view(&c, 1), pos);
+}
+
+string_view::size_type string_view::find_last_not_of(const char* s,
+                                                     size_type pos,
+                                                     size_type count) const
+{
+  return find_last_not_of(string_view(s, count), pos);
+}
+
+string_view::size_type string_view::find_last_not_of(const char* s,
+                                                     size_type pos) const
+{
+  return find_last_not_of(string_view(s), pos);
+}
+
+std::ostream& operator<<(std::ostream& o, string_view v)
+{
+  return o.write(v.data(), v.size());
+}
+
+std::string& operator+=(std::string& s, string_view v)
+{
+  s.append(v.data(), v.size());
+  return s;
+}
+}
+
+std::hash<cm::string_view>::result_type std::hash<cm::string_view>::operator()(
+  argument_type const& s) const noexcept
+{
+  // FNV-1a hash.
+  static KWIML_INT_uint64_t const fnv_offset_basis = 0xcbf29ce484222325;
+  static KWIML_INT_uint64_t const fnv_prime = 0x100000001b3;
+  KWIML_INT_uint64_t h = fnv_offset_basis;
+  for (char const& c : s) {
+    h = h ^ KWIML_INT_uint64_t(KWIML_INT_uint8_t(c));
+    h = h * fnv_prime;
+  }
+  return result_type(h);
+}
+#else
+// Avoid empty translation unit.
+void cm_string_view_cxx()
+{
+}
+#endif
diff --git a/Source/cm_string_view.hxx b/Source/cm_string_view.hxx
new file mode 100644
index 0000000..d368ed8
--- /dev/null
+++ b/Source/cm_string_view.hxx
@@ -0,0 +1,217 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cm_string_view_hxx
+#define cm_string_view_hxx
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#if __cplusplus >= 201703L || defined(_MSVC_LANG) && _MSVC_LANG >= 201703L
+#  define CMake_HAVE_CXX_STRING_VIEW
+#endif
+
+#ifdef CMake_HAVE_CXX_STRING_VIEW
+#  include <string_view>
+namespace cm {
+using std::string_view;
+}
+#else
+#  include <cstddef>
+#  include <functional>
+#  include <iosfwd>
+#  include <iterator>
+#  include <string>
+
+namespace cm {
+
+class string_view
+{
+public:
+  using traits_type = std::string::traits_type;
+  using value_type = char;
+  using pointer = char*;
+  using const_pointer = const char*;
+  using reference = char&;
+  using const_reference = char const&;
+  using const_iterator = const char*;
+  using iterator = const_iterator;
+  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+  using reverse_iterator = const_reverse_iterator;
+  using size_type = std::string::size_type;
+  using difference_type = std::string::difference_type;
+
+  static size_type const npos = static_cast<size_type>(-1);
+
+  string_view() noexcept = default;
+  string_view(string_view const&) noexcept = default;
+
+  string_view(const char* s, size_t count) noexcept
+    : data_(s)
+    , size_(count)
+  {
+  }
+
+  string_view(const char* s) noexcept
+    : data_(s)
+    , size_(traits_type::length(s))
+  {
+  }
+
+  // C++17 does not define this constructor.  Instead it defines
+  // a conversion operator on std::string to create a string_view.
+  // Since this implementation is used in C++11, std::string does
+  // not have that conversion.
+  string_view(std::string const& s) noexcept
+    : data_(s.data())
+    , size_(s.size())
+  {
+  }
+
+  // C++17 does not define this conversion.  Instead it defines
+  // a constructor on std::string that can take a string_view.
+  // Since this implementation is used in C++11, std::string does
+  // not have that constructor.
+  explicit operator std::string() const { return std::string(data_, size_); }
+
+  string_view& operator=(string_view const&) = default;
+
+  const_iterator begin() const noexcept { return data_; }
+  const_iterator end() const noexcept { return data_ + size_; }
+  const_iterator cbegin() const noexcept { return begin(); }
+  const_iterator cend() const noexcept { return end(); }
+
+  const_reverse_iterator rbegin() const noexcept
+  {
+    return const_reverse_iterator(end());
+  }
+  const_reverse_iterator rend() const noexcept
+  {
+    return const_reverse_iterator(begin());
+  }
+  const_reverse_iterator crbegin() const noexcept { return rbegin(); }
+  const_reverse_iterator crend() const noexcept { return rend(); }
+
+  const_reference operator[](size_type pos) const noexcept
+  {
+    return data_[pos];
+  }
+  const_reference at(size_type pos) const;
+  const_reference front() const noexcept { return data_[0]; }
+  const_reference back() const noexcept { return data_[size_ - 1]; }
+  const_pointer data() const noexcept { return data_; }
+
+  size_type size() const noexcept { return size_; }
+  size_type length() const noexcept { return size_; }
+  size_type max_size() const noexcept { return npos - 1; }
+  bool empty() const noexcept { return size_ == 0; }
+
+  void remove_prefix(size_type n) noexcept
+  {
+    data_ += n;
+    size_ -= n;
+  }
+  void remove_suffix(size_type n) noexcept { size_ -= n; }
+  void swap(string_view& v) noexcept
+  {
+    string_view tmp = v;
+    v = *this;
+    *this = tmp;
+  }
+
+  size_type copy(char* dest, size_type count, size_type pos = 0) const;
+  string_view substr(size_type pos = 0, size_type count = npos) const;
+
+  int compare(string_view v) const noexcept;
+  int compare(size_type pos1, size_type count1, string_view v) const;
+  int compare(size_type pos1, size_type count1, string_view v, size_type pos2,
+              size_type count2) const;
+  int compare(const char* s) const;
+  int compare(size_type pos1, size_type count1, const char* s) const;
+  int compare(size_type pos1, size_type count1, const char* s,
+              size_type count2) const;
+
+  size_type find(string_view v, size_type pos = 0) const noexcept;
+  size_type find(char c, size_type pos = 0) const noexcept;
+  size_type find(const char* s, size_type pos, size_type count) const;
+  size_type find(const char* s, size_type pos = 0) const;
+
+  size_type rfind(string_view v, size_type pos = npos) const noexcept;
+  size_type rfind(char c, size_type pos = npos) const noexcept;
+  size_type rfind(const char* s, size_type pos, size_type count) const;
+  size_type rfind(const char* s, size_type pos = npos) const;
+
+  size_type find_first_of(string_view v, size_type pos = 0) const noexcept;
+  size_type find_first_of(char c, size_type pos = 0) const noexcept;
+  size_type find_first_of(const char* s, size_type pos, size_type count) const;
+  size_type find_first_of(const char* s, size_type pos = 0) const;
+
+  size_type find_last_of(string_view v, size_type pos = npos) const noexcept;
+  size_type find_last_of(char c, size_type pos = npos) const noexcept;
+  size_type find_last_of(const char* s, size_type pos, size_type count) const;
+  size_type find_last_of(const char* s, size_type pos = npos) const;
+
+  size_type find_first_not_of(string_view v, size_type pos = 0) const noexcept;
+  size_type find_first_not_of(char c, size_type pos = 0) const noexcept;
+  size_type find_first_not_of(const char* s, size_type pos,
+                              size_type count) const;
+  size_type find_first_not_of(const char* s, size_type pos = 0) const;
+
+  size_type find_last_not_of(string_view v, size_type pos = npos) const
+    noexcept;
+  size_type find_last_not_of(char c, size_type pos = npos) const noexcept;
+  size_type find_last_not_of(const char* s, size_type pos,
+                             size_type count) const;
+  size_type find_last_not_of(const char* s, size_type pos = npos) const;
+
+private:
+  const char* data_ = nullptr;
+  size_type size_ = 0;
+};
+
+std::ostream& operator<<(std::ostream& o, string_view v);
+
+std::string& operator+=(std::string& s, string_view v);
+
+inline bool operator==(string_view l, string_view r) noexcept
+{
+  return l.compare(r) == 0;
+}
+
+inline bool operator!=(string_view l, string_view r) noexcept
+{
+  return l.compare(r) != 0;
+}
+
+inline bool operator<(string_view l, string_view r) noexcept
+{
+  return l.compare(r) < 0;
+}
+
+inline bool operator<=(string_view l, string_view r) noexcept
+{
+  return l.compare(r) <= 0;
+}
+
+inline bool operator>(string_view l, string_view r) noexcept
+{
+  return l.compare(r) > 0;
+}
+
+inline bool operator>=(string_view l, string_view r) noexcept
+{
+  return l.compare(r) >= 0;
+}
+}
+
+namespace std {
+
+template <>
+struct hash<cm::string_view>
+{
+  typedef cm::string_view argument_type;
+  typedef size_t result_type;
+  result_type operator()(argument_type const& s) const noexcept;
+};
+}
+
+#endif
+#endif
diff --git a/bootstrap b/bootstrap
index 6710d1a..dcc3a83 100755
--- a/bootstrap
+++ b/bootstrap
@@ -430,6 +430,7 @@ CMAKE_CXX_SOURCES="\
   cmake  \
   cmakemain \
   cmcmd  \
+  cm_string_view \
 "
 
 if ${cmake_system_mingw}; then

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=c3635e502c9804fc0ab781b9f20e07449d4e6c23
commit c3635e502c9804fc0ab781b9f20e07449d4e6c23
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Mon Aug 27 10:14:26 2018 -0400
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Tue Dec 11 12:50:21 2018 -0500

    Tests: Add RunCMake "prep" step
    
    Give tests a chance to write content to the build tree before CMake
    runs on it.

diff --git a/Tests/RunCMake/RunCMake.cmake b/Tests/RunCMake/RunCMake.cmake
index c076ad9..4bacd96 100644
--- a/Tests/RunCMake/RunCMake.cmake
+++ b/Tests/RunCMake/RunCMake.cmake
@@ -45,6 +45,11 @@ function(run_cmake test)
     file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
   endif()
   file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+  if(RunCMake-prep-file AND EXISTS ${top_src}/${RunCMake-prep-file})
+    include(${top_src}/${RunCMake-prep-file})
+  else()
+    include(${top_src}/${test}-prep.cmake OPTIONAL)
+  endif()
   if(NOT DEFINED RunCMake_TEST_OPTIONS)
     set(RunCMake_TEST_OPTIONS "")
   endif()

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=1c03c12b1dfe56be5f303aef022da0fed8dee609
commit 1c03c12b1dfe56be5f303aef022da0fed8dee609
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Tue Dec 11 09:55:59 2018 -0500
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Tue Dec 11 12:50:21 2018 -0500

    jsoncpp: Require version 1.4.1 when using system-provided library
    
    We need the `failIfExtra` diagnostic added by that version.

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 756e379..2213850 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -544,7 +544,7 @@ macro (CMAKE_BUILD_UTILITIES)
   #---------------------------------------------------------------------
   # Build jsoncpp library.
   if(CMAKE_USE_SYSTEM_JSONCPP)
-    find_package(JsonCpp)
+    find_package(JsonCpp 1.4.1)
     if(NOT JsonCpp_FOUND)
       message(FATAL_ERROR
         "CMAKE_USE_SYSTEM_JSONCPP is ON but a JsonCpp is not found!")

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=4a8898cd6d4ab86d04f64a699a2dc95d35846744
commit 4a8898cd6d4ab86d04f64a699a2dc95d35846744
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Tue Dec 11 08:10:01 2018 -0500
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Tue Dec 11 12:50:21 2018 -0500

    cmTimestamp: Expose CreateTimestampFromTimeT publicly

diff --git a/Source/cmTimestamp.h b/Source/cmTimestamp.h
index 8dd499a..2f75acb 100644
--- a/Source/cmTimestamp.h
+++ b/Source/cmTimestamp.h
@@ -23,12 +23,12 @@ public:
                                    const std::string& formatString,
                                    bool utcFlag);
 
-private:
-  time_t CreateUtcTimeTFromTm(struct tm& timeStruct) const;
-
   std::string CreateTimestampFromTimeT(time_t timeT, std::string formatString,
                                        bool utcFlag) const;
 
+private:
+  time_t CreateUtcTimeTFromTm(struct tm& timeStruct) const;
+
   std::string AddTimestampComponent(char flag, struct tm& timeStruct,
                                     time_t timeT) const;
 };

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=03d40110dc791a803345150806ef3a13ba3a0957
commit 03d40110dc791a803345150806ef3a13ba3a0957
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Tue Nov 13 07:01:28 2018 -0500
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Tue Dec 11 12:50:21 2018 -0500

    cmStateSnapshot: Add method to get current directory snapshot

diff --git a/Source/cmStateSnapshot.cxx b/Source/cmStateSnapshot.cxx
index c2510f3..a649f5e 100644
--- a/Source/cmStateSnapshot.cxx
+++ b/Source/cmStateSnapshot.cxx
@@ -66,6 +66,12 @@ bool cmStateSnapshot::IsValid() const
     : false;
 }
 
+cmStateSnapshot cmStateSnapshot::GetBuildsystemDirectory() const
+{
+  return cmStateSnapshot(this->State,
+                         this->Position->BuildSystemDirectory->DirectoryEnd);
+}
+
 cmStateSnapshot cmStateSnapshot::GetBuildsystemDirectoryParent() const
 {
   cmStateSnapshot snapshot;
diff --git a/Source/cmStateSnapshot.h b/Source/cmStateSnapshot.h
index 014c62e..c315f48 100644
--- a/Source/cmStateSnapshot.h
+++ b/Source/cmStateSnapshot.h
@@ -37,6 +37,7 @@ public:
   std::vector<cmStateSnapshot> GetChildren();
 
   bool IsValid() const;
+  cmStateSnapshot GetBuildsystemDirectory() const;
   cmStateSnapshot GetBuildsystemDirectoryParent() const;
   cmStateSnapshot GetCallStackParent() const;
   cmStateSnapshot GetCallStackBottom() const;

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=5751a09092a35554ebdd75ea0aa05c63ec414734
commit 5751a09092a35554ebdd75ea0aa05c63ec414734
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Tue Dec 11 12:44:57 2018 -0500
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Tue Dec 11 12:50:21 2018 -0500

    jsoncpp: fix signed overflow when parsing negative value
    
    Clang's ubsan (-fsanitize=undefined) reports:
    
        runtime error: negation of -9223372036854775808 cannot be represented in
        type 'Json::Value::LargestInt' (aka 'long'); cast to an unsigned type to
        negate this value to itself
    
    Follow its advice and update the code to remove the explicit negation.

diff --git a/Utilities/cmjsoncpp/src/lib_json/json_reader.cpp b/Utilities/cmjsoncpp/src/lib_json/json_reader.cpp
index 0249cc9..6eeba0e 100644
--- a/Utilities/cmjsoncpp/src/lib_json/json_reader.cpp
+++ b/Utilities/cmjsoncpp/src/lib_json/json_reader.cpp
@@ -1581,7 +1581,7 @@ bool OurReader::decodeNumber(Token& token, Value& decoded) {
     ++current;
   // TODO: Help the compiler do the div and mod at compile time or get rid of them.
   Value::LargestUInt maxIntegerValue =
-      isNegative ? Value::LargestUInt(-Value::minLargestInt)
+      isNegative ? Value::LargestUInt(Value::minLargestInt)
                  : Value::maxLargestUInt;
   Value::LargestUInt threshold = maxIntegerValue / 10;
   Value::LargestUInt value = 0;

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=5aa88f0e026c004524b88fd8d2be13ab85f17e71
commit 5aa88f0e026c004524b88fd8d2be13ab85f17e71
Author:     Marc Chevrier <marc.chevrier at gmail.com>
AuthorDate: Tue Dec 11 15:17:24 2018 +0100
Commit:     Marc Chevrier <marc.chevrier at gmail.com>
CommitDate: Tue Dec 11 15:17:24 2018 +0100

    UseJava: add_jar: rebuild jar on dependencies changes
    
    Fixes: #18683

diff --git a/Modules/UseJava.cmake b/Modules/UseJava.cmake
index 68c9a27..2bcd28e 100644
--- a/Modules/UseJava.cmake
+++ b/Modules/UseJava.cmake
@@ -644,7 +644,7 @@ function(add_jar _TARGET_NAME)
                 string(APPEND CMAKE_JAVA_INCLUDE_PATH_FINAL "${CMAKE_JAVA_INCLUDE_FLAG_SEP}${_JAVA_JAR_PATH}")
                 list(APPEND CMAKE_JAVA_INCLUDE_PATH ${_JAVA_JAR_PATH})
                 list(APPEND _JAVA_DEPENDS ${_JAVA_INCLUDE_JAR})
-                list(APPEND _JAVA_COMPILE_DEPENDS ${_JAVA_INCLUDE_JAR})
+                list(APPEND _JAVA_COMPILE_DEPENDS ${_JAVA_JAR_PATH})
             else ()
                 message(SEND_ERROR "add_jar: INCLUDE_JARS target ${_JAVA_INCLUDE_JAR} is not a jar")
             endif ()

-----------------------------------------------------------------------

Summary of changes:
 CMakeLists.txt                                     |    2 +-
 Help/index.rst                                     |    1 +
 Help/manual/cmake-file-api.7.rst                   | 1111 +++++
 Help/release/dev/fileapi.rst                       |    5 +
 Modules/UseJava.cmake                              |    2 +-
 Source/CMakeLists.txt                              |   13 +
 Source/cmFileAPI.cxx                               |  800 +++
 Source/cmFileAPI.h                                 |  204 +
 Source/cmFileAPICMakeFiles.cxx                     |  113 +
 Source/cmFileAPICMakeFiles.h                       |   15 +
 Source/cmFileAPICache.cxx                          |  105 +
 Source/cmFileAPICache.h                            |   15 +
 Source/cmFileAPICodemodel.cxx                      | 1247 +++++
 Source/cmFileAPICodemodel.h                        |   15 +
 Source/cmGlobalGenerator.cxx                       |    9 +
 Source/cmGlobalGenerator.h                         |    6 +
 Source/cmGlobalVisualStudio7Generator.cxx          |    9 +
 Source/cmGlobalVisualStudio7Generator.h            |    4 +
 Source/cmJsonObjects.cxx                           |   10 +-
 Source/cmStateSnapshot.cxx                         |    6 +
 Source/cmStateSnapshot.h                           |    1 +
 Source/cmString.cxx                                |  152 +
 Source/cmString.hxx                                |  815 ++++
 Source/cmTimestamp.h                               |    6 +-
 Source/cm_static_string_view.hxx                   |   41 +
 Source/cm_string_view.cxx                          |  301 ++
 Source/cm_string_view.hxx                          |  217 +
 Source/cmake.cxx                                   |   10 +
 Source/cmake.h                                     |    3 +
 Source/kwsys/SystemTools.cxx                       |  179 +-
 Tests/CMakeLib/CMakeLists.txt                      |    1 +
 Tests/CMakeLib/testString.cxx                      | 1347 ++++++
 Tests/RunCMake/CMakeLists.txt                      |    4 +
 .../CMakeLists.txt                                 |    0
 Tests/RunCMake/FileAPI/ClientStateful-check.cmake  |   68 +
 Tests/RunCMake/FileAPI/ClientStateful-check.py     |  258 +
 Tests/RunCMake/FileAPI/ClientStateful-prep.cmake   |   73 +
 .../FileAPI/ClientStateful.cmake}                  |    0
 Tests/RunCMake/FileAPI/ClientStateless-check.cmake |   15 +
 Tests/RunCMake/FileAPI/ClientStateless-check.py    |   26 +
 Tests/RunCMake/FileAPI/ClientStateless-prep.cmake  |    5 +
 .../FileAPI/ClientStateless.cmake}                 |    0
 .../FileAPI/DuplicateStateless-check.cmake         |   20 +
 Tests/RunCMake/FileAPI/DuplicateStateless-check.py |   31 +
 .../RunCMake/FileAPI/DuplicateStateless-prep.cmake |   10 +
 .../FileAPI/DuplicateStateless.cmake}              |    0
 Tests/RunCMake/FileAPI/Empty-check.cmake           |    8 +
 Tests/RunCMake/FileAPI/Empty-check.py              |   15 +
 Tests/RunCMake/FileAPI/Empty-prep.cmake            |    1 +
 .../FileAPI/Empty.cmake}                           |    0
 Tests/RunCMake/FileAPI/EmptyClient-check.cmake     |    9 +
 Tests/RunCMake/FileAPI/EmptyClient-check.py        |   20 +
 Tests/RunCMake/FileAPI/EmptyClient-prep.cmake      |    2 +
 .../FileAPI/EmptyClient.cmake}                     |    0
 Tests/RunCMake/FileAPI/MixedStateless-check.cmake  |   16 +
 Tests/RunCMake/FileAPI/MixedStateless-check.py     |   27 +
 Tests/RunCMake/FileAPI/MixedStateless-prep.cmake   |    6 +
 .../FileAPI/MixedStateless.cmake}                  |    0
 Tests/RunCMake/FileAPI/Nothing-check.cmake         |    1 +
 Tests/RunCMake/FileAPI/Nothing-prep.cmake          |    1 +
 .../FileAPI/Nothing.cmake}                         |    0
 Tests/RunCMake/FileAPI/RunCMakeTest.cmake          |   58 +
 Tests/RunCMake/FileAPI/SharedStateless-check.cmake |   15 +
 Tests/RunCMake/FileAPI/SharedStateless-check.py    |   22 +
 Tests/RunCMake/FileAPI/SharedStateless-prep.cmake  |    6 +
 .../FileAPI/SharedStateless.cmake}                 |    0
 Tests/RunCMake/FileAPI/Stale-check.cmake           |    4 +
 Tests/RunCMake/FileAPI/Stale-prep.cmake            |    1 +
 .../FileAPI/Stale.cmake}                           |    0
 Tests/RunCMake/FileAPI/alias/CMakeLists.txt        |   10 +
 .../FileAPI/cache-v2-ClientStateful-check.cmake    |   11 +
 .../FileAPI/cache-v2-ClientStateful-prep.cmake     |    4 +
 .../FileAPI/cache-v2-ClientStateless-check.cmake   |   11 +
 .../FileAPI/cache-v2-ClientStateless-prep.cmake    |    2 +
 .../FileAPI/cache-v2-SharedStateless-check.cmake   |   10 +
 .../FileAPI/cache-v2-SharedStateless-prep.cmake    |    2 +
 Tests/RunCMake/FileAPI/cache-v2-check.py           |  134 +
 Tests/RunCMake/FileAPI/cache-v2.cmake              |   14 +
 Tests/RunCMake/FileAPI/check_index.py              |  163 +
 .../cmakeFiles-v1-ClientStateful-check.cmake       |   11 +
 .../cmakeFiles-v1-ClientStateful-prep.cmake        |    4 +
 .../cmakeFiles-v1-ClientStateless-check.cmake      |   11 +
 .../cmakeFiles-v1-ClientStateless-prep.cmake       |    2 +
 .../cmakeFiles-v1-SharedStateless-check.cmake      |   10 +
 .../cmakeFiles-v1-SharedStateless-prep.cmake       |    2 +
 Tests/RunCMake/FileAPI/cmakeFiles-v1-check.py      |   94 +
 Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake         |    8 +
 .../codemodel-v2-ClientStateful-check.cmake        |   12 +
 .../FileAPI/codemodel-v2-ClientStateful-prep.cmake |    4 +
 .../codemodel-v2-ClientStateless-check.cmake       |   12 +
 .../codemodel-v2-ClientStateless-prep.cmake        |    2 +
 .../codemodel-v2-SharedStateless-check.cmake       |   11 +
 .../codemodel-v2-SharedStateless-prep.cmake        |    2 +
 Tests/RunCMake/FileAPI/codemodel-v2-check.py       | 5085 ++++++++++++++++++++
 Tests/RunCMake/FileAPI/codemodel-v2.cmake          |   35 +
 Tests/RunCMake/FileAPI/custom/CMakeLists.txt       |    5 +
 Tests/RunCMake/FileAPI/cxx/CMakeLists.txt          |   15 +
 Tests/RunCMake/FileAPI/dir/CMakeLists.txt          |    1 +
 .../sub2 => FileAPI/dir/dir}/CMakeLists.txt        |    0
 .../FileAPI/dir/dirtest.cmake}                     |    0
 .../{target_link_libraries => FileAPI}/empty.c     |    0
 .../vtkIncluded.cxx => RunCMake/FileAPI/empty.cxx} |    0
 Tests/RunCMake/FileAPI/imported/CMakeLists.txt     |   24 +
 Tests/RunCMake/FileAPI/include_test.cmake          |    9 +
 Tests/RunCMake/FileAPI/object/CMakeLists.txt       |   13 +
 .../FileAPIDummyFile.cmake}                        |    0
 .../RunCMake/FileAPIExternalSource/CMakeLists.txt  |   12 +
 .../empty.c                                        |    0
 Tests/RunCMake/RunCMake.cmake                      |    5 +
 Utilities/IWYU/mapping.imp                         |    2 +
 Utilities/cmjsoncpp/src/lib_json/json_reader.cpp   |    2 +-
 bootstrap                                          |    1 +
 112 files changed, 13177 insertions(+), 65 deletions(-)
 create mode 100644 Help/manual/cmake-file-api.7.rst
 create mode 100644 Help/release/dev/fileapi.rst
 create mode 100644 Source/cmFileAPI.cxx
 create mode 100644 Source/cmFileAPI.h
 create mode 100644 Source/cmFileAPICMakeFiles.cxx
 create mode 100644 Source/cmFileAPICMakeFiles.h
 create mode 100644 Source/cmFileAPICache.cxx
 create mode 100644 Source/cmFileAPICache.h
 create mode 100644 Source/cmFileAPICodemodel.cxx
 create mode 100644 Source/cmFileAPICodemodel.h
 create mode 100644 Source/cmString.cxx
 create mode 100644 Source/cmString.hxx
 create mode 100644 Source/cm_static_string_view.hxx
 create mode 100644 Source/cm_string_view.cxx
 create mode 100644 Source/cm_string_view.hxx
 create mode 100644 Tests/CMakeLib/testString.cxx
 copy Tests/RunCMake/{WriteBasicConfigVersionFile => FileAPI}/CMakeLists.txt (100%)
 create mode 100644 Tests/RunCMake/FileAPI/ClientStateful-check.cmake
 create mode 100644 Tests/RunCMake/FileAPI/ClientStateful-check.py
 create mode 100644 Tests/RunCMake/FileAPI/ClientStateful-prep.cmake
 copy Tests/{Wrapping/vtkIncluded.cxx => RunCMake/FileAPI/ClientStateful.cmake} (100%)
 create mode 100644 Tests/RunCMake/FileAPI/ClientStateless-check.cmake
 create mode 100644 Tests/RunCMake/FileAPI/ClientStateless-check.py
 create mode 100644 Tests/RunCMake/FileAPI/ClientStateless-prep.cmake
 copy Tests/{Wrapping/vtkIncluded.cxx => RunCMake/FileAPI/ClientStateless.cmake} (100%)
 create mode 100644 Tests/RunCMake/FileAPI/DuplicateStateless-check.cmake
 create mode 100644 Tests/RunCMake/FileAPI/DuplicateStateless-check.py
 create mode 100644 Tests/RunCMake/FileAPI/DuplicateStateless-prep.cmake
 copy Tests/{Wrapping/vtkIncluded.cxx => RunCMake/FileAPI/DuplicateStateless.cmake} (100%)
 create mode 100644 Tests/RunCMake/FileAPI/Empty-check.cmake
 create mode 100644 Tests/RunCMake/FileAPI/Empty-check.py
 create mode 100644 Tests/RunCMake/FileAPI/Empty-prep.cmake
 copy Tests/{Wrapping/vtkIncluded.cxx => RunCMake/FileAPI/Empty.cmake} (100%)
 create mode 100644 Tests/RunCMake/FileAPI/EmptyClient-check.cmake
 create mode 100644 Tests/RunCMake/FileAPI/EmptyClient-check.py
 create mode 100644 Tests/RunCMake/FileAPI/EmptyClient-prep.cmake
 copy Tests/{Wrapping/vtkIncluded.cxx => RunCMake/FileAPI/EmptyClient.cmake} (100%)
 create mode 100644 Tests/RunCMake/FileAPI/MixedStateless-check.cmake
 create mode 100644 Tests/RunCMake/FileAPI/MixedStateless-check.py
 create mode 100644 Tests/RunCMake/FileAPI/MixedStateless-prep.cmake
 copy Tests/{Wrapping/vtkIncluded.cxx => RunCMake/FileAPI/MixedStateless.cmake} (100%)
 create mode 100644 Tests/RunCMake/FileAPI/Nothing-check.cmake
 create mode 100644 Tests/RunCMake/FileAPI/Nothing-prep.cmake
 copy Tests/{Wrapping/vtkIncluded.cxx => RunCMake/FileAPI/Nothing.cmake} (100%)
 create mode 100644 Tests/RunCMake/FileAPI/RunCMakeTest.cmake
 create mode 100644 Tests/RunCMake/FileAPI/SharedStateless-check.cmake
 create mode 100644 Tests/RunCMake/FileAPI/SharedStateless-check.py
 create mode 100644 Tests/RunCMake/FileAPI/SharedStateless-prep.cmake
 copy Tests/{Wrapping/vtkIncluded.cxx => RunCMake/FileAPI/SharedStateless.cmake} (100%)
 create mode 100644 Tests/RunCMake/FileAPI/Stale-check.cmake
 create mode 100644 Tests/RunCMake/FileAPI/Stale-prep.cmake
 copy Tests/{Wrapping/vtkIncluded.cxx => RunCMake/FileAPI/Stale.cmake} (100%)
 create mode 100644 Tests/RunCMake/FileAPI/alias/CMakeLists.txt
 create mode 100644 Tests/RunCMake/FileAPI/cache-v2-ClientStateful-check.cmake
 create mode 100644 Tests/RunCMake/FileAPI/cache-v2-ClientStateful-prep.cmake
 create mode 100644 Tests/RunCMake/FileAPI/cache-v2-ClientStateless-check.cmake
 create mode 100644 Tests/RunCMake/FileAPI/cache-v2-ClientStateless-prep.cmake
 create mode 100644 Tests/RunCMake/FileAPI/cache-v2-SharedStateless-check.cmake
 create mode 100644 Tests/RunCMake/FileAPI/cache-v2-SharedStateless-prep.cmake
 create mode 100644 Tests/RunCMake/FileAPI/cache-v2-check.py
 create mode 100644 Tests/RunCMake/FileAPI/cache-v2.cmake
 create mode 100644 Tests/RunCMake/FileAPI/check_index.py
 create mode 100644 Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateful-check.cmake
 create mode 100644 Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateful-prep.cmake
 create mode 100644 Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateless-check.cmake
 create mode 100644 Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateless-prep.cmake
 create mode 100644 Tests/RunCMake/FileAPI/cmakeFiles-v1-SharedStateless-check.cmake
 create mode 100644 Tests/RunCMake/FileAPI/cmakeFiles-v1-SharedStateless-prep.cmake
 create mode 100644 Tests/RunCMake/FileAPI/cmakeFiles-v1-check.py
 create mode 100644 Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake
 create mode 100644 Tests/RunCMake/FileAPI/codemodel-v2-ClientStateful-check.cmake
 create mode 100644 Tests/RunCMake/FileAPI/codemodel-v2-ClientStateful-prep.cmake
 create mode 100644 Tests/RunCMake/FileAPI/codemodel-v2-ClientStateless-check.cmake
 create mode 100644 Tests/RunCMake/FileAPI/codemodel-v2-ClientStateless-prep.cmake
 create mode 100644 Tests/RunCMake/FileAPI/codemodel-v2-SharedStateless-check.cmake
 create mode 100644 Tests/RunCMake/FileAPI/codemodel-v2-SharedStateless-prep.cmake
 create mode 100644 Tests/RunCMake/FileAPI/codemodel-v2-check.py
 create mode 100644 Tests/RunCMake/FileAPI/codemodel-v2.cmake
 create mode 100644 Tests/RunCMake/FileAPI/custom/CMakeLists.txt
 create mode 100644 Tests/RunCMake/FileAPI/cxx/CMakeLists.txt
 create mode 100644 Tests/RunCMake/FileAPI/dir/CMakeLists.txt
 copy Tests/RunCMake/{get_property/directory_properties/sub2 => FileAPI/dir/dir}/CMakeLists.txt (100%)
 copy Tests/{Wrapping/vtkIncluded.cxx => RunCMake/FileAPI/dir/dirtest.cmake} (100%)
 copy Tests/RunCMake/{target_link_libraries => FileAPI}/empty.c (100%)
 copy Tests/{Wrapping/vtkIncluded.cxx => RunCMake/FileAPI/empty.cxx} (100%)
 create mode 100644 Tests/RunCMake/FileAPI/imported/CMakeLists.txt
 create mode 100644 Tests/RunCMake/FileAPI/include_test.cmake
 create mode 100644 Tests/RunCMake/FileAPI/object/CMakeLists.txt
 copy Tests/{Wrapping/vtkIncluded.cxx => RunCMake/FileAPIDummyFile.cmake} (100%)
 create mode 100644 Tests/RunCMake/FileAPIExternalSource/CMakeLists.txt
 copy Tests/RunCMake/{target_link_libraries => FileAPIExternalSource}/empty.c (100%)


hooks/post-receive
-- 
CMake


More information about the Cmake-commits mailing list