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.
This commit is contained in:
Justin Goshi
2018-08-10 10:45:56 -07:00
parent eba2b13a83
commit d74c2282ea
3 changed files with 191 additions and 40 deletions

View File

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

View File

@@ -96,6 +96,7 @@ static const std::string kCTEST_COMMAND = "ctestCommand";
static const std::string kCTEST_INFO = "ctestInfo"; static const std::string kCTEST_INFO = "ctestInfo";
static const std::string kMINIMUM_CMAKE_VERSION = "minimumCMakeVersion"; static const std::string kMINIMUM_CMAKE_VERSION = "minimumCMakeVersion";
static const std::string kIS_GENERATOR_PROVIDED_KEY = "isGeneratorProvided"; 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 kSTART_MAGIC = "[== \"CMake Server\" ==[";
static const std::string kEND_MAGIC = "]== \"CMake Server\" ==]"; static const std::string kEND_MAGIC = "]== \"CMake Server\" ==]";

View File

@@ -256,7 +256,12 @@ bool cmServerProtocol::DoActivate(const cmServerRequest& /*request*/,
std::pair<int, int> cmServerProtocol1::ProtocolVersion() const 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) static void setErrorMessage(std::string* errorMessage, const std::string& text)
@@ -593,6 +598,8 @@ cmServerResponse cmServerProtocol1::ProcessCMakeInputs(
return request.Reply(result); return request.Reply(result);
} }
const std::string kInterfaceSourcesLanguageDataKey =
"INTERFACE_SOURCES_LD_KEY";
class LanguageData class LanguageData
{ {
public: public:
@@ -625,6 +632,12 @@ void LanguageData::SetDefines(const std::set<std::string>& defines)
Defines = std::move(result); Defines = std::move(result);
} }
struct FileGroupSources
{
bool IsInterfaceSources;
std::vector<std::string> Files;
};
namespace std { namespace std {
template <> template <>
@@ -652,31 +665,35 @@ struct hash<LanguageData>
} // namespace std } // namespace std
static Json::Value DumpSourceFileGroup(const LanguageData& data, static Json::Value DumpSourceFileGroup(const LanguageData& data,
bool isInterfaceSource,
const std::vector<std::string>& files, const std::vector<std::string>& files,
const std::string& baseDir) const std::string& baseDir)
{ {
Json::Value result = Json::objectValue; Json::Value result = Json::objectValue;
if (isInterfaceSource) {
result[kIS_INTERFACE_SOURCES_KEY] = true;
}
if (!data.Language.empty()) { if (!data.Language.empty()) {
result[kLANGUAGE_KEY] = data.Language; result[kLANGUAGE_KEY] = data.Language;
if (!data.Flags.empty()) { }
result[kCOMPILE_FLAGS_KEY] = data.Flags; if (!data.Flags.empty()) {
} result[kCOMPILE_FLAGS_KEY] = data.Flags;
if (!data.IncludePathList.empty()) { }
Json::Value includes = Json::arrayValue; if (!data.IncludePathList.empty()) {
for (auto const& i : data.IncludePathList) { Json::Value includes = Json::arrayValue;
Json::Value tmp = Json::objectValue; for (auto const& i : data.IncludePathList) {
tmp[kPATH_KEY] = i.first; Json::Value tmp = Json::objectValue;
if (i.second) { tmp[kPATH_KEY] = i.first;
tmp[kIS_SYSTEM_KEY] = i.second; if (i.second) {
} tmp[kIS_SYSTEM_KEY] = i.second;
includes.append(tmp);
} }
result[kINCLUDE_PATH_KEY] = includes; includes.append(tmp);
}
if (!data.Defines.empty()) {
result[kDEFINES_KEY] = fromStringList(data.Defines);
} }
result[kINCLUDE_PATH_KEY] = includes;
}
if (!data.Defines.empty()) {
result[kDEFINES_KEY] = fromStringList(data.Defines);
} }
result[kIS_GENERATED_KEY] = data.IsGenerated; result[kIS_GENERATED_KEY] = data.IsGenerated;
@@ -691,21 +708,19 @@ static Json::Value DumpSourceFileGroup(const LanguageData& data,
return result; return result;
} }
static Json::Value DumpSourceFilesList( static void PopulateFileGroupData(
cmGeneratorTarget* target, const std::string& config, cmGeneratorTarget* target, bool isInterfaceSources,
const std::map<std::string, LanguageData>& languageDataMap) 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) { for (cmSourceFile* file : files) {
LanguageData fileData; LanguageData fileData;
fileData.Language = file->GetLanguage(); fileData.Language = file->GetLanguage();
if (!fileData.Language.empty()) { if (!fileData.Language.empty() || isInterfaceSources) {
const LanguageData& ld = languageDataMap.at(fileData.Language); const LanguageData& ld = isInterfaceSources
? languageDataMap.at(kInterfaceSourcesLanguageDataKey)
: languageDataMap.at(fileData.Language);
cmLocalGenerator* lg = target->GetLocalGenerator(); cmLocalGenerator* lg = target->GetLocalGenerator();
cmGeneratorExpressionInterpreter genexInterpreter( cmGeneratorExpressionInterpreter genexInterpreter(
lg, target, config, target->GetName(), fileData.Language); lg, target, config, target->GetName(), fileData.Language);
@@ -733,10 +748,14 @@ static Json::Value DumpSourceFilesList(
lg->AppendIncludeDirectories(includes, evaluatedIncludes, *file); lg->AppendIncludeDirectories(includes, evaluatedIncludes, *file);
for (const auto& include : includes) { 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( fileData.IncludePathList.push_back(
std::make_pair(include, std::make_pair(include, isSystemInclude));
target->IsSystemIncludeDirectory(
include, config, fileData.Language)));
} }
} }
@@ -765,14 +784,71 @@ static Json::Value DumpSourceFilesList(
} }
fileData.IsGenerated = file->GetPropertyAsBool("GENERATED"); fileData.IsGenerated = file->GetPropertyAsBool("GENERATED");
std::vector<std::string>& groupFileList = fileGroups[fileData]; FileGroupSources& groupFileList = fileGroups[fileData];
groupFileList.push_back(file->GetFullPath()); 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(); const std::string baseDir = target->Makefile->GetCurrentSourceDirectory();
Json::Value result = Json::arrayValue; Json::Value result = Json::arrayValue;
for (auto const& it : fileGroups) { 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()) { if (!group.isNull()) {
result.append(group); result.append(group);
} }
@@ -883,6 +959,59 @@ static Json::Value DumpCTestConfigurationsList(const cmake* cm)
return result; 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, static Json::Value DumpTarget(cmGeneratorTarget* target,
const std::string& config) const std::string& config)
{ {
@@ -912,11 +1041,6 @@ static Json::Value DumpTarget(cmGeneratorTarget* target,
result[kTYPE_KEY] = typeName; result[kTYPE_KEY] = typeName;
result[kSOURCE_DIRECTORY_KEY] = lg->GetCurrentSourceDirectory(); result[kSOURCE_DIRECTORY_KEY] = lg->GetCurrentSourceDirectory();
result[kBUILD_DIRECTORY_KEY] = lg->GetCurrentBinaryDirectory(); result[kBUILD_DIRECTORY_KEY] = lg->GetCurrentBinaryDirectory();
if (type == cmStateEnums::INTERFACE_LIBRARY) {
return result;
}
result[kFULL_NAME_KEY] = target->GetFullName(config); result[kFULL_NAME_KEY] = target->GetFullName(config);
if (target->Target->GetHaveInstallRule()) { if (target->Target->GetHaveInstallRule()) {
@@ -1003,8 +1127,22 @@ static Json::Value DumpTarget(cmGeneratorTarget* target,
} }
std::set<std::string> languages; std::set<std::string> languages;
target->GetLanguages(languages, config);
std::map<std::string, LanguageData> languageDataMap; 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) { for (std::string const& lang : languages) {
LanguageData& ld = languageDataMap[lang]; LanguageData& ld = languageDataMap[lang];
ld.Language = 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 = Json::Value sourceGroupsValue =
DumpSourceFilesList(target, config, languageDataMap); DumpSourceFilesList(target, config, languageDataMap);
if (!sourceGroupsValue.empty()) { if (!sourceGroupsValue.empty()) {