feat(library/process): support working directory on windows

This commit is contained in:
Gabriel Ebner 2017-04-27 17:22:25 +02:00 committed by Leonardo de Moura
parent c14221f6d4
commit 44bfceb6a6
3 changed files with 101 additions and 75 deletions

View file

@ -59,17 +59,43 @@ process & process::set_cwd(std::string const &cwd) {
#if defined(LEAN_WINDOWS) && !defined(LEAN_CYGWIN)
HANDLE to_win_handle(FILE * file) {
intptr_t handle = _get_osfhandle(fileno(file));
return reinterpret_cast<HANDLE>(handle);
}
struct windows_child : public child {
handle_ref m_stdin;
handle_ref m_stdout;
handle_ref m_stderr;
HANDLE m_process;
FILE * from_win_handle(HANDLE handle, char const * mode) {
windows_child(HANDLE p, handle_ref hstdin, handle_ref hstdout, handle_ref hstderr) :
m_stdin(hstdin), m_stdout(hstdout), m_stderr(hstderr), m_process(p) {}
~windows_child() {
CloseHandle(m_process);
}
handle_ref get_stdin() override { return m_stdin; }
handle_ref get_stdout() override { return m_stdout; }
handle_ref get_stderr() override { return m_stderr; }
unsigned wait() override {
DWORD exit_code;
WaitForSingleObject(m_process, INFINITE);
GetExitCodeProcess(m_process, &exit_code);
return static_cast<unsigned>(exit_code);
}
};
// static HANDLE to_win_handle(FILE * file) {
// intptr_t handle = _get_osfhandle(fileno(file));
// return reinterpret_cast<HANDLE>(handle);
// }
static FILE * from_win_handle(HANDLE handle, char const * mode) {
int fd = _open_osfhandle(reinterpret_cast<intptr_t>(handle), _O_APPEND);
return fdopen(fd, mode);
}
void create_child_process(std::string cmd_name, HANDLE hstdin, HANDLE hstdout, HANDLE hstderr);
static HANDLE create_child_process(std::string const & cmd_name, optional<std::string> const & cwd,
HANDLE hstdin, HANDLE hstdout, HANDLE hstderr);
// TODO(@jroesch): unify this code between platforms better.
static optional<pipe> setup_stdio(SECURITY_ATTRIBUTES * saAttr, optional<stdio> cfg) {
@ -98,10 +124,8 @@ static optional<pipe> setup_stdio(SECURITY_ATTRIBUTES * saAttr, optional<stdio>
}
}
// TODO(gabriel): add support for cwd parameter
// This code is adapted from: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx
child process::spawn() {
std::shared_ptr<child> process::spawn() {
HANDLE child_stdin = stdin;
HANDLE child_stdout = stdout;
HANDLE child_stderr = stderr;
@ -162,7 +186,8 @@ child process::spawn() {
}
// Create the child process.
create_child_process(command, child_stdin, child_stdout, child_stderr);
auto proc_handle =
create_child_process(command, m_cwd, child_stdin, child_stdout, child_stderr);
if (stdin_pipe) {
CloseHandle(stdin_pipe->m_read_fd);
@ -179,15 +204,15 @@ child process::spawn() {
parent_stderr = stderr_pipe->m_read_fd;
}
return child(
0,
return std::make_shared<windows_child>(proc_handle,
std::make_shared<handle>(from_win_handle(parent_stdin, "w")),
std::make_shared<handle>(from_win_handle(parent_stdout, "r")),
std::make_shared<handle>(from_win_handle(parent_stderr, "r")));
}
// Create a child process that uses the previously created pipes for STDIN and STDOUT.
void create_child_process(std::string command, HANDLE hstdin, HANDLE hstdout, HANDLE hstderr) {
static HANDLE create_child_process(std::string const & command, optional<std::string> const & cwd,
HANDLE hstdin, HANDLE hstdout, HANDLE hstderr) {
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
BOOL bSuccess = FALSE;
@ -215,7 +240,7 @@ void create_child_process(std::string command, HANDLE hstdin, HANDLE hstdout, HA
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
cwd ? cwd->c_str() : NULL, // current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
@ -227,23 +252,19 @@ void create_child_process(std::string command, HANDLE hstdin, HANDLE hstdout, HA
// Some applications might keep these handles to monitor the status
// of the child process, for example.
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
return piProcInfo.hProcess;
}
}
void process::run() {
throw exception("process::run not supported on Windows");
}
unsigned child::wait() {
// TODO(gabriel): not implemented yet
return 0;
spawn()->wait();
}
#else
optional<pipe> setup_stdio(optional<stdio> cfg) {
static optional<pipe> setup_stdio(optional<stdio> cfg) {
/* Setup stdio based on process configuration. */
if (cfg) {
switch (*cfg) {
@ -265,7 +286,27 @@ optional<pipe> setup_stdio(optional<stdio> cfg) {
}
}
child process::spawn() {
struct unix_child : public child {
handle_ref m_stdin;
handle_ref m_stdout;
handle_ref m_stderr;
int m_pid;
unix_child(int pid, handle_ref hstdin, handle_ref hstdout, handle_ref hstderr) :
m_stdin(hstdin), m_stdout(hstdout), m_stderr(hstderr), m_pid(pid) {}
handle_ref get_stdin() override { return m_stdin; }
handle_ref get_stdout() override { return m_stdout; }
handle_ref get_stderr() override { return m_stderr; }
unsigned wait() override {
int status;
waitpid(m_pid, &status, 0);
return static_cast<unsigned>(WEXITSTATUS(status));
}
};
std::shared_ptr<child> process::spawn() {
/* Setup stdio based on process configuration. */
auto stdin_pipe = setup_stdio(m_stdin);
auto stdout_pipe = setup_stdio(m_stdout);
@ -327,20 +368,14 @@ child process::spawn() {
parent_stderr = fdopen(stderr_pipe->m_read_fd, "r");
}
return child(pid,
return std::make_shared<unix_child>(pid,
std::make_shared<handle>(parent_stdin),
std::make_shared<handle>(parent_stdout),
std::make_shared<handle>(parent_stderr));
}
void process::run() {
spawn().wait();
}
unsigned child::wait() {
int status;
waitpid(m_pid, &status, 0);
return static_cast<unsigned>(WEXITSTATUS(status));
spawn()->wait();
}
#endif

View file

@ -21,24 +21,11 @@ enum stdio {
};
struct child {
handle_ref m_stdin;
handle_ref m_stdout;
handle_ref m_stderr;
int m_pid;
child(child const & ch) :
m_stdin(ch.m_stdin),
m_stdout(ch.m_stdout),
m_stderr(ch.m_stderr),
m_pid(ch.m_pid) {}
child(int pid, handle_ref hstdin, handle_ref hstdout, handle_ref hstderr) :
m_stdin(hstdin),
m_stdout(hstdout),
m_stderr(hstderr),
m_pid(pid) {}
unsigned wait();
virtual ~child() {};
virtual handle_ref get_stdin() = 0;
virtual handle_ref get_stdout() = 0;
virtual handle_ref get_stderr() = 0;
virtual unsigned wait() = 0;
};
class process {
@ -56,7 +43,7 @@ public:
process & set_stdout(stdio cfg);
process & set_stderr(stdio cfg);
process & set_cwd(std::string const & cwd);
child spawn();
std::shared_ptr<child> spawn();
void run();
};
}

View file

@ -75,8 +75,8 @@ struct vm_handle : public vm_external {
vm_handle(handle_ref const & h):m_handle(h) {}
virtual ~vm_handle() {}
virtual void dealloc() override { this->~vm_handle(); get_vm_allocator().deallocate(sizeof(vm_handle), this); }
virtual vm_external * clone(vm_clone_fn const &) override { throw exception("handle objects cannot be cloned"); }
virtual vm_external * ts_clone(vm_clone_fn const &) override { throw exception("handle objects cannot be cloned"); }
virtual vm_external * clone(vm_clone_fn const &) override { return new vm_handle(m_handle); }
virtual vm_external * ts_clone(vm_clone_fn const &) override { lean_unreachable(); }
};
bool is_handle(vm_obj const & o) {
@ -92,6 +92,28 @@ vm_obj to_obj(handle_ref const & h) {
return mk_vm_external(new (get_vm_allocator().allocate(sizeof(vm_handle))) vm_handle(h));
}
struct vm_child : public vm_external {
std::shared_ptr<child> m_child;
vm_child(std::shared_ptr<child> const & h):m_child(h) {}
virtual ~vm_child() {}
virtual void dealloc() override { this->~vm_child(); get_vm_allocator().deallocate(sizeof(vm_child), this); }
virtual vm_external * clone(vm_clone_fn const &) override { return new vm_child(m_child); }
virtual vm_external * ts_clone(vm_clone_fn const &) override { lean_unreachable(); }
};
bool is_child(vm_obj const & o) {
return is_external(o) && dynamic_cast<vm_child*>(to_external(o));
}
std::shared_ptr<child> const & to_child(vm_obj const & o) {
lean_vm_check(dynamic_cast<vm_child*>(to_external(o)));
return static_cast<vm_child*>(to_external(o))->m_child;
}
vm_obj to_obj(std::shared_ptr<child> const & h) {
return mk_vm_external(new (get_vm_allocator().allocate(sizeof(vm_child))) vm_child(h));
}
/*
inductive io.mode
| read | write | read_write | append
@ -285,24 +307,6 @@ stdio to_stdio(vm_obj const & o) {
}
}
vm_obj to_obj(child const & ch) {
return mk_vm_constructor(0, {
mk_vm_nat(static_cast<unsigned>(ch.m_pid)),
to_obj(ch.m_stdin),
to_obj(ch.m_stdout),
to_obj(ch.m_stderr),
});
}
child to_child(vm_obj const & o) {
return child {
static_cast<int>(to_unsigned(cfield(o, 0))),
to_handle(cfield(o, 1)),
to_handle(cfield(o, 2)),
to_handle(cfield(o, 3)),
};
}
/*
structure spawn_args :=
(cmd : string)
@ -342,12 +346,12 @@ static vm_obj io_process_spawn(vm_obj const & process_obj, vm_obj const &) {
if (cwd) proc.set_cwd(*cwd);
child ch = proc.spawn();
auto ch = proc.spawn();
return mk_io_result(to_obj(ch));
}
static vm_obj io_process_wait(vm_obj const & ch, vm_obj const &) {
return mk_io_result(mk_vm_nat(to_child(ch).wait()));
return mk_io_result(mk_vm_nat(to_child(ch)->wait()));
}
/*
@ -361,9 +365,9 @@ structure io.process (Err : Type) (handle : Type) (m : Type → Type → Type) :
*/
static vm_obj mk_process() {
return mk_vm_constructor(0, {
mk_native_closure([] (vm_obj const & c) { return to_obj(to_child(c).m_stdin); }),
mk_native_closure([] (vm_obj const & c) { return to_obj(to_child(c).m_stdout); }),
mk_native_closure([] (vm_obj const & c) { return to_obj(to_child(c).m_stderr); }),
mk_native_closure([] (vm_obj const & c) { return to_obj(to_child(c)->get_stdin()); }),
mk_native_closure([] (vm_obj const & c) { return to_obj(to_child(c)->get_stdout()); }),
mk_native_closure([] (vm_obj const & c) { return to_obj(to_child(c)->get_stderr()); }),
mk_native_closure(io_process_spawn),
mk_native_closure(io_process_wait),
});