From e43486a63919b64fd2eef5d60c9fca1cea83ca94 Mon Sep 17 00:00:00 2001
From: Justin Goshi <jgoshi@microsoft.com>
Date: Thu, 18 Jun 2020 15:42:15 -0700
Subject: cmGeneratorTarget: Clarify name of language property lookup helper

---
 Source/cmGeneratorTarget.cxx | 14 ++++++++------
 Source/cmGeneratorTarget.h   |  4 ++--
 2 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 1f66a9f..d537be7 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -959,16 +959,17 @@ cmProp cmGeneratorTarget::GetLanguageStandard(std::string const& lang,
   return this->Target->GetProperty(cmStrCat(lang, "_STANDARD"));
 }
 
-cmProp cmGeneratorTarget::GetLanguageStandardProperty(std::string const& lang,
-                                                      const char* suffix) const
+cmProp cmGeneratorTarget::GetPropertyWithPairedLanguageSupport(
+  std::string const& lang, const char* suffix) const
 {
   cmProp propertyValue = this->Target->GetProperty(cmStrCat(lang, suffix));
   if (propertyValue == nullptr) {
     // Check if we should use the value set by another language.
     if (lang == "OBJC") {
-      propertyValue = this->GetLanguageStandardProperty("C", suffix);
+      propertyValue = this->GetPropertyWithPairedLanguageSupport("C", suffix);
     } else if (lang == "OBJCXX" || lang == "CUDA") {
-      propertyValue = this->GetLanguageStandardProperty("CXX", suffix);
+      propertyValue =
+        this->GetPropertyWithPairedLanguageSupport("CXX", suffix);
     }
   }
   return propertyValue;
@@ -976,13 +977,14 @@ cmProp cmGeneratorTarget::GetLanguageStandardProperty(std::string const& lang,
 
 cmProp cmGeneratorTarget::GetLanguageExtensions(std::string const& lang) const
 {
-  return this->GetLanguageStandardProperty(lang, "_EXTENSIONS");
+  return this->GetPropertyWithPairedLanguageSupport(lang, "_EXTENSIONS");
 }
 
 bool cmGeneratorTarget::GetLanguageStandardRequired(
   std::string const& lang) const
 {
-  cmProp p = this->GetLanguageStandardProperty(lang, "_STANDARD_REQUIRED");
+  cmProp p =
+    this->GetPropertyWithPairedLanguageSupport(lang, "_STANDARD_REQUIRED");
   return p && cmIsOn(*p);
 }
 
diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h
index a71e64c..cd53611 100644
--- a/Source/cmGeneratorTarget.h
+++ b/Source/cmGeneratorTarget.h
@@ -1052,8 +1052,8 @@ private:
 
   mutable std::map<std::string, std::string> LanguageStandardMap;
 
-  cmProp GetLanguageStandardProperty(std::string const& lang,
-                                     const char* suffix) const;
+  cmProp GetPropertyWithPairedLanguageSupport(std::string const& lang,
+                                              const char* suffix) const;
 
 public:
   const std::vector<const cmGeneratorTarget*>& GetLinkImplementationClosure(
-- 
cgit v0.12


From ba835874a43599bcc5ee2f8321662eb34fb341aa Mon Sep 17 00:00:00 2001
From: Justin Goshi <jgoshi@microsoft.com>
Date: Thu, 18 Jun 2020 15:42:15 -0700
Subject: Add backtrace support for language standard

---
 Source/cmGeneratorTarget.cxx | 37 +++++++++++++++++-------
 Source/cmGeneratorTarget.h   |  5 +++-
 Source/cmMakefile.cxx        |  6 ++--
 Source/cmTarget.cxx          | 69 ++++++++++++++++++++++++++++++++++++++++++++
 Source/cmTarget.h            |  7 +++++
 5 files changed, 109 insertions(+), 15 deletions(-)

diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index d537be7..992682f 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -947,8 +947,8 @@ bool cmGeneratorTarget::HasExplicitObjectName(cmSourceFile const* file) const
   return it != this->ExplicitObjectName.end();
 }
 
-cmProp cmGeneratorTarget::GetLanguageStandard(std::string const& lang,
-                                              std::string const& config) const
+BT<std::string> const* cmGeneratorTarget::GetLanguageStandardProperty(
+  std::string const& lang, std::string const& config) const
 {
   std::string key = cmStrCat(cmSystemTools::UpperCase(config), '-', lang);
   auto langStandardIter = this->LanguageStandardMap.find(key);
@@ -956,7 +956,21 @@ cmProp cmGeneratorTarget::GetLanguageStandard(std::string const& lang,
     return &langStandardIter->second;
   }
 
-  return this->Target->GetProperty(cmStrCat(lang, "_STANDARD"));
+  return this->Target->GetLanguageStandardProperty(
+    cmStrCat(lang, "_STANDARD"));
+}
+
+cmProp cmGeneratorTarget::GetLanguageStandard(std::string const& lang,
+                                              std::string const& config) const
+{
+  BT<std::string> const* languageStandard =
+    this->GetLanguageStandardProperty(lang, config);
+
+  if (languageStandard) {
+    return &(languageStandard->Value);
+  }
+
+  return nullptr;
 }
 
 cmProp cmGeneratorTarget::GetPropertyWithPairedLanguageSupport(
@@ -4469,7 +4483,8 @@ bool cmGeneratorTarget::ComputeCompileFeatures(std::string const& config) const
     }
 
     if (!newRequiredStandard.empty()) {
-      this->LanguageStandardMap[key] = newRequiredStandard;
+      this->LanguageStandardMap[key] =
+        BT<std::string>(newRequiredStandard, f.Backtrace);
     }
   }
 
@@ -4480,15 +4495,15 @@ bool cmGeneratorTarget::ComputeCompileFeatures(
   std::string const& config, std::set<LanguagePair> const& languagePairs) const
 {
   for (const auto& language : languagePairs) {
-    cmProp generatorTargetLanguageStandard =
-      this->GetLanguageStandard(language.first, config);
+    BT<std::string> const* generatorTargetLanguageStandard =
+      this->GetLanguageStandardProperty(language.first, config);
     if (!generatorTargetLanguageStandard) {
       // If the standard isn't explicitly set we copy it over from the
       // specified paired language.
       std::string key =
         cmStrCat(cmSystemTools::UpperCase(config), '-', language.first);
-      cmProp standardToCopy =
-        this->GetLanguageStandard(language.second, config);
+      BT<std::string> const* standardToCopy =
+        this->GetLanguageStandardProperty(language.second, config);
       if (standardToCopy != nullptr) {
         this->LanguageStandardMap[key] = *standardToCopy;
         generatorTargetLanguageStandard = &this->LanguageStandardMap[key];
@@ -4496,7 +4511,7 @@ bool cmGeneratorTarget::ComputeCompileFeatures(
         cmProp defaultStandard = this->Makefile->GetDef(
           cmStrCat("CMAKE_", language.second, "_STANDARD_DEFAULT"));
         if (defaultStandard != nullptr) {
-          this->LanguageStandardMap[key] = *defaultStandard;
+          this->LanguageStandardMap[key] = BT<std::string>(*defaultStandard);
           generatorTargetLanguageStandard = &this->LanguageStandardMap[key];
         }
       }
@@ -4504,8 +4519,8 @@ bool cmGeneratorTarget::ComputeCompileFeatures(
       // Custom updates for the CUDA standard.
       if (generatorTargetLanguageStandard != nullptr &&
           language.first == "CUDA") {
-        if (*generatorTargetLanguageStandard == "98") {
-          this->LanguageStandardMap[key] = "03";
+        if (generatorTargetLanguageStandard->Value == "98") {
+          this->LanguageStandardMap[key].Value = "03";
         }
       }
     }
diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h
index cd53611..20f3a07 100644
--- a/Source/cmGeneratorTarget.h
+++ b/Source/cmGeneratorTarget.h
@@ -148,6 +148,9 @@ public:
   bool HasExplicitObjectName(cmSourceFile const* file) const;
   void AddExplicitObjectName(cmSourceFile const* sf);
 
+  BT<std::string> const* GetLanguageStandardProperty(
+    std::string const& lang, std::string const& config) const;
+
   cmProp GetLanguageStandard(std::string const& lang,
                              std::string const& config) const;
 
@@ -1050,7 +1053,7 @@ private:
   bool GetRPATH(const std::string& config, const std::string& prop,
                 std::string& rpath) const;
 
-  mutable std::map<std::string, std::string> LanguageStandardMap;
+  mutable std::map<std::string, BT<std::string>> LanguageStandardMap;
 
   cmProp GetPropertyWithPairedLanguageSupport(std::string const& lang,
                                               const char* suffix) const;
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 1f1c06a..3161e38 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -5070,7 +5070,7 @@ bool cmMakefile::AddRequiredTargetCxxFeature(cmTarget* target,
         target->GetProperty(cmStrCat(lang, "_STANDARD")), newRequiredStandard,
         error)) {
     if (!newRequiredStandard.empty()) {
-      target->SetProperty(cmStrCat(lang, "_STANDARD"), newRequiredStandard);
+      target->SetLanguageStandardProperty(lang, newRequiredStandard, feature);
     }
     return true;
   }
@@ -5251,7 +5251,7 @@ bool cmMakefile::AddRequiredTargetCudaFeature(cmTarget* target,
         target->GetProperty(cmStrCat(lang, "_STANDARD")), newRequiredStandard,
         error)) {
     if (!newRequiredStandard.empty()) {
-      target->SetProperty(cmStrCat(lang, "_STANDARD"), newRequiredStandard);
+      target->SetLanguageStandardProperty(lang, newRequiredStandard, feature);
     }
     return true;
   }
@@ -5356,7 +5356,7 @@ bool cmMakefile::AddRequiredTargetCFeature(cmTarget* target,
         target->GetProperty(cmStrCat(lang, "_STANDARD")), newRequiredStandard,
         error)) {
     if (!newRequiredStandard.empty()) {
-      target->SetProperty(cmStrCat(lang, "_STANDARD"), newRequiredStandard);
+      target->SetLanguageStandardProperty(lang, newRequiredStandard, feature);
     }
     return true;
   }
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index 36e1ad5..d3b37cb 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -7,6 +7,7 @@
 #include <cstring>
 #include <initializer_list>
 #include <iterator>
+#include <map>
 #include <set>
 #include <sstream>
 #include <unordered_set>
@@ -185,6 +186,7 @@ public:
   std::vector<cmInstallTargetGenerator*> InstallGenerators;
   std::set<std::string> SystemIncludeDirectories;
   cmTarget::LinkLibraryVectorType OriginalLinkLibraries;
+  std::map<std::string, BT<std::string>> LanguageStandardProperties;
   std::vector<std::string> IncludeDirectoriesEntries;
   std::vector<cmListFileBacktrace> IncludeDirectoriesBacktraces;
   std::vector<std::string> CompileOptionsEntries;
@@ -598,6 +600,35 @@ cmGlobalGenerator* cmTarget::GetGlobalGenerator() const
   return impl->Makefile->GetGlobalGenerator();
 }
 
+BT<std::string> const* cmTarget::GetLanguageStandardProperty(
+  const std::string& propertyName) const
+{
+  auto entry = impl->LanguageStandardProperties.find(propertyName);
+  if (entry != impl->LanguageStandardProperties.end()) {
+    return &entry->second;
+  }
+
+  return nullptr;
+}
+
+void cmTarget::SetLanguageStandardProperty(std::string const& lang,
+                                           std::string const& value,
+                                           const std::string& feature)
+{
+  cmListFileBacktrace featureBacktrace;
+  for (size_t i = 0; i < impl->CompileFeaturesEntries.size(); i++) {
+    if (impl->CompileFeaturesEntries[i] == feature) {
+      if (i < impl->CompileFeaturesBacktraces.size()) {
+        featureBacktrace = impl->CompileFeaturesBacktraces[i];
+      }
+      break;
+    }
+  }
+
+  impl->LanguageStandardProperties[cmStrCat(lang, "_STANDARD")] =
+    BT<std::string>(value, featureBacktrace);
+}
+
 void cmTarget::AddUtility(std::string const& name, bool cross, cmMakefile* mf)
 {
   impl->Utilities.insert(BT<std::pair<std::string, bool>>(
@@ -1127,6 +1158,11 @@ void cmTarget::SetProperty(const std::string& prop, const char* value)
     return;
   }
 #define MAKE_STATIC_PROP(PROP) static const std::string prop##PROP = #PROP
+  MAKE_STATIC_PROP(C_STANDARD);
+  MAKE_STATIC_PROP(CXX_STANDARD);
+  MAKE_STATIC_PROP(CUDA_STANDARD);
+  MAKE_STATIC_PROP(OBJC_STANDARD);
+  MAKE_STATIC_PROP(OBJCXX_STANDARD);
   MAKE_STATIC_PROP(COMPILE_DEFINITIONS);
   MAKE_STATIC_PROP(COMPILE_FEATURES);
   MAKE_STATIC_PROP(COMPILE_OPTIONS);
@@ -1310,6 +1346,15 @@ void cmTarget::SetProperty(const std::string& prop, const char* value)
     cmProp tmp = reusedTarget->GetProperty("COMPILE_PDB_NAME");
     this->SetProperty("COMPILE_PDB_NAME", tmp ? tmp->c_str() : nullptr);
     this->AddUtility(reusedFrom, false, impl->Makefile);
+  } else if (prop == propC_STANDARD || prop == propCXX_STANDARD ||
+             prop == propCUDA_STANDARD || prop == propOBJC_STANDARD ||
+             prop == propOBJCXX_STANDARD) {
+    if (value) {
+      impl->LanguageStandardProperties[prop] =
+        BT<std::string>(value, impl->Makefile->GetBacktrace());
+    } else {
+      impl->LanguageStandardProperties.erase(prop);
+    }
   } else {
     impl->Properties.SetProperty(prop, value);
   }
@@ -1413,6 +1458,11 @@ void cmTarget::AppendProperty(const std::string& prop,
   } else if (cmHasLiteralPrefix(prop, "IMPORTED_LIBNAME")) {
     impl->Makefile->IssueMessage(MessageType::FATAL_ERROR,
                                  prop + " property may not be APPENDed.");
+  } else if (prop == "C_STANDARD" || prop == "CXX_STANDARD" ||
+             prop == "CUDA_STANDARD" || prop == "OBJC_STANDARD" ||
+             prop == "OBJCXX_STANDARD") {
+    impl->Makefile->IssueMessage(MessageType::FATAL_ERROR,
+                                 prop + " property may not be appended.");
   } else {
     impl->Properties.AppendProperty(prop, value, asString);
   }
@@ -1626,6 +1676,11 @@ cmProp cmTarget::GetComputedProperty(const std::string& prop,
 cmProp cmTarget::GetProperty(const std::string& prop) const
 {
 #define MAKE_STATIC_PROP(PROP) static const std::string prop##PROP = #PROP
+  MAKE_STATIC_PROP(C_STANDARD);
+  MAKE_STATIC_PROP(CXX_STANDARD);
+  MAKE_STATIC_PROP(CUDA_STANDARD);
+  MAKE_STATIC_PROP(OBJC_STANDARD);
+  MAKE_STATIC_PROP(OBJCXX_STANDARD);
   MAKE_STATIC_PROP(LINK_LIBRARIES);
   MAKE_STATIC_PROP(TYPE);
   MAKE_STATIC_PROP(INCLUDE_DIRECTORIES);
@@ -1646,6 +1701,11 @@ cmProp cmTarget::GetProperty(const std::string& prop) const
   MAKE_STATIC_PROP(TRUE);
 #undef MAKE_STATIC_PROP
   static std::unordered_set<std::string> const specialProps{
+    propC_STANDARD,
+    propCXX_STANDARD,
+    propCUDA_STANDARD,
+    propOBJC_STANDARD,
+    propOBJCXX_STANDARD,
     propLINK_LIBRARIES,
     propTYPE,
     propINCLUDE_DIRECTORIES,
@@ -1664,6 +1724,15 @@ cmProp cmTarget::GetProperty(const std::string& prop) const
     propSOURCES
   };
   if (specialProps.count(prop)) {
+    if (prop == propC_STANDARD || prop == propCXX_STANDARD ||
+        prop == propCUDA_STANDARD || prop == propOBJC_STANDARD ||
+        prop == propOBJCXX_STANDARD) {
+      auto propertyIter = impl->LanguageStandardProperties.find(prop);
+      if (propertyIter == impl->LanguageStandardProperties.end()) {
+        return nullptr;
+      }
+      return &(propertyIter->second.Value);
+    }
     if (prop == propLINK_LIBRARIES) {
       if (impl->LinkImplementationPropertyEntries.empty()) {
         return nullptr;
diff --git a/Source/cmTarget.h b/Source/cmTarget.h
index f0ddb68..175822d 100644
--- a/Source/cmTarget.h
+++ b/Source/cmTarget.h
@@ -233,6 +233,13 @@ public:
   void AddSystemIncludeDirectories(std::set<std::string> const& incs);
   std::set<std::string> const& GetSystemIncludeDirectories() const;
 
+  BT<std::string> const* GetLanguageStandardProperty(
+    const std::string& propertyName) const;
+
+  void SetLanguageStandardProperty(std::string const& lang,
+                                   std::string const& value,
+                                   const std::string& feature);
+
   cmStringRange GetIncludeDirectoriesEntries() const;
   cmBacktraceRange GetIncludeDirectoriesBacktraces() const;
 
-- 
cgit v0.12


From 7d6861f367673b57bf45e14863f1104e8e28eafa Mon Sep 17 00:00:00 2001
From: Justin Goshi <jgoshi@microsoft.com>
Date: Thu, 18 Jun 2020 15:43:08 -0700
Subject: fileapi: Extend codemodel targets with language standard

---
 Help/manual/cmake-file-api.7.rst                   |  18 ++++
 Source/cmFileAPI.cxx                               |   2 +-
 Source/cmFileAPICodemodel.cxx                      |  36 +++++++
 .../RunCMake/CommandLine/E_capabilities-stdout.txt |   2 +-
 Tests/RunCMake/FileAPI/codemodel-v2-check.py       |  33 ++++++-
 .../FileAPI/codemodel-v2-data/directories/cxx.json |   2 +
 .../FileAPI/codemodel-v2-data/projects/cxx.json    |   2 +
 .../codemodel-v2-data/targets/all_build_cxx.json   |   8 ++
 .../codemodel-v2-data/targets/all_build_top.json   |   8 ++
 .../targets/cxx_standard_compile_feature_exe.json  | 110 +++++++++++++++++++++
 ...ndard_compile_feature_exe_languagestandard.json |  22 +++++
 .../targets/cxx_standard_exe.json                  | 110 +++++++++++++++++++++
 Tests/RunCMake/FileAPI/cxx/CMakeLists.txt          |  10 ++
 13 files changed, 360 insertions(+), 3 deletions(-)
 create mode 100644 Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_standard_compile_feature_exe.json
 create mode 100644 Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_standard_compile_feature_exe_languagestandard.json
 create mode 100644 Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_standard_exe.json

diff --git a/Help/manual/cmake-file-api.7.rst b/Help/manual/cmake-file-api.7.rst
index 91be31d..b54859c 100644
--- a/Help/manual/cmake-file-api.7.rst
+++ b/Help/manual/cmake-file-api.7.rst
@@ -869,6 +869,24 @@ with members:
     A string specifying the language (e.g. ``C``, ``CXX``, ``Fortran``)
     of the toolchain is used to compile the source file.
 
+  ``languageStandard``
+    Optional member that is present when the language standard is set
+    explicitly (e.g. via :prop_tgt:`CXX_STANDARD`) or implicitly by
+    compile features.  Each entry is a JSON object with two members:
+
+    ``backtraces``
+      Optional member that is present when a CMake language backtrace to
+      the ``<LANG>_STANDARD`` setting is available.  If the language
+      standard was set implicitly by compile features those are used as
+      the backtrace(s).  It's possible for multiple compile features to
+      require the same language standard so there could be multiple
+      backtraces. The value is a JSON array with each entry being an
+      unsigned integer 0-based index into the ``backtraceGraph``
+      member's ``nodes`` array.
+
+    ``standard``
+      String representing the language standard.
+
   ``compileCommandFragments``
     Optional member that is present when fragments of the compiler command
     line invocation are available.  The value is a JSON array of entries
diff --git a/Source/cmFileAPI.cxx b/Source/cmFileAPI.cxx
index 594969b..c2ab2f1 100644
--- a/Source/cmFileAPI.cxx
+++ b/Source/cmFileAPI.cxx
@@ -665,7 +665,7 @@ std::string cmFileAPI::NoSupportedVersion(
 
 // The "codemodel" object kind.
 
-static unsigned int const CodeModelV2Minor = 1;
+static unsigned int const CodeModelV2Minor = 2;
 
 void cmFileAPI::BuildClientRequestCodeModel(
   ClientRequest& r, std::vector<RequestVersion> const& versions)
diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx
index fe331ec..21d9abb 100644
--- a/Source/cmFileAPICodemodel.cxx
+++ b/Source/cmFileAPICodemodel.cxx
@@ -277,6 +277,7 @@ struct CompileData
 
   std::string Language;
   std::string Sysroot;
+  JBT<std::string> LanguageStandard;
   std::vector<JBT<std::string>> Flags;
   std::vector<JBT<std::string>> Defines;
   std::vector<JBT<std::string>> PrecompileHeaders;
@@ -287,6 +288,7 @@ struct CompileData
     return (l.Language == r.Language && l.Sysroot == r.Sysroot &&
             l.Flags == r.Flags && l.Defines == r.Defines &&
             l.PrecompileHeaders == r.PrecompileHeaders &&
+            l.LanguageStandard == r.LanguageStandard &&
             l.Includes == r.Includes);
   }
 };
@@ -320,6 +322,10 @@ struct hash<CompileData>
       result = result ^ hash<std::string>()(i.Value) ^
         hash<Json::ArrayIndex>()(i.Backtrace.Index);
     }
+    if (!in.LanguageStandard.Value.empty()) {
+      result = result ^ hash<std::string>()(in.LanguageStandard.Value) ^
+        hash<Json::ArrayIndex>()(in.LanguageStandard.Backtrace.Index);
+    }
     return result;
   }
 };
@@ -377,6 +383,7 @@ class Target
   Json::Value DumpCompileData(CompileData const& cd);
   Json::Value DumpInclude(CompileData::IncludeEntry const& inc);
   Json::Value DumpPrecompileHeader(JBT<std::string> const& header);
+  Json::Value DumpLanguageStandard(JBT<std::string> const& standard);
   Json::Value DumpDefine(JBT<std::string> const& def);
   Json::Value DumpSources();
   Json::Value DumpSource(cmGeneratorTarget::SourceAndKind const& sk,
@@ -838,6 +845,11 @@ void Target::ProcessLanguage(std::string const& lang)
   for (BT<std::string> const& pch : precompileHeaders) {
     cd.PrecompileHeaders.emplace_back(this->ToJBT(pch));
   }
+  BT<std::string> const* languageStandard =
+    this->GT->GetLanguageStandardProperty(lang, this->Config);
+  if (languageStandard) {
+    cd.LanguageStandard = this->ToJBT(*languageStandard);
+  }
 }
 
 Json::ArrayIndex Target::AddSourceGroup(cmSourceGroup* sg, Json::ArrayIndex si)
@@ -996,6 +1008,9 @@ CompileData Target::MergeCompileData(CompileData const& fd)
   // All compile groups share the precompile headers of the target.
   cd.PrecompileHeaders = td.PrecompileHeaders;
 
+  // All compile groups share the language standard of the target.
+  cd.LanguageStandard = td.LanguageStandard;
+
   // Use target-wide flags followed by source-specific flags.
   cd.Flags.reserve(td.Flags.size() + fd.Flags.size());
   cd.Flags.insert(cd.Flags.end(), td.Flags.begin(), td.Flags.end());
@@ -1153,6 +1168,10 @@ Json::Value Target::DumpCompileData(CompileData const& cd)
     }
     result["precompileHeaders"] = std::move(precompileHeaders);
   }
+  if (!cd.LanguageStandard.Value.empty()) {
+    result["languageStandard"] =
+      this->DumpLanguageStandard(cd.LanguageStandard);
+  }
 
   return result;
 }
@@ -1176,6 +1195,23 @@ Json::Value Target::DumpPrecompileHeader(JBT<std::string> const& header)
   return precompileHeader;
 }
 
+Json::Value Target::DumpLanguageStandard(JBT<std::string> const& standard)
+{
+  Json::Value languageStandard = Json::objectValue;
+  languageStandard["standard"] = standard.Value;
+  if (standard.Backtrace) {
+    // Only one backtrace is currently stored for a given language standard,
+    // but we represent this as an array because it's possible for multiple
+    // compile features to set the same language standard value. Representing
+    // this as an array will allow things to just work once we support storing
+    // multiple backtraces for a language standard value.
+    Json::Value backtraces = Json::arrayValue;
+    backtraces.append(standard.Backtrace.Index);
+    languageStandard["backtraces"] = backtraces;
+  }
+  return languageStandard;
+}
+
 Json::Value Target::DumpDefine(JBT<std::string> const& def)
 {
   Json::Value define = Json::objectValue;
diff --git a/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt b/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt
index 03286f1..e24e131 100644
--- a/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt
+++ b/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt
@@ -1 +1 @@
-^{"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":1}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":0}]}]},"generators":\[.*\],"serverMode":true,"version":{.*}}$
+^{"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":2}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":0}]}]},"generators":\[.*\],"serverMode":true,"version":{.*}}$
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-check.py b/Tests/RunCMake/FileAPI/codemodel-v2-check.py
index a3dd9ff..b567bf1 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-check.py
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-check.py
@@ -12,7 +12,7 @@ def read_codemodel_json_data(filename):
 def check_objects(o, g):
     assert is_list(o)
     assert len(o) == 1
-    check_index_object(o[0], "codemodel", 2, 1, check_object_codemodel(g))
+    check_index_object(o[0], "codemodel", 2, 2, check_object_codemodel(g))
 
 def check_backtrace(t, b, backtrace):
     btg = t["backtraceGraph"]
@@ -42,6 +42,16 @@ def check_backtrace(t, b, backtrace):
 
     assert b is None
 
+def check_backtraces(t, actual, expected):
+    assert is_list(actual)
+    assert is_list(expected)
+    assert len(actual) == len(expected)
+
+    i = 0
+    while i < len(actual):
+        check_backtrace(t, actual[i], expected[i])
+        i += 1
+
 def check_directory(c):
     def _check(actual, expected):
         assert is_dict(actual)
@@ -421,6 +431,19 @@ def check_target(c):
                                      missing_exception=lambda e: "Precompile header: %s" % e["header"],
                                      extra_exception=lambda a: "Precompile header: %s" % a["header"])
 
