1
0
Эх сурвалжийг харах

ENH: Add NAME mode to ADD_TEST command

This creates command mode add_test(NAME ...).  This signature is
extensible with more keyword arguments later.  The main purpose is to
enable automatic replacement of target names with built target file
locations.  A side effect of this feature is support for tests that only
run under specific configurations.
Brad King 16 жил өмнө
parent
commit
9862f383d0

+ 110 - 1
Source/cmAddTestCommand.cxx

@@ -25,6 +25,11 @@
 bool cmAddTestCommand
 ::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &)
 {
+  if(!args.empty() && args[0] == "NAME")
+    {
+    return this->HandleNameMode(args);
+    }
+
   // First argument is the name of the test Second argument is the name of
   // the executable to run (a target or external program) Remaining arguments
   // are the arguments to pass to the executable
@@ -45,12 +50,116 @@ bool cmAddTestCommand
   // Create the test but add a generator only the first time it is
   // seen.  This preserves behavior from before test generators.
   cmTest* test = this->Makefile->GetTest(args[0].c_str());
-  if(!test)
+  if(test)
+    {
+    // If the test was already added by a new-style signature do not
+    // allow it to be duplicated.
+    if(!test->GetOldStyle())
+      {
+      cmOStringStream e;
+      e << " given test name \"" << args[0]
+        << "\" which already exists in this directory.";
+      this->SetError(e.str().c_str());
+      return false;
+      }
+    }
+  else
     {
     test = this->Makefile->CreateTest(args[0].c_str());
+    test->SetOldStyle(true);
     this->Makefile->AddTestGenerator(new cmTestGenerator(test));
     }
   test->SetCommand(command);
 
   return true;
 }
+
+//----------------------------------------------------------------------------
+bool cmAddTestCommand::HandleNameMode(std::vector<std::string> const& args)
+{
+  std::string name;
+  std::vector<std::string> configurations;
+  std::vector<std::string> command;
+
+  // Read the arguments.
+  enum Doing {
+    DoingName,
+    DoingCommand,
+    DoingConfigs,
+    DoingNone
+  };
+  Doing doing = DoingName;
+  for(unsigned int i=1; i < args.size(); ++i)
+    {
+    if(args[i] == "COMMAND")
+      {
+      if(!command.empty())
+        {
+        this->SetError(" may be given at most one COMMAND.");
+        return false;
+        }
+      doing = DoingCommand;
+      }
+    else if(args[i] == "CONFIGURATIONS")
+      {
+      if(!configurations.empty())
+        {
+        this->SetError(" may be given at most one set of CONFIGURATIONS.");
+        return false;
+        }
+      doing = DoingConfigs;
+      }
+    else if(doing == DoingName)
+      {
+      name = args[i];
+      doing = DoingNone;
+      }
+    else if(doing == DoingCommand)
+      {
+      command.push_back(args[i]);
+      }
+    else if(doing == DoingConfigs)
+      {
+      configurations.push_back(args[i]);
+      }
+    else
+      {
+      cmOStringStream e;
+      e << " given unknown argument:\n  " << args[i] << "\n";
+      this->SetError(e.str().c_str());
+      return false;
+      }
+    }
+
+  // Require a test name.
+  if(name.empty())
+    {
+    this->SetError(" must be given non-empty NAME.");
+    return false;
+    }
+
+  // Require a command.
+  if(command.empty())
+    {
+    this->SetError(" must be given non-empty COMMAND.");
+    return false;
+    }
+
+  // Require a unique test name within the directory.
+  if(this->Makefile->GetTest(name.c_str()))
+    {
+    cmOStringStream e;
+    e << " given test NAME \"" << name
+      << "\" which already exists in this directory.";
+    this->SetError(e.str().c_str());
+    return false;
+    }
+
+  // Add the test.
+  cmTest* test = this->Makefile->CreateTest(name.c_str());
+  test->SetOldStyle(false);
+  test->SetCommand(command);
+  this->Makefile->AddTestGenerator(new cmTestGenerator(test, configurations));
+
+  return true;
+}

+ 12 - 2
Source/cmAddTestCommand.h

@@ -70,11 +70,21 @@ public:
       "built by this project or an arbitrary executable on the "
       "system (like tclsh).  The test will be run with the current working "
       "directory set to the CMakeList.txt files corresponding directory "
-      "in the binary tree.";
+      "in the binary tree."
+      "\n"
+      "  add_test(NAME <name> [CONFIGURATIONS [Debug|Release|...]]\n"
+      "           COMMAND <command> [arg1 [arg2 ...]])\n"
+      "If COMMAND specifies an executable target (created by "
+      "add_executable) it will automatically be replaced by the location "
+      "of the executable created at build time.  "
+      "If a CONFIGURATIONS option is given then the test will be executed "
+      "only when testing under one of the named configurations."
+      ;
     }
   
   cmTypeMacro(cmAddTestCommand, cmCommand);
-
+private:
+  bool HandleNameMode(std::vector<std::string> const& args);
 };
 
 

+ 1 - 0
Source/cmTest.cxx

@@ -23,6 +23,7 @@
 cmTest::cmTest() 
 {
   this->Makefile = 0;
+  this->OldStyle = true;
 }
 
 cmTest::~cmTest()

+ 6 - 0
Source/cmTest.h

