From 8e64a34b0b975e6543ff94993108e2c6c2a1c1f8 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Tue, 21 Apr 2026 16:55:16 +0300 Subject: [PATCH] tools: fix v repl not working in certain shells on windows (fixes #15712) --- cmd/tools/vrepl.v | 33 ++++++++++++++-- vlib/v/slow_tests/repl/repl_time_state_test.v | 38 +++++++++++++++++++ 2 files changed, 67 insertions(+), 4 deletions(-) diff --git a/cmd/tools/vrepl.v b/cmd/tools/vrepl.v index e57637518..c6ae5f6a2 100644 --- a/cmd/tools/vrepl.v +++ b/cmd/tools/vrepl.v @@ -344,8 +344,9 @@ fn (r &Repl) check_fn_type_kind(new_line string) FnType { // -usecache keeps repeated REPL checks responsive by reusing cached modules. // -w suppresses warnings from this synthetic println probe. // -check just does syntax and checker analysis without generating/running code. - os_response := - os.execute('${os.quoted_path(vexe)} -usecache -w -check ${os.quoted_path(check_file)}') + os_response := execute_repl_v_command(vexe, ['-usecache', '-w', '-check', check_file]) or { + return FnType.none + } str_response := convert_output(os_response.output) if os_response.exit_code != 0 && str_response.contains('can not print void expressions') { return FnType.void @@ -1035,11 +1036,35 @@ fn cleanup_files(file string) { } } +fn execute_repl_v_command(v_path string, args []string) !os.Result { + $if windows { + mut process := os.new_process(v_path) + process.set_args(args) + process.set_redirect_stdio() + process.create_no_window = true + process.wait() + stdout_output := process.stdout_slurp() + stderr_output := process.stderr_slurp() + exit_code := process.code + process.close() + return os.Result{ + exit_code: exit_code + output: stdout_output + stderr_output + } + } $else { + mut cmd := os.quoted_path(v_path) + for arg in args { + cmd += ' ' + os.quoted_path(arg) + } + return os.execute(cmd) + } +} + fn repl_run_vfile(file string) !os.Result { $if trace_repl_temp_files ? { eprintln('>> repl_run_vfile file: ${file}') } - s := os.execute('${os.quoted_path(vexe)} -message-limit 1 -repl run ${os.quoted_path(file)}') + s := execute_repl_v_command(vexe, ['-message-limit', '1', '-repl', 'run', file])! if s.exit_code < 0 { rerror(s.output) return error(s.output) @@ -1054,7 +1079,7 @@ fn repl_check_vfile(file string) !os.Result { // Declaration-only REPL lines do not need code generation or execution. // Cached checks keep repeated imports and definitions responsive. s := - os.execute('${os.quoted_path(vexe)} -usecache -message-limit 1 -repl -check ${os.quoted_path(file)}') + execute_repl_v_command(vexe, ['-usecache', '-message-limit', '1', '-repl', '-check', file])! if s.exit_code < 0 { rerror(s.output) return error(s.output) diff --git a/vlib/v/slow_tests/repl/repl_time_state_test.v b/vlib/v/slow_tests/repl/repl_time_state_test.v index d2b3d48d3..038e10550 100644 --- a/vlib/v/slow_tests/repl/repl_time_state_test.v +++ b/vlib/v/slow_tests/repl/repl_time_state_test.v @@ -30,3 +30,41 @@ fn test_repl_keeps_time_assignments_stable_across_reads() { assert lines.len == 2, 'expected 2 repl outputs, got ${lines.len}: ${lines}' assert lines[0] == lines[1], 'expected the stored time to remain stable, got ${lines}' } + +fn test_repl_bypasses_local_cmd_exe_on_windows() { + $if !windows { + return + } + vexec := runner.full_path_to_v(5) + temp_dir := os.join_path(os.vtmp_dir(), 'v_repl_cmd_bypass_${os.getpid()}') + os.rmdir_all(temp_dir) or {} + os.mkdir_all(temp_dir) or { panic(err) } + defer { + os.rmdir_all(temp_dir) or {} + } + fake_cmd_source := os.join_path(temp_dir, 'fake_cmd.v') + fake_cmd_exe := os.join_path(temp_dir, 'cmd.exe') + os.write_file(fake_cmd_source, "fn main() {\n\tprintln('fake cmd invoked')\n}\n") or { + panic(err) + } + build_fake_cmd := + os.execute('${os.quoted_path(vexec)} -o ${os.quoted_path(fake_cmd_exe)} ${os.quoted_path(fake_cmd_source)}') + assert build_fake_cmd.exit_code == 0, build_fake_cmd.output + original_vexe := os.getenv('VEXE') + os.setenv('VEXE', vexec, true) + defer { + os.setenv('VEXE', original_vexe, true) + } + mut repl := os.new_process(vexec) + repl.set_args(['repl', '-replfolder', temp_dir, '-replprefix', 'cmd_bypass.']) + repl.set_redirect_stdio() + repl.create_no_window = true + repl.run() + repl.stdin_write('1+1\nexit\n') + repl.wait() + output := repl.stdout_slurp() + repl.stderr_slurp() + repl.close() + assert repl.code == 0, output + assert !output.contains('fake cmd invoked'), output + assert output.trim_space() == '2', output +} -- 2.39.5