From 44bfceb6a61b5631384bf61ac3b3d38fb2bb64c1 Mon Sep 17 00:00:00 2001 From: Gabriel Ebner Date: Thu, 27 Apr 2017 17:22:25 +0200 Subject: [PATCH] feat(library/process): support working directory on windows --- src/library/process.cpp | 97 +++++++++++++++++++++++++++------------- src/library/process.h | 25 +++-------- src/library/vm/vm_io.cpp | 54 +++++++++++----------- 3 files changed, 101 insertions(+), 75 deletions(-) diff --git a/src/library/process.cpp b/src/library/process.cpp index 15a92e2c53..b89486c2d1 100644 --- a/src/library/process.cpp +++ b/src/library/process.cpp @@ -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); -} +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(exit_code); + } +}; + +// static HANDLE to_win_handle(FILE * file) { +// intptr_t handle = _get_osfhandle(fileno(file)); +// return reinterpret_cast(handle); +// } + +static FILE * from_win_handle(HANDLE handle, char const * mode) { int fd = _open_osfhandle(reinterpret_cast(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 const & cwd, + HANDLE hstdin, HANDLE hstdout, HANDLE hstderr); // TODO(@jroesch): unify this code between platforms better. static optional setup_stdio(SECURITY_ATTRIBUTES * saAttr, optional cfg) { @@ -98,10 +124,8 @@ static optional setup_stdio(SECURITY_ATTRIBUTES * saAttr, optional } } -// 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 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(proc_handle, std::make_shared(from_win_handle(parent_stdin, "w")), std::make_shared(from_win_handle(parent_stdout, "r")), std::make_shared(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 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 setup_stdio(optional cfg) { +static optional setup_stdio(optional cfg) { /* Setup stdio based on process configuration. */ if (cfg) { switch (*cfg) { @@ -265,7 +286,27 @@ optional setup_stdio(optional 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(WEXITSTATUS(status)); + } +}; + +std::shared_ptr 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(pid, std::make_shared(parent_stdin), std::make_shared(parent_stdout), std::make_shared(parent_stderr)); } void process::run() { - spawn().wait(); -} - -unsigned child::wait() { - int status; - waitpid(m_pid, &status, 0); - return static_cast(WEXITSTATUS(status)); + spawn()->wait(); } #endif diff --git a/src/library/process.h b/src/library/process.h index 745e481e1b..89fc0c5f51 100644 --- a/src/library/process.h +++ b/src/library/process.h @@ -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 spawn(); void run(); }; } diff --git a/src/library/vm/vm_io.cpp b/src/library/vm/vm_io.cpp index 07ad56be07..75d19382c2 100644 --- a/src/library/vm/vm_io.cpp +++ b/src/library/vm/vm_io.cpp @@ -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 m_child; + vm_child(std::shared_ptr 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(to_external(o)); +} + +std::shared_ptr const & to_child(vm_obj const & o) { + lean_vm_check(dynamic_cast(to_external(o))); + return static_cast(to_external(o))->m_child; +} + +vm_obj to_obj(std::shared_ptr 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(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(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), });