Просмотр исходного кода

Merge topic 'labels-for-subprojects'

376dc3eb Help: Add notes for topic 'labels_for_subprojects'
a70d8e93 Add tests for new directory labels and labels-for-subprojects features
47b3a57c Display subproject timing summary
d3859624 Add directory property 'LABELS' and CMAKE_DIRECTORY_LABELS variable
d08ec4d2 Add CTEST_LABELS_FOR_SUBPROJECTS as a CTest module and script variable

Acked-by: Kitware Robot <[email protected]>
Merge-request: !1004
Brad King 8 лет назад
Родитель
Сommit
f5be951117
57 измененных файлов с 883 добавлено и 15 удалено
  1. 0 1
      Auxiliary/vim/syntax/cmake.vim
  2. 1 0
      Help/manual/cmake-properties.7.rst
  3. 2 0
      Help/manual/cmake-variables.7.rst
  4. 33 0
      Help/manual/ctest.1.rst
  5. 13 0
      Help/prop_dir/LABELS.rst
  6. 14 0
      Help/release/dev/labels_for_subprojects.rst
  7. 6 0
      Help/variable/CMAKE_DIRECTORY_LABELS.rst
  8. 5 0
      Help/variable/CTEST_LABELS_FOR_SUBPROJECTS.rst
  9. 3 0
      Modules/DartConfiguration.tcl.in
  10. 6 0
      Source/CTest/cmCTestBuildCommand.cxx
  11. 1 0
      Source/CTest/cmCTestBuildHandler.cxx
  12. 6 0
      Source/CTest/cmCTestConfigureCommand.cxx
  13. 1 0
      Source/CTest/cmCTestConfigureHandler.cxx
  14. 1 0
      Source/CTest/cmCTestMemCheckHandler.cxx
  15. 1 1
      Source/CTest/cmCTestScriptHandler.cxx
  16. 6 0
      Source/CTest/cmCTestTestCommand.cxx
  17. 179 2
      Source/CTest/cmCTestTestHandler.cxx
  18. 7 0
      Source/CTest/cmCTestTestHandler.h
  19. 34 0
      Source/cmCTest.cxx
  20. 6 0
      Source/cmCTest.h
  21. 2 2
      Source/cmCommands.cxx
  22. 48 9
      Source/cmGlobalGenerator.cxx
  23. 20 0
      Source/cmLocalGenerator.cxx
  24. 3 0
      Source/cmMakefile.cxx
  25. 2 0
      Source/ctest.cxx
  26. 1 0
      Tests/RunCMake/CMakeLists.txt
  27. 5 0
      Tests/RunCMake/ctest_labels_for_subprojects/CMakeLists.txt.in
  28. 2 0
      Tests/RunCMake/ctest_labels_for_subprojects/CTestConfig.cmake.in
  29. 36 0
      Tests/RunCMake/ctest_labels_for_subprojects/CTestConfigCTestScript-check.cmake
  30. 7 0
      Tests/RunCMake/ctest_labels_for_subprojects/CTestConfigCTestScript-stdout.txt
  31. 36 0
      Tests/RunCMake/ctest_labels_for_subprojects/CTestScriptVariable-check.cmake
  32. 7 0
      Tests/RunCMake/ctest_labels_for_subprojects/CTestScriptVariable-stdout.txt
  33. 34 0
      Tests/RunCMake/ctest_labels_for_subprojects/CTestScriptVariableCommandLine-check.cmake
  34. 1 0
      Tests/RunCMake/ctest_labels_for_subprojects/CTestScriptVariableCommandLine-result.txt
  35. 1 0
      Tests/RunCMake/ctest_labels_for_subprojects/CTestScriptVariableCommandLine-stderr.txt
  36. 6 0
      Tests/RunCMake/ctest_labels_for_subprojects/CTestScriptVariableCommandLine-stdout.txt
  37. 1 0
      Tests/RunCMake/ctest_labels_for_subprojects/ModuleVariableCMakeLists-result.txt
  38. 1 0
      Tests/RunCMake/ctest_labels_for_subprojects/ModuleVariableCMakeLists-stderr.txt
  39. 6 0
      Tests/RunCMake/ctest_labels_for_subprojects/ModuleVariableCMakeLists-stdout.txt
  40. 1 0
      Tests/RunCMake/ctest_labels_for_subprojects/ModuleVariableCTestConfig-result.txt
  41. 1 0
      Tests/RunCMake/ctest_labels_for_subprojects/ModuleVariableCTestConfig-stderr.txt
  42. 6 0
      Tests/RunCMake/ctest_labels_for_subprojects/ModuleVariableCTestConfig-stdout.txt
  43. 1 0
      Tests/RunCMake/ctest_labels_for_subprojects/ModuleVariableCTestConfigNoSummary-result.txt
  44. 1 0
      Tests/RunCMake/ctest_labels_for_subprojects/ModuleVariableCTestConfigNoSummary-stderr.txt
  45. 7 0
      Tests/RunCMake/ctest_labels_for_subprojects/ModuleVariableCTestConfigNoSummary-stdout.txt
  46. 1 0
      Tests/RunCMake/ctest_labels_for_subprojects/ModuleVariableCommandLine-result.txt
  47. 1 0
      Tests/RunCMake/ctest_labels_for_subprojects/ModuleVariableCommandLine-stderr.txt
  48. 6 0
      Tests/RunCMake/ctest_labels_for_subprojects/ModuleVariableCommandLine-stdout.txt
  49. 33 0
      Tests/RunCMake/ctest_labels_for_subprojects/MyExperimentalFeature/CMakeLists.txt
  50. 16 0
      Tests/RunCMake/ctest_labels_for_subprojects/MyExperimentalFeature/experimental.c
  51. 12 0
      Tests/RunCMake/ctest_labels_for_subprojects/MyProductionCode/CMakeLists.txt
  52. 16 0
      Tests/RunCMake/ctest_labels_for_subprojects/MyProductionCode/production.c
  53. 7 0
      Tests/RunCMake/ctest_labels_for_subprojects/MyThirdPartyDependency/CMakeLists.txt
  54. 10 0
      Tests/RunCMake/ctest_labels_for_subprojects/MyThirdPartyDependency/src/CMakeLists.txt
  55. 14 0
      Tests/RunCMake/ctest_labels_for_subprojects/MyThirdPartyDependency/src/thirdparty.c
  56. 185 0
      Tests/RunCMake/ctest_labels_for_subprojects/RunCMakeTest.cmake
  57. 21 0
      Tests/RunCMake/ctest_labels_for_subprojects/test.cmake.in

Разница между файлами не показана из-за своего большого размера
+ 0 - 1
Auxiliary/vim/syntax/cmake.vim


+ 1 - 0
Help/manual/cmake-properties.7.rst

@@ -74,6 +74,7 @@ Properties on Directories
    /prop_dir/INCLUDE_REGULAR_EXPRESSION
    /prop_dir/INCLUDE_REGULAR_EXPRESSION
    /prop_dir/INTERPROCEDURAL_OPTIMIZATION_CONFIG
    /prop_dir/INTERPROCEDURAL_OPTIMIZATION_CONFIG
    /prop_dir/INTERPROCEDURAL_OPTIMIZATION
    /prop_dir/INTERPROCEDURAL_OPTIMIZATION
+   /prop_dir/LABELS
    /prop_dir/LINK_DIRECTORIES
    /prop_dir/LINK_DIRECTORIES
    /prop_dir/LISTFILE_STACK
    /prop_dir/LISTFILE_STACK
    /prop_dir/MACROS
    /prop_dir/MACROS

+ 2 - 0
Help/manual/cmake-variables.7.rst

@@ -32,6 +32,7 @@ Variables that Provide Information
    /variable/CMAKE_CURRENT_LIST_FILE
    /variable/CMAKE_CURRENT_LIST_FILE
    /variable/CMAKE_CURRENT_LIST_LINE
    /variable/CMAKE_CURRENT_LIST_LINE
    /variable/CMAKE_CURRENT_SOURCE_DIR
    /variable/CMAKE_CURRENT_SOURCE_DIR
+   /variable/CMAKE_DIRECTORY_LABELS
    /variable/CMAKE_DL_LIBS
    /variable/CMAKE_DL_LIBS
    /variable/CMAKE_EDIT_COMMAND
    /variable/CMAKE_EDIT_COMMAND
    /variable/CMAKE_EXECUTABLE_SUFFIX
    /variable/CMAKE_EXECUTABLE_SUFFIX
@@ -482,6 +483,7 @@ Variables for CTest
    /variable/CTEST_GIT_UPDATE_OPTIONS
    /variable/CTEST_GIT_UPDATE_OPTIONS
    /variable/CTEST_HG_COMMAND
    /variable/CTEST_HG_COMMAND
    /variable/CTEST_HG_UPDATE_OPTIONS
    /variable/CTEST_HG_UPDATE_OPTIONS
+   /variable/CTEST_LABELS_FOR_SUBPROJECTS
    /variable/CTEST_MEMORYCHECK_COMMAND
    /variable/CTEST_MEMORYCHECK_COMMAND
    /variable/CTEST_MEMORYCHECK_COMMAND_OPTIONS
    /variable/CTEST_MEMORYCHECK_COMMAND_OPTIONS
    /variable/CTEST_MEMORYCHECK_SANITIZER_OPTIONS
    /variable/CTEST_MEMORYCHECK_SANITIZER_OPTIONS

+ 33 - 0
Help/manual/ctest.1.rst

@@ -250,6 +250,13 @@ Options
  label associated with the tests run.  If there are no labels on the
  label associated with the tests run.  If there are no labels on the
  tests, nothing extra is printed.
  tests, nothing extra is printed.
 
 
