From 894d44346e31ec43f2bd473e35a8453d325e3edc Mon Sep 17 00:00:00 2001 From: David Bader <65665893+BunterSchatten@users.noreply.github.com> Date: Thu, 30 Jul 2026 09:10:27 +0200 Subject: [PATCH 1/4] run_args_start_idx is identical to double_dash_pos, no need to check again --- src/main.cpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index c4b4441d8..ddfa2e3ff 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3811,23 +3811,16 @@ int main(int arg_count, char const **arg_ptr) { build_context.command_kind = Command_test; } - isize run_args_start_idx = -1; - for_array(i, args) { - if (args[i] == "--") { - run_args_start_idx = i; - break; - } - } - if (run_args_start_idx != -1) { - last_non_run_arg = run_args_start_idx; + if (double_dash_pos != -1) { + last_non_run_arg = double_dash_pos; - if (run_args_start_idx == 2) { + if (double_dash_pos == 2) { // missing src path on argv[2], invocation: odin [run|test] -- usage(args[0]); return 1; } - for(isize i = run_args_start_idx+1; i < args.count; ++i) { + for(isize i = double_dash_pos+1; i < args.count; ++i) { array_add(&run_args, args[i]); } } From 16ee03828bc345b4c45239ca93ffce2a6a9636d5 Mon Sep 17 00:00:00 2001 From: David Bader <65665893+BunterSchatten@users.noreply.github.com> Date: Thu, 30 Jul 2026 12:31:20 +0200 Subject: [PATCH 2/4] handle spaces in args for 'odin run . -- ' on Windows --- src/common.cpp | 27 +++++++++++++++++--- src/main.cpp | 69 +++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 84 insertions(+), 12 deletions(-) diff --git a/src/common.cpp b/src/common.cpp index 89964309b..133b1888a 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -640,7 +640,7 @@ gb_internal gb_inline f64 gb_sqrt(f64 x) { #if defined(GB_SYSTEM_WINDOWS) -gb_internal wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc) { +gb_internal wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc, wchar_t **_after_double_dash_raw) { u32 i, j; u32 len = cast(u32)string16_len(cast(u16 *)cmd_line); @@ -649,6 +649,8 @@ gb_internal wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc) { wchar_t **argv = cast(wchar_t **)GlobalAlloc(GMEM_FIXED, i + (len+2)*gb_size_of(wchar_t)); wchar_t *_argv = cast(wchar_t *)((cast(u8 *)argv)+i); + wchar_t *after_double_dash_raw = nullptr; + u32 argc = 0; argv[argc] = _argv; bool in_quote = false; @@ -657,6 +659,17 @@ gb_internal wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc) { i = 0; j = 0; + auto const check_double_dash = [&]() { + if (!after_double_dash_raw && + argc >= 1 && + argv[argc - 1][0] == '-' && + argv[argc - 1][1] == '-' && + argv[argc - 1][2] == '\0') { + + after_double_dash_raw = cmd_line + i; + } + }; + for (;;) { wchar_t a = cmd_line[i]; if (a == 0) { @@ -673,7 +686,10 @@ gb_internal wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc) { case '\"': in_quote = true; in_text = true; - if (in_space) argv[argc++] = _argv+j; + if (in_space) { + check_double_dash(); + argv[argc++] = _argv + j; + } in_space = false; break; case ' ': @@ -686,7 +702,10 @@ gb_internal wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc) { break; default: in_text = true; - if (in_space) argv[argc++] = _argv+j; + if (in_space) { + check_double_dash(); + argv[argc++] = _argv + j; + } _argv[j++] = a; in_space = false; break; @@ -696,8 +715,10 @@ gb_internal wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc) { } _argv[j] = '\0'; argv[argc] = nullptr; + check_double_dash(); if (_argc) *_argc = argc; + if (_after_double_dash_raw) *_after_double_dash_raw = after_double_dash_raw; return argv; } diff --git a/src/main.cpp b/src/main.cpp index ddfa2e3ff..a176ace00 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -178,17 +178,63 @@ gb_internal i32 system_exec_command_line_app(char const *name, char const *fmt, return exit_code; } -#if defined(GB_SYSTEM_WINDOWS) -#include -#else +#if !defined(GB_SYSTEM_WINDOWS) #include extern char **environ; #endif -int run_subprocess(const char *name, const char **args) { #if defined(GB_SYSTEM_WINDOWS) - return (int)_spawnv(_P_WAIT, name, args); +int run_subprocess(String const &exe_name, wchar_t *after_double_dash_raw) { + gbAllocator a = heap_allocator(); + + String16 wexe_name = string_to_string16(a, exe_name); + defer (gb_free(a, wexe_name.text)); + + isize args_len = 0; + if (after_double_dash_raw) { + args_len = string16_len(cast(u16 *)after_double_dash_raw); + } + + isize cmd_len = wexe_name.len + 2; + if (args_len > 0) cmd_len += args_len + 1; + + wchar_t *cmd_line = gb_alloc_array(a, wchar_t, cmd_len + 1); + defer (gb_free(a, cmd_line)); + + isize n = 0; + cmd_line[n++] = '"'; + gb_memmove(cmd_line + n, wexe_name.text, wexe_name.len * gb_size_of(wchar_t)); + n += wexe_name.len; + cmd_line[n++] = '"'; + if (args_len > 0) { + cmd_line[n++] = ' '; + gb_memmove(cmd_line + n, after_double_dash_raw, args_len * gb_size_of(wchar_t)); + n += args_len; + } + cmd_line[n] = '\0'; + + STARTUPINFOW start_info = {gb_size_of(STARTUPINFOW)}; + PROCESS_INFORMATION pi = {0}; + int exit_code = 0; + + if (CreateProcessW(nullptr, cmd_line, + nullptr, nullptr, true, 0, nullptr, nullptr, + &start_info, &pi)) { + WaitForSingleObject(pi.hProcess, INFINITE); + GetExitCodeProcess(pi.hProcess, cast(DWORD *)&exit_code); + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } else { + String cmd_line_utf8 = string16_to_string(a, make_string16(cast(u16 *)cmd_line, n)); + gb_printf_err("Failed to execute command:\n\t%.*s\n", LIT(cmd_line_utf8)); + gb_free(a, cmd_line_utf8.text); + exit_code = -1; + } + return exit_code; +} #else +int run_subprocess(const char *name, const char **args) { pid_t pid; int status; status = posix_spawn(&pid, name, NULL, NULL, (char *const *)args, environ); @@ -215,8 +261,8 @@ int run_subprocess(const char *name, const char **args) { } } GB_PANIC("Subprocess failure"); -#endif } +#endif #if defined(GB_SYSTEM_WINDOWS) #define popen _popen @@ -250,12 +296,12 @@ gb_internal bool system_exec_command_line_app_output(char const *command, gbStri return true; } -gb_internal Array setup_args(int argc, char const **argv) { +gb_internal Array setup_args(int argc, char const **argv, wchar_t **after_double_dash_raw) { gbAllocator a = heap_allocator(); #if defined(GB_SYSTEM_WINDOWS) int wargc = 0; - wchar_t **wargv = command_line_to_wargv(GetCommandLineW(), &wargc); + wchar_t **wargv = command_line_to_wargv(GetCommandLineW(), &wargc, after_double_dash_raw); auto args = array_make(a, 0, wargc); for (isize i = 0; i < wargc; i++) { u16 *warg = cast(u16 *)wargv[i]; @@ -3757,7 +3803,8 @@ int main(int arg_count, char const **arg_ptr) { init_build_context_error_pos_style(); - Array args = setup_args(arg_count, arg_ptr); + wchar_t *after_double_dash_raw = nullptr; + Array args = setup_args(arg_count, arg_ptr, &after_double_dash_raw); Array run_args = array_make(heap_allocator(), 0, arg_count); defer (array_free(&run_args)); @@ -4415,6 +4462,9 @@ end_of_code_gen:; String exe_name = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]); defer (gb_free(heap_allocator(), exe_name.text)); +#if defined(GB_SYSTEM_WINDOWS) + int subprocess_res = run_subprocess(exe_name, after_double_dash_raw); +#else const char* exe_name_cstring = alloc_cstring(heap_allocator(), exe_name); Array run_args_cstring = array_make(heap_allocator(), 0, run_args.count); defer({ @@ -4429,6 +4479,7 @@ end_of_code_gen:; array_add(&run_args_cstring, NULL); int subprocess_res = run_subprocess(exe_name_cstring, run_args_cstring.data); +#endif if (subprocess_res) { gb_exit(subprocess_res); } From 7130f042dcc40a46975c8d99701f13a3d3942e70 Mon Sep 17 00:00:00 2001 From: David Bader <65665893+BunterSchatten@users.noreply.github.com> Date: Thu, 30 Jul 2026 12:32:02 +0200 Subject: [PATCH 3/4] define out unused run_args on Windows --- src/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index a176ace00..ef304cd16 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3805,8 +3805,10 @@ int main(int arg_count, char const **arg_ptr) { wchar_t *after_double_dash_raw = nullptr; Array args = setup_args(arg_count, arg_ptr, &after_double_dash_raw); +#if !defined(GB_SYSTEM_WINDOWS) Array run_args = array_make(heap_allocator(), 0, arg_count); defer (array_free(&run_args)); +#endif String command = args[1]; String init_filename = {}; @@ -3867,9 +3869,11 @@ int main(int arg_count, char const **arg_ptr) { return 1; } +#if !defined(GB_SYSTEM_WINDOWS) for(isize i = double_dash_pos+1; i < args.count; ++i) { array_add(&run_args, args[i]); } +#endif } args = array_slice(args, 0, last_non_run_arg); From edc522a05c2e99d93948f5596fb6383cf66ab50d Mon Sep 17 00:00:00 2001 From: David Bader <65665893+BunterSchatten@users.noreply.github.com> Date: Thu, 30 Jul 2026 12:31:20 +0200 Subject: [PATCH 4/4] add assert to make sure old and new double_dash_pos are the same --- src/common.cpp | 7 +++++-- src/main.cpp | 13 +++++++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/common.cpp b/src/common.cpp index 133b1888a..76ace2755 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -640,7 +640,7 @@ gb_internal gb_inline f64 gb_sqrt(f64 x) { #if defined(GB_SYSTEM_WINDOWS) -gb_internal wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc, wchar_t **_after_double_dash_raw) { +gb_internal wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc, isize *_double_dash_pos, wchar_t **_after_double_dash_raw) { u32 i, j; u32 len = cast(u32)string16_len(cast(u16 *)cmd_line); @@ -650,6 +650,7 @@ gb_internal wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc, wchar wchar_t *_argv = cast(wchar_t *)((cast(u8 *)argv)+i); wchar_t *after_double_dash_raw = nullptr; + isize double_dash_pos = -1; u32 argc = 0; argv[argc] = _argv; @@ -660,12 +661,13 @@ gb_internal wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc, wchar j = 0; auto const check_double_dash = [&]() { - if (!after_double_dash_raw && + if (double_dash_pos == -1 && argc >= 1 && argv[argc - 1][0] == '-' && argv[argc - 1][1] == '-' && argv[argc - 1][2] == '\0') { + double_dash_pos = argc - 1; after_double_dash_raw = cmd_line + i; } }; @@ -718,6 +720,7 @@ gb_internal wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc, wchar check_double_dash(); if (_argc) *_argc = argc; + if (_double_dash_pos) *_double_dash_pos = double_dash_pos; if (_after_double_dash_raw) *_after_double_dash_raw = after_double_dash_raw; return argv; } diff --git a/src/main.cpp b/src/main.cpp index ef304cd16..de4c219e2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -296,12 +296,12 @@ gb_internal bool system_exec_command_line_app_output(char const *command, gbStri return true; } -gb_internal Array setup_args(int argc, char const **argv, wchar_t **after_double_dash_raw) { +gb_internal Array setup_args(int argc, char const **argv, isize *double_dash_pos, wchar_t **after_double_dash_raw) { gbAllocator a = heap_allocator(); #if defined(GB_SYSTEM_WINDOWS) int wargc = 0; - wchar_t **wargv = command_line_to_wargv(GetCommandLineW(), &wargc, after_double_dash_raw); + wchar_t **wargv = command_line_to_wargv(GetCommandLineW(), &wargc, double_dash_pos, after_double_dash_raw); auto args = array_make(a, 0, wargc); for (isize i = 0; i < wargc; i++) { u16 *warg = cast(u16 *)wargv[i]; @@ -310,6 +310,8 @@ gb_internal Array setup_args(int argc, char const **argv, wchar_t **afte String arg = string16_to_string(a, wstr); if (arg.len > 0) { array_add(&args, arg); + } else if (double_dash_pos && *double_dash_pos > 0 && args.count < *double_dash_pos) { + *double_dash_pos -= 1; } } return args; @@ -3803,8 +3805,9 @@ int main(int arg_count, char const **arg_ptr) { init_build_context_error_pos_style(); + isize double_dash_pos = -1; wchar_t *after_double_dash_raw = nullptr; - Array args = setup_args(arg_count, arg_ptr, &after_double_dash_raw); + Array args = setup_args(arg_count, arg_ptr, &double_dash_pos, &after_double_dash_raw); #if !defined(GB_SYSTEM_WINDOWS) Array run_args = array_make(heap_allocator(), 0, arg_count); defer (array_free(&run_args)); @@ -3814,9 +3817,11 @@ int main(int arg_count, char const **arg_ptr) { String init_filename = {}; isize last_non_run_arg = args.count; - isize double_dash_pos = -1; for_array(i, args) { if (args[i] == "--") { +#if defined(GB_SYSTEM_WINDOWS) + GB_ASSERT(double_dash_pos == i); +#endif double_dash_pos = i; break; }