Browse Source

Merge topic 'cmakeServerSourcesForInterfaceLibraries'

d74c2282ea cmake-server: Support codemodel filegroups for INTERFACE_SOURCES

Acked-by: Kitware Robot <[email protected]>
Acked-by: Tobias Hunger <[email protected]>
Acked-by: Markus Enzenberger <[email protected]>
Merge-request: !2282
Brad King 7 years ago
parent
commit
69ce062969
3 changed files with 191 additions and 40 deletions
  1. 7 0
      Help/manual/cmake-server.7.rst
  2. 1 0
      Source/cmServerDictionary.h
  3. 183 40
      Source/cmServerProtocol.cxx

+ 7 - 0
Help/manual/cmake-server.7.rst

@@ -308,6 +308,9 @@ which will result in a response type "reply"::
 
 indicating that the server is ready for action.
 
+Protocol version 1.3 introduces an optional flag on the target filegroup
+that indicates if the filegroup represents :prop_tgt:`INTERFACE_SOURCES`.
+
 
 Type "globalSettings"
 ^^^^^^^^^^^^^^^^^^^^^
@@ -524,6 +527,8 @@ FileGroups are used to group sources using similar settings together.
 
 Each fileGroup object may contain the following keys:
 
+"isInterfaceSources"
+  true if the fileGroup represents :prop_tgt:`INTERFACE_SOURCES`.
 "language"
   contains the programming language used by all files in the group.
 "compileFlags"
@@ -538,6 +543,8 @@ Each fileGroup object may contain the following keys:
 "defines"
   with a list of defines in the form "SOMEVALUE" or "SOMEVALUE=42". This
   value is encoded in the system's native shell format.
+"isGenerated"
+  true if the files were generated.
 "sources"
   with a list of source files.
 

+ 1 - 0
Source/cmServerDictionary.h

@@ -96,6 +96,7 @@ static const std::string kCTEST_COMMAND = "ctestCommand";
 static const std::string kCTEST_INFO = "ctestInfo";
 static const std::string kMINIMUM_CMAKE_VERSION = "minimumCMakeVersion";
 static const std::string kIS_GENERATOR_PROVIDED_KEY = "isGeneratorProvided";
+static const std::string kIS_INTERFACE_SOURCES_KEY = "isInterfaceSources";
 
 static const std::string kSTART_MAGIC = "[== \"CMake Server\" ==[";
 static const std::string kEND_MAGIC = "]== \"CMake Server\" ==]";

+ 183 - 40
Source/cmServerProtocol.cxx

