mirror of
https://github.com/Kitware/CMake.git
synced 2026-04-28 01:49:23 -05: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>]
|
[WORKING_DIRECTORY <directory>]
|
||||||
[TIMEOUT <seconds>]
|
[TIMEOUT <seconds>]
|
||||||
[RESULT_VARIABLE <variable>]
|
[RESULT_VARIABLE <variable>]
|
||||||
|
[RESULTS_VARIABLE <variable>]
|
||||||
[OUTPUT_VARIABLE <variable>]
|
[OUTPUT_VARIABLE <variable>]
|
||||||
[ERROR_VARIABLE <variable>]
|
[ERROR_VARIABLE <variable>]
|
||||||
[INPUT_FILE <file>]
|
[INPUT_FILE <file>]
|
||||||
@@ -49,10 +50,16 @@ Options:
|
|||||||
specified number of seconds (fractions are allowed).
|
specified number of seconds (fractions are allowed).
|
||||||
|
|
||||||
``RESULT_VARIABLE``
|
``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
|
This will be an integer return code from the last child or a string
|
||||||
describing an error condition.
|
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``
|
``OUTPUT_VARIABLE``, ``ERROR_VARIABLE``
|
||||||
The variable named will be set with the contents of the standard output
|
The variable named will be set with the contents of the standard output
|
||||||
and standard error pipes, respectively. If the same variable is named
|
and standard error pipes, respectively. If the same variable is named
|
||||||
|
|||||||
@@ -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 <sstream>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "cmAlgorithms.h"
|
||||||
#include "cmMakefile.h"
|
#include "cmMakefile.h"
|
||||||
#include "cmProcessOutput.h"
|
#include "cmProcessOutput.h"
|
||||||
#include "cmSystemTools.h"
|
#include "cmSystemTools.h"
|
||||||
@@ -46,6 +47,7 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args,
|
|||||||
std::string output_variable;
|
std::string output_variable;
|
||||||
std::string error_variable;
|
std::string error_variable;
|
||||||
std::string result_variable;
|
std::string result_variable;
|
||||||
|
std::string results_variable;
|
||||||
std::string working_directory;
|
std::string working_directory;
|
||||||
cmProcessOutput::Encoding encoding = cmProcessOutput::None;
|
cmProcessOutput::Encoding encoding = cmProcessOutput::None;
|
||||||
for (size_t i = 0; i < args.size(); ++i) {
|
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.");
|
this->SetError(" called with no value for RESULT_VARIABLE.");
|
||||||
return false;
|
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") {
|
} else if (args[i] == "WORKING_DIRECTORY") {
|
||||||
doing_command = false;
|
doing_command = false;
|
||||||
if (++i < args.size()) {
|
if (++i < args.size()) {
|
||||||
@@ -287,7 +297,7 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args,
|
|||||||
switch (cmsysProcess_GetState(cp)) {
|
switch (cmsysProcess_GetState(cp)) {
|
||||||
case cmsysProcess_State_Exited: {
|
case cmsysProcess_State_Exited: {
|
||||||
int v = cmsysProcess_GetExitValue(cp);
|
int v = cmsysProcess_GetExitValue(cp);
|
||||||
char buf[100];
|
char buf[16];
|
||||||
sprintf(buf, "%d", v);
|
sprintf(buf, "%d", v);
|
||||||
this->Makefile->AddDefinition(result_variable, buf);
|
this->Makefile->AddDefinition(result_variable, buf);
|
||||||
} break;
|
} break;
|
||||||
@@ -305,6 +315,47 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args,
|
|||||||
break;
|
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.
|
// Delete the process instance.
|
||||||
cmsysProcess_Delete(cp);
|
cmsysProcess_Delete(cp);
|
||||||
|
|||||||
@@ -181,8 +181,10 @@ add_RunCMake_test(add_custom_target)
|
|||||||
add_RunCMake_test(add_dependencies)
|
add_RunCMake_test(add_dependencies)
|
||||||
add_RunCMake_test(add_subdirectory)
|
add_RunCMake_test(add_subdirectory)
|
||||||
add_RunCMake_test(build_command)
|
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)
|
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()
|
endif()
|
||||||
add_RunCMake_test(execute_process)
|
add_RunCMake_test(execute_process)
|
||||||
add_RunCMake_test(export)
|
add_RunCMake_test(export)
|
||||||
|
|||||||
@@ -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$
|
||||||
@@ -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)
|
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)
|
run_cmake_command(EncodingUTF8 ${CMAKE_COMMAND} -DTEST_ENCODING=UTF8 -DTEST_ENCODING_EXE=${TEST_ENCODING_EXE} -P ${RunCMake_SOURCE_DIR}/Encoding.cmake)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(EXIT_CODE_EXE)
|
||||||
|
run_cmake_command(ExitValues ${CMAKE_COMMAND} -DEXIT_CODE_EXE=${EXIT_CODE_EXE} -P ${RunCMake_SOURCE_DIR}/ExitValues.cmake)
|
||||||
|
endif()
|
||||||
|
|||||||
@@ -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