fix os2.process_exec on non-windows and add a smoke test

This commit is contained in:
Laytan Laats
2024-10-03 13:51:27 +02:00
parent 0d834a2c2e
commit 76806080ef
4 changed files with 78 additions and 35 deletions
+4 -2
View File
@@ -49,7 +49,7 @@ _pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) {
if r == nil || r.impl == nil { if r == nil || r.impl == nil {
return false, nil return false, nil
} }
fd := posix.FD((^File_Impl)(r.impl).fd) fd := __fd(r)
poll_fds := []posix.pollfd { poll_fds := []posix.pollfd {
posix.pollfd { posix.pollfd {
fd = fd, fd = fd,
@@ -57,8 +57,10 @@ _pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) {
}, },
} }
n := posix.poll(raw_data(poll_fds), u32(len(poll_fds)), 0) n := posix.poll(raw_data(poll_fds), u32(len(poll_fds)), 0)
if n != 1 { if n < 0 {
return false, _get_platform_error() return false, _get_platform_error()
} else if n != 1 {
return false, nil
} }
pipe_events := poll_fds[0].revents pipe_events := poll_fds[0].revents
if pipe_events >= {.IN} { if pipe_events >= {.IN} {
+44 -32
View File
@@ -371,16 +371,18 @@ process_exec :: proc(
loc := #caller_location, loc := #caller_location,
) -> ( ) -> (
state: Process_State, state: Process_State,
stdout: []u8, stdout: []byte,
stderr: []u8, stderr: []byte,
err: Error, err: Error,
) { ) {
assert(desc.stdout == nil, "Cannot redirect stdout when it's being captured", loc) assert(desc.stdout == nil, "Cannot redirect stdout when it's being captured", loc)
assert(desc.stderr == nil, "Cannot redirect stderr when it's being captured", loc) assert(desc.stderr == nil, "Cannot redirect stderr when it's being captured", loc)
stdout_r, stdout_w := pipe() or_return stdout_r, stdout_w := pipe() or_return
defer close(stdout_r) defer close(stdout_r)
stderr_r, stderr_w := pipe() or_return stderr_r, stderr_w := pipe() or_return
defer close(stdout_w) defer close(stdout_w)
process: Process process: Process
{ {
// NOTE(flysand): Make sure the write-ends are closed, regardless // NOTE(flysand): Make sure the write-ends are closed, regardless
@@ -392,44 +394,54 @@ process_exec :: proc(
desc.stderr = stderr_w desc.stderr = stderr_w
process = process_start(desc) or_return process = process_start(desc) or_return
} }
stdout_builder := strings.builder_make(allocator) or_return stdout_builder := strings.builder_make(allocator) or_return
stderr_builder := strings.builder_make(allocator) or_return stderr_builder := strings.builder_make(allocator) or_return
read_data: for {
buf: [1024]u8 has_stdout, has_stderr: bool
read_data: for !has_stdout || !has_stderr {
buf: [1024]u8 = ---
n: int n: int
has_data: bool has_data: bool
hangup := false
has_data, err = pipe_has_data(stdout_r) if !has_stdout {
if has_data { has_data, err = pipe_has_data(stdout_r)
n, err = read(stdout_r, buf[:]) if has_data {
strings.write_bytes(&stdout_builder, buf[:n]) n, err = read(stdout_r, buf[:])
if strings.write_bytes(&stdout_builder, buf[:n]) != n {
err = .Out_Of_Memory
}
}
switch err {
case nil: // nothing
case .EOF, .Broken_Pipe:
stdout = stdout_builder.buf[:]
has_stdout = true
case:
return
}
} }
switch err {
case nil: // nothing if !has_stderr {
case .Broken_Pipe: has_data, err = pipe_has_data(stderr_r)
hangup = true if has_data {
case: n, err = read(stderr_r, buf[:])
return if strings.write_bytes(&stderr_builder, buf[:n]) != n {
} err = .Out_Of_Memory
has_data, err = pipe_has_data(stderr_r) }
if has_data { }
n, err = read(stderr_r, buf[:]) switch err {
strings.write_bytes(&stderr_builder, buf[:n]) case nil: // nothing
} case .EOF, .Broken_Pipe:
switch err { stderr = stderr_builder.buf[:]
case nil: // nothing has_stderr = true
case .Broken_Pipe: case:
hangup = true return
case: }
return
}
if hangup {
break read_data
} }
} }
err = nil err = nil
stdout = transmute([]u8) strings.to_string(stdout_builder)
stderr = transmute([]u8) strings.to_string(stderr_builder)
state = process_wait(process) or_return state = process_wait(process) or_return
return return
} }
+1
View File
@@ -33,6 +33,7 @@ download_assets :: proc() {
@(require) import "net" @(require) import "net"
@(require) import "odin" @(require) import "odin"
@(require) import "os" @(require) import "os"
@(require) import "os/os2"
@(require) import "path/filepath" @(require) import "path/filepath"
@(require) import "reflect" @(require) import "reflect"
@(require) import "runtime" @(require) import "runtime"
+28
View File
@@ -0,0 +1,28 @@
package tests_core_os_os2
import "base:runtime"
import "core:log"
import os "core:os/os2"
import "core:testing"
_ :: log
@(test)
test_process_exec :: proc(t: ^testing.T) {
state, stdout, stderr, err := os.process_exec({
command = {"echo", "hellope"},
}, context.allocator)
defer delete(stdout)
defer delete(stderr)
when (ODIN_OS not_in runtime.Odin_OS_Types{.Linux, .Darwin, .Windows}) {
testing.expect_value(t, err, os.General_Error.Unsupported)
_ = state
} else {
testing.expect_value(t, state.exited, true)
testing.expect_value(t, state.success, true)
testing.expect_value(t, err, nil)
testing.expect_value(t, string(stdout), "hellope\n")
}
}