@@ -256,7 +256,12 @@ bool cmServerProtocol::DoActivate(const cmServerRequest& /*request*/,
 
 std::pair<int, int> cmServerProtocol1::ProtocolVersion() const
 {
-  return std::make_pair(1, 2);
+  // Revision history
+  // 1, 1 - Report backtraces in codemodel response
+  // 1, 2 - Add target install destinations to codemodel
+  // 1, 3 - Add a flag to target filegroups indicating whether or not the
+  // filegroup is for INTERFACE_SOURCES
+  return std::make_pair(1, 3);
 }
 
 static void setErrorMessage(std::string* errorMessage, const std::string& text)
@@ -593,6 +598,8 @@ cmServerResponse cmServerProtocol1::ProcessCMakeInputs(
   return request.Reply(result);
 }
 
+const std::string kInterfaceSourcesLanguageDataKey =
+  "INTERFACE_SOURCES_LD_KEY";
 class LanguageData
 {
 public:
@@ -625,6 +632,12 @@ void LanguageData::SetDefines(const std::set<std::string>& defines)
   Defines = std::move(result);
 }
 
+struct FileGroupSources
+{
+  bool IsInterfaceSources;
+  std::vector<std::string> Files;
+};
+
 namespace std {
 
 template <>
@@ -652,31 +665,35 @@ struct hash<LanguageData>
 } // namespace std
 
 static Json::Value DumpSourceFileGroup(const LanguageData& data,
+                                       bool isInterfaceSource,
                                        const std::vector<std::string>& files,
                                        const std::string& baseDir)
 {
   Json::Value result = Json::objectValue;
 
+  if (isInterfaceSource) {
+    result[kIS_INTERFACE_SOURCES_KEY] = true;
+  }
   if (!data.Language.empty()) {
     result[kLANGUAGE_KEY] = data.Language;
-    if (!data.Flags.empty()) {
-      result[kCOMPILE_FLAGS_KEY] = data.Flags;
-    }
-    if (!data.IncludePathList.empty()) {
-      Json::Value includes = Json::arrayValue;
-      for (auto const& i : data.IncludePathList) {
-        Json::Value tmp = Json::objectValue;
-        tmp[kPATH_KEY] = i.first;
-        if (i.second) {
-          tmp[kIS_SYSTEM_KEY] = i.second;
-        }
-        includes.append(tmp);
+  }
+  if (!data.Flags.empty()) {
+    result[kCOMPILE_FLAGS_KEY] = data.Flags;
+  }
+  if (!data.IncludePathList.empty()) {
+    Json::Value includes = Json::arrayValue;
+    for (auto const& i : data.IncludePathList) {
+      Json::Value tmp = Json::objectValue;
+      tmp[kPATH_KEY] = i.first;
+      if (i.second) {
+        tmp[kIS_SYSTEM_KEY] = i.second;
       }
-      result[kINCLUDE_PATH_KEY] = includes;
-    }
-    if (!data.Defines.empty()) {
-      result[kDEFINES_KEY] = fromStringList(data.Defines);
+      includes.append(tmp);
     }
+    result[kINCLUDE_PATH_KEY] = includes;
+  }
+  if (!data.Defines.empty()) {
+    result[kDEFINES_KEY] = fromStringList(data.Defines);
   }
 
   result[kIS_GENERATED_KEY] = data.IsGenerated;
@@ -691,21 +708,19 @@ static Json::Value DumpSourceFileGroup(const LanguageData& data,
   return result;
 }
 
-static Json::Value DumpSourceFilesList(
-  cmGeneratorTarget* target, const std::string& config,
-  const std::map<std::string, LanguageData>& languageDataMap)
+static void PopulateFileGroupData(
+  cmGeneratorTarget* target, bool isInterfaceSources,
+  const std::vector<cmSourceFile*>& files, const std::string& config,
+  const std::map<std::string, LanguageData>& languageDataMap,
+  std::unordered_map<LanguageData, FileGroupSources>& fileGroups)
 {
-  // Collect sourcefile groups:
-
-  std::vector<cmSourceFile*> files;
-  target->GetSourceFiles(files, config);
-
-  std::unordered_map<LanguageData, std::vector<std::string>> fileGroups;
   for (cmSourceFile* file : files) {
     LanguageData fileData;
     fileData.Language = file->GetLanguage();
-    if (!fileData.Language.empty()) {
-      const LanguageData& ld = languageDataMap.at(fileData.Language);
+    if (!fileData.Language.empty() || isInterfaceSources) {
+      const LanguageData& ld = isInterfaceSources
+        ? languageDataMap.at(kInterfaceSourcesLanguageDataKey)
+        : languageDataMap.at(fileData.Language);
       cmLocalGenerator* lg = target->GetLocalGenerator();
       cmGeneratorExpressionInterpreter genexInterpreter(
         lg, target, config, target->GetName(), fileData.Language);
@@ -733,10 +748,14 @@ static Json::Value DumpSourceFilesList(
         lg->AppendIncludeDirectories(includes, evaluatedIncludes, *file);
 
         for (const auto& include : includes) {
+          // INTERFACE_LIBRARY targets do not support the
+          // IsSystemIncludeDirectory call so just set it to false.
+          const bool isSystemInclude = isInterfaceSources
+            ? false
+            : target->IsSystemIncludeDirectory(include, config,
+                                               fileData.Language);
           fileData.IncludePathList.push_back(
-            std::make_pair(include,
-                           target->IsSystemIncludeDirectory(
-                             include, config, fileData.Language)));
+            std::make_pair(include, isSystemInclude));
         }
       }
 
@@ -765,14 +784,71 @@ static Json::Value DumpSourceFilesList(
     }
 
     fileData.IsGenerated = file->GetPropertyAsBool("GENERATED");
-    std::vector<std::string>& groupFileList = fileGroups[fileData];
-    groupFileList.push_back(file->GetFullPath());
+    FileGroupSources& groupFileList = fileGroups[fileData];
+    groupFileList.IsInterfaceSources = isInterfaceSources;
+    groupFileList.Files.push_back(file->GetFullPath());
+  }
+}
+
+static Json::Value DumpSourceFilesList(
+  cmGeneratorTarget* target, const std::string& config,
+  const std::map<std::string, LanguageData>& languageDataMap)
+{
+  const cmStateEnums::TargetType type = target->GetType();
+  std::unordered_map<LanguageData, FileGroupSources> fileGroups;
+
+  // Collect sourcefile groups:
+
+  std::vector<cmSourceFile*> files;
+  if (type == cmStateEnums::INTERFACE_LIBRARY) {
+    // INTERFACE_LIBRARY targets do not create all the data structures
+    // associated with regular targets. If properties are explicitly specified
+    // for files in INTERFACE_SOURCES then we can get them through the Makefile
+    // rather than the target.
+    files = target->Makefile->GetSourceFiles();
+  } else {
+    target->GetSourceFiles(files, config);
+    PopulateFileGroupData(target, false /* isInterfaceSources */, files,
+                          config, languageDataMap, fileGroups);
+  }
+
+  // Collect interface sourcefile groups:
+
+  auto targetProp = target->Target->GetProperty("INTERFACE_SOURCES");
+  if (targetProp != nullptr) {
+    cmGeneratorExpressionInterpreter genexInterpreter(
+      target->GetLocalGenerator(), target, config, target->GetName(), "");
+
+    auto evaluatedSources = cmsys::SystemTools::SplitString(
+      genexInterpreter.Evaluate(targetProp, "INTERFACE_SOURCES"), ';');
+
+    std::map<std::string, cmSourceFile*> filesMap;
+    for (auto file : files) {
+      filesMap[file->GetFullPath()] = file;
+    }
+
+    std::vector<cmSourceFile*> interfaceSourceFiles;
+    for (const std::string& interfaceSourceFilePath : evaluatedSources) {
+      auto entry = filesMap.find(interfaceSourceFilePath);
+      if (entry != filesMap.end()) {
+        // use what we have since it has all the associated properties
+        interfaceSourceFiles.push_back(entry->second);
+      } else {
+        interfaceSourceFiles.push_back(
+          new cmSourceFile(target->Makefile, interfaceSourceFilePath));
+      }
+    }
+
+    PopulateFileGroupData(target, true /* isInterfaceSources */,
+                          interfaceSourceFiles, config, languageDataMap,
+                          fileGroups);
   }
 
   const std::string& baseDir = target->Makefile->GetCurrentSourceDirectory();
   Json::Value result = Json::arrayValue;
   for (auto const& it : fileGroups) {
-    Json::Value group = DumpSourceFileGroup(it.first, it.second, baseDir);
+    Json::Value group = DumpSourceFileGroup(
+      it.first, it.second.IsInterfaceSources, it.second.Files, baseDir);
     if (!group.isNull()) {
       result.append(group);
     }
@@ -882,6 +958,59 @@ static Json::Value DumpCTestConfigurationsList(const cmake* cm)
   return result;
 }
 
+static void GetTargetProperty(
+  cmGeneratorExpressionInterpreter& genexInterpreter,
+  cmGeneratorTarget* target, const char* propertyName,
+  std::vector<std::string>& propertyValue)
+{
+  auto targetProp = target->Target->GetProperty(propertyName);
+  if (targetProp != nullptr) {
+    propertyValue = cmsys::SystemTools::SplitString(
+      genexInterpreter.Evaluate(targetProp, propertyName), ';');
+  }
+}
+
+static void CreateInterfaceSourcesEntry(
+  cmLocalGenerator* lg, cmGeneratorTarget* target, const std::string& config,
+  std::map<std::string, LanguageData>& languageDataMap)
+{
+  LanguageData& ld = languageDataMap[kInterfaceSourcesLanguageDataKey];
+  ld.Language = "";
+
+  cmGeneratorExpressionInterpreter genexInterpreter(lg, target, config,
+                                                    target->GetName(), "");
+  std::vector<std::string> propertyValue;
+  GetTargetProperty(genexInterpreter, target, "INTERFACE_INCLUDE_DIRECTORIES",
+                    propertyValue);
+  for (std::string const& i : propertyValue) {
+    ld.IncludePathList.push_back(
+      std::make_pair(i, false /* isSystemInclude */));
+  }
+
+  propertyValue.clear();
+  GetTargetProperty(genexInterpreter, target,
+                    "INTERFACE_SYSTEM_INCLUDE_DIRECTORIES", propertyValue);
+  for (std::string const& i : propertyValue) {
+    ld.IncludePathList.push_back(
+      std::make_pair(i, true /* isSystemInclude */));
+  }
+
+  propertyValue.clear();
+  GetTargetProperty(genexInterpreter, target, "INTERFACE_COMPILE_OPTIONS",
+                    propertyValue);
+  for (const auto& s : propertyValue) {
+    ld.Flags += " " + s;
+  }
+
+  propertyValue.clear();
+  GetTargetProperty(genexInterpreter, target, "INTERFACE_COMPILE_DEFINITIONS",
+                    propertyValue);
+  if (!propertyValue.empty()) {
+    std::set<std::string> defines(propertyValue.begin(), propertyValue.end());
+    ld.SetDefines(defines);
+  }
+}
+
 static Json::Value DumpTarget(cmGeneratorTarget* target,
                               const std::string& config)
 {
@@ -911,11 +1040,6 @@ static Json::Value DumpTarget(cmGeneratorTarget* target,
   result[kTYPE_KEY] = typeName;
   result[kSOURCE_DIRECTORY_KEY] = lg->GetCurrentSourceDirectory();
   result[kBUILD_DIRECTORY_KEY] = lg->GetCurrentBinaryDirectory();
-
-  if (type == cmStateEnums::INTERFACE_LIBRARY) {
-    return result;
-  }
-
   result[kFULL_NAME_KEY] = target->GetFullName(config);
 
   if (target->Target->GetHaveInstallRule()) {
@@ -1002,8 +1126,22 @@ static Json::Value DumpTarget(cmGeneratorTarget* target,
   }
 
   std::set<std::string> languages;
-  target->GetLanguages(languages, config);
   std::map<std::string, LanguageData> languageDataMap;
+  if (type == cmStateEnums::INTERFACE_LIBRARY) {
+    // INTERFACE_LIBRARY targets do not create all the data structures
+    // associated with regular targets. If properties are explicitly specified
+    // for files in INTERFACE_SOURCES then we can get them through the Makefile
+    // rather than the target.
+    for (auto file : target->Makefile->GetSourceFiles()) {
+      const std::string& language = file->GetLanguage();
+      if (!language.empty()) {
+        languages.insert(language);
+      }
+    }
+  } else {
+    target->GetLanguages(languages, config);
+  }
+
   for (std::string const& lang : languages) {
     LanguageData& ld = languageDataMap[lang];
     ld.Language = lang;
@@ -1019,6 +1157,11 @@ static Json::Value DumpTarget(cmGeneratorTarget* target,
     }
   }
 
+  if (target->Target->GetProperty("INTERFACE_SOURCES") != nullptr) {
+    // Create an entry in the languageDataMap for interface sources.
+    CreateInterfaceSourcesEntry(lg, target, config, languageDataMap);
+  }
+
   Json::Value sourceGroupsValue =
     DumpSourceFilesList(target, config, languageDataMap);
   if (!sourceGroupsValue.empty()) {