+                if "languageStandard" in expected:
+                    expected_keys.append("languageStandard")
+
+                    def check_language_standard(actual, expected):
+                        assert is_dict(actual)
+                        expected_keys = ["backtraces", "standard"]
+                        assert actual["standard"] == expected["standard"]
+                        check_backtraces(obj, actual["backtraces"], expected["backtraces"])
+
+                        assert sorted(actual.keys()) == sorted(expected_keys)
+
+                    check_language_standard(actual["languageStandard"], expected["languageStandard"])
+
                 if expected["defines"] is not None:
                     expected_keys.append("defines")
 
@@ -544,6 +567,8 @@ def gen_check_targets(c, g, inSource):
         read_codemodel_json_data("targets/zero_check_cxx.json"),
         read_codemodel_json_data("targets/cxx_lib.json"),
         read_codemodel_json_data("targets/cxx_exe.json"),
+        read_codemodel_json_data("targets/cxx_standard_compile_feature_exe.json"),
+        read_codemodel_json_data("targets/cxx_standard_exe.json"),
         read_codemodel_json_data("targets/cxx_shared_lib.json"),
         read_codemodel_json_data("targets/cxx_shared_exe.json"),
         read_codemodel_json_data("targets/cxx_static_lib.json"),
@@ -592,6 +617,12 @@ def gen_check_targets(c, g, inSource):
                 e["sources"] = precompile_header_data["sources"]
                 e["sourceGroups"] = precompile_header_data["sourceGroups"]
 
