Quellcode durchsuchen

ENH: Implement feature request from issue 7885. Allow setting environment variables on a per-test basis for ctest using set_test_properties ENVIRONMENT.

David Cole vor 17 Jahren
Ursprung
Commit
ceaef94ccc

+ 1 - 1
Source/CTest/cmCTestBuildAndTestHandler.cxx

@@ -382,7 +382,7 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring)
     }
   
   int runTestRes = this->CTest->RunTest(testCommand, &outs, &retval, 0, 
-                                        remainingTime);
+                                        remainingTime, 0);
 
   if(runTestRes != cmsysProcess_State_Exited || retval != 0)
     {

+ 15 - 5
Source/CTest/cmCTestTestHandler.cxx

@@ -733,11 +733,11 @@ void cmCTestTestHandler::ProcessOneTest(cmCTestTestProperties *it,
   if ( !this->CTest->GetShowOnly() )
     {
     res = this->CTest->RunTest(arguments, &output, &retVal, this->LogFile,
-                               it->Timeout);
+                               it->Timeout, &it->Environment);
     }
-  
-  clock_finish = cmSystemTools::GetTime();
-  
+
+  clock_finish = cmSystemTools::GetTime();  
+
   if ( this->LogFile )
     {
     double ttime = clock_finish - clock_start;
@@ -2209,7 +2209,7 @@ bool cmCTestTestHandler::SetTestsProperties(
               }
             }
           if ( key == "DEPENDS" )
-            { 
+            {
             std::vector<std::string> lval;
             cmSystemTools::ExpandListArgument(val.c_str(), lval);
             std::vector<std::string>::iterator crit;
@@ -2218,6 +2218,16 @@ bool cmCTestTestHandler::SetTestsProperties(
               rtit->Depends.push_back(*crit);
               }
             }
+          if ( key == "ENVIRONMENT" )
+            { 
+            std::vector<std::string> lval;
+            cmSystemTools::ExpandListArgument(val.c_str(), lval);
+            std::vector<std::string>::iterator crit;
+            for ( crit = lval.begin(); crit != lval.end(); ++ crit )
+              {
+              rtit->Environment.push_back(*crit);
+              }
+            }
           if ( key == "MEASUREMENT" )
             {
             size_t pos = val.find_first_of("=");

+ 1 - 0
Source/CTest/cmCTestTestHandler.h

@@ -90,6 +90,7 @@ public:
     bool WillFail;
     double Timeout;
     int Index;
+    std::vector<std::string> Environment;
   };
 
   struct cmCTestTestResult

+ 26 - 1
Source/cmCTest.cxx

@@ -1105,8 +1105,12 @@ int cmCTest::RunMakeCommand(const char* command, std::string* output,
 //----------------------------------------------------------------------
 int cmCTest::RunTest(std::vector<const char*> argv,
                      std::string* output, int *retVal,
-                     std::ostream* log, double testTimeOut)
+                     std::ostream* log, double testTimeOut,
+                     std::vector<std::string>* environment)
 {
+  std::vector<std::string> origEnv;
+  bool modifyEnv = (environment && environment->size()>0);
+
   // determine how much time we have
   double timeout = this->GetRemainingTimeAllowed() - 120;
   if (this->TimeOut && this->TimeOut < timeout)
@@ -1156,6 +1160,11 @@ int cmCTest::RunTest(std::vector<const char*> argv,
       }
     std::string oldpath = cmSystemTools::GetCurrentWorkingDirectory();
 
+    if (modifyEnv)
+      {
+      origEnv = cmSystemTools::AppendEnv(environment);
+      }
+
     *retVal = inst.Run(args, output);
     if ( *log )
       {
@@ -1166,6 +1175,12 @@ int cmCTest::RunTest(std::vector<const char*> argv,
     cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
       "Internal cmCTest object used to run test." << std::endl
       <<  *output << std::endl);
+
+    if (modifyEnv)
+      {
+      cmSystemTools::RestoreEnv(origEnv);
+      }
+
     return cmsysProcess_State_Exited;
     }
   std::vector<char> tempOutput;
@@ -1174,6 +1189,11 @@ int cmCTest::RunTest(std::vector<const char*> argv,
     *output = "";
     }
 
+  if (modifyEnv)
+    {
+    origEnv = cmSystemTools::AppendEnv(environment);
+    }
+
   cmsysProcess* cp = cmsysProcess_New();
   cmsysProcess_SetCommand(cp, &*argv.begin());
   cmCTestLog(this, DEBUG, "Command is: " << argv[0] << std::endl);
@@ -1233,6 +1253,11 @@ int cmCTest::RunTest(std::vector<const char*> argv,
     }
   cmsysProcess_Delete(cp);
 
+  if (modifyEnv)
+    {
+    cmSystemTools::RestoreEnv(origEnv);
+    }
+
   return result;
 }
 

+ 4 - 2
Source/cmCTest.h

@@ -246,9 +246,11 @@ public:
   void SetProduceXML(bool v);
 
   //! Run command specialized for tests. Returns process status and retVal is
-  // return value or exception.
+  // return value or exception. If environment is non-null, it is used to set
+  // environment variables prior to running the test. After running the test,
+  // environment variables are restored to their previous values.
   int RunTest(std::vector<const char*> args, std::string* output, int *retVal,
-    std::ostream* logfile, double testTimeOut);
+    std::ostream* logfile, double testTimeOut, std::vector<std::string>* environment);
 
   /**
    * Execute handler and return its result. If the handler fails, it returns

+ 50 - 0
Source/cmSystemTools.cxx

@@ -1556,6 +1556,7 @@ bool cmSystemTools::PutEnv(const char* value)
 }
 
 #ifdef CMAKE_BUILD_WITH_CMAKE
+//----------------------------------------------------------------------
 bool cmSystemTools::UnsetEnv(const char* value)
 {
 #if !defined(HAVE_UNSETENV)
@@ -1568,6 +1569,7 @@ bool cmSystemTools::UnsetEnv(const char* value)
 #endif
 }
 
+//----------------------------------------------------------------------
 std::vector<std::string> cmSystemTools::GetEnvironmentVariables()
 {
   std::vector<std::string> env;
@@ -1578,6 +1580,54 @@ std::vector<std::string> cmSystemTools::GetEnvironmentVariables()
     }
   return env;
 }
+
+//----------------------------------------------------------------------
+std::vector<std::string> cmSystemTools::AppendEnv(
+  std::vector<std::string>* env)
+{
+  std::vector<std::string> origEnv = GetEnvironmentVariables();
+
+  if (env && env->size()>0)
+    {
+    std::vector<std::string>::const_iterator eit;
+
+    for (eit = env->begin(); eit!= env->end(); ++eit)
+      {
+      PutEnv(eit->c_str());
+      }
+    }
+
+  return origEnv;
+}
+
+//----------------------------------------------------------------------
+void cmSystemTools::RestoreEnv(const std::vector<std::string>& env)
+{
+  std::vector<std::string>::const_iterator eit;
+
+  // First clear everything in the current environment:
+  //
+  std::vector<std::string> currentEnv = GetEnvironmentVariables();
+  for (eit = currentEnv.begin(); eit!= currentEnv.end(); ++eit)
+    {
+    std::string var(*eit);
+
+    int pos = var.find("=");
+    if (pos != std::string::npos)
+      {
+      var = var.substr(0, pos);
+      }
+
+    UnsetEnv(var.c_str());
+    }
+
+  // Then put back each entry from the original environment:
+  //
+  for (eit = env.begin(); eit!= env.end(); ++eit)
+    {
+    PutEnv(eit->c_str());
+    }
+}
 #endif
 
 void cmSystemTools::EnableVSConsoleOutput()

+ 11 - 0
Source/cmSystemTools.h

@@ -335,6 +335,17 @@ public:
 
   /** Get the list of all environment variables */
   static std::vector<std::string> GetEnvironmentVariables();
+
+  /** Append multiple variables to the current environment.
+      Return the original environment, as it was before the
+      append. */
+  static std::vector<std::string> AppendEnv(
+    std::vector<std::string>* env);
+
+  /** Restore the full environment to "env" - use after
+      AppendEnv to put the environment back to the way it
+      was. */
+  static void RestoreEnv(const std::vector<std::string>& env);
 #endif
 
   /** Setup the environment to enable VS 8 IDE output.  */

+ 13 - 0
Tests/CMakeLists.txt

@@ -497,6 +497,19 @@ ${CMake_BINARY_DIR}/bin/cmake -DVERSION=CVS -P ${CMake_SOURCE_DIR}/Utilities/Rel
     )
   LIST(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Example")
 
+  ADD_TEST(Environment ${CMAKE_CTEST_COMMAND}
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/Environment"
+    "${CMake_BINARY_DIR}/Tests/Environment"
+    --build-generator ${CMAKE_TEST_GENERATOR}
+    --build-project EnvironmentProj
+    --build-makeprogram ${CMAKE_TEST_MAKEPROGRAM}
+    --build-exe-dir "${CMake_BINARY_DIR}/Tests/Environment"
+    --force-new-ctest-process
+    --test-command ${CMAKE_CTEST_COMMAND} -VV
+    )
+  LIST(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Environment")
+
   # do each of the tutorial steps
   FOREACH(STP RANGE 1 7)
     ADD_TEST(TutorialStep${STP} ${CMAKE_CTEST_COMMAND}

+ 26 - 0
Tests/Environment/CMakeLists.txt

@@ -0,0 +1,26 @@
+cmake_minimum_required(VERSION 2.6)
+project(EnvironmentProj)
+
+add_executable(Environment main.cxx)
+
+enable_testing()
+
+add_test(Environment1 Environment)
+add_test(Environment2 Environment)
+add_test(EchoEnvironment1 ${CMAKE_COMMAND} -E environment)
+add_test(EchoEnvironment2 ${CMAKE_COMMAND} -E environment)
+
+# Make sure "CMAKE_ENV.*Happy Thanksgiving" is in the output of
+# the "1" tests:
+#
+set_tests_properties(Environment1 EchoEnvironment1 PROPERTIES
+  ENVIRONMENT "CMAKE_ENVIRONMENT_TEST_VAR=Happy Thanksgiving!"
+  PASS_REGULAR_EXPRESSION "CMAKE_ENV.*Happy Thanksgiving"
+)
+
+# Make sure "CMAKE_ENV.*Happy Thanksgiving" is *NOT* in the output of
+# the "2" tests:
+#
+set_tests_properties(Environment2 EchoEnvironment2 PROPERTIES
+  FAIL_REGULAR_EXPRESSION "CMAKE_ENV.*Happy Thanksgiving"
+)

+ 16 - 0
Tests/Environment/main.cxx

@@ -0,0 +1,16 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char *argv[])
+{
+  char* var = getenv("CMAKE_ENVIRONMENT_TEST_VAR");
+  if (!var)
+    {
+    var = "(null)";
+    }
+
+  fprintf(stdout, "Environment:\n");
+  fprintf(stdout, "  CMAKE_ENVIRONMENT_TEST_VAR='%s'\n", var);
+
+  return 0;
+}