+ ``--no-subproject-summary``
+ Disable timing summary information for subprojects.
+
+ This option tells ctest not to print summary information for each
+ subproject associated with the tests run.  If there are no subprojects on the
+ tests, nothing extra is printed.
+
 ``--build-and-test <path-to-source> <path-to-build>``
 ``--build-and-test <path-to-source> <path-to-build>``
  Configure, build and run a test.
  Configure, build and run a test.
 
 
@@ -758,6 +765,15 @@ Configuration settings include:
   * :module:`CTest` module variable: :variable:`CMAKE_COMMAND`
   * :module:`CTest` module variable: :variable:`CMAKE_COMMAND`
     followed by :variable:`PROJECT_SOURCE_DIR`
     followed by :variable:`PROJECT_SOURCE_DIR`
 
 
+``LabelsForSubprojects``
+  Specify a semicolon-separated list of labels that will be treated as
+  subprojects. This mapping will be passed on to CDash when configure, test or
+  build results are submitted.
+
+  * `CTest Script`_ variable: :variable:`CTEST_LABELS_FOR_SUBPROJECTS`
+  * :module:`CTest` module variable: ``CTEST_LABELS_FOR_SUBPROJECTS``
+
+
 .. _`CTest Build Step`:
 .. _`CTest Build Step`:
 
 
 CTest Build Step
 CTest Build Step
@@ -780,6 +796,14 @@ Configuration settings include:
   * :module:`CTest` module variable: ``DEFAULT_CTEST_CONFIGURATION_TYPE``,
   * :module:`CTest` module variable: ``DEFAULT_CTEST_CONFIGURATION_TYPE``,
     initialized by the ``CMAKE_CONFIG_TYPE`` environment variable
     initialized by the ``CMAKE_CONFIG_TYPE`` environment variable
 
 
+``LabelsForSubprojects``
+  Specify a semicolon-separated list of labels that will be treated as
+  subprojects. This mapping will be passed on to CDash when configure, test or
+  build results are submitted.
+
+  * `CTest Script`_ variable: :variable:`CTEST_LABELS_FOR_SUBPROJECTS`
+  * :module:`CTest` module variable: ``CTEST_LABELS_FOR_SUBPROJECTS``
+
 ``MakeCommand``
 ``MakeCommand``
   Command-line to launch the software build process.
   Command-line to launch the software build process.
   It will be executed in the location specified by the
   It will be executed in the location specified by the
@@ -815,6 +839,15 @@ Arguments to the command may specify some of the step settings.
 
 
 Configuration settings include:
 Configuration settings include:
 
 
+``LabelsForSubprojects``
+  Specify a semicolon-separated list of labels that will be treated as
+  subprojects. This mapping will be passed on to CDash when configure, test or
+  build results are submitted.
+
+  * `CTest Script`_ variable: :variable:`CTEST_LABELS_FOR_SUBPROJECTS`
+  * :module:`CTest` module variable: ``CTEST_LABELS_FOR_SUBPROJECTS``
+
+
 ``TestLoad``
 ``TestLoad``
   While running tests in parallel (e.g. with ``-j``), try not to start
   While running tests in parallel (e.g. with ``-j``), try not to start
   tests when they may cause the CPU load to pass above a given threshold.
   tests when they may cause the CPU load to pass above a given threshold.

+ 13 - 0
Help/prop_dir/LABELS.rst

@@ -0,0 +1,13 @@
+LABELS
+------
+
+Specify a list of text labels associated with a directory and all of its
+subdirectories. This is equivalent to setting the :prop_tgt:`LABELS` target
+property and the :prop_test:`LABELS` test property on all targets and tests in
+the current directory and subdirectories. Note: Launchers must enabled to
+propagate labels to targets.
+
+The :variable:`CMAKE_DIRECTORY_LABELS` variable can be used to initialize this
+property.
+
+The list is reported in dashboard submissions.

+ 14 - 0
Help/release/dev/labels_for_subprojects.rst

@@ -0,0 +1,14 @@
+labels_for_subprojects
+----------------------
+
+* A :variable:`CTEST_LABELS_FOR_SUBPROJECTS` CTest module variable and CTest
+  script variable was added to specify a list of labels that should be treated
+  as subprojects by CDash. To use this value in both the CTest module and the
+  ctest command line `Dashboard Client` mode (e.g. ctest -S) set it in the
+  CTestConfig.cmake config file.
+
+* A :prop_dir:`LABELS` directory property was added to specify labels
+  for all targets and tests in a directory.
+
+* A :variable:`CMAKE_DIRECTORY_LABELS` variable was added to specify
+  labels for all tests in a directory.

+ 6 - 0
Help/variable/CMAKE_DIRECTORY_LABELS.rst

@@ -0,0 +1,6 @@
+CMAKE_DIRECTORY_LABELS
+-----------------------
+
+Specify labels for the current directory.
+
+This is used to initialize the :prop_dir:`LABELS` directory property.

+ 5 - 0
Help/variable/CTEST_LABELS_FOR_SUBPROJECTS.rst

@@ -0,0 +1,5 @@
+CTEST_LABELS_FOR_SUBPROJECTS
+----------------------------
+
+Specify the CTest ``LabelsForSubprojects`` setting
+in a :manual:`ctest(1)` dashboard client script.

+ 3 - 0
Modules/DartConfiguration.tcl.in

@@ -16,6 +16,9 @@ Site: @SITE@
 # Build name is osname-revision-compiler, i.e. Linux-2.4.2-2smp-c++
 # Build name is osname-revision-compiler, i.e. Linux-2.4.2-2smp-c++
 BuildName: @BUILDNAME@
 BuildName: @BUILDNAME@
 
 
+# Subprojects
+LabelsForSubprojects: @CTEST_LABELS_FOR_SUBPROJECTS@
+
 # Submission information
 # Submission information
 IsCDash: @CTEST_DROP_SITE_CDASH@
 IsCDash: @CTEST_DROP_SITE_CDASH@
 CDashVersion: @CTEST_CDASH_VERSION@
 CDashVersion: @CTEST_CDASH_VERSION@

+ 6 - 0
Source/CTest/cmCTestBuildCommand.cxx

@@ -153,6 +153,12 @@ cmCTestGenericHandler* cmCTestBuildCommand::InitializeHandler()
                                        this->Quiet);
                                        this->Quiet);
   }
   }
 
 
+  if (const char* labelsForSubprojects =
+        this->Makefile->GetDefinition("CTEST_LABELS_FOR_SUBPROJECTS")) {
+    this->CTest->SetCTestConfiguration("LabelsForSubprojects",
+                                       labelsForSubprojects, this->Quiet);
+  }
+
   handler->SetQuiet(this->Quiet);
   handler->SetQuiet(this->Quiet);
   return handler;
   return handler;
 }
 }

+ 1 - 0
Source/CTest/cmCTestBuildHandler.cxx

@@ -488,6 +488,7 @@ int cmCTestBuildHandler::ProcessHandler()
 void cmCTestBuildHandler::GenerateXMLHeader(cmXMLWriter& xml)
 void cmCTestBuildHandler::GenerateXMLHeader(cmXMLWriter& xml)
 {
 {
   this->CTest->StartXML(xml, this->AppendXML);
   this->CTest->StartXML(xml, this->AppendXML);
+  this->CTest->GenerateSubprojectsOutput(xml);
   xml.StartElement("Build");
   xml.StartElement("Build");
   xml.Element("StartDateTime", this->StartBuild);
   xml.Element("StartDateTime", this->StartBuild);
   xml.Element("StartBuildTime",
   xml.Element("StartBuildTime",

+ 6 - 0
Source/CTest/cmCTestConfigureCommand.cxx

@@ -141,6 +141,12 @@ cmCTestGenericHandler* cmCTestConfigureCommand::InitializeHandler()
     }
     }
   }
   }
 
 
+  if (const char* labelsForSubprojects =
+        this->Makefile->GetDefinition("CTEST_LABELS_FOR_SUBPROJECTS")) {
+    this->CTest->SetCTestConfiguration("LabelsForSubprojects",
+                                       labelsForSubprojects, this->Quiet);
+  }
+
   cmCTestGenericHandler* handler =
   cmCTestGenericHandler* handler =
     this->CTest->GetInitializedHandler("configure");
     this->CTest->GetInitializedHandler("configure");
   if (!handler) {
   if (!handler) {

+ 1 - 0
Source/CTest/cmCTestConfigureHandler.cxx

@@ -73,6 +73,7 @@ int cmCTestConfigureHandler::ProcessHandler()
     if (os) {
     if (os) {
       cmXMLWriter xml(os);
       cmXMLWriter xml(os);
       this->CTest->StartXML(xml, this->AppendXML);
       this->CTest->StartXML(xml, this->AppendXML);
+      this->CTest->GenerateSubprojectsOutput(xml);
       xml.StartElement("Configure");
       xml.StartElement("Configure");
       xml.Element("StartDateTime", start_time);
       xml.Element("StartDateTime", start_time);
       xml.Element("StartConfigureTime", start_time_time);
       xml.Element("StartConfigureTime", start_time_time);

+ 1 - 0
Source/CTest/cmCTestMemCheckHandler.cxx

@@ -291,6 +291,7 @@ void cmCTestMemCheckHandler::GenerateDartOutput(cmXMLWriter& xml)
     return;
     return;
   }
   }
   this->CTest->StartXML(xml, this->AppendXML);
   this->CTest->StartXML(xml, this->AppendXML);
+  this->CTest->GenerateSubprojectsOutput(xml);
   xml.StartElement("DynamicAnalysis");
   xml.StartElement("DynamicAnalysis");
   switch (this->MemoryTesterStyle) {
   switch (this->MemoryTesterStyle) {
     case cmCTestMemCheckHandler::VALGRIND:
     case cmCTestMemCheckHandler::VALGRIND:

+ 1 - 1
Source/CTest/cmCTestScriptHandler.cxx

@@ -394,7 +394,7 @@ int cmCTestScriptHandler::ReadInScript(const std::string& total_script_arg)
   return 0;
   return 0;
 }
 }
 
 
-// extract variabels from the script to set ivars
+// extract variables from the script to set ivars
 int cmCTestScriptHandler::ExtractVariables()
 int cmCTestScriptHandler::ExtractVariables()
 {
 {
   // Temporary variables
   // Temporary variables

+ 6 - 0
Source/CTest/cmCTestTestCommand.cxx

@@ -124,6 +124,12 @@ cmCTestGenericHandler* cmCTestTestCommand::InitializeHandler()
   }
   }
   handler->SetTestLoad(testLoad);
   handler->SetTestLoad(testLoad);
 
 
+  if (const char* labelsForSubprojects =
+        this->Makefile->GetDefinition("CTEST_LABELS_FOR_SUBPROJECTS")) {
+    this->CTest->SetCTestConfiguration("LabelsForSubprojects",
+                                       labelsForSubprojects, this->Quiet);
+  }
+
   handler->SetQuiet(this->Quiet);
   handler->SetQuiet(this->Quiet);
   return handler;
   return handler;
 }
 }