+    if os.path.exists(os.path.join(reply_dir, "..", "..", "..", "..", "cxx", "cxx_std_11.txt")):
+        for e in expected:
+            if e["name"] == "cxx_standard_compile_feature_exe":
+                language_standard_data = read_codemodel_json_data("targets/cxx_standard_compile_feature_exe_languagestandard.json")
+                e["compileGroups"][0]["languageStandard"] = language_standard_data["languageStandard"]
+
     if not os.path.exists(os.path.join(reply_dir, "..", "..", "..", "..", "ipo_enabled.txt")):
         for e in expected:
             try:
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.json
index ebe717a..a51b6eb 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.json
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.json
@@ -7,6 +7,8 @@
         "^ALL_BUILD::@a56b12a3f5c0529fb296$",
         "^ZERO_CHECK::@a56b12a3f5c0529fb296$",
         "^cxx_exe::@a56b12a3f5c0529fb296$",
+        "^cxx_standard_compile_feature_exe::@a56b12a3f5c0529fb296$",
+        "^cxx_standard_exe::@a56b12a3f5c0529fb296$",
         "^cxx_lib::@a56b12a3f5c0529fb296$",
         "^cxx_shared_exe::@a56b12a3f5c0529fb296$",
         "^cxx_shared_lib::@a56b12a3f5c0529fb296$",
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/projects/cxx.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/projects/cxx.json
index 296ae6c..363e853 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-data/projects/cxx.json
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/projects/cxx.json
@@ -10,6 +10,8 @@
         "^ZERO_CHECK::@a56b12a3f5c0529fb296$",
         "^cxx_lib::@a56b12a3f5c0529fb296$",
         "^cxx_exe::@a56b12a3f5c0529fb296$",
