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.
This commit is contained in:
Adam Weisi
2017-04-13 19:10:04 +02:00
committed by Brad King
parent c095e90f3a
commit d6051ca39e
8 changed files with 237 additions and 3 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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