@@ -63,11 +63,17 @@ public:
   void SetMakefile(cmMakefile *mf);
   cmMakefile *GetMakefile() { return this->Makefile;};
 
+  /** Get/Set whether this is an old-style test.  */
+  bool GetOldStyle() const { return this->OldStyle; }
+  void SetOldStyle(bool b) { this->OldStyle = b; }
+
 private:
   cmPropertyMap Properties;
   cmStdString Name;
   std::vector<std::string> Command;
 
+  bool OldStyle;
+
   // The cmMakefile instance that owns this target.  This should
   // always be set.
   cmMakefile* Makefile;  

+ 66 - 2
Source/cmTestGenerator.cxx

@@ -16,7 +16,10 @@
 =========================================================================*/
 #include "cmTestGenerator.h"
 
+#include "cmLocalGenerator.h"
+#include "cmMakefile.h"
 #include "cmSystemTools.h"
+#include "cmTarget.h"
 #include "cmTest.h"
 
 //----------------------------------------------------------------------------
@@ -26,7 +29,7 @@ cmTestGenerator
   cmScriptGenerator("CTEST_CONFIGURATION_TYPE", configurations),
   Test(test)
 {
-  this->ActionsPerConfig = false;
+  this->ActionsPerConfig = !test->GetOldStyle();
   this->TestGenerated = false;
 }
 
@@ -92,8 +95,69 @@ void cmTestGenerator::GenerateScriptConfigs(std::ostream& os,
 }
 
 //----------------------------------------------------------------------------
-void cmTestGenerator::GenerateScriptActions(std::ostream& fout,
+void cmTestGenerator::GenerateScriptActions(std::ostream& os,
                                             Indent const& indent)
+{
+  if(this->ActionsPerConfig)
+    {
+    // This is the per-config generation in a single-configuration
+    // build generator case.  The superclass will call our per-config
+    // method.
+    this->cmScriptGenerator::GenerateScriptActions(os, indent);
+    }
+  else
+    {
+    // This is an old-style test, so there is only one config.
+    //assert(this->Test->GetOldStyle());
+    this->GenerateOldStyle(os, indent);
+    }
+}
+
+//----------------------------------------------------------------------------
+void cmTestGenerator::GenerateScriptForConfig(std::ostream& os,
+                                              const char* config,
+                                              Indent const& indent)
+{
+  this->TestGenerated = true;
+
+  // Start the test command.
+  os << indent << "ADD_TEST(" << this->Test->GetName() << " ";
+
+  // Get the test command line to be executed.
+  std::vector<std::string> const& command = this->Test->GetCommand();
+
+  // Check whether the command executable is a target whose name is to
+  // be translated.
+  std::string exe = command[0];
+  cmMakefile* mf = this->Test->GetMakefile();
+  cmTarget* target = mf->FindTargetToUse(exe.c_str());
+  if(target && target->GetType() == cmTarget::EXECUTABLE)
+    {
+    // Use the target file on disk.
+    exe = target->GetFullPath(config);
+    }
+  else
+    {
+    // Use the command name given.
+    cmSystemTools::ConvertToUnixSlashes(exe);
+    }
+
+  // Generate the command line with full escapes.
+  cmLocalGenerator* lg = mf->GetLocalGenerator();
+  os << lg->EscapeForCMake(exe.c_str());
+  for(std::vector<std::string>::const_iterator ci = command.begin()+1;
+      ci != command.end(); ++ci)
+    {
+    os << " " << lg->EscapeForCMake(ci->c_str());
+    }
+
+  // Finish the test command.
+  os << ")\n";
+}
+
+//----------------------------------------------------------------------------
+void cmTestGenerator::GenerateOldStyle(std::ostream& fout,
+                                       Indent const& indent)
 {
   this->TestGenerated = true;
 

+ 4 - 0
Source/cmTestGenerator.h

@@ -36,6 +36,10 @@ public:
 protected:
   virtual void GenerateScriptConfigs(std::ostream& os, Indent const& indent);
   virtual void GenerateScriptActions(std::ostream& os, Indent const& indent);
+  virtual void GenerateScriptForConfig(std::ostream& os,
+                                       const char* config,
+                                       Indent const& indent);
+  void GenerateOldStyle(std::ostream& os, Indent const& indent);
 
   cmTest* Test;
   bool TestGenerated;

+ 7 - 1
Tests/Testing/CMakeLists.txt

@@ -1,7 +1,7 @@
 #
 # Testing
 #
-cmake_minimum_required (VERSION 2.6)
+cmake_minimum_required (VERSION 2.7)
 PROJECT (Testing)
 
 #
@@ -52,3 +52,9 @@ ADD_TEST(testing.1 ${Testing_BINARY_DIR}/bin/testing)
 # skip level test
 #
 ADD_SUBDIRECTORY(Sub/Sub2)
+
+# Per-config target name test.
+ADD_EXECUTABLE(perconfig perconfig.c)
+SET_PROPERTY(TARGET perconfig PROPERTY RELEASE_POSTFIX -opt)
+SET_PROPERTY(TARGET perconfig PROPERTY DEBUG_POSTFIX -dbg)
+ADD_TEST(NAME testing.perconfig COMMAND perconfig)

+ 4 - 0
Tests/Testing/perconfig.c

@@ -0,0 +1,4 @@
+int main()
+{
+  return 0;
+}