From d74c2282ea2b7df2276ce73c790fafe9a5571c56 Mon Sep 17 00:00:00 2001 From: Justin Goshi Date: Fri, 10 Aug 2018 10:45:56 -0700 Subject: cmake-server: Support codemodel filegroups for INTERFACE_SOURCES This change returns information for INTERFACE_SOURCES. We add a flag to the filegroup to indicate if the target represents interface sources. Protocol version is updated to 1.3 since this is a change to what was released in cmake version 3.12. --- Help/manual/cmake-server.7.rst | 7 ++ Source/cmServerDictionary.h | 1 + Source/cmServerProtocol.cxx | 223 +++++++++++++++++++++++++++++++++-------- 3 files changed, 191 insertions(+), 40 deletions(-) diff --git a/Help/manual/cmake-server.7.rst b/Help/manual/cmake-server.7.rst index 25d364c..b4c1436 100644 --- a/Help/manual/cmake-server.7.rst +++ b/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. diff --git a/Source/cmServerDictionary.h b/Source/cmServerDictionary.h index 685542c..ebf16eb 100644 --- a/Source/cmServerDictionary.h +++ b/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\" ==]"; diff --git a/Source/cmServerProtocol.cxx b/Source/cmServerProtocol.cxx index c267160..db7be2a 100644 --- a/Source/cmServerProtocol.cxx +++ b/Source/cmServerProtocol.cxx @@ -256,7 +256,12 @@ bool cmServerProtocol::DoActivate(const cmServerRequest& /*request*/, std::pair 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& defines) Defines = std::move(result); } +struct FileGroupSources +{ + bool IsInterfaceSources; + std::vector Files; +}; + namespace std { template <> @@ -652,31 +665,35 @@ struct hash } // namespace std static Json::Value DumpSourceFileGroup(const LanguageData& data, + bool isInterfaceSource, const std::vector& 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& languageDataMap) +static void PopulateFileGroupData( + cmGeneratorTarget* target, bool isInterfaceSources, + const std::vector& files, const std::string& config, + const std::map& languageDataMap, + std::unordered_map& fileGroups) { - // Collect sourcefile groups: - - std::vector files; - target->GetSourceFiles(files, config); - - std::unordered_map> 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& 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& languageDataMap) +{ + const cmStateEnums::TargetType type = target->GetType(); + std::unordered_map fileGroups; + + // Collect sourcefile groups: + + std::vector 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 filesMap; + for (auto file : files) { + filesMap[file->GetFullPath()] = file; + } + + std::vector 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); } @@ -883,6 +959,59 @@ static Json::Value DumpCTestConfigurationsList(const cmake* cm) return result; } +static void GetTargetProperty( + cmGeneratorExpressionInterpreter& genexInterpreter, + cmGeneratorTarget* target, const char* propertyName, + std::vector& 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& languageDataMap) +{ + LanguageData& ld = languageDataMap[kInterfaceSourcesLanguageDataKey]; + ld.Language = ""; + + cmGeneratorExpressionInterpreter genexInterpreter(lg, target, config, + target->GetName(), ""); + std::vector 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 defines(propertyValue.begin(), propertyValue.end()); + ld.SetDefines(defines); + } +} + static Json::Value DumpTarget(cmGeneratorTarget* target, const std::string& config) { @@ -912,11 +1041,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()) { @@ -1003,8 +1127,22 @@ static Json::Value DumpTarget(cmGeneratorTarget* target, } std::set languages; - target->GetLanguages(languages, config); std::map 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; @@ -1020,6 +1158,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()) { -- cgit v0.12