225 lines
4.5 KiB
Odin
225 lines
4.5 KiB
Odin
package builder
|
|
|
|
import "base:intrinsics"
|
|
import "core:bufio"
|
|
import "core:fmt"
|
|
import "core:io"
|
|
import "core:log"
|
|
import os "core:os/os2"
|
|
import "core:path/filepath"
|
|
import "core:strings"
|
|
|
|
Process_Error :: enum {
|
|
OK,
|
|
Invalid_Exit_Code,
|
|
Crash,
|
|
}
|
|
|
|
Run_Error :: union #shared_nil {
|
|
Process_Error,
|
|
os.Error,
|
|
}
|
|
|
|
@(private = "file")
|
|
run_cmd_internal :: proc(
|
|
cmd: []string,
|
|
cwd: string,
|
|
capture: bool,
|
|
loc := #caller_location,
|
|
) -> (
|
|
result: string,
|
|
err: Run_Error,
|
|
) {
|
|
if len(cwd) > 0 {
|
|
log.infof(
|
|
"running [%s]: %s",
|
|
cwd,
|
|
strings.join(cmd, " ", context.temp_allocator),
|
|
location = loc,
|
|
)
|
|
} else {
|
|
log.infof("running: %s", strings.join(cmd, " ", context.temp_allocator), location = loc)
|
|
}
|
|
|
|
r, w := handle_error2(os.pipe())
|
|
|
|
defer handle_error(os.close(r))
|
|
desc := os.Process_Desc {
|
|
command = cmd,
|
|
working_dir = cwd,
|
|
stderr = os.stderr,
|
|
stdout = w,
|
|
}
|
|
|
|
process: os.Process
|
|
{
|
|
defer handle_error(os.close(w))
|
|
process = handle_error1(os.process_start(desc))
|
|
}
|
|
|
|
state: os.Process_State
|
|
|
|
b := strings.builder_make(context.temp_allocator)
|
|
defer strings.builder_destroy(&b)
|
|
|
|
for {
|
|
has_data, pipe_err := os.pipe_has_data(r)
|
|
if pipe_err != .Broken_Pipe {
|
|
handle_error(pipe_err)
|
|
}
|
|
|
|
n: int
|
|
buf: [1024]u8
|
|
if has_data {
|
|
n = handle_error1(os.read(r, buf[:]))
|
|
if capture {
|
|
strings.write_bytes(&b, buf[0:n])
|
|
}
|
|
handle_error1(os.write(os.stderr, buf[0:n]))
|
|
}
|
|
|
|
if state.exited {
|
|
break
|
|
}
|
|
|
|
wait_for_kill := false
|
|
if pipe_err == .Broken_Pipe {
|
|
_ = os.process_kill(process)
|
|
wait_for_kill = true
|
|
}
|
|
wait_err: os.Error
|
|
state, wait_err = os.process_wait(process, wait_for_kill ? os.TIMEOUT_INFINITE : 0)
|
|
if wait_err != .Timeout {
|
|
handle_error(wait_err)
|
|
}
|
|
}
|
|
|
|
result = strings.to_string(b)
|
|
|
|
if !state.success {
|
|
return "", .Crash
|
|
}
|
|
|
|
if state.exit_code != 0 {
|
|
return "", .Invalid_Exit_Code
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
run_cmd :: proc(
|
|
cmd: []string,
|
|
cwd: string,
|
|
capture := true,
|
|
expr := #caller_expression,
|
|
loc := #caller_location,
|
|
) -> string {
|
|
result, err := run_cmd_internal(cmd, cwd, capture, loc)
|
|
handle_error(err, "", expr, loc)
|
|
return result
|
|
}
|
|
|
|
handle_error :: proc(
|
|
err: $E,
|
|
msg: string = "",
|
|
expr := #caller_expression,
|
|
loc := #caller_location,
|
|
) where intrinsics.type_is_enum(E) ||
|
|
intrinsics.type_is_union(E) {
|
|
if err != nil {
|
|
log.panicf("%v %s error: %v", expr, msg, err, location = loc)
|
|
}
|
|
}
|
|
|
|
handle_error1 :: proc(
|
|
val: $V,
|
|
err: $E,
|
|
msg: string = "",
|
|
expr := #caller_expression,
|
|
loc := #caller_location,
|
|
) -> V {
|
|
handle_error(err, msg, expr, loc)
|
|
return val
|
|
}
|
|
|
|
handle_error2 :: proc(
|
|
val1: $V1,
|
|
val2: $V2,
|
|
err: $E,
|
|
msg: string = "",
|
|
expr := #caller_expression,
|
|
loc := #caller_location,
|
|
) -> (
|
|
V1,
|
|
V2,
|
|
) {
|
|
handle_error(err, msg, expr, loc)
|
|
return val1, val2
|
|
}
|
|
|
|
remove_file :: proc(path: string, expr := #caller_expression, loc := #caller_location) {
|
|
log.infof("remove(%s)", path, location = loc)
|
|
err := os.remove(path)
|
|
if err != .Not_Exist {
|
|
handle_error(err, fmt.tprintf("failed to remove %s", path), expr = expr, loc = loc)
|
|
}
|
|
}
|
|
|
|
remove_all :: proc(path: string, expr := #caller_expression, loc := #caller_location) {
|
|
log.infof("remove_all(%s)", path, location = loc)
|
|
err := os.remove_all(path)
|
|
if err != .Not_Exist {
|
|
handle_error(err, fmt.tprintf("failed to remove %s", path), expr = expr, loc = loc)
|
|
}
|
|
}
|
|
|
|
copy_file :: proc(src, dst: string, expr := #caller_expression, loc := #caller_location) {
|
|
log.infof("cp %s %s", src, dst)
|
|
handle_error(os.copy_file(dst, src), "", expr, loc)
|
|
}
|
|
|
|
rename :: proc(src, dst: string, expr := #caller_expression, loc := #caller_location) {
|
|
log.infof("rename %s -> %s", src, dst)
|
|
handle_error(os.rename(src, dst), "", expr, loc)
|
|
}
|
|
|
|
mkdir_all :: proc(path: string, expr := #caller_expression, loc := #caller_location) {
|
|
log.infof("mkdir -p %s", path)
|
|
err := os.mkdir_all(path)
|
|
if err != .Exist {
|
|
handle_error(err, "", expr, loc)
|
|
}
|
|
}
|
|
|
|
absolute_path :: proc(
|
|
path: string,
|
|
allocator := context.temp_allocator,
|
|
expr := #caller_expression,
|
|
loc := #caller_location,
|
|
) -> string {
|
|
abs_path, err := os.get_absolute_path(path, allocator)
|
|
handle_error(err, "", expr, loc)
|
|
|
|
return abs_path
|
|
}
|
|
|
|
set_env :: proc(env, val: string) {
|
|
log.infof("set_env(%s, %s)", env, val)
|
|
handle_error(os.set_env(env, val))
|
|
}
|
|
|
|
process_exists :: proc(name: string) -> bool {
|
|
pids := handle_error1(os.process_list(context.temp_allocator))
|
|
|
|
for pid in pids {
|
|
pinfo, err := os.process_info_by_pid(pid, {.Executable_Path}, context.temp_allocator)
|
|
if err == nil {
|
|
if filepath.base(pinfo.executable_path) == name {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|