server: add "ctestInfo" request to get test info

This commit is contained in:
Justin Goshi 2017-10-25 15:00:15 -07:00
parent 206354ac4c
commit 35a52bd1b4
7 changed files with 263 additions and 4 deletions

View File

@ -628,6 +628,79 @@ CMake will reply::
]== "CMake Server" ==]
Type "ctestInfo"
^^^^^^^^^^^^^^^^
The "ctestInfo" request can be used after a project was "compute"d successfully.
It will list the complete project test structure as it is known to cmake.
The reply will contain a key "configurations", which will contain a list of
configuration objects. Configuration objects are used to destinquish between
different configurations the build directory might have enabled. While most
generators only support one configuration, others might support several.
Each configuration object can have the following keys:
"name"
contains the name of the configuration. The name may be empty.
"projects"
contains a list of project objects, one for each build project.
Project objects define one (sub-)project defined in the cmake build system.
Each project object can have the following keys:
"name"
contains the (sub-)projects name.
"targets"
contains a list of build system target objects.
Target objects define individual build targets for a certain configuration.
Each target object can have the following keys:
"name"
contains the name of the target.
"type"
defines the type of build of the target. Possible values are
"STATIC_LIBRARY", "MODULE_LIBRARY", "SHARED_LIBRARY", "OBJECT_LIBRARY",
"EXECUTABLE", "UTILITY" and "INTERFACE_LIBRARY".
"fullName"
contains the full name of the build result (incl. extensions, etc.).
"hasEnabledTests"
true if testing is enabled for this target.
"ctestInfo"
contains a list of test objects for this target.
Each test object can have the following keys:
"ctestName"
contains the name of the test.
"ctestCommand"
contains the test command.
"properties"
contains a list of test property objects.
"backtrace"
contains a list of backtrace objects that specify where the test was defined.
Each backtrace object can have the following keys:
"path"
contains the full path to the file containing the statement.
"line"
contains the line number in the file where the statement was defined.
"name"
contains the name of the statement that added the test.
Each test property object can have the following keys:
"key"
contains the test property key.
"value"
contains the test property value.
Type "cmakeInputs"
^^^^^^^^^^^^^^^^^^

View File

