mirror of
https://github.com/Kitware/CMake.git
synced 2025-12-31 10:50:16 -06:00
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:
@@ -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
|
||||
|
||||
6
Help/release/dev/execute_process-pipeline-results.rst
Normal file
6
Help/release/dev/execute_process-pipeline-results.rst
Normal 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.
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
14
Tests/RunCMake/execute_process/ExitValues-stdout.txt
Normal file
14
Tests/RunCMake/execute_process/ExitValues-stdout.txt
Normal 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$
|
||||
120
Tests/RunCMake/execute_process/ExitValues.cmake
Normal file
120
Tests/RunCMake/execute_process/ExitValues.cmake
Normal 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()
|
||||
@@ -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()
|
||||
|
||||
30
Tests/RunCMake/exit_code.c
Normal file
30
Tests/RunCMake/exit_code.c
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user