+        "^cxx_standard_compile_feature_exe::@a56b12a3f5c0529fb296$",
+        "^cxx_standard_exe::@a56b12a3f5c0529fb296$",
         "^cxx_shared_lib::@a56b12a3f5c0529fb296$",
         "^cxx_shared_exe::@a56b12a3f5c0529fb296$",
         "^cxx_static_lib::@a56b12a3f5c0529fb296$",
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_cxx.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_cxx.json
index 92a7944..1f443b1 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_cxx.json
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_cxx.json
@@ -80,6 +80,14 @@
             "backtrace": null
         },
         {
+            "id": "^cxx_standard_compile_feature_exe::@a56b12a3f5c0529fb296$",
+            "backtrace": null
+        },
+        {
+            "id": "^cxx_standard_exe::@a56b12a3f5c0529fb296$",
+            "backtrace": null
+        },
+        {
             "id": "^cxx_shared_lib::@a56b12a3f5c0529fb296$",
             "backtrace": null
         },
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_top.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_top.json
index b4def78..59bd750 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_top.json
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_top.json
@@ -116,6 +116,14 @@
             "backtrace": null
         },
         {
+            "id": "^cxx_standard_compile_feature_exe::@a56b12a3f5c0529fb296$",
+            "backtrace": null
+        },
+        {
+            "id": "^cxx_standard_exe::@a56b12a3f5c0529fb296$",
+            "backtrace": null
+        },
+        {
             "id": "^cxx_shared_lib::@a56b12a3f5c0529fb296$",
             "backtrace": null
         },
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_standard_compile_feature_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_standard_compile_feature_exe.json
new file mode 100644
index 0000000..d6d573f
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_standard_compile_feature_exe.json
@@ -0,0 +1,110 @@
+{
+    "name": "cxx_standard_compile_feature_exe",
+    "id": "^cxx_standard_compile_feature_exe::@a56b12a3f5c0529fb296$",
+    "directorySource": "^cxx$",
+    "projectName": "Cxx",
+    "type": "EXECUTABLE",
+    "isGeneratorProvided": null,
+    "sources": [
+        {
+            "path": "^empty\\.cxx$",
+            "isGenerated": null,
+            "sourceGroupName": "Source Files",
+            "compileGroupLanguage": "CXX",
+            "backtrace": [
+                {
+                    "file": "^cxx/CMakeLists\\.txt$",
+                    "line": 26,
+                    "command": "add_executable",
+                    "hasParent": true
+                },
+                {
+                    "file": "^cxx/CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                }
+            ]
+        }
+    ],
+    "sourceGroups": [
+        {
+            "name": "Source Files",
+            "sourcePaths": [
+                "^empty\\.cxx$"
+            ]
+        }
+    ],
+    "compileGroups": [
+        {
+            "language": "CXX",
+            "languageStandard" :
+            {
+                "backtraces": [
+                    [
+                        {
+                            "file": "^cxx/CMakeLists\\.txt$",
+                            "line": 27,
+                            "command": "set_property",
+                            "hasParent": true
+                        },
+                        {
+                            "file": "^cxx/CMakeLists\\.txt$",
+                            "line": null,
+                            "command": null,
+                            "hasParent": false
+                        }
+                    ]
+                ],
+                "standard" : "98"
+            },
+            "sourcePaths": [
+                "^empty\\.cxx$"
+            ],
+            "includes": null,
+            "defines": null,
+            "compileCommandFragments": null
+        }
+    ],
+    "backtrace": [
+        {
+            "file": "^cxx/CMakeLists\\.txt$",
+            "line": 26,
+            "command": "add_executable",
+            "hasParent": true
+        },
+        {
+            "file": "^cxx/CMakeLists\\.txt$",
+            "line": null,
+            "command": null,
+            "hasParent": false
+        }
+    ],
+    "folder": null,
+    "nameOnDisk": "^cxx_standard_compile_feature_exe(\\.exe)?$",
+    "artifacts": [
+        {
+            "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_standard_compile_feature_exe(\\.exe)?$",
+            "_dllExtra": false
+        },
+        {
+            "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_standard_compile_feature_exe\\.pdb$",
+            "_dllExtra": true
+        }
+    ],
+    "build": "^cxx$",
+    "source": "^cxx$",
+    "install": null,
+    "link": {
+        "language": "CXX",
+        "lto": null,
+        "commandFragments": null
+    },
+    "archive": null,
+    "dependencies": [
+        {
+            "id": "^ZERO_CHECK::@a56b12a3f5c0529fb296$",
+            "backtrace": null
+        }
+    ]
+}
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_standard_compile_feature_exe_languagestandard.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_standard_compile_feature_exe_languagestandard.json
new file mode 100644
index 0000000..57b4161
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_standard_compile_feature_exe_languagestandard.json
@@ -0,0 +1,22 @@
+{
+    "languageStandard" :
+    {
+      "backtraces": [
+          [
+              {
+                  "file": "^cxx/CMakeLists\\.txt$",
+                  "line": 29,
+                  "command": "target_compile_features",
+                  "hasParent": true
+              },
+              {
+                  "file": "^cxx/CMakeLists\\.txt$",
+                  "line": null,
+                  "command": null,
+                  "hasParent": false
+              }
+          ]
+        ],
+        "standard" : "11"
+    }
+}
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_standard_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_standard_exe.json
new file mode 100644
index 0000000..9cb2832
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_standard_exe.json
@@ -0,0 +1,110 @@
+{
+    "name": "cxx_standard_exe",
+    "id": "^cxx_standard_exe::@a56b12a3f5c0529fb296$",
+    "directorySource": "^cxx$",
+    "projectName": "Cxx",
+    "type": "EXECUTABLE",
+    "isGeneratorProvided": null,
+    "sources": [
+        {
+            "path": "^empty\\.cxx$",
+            "isGenerated": null,
+            "sourceGroupName": "Source Files",
+            "compileGroupLanguage": "CXX",
+            "backtrace": [
+                {
+                    "file": "^cxx/CMakeLists\\.txt$",
+                    "line": 23,
+                    "command": "add_executable",
+                    "hasParent": true
+                },
+                {
+                    "file": "^cxx/CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                }
+            ]
+        }
+    ],
+    "sourceGroups": [
+        {
+            "name": "Source Files",
+            "sourcePaths": [
+                "^empty\\.cxx$"
+            ]
+        }
+    ],
+    "compileGroups": [
+        {
+            "language": "CXX",
+            "languageStandard" :
+            {
+                "backtraces": [
+                    [
+                        {
+                            "file": "^cxx/CMakeLists\\.txt$",
+                            "line": 24,
+                            "command": "set_property",
+                            "hasParent": true
+                        },
+                        {
+                            "file": "^cxx/CMakeLists\\.txt$",
+                            "line": null,
+                            "command": null,
+                            "hasParent": false
+                        }
+                    ]
+                ],
+                "standard" : "17"
+            },
+            "sourcePaths": [
+                "^empty\\.cxx$"
+            ],
+            "includes": null,
+            "defines": null,
+            "compileCommandFragments": null
+        }
+    ],
+    "backtrace": [
+        {
+            "file": "^cxx/CMakeLists\\.txt$",
+            "line": 23,
+            "command": "add_executable",
+            "hasParent": true
+        },
+        {
+            "file": "^cxx/CMakeLists\\.txt$",
+            "line": null,
+            "command": null,
+            "hasParent": false
+        }
+    ],
+    "folder": null,
+    "nameOnDisk": "^cxx_standard_exe(\\.exe)?$",
+    "artifacts": [
+        {
+            "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_standard_exe(\\.exe)?$",
+            "_dllExtra": false
+        },
+        {
+            "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_standard_exe\\.pdb$",
+            "_dllExtra": true
+        }
+    ],
+    "build": "^cxx$",
+    "source": "^cxx$",
+    "install": null,
+    "link": {
+        "language": "CXX",
+        "lto": null,
+        "commandFragments": null
+    },
+    "archive": null,
+    "dependencies": [
+        {
+            "id": "^ZERO_CHECK::@a56b12a3f5c0529fb296$",
+            "backtrace": null
+        }
+    ]
+}
diff --git a/Tests/RunCMake/FileAPI/cxx/CMakeLists.txt b/Tests/RunCMake/FileAPI/cxx/CMakeLists.txt
index fa51195..5758cc4 100644
--- a/Tests/RunCMake/FileAPI/cxx/CMakeLists.txt
+++ b/Tests/RunCMake/FileAPI/cxx/CMakeLists.txt
@@ -19,3 +19,13 @@ target_link_options(cxx_exe PUBLIC TargetLinkOptions)
 target_link_directories(cxx_exe PUBLIC "${CMAKE_BINARY_DIR}/TargetLinkDir")
 
 target_precompile_headers(cxx_exe PUBLIC ../empty.h)
+
+add_executable(cxx_standard_exe ../empty.cxx)
+set_property(TARGET cxx_standard_exe PROPERTY CXX_STANDARD 17)
+
+add_executable(cxx_standard_compile_feature_exe ../empty.cxx)
+set_property(TARGET cxx_standard_compile_feature_exe PROPERTY CXX_STANDARD 98)
+if(CMAKE_CXX_STANDARD_DEFAULT AND DEFINED CMAKE_CXX11_STANDARD_COMPILE_OPTION)
+  target_compile_features(cxx_standard_compile_feature_exe PRIVATE cxx_std_11)
+  file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cxx_std_11.txt" "")
+endif()
-- 
cgit v0.12