wpiutil: uv::Process: Revamp args/options approach (#1434)

This commit is contained in:
Peter Johnson
2018-11-19 19:54:54 -08:00
committed by GitHub
parent e4aa45f34b
commit df347e3d80
2 changed files with 292 additions and 160 deletions

View File

@@ -13,6 +13,7 @@
#include <memory>
#include <string>
#include "wpi/ArrayRef.h"
#include "wpi/Signal.h"
#include "wpi/SmallVector.h"
#include "wpi/Twine.h"
@@ -24,114 +25,6 @@ namespace uv {
class Loop;
class Pipe;
/**
* Process options.
*/
class ProcessOptions final {
friend class Process;
public:
/**
* Set environment variables for the subprocess. If not set or set to
* nullptr, the parent's environment is used.
* @param env environment variables
*/
ProcessOptions& SetEnv(char** env) {
m_env = env;
return *this;
}
/**
* Set the current working directory for the subprocess.
* @param cwd current working directory
*/
ProcessOptions& SetCwd(const Twine& cwd) {
m_cwd = cwd.str();
return *this;
}
/**
* Set the child process' user id.
* @param uid user id
*/
ProcessOptions& SetUid(uv_uid_t uid) noexcept {
m_uid = uid;
m_flags |= UV_PROCESS_SETUID;
return *this;
}
/**
* Set the child process' group id.
* @param gid group id
*/
ProcessOptions& SetGid(uv_gid_t gid) noexcept {
m_gid = gid;
m_flags |= UV_PROCESS_SETGID;
return *this;
}
/**
* Set flags.
* @param flags Bitmask values from uv_process_flags.
*/
ProcessOptions& SetFlags(unsigned int flags) noexcept {
m_flags |= flags;
return *this;
}
/**
* Clear flags.
* @param flags Bitmask values from uv_process_flags.
*/
ProcessOptions& ClearFlags(unsigned int flags) noexcept {
m_flags &= ~flags;
return *this;
}
/**
* Explicitly ignore a stdio.
* @param index stdio index
*/
ProcessOptions& StdioIgnore(size_t index);
/**
* Inherit a file descriptor from the parent process.
* @param index stdio index
* @param fd parent file descriptor
*/
ProcessOptions& StdioInherit(size_t index, int fd);
/**
* Inherit a pipe from the parent process.
* @param index stdio index
* @param pipe pipe
*/
ProcessOptions& StdioInherit(size_t index, Pipe& pipe);
/**
* Create a pipe between the child and the parent.
* @param index stdio index
* @param pipe pipe
* @param flags Some combination of UV_READABLE_PIPE, UV_WRITABLE_PIPE, and
* UV_OVERLAPPED_PIPE (Windows only, ignored on Unix).
*/
ProcessOptions& StdioCreatePipe(size_t index, Pipe& pipe, unsigned int flags);
private:
char** m_env = nullptr;
std::string m_cwd;
unsigned int m_flags = 0;
struct StdioContainer : public uv_stdio_container_t {
StdioContainer() {
flags = UV_IGNORE;
data.fd = 0;
}
};
SmallVector<StdioContainer, 4> m_stdio;
uv_uid_t m_uid = 0;
uv_gid_t m_gid = 0;
};
/**
* Process handle.
* Process handles will spawn a new process and allow the user to control it
@@ -144,6 +37,185 @@ class Process final : public HandleImpl<Process, uv_process_t> {
explicit Process(const private_init&) {}
~Process() noexcept override = default;
/**
* Structure for Spawn() option temporaries. This is a reference type
* similar to StringRef, so if this value is stored outside of a temporary,
* be careful about overwriting what it points to.
*/
struct Option {
enum Type {
kNone,
kArg,
kEnv,
kCwd,
kUid,
kGid,
kSetFlags,
kClearFlags,
kStdioIgnore,
kStdioInheritFd,
kStdioInheritPipe,
kStdioCreatePipe
};
Option() : m_type(kNone) {}
/*implicit*/ Option(const char* arg) { // NOLINT(runtime/explicit)
m_data.str = arg;
}
/*implicit*/ Option(const std::string& arg) { // NOLINT(runtime/explicit)
m_data.str = arg.data();
}
/*implicit*/ Option(StringRef arg) // NOLINT(runtime/explicit)
: m_strData(arg) {
m_data.str = m_strData.c_str();
}
/*implicit*/ Option(
const SmallVectorImpl<char>& arg) // NOLINT(runtime/explicit)
: m_strData(arg.data(), arg.size()) {
m_data.str = m_strData.c_str();
}
/*implicit*/ Option(const Twine& arg) // NOLINT(runtime/explicit)
: m_strData(arg.str()) {
m_data.str = m_strData.c_str();
}
explicit Option(Type type) : m_type(type) {}
Type m_type = kArg;
std::string m_strData;
union {
const char* str;
uv_uid_t uid;
uv_gid_t gid;
unsigned int flags;
struct {
size_t index;
union {
int fd;
Pipe* pipe;
};
unsigned int flags;
} stdio;
} m_data;
};
/**
* Set environment variable for the subprocess. If not set, the parent's
* environment is used.
* @param env environment variable
*/
static Option Env(const Twine& env) {
Option o(Option::kEnv);
o.m_strData = env.str();
o.m_data.str = o.m_strData.c_str();
return o;
}
/**
* Set the current working directory for the subprocess.
* @param cwd current working directory
*/
static Option Cwd(const Twine& cwd) {
Option o(Option::kCwd);
o.m_strData = cwd.str();
o.m_data.str = o.m_strData.c_str();
return o;
}
/**
* Set the child process' user id.
* @param uid user id
*/
static Option Uid(uv_uid_t uid) {
Option o(Option::kUid);
o.m_data.uid = uid;
return o;
}
/**
* Set the child process' group id.
* @param gid group id
*/
static Option Gid(uv_gid_t gid) {
Option o(Option::kGid);
o.m_data.gid = gid;
return o;
}
/**
* Set spawn flags.
* @param flags Bitmask values from uv_process_flags.
*/
static Option SetFlags(unsigned int flags) {
Option o(Option::kSetFlags);
o.m_data.flags = flags;
return o;
}
/**
* Clear spawn flags.
* @param flags Bitmask values from uv_process_flags.
*/
static Option ClearFlags(unsigned int flags) {
Option o(Option::kClearFlags);
o.m_data.flags = flags;
return o;
}
/**
* Explicitly ignore a stdio.
* @param index stdio index
*/
static Option StdioIgnore(size_t index) {
Option o(Option::kStdioIgnore);
o.m_data.stdio.index = index;
return o;
}
/**
* Inherit a file descriptor from the parent process.
* @param index stdio index
* @param fd parent file descriptor
*/
static Option StdioInherit(size_t index, int fd) {
Option o(Option::kStdioInheritFd);
o.m_data.stdio.index = index;
o.m_data.stdio.fd = fd;
return o;
}
/**
* Inherit a pipe from the parent process.
* @param index stdio index
* @param pipe pipe
*/
static Option StdioInherit(size_t index, Pipe& pipe) {
Option o(Option::kStdioInheritPipe);
o.m_data.stdio.index = index;
o.m_data.stdio.pipe = &pipe;
return o;
}
/**
* Create a pipe between the child and the parent.
* @param index stdio index
* @param pipe pipe
* @param flags Some combination of UV_READABLE_PIPE, UV_WRITABLE_PIPE, and
* UV_OVERLAPPED_PIPE (Windows only, ignored on Unix).
*/
static Option StdioCreatePipe(size_t index, Pipe& pipe, unsigned int flags) {
Option o(Option::kStdioCreatePipe);
o.m_data.stdio.index = index;
o.m_data.stdio.pipe = &pipe;
o.m_data.stdio.flags = flags;
return o;
}
/**
* Disables inheritance for file descriptors / handles that this process
* inherited from its parent. The effect is that child processes spawned
@@ -166,12 +238,16 @@ class Process final : public HandleImpl<Process, uv_process_t> {
*
* @param loop Loop object where this handle runs.
* @param file Path pointing to the program to be executed
* @param args Command line arguments
* @param options Process options
*/
static std::shared_ptr<Process> Spawn(
Loop& loop, const Twine& file, char** args,
const ProcessOptions& options = ProcessOptions{});
static std::shared_ptr<Process> SpawnArray(Loop& loop, const Twine& file,
ArrayRef<Option> options);
template <typename... Args>
static std::shared_ptr<Process> Spawn(Loop& loop, const Twine& file,
const Args&... options) {
return SpawnArray(loop, file, {options...});
}
/**
* Starts a process. If the process is not successfully spawned, an error
@@ -184,13 +260,19 @@ class Process final : public HandleImpl<Process, uv_process_t> {
*
* @param loop Loop object where this handle runs.
* @param file Path pointing to the program to be executed
* @param args Command line arguments
* @param options Process options
*/
static std::shared_ptr<Process> Spawn(
const std::shared_ptr<Loop>& loop, const Twine& file, char** args,
const ProcessOptions& options = ProcessOptions{}) {
return Spawn(*loop, file, args, options);
static std::shared_ptr<Process> SpawnArray(const std::shared_ptr<Loop>& loop,
const Twine& file,
ArrayRef<Option> options) {
return SpawnArray(*loop, file, options);
}
template <typename... Args>
static std::shared_ptr<Process> Spawn(const std::shared_ptr<Loop>& loop,
const Twine& file,
const Args&... options) {
return SpawnArray(*loop, file, {options...});
}
/**