+ 179 - 2
Source/CTest/cmCTestTestHandler.cxx

@@ -238,6 +238,36 @@ bool cmCTestSetTestsPropertiesCommand::InitialPass(
   return this->TestHandler->SetTestsProperties(args);
   return this->TestHandler->SetTestsProperties(args);
 }
 }
 
 
+class cmCTestSetDirectoryPropertiesCommand : public cmCommand
+{
+public:
+  /**
+   * This is a virtual constructor for the command.
+   */
+  cmCommand* Clone() CM_OVERRIDE
+  {
+    cmCTestSetDirectoryPropertiesCommand* c =
+      new cmCTestSetDirectoryPropertiesCommand;
+    c->TestHandler = this->TestHandler;
+    return c;
+  }
+
+  /**
+   * This is called when the command is first encountered in
+   * the CMakeLists.txt file.
+  */
+  bool InitialPass(std::vector<std::string> const& /*unused*/,
+                   cmExecutionStatus& /*unused*/) CM_OVERRIDE;
+
+  cmCTestTestHandler* TestHandler;
+};
+
+bool cmCTestSetDirectoryPropertiesCommand::InitialPass(
+  std::vector<std::string> const& args, cmExecutionStatus&)
+{
+  return this->TestHandler->SetDirectoryProperties(args);
+}
+
 // get the next number in a string with numbers separated by ,
 // get the next number in a string with numbers separated by ,
 // pos is the start of the search and pos2 is the end of the search
 // pos is the start of the search and pos2 is the end of the search
 // pos becomes pos2 after a call to GetNextNumber.
 // pos becomes pos2 after a call to GetNextNumber.
@@ -506,9 +536,14 @@ int cmCTestTestHandler::ProcessHandler()
                  << static_cast<int>(percent + .5f) << "% tests passed, "
                  << static_cast<int>(percent + .5f) << "% tests passed, "
                  << failed.size() << " tests failed out of " << total
                  << failed.size() << " tests failed out of " << total
                  << std::endl);
                  << std::endl);
-    if (this->CTest->GetLabelSummary()) {
+
+    if (!this->CTest->GetLabelsForSubprojects().empty() &&
+        this->CTest->GetSubprojectSummary()) {
+      this->PrintSubprojectSummary();
+    } else if (this->CTest->GetLabelSummary()) {
       this->PrintLabelSummary();
       this->PrintLabelSummary();
     }
     }
+
     char realBuf[1024];
     char realBuf[1024];
     sprintf(realBuf, "%6.2f sec", (double)(clock_finish - clock_start));
     sprintf(realBuf, "%6.2f sec", (double)(clock_finish - clock_start));
     cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
     cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
@@ -658,6 +693,84 @@ void cmCTestTestHandler::PrintLabelSummary()
   }
   }
 }
 }
 
 
+void cmCTestTestHandler::PrintSubprojectSummary()
+{
+  std::vector<std::string> subprojects =
+    this->CTest->GetLabelsForSubprojects();
+
+  cmCTestTestHandler::ListOfTests::iterator it = this->TestList.begin();
+  std::map<std::string, double> labelTimes;
+  std::map<std::string, int> labelCounts;
+  std::set<std::string> labels;
+  // initialize maps
+  std::string::size_type maxlen = 0;
+  for (; it != this->TestList.end(); ++it) {
+    cmCTestTestProperties& p = *it;
+    for (std::vector<std::string>::iterator l = p.Labels.begin();
+         l != p.Labels.end(); ++l) {
+      std::vector<std::string>::iterator subproject =
+        std::find(subprojects.begin(), subprojects.end(), *l);
+      if (subproject != subprojects.end()) {
+        if ((*l).size() > maxlen) {
+          maxlen = (*l).size();
+        }
+        labels.insert(*l);
+        labelTimes[*l] = 0;
+        labelCounts[*l] = 0;
+      }
+    }
+  }
+  cmCTestTestHandler::TestResultsVector::iterator ri =
+    this->TestResults.begin();
+  // fill maps
+  for (; ri != this->TestResults.end(); ++ri) {
+    cmCTestTestResult& result = *ri;
+    cmCTestTestProperties& p = *result.Properties;
+    for (std::vector<std::string>::iterator l = p.Labels.begin();
+         l != p.Labels.end(); ++l) {
+      std::vector<std::string>::iterator subproject =
+        std::find(subprojects.begin(), subprojects.end(), *l);
+      if (subproject != subprojects.end()) {
+        labelTimes[*l] += result.ExecutionTime;
+        ++labelCounts[*l];
+      }
+    }
+  }
+  // now print times
+  if (!labels.empty()) {
+    cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
+                       "\nSubproject Time Summary:", this->Quiet);
+  }
+  for (std::set<std::string>::const_iterator i = labels.begin();
+       i != labels.end(); ++i) {
+    std::string label = *i;
+    label.resize(maxlen + 3, ' ');
+
+    char buf[1024];
+    sprintf(buf, "%6.2f sec", labelTimes[*i]);
+
+    std::ostringstream labelCountStr;
+    labelCountStr << "(" << labelCounts[*i] << " test";
+    if (labelCounts[*i] > 1) {
+      labelCountStr << "s";
+    }
+    labelCountStr << ")";
+
+    cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "\n"
+                         << label << " = " << buf << " "
+                         << labelCountStr.str(),
+                       this->Quiet);
+    if (this->LogFile) {
+      *this->LogFile << "\n" << *i << " = " << buf << "\n";
+    }
+  }
+  if (!labels.empty()) {
+    if (this->LogFile) {
+      *this->LogFile << "\n";
+    }
+    cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "\n", this->Quiet);
+  }
+}
 void cmCTestTestHandler::CheckLabelFilterInclude(cmCTestTestProperties& it)
 void cmCTestTestHandler::CheckLabelFilterInclude(cmCTestTestProperties& it)
 {
 {
   // if not using Labels to filter then return
   // if not using Labels to filter then return
@@ -1277,6 +1390,7 @@ void cmCTestTestHandler::GenerateDartOutput(cmXMLWriter& xml)
   }
   }
 
 
   this->CTest->StartXML(xml, this->AppendXML);
   this->CTest->StartXML(xml, this->AppendXML);
+  this->CTest->GenerateSubprojectsOutput(xml);
   xml.StartElement("Testing");
   xml.StartElement("Testing");
   xml.Element("StartDateTime", this->StartTest);
   xml.Element("StartDateTime", this->StartTest);
   xml.Element("StartTestTime", this->StartTestTime);
   xml.Element("StartTestTime", this->StartTestTime);
@@ -1660,6 +1774,12 @@ void cmCTestTestHandler::GetListOfTests()
   newCom4->TestHandler = this;
   newCom4->TestHandler = this;
   cm.GetState()->AddBuiltinCommand("set_tests_properties", newCom4);
   cm.GetState()->AddBuiltinCommand("set_tests_properties", newCom4);
 
 
