Browse Source

Merge topic 'server-test-info'

35a52bd1 server: add "ctestInfo" request to get test info

Acked-by: Kitware Robot <[email protected]>
Merge-request: !1414
Brad King 8 years ago
parent
commit
1348f97784

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

@@ -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"
 ^^^^^^^^^^^^^^^^^^
 

+ 7 - 4
Source/cmLocalGenerator.cxx

@@ -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);
 

+ 7 - 0
Source/cmMakefile.cxx

@@ -3319,6 +3319,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
 {

+ 5 - 0
Source/cmMakefile.h

@@ -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
    */

+ 5 - 0
Source/cmServerDictionary.h

@@ -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";

+ 165 - 0
Source/cmServerProtocol.cxx

@@ -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,

+ 1 - 0
Source/cmServerProtocol.h

@@ -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
   {