diff --git a/.vscode/launch.json b/.vscode/launch.json index ba683d5..ca37f2e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -36,6 +36,17 @@ "cwd": "${workspaceFolder}" }, // Linux / Mac configs + { + "type": "lldb", + "request": "launch", + "name": "Debug Builder", + "args": [ + "run", + "./builder" + ], + "cwd": "${workspaceFolder}", + "program": "odin", + }, { "type": "lldb", "request": "launch", diff --git a/.vscode/tasks.json b/.vscode/tasks.json index afa5f01..e6fb0cb 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -66,6 +66,23 @@ "kind": "build", "isDefault": true }, + }, + { + "label": "Build Builder", + "type": "shell", + "command": "odin build ./builder -debug", + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "shared", + "showReuseMessage": false, + "clear": true + }, + "group": { + "kind": "build", + "isDefault": false + }, } ] -} +} \ No newline at end of file diff --git a/build.sh b/build.sh index 036208d..49475dc 100755 --- a/build.sh +++ b/build.sh @@ -1,3 +1,3 @@ #!/usr/bin/env bash -odin run ./builder -- $@ +odin run ./builder -sanitize:address -- $@ diff --git a/builder/builder.odin b/builder/builder.odin index 3784b85..ba596e7 100644 --- a/builder/builder.odin +++ b/builder/builder.odin @@ -35,8 +35,9 @@ build_deps :: proc(opts: Options) { { cwd := "./libs/raylib" out_dir := shared ? "zig-out-shared" : "zig-out-static" + remove_all(fmt.tprintf("./libs/raylib/%s", out_dir)) if force { - remove_all(fmt.tprintf("./libs/raylib/%s", out_dir)) + remove_all("./libs/raylib/.zig-cache") } target := opts.variant == .Web ? "wasm32-emscripten" : "native" @@ -71,14 +72,12 @@ build_deps :: proc(opts: Options) { "./physfs", } build_cmd := []string{"cmake", "--build", "build", "--config", "MinSizeRel"} - if !is_built || force { - if opts.variant == .Web { - run_cmd(temp_concat({"emcmake"}, prepare_cmd), cwd) - run_cmd(temp_concat({"emmake"}, build_cmd), cwd) - } else { - run_cmd(prepare_cmd, cwd) - run_cmd(build_cmd, cwd) - } + if opts.variant == .Web { + run_cmd(temp_concat({"emcmake"}, prepare_cmd), cwd) + run_cmd(temp_concat({"emmake"}, build_cmd), cwd) + } else { + run_cmd(prepare_cmd, cwd) + run_cmd(build_cmd, cwd) } } diff --git a/builder/helpers.odin b/builder/helpers.odin index 879d051..0eb9237 100644 --- a/builder/helpers.odin +++ b/builder/helpers.odin @@ -8,7 +8,6 @@ import "core:log" import os "core:os/os2" import "core:path/filepath" import "core:strings" -import "core:thread" Process_Error :: enum { OK, @@ -40,8 +39,9 @@ run_cmd_internal :: proc( log.infof("running: %s", strings.join(cmd, " ", context.temp_allocator), location = loc) } - r, w := os.pipe() or_return - defer os.close(r) + r, w := handle_error2(os.pipe()) + + defer handle_error(os.close(r)) desc := os.Process_Desc { command = cmd, working_dir = cwd, @@ -49,56 +49,45 @@ run_cmd_internal :: proc( stdout = w, } - Read_Context :: struct { - r: ^os.File, - result: ^string, + process: os.Process + { + defer handle_error(os.close(w)) + process = handle_error1(os.process_start(desc)) } - read_ctx := Read_Context { - r = r, - result = &result, + state: os.Process_State + + b := strings.builder_make(context.temp_allocator) + + 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[:])) + handle_error1(os.write(os.stderr, buf[0:n])) + } + + if state.exited { + break + } + + wait_for_kill := false + if pipe_err == .Broken_Pipe { + handle_error(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) + } } - thread_ctx := context - thread_ctx.user_ptr = &read_ctx - read_thread := thread.create_and_start( - proc() { - ctx := cast(^Read_Context)context.user_ptr - - builder: strings.Builder - string_writer := strings.to_writer(&builder) - defer io.destroy(string_writer) - - mw: io.Multi_Writer - w := io.multi_writer_init(&mw, string_writer, os.to_writer(os.stderr)) - - tee: io.Tee_Reader - reader := io.tee_reader_init(&tee, os.to_reader(ctx.r), w) - buf: [1024]u8 - - err: io.Error - for { - n: int - n, err = io.read(reader, buf[:], nil) - if err == .EOF { - break - } - handle_error(err) - } - - // NOTE: mem leak, but who cares - ctx.result^ = strings.clone(strings.to_string(builder)) - }, - thread_ctx, - ) - - process := os.process_start(desc) or_return - os.close(w) - - state := os.process_wait(process) or_return - - thread.join(read_thread) - if !state.success { return "", .Crash } @@ -133,6 +122,32 @@ handle_error :: proc( } } +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)