+  // Add handler for SET_DIRECTORY_PROPERTIES
+  cmCTestSetDirectoryPropertiesCommand* newCom5 =
+    new cmCTestSetDirectoryPropertiesCommand;
+  newCom5->TestHandler = this;
+  cm.GetState()->AddBuiltinCommand("set_directory_properties", newCom5);
+
   const char* testFilename;
   const char* testFilename;
   if (cmSystemTools::FileExists("CTestTestfile.cmake")) {
   if (cmSystemTools::FileExists("CTestTestfile.cmake")) {
     // does the CTestTestfile.cmake exist ?
     // does the CTestTestfile.cmake exist ?
@@ -2171,7 +2291,16 @@ bool cmCTestTestHandler::SetTestsProperties(
             cmSystemTools::ExpandListArgument(val, rtit->Environment);
             cmSystemTools::ExpandListArgument(val, rtit->Environment);
           }
           }
           if (key == "LABELS") {
           if (key == "LABELS") {
-            cmSystemTools::ExpandListArgument(val, rtit->Labels);
+            std::vector<std::string> Labels;
+            cmSystemTools::ExpandListArgument(val, Labels);
+            rtit->Labels.insert(rtit->Labels.end(), Labels.begin(),
+                                Labels.end());
+            // sort the array
+            std::sort(rtit->Labels.begin(), rtit->Labels.end());
+            // remove duplicates
+            std::vector<std::string>::iterator new_end =
+              std::unique(rtit->Labels.begin(), rtit->Labels.end());
+            rtit->Labels.erase(new_end, rtit->Labels.end());
           }
           }
           if (key == "MEASUREMENT") {
           if (key == "MEASUREMENT") {
             size_t pos = val.find_first_of('=');
             size_t pos = val.find_first_of('=');
@@ -2224,6 +2353,54 @@ bool cmCTestTestHandler::SetTestsProperties(
   return true;
   return true;
 }
 }
 
 
+bool cmCTestTestHandler::SetDirectoryProperties(
+  const std::vector<std::string>& args)
+{
+  std::vector<std::string>::const_iterator it;
+  std::vector<std::string> tests;
+  bool found = false;
+  for (it = args.begin(); it != args.end(); ++it) {
+    if (*it == "PROPERTIES") {
+      found = true;
+      break;
+    }
+    tests.push_back(*it);
+  }
+
+  if (!found) {
+    return false;
+  }
+  ++it; // skip PROPERTIES
+  for (; it != args.end(); ++it) {
+    std::string key = *it;
+    ++it;
+    if (it == args.end()) {
+      break;
+    }
+    std::string val = *it;
+    cmCTestTestHandler::ListOfTests::iterator rtit;
+    for (rtit = this->TestList.begin(); rtit != this->TestList.end(); ++rtit) {
+      std::string cwd = cmSystemTools::GetCurrentWorkingDirectory();
+      if (cwd == rtit->Directory) {
+        if (key == "LABELS") {
+          std::vector<std::string> DirectoryLabels;
+          cmSystemTools::ExpandListArgument(val, DirectoryLabels);
+          rtit->Labels.insert(rtit->Labels.end(), DirectoryLabels.begin(),
+                              DirectoryLabels.end());
+
+          // sort the array
+          std::sort(rtit->Labels.begin(), rtit->Labels.end());
+          // remove duplicates
+          std::vector<std::string>::iterator new_end =
+            std::unique(rtit->Labels.begin(), rtit->Labels.end());
+          rtit->Labels.erase(new_end, rtit->Labels.end());
+        }
+      }
+    }
+  }
+  return true;
+}
+
 bool cmCTestTestHandler::AddTest(const std::vector<std::string>& args)
 bool cmCTestTestHandler::AddTest(const std::vector<std::string>& args)
 {
 {
   const std::string& testname = args[0];
   const std::string& testname = args[0];

+ 7 - 0
Source/CTest/cmCTestTestHandler.h

@@ -90,6 +90,11 @@ public:
    */
    */
   bool SetTestsProperties(const std::vector<std::string>& args);
   bool SetTestsProperties(const std::vector<std::string>& args);
 
 
+  /**
+   * Set directory properties
+   */
+  bool SetDirectoryProperties(const std::vector<std::string>& args);
+
   void Initialize() CM_OVERRIDE;
   void Initialize() CM_OVERRIDE;
 
 
   // NOTE: This struct is Saved/Restored
   // NOTE: This struct is Saved/Restored
@@ -227,6 +232,8 @@ private:
   virtual void GenerateDartOutput(cmXMLWriter& xml);
   virtual void GenerateDartOutput(cmXMLWriter& xml);
 
 
   void PrintLabelSummary();
   void PrintLabelSummary();
+  void PrintSubprojectSummary();
+
   /**
   /**
    * Run the tests for a directory and any subdirectories
    * Run the tests for a directory and any subdirectories
    */
    */

+ 34 - 0
Source/cmCTest.cxx

@@ -11,6 +11,7 @@
 #include "cmsys/Process.h"
 #include "cmsys/Process.h"
 #include "cmsys/String.hxx"
 #include "cmsys/String.hxx"
 #include "cmsys/SystemInformation.hxx"
 #include "cmsys/SystemInformation.hxx"
+#include <algorithm>
 #include <ctype.h>
 #include <ctype.h>
 #include <iostream>
 #include <iostream>
 #include <map>
 #include <map>
@@ -253,6 +254,7 @@ std::string cmCTest::DecodeURL(const std::string& in)
 cmCTest::cmCTest()
 cmCTest::cmCTest()
 {
 {
   this->LabelSummary = true;
   this->LabelSummary = true;
+  this->SubprojectSummary = true;
   this->ParallelLevel = 1;
   this->ParallelLevel = 1;
   this->ParallelLevelSetInCli = false;
   this->ParallelLevelSetInCli = false;
   this->TestLoad = 0;
   this->TestLoad = 0;
@@ -1364,6 +1366,35 @@ void cmCTest::AddSiteProperties(cmXMLWriter& xml)
   }
   }
 }
 }
 
 
+void cmCTest::GenerateSubprojectsOutput(cmXMLWriter& xml)
+{
+  std::vector<std::string> subprojects = this->GetLabelsForSubprojects();
+  std::vector<std::string>::const_iterator i;
+  for (i = subprojects.begin(); i != subprojects.end(); ++i) {
+    xml.StartElement("Subproject");
+    xml.Attribute("name", *i);
+    xml.Element("Label", *i);
+    xml.EndElement(); // Subproject
+  }
+}
+
+std::vector<std::string> cmCTest::GetLabelsForSubprojects()
+{
+  std::string labelsForSubprojects =
+    this->GetCTestConfiguration("LabelsForSubprojects");
+  std::vector<std::string> subprojects;
+  cmSystemTools::ExpandListArgument(labelsForSubprojects, subprojects);
+
+  // sort the array
+  std::sort(subprojects.begin(), subprojects.end());
+  // remove duplicates
+  std::vector<std::string>::iterator new_end =
+    std::unique(subprojects.begin(), subprojects.end());
+  subprojects.erase(new_end, subprojects.end());
+
+  return subprojects;
+}
+
 void cmCTest::EndXML(cmXMLWriter& xml)
 void cmCTest::EndXML(cmXMLWriter& xml)
 {
 {
   xml.EndElement(); // Site
   xml.EndElement(); // Site
@@ -1765,6 +1796,9 @@ bool cmCTest::HandleCommandLineArguments(size_t& i,
   if (this->CheckArgument(arg, "--no-label-summary")) {
   if (this->CheckArgument(arg, "--no-label-summary")) {
     this->LabelSummary = false;
     this->LabelSummary = false;
   }
   }
+  if (this->CheckArgument(arg, "--no-subproject-summary")) {
+    this->SubprojectSummary = false;
+  }
   if (this->CheckArgument(arg, "-Q", "--quiet")) {
   if (this->CheckArgument(arg, "-Q", "--quiet")) {
     this->Quiet = true;
     this->Quiet = true;
   }
   }

+ 6 - 0
Source/cmCTest.h

@@ -438,7 +438,9 @@ public:
     this->StreamErr = err;
     this->StreamErr = err;
   }
   }
   void AddSiteProperties(cmXMLWriter& xml);
   void AddSiteProperties(cmXMLWriter& xml);
+
   bool GetLabelSummary() { return this->LabelSummary; }
   bool GetLabelSummary() { return this->LabelSummary; }
+  bool GetSubprojectSummary() { return this->SubprojectSummary; }
 
 
   std::string GetCostDataFile();
   std::string GetCostDataFile();
 
 
@@ -453,6 +455,9 @@ public:
   /** Return true if test should run until fail */
   /** Return true if test should run until fail */
   bool GetRepeatUntilFail() { return this->RepeatUntilFail; }
   bool GetRepeatUntilFail() { return this->RepeatUntilFail; }
 
 
+  void GenerateSubprojectsOutput(cmXMLWriter& xml);
+  std::vector<std::string> GetLabelsForSubprojects();
+
 private:
 private:
   int RepeatTests;
   int RepeatTests;
   bool RepeatUntilFail;
   bool RepeatUntilFail;
@@ -464,6 +469,7 @@ private:
   bool ExtraVerbose;
   bool ExtraVerbose;
   bool ProduceXML;
   bool ProduceXML;
   bool LabelSummary;
   bool LabelSummary;
+  bool SubprojectSummary;
   bool UseHTTP10;
   bool UseHTTP10;
   bool PrintLabels;
   bool PrintLabels;
   bool Failover;
   bool Failover;

+ 2 - 2
Source/cmCommands.cxx

@@ -147,8 +147,6 @@ void GetScriptingCommands(cmState* state)
   state->AddBuiltinCommand("separate_arguments",
   state->AddBuiltinCommand("separate_arguments",
                            new cmSeparateArgumentsCommand);
                            new cmSeparateArgumentsCommand);
   state->AddBuiltinCommand("set", new cmSetCommand);
   state->AddBuiltinCommand("set", new cmSetCommand);
-  state->AddBuiltinCommand("set_directory_properties",
-                           new cmSetDirectoryPropertiesCommand);
   state->AddBuiltinCommand("set_property", new cmSetPropertyCommand);
   state->AddBuiltinCommand("set_property", new cmSetPropertyCommand);
   state->AddBuiltinCommand("site_name", new cmSiteNameCommand);
   state->AddBuiltinCommand("site_name", new cmSiteNameCommand);
   state->AddBuiltinCommand("string", new cmStringCommand);
   state->AddBuiltinCommand("string", new cmStringCommand);
@@ -231,6 +229,8 @@ void GetProjectCommands(cmState* state)
   state->AddBuiltinCommand("install_targets", new cmInstallTargetsCommand);
   state->AddBuiltinCommand("install_targets", new cmInstallTargetsCommand);
   state->AddBuiltinCommand("link_directories", new cmLinkDirectoriesCommand);
   state->AddBuiltinCommand("link_directories", new cmLinkDirectoriesCommand);
   state->AddBuiltinCommand("project", new cmProjectCommand);
   state->AddBuiltinCommand("project", new cmProjectCommand);
+  state->AddBuiltinCommand("set_directory_properties",
+                           new cmSetDirectoryPropertiesCommand);
   state->AddBuiltinCommand("set_source_files_properties",
   state->AddBuiltinCommand("set_source_files_properties",
                            new cmSetSourceFilesPropertiesCommand);
                            new cmSetSourceFilesPropertiesCommand);
   state->AddBuiltinCommand("set_target_properties",
   state->AddBuiltinCommand("set_target_properties",

+ 48 - 9
Source/cmGlobalGenerator.cxx

@@ -2811,7 +2811,12 @@ void cmGlobalGenerator::WriteSummary(cmGeneratorTarget* target)
 
 
 #ifdef CMAKE_BUILD_WITH_CMAKE
 #ifdef CMAKE_BUILD_WITH_CMAKE
   // Check whether labels are enabled for this target.
   // Check whether labels are enabled for this target.
-  if (const char* value = target->GetProperty("LABELS")) {
+  const char* targetLabels = target->GetProperty("LABELS");
+  const char* directoryLabels =
+    target->Target->GetMakefile()->GetProperty("LABELS");
+  const char* cmakeDirectoryLabels =
+    target->Target->GetMakefile()->GetDefinition("CMAKE_DIRECTORY_LABELS");
+  if (targetLabels || directoryLabels || cmakeDirectoryLabels) {
     Json::Value lj_root(Json::objectValue);
     Json::Value lj_root(Json::objectValue);
     Json::Value& lj_target = lj_root["target"] = Json::objectValue;
     Json::Value& lj_target = lj_root["target"] = Json::objectValue;
     lj_target["name"] = target->GetName();
     lj_target["name"] = target->GetName();
@@ -2821,19 +2826,53 @@ void cmGlobalGenerator::WriteSummary(cmGeneratorTarget* target)
     cmSystemTools::MakeDirectory(dir.c_str());
     cmSystemTools::MakeDirectory(dir.c_str());
     cmGeneratedFileStream fout(file.c_str());
     cmGeneratedFileStream fout(file.c_str());
 
 
+    std::vector<std::string> labels;
+
     // List the target-wide labels.  All sources in the target get
     // List the target-wide labels.  All sources in the target get
     // these labels.
     // these labels.
-    std::vector<std::string> labels;
-    cmSystemTools::ExpandListArgument(value, labels);
-    if (!labels.empty()) {
-      fout << "# Target labels\n";
-      for (std::vector<std::string>::const_iterator li = labels.begin();
-           li != labels.end(); ++li) {
-        fout << " " << *li << "\n";
-        lj_target_labels.append(*li);
+    if (targetLabels) {
+      cmSystemTools::ExpandListArgument(targetLabels, labels);
+      if (!labels.empty()) {
+        fout << "# Target labels\n";
+        for (std::vector<std::string>::const_iterator li = labels.begin();
+             li != labels.end(); ++li) {
+          fout << " " << *li << "\n";
+          lj_target_labels.append(*li);
+        }
       }
       }
     }
     }
 
 
+    // List directory labels
+    std::vector<std::string> directoryLabelsList;
+    std::vector<std::string> cmakeDirectoryLabelsList;
+
+    if (directoryLabels) {
+      cmSystemTools::ExpandListArgument(directoryLabels, directoryLabelsList);
+    }
+
+    if (cmakeDirectoryLabels) {
+      cmSystemTools::ExpandListArgument(cmakeDirectoryLabels,
+                                        cmakeDirectoryLabelsList);
+    }
+
+    if (!directoryLabelsList.empty() || !cmakeDirectoryLabelsList.empty()) {
+      fout << "# Directory labels\n";
+    }
+
+    for (std::vector<std::string>::const_iterator li =
+           directoryLabelsList.begin();
+         li != directoryLabelsList.end(); ++li) {
+      fout << " " << *li << "\n";
+      lj_target_labels.append(*li);
+    }
+
+    for (std::vector<std::string>::const_iterator li =
+           cmakeDirectoryLabelsList.begin();
+         li != cmakeDirectoryLabelsList.end(); ++li) {
+      fout << " " << *li << "\n";
+      lj_target_labels.append(*li);
+    }
+
     // List the source files with any per-source labels.
     // List the source files with any per-source labels.
     fout << "# Source files and their labels\n";
     fout << "# Source files and their labels\n";
     std::vector<cmSourceFile*> sources;
     std::vector<cmSourceFile*> sources;

+ 20 - 0
Source/cmLocalGenerator.cxx

@@ -277,6 +277,25 @@ void cmLocalGenerator::GenerateTestFiles()
     outP = cmOutputConverter::EscapeForCMake(outP);
     outP = cmOutputConverter::EscapeForCMake(outP);
     fout << "subdirs(" << outP << ")" << std::endl;
     fout << "subdirs(" << outP << ")" << std::endl;
   }
   }
+
+  // Add directory labels property
+  const char* directoryLabels =
+    this->Makefile->GetDefinition("CMAKE_DIRECTORY_LABELS");
+  const char* labels = this->Makefile->GetProperty("LABELS");
+
+  if (labels || directoryLabels) {
+    fout << "set_directory_properties(PROPERTIES LABELS ";
+    if (labels) {
+      fout << cmOutputConverter::EscapeForCMake(labels);
+    }
+    if (labels && directoryLabels) {
+      fout << ";";
+    }
+    if (directoryLabels) {
+      fout << cmOutputConverter::EscapeForCMake(directoryLabels);
+    }
+    fout << ")" << std::endl;
+  }
 }
 }
 
 
 void cmLocalGenerator::CreateEvaluationFileOutputs(std::string const& config)
 void cmLocalGenerator::CreateEvaluationFileOutputs(std::string const& config)
@@ -327,6 +346,7 @@ void cmLocalGenerator::GenerateInstallRules()
 {
 {
   // Compute the install prefix.
   // Compute the install prefix.
   const char* prefix = this->Makefile->GetDefinition("CMAKE_INSTALL_PREFIX");
   const char* prefix = this->Makefile->GetDefinition("CMAKE_INSTALL_PREFIX");
+
 #if defined(_WIN32) && !defined(__CYGWIN__)
 #if defined(_WIN32) && !defined(__CYGWIN__)
   std::string prefix_win32;
   std::string prefix_win32;
   if (!prefix) {
   if (!prefix) {

+ 3 - 0
Source/cmMakefile.cxx

@@ -1235,6 +1235,9 @@ void cmMakefile::InitializeFromParent(cmMakefile* parent)
     }
     }
   }
   }
 
 
+  // labels
+  this->SetProperty("LABELS", parent->GetProperty("LABELS"));
+
   // link libraries
   // link libraries
   this->SetProperty("LINK_LIBRARIES", parent->GetProperty("LINK_LIBRARIES"));
   this->SetProperty("LINK_LIBRARIES", parent->GetProperty("LINK_LIBRARIES"));
 
 

+ 2 - 0
Source/ctest.cxx

@@ -83,6 +83,8 @@ static const char* cmDocumentationOptions[][2] = {
   { "--max-width <width>", "Set the max width for a test name to output" },
   { "--max-width <width>", "Set the max width for a test name to output" },
   { "--interactive-debug-mode [0|1]", "Set the interactive mode to 0 or 1." },
   { "--interactive-debug-mode [0|1]", "Set the interactive mode to 0 or 1." },
   { "--no-label-summary", "Disable timing summary information for labels." },
   { "--no-label-summary", "Disable timing summary information for labels." },
+  { "--no-subproject-summary", "Disable timing summary information for "
+                               "subprojects." },
   { "--build-and-test", "Configure, build and run a test." },
   { "--build-and-test", "Configure, build and run a test." },
   { "--build-target", "Specify a specific target to build." },
   { "--build-target", "Specify a specific target to build." },
   { "--build-nocmake", "Run the build without running cmake first." },
   { "--build-nocmake", "Run the build without running cmake first." },

+ 1 - 0
Tests/RunCMake/CMakeLists.txt

@@ -348,6 +348,7 @@ if("${CMAKE_GENERATOR}" MATCHES "Make|Ninja")
     list(APPEND CompilerLauncher_ARGS -DCMake_TEST_CUDA=${CMake_TEST_CUDA})
     list(APPEND CompilerLauncher_ARGS -DCMake_TEST_CUDA=${CMake_TEST_CUDA})
   endif()
   endif()
   add_RunCMake_test(CompilerLauncher)
   add_RunCMake_test(CompilerLauncher)
+  add_RunCMake_test(ctest_labels_for_subprojects)
 endif()
 endif()
 
 
 add_RunCMake_test_group(CPack "DEB;RPM;7Z;TBZ2;TGZ;TXZ;TZ;ZIP;STGZ")
 add_RunCMake_test_group(CPack "DEB;RPM;7Z;TBZ2;TGZ;TXZ;TZ;ZIP;STGZ")

+ 5 - 0
Tests/RunCMake/ctest_labels_for_subprojects/CMakeLists.txt.in

@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 3.9)
+@CASE_CMAKELISTS_PREFIX_CODE@
+project(CTestLabelsForSubprojects@CASE_NAME@ NONE)
+include(CTest)
+@CASE_CMAKELISTS_SUFFIX_CODE@

+ 2 - 0
Tests/RunCMake/ctest_labels_for_subprojects/CTestConfig.cmake.in

@@ -0,0 +1,2 @@
+set(CTEST_PROJECT_NAME "CTestLabelsForSubprojects@CASE_NAME@")
+@CTEST_EXTRA_CONFIG@

+ 36 - 0
Tests/RunCMake/ctest_labels_for_subprojects/CTestConfigCTestScript-check.cmake

@@ -0,0 +1,36 @@
+set(EXPERIMENTAL_FEATURE_REGEX "<Subproject name=\"MyExperimentalFeature\">.*<Label>MyExperimentalFeature</Label>.*</Subproject>")
+set(PRODUCTION_CODE_REGEX "<Subproject name=\"MyProductionCode\">.*<Label>MyProductionCode</Label>.*</Subproject>")
+set(SUBPROJECTS_REGEX "${EXPERIMENTAL_FEATURE_REGEX}.*${PRODUCTION_CODE_REGEX}")
+
+file(GLOB configure_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Configure.xml")
+if(configure_xml_file)
+  file(READ "${configure_xml_file}" configure_xml)
+  if(NOT configure_xml MATCHES "${SUBPROJECTS_REGEX}.*<Configure>")
+     set(RunCMake_TEST_FAILED "Configure.xml does not contain the expected list of subprojects")
+  endif()
+else()
+  set(RunCMake_TEST_FAILED "Configure.xml not found")
+endif()
+
+file(GLOB build_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Build.xml")
+if(build_xml_file)
+  file(READ "${build_xml_file}" build_xml)
+  set(BUILD_WARNING_REGEX "<Failure type=\"Warning\">.*<Labels>.*<Label>MyExperimentalFeature</Label>.*</Labels>")
+  if(NOT build_xml MATCHES "${SUBPROJECTS_REGEX}.*<Build>.*${BUILD_ERROR_REGEX}.*</Build>")
+    set(RunCMake_TEST_FAILED "Build.xml does not contain the expected list of subprojects and labels")
+  endif()
+else()
+  set(RunCMake_TEST_FAILED "Build.xml not found")
+endif()
+
+file(GLOB test_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Test.xml")
+if(test_xml_file)
+  file(READ "${test_xml_file}" test_xml)
+  set(TEST_FAILED_REGEX "<Test Status=\"failed\">.*<Labels>.*<Label>MyExperimentalFeature</Label>.*<Label>NotASubproject</Label>.*</Labels>")
+  set(TEST_PASSED_REGEX "<Test Status=\"passed\">.*<Labels>.*<Label>MyProductionCode</Label>.*</Labels>")
+  if(NOT test_xml MATCHES "${SUBPROJECTS_REGEX}.*<Testing>.*${TEST_FAILED_REGEX}.*${TEST_PASSED_REGEX}.*${TEST_NOTRUN_REGEX}.*</Testing>")
+    set(RunCMake_TEST_FAILED "Test.xml does not contain the expected list of subprojects and labels")
+  endif()
+else()
+  set(RunCMake_TEST_FAILED "${CTEST_BINARY_DIRECTORY}/Testing/*/Test.xml not found")
+endif()

+ 7 - 0
Tests/RunCMake/ctest_labels_for_subprojects/CTestConfigCTestScript-stdout.txt

@@ -0,0 +1,7 @@
+17% tests passed, 5 tests failed out of 6
++
+Subproject Time Summary:
+MyExperimentalFeature += +[0-9.]+ sec \(5 tests\)
+MyProductionCode += +[0-9.]+ sec \(1 test\)
++
+Total Test time \(real\) = +[0-9.]+ sec

+ 36 - 0
Tests/RunCMake/ctest_labels_for_subprojects/CTestScriptVariable-check.cmake

@@ -0,0 +1,36 @@
+set(EXPERIMENTAL_FEATURE_REGEX "<Subproject name=\"MyExperimentalFeature\">.*<Label>MyExperimentalFeature</Label>.*</Subproject>")
+set(PRODUCTION_CODE_REGEX "<Subproject name=\"MyProductionCode\">.*<Label>MyProductionCode</Label>.*</Subproject>")
+set(SUBPROJECTS_REGEX "${EXPERIMENTAL_FEATURE_REGEX}.*${PRODUCTION_CODE_REGEX}")
+
+file(GLOB configure_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Configure.xml")
+if(configure_xml_file)
+  file(READ "${configure_xml_file}" configure_xml)
+  if(NOT configure_xml MATCHES "${SUBPROJECTS_REGEX}.*<Configure>")
+     set(RunCMake_TEST_FAILED "Configure.xml does not contain the expected list of subprojects")
+  endif()
+else()
+  set(RunCMake_TEST_FAILED "Configure.xml not found")
+endif()
+
+file(GLOB build_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Build.xml")
+if(build_xml_file)
+  file(READ "${build_xml_file}" build_xml)
+  set(BUILD_WARNING_REGEX "<Failure type=\"Warning\">.*<Labels>.*<Label>MyExperimentalFeature</Label>.*</Labels>")
+  if(NOT build_xml MATCHES "${SUBPROJECTS_REGEX}.*<Build>.*${BUILD_ERROR_REGEX}.*</Build>")
+    set(RunCMake_TEST_FAILED "Build.xml does not contain the expected list of subprojects and labels")
+  endif()
+else()
+  set(RunCMake_TEST_FAILED "Build.xml not found")
+endif()
+
+file(GLOB test_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Test.xml")
+if(test_xml_file)
+  file(READ "${test_xml_file}" test_xml)
+  set(TEST_FAILED_REGEX "<Test Status=\"failed\">.*<Labels>.*<Label>MyExperimentalFeature</Label>.*<Label>NotASubproject</Label>.*</Labels>")
+  set(TEST_PASSED_REGEX "<Test Status=\"passed\">.*<Labels>.*<Label>MyProductionCode</Label>.*</Labels>")
+  if(NOT test_xml MATCHES "${SUBPROJECTS_REGEX}.*<Testing>.*${TEST_FAILED_REGEX}.*${TEST_PASSED_REGEX}.*${TEST_NOTRUN_REGEX}.*</Testing>")
+    set(RunCMake_TEST_FAILED "Test.xml does not contain the expected list of subprojects and labels")
+  endif()
+else()
+  set(RunCMake_TEST_FAILED "${CTEST_BINARY_DIRECTORY}/Testing/*/Test.xml not found")
+endif()

+ 7 - 0
Tests/RunCMake/ctest_labels_for_subprojects/CTestScriptVariable-stdout.txt

@@ -0,0 +1,7 @@
+17% tests passed, 5 tests failed out of 6
++
+Subproject Time Summary:
+MyExperimentalFeature += +[0-9.]+ sec \(5 tests\)
+MyProductionCode += +[0-9.]+ sec \(1 test\)
++
+Total Test time \(real\) = +[0-9.]+ sec

+ 34 - 0
Tests/RunCMake/ctest_labels_for_subprojects/CTestScriptVariableCommandLine-check.cmake

@@ -0,0 +1,34 @@
+set(THIRD_PARTY_REGEX "<Subproject name=\"MyThirdPartyDependency\">.*<Label>MyThirdPartyDependency</Label>.*</Subproject>")
+set(SUBPROJECTS_REGEX "${THIRD_PARTY_REGEX}")
+
+file(GLOB configure_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Configure.xml")
+if(configure_xml_file)
+  file(READ "${configure_xml_file}" configure_xml)
+  if(NOT configure_xml MATCHES "${SUBPROJECTS_REGEX}.*<Configure>")
+     set(RunCMake_TEST_FAILED "Configure.xml does not contain the expected list of subprojects")
+  endif()
+else()
+  set(RunCMake_TEST_FAILED "Configure.xml not found")
+endif()
+
+file(GLOB build_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Build.xml")
+if(build_xml_file)
+  file(READ "${build_xml_file}" build_xml)
+  set(BUILD_ERROR_REGEX "<Failure type=\"Error\">.*<Labels>.*<Label>MyThirdPartyDependency</Label>.*<Label>NotASubproject</Label>.*</Labels>")
+  if(NOT build_xml MATCHES "${SUBPROJECTS_REGEX}.*<Build>.*${BUILD_ERROR_REGEX}.*</Build>")
+    set(RunCMake_TEST_FAILED "Build.xml does not contain the expected list of subprojects and labels")
+  endif()
+else()
+  set(RunCMake_TEST_FAILED "Build.xml not found")
+endif()
+
+file(GLOB test_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Test.xml")
+if(test_xml_file)
+  file(READ "${test_xml_file}" test_xml)
+  set(TEST_NOTRUN_REGEX "<Test Status=\"notrun\">.*<Labels>.*<Label>MyThirdPartyDependency</Label>.*<Label>NotASubproject</Label>.*</Labels>")
+  if(NOT test_xml MATCHES "${SUBPROJECTS_REGEX}.*<Testing>.*${TEST_FAILED_REGEX}.*${TEST_PASSED_REGEX}.*${TEST_NOTRUN_REGEX}.*</Testing>")
+    set(RunCMake_TEST_FAILED "Test.xml does not contain the expected list of subprojects and labels")
+  endif()
+else()
+  set(RunCMake_TEST_FAILED "${CTEST_BINARY_DIRECTORY}/Testing/*/Test.xml not found")
+endif()

+ 1 - 0
Tests/RunCMake/ctest_labels_for_subprojects/CTestScriptVariableCommandLine-result.txt

@@ -0,0 +1 @@
+(-1|255)

+ 1 - 0
Tests/RunCMake/ctest_labels_for_subprojects/CTestScriptVariableCommandLine-stderr.txt

@@ -0,0 +1 @@
+Unable to find executable:.*MyThirdPartyDependency/src/thirdparty

+ 6 - 0
Tests/RunCMake/ctest_labels_for_subprojects/CTestScriptVariableCommandLine-stdout.txt

@@ -0,0 +1,6 @@
+0% tests passed, 1 tests failed out of 1
++
+Subproject Time Summary:
+MyThirdPartyDependency += +[0-9.]+ sec \(1 test\)
++
+Total Test time \(real\) = +[0-9.]+ sec

+ 1 - 0
Tests/RunCMake/ctest_labels_for_subprojects/ModuleVariableCMakeLists-result.txt

@@ -0,0 +1 @@
+8

+ 1 - 0
Tests/RunCMake/ctest_labels_for_subprojects/ModuleVariableCMakeLists-stderr.txt

@@ -0,0 +1 @@
+Errors while running CTest

+ 6 - 0
Tests/RunCMake/ctest_labels_for_subprojects/ModuleVariableCMakeLists-stdout.txt

@@ -0,0 +1,6 @@
+50% tests passed, 1 tests failed out of 2
++
+Subproject Time Summary:
+MySubproject += +[0-9.]+ sec \(2 tests\)
++
+Total Test time \(real\) = +[0-9.]+ sec

+ 1 - 0
Tests/RunCMake/ctest_labels_for_subprojects/ModuleVariableCTestConfig-result.txt

@@ -0,0 +1 @@
+8

+ 1 - 0
Tests/RunCMake/ctest_labels_for_subprojects/ModuleVariableCTestConfig-stderr.txt

@@ -0,0 +1 @@
+Errors while running CTest

+ 6 - 0
Tests/RunCMake/ctest_labels_for_subprojects/ModuleVariableCTestConfig-stdout.txt

@@ -0,0 +1,6 @@
+67% tests passed, 1 tests failed out of 3
++
+Subproject Time Summary:
+MySubproject += +[0-9.]+ sec \(2 tests\)
++
+Total Test time \(real\) = +[0-9.]+ sec

+ 1 - 0
Tests/RunCMake/ctest_labels_for_subprojects/ModuleVariableCTestConfigNoSummary-result.txt

@@ -0,0 +1 @@
+8

+ 1 - 0
Tests/RunCMake/ctest_labels_for_subprojects/ModuleVariableCTestConfigNoSummary-stderr.txt

@@ -0,0 +1 @@
+Errors while running CTest

+ 7 - 0
Tests/RunCMake/ctest_labels_for_subprojects/ModuleVariableCTestConfigNoSummary-stdout.txt

@@ -0,0 +1,7 @@
+67% tests passed, 1 tests failed out of 3
++
+Label Time Summary:
+MySubproject += +[0-9.]+ sec \(2 tests\)
+NotASubproject += +[0-9.]+ sec \(1 test\)
++
+Total Test time \(real\) = +[0-9.]+ sec

+ 1 - 0
Tests/RunCMake/ctest_labels_for_subprojects/ModuleVariableCommandLine-result.txt

@@ -0,0 +1 @@
+8

+ 1 - 0
Tests/RunCMake/ctest_labels_for_subprojects/ModuleVariableCommandLine-stderr.txt

@@ -0,0 +1 @@
+Errors while running CTest

+ 6 - 0
Tests/RunCMake/ctest_labels_for_subprojects/ModuleVariableCommandLine-stdout.txt

@@ -0,0 +1,6 @@
+50% tests passed, 1 tests failed out of 2
++
+Subproject Time Summary:
+MySubproject += +[0-9.]+ sec \(2 tests\)
++
+Total Test time \(real\) = +[0-9.]+ sec

+ 33 - 0
Tests/RunCMake/ctest_labels_for_subprojects/MyExperimentalFeature/CMakeLists.txt

@@ -0,0 +1,33 @@
+project(MyExperimentalFeature)
+cmake_minimum_required(VERSION 3.8)
+
+include(CTest)
+
+set(CMAKE_DIRECTORY_LABELS "MyExperimentalFeature;NotASubproject")
+
+add_executable(testapp experimental.c)
+
+add_test(experimentalFail1 testapp 5)
+set_tests_properties (experimentalFail1
+  PROPERTIES PASS_REGULAR_EXPRESSION "Test!"
+  )
+
+add_test(experimentalFail2 testapp -5)
+set_tests_properties (experimentalFail2
+  PROPERTIES PASS_REGULAR_EXPRESSION "Test!"
+  )
+
+add_test(experimentalFail3 testapp -5)
+set_tests_properties (experimentalFail3
+  PROPERTIES PASS_REGULAR_EXPRESSION "Test!"
+  )
+
+add_test(experimentalFail4 testapp -5)
+set_tests_properties (experimentalFail4
+  PROPERTIES PASS_REGULAR_EXPRESSION "Test!"
+  )
+
+add_test(experimentalFail5 testapp -5)
+set_tests_properties (experimentalFail5
+  PROPERTIES PASS_REGULAR_EXPRESSION "Test!"
+  )

+ 16 - 0
Tests/RunCMake/ctest_labels_for_subprojects/MyExperimentalFeature/experimental.c

@@ -0,0 +1,16 @@
+#include <stdio.h>
+
+int main(void)
+{
+  int i = 0;
+  if (i > 0) {
+    printf("This doesn't happen.\n");
+    printf("Neither does this.\n");
+  }
+  i = i + 1;
+  if (i > 0) {
+    printf("This does happen.\n");
+  }
+
+  return 0;
+}

+ 12 - 0
Tests/RunCMake/ctest_labels_for_subprojects/MyProductionCode/CMakeLists.txt

@@ -0,0 +1,12 @@
+project(MyProductionCode)
+cmake_minimum_required(VERSION 3.8)
+
+include(CTest)
+
+add_executable(production production.c)
+add_test(NAME production COMMAND production)
+
+set_property(TARGET production PROPERTY LABELS MyProductionCode)
+set_property(TEST production PROPERTY LABELS MyProductionCode)
+
+set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY LABELS "NotASubproject")

+ 16 - 0
Tests/RunCMake/ctest_labels_for_subprojects/MyProductionCode/production.c

@@ -0,0 +1,16 @@
+#include <stdio.h>
+
+int main(void)
+{
+  int j = 0;
+  if (j > 0) {
+    printf("This doesn't happen.\n");
+    printf("Neither does this.\n");
+  }
+  j = j + 1;
+  if (j > 0) {
+    printf("This does happen.\n");
+  }
+
+  return 0;
+}

+ 7 - 0
Tests/RunCMake/ctest_labels_for_subprojects/MyThirdPartyDependency/CMakeLists.txt

@@ -0,0 +1,7 @@
+project(MyThirdPartyDependency)
+cmake_minimum_required(VERSION 3.8)
+
+include(CTest)
+
+set_directory_properties(PROPERTIES LABELS "NotASubproject;MyThirdPartyDependency")
+add_subdirectory(src)

+ 10 - 0
Tests/RunCMake/ctest_labels_for_subprojects/MyThirdPartyDependency/src/CMakeLists.txt

@@ -0,0 +1,10 @@
+project(MyThirdPartyDependency)
+cmake_minimum_required(VERSION 3.8)
+
+include(CTest)
+
+add_executable(thirdparty thirdparty.c)
+add_test(NAME thirdparty COMMAND thirdparty)
+
+set_property(TARGET thirdparty PROPERTY LABELS NotASubproject)
+set_property(TEST thirdparty PROPERTY LABELS NotASubproject)

+ 14 - 0
Tests/RunCMake/ctest_labels_for_subprojects/MyThirdPartyDependency/src/thirdparty.c

@@ -0,0 +1,14 @@
+#include <stdio.h>
+
+int main(void)
+{
+  printf(This function has an error!\n");
+  n = 5;
+  return 0;
+}
+
+int notcalled()
+{
+  printf(This function doesn't get called.\n");
+  return 0;
+}

+ 185 - 0
Tests/RunCMake/ctest_labels_for_subprojects/RunCMakeTest.cmake

@@ -0,0 +1,185 @@
+include(RunCTest)
+
+
+# 1. Specify subprojects in the CTest script
+function(run_CTestScriptVariable)
+  set(CTEST_EXTRA_CONFIG "set(CTEST_USE_LAUNCHERS 1)")
+  set(CASE_TEST_PREFIX_CODE [[
+file(COPY "${CTEST_RUNCMAKE_SOURCE_DIRECTORY}/MyProductionCode"
+  DESTINATION ${CTEST_SOURCE_DIRECTORY})
+file(COPY "${CTEST_RUNCMAKE_SOURCE_DIRECTORY}/MyExperimentalFeature"
+  DESTINATION ${CTEST_SOURCE_DIRECTORY})
+
+set(CTEST_LABELS_FOR_SUBPROJECTS "MyProductionCode;MyExperimentalFeature")
+  ]])
+  set(CASE_CMAKELISTS_SUFFIX_CODE [[
+add_subdirectory(MyExperimentalFeature)
+add_subdirectory(MyProductionCode)
+  ]])
+
+  run_ctest(CTestScriptVariable)
+
+  unset(CTEST_EXTRA_CONFIG)
+  unset(CASE_TEST_PREFIX_CODE)
+  unset(CASE_CMAKELISTS_SUFFIX_CODE)
+endfunction()
+run_CTestScriptVariable()
+
+# 2. Specify subprojects via a CTest script variable on the command line e.g.
+#    ctest -S test.cmake -DCTEST_LABELS_FOR_SUBPROJECTS:STRING="MySubproject"
+# Note: This test includes a failing build
+function(run_CTestScriptVariableCommandLine)
+  set(CTEST_EXTRA_CONFIG "set(CTEST_USE_LAUNCHERS 1)")
+  set(CASE_TEST_PREFIX_CODE [[
+file(COPY "${CTEST_RUNCMAKE_SOURCE_DIRECTORY}/MyThirdPartyDependency"
+  DESTINATION ${CTEST_SOURCE_DIRECTORY})
+  ]])
+  set(CASE_CMAKELISTS_SUFFIX_CODE [[
+add_subdirectory(MyThirdPartyDependency)
+  ]])
+
+  run_ctest(CTestScriptVariableCommandLine "-DCTEST_LABELS_FOR_SUBPROJECTS:STRING=MyThirdPartyDependency")
+
+  unset(CTEST_EXTRA_CONFIG)
+  unset(CASE_TEST_PREFIX_CODE)
+  unset(CASE_CMAKELISTS_SUFFIX_CODE)
+endfunction()
+run_CTestScriptVariableCommandLine()
+
+# 3. Set subprojects via a CTest module variable on the command line
+#    (will populate DartConfiguration.tcl)
+function(run_ModuleVariableCommandLine)
+  set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/ModuleVariableCommandLine")
+  set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/ModuleVariableCommandLine-build")
+  set(RunCMake_TEST_NO_CLEAN 1)
+  file(REMOVE_RECURSE "${RunCMake_TEST_SOURCE_DIR}")
+  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+
+  set(CASE_CMAKELISTS_SUFFIX_CODE [[
+set(someFile "${CMAKE_CURRENT_SOURCE_DIR}/test.cmake")
+add_test(NAME SuccessfulTest COMMAND "${CMAKE_COMMAND}" --version)
+set_property(TEST SuccessfulTest PROPERTY LABELS MySubproject)
+add_test(NAME FailingTest
+          COMMAND ${CMAKE_COMMAND} -E compare_files "${someFile}" "${someFile}xxx")
+set_property(TEST FailingTest PROPERTY LABELS MySubproject)
+  ]])
+  configure_file(${RunCMake_SOURCE_DIR}/CMakeLists.txt.in
+                 ${RunCMake_TEST_SOURCE_DIR}/CMakeLists.txt @ONLY)
+
+  set(RunCMake_TEST_OPTIONS "-DCTEST_LABELS_FOR_SUBPROJECTS:STRING=MySubproject")
+  run_cmake(ModuleVariableCommandLine-cmake)
+  unset(RunCMake_TEST_OPTIONS)
+  run_cmake_command(ModuleVariableCommandLine ${CMAKE_CTEST_COMMAND} -C Debug -V)
+
+  unset(RunCMake_TEST_SOURCE_DIR)
+  unset(RunCMake_TEST_BINARY_DIR)
+  unset(RunCMake_TEST_NO_CLEAN)
+  unset(CASE_CMAKELISTS_SUFFIX_CODE)
+endfunction()
+run_ModuleVariableCommandLine()
+
+# 4. Set subprojects via a CTest module variable in CMakeLists.txt
+#    (will populate DartConfiguration.tcl)
+function(run_ModuleVariableCMakeLists)
+  set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/ModuleVariableCMakeLists")
+  set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/ModuleVariableCMakeLists-build")
+  set(RunCMake_TEST_NO_CLEAN 1)
+  file(REMOVE_RECURSE "${RunCMake_TEST_SOURCE_DIR}")
+  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+
+  set(CASE_CMAKELISTS_PREFIX_CODE [[
+set(CTEST_LABELS_FOR_SUBPROJECTS MySubproject)
+]])
+
+  set(CASE_CMAKELISTS_SUFFIX_CODE [[
+set(someFile "${CMAKE_CURRENT_SOURCE_DIR}/test.cmake")
+add_test(NAME SuccessfulTest COMMAND "${CMAKE_COMMAND}" --version)
+set_property(TEST SuccessfulTest PROPERTY LABELS MySubproject)
+add_test(NAME FailingTest
+          COMMAND ${CMAKE_COMMAND} -E compare_files "${someFile}" "${someFile}xxx")
+set_property(TEST FailingTest PROPERTY LABELS MySubproject)
+  ]])
+  configure_file(${RunCMake_SOURCE_DIR}/CMakeLists.txt.in
+                 ${RunCMake_TEST_SOURCE_DIR}/CMakeLists.txt @ONLY)
+
+  run_cmake(ModuleVariableCMakeLists-cmake)
+  run_cmake_command(ModuleVariableCMakeLists ${CMAKE_CTEST_COMMAND} -C Debug -V)
+
+  unset(RunCMake_TEST_SOURCE_DIR)
+  unset(RunCMake_TEST_BINARY_DIR)
+  unset(RunCMake_TEST_NO_CLEAN)
+  unset(CASE_CMAKELISTS_SUFFIX_CODE)
+  unset(CASE_CMAKELISTS_SUFFIX_CODE)
+endfunction()
+run_ModuleVariableCMakeLists()
+
+# The remaining tests set subprojects in CTestConfig.cmake. Settings in this
+# config file are shared by both the CTest module and the ctest command line
+# `Dashboard Client` mode (e.g. ctest -S).
+
+function(run_ModuleVariableCTestConfig CASE_NAME)
+  set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/${CASE_NAME}")
+  set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/${CASE_NAME}-build")
+  set(RunCMake_TEST_NO_CLEAN 1)
+  file(REMOVE_RECURSE "${RunCMake_TEST_SOURCE_DIR}")
+  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+
+  set(CTEST_EXTRA_CONFIG "set(CTEST_LABELS_FOR_SUBPROJECTS \"MySubproject\")")
+  configure_file(${RunCMake_SOURCE_DIR}/CTestConfig.cmake.in
+                 ${RunCMake_TEST_SOURCE_DIR}/CTestConfig.cmake @ONLY)
+
+  set(CASE_CMAKELISTS_SUFFIX_CODE [[
+set(someFile "${CMAKE_CURRENT_SOURCE_DIR}/test.cmake")
+add_test(NAME SuccessfulTest COMMAND "${CMAKE_COMMAND}" --version)
+set_property(TEST SuccessfulTest PROPERTY LABELS MySubproject)
+add_test(NAME FailingTest
+          COMMAND ${CMAKE_COMMAND} -E compare_files "${someFile}" "${someFile}xxx")
+set_property(TEST FailingTest PROPERTY LABELS MySubproject)
+add_test(NAME AnotherSuccessfulTest COMMAND "${CMAKE_COMMAND}" --version)
+set_property(TEST AnotherSuccessfulTest PROPERTY LABELS NotASubproject)
+  ]])
+  configure_file(${RunCMake_SOURCE_DIR}/CMakeLists.txt.in
+                 ${RunCMake_TEST_SOURCE_DIR}/CMakeLists.txt @ONLY)
+
+  run_cmake(${CASE_NAME}-cmake)
+  run_cmake_command(${CASE_NAME} ${CMAKE_CTEST_COMMAND} -C Debug -V ${ARGN})
+
+  unset(RunCMake_TEST_SOURCE_DIR)
+  unset(RunCMake_TEST_BINARY_DIR)
+  unset(RunCMake_TEST_NO_CLEAN)
+  unset(CTEST_EXTRA_CONFIG)
+  unset(CASE_CMAKELISTS_SUFFIX_CODE)
+endfunction()
+
+# 5. Check that the Subproject timing Summary is printed
+run_ModuleVariableCTestConfig(ModuleVariableCTestConfig)
+
+# 6. Use --no-subproject-summary to disable the Subproject timing summary.
+run_ModuleVariableCTestConfig(ModuleVariableCTestConfigNoSummary --no-subproject-summary)
+
+# 7. Verify that subprojects are sent to CDash when running a CTest script
+function(run_CTestConfigCTestScript)
+  set(CTEST_EXTRA_CONFIG [[
+set(CTEST_USE_LAUNCHERS 1)
+set(CTEST_LABELS_FOR_SUBPROJECTS "MyProductionCode;MyExperimentalFeature")
+  ]])
+  set(CASE_TEST_PREFIX_CODE [[
+file(COPY "${CTEST_RUNCMAKE_SOURCE_DIRECTORY}/MyProductionCode"
+  DESTINATION ${CTEST_SOURCE_DIRECTORY})
+file(COPY "${CTEST_RUNCMAKE_SOURCE_DIRECTORY}/MyExperimentalFeature"
+  DESTINATION ${CTEST_SOURCE_DIRECTORY})
+  ]])
+  set(CASE_CMAKELISTS_SUFFIX_CODE [[
+add_subdirectory(MyExperimentalFeature)
+add_subdirectory(MyProductionCode)
+  ]])
+  run_ctest(CTestConfigCTestScript)
+
+  unset(CTEST_EXTRA_CONFIG)
+  unset(CASE_TEST_PREFIX_CODE)
+  unset(CASE_CMAKELISTS_SUFFIX_CODE)
+endfunction()
+run_CTestConfigCTestScript()

+ 21 - 0
Tests/RunCMake/ctest_labels_for_subprojects/test.cmake.in

@@ -0,0 +1,21 @@
+cmake_minimum_required(VERSION 3.7)
+
+# Settings:
+
+set(CTEST_SITE                          "test-site")
+set(CTEST_BUILD_NAME                    "test-build-name")
+set(CTEST_SOURCE_DIRECTORY              "@RunCMake_BINARY_DIR@/@CASE_NAME@")
+set(CTEST_BINARY_DIRECTORY              "@RunCMake_BINARY_DIR@/@CASE_NAME@-build")
+set(CTEST_CMAKE_GENERATOR               "@RunCMake_GENERATOR@")
+set(CTEST_CMAKE_GENERATOR_PLATFORM      "@RunCMake_GENERATOR_PLATFORM@")
+set(CTEST_CMAKE_GENERATOR_TOOLSET       "@RunCMake_GENERATOR_TOOLSET@")
+set(CTEST_BUILD_CONFIGURATION           "$ENV{CMAKE_CONFIG_TYPE}")
+set(CTEST_RUNCMAKE_SOURCE_DIRECTORY     "@RunCMake_SOURCE_DIR@")
+
+@CASE_TEST_PREFIX_CODE@
+
+set(ctest_test_args "@CASE_CTEST_TEST_ARGS@")
+ctest_start(Experimental)
+ctest_configure()
+ctest_build()
+ctest_test(${ctest_test_args})

Некоторые файлы не были показаны из-за большого количества измененных файлов