|
|
@ -3,9 +3,13 @@ |
|
|
|
#include "cmServerProtocol.h"
|
|
|
|
|
|
|
|
#include "cmExternalMakefileProjectGenerator.h"
|
|
|
|
#include "cmGeneratorTarget.h"
|
|
|
|
#include "cmGlobalGenerator.h"
|
|
|
|
#include "cmLocalGenerator.h"
|
|
|
|
#include "cmMakefile.h"
|
|
|
|
#include "cmServer.h"
|
|
|
|
#include "cmServerDictionary.h"
|
|
|
|
#include "cmSourceFile.h"
|
|
|
|
#include "cmSystemTools.h"
|
|
|
|
#include "cmake.h"
|
|
|
|
|
|
|
@ -16,6 +20,49 @@ |
|
|
|
#include "cm_jsoncpp_value.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
// Get rid of some windows macros:
|
|
|
|
#undef max
|
|
|
|
|
|
|
|
namespace { |
|
|
|
|
|
|
|
static std::vector<std::string> getConfigurations(const cmake* cm) |
|
|
|
{ |
|
|
|
std::vector<std::string> configurations; |
|
|
|
auto makefiles = cm->GetGlobalGenerator()->GetMakefiles(); |
|
|
|
if (makefiles.empty()) { |
|
|
|
return configurations; |
|
|
|
} |
|
|
|
|
|
|
|
makefiles[0]->GetConfigurations(configurations); |
|
|
|
if (configurations.empty()) |
|
|
|
configurations.push_back(""); |
|
|
|
return configurations; |
|
|
|
} |
|
|
|
|
|
|
|
static bool hasString(const Json::Value& v, const std::string& s) |
|
|
|
{ |
|
|
|
return !v.isNull() && |
|
|
|
std::find_if(v.begin(), v.end(), [s](const Json::Value& i) { |
|
|
|
return i.asString() == s; |
|
|
|
}) != v.end(); |
|
|
|
} |
|
|
|
|
|
|
|
template <class T> |
|
|
|
static Json::Value fromStringList(const T& in) |
|
|
|
{ |
|
|
|
Json::Value result = Json::arrayValue; |
|
|
|
for (const std::string& i : in) { |
|
|
|
result.append(i); |
|
|
|
} |
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
cmServerRequest::cmServerRequest(cmServer* server, const std::string& t, |
|
|
|
const std::string& c, const Json::Value& d) |
|
|
|
: Type(t) |
|
|
@ -270,6 +317,9 @@ const cmServerResponse cmServerProtocol1_0::Process( |
|
|
|
{ |
|
|
|
assert(this->m_State >= STATE_ACTIVE); |
|
|
|
|
|
|
|
if (request.Type == kCODE_MODEL_TYPE) { |
|
|
|
return this->ProcessCodeModel(request); |
|
|
|
} |
|
|
|
if (request.Type == kCOMPUTE_TYPE) { |
|
|
|
return this->ProcessCompute(request); |
|
|
|
} |
|
|
@ -291,6 +341,342 @@ bool cmServerProtocol1_0::IsExperimental() const |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
class LanguageData |
|
|
|
{ |
|
|
|
public: |
|
|
|
bool operator==(const LanguageData& other) const; |
|
|
|
|
|
|
|
void SetDefines(const std::set<std::string>& defines); |
|
|
|
|
|
|
|
bool IsGenerated = false; |
|
|
|
std::string Language; |
|
|
|
std::string Flags; |
|
|
|
std::vector<std::string> Defines; |
|
|
|
std::vector<std::pair<std::string, bool> > IncludePathList; |
|
|
|
}; |
|
|
|
|
|
|
|
bool LanguageData::operator==(const LanguageData& other) const |
|
|
|
{ |
|
|
|
return Language == other.Language && Defines == other.Defines && |
|
|
|
Flags == other.Flags && IncludePathList == other.IncludePathList && |
|
|
|
IsGenerated == other.IsGenerated; |
|
|
|
} |
|
|
|
|
|
|
|
void LanguageData::SetDefines(const std::set<std::string>& defines) |
|
|
|
{ |
|
|
|
std::vector<std::string> result; |
|
|
|
for (auto i : defines) { |
|
|
|
result.push_back(i); |
|
|
|
} |
|
|
|
std::sort(result.begin(), result.end()); |
|
|
|
Defines = result; |
|
|
|
} |
|
|
|
|
|
|
|
namespace std { |
|
|
|
|
|
|
|
template <> |
|
|
|
struct hash<LanguageData> |
|
|
|
{ |
|
|
|
std::size_t operator()(const LanguageData& in) const |
|
|
|
{ |
|
|
|
using std::hash; |
|
|
|
size_t result = |
|
|
|
hash<std::string>()(in.Language) ^ hash<std::string>()(in.Flags); |
|
|
|
for (auto i : in.IncludePathList) { |
|
|
|
result = result ^ (hash<std::string>()(i.first) ^ |
|
|
|
(i.second ? std::numeric_limits<size_t>::max() : 0)); |
|
|
|
} |
|
|
|
for (auto i : in.Defines) { |
|
|
|
result = result ^ hash<std::string>()(i); |
|
|
|
} |
|
|
|
result = |
|
|
|
result ^ (in.IsGenerated ? std::numeric_limits<size_t>::max() : 0); |
|
|
|
return result; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
} // namespace std
|
|
|
|
|
|
|
|
static Json::Value DumpSourceFileGroup(const LanguageData& data, |
|
|
|
const std::vector<std::string>& files, |
|
|
|
const std::string& baseDir) |
|
|
|
{ |
|
|
|
Json::Value result = Json::objectValue; |
|
|
|
|
|
|
|
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 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); |
|
|
|
} |
|
|
|
result[kINCLUDE_PATH_KEY] = includes; |
|
|
|
} |
|
|
|
if (!data.Defines.empty()) { |
|
|
|
result[kDEFINES_KEY] = fromStringList(data.Defines); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
result[kIS_GENERATED_KEY] = data.IsGenerated; |
|
|
|
|
|
|
|
Json::Value sourcesValue = Json::arrayValue; |
|
|
|
for (auto i : files) { |
|
|
|
const std::string relPath = |
|
|
|
cmSystemTools::RelativePath(baseDir.c_str(), i.c_str()); |
|
|
|
sourcesValue.append(relPath.size() < i.size() ? relPath : i); |
|
|
|
} |
|
|
|
|
|
|
|
result[kSOURCES_KEY] = sourcesValue; |
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
static Json::Value DumpSourceFilesList( |
|
|
|
cmGeneratorTarget* target, const std::string& config, |
|
|
|
const std::map<std::string, LanguageData>& languageDataMap) |
|
|
|
{ |
|
|
|
// 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); |
|
|
|
cmLocalGenerator* lg = target->GetLocalGenerator(); |
|
|
|
|
|
|
|
std::string compileFlags = ld.Flags; |
|
|
|
lg->AppendFlags(compileFlags, file->GetProperty("COMPILE_FLAGS")); |
|
|
|
fileData.Flags = compileFlags; |
|
|
|
|
|
|
|
fileData.IncludePathList = ld.IncludePathList; |
|
|
|
|
|
|
|
std::set<std::string> defines; |
|
|
|
lg->AppendDefines(defines, file->GetProperty("COMPILE_DEFINITIONS")); |
|
|
|
const std::string defPropName = |
|
|
|
"COMPILE_DEFINITIONS_" + cmSystemTools::UpperCase(config); |
|
|
|
lg->AppendDefines(defines, file->GetProperty(defPropName)); |
|
|
|
defines.insert(ld.Defines.begin(), ld.Defines.end()); |
|
|
|
|
|
|
|
fileData.SetDefines(defines); |
|
|
|
} |
|
|
|
|
|
|
|
fileData.IsGenerated = file->GetPropertyAsBool("GENERATED"); |
|
|
|
std::vector<std::string>& groupFileList = fileGroups[fileData]; |
|
|
|
groupFileList.push_back(file->GetFullPath()); |
|
|
|
} |
|
|
|
|
|
|
|
const std::string baseDir = target->Makefile->GetCurrentSourceDirectory(); |
|
|
|
Json::Value result = Json::arrayValue; |
|
|
|
for (auto it = fileGroups.begin(); it != fileGroups.end(); ++it) { |
|
|
|
Json::Value group = DumpSourceFileGroup(it->first, it->second, baseDir); |
|
|
|
if (!group.isNull()) |
|
|
|
result.append(group); |
|
|
|
} |
|
|
|
|
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
static Json::Value DumpTarget(cmGeneratorTarget* target, |
|
|
|
const std::string& config) |
|
|
|
{ |
|
|
|
cmLocalGenerator* lg = target->GetLocalGenerator(); |
|
|
|
const cmState* state = lg->GetState(); |
|
|
|
|
|
|
|
const cmState::TargetType type = target->GetType(); |
|
|
|
const std::string typeName = state->GetTargetTypeName(type); |
|
|
|
|
|
|
|
Json::Value ttl = Json::arrayValue; |
|
|
|
ttl.append("EXECUTABLE"); |
|
|
|
ttl.append("STATIC_LIBRARY"); |
|
|
|
ttl.append("SHARED_LIBRARY"); |
|
|
|
ttl.append("MODULE_LIBRARY"); |
|
|
|
ttl.append("OBJECT_LIBRARY"); |
|
|
|
ttl.append("UTILITY"); |
|
|
|
ttl.append("INTERFACE_LIBRARY"); |
|
|
|
|
|
|
|
if (!hasString(ttl, typeName) || target->IsImported()) { |
|
|
|
return Json::Value(); |
|
|
|
} |
|
|
|
|
|
|
|
Json::Value result = Json::objectValue; |
|
|
|
result[kNAME_KEY] = target->GetName(); |
|
|
|
|
|
|
|
result[kTYPE_KEY] = typeName; |
|
|
|
result[kFULL_NAME_KEY] = target->GetFullName(config); |
|
|
|
result[kSOURCE_DIRECTORY_KEY] = lg->GetCurrentSourceDirectory(); |
|
|
|
result[kBUILD_DIRECTORY_KEY] = lg->GetCurrentBinaryDirectory(); |
|
|
|
|
|
|
|
if (target->HaveWellDefinedOutputFiles()) { |
|
|
|
Json::Value artifacts = Json::arrayValue; |
|
|
|
artifacts.append(target->GetFullPath(config, false)); |
|
|
|
if (target->IsDLLPlatform()) { |
|
|
|
artifacts.append(target->GetFullPath(config, true)); |
|
|
|
const cmGeneratorTarget::OutputInfo* output = |
|
|
|
target->GetOutputInfo(config); |
|
|
|
if (output && !output->PdbDir.empty()) { |
|
|
|
artifacts.append(output->PdbDir + '/' + target->GetPDBName(config)); |
|
|
|
} |
|
|
|
} |
|
|
|
result[kARTIFACTS_KEY] = artifacts; |
|
|
|
|
|
|
|
result[kLINKER_LANGUAGE_KEY] = target->GetLinkerLanguage(config); |
|
|
|
|
|
|
|
std::string linkLibs; |
|
|
|
std::string linkFlags; |
|
|
|
std::string linkLanguageFlags; |
|
|
|
std::string frameworkPath; |
|
|
|
std::string linkPath; |
|
|
|
lg->GetTargetFlags(config, linkLibs, linkLanguageFlags, linkFlags, |
|
|
|
frameworkPath, linkPath, target, false); |
|
|
|
|
|
|
|
linkLibs = cmSystemTools::TrimWhitespace(linkLibs); |
|
|
|
linkFlags = cmSystemTools::TrimWhitespace(linkFlags); |
|
|
|
linkLanguageFlags = cmSystemTools::TrimWhitespace(linkLanguageFlags); |
|
|
|
frameworkPath = cmSystemTools::TrimWhitespace(frameworkPath); |
|
|
|
linkPath = cmSystemTools::TrimWhitespace(linkPath); |
|
|
|
|
|
|
|
if (!cmSystemTools::TrimWhitespace(linkLibs).empty()) { |
|
|
|
result[kLINK_LIBRARIES_KEY] = linkLibs; |
|
|
|
} |
|
|
|
if (!cmSystemTools::TrimWhitespace(linkFlags).empty()) { |
|
|
|
result[kLINK_FLAGS_KEY] = linkFlags; |
|
|
|
} |
|
|
|
if (!cmSystemTools::TrimWhitespace(linkLanguageFlags).empty()) { |
|
|
|
result[kLINK_LANGUAGE_FLAGS_KEY] = linkLanguageFlags; |
|
|
|
} |
|
|
|
if (!frameworkPath.empty()) { |
|
|
|
result[kFRAMEWORK_PATH_KEY] = frameworkPath; |
|
|
|
} |
|
|
|
if (!linkPath.empty()) { |
|
|
|
result[kLINK_PATH_KEY] = linkPath; |
|
|
|
} |
|
|
|
const std::string sysroot = |
|
|
|
lg->GetMakefile()->GetSafeDefinition("CMAKE_SYSROOT"); |
|
|
|
if (!sysroot.empty()) { |
|
|
|
result[kSYSROOT_KEY] = sysroot; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
std::set<std::string> languages; |
|
|
|
target->GetLanguages(languages, config); |
|
|
|
std::map<std::string, LanguageData> languageDataMap; |
|
|
|
for (auto lang : languages) { |
|
|
|
LanguageData& ld = languageDataMap[lang]; |
|
|
|
ld.Language = lang; |
|
|
|
lg->GetTargetCompileFlags(target, config, lang, ld.Flags); |
|
|
|
std::set<std::string> defines; |
|
|
|
lg->GetTargetDefines(target, config, lang, defines); |
|
|
|
ld.SetDefines(defines); |
|
|
|
std::vector<std::string> includePathList; |
|
|
|
lg->GetIncludeDirectories(includePathList, target, lang, config, true); |
|
|
|
for (auto i : includePathList) { |
|
|
|
ld.IncludePathList.push_back( |
|
|
|
std::make_pair(i, target->IsSystemIncludeDirectory(i, config))); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
Json::Value sourceGroupsValue = |
|
|
|
DumpSourceFilesList(target, config, languageDataMap); |
|
|
|
if (!sourceGroupsValue.empty()) { |
|
|
|
result[kFILE_GROUPS_KEY] = sourceGroupsValue; |
|
|
|
} |
|
|
|
|
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
static Json::Value DumpTargetsList( |
|
|
|
const std::vector<cmLocalGenerator*>& generators, const std::string& config) |
|
|
|
{ |
|
|
|
Json::Value result = Json::arrayValue; |
|
|
|
|
|
|
|
std::vector<cmGeneratorTarget*> targetList; |
|
|
|
for (const auto& lgIt : generators) { |
|
|
|
auto list = lgIt->GetGeneratorTargets(); |
|
|
|
targetList.insert(targetList.end(), list.begin(), list.end()); |
|
|
|
} |
|
|
|
std::sort(targetList.begin(), targetList.end()); |
|
|
|
|
|
|
|
for (cmGeneratorTarget* target : targetList) { |
|
|
|
Json::Value tmp = DumpTarget(target, config); |
|
|
|
if (!tmp.isNull()) { |
|
|
|
result.append(tmp); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
static Json::Value DumpProjectList(const cmake* cm, const std::string config) |
|
|
|
{ |
|
|
|
Json::Value result = Json::arrayValue; |
|
|
|
|
|
|
|
auto globalGen = cm->GetGlobalGenerator(); |
|
|
|
|
|
|
|
for (const auto& projectIt : globalGen->GetProjectMap()) { |
|
|
|
Json::Value pObj = Json::objectValue; |
|
|
|
pObj[kNAME_KEY] = projectIt.first; |
|
|
|
|
|
|
|
assert(projectIt.second.size() > |
|
|
|
0); // All Projects must have at least one local generator
|
|
|
|
const cmLocalGenerator* lg = projectIt.second.at(0); |
|
|
|
|
|
|
|
// Project structure information:
|
|
|
|
const cmMakefile* mf = lg->GetMakefile(); |
|
|
|
pObj[kSOURCE_DIRECTORY_KEY] = mf->GetCurrentSourceDirectory(); |
|
|
|
pObj[kBUILD_DIRECTORY_KEY] = mf->GetCurrentBinaryDirectory(); |
|
|
|
pObj[kTARGETS_KEY] = DumpTargetsList(projectIt.second, config); |
|
|
|
|
|
|
|
result.append(pObj); |
|
|
|
} |
|
|
|
|
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
static Json::Value DumpConfiguration(const cmake* cm, |
|
|
|
const std::string& config) |
|
|
|
{ |
|
|
|
Json::Value result = Json::objectValue; |
|
|
|
result[kNAME_KEY] = config; |
|
|
|
|
|
|
|
result[kPROJECTS_KEY] = DumpProjectList(cm, config); |
|
|
|
|
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
static Json::Value DumpConfigurationsList(const cmake* cm) |
|
|
|
{ |
|
|
|
Json::Value result = Json::arrayValue; |
|
|
|
|
|
|
|
for (const std::string& c : getConfigurations(cm)) { |
|
|
|
result.append(DumpConfiguration(cm, c)); |
|
|
|
} |
|
|
|
|
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
cmServerResponse cmServerProtocol1_0::ProcessCodeModel( |
|
|
|
const cmServerRequest& request) |
|
|
|
{ |
|
|
|
if (this->m_State != STATE_COMPUTED) { |
|
|
|
return request.ReportError("No build system was generated yet."); |
|
|
|
} |
|
|
|
|
|
|
|
Json::Value result = Json::objectValue; |
|
|
|
result[kCONFIGURATIONS_KEY] = DumpConfigurationsList(this->CMakeInstance()); |
|
|
|
return request.Reply(result); |
|
|
|
} |
|
|
|
|
|
|
|
cmServerResponse cmServerProtocol1_0::ProcessCompute( |
|
|
|
const cmServerRequest& request) |
|
|
|
{ |
|
|
|