@ -222,7 +222,14 @@ void cmLocalGenerator::TraceDependencies()
void cmLocalGenerator::GenerateTestFiles()
{
std::string file = this->StateSnapshot.GetDirectory().GetCurrentBinary();
file += "/";
file += "CTestTestfile.cmake";
if (!this->Makefile->IsOn("CMAKE_TESTING_ENABLED")) {
if (cmSystemTools::FileExists(file)) {
cmSystemTools::RemoveFile(file);
}
return;
}
@ -231,10 +238,6 @@ void cmLocalGenerator::GenerateTestFiles()
const std::string& config =
this->Makefile->GetConfigurations(configurationTypes, false);
std::string file = this->StateSnapshot.GetDirectory().GetCurrentBinary();
file += "/";
file += "CTestTestfile.cmake";
cmGeneratedFileStream fout(file.c_str());
fout.SetCopyIfDifferent(true);

View File

@ -3282,6 +3282,13 @@ cmGlobalGenerator* cmMakefile::GetGlobalGenerator() const
return this->GlobalGenerator;
}
void cmMakefile::GetTestNames(std::vector<std::string>& testNames)
{
for (const auto& iter : Tests) {
testNames.push_back(iter.first);
}
}
#ifdef CMAKE_BUILD_WITH_CMAKE
cmVariableWatch* cmMakefile::GetVariableWatch() const
{

View File

@ -616,6 +616,11 @@ public:
cmMessenger* GetMessenger() const;
cmGlobalGenerator* GetGlobalGenerator() const;
/**
* Get all the test names this makefile knows about
*/
void GetTestNames(std::vector<std::string>& testNames);
/**
* Get all the source files this makefile knows about
*/

View File

@ -23,6 +23,7 @@ static const std::string kPROGRESS_TYPE = "progress";
static const std::string kREPLY_TYPE = "reply";
static const std::string kSET_GLOBAL_SETTINGS_TYPE = "setGlobalSettings";
static const std::string kSIGNAL_TYPE = "signal";
static const std::string kCTEST_INFO_TYPE = "ctestInfo";
static const std::string kARTIFACTS_KEY = "artifacts";
static const std::string kBUILD_DIRECTORY_KEY = "buildDirectory";
@ -90,6 +91,10 @@ static const std::string kWATCHED_DIRECTORIES_KEY = "watchedDirectories";
static const std::string kWATCHED_FILES_KEY = "watchedFiles";
static const std::string kHAS_INSTALL_RULE = "hasInstallRule";
static const std::string kINSTALL_PATHS = "installPaths";
static const std::string kHAS_ENABLED_TESTS = "hasEnabledTests";
static const std::string kCTEST_NAME = "ctestName";
static const std::string kCTEST_COMMAND = "ctestCommand";
static const std::string kCTEST_INFO = "ctestInfo";
static const std::string kTARGET_CROSS_REFERENCES_KEY = "crossReferences";
static const std::string kLINE_NUMBER_KEY = "line";

View File

@ -14,6 +14,7 @@
#include "cmListFileCache.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmProperty.h"
#include "cmServer.h"
#include "cmServerDictionary.h"
#include "cmSourceFile.h"
@ -23,6 +24,7 @@
#include "cmStateTypes.h"
#include "cmSystemTools.h"
#include "cmTarget.h"
#include "cmTest.h"
#include "cm_uv.h"
#include "cmake.h"
@ -479,6 +481,9 @@ const cmServerResponse cmServerProtocol1::Process(
if (request.Type == kSET_GLOBAL_SETTINGS_TYPE) {
return this->ProcessSetGlobalSettings(request);
}
if (request.Type == kCTEST_INFO_TYPE) {
return this->ProcessCTests(request);
}
return request.ReportError("Unknown command!");
}
@ -766,6 +771,153 @@ static void DumpBacktraceRange(Json::Value& result, const std::string& type,
}
}
static Json::Value DumpCTestInfo(const std::string& name, cmTest* testInfo)
{
Json::Value result = Json::objectValue;
result[kCTEST_NAME] = name;
// Concat command entries together. After the first should be the arguments
// for the command
std::string command;
for (auto const& cmd : testInfo->GetCommand()) {
command.append(cmd);
command.append(" ");
}
result[kCTEST_COMMAND] = command;
// Build up the list of properties that may have been specified
Json::Value properties = Json::arrayValue;
for (auto& prop : testInfo->GetProperties()) {
Json::Value entry = Json::objectValue;
entry[kKEY_KEY] = prop.first;
entry[kVALUE_KEY] = prop.second.GetValue();
properties.append(entry);
}
result[kPROPERTIES_KEY] = properties;
// Need backtrace to figure out where this test was originally added
result[kBACKTRACE_KEY] = DumpBacktrace(testInfo->GetBacktrace());
return result;
}
static Json::Value DumpCTestTarget(cmGeneratorTarget* target,
const std::string& config)
{
cmLocalGenerator* lg = target->GetLocalGenerator();
const cmState* state = lg->GetState();
const cmStateEnums::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;
if (type == cmStateEnums::INTERFACE_LIBRARY) {
return result;
}
result[kFULL_NAME_KEY] = target->GetFullName(config);
if (target->Makefile->IsOn("CMAKE_TESTING_ENABLED")) {
result[kHAS_ENABLED_TESTS] = true;
std::vector<std::string> CTestNames;
Json::Value testInfo = Json::arrayValue;
std::vector<std::string> testNames;
target->Makefile->GetTestNames(testNames);
for (auto& name : testNames) {
auto test = target->Makefile->GetTest(name);
if (test != nullptr) {
testInfo.append(DumpCTestInfo(name, test));
}
}
result[kCTEST_INFO] = testInfo;
}
return result;
}
static Json::Value DumpCTestTargetsList(
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 = DumpCTestTarget(target, config);
if (!tmp.isNull()) {
result.append(tmp);
}
}
return result;
}
static Json::Value DumpCTestProjectList(const cmake* cm,
std::string const& 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;
// All Projects must have at least one local generator
assert(!projectIt.second.empty());
// Project structure information:
pObj[kTARGETS_KEY] = DumpCTestTargetsList(projectIt.second, config);
result.append(pObj);
}
return result;
}
static Json::Value DumpCTestConfiguration(const cmake* cm,
const std::string& config)
{
Json::Value result = Json::objectValue;
result[kNAME_KEY] = config;
result[kPROJECTS_KEY] = DumpCTestProjectList(cm, config);
return result;
}
static Json::Value DumpCTestConfigurationsList(const cmake* cm)
{
Json::Value result = Json::arrayValue;
for (const std::string& c : getConfigurations(cm)) {
result.append(DumpCTestConfiguration(cm, c));
}
return result;
}
static Json::Value DumpTarget(cmGeneratorTarget* target,
const std::string& config)
{
@ -1222,6 +1374,19 @@ cmServerResponse cmServerProtocol1::ProcessFileSystemWatchers(
return request.Reply(result);
}
cmServerResponse cmServerProtocol1::ProcessCTests(
const cmServerRequest& request)
{
if (this->m_State < STATE_COMPUTED) {
return request.ReportError("This instance was not yet computed.");
}
Json::Value result = Json::objectValue;
result[kCONFIGURATIONS_KEY] =
DumpCTestConfigurationsList(this->CMakeInstance());
return request.Reply(result);
}
cmServerProtocol1::GeneratorInformation::GeneratorInformation(
const std::string& generatorName, const std::string& extraGeneratorName,
const std::string& toolset, const std::string& platform,

View File

@ -123,6 +123,7 @@ private:
cmServerResponse ProcessGlobalSettings(const cmServerRequest& request);
cmServerResponse ProcessSetGlobalSettings(const cmServerRequest& request);
cmServerResponse ProcessFileSystemWatchers(const cmServerRequest& request);
cmServerResponse ProcessCTests(const cmServerRequest& request);
enum State
{