Skip to content
Snippets Groups Projects
Commit bd25b881 authored by Timothee Chabat's avatar Timothee Chabat Committed by Kitware Robot
Browse files

Merge topic 'addCommandLineProcessB'


43512b79 Add command line process vtk object

Acked-by: default avatarKitware Robot <kwrobot@kitware.com>
Acked-by: default avatarCharles Gueunet <charles.gueunet@kitware.com>
Acked-by: default avatarFrancois Mazen <francois.mazen@kitware.com>
Merge-request: !8185
parents 56289572 43512b79
Branches
No related tags found
No related merge requests found
......@@ -3,6 +3,7 @@ vtk_module_find_package(
set(classes
vtkClientSocket
vtkCommandLineProcess
vtkDirectory
vtkServerSocket
vtkSocket
......
......@@ -3,4 +3,13 @@ vtk_add_test_cxx(vtkCommonSystemCxxTests tests
TestDirectory.cxx
otherTimerLog.cxx
)
if(NOT WIN32)
vtk_add_test_cxx(vtkCommonSystemCxxTests tests
NO_DATA NO_VALID NO_OUTPUT
# command line process is not as trivial to test on Windows as on Unix systems
TestCommandLineProcess.cxx
)
endif()
vtk_test_cxx_executable(vtkCommonSystemCxxTests tests)
#include "vtkCommandLineProcess.h"
#include "vtkNew.h"
#include "vtkLogger.h"
#include <iostream>
#include <string>
int TestCommandLineProcess(int, char*[])
{
vtkNew<vtkCommandLineProcess> process;
process->SetCommand("echo \"Hello World\"");
process->Execute();
std::string out = process->GetStdOut();
std::string err = process->GetStdErr();
int code = process->GetReturnValue();
// ---
if (code != 0)
{
std::cerr << "ERROR: command did not succeed" << std::endl;
return EXIT_FAILURE;
}
if (out != "Hello World")
{
std::cerr << "ERROR: wrong command output" << std::endl;
return EXIT_FAILURE;
}
if (!err.empty())
{
std::cerr << "ERROR: there is output in the error stream" << std::endl;
return EXIT_FAILURE;
}
// ---
process->Execute();
if (process->GetStdOut() != out || !std::string(process->GetStdErr()).empty())
{
std::cerr << "ERROR: ran twice the same process, expected the same result" << std::endl;
return EXIT_FAILURE;
}
// ---
process->SetCommand("abcdefghijklmnopqrstuvw");
// Disable gloabal logger for this test as we don't want the error returned by the filter
// to mess with our test
int warning = vtkObject::GetGlobalWarningDisplay();
vtkObject::SetGlobalWarningDisplay(0);
process->Execute();
vtkObject::SetGlobalWarningDisplay(warning);
code = process->GetReturnValue();
if (code == 0)
{
std::cerr << "ERROR: command did not return a failure but was supposed to." << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
/*=========================================================================
Program: Visualization Toolkit
Module: vtkCommandLineProcess.cxx
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#include "vtkCommandLineProcess.h"
#include "vtkObjectFactory.h"
#include <algorithm>
#include <regex>
#include <sstream>
#include <string>
#include <vector>
namespace details
{
// trim strings. This should go in vtksys at some point
static inline void ltrim(std::string& s)
{
s.erase(s.begin(),
std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); }));
}
static inline void rtrim(std::string& s)
{
s.erase(
std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(),
s.end());
}
static inline void trim(std::string& s)
{
ltrim(s);
rtrim(s);
}
std::vector<std::string> ParseCommand(std::string command)
{
details::trim(command);
std::vector<std::string> res;
// Extract program name using regex
// regex recognize pattern such as `exec`, `./exec`, `/d1/d_2/exec`, `/d1/d\ 2/exec`, or `"/d1/d
// 2/exec"`. There is only one capturing group and that is the executable without the "
// characters, when this technique is used to escape the spaces.
std::regex programRegex =
std::regex(R"~(^\.?(?:\/?(?:\w+(?:\\ )?)+)+|^"(\.?(?:\/?(?:\w+ ?)+)+)")~");
std::smatch match;
if (std::regex_search(command, match, programRegex))
{
// If escape group for '"/../..."' matched
if (match[1].matched)
{
res.emplace_back(match[1].str());
}
else
{
res.emplace_back(match[0].str());
}
command = command.substr(match[0].length(), command.length());
}
// Extract arguments. Split by space except if surrounded by the '"' character.
std::regex argRegex = std::regex(R"~([^\s"]+|"([^"]*)")~");
while (std::regex_search(command, match, argRegex))
{
// If escape group for argumeetns with '"' has matched
if (match[1].matched)
{
res.emplace_back(match[1].str());
}
else
{
res.emplace_back(match[0].str());
}
command = command.substr(match[0].length(), command.length());
}
return res;
}
}
//------------------------------------------------------------------------------
vtkStandardNewMacro(vtkCommandLineProcess);
//------------------------------------------------------------------------------
vtkCommandLineProcess::vtkCommandLineProcess()
{
this->SetStdErr("");
this->SetStdOut("");
}
//------------------------------------------------------------------------------
vtkCommandLineProcess::~vtkCommandLineProcess()
{
this->SetStdErr(nullptr);
this->SetStdOut(nullptr);
}
//------------------------------------------------------------------------------
void vtkCommandLineProcess::Execute()
{
if (this->Command && strlen(this->Command))
{
std::vector<std::string> parsed = details::ParseCommand(this->Command);
// get a vector of C string for vtksys
const auto size = parsed.size();
std::vector<const char*> stringViewC(size + 1);
for (std::size_t i = 0; i < size; ++i)
{
stringViewC[i] = parsed[i].c_str();
}
stringViewC[size] = nullptr;
// Configure and launch process
vtksysProcess* process = vtksysProcess_New();
vtksysProcess_SetCommand(process, &stringViewC[0]);
vtksysProcess_SetPipeShared(process, vtksysProcess_Pipe_STDOUT, 0);
vtksysProcess_SetPipeShared(process, vtksysProcess_Pipe_STDERR, 0);
vtksysProcess_SetTimeout(process, this->Timeout);
vtksysProcess_Execute(process);
// Get ouput streams
int pipe;
std::string out;
std::string err;
// While loop needed because there is a limit to the buffer size of
// the vtksysProcess streams. If output is too big we have to append.
do
{
char* cp;
int length = 0;
pipe = vtksysProcess_WaitForData(process, &cp, &length, nullptr);
switch (pipe)
{
case vtksysProcess_Pipe_STDOUT:
out.append(std::string(cp, length));
break;
case vtksysProcess_Pipe_STDERR:
err.append(std::string(cp, length));
break;
}
} while (pipe != vtksysProcess_Pipe_None);
// Exit properly
this->ReturnValue = this->ExitProcess(process);
vtksysProcess_Delete(process);
// Trim last whitespaces
if (this->RightTrimResult)
{
details::rtrim(out);
details::rtrim(err);
}
this->SetStdOut(out.c_str());
this->SetStdErr(err.c_str());
}
}
//------------------------------------------------------------------------------
int vtkCommandLineProcess::ExitProcess(vtksysProcess* process)
{
vtksysProcess_WaitForExit(process, &this->Timeout);
int state = vtksysProcess_GetState(process);
int code = -1;
switch (state)
{
case vtksysProcess_State_Error:
vtkErrorMacro("Error administrating the child process");
break;
case vtksysProcess_State_Exception:
vtkErrorMacro(
"Child process exited abnormally: " << vtksysProcess_GetExceptionString(process));
break;
case vtksysProcess_State_Expired:
vtkErrorMacro("Child process's timeout expired");
break;
case vtksysProcess_State_Killed:
vtkErrorMacro("Child process terminated by Kill method.");
break;
case vtksysProcess_State_Exited:
code = vtksysProcess_GetExitValue(process);
vtkDebugMacro("Childs process returned with value: " << code);
if (code)
{
vtkWarningMacro("Child process exited with error code: " << code);
}
break;
default:
break;
}
return code;
}
//------------------------------------------------------------------------------
void vtkCommandLineProcess::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
os << indent << "Command: " << this->GetCommand() << std::endl;
os << indent << "Timeout: " << this->GetTimeout() << std::endl;
os << indent << "RightTrimResult: " << this->GetRightTrimResult() << std::endl;
}
/*=========================================================================
Program: Visualization Toolkit
Module: vtkCommandLineProcess.h
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#ifndef vtkCommandLineProcess_h
#define vtkCommandLineProcess_h
#include "vtkCommonSystemModule.h" // For export macro
#include "vtkObject.h"
#include "vtksys/Process.h" // For class vtksysProcess
#include <string> // fot class std::string
/**
* @class vtkCommandLineProcess
* @brief Launch a process on the current machine and get its output
*
* Launch a process on the current machine and get its standard output and
* standard error output.
*/
class VTKCOMMONSYSTEM_EXPORT vtkCommandLineProcess : public vtkObject
{
public:
static vtkCommandLineProcess* New();
vtkTypeMacro(vtkCommandLineProcess, vtkObject);
void PrintSelf(ostream& os, vtkIndent indent) override;
vtkCommandLineProcess();
virtual ~vtkCommandLineProcess();
/**
* Execute the command currently set if any.
* This will update the StdOut and StdErr properties.
*/
void Execute();
//@{
/**
* Set/Get command timeout in seconds. A non-positive (<= 0) value will
* disable the timeout.
*
* Default is 5
*/
vtkSetMacro(Timeout, double);
vtkGetMacro(Timeout, double);
//@}
//@{
/**
* Set/Get if we trim the ending whitespaces of the output.
*
* Default is true.
*/
vtkSetMacro(RightTrimResult, bool);
vtkGetMacro(RightTrimResult, bool);
vtkBooleanMacro(RightTrimResult, bool);
//@}
//@{
/**
* Set/Get command to execute. An empty command will do nothing.
*/
vtkGetStringMacro(Command);
vtkSetStringMacro(Command);
//@}
//@{
/**
* Get output of the previously run command.
*/
vtkGetStringMacro(StdOut);
vtkGetStringMacro(StdErr);
//@}
/**
* Get return value of last command. If no command has been
* executed or if the command has failed in some way value is != 0,
* else return 0.
*/
vtkGetMacro(ReturnValue, int);
protected:
vtkSetStringMacro(StdOut);
vtkSetStringMacro(StdErr);
int ExitProcess(vtksysProcess* process);
private:
vtkCommandLineProcess(const vtkCommandLineProcess&) = delete;
void operator=(const vtkCommandLineProcess&) = delete;
bool RightTrimResult = true;
double Timeout = 5;
char* Command = nullptr;
int ReturnValue = -1;
char* StdOut = nullptr;
char* StdErr = nullptr;
};
#endif // vtkCommandLineProcess_h
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment