summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Weisi <adam.weisi@intel.com>2017-04-13 17:10:04 (GMT)
committerBrad King <brad.king@kitware.com>2017-06-10 12:03:06 (GMT)
commitd6051ca39e2ac8e5afc8f6308fd1bda7d1e8c17b (patch)
tree231f86c5da5035fb62d94c2484de56785b3bf968
parentc095e90f3af78fb3421c80b725ebc6ad9ec85999 (diff)
downloadCMake-d6051ca39e2ac8e5afc8f6308fd1bda7d1e8c17b.zip
CMake-d6051ca39e2ac8e5afc8f6308fd1bda7d1e8c17b.tar.gz
CMake-d6051ca39e2ac8e5afc8f6308fd1bda7d1e8c17b.tar.bz2
execute_process: Add option to get results of every child
Add a `RESULTS_VARIABLE` option to get the results of all children in a pipeline of one or more `COMMAND`s.
-rw-r--r--Help/command/execute_process.rst9
-rw-r--r--Help/release/dev/execute_process-pipeline-results.rst6
-rw-r--r--Source/cmExecuteProcessCommand.cxx53
-rw-r--r--Tests/RunCMake/CMakeLists.txt4
-rw-r--r--Tests/RunCMake/execute_process/ExitValues-stdout.txt14
-rw-r--r--Tests/RunCMake/execute_process/ExitValues.cmake120
-rw-r--r--Tests/RunCMake/execute_process/RunCMakeTest.cmake4
-rw-r--r--Tests/RunCMake/exit_code.c30
8 files changed, 237 insertions, 3 deletions
diff --git a/Help/command/execute_process.rst b/Help/command/execute_process.rst
index d617243..799493f 100644
--- a/Help/command/execute_process.rst
+++ b/Help/command/execute_process.rst
@@ -10,6 +10,7 @@ Execute one or more child processes.
[WORKING_DIRECTORY <directory>]
[TIMEOUT <seconds>]
[RESULT_VARIABLE <variable>]
+ [RESULTS_VARIABLE <variable>]
[OUTPUT_VARIABLE <variable>]
[ERROR_VARIABLE <variable>]
[INPUT_FILE <file>]
@@ -49,10 +50,16 @@ Options:
specified number of seconds (fractions are allowed).
``RESULT_VARIABLE``
- The variable will be set to contain the result of running the processes.
+ The variable will be set to contain the result of last child process.
This will be an integer return code from the last child or a string
describing an error condition.
+``RESULTS_VARIABLE <variable>``
+ The variable will be set to contain the result of all processes as a
+ :ref:`;-list <CMake Language Lists>`, in order of the given ``COMMAND``
+ arguments. Each entry will be an integer return code from the
+ corresponding child or a string describing an error condition.
+
``OUTPUT_VARIABLE``, ``ERROR_VARIABLE``
The variable named will be set with the contents of the standard output
and standard error pipes, respectively. If the same variable is named
diff --git a/Help/release/dev/execute_process-pipeline-results.rst b/Help/release/dev/execute_process-pipeline-results.rst
new file mode 100644
index 0000000..9755ef5
--- /dev/null
+++ b/Help/release/dev/execute_process-pipeline-results.rst
@@ -0,0 +1,6 @@
+execute_process-pipeline-results
+--------------------------------
+
+* The :command:`execute_process` command gained a ``RESULTS_VARIABLE``
+ option to collect a list of results from all children in a pipeline
+ of processes when multiple ``COMMAND`` arguments are given.
diff --git a/Source/cmExecuteProcessCommand.cxx b/Source/cmExecuteProcessCommand.cxx
index 8c10dbe..435adca 100644
--- a/Source/cmExecuteProcessCommand.cxx
+++ b/Source/cmExecuteProcessCommand.cxx
@@ -7,6 +7,7 @@
#include <sstream>
#include <stdio.h>
+#include "cmAlgorithms.h"
#include "cmMakefile.h"
#include "cmProcessOutput.h"
#include "cmSystemTools.h"
@@ -46,6 +47,7 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args,
std::string output_variable;
std::string error_variable;
std::string result_variable;
+ std::string results_variable;
std::string working_directory;
cmProcessOutput::Encoding encoding = cmProcessOutput::None;
for (size_t i = 0; i < args.size(); ++i) {
@@ -77,6 +79,14 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args,
this->SetError(" called with no value for RESULT_VARIABLE.");
return false;
}
+ } else if (args[i] == "RESULTS_VARIABLE") {
+ doing_command = false;
+ if (++i < args.size()) {
+ results_variable = args[i];
+ } else {
+ this->SetError(" called with no value for RESULTS_VARIABLE.");
+ return false;
+ }
} else if (args[i] == "WORKING_DIRECTORY") {
doing_command = false;
if (++i < args.size()) {
@@ -287,7 +297,7 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args,
switch (cmsysProcess_GetState(cp)) {
case cmsysProcess_State_Exited: {
int v = cmsysProcess_GetExitValue(cp);
- char buf[100];
+ char buf[16];
sprintf(buf, "%d", v);
this->Makefile->AddDefinition(result_variable, buf);
} break;
@@ -305,6 +315,47 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args,
break;
}
}
+ // Store the result of running the processes.
+ if (!results_variable.empty()) {
+ switch (cmsysProcess_GetState(cp)) {
+ case cmsysProcess_State_Exited: {
+ std::vector<std::string> res;
+ for (size_t i = 0; i < cmds.size(); ++i) {
+ switch (cmsysProcess_GetStateByIndex(cp, static_cast<int>(i))) {
+ case kwsysProcess_StateByIndex_Exited: {
+ int exitCode =
+ cmsysProcess_GetExitValueByIndex(cp, static_cast<int>(i));
+ char buf[16];
+ sprintf(buf, "%d", exitCode);
+ res.push_back(buf);
+ } break;
+ case kwsysProcess_StateByIndex_Exception:
+ res.push_back(cmsysProcess_GetExceptionStringByIndex(
+ cp, static_cast<int>(i)));
+ break;
+ case kwsysProcess_StateByIndex_Error:
+ default:
+ res.push_back("Error getting the child return code");
+ break;
+ }
+ }
+ this->Makefile->AddDefinition(results_variable,
+ cmJoin(res, ";").c_str());
+ } break;
+ case cmsysProcess_State_Exception:
+ this->Makefile->AddDefinition(results_variable,
+ cmsysProcess_GetExceptionString(cp));
+ break;
+ case cmsysProcess_State_Error:
+ this->Makefile->AddDefinition(results_variable,
+ cmsysProcess_GetErrorString(cp));
+ break;
+ case cmsysProcess_State_Expired:
+ this->Makefile->AddDefinition(results_variable,
+ "Process terminated due to timeout");
+ break;
+ }
+ }
// Delete the process instance.
cmsysProcess_Delete(cp);
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index 79f487d..91e0cdd 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -181,8 +181,10 @@ add_RunCMake_test(add_custom_target)
add_RunCMake_test(add_dependencies)
add_RunCMake_test(add_subdirectory)
add_RunCMake_test(build_command)
+add_executable(exit_code exit_code.c)
+set(execute_process_ARGS -DEXIT_CODE_EXE=$<TARGET_FILE:exit_code>)
if(NOT CMake_TEST_EXTERNAL_CMAKE)
- set(execute_process_ARGS -DTEST_ENCODING_EXE=$<TARGET_FILE:testEncoding>)
+ list(APPEND execute_process_ARGS -DTEST_ENCODING_EXE=$<TARGET_FILE:testEncoding>)
endif()
add_RunCMake_test(execute_process)
add_RunCMake_test(export)
diff --git a/Tests/RunCMake/execute_process/ExitValues-stdout.txt b/Tests/RunCMake/execute_process/ExitValues-stdout.txt
new file mode 100644
index 0000000..3bcaf46
--- /dev/null
+++ b/Tests/RunCMake/execute_process/ExitValues-stdout.txt
@@ -0,0 +1,14 @@
+^-- 1 - 1 RESULT_VARIABLE: 0
+-- 1 - 2 RESULT_VARIABLE: [^0].*
+-- 2 - 1 RESULT_VARIABLE: 0
+-- 2 - 1 RESULTS_VARIABLE: 0
+-- 2 - 2 RESULT_VARIABLE: [^0].*
+-- 2 - 2 RESULTS_VARIABLE: [^0].*
+-- 3 - 1 RESULTS_VARIABLE: 0
+-- 3 - 2 RESULTS_VARIABLE: [^0].*
+-- 4 - 1 RESULT_VARIABLE: 0
+-- 4 - 1 RESULTS_VARIABLE: [^0].*;0;[^0].*;0;[^0].*;0
+-- 4 - 1 RESULTS_VARIABLE_LENGTH: 6
+-- 5 - 1 RESULT_VARIABLE: [^0].*
+-- 5 - 1 RESULTS_VARIABLE: 0;0;[^0].*
+-- 5 - 1 RESULTS_VARIABLE_LENGTH: 3$
diff --git a/Tests/RunCMake/execute_process/ExitValues.cmake b/Tests/RunCMake/execute_process/ExitValues.cmake
new file mode 100644
index 0000000..d80a57b
--- /dev/null
+++ b/Tests/RunCMake/execute_process/ExitValues.cmake
@@ -0,0 +1,120 @@
+#1st TEST RESULT_VARIABLE ONLY
+execute_process(COMMAND ${EXIT_CODE_EXE} "zero_exit"
+ RESULT_VARIABLE r0
+ )
+message(STATUS " 1 - 1 RESULT_VARIABLE: ${r0}")
+if(NOT r0 EQUAL 0)
+ message(FATAL_ERROR "zero exit code expected")
+endif()
+execute_process(COMMAND ${EXIT_CODE_EXE} "non_zero_exit"
+ RESULT_VARIABLE r01
+ ERROR_QUIET
+ )
+message(STATUS " 1 - 2 RESULT_VARIABLE: ${r01}")
+if(r01 EQUAL 0)
+ message(FATAL_ERROR "non-zero exit code expected")
+endif()
+#2nd TEST RESULT_VARIABLE and RESULTS_VARIABLE
+execute_process(COMMAND ${EXIT_CODE_EXE} "zero_exit"
+ RESULT_VARIABLE r1
+ RESULTS_VARIABLE r1s
+ )
+message(STATUS " 2 - 1 RESULT_VARIABLE: ${r1}")
+message(STATUS " 2 - 1 RESULTS_VARIABLE: ${r1s}")
+if(NOT r1 EQUAL 0 OR NOT r1s EQUAL 0)
+ message(FATAL_ERROR "zero exit code expected")
+endif()
+execute_process(COMMAND ${EXIT_CODE_EXE} "non_zero_exit"
+ RESULT_VARIABLE r11
+ RESULTS_VARIABLE r11s
+ ERROR_QUIET
+ )
+message(STATUS " 2 - 2 RESULT_VARIABLE: ${r11}")
+message(STATUS " 2 - 2 RESULTS_VARIABLE: ${r11s}")
+if(r11 EQUAL 0 OR r11s EQUAL 0)
+ message(FATAL_ERROR "non-zero exit code expected")
+endif()
+#3rd TEST RESULTS_VARIABLE
+execute_process(COMMAND ${EXIT_CODE_EXE} "zero_exit"
+ RESULTS_VARIABLE r2s
+ )
+message(STATUS " 3 - 1 RESULTS_VARIABLE: ${r2s}")
+if(NOT r2s EQUAL 0)
+ message(FATAL_ERROR "zero exit code expected")
+endif()
+execute_process(COMMAND ${EXIT_CODE_EXE} "non_zero_exit"
+ RESULTS_VARIABLE r21s
+ ERROR_QUIET
+ )
+message(STATUS " 3 - 2 RESULTS_VARIABLE: ${r21s}")
+if(r21s EQUAL 0)
+ message(FATAL_ERROR "non-zero exit code expected")
+endif()
+#4th TEST RESULT_VARIABLE and RESULTS_VARIABLE WITH MULTICOMMAND
+execute_process(COMMAND ${EXIT_CODE_EXE} "non_zero_exit"
+ COMMAND ${EXIT_CODE_EXE} "zero_exit"
+ COMMAND ${EXIT_CODE_EXE} "non_zero_exit"
+ COMMAND ${EXIT_CODE_EXE} "zero_exit"
+ COMMAND ${EXIT_CODE_EXE} "non_zero_exit"
+ COMMAND ${EXIT_CODE_EXE} "zero_exit"
+ RESULT_VARIABLE r31
+ RESULTS_VARIABLE r31s
+ OUTPUT_QUIET
+ ERROR_QUIET
+ )
+message(STATUS " 4 - 1 RESULT_VARIABLE: ${r31}")
+message(STATUS " 4 - 1 RESULTS_VARIABLE: ${r31s}")
+if(NOT r31 EQUAL 0)
+ message(FATAL_ERROR "zero exit code expected for last command")
+endif()
+list(LENGTH r31s r31sLen)
+message(STATUS " 4 - 1 RESULTS_VARIABLE_LENGTH: ${r31sLen}")
+if(NOT r31sLen EQUAL 6)
+ message(FATAL_ERROR "length of RESULTS_VARIABLE is not as expected")
+else()
+ foreach(loop_var RANGE 5)
+ list(GET r31s ${loop_var} rsLocal)
+ math(EXPR isOdd "${loop_var} % 2")
+ if(isOdd)
+ if(NOT rsLocal EQUAL 0)
+ message(FATAL_ERROR "zero exit code expected")
+ endif()
+ else()
+ if(rsLocal EQUAL 0)
+ message(FATAL_ERROR "non-zero exit code expected")
+ endif()
+ endif()
+ endforeach()
+endif()
+#5th TEST RESULT_VARIABLE and RESULTS_VARIABLE WITH MULTICOMMAND
+execute_process(COMMAND ${EXIT_CODE_EXE} "zero_exit"
+ COMMAND ${EXIT_CODE_EXE} "zero_exit"
+ COMMAND ${EXIT_CODE_EXE} "non_zero_exit"
+ RESULT_VARIABLE r41
+ RESULTS_VARIABLE r41s
+ OUTPUT_QUIET
+ ERROR_QUIET
+ )
+message(STATUS " 5 - 1 RESULT_VARIABLE: ${r41}")
+message(STATUS " 5 - 1 RESULTS_VARIABLE: ${r41s}")
+if(r41 EQUAL 0)
+ message(FATAL_ERROR "non-zero exit code expected for last command")
+endif()
+list(LENGTH r41s r41sLen)
+message(STATUS " 5 - 1 RESULTS_VARIABLE_LENGTH: ${r41sLen}")
+if(NOT r31sLen EQUAL 6)
+ message(FATAL_ERROR "length of RESULTS_VARIABLE is not as expected")
+else()
+ list(GET r41s 0 rsLocal)
+ if(NOT rsLocal EQUAL 0)
+ message(FATAL_ERROR "zero exit code expected")
+ endif()
+ list(GET r41s 1 rsLocal)
+ if(NOT rsLocal EQUAL 0)
+ message(FATAL_ERROR "zero exit code expected")
+ endif()
+ list(GET r41s 2 rsLocal)
+ if(rsLocal EQUAL 0)
+ message(FATAL_ERROR "non-zero exit code expected")
+ endif()
+endif()
diff --git a/Tests/RunCMake/execute_process/RunCMakeTest.cmake b/Tests/RunCMake/execute_process/RunCMakeTest.cmake
index 62e18c6..83589bb 100644
--- a/Tests/RunCMake/execute_process/RunCMakeTest.cmake
+++ b/Tests/RunCMake/execute_process/RunCMakeTest.cmake
@@ -11,3 +11,7 @@ run_cmake(EncodingMissing)
if(TEST_ENCODING_EXE)
run_cmake_command(EncodingUTF8 ${CMAKE_COMMAND} -DTEST_ENCODING=UTF8 -DTEST_ENCODING_EXE=${TEST_ENCODING_EXE} -P ${RunCMake_SOURCE_DIR}/Encoding.cmake)
endif()
+
+if(EXIT_CODE_EXE)
+ run_cmake_command(ExitValues ${CMAKE_COMMAND} -DEXIT_CODE_EXE=${EXIT_CODE_EXE} -P ${RunCMake_SOURCE_DIR}/ExitValues.cmake)
+endif()
diff --git a/Tests/RunCMake/exit_code.c b/Tests/RunCMake/exit_code.c
new file mode 100644
index 0000000..3eba019
--- /dev/null
+++ b/Tests/RunCMake/exit_code.c
@@ -0,0 +1,30 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// Usage:
+//
+// /path/to/program arg1 [arg2 [...]]
+//
+// Return EXIT_SUCCESS if 'zero_exit'
+// string was found in <arg1>.
+// Return EXIT_FAILURE if 'non_zero_exit'
+// string was found in <arg1>.
+
+int main(int argc, const char* argv[])
+{
+ const char* substring_failure = "non_zero_exit";
+ const char* substring_success = "zero_exit";
+ const char* str = argv[1];
+ if (argc < 2) {
+ return EXIT_FAILURE;
+ }
+ if (strcmp(str, substring_success) == 0) {
+ return EXIT_SUCCESS;
+ } else if (strcmp(str, substring_failure) == 0) {
+ return EXIT_FAILURE;
+ }
+ fprintf(stderr, "Failed to find string '%s' in '%s'\n", substring_success,
+ str);
+ return EXIT_FAILURE;
+}