From 62c4e78a05d219a71db5b32357a13c08a4f96fa6 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 15 Apr 2026 04:42:36 +0300 Subject: [PATCH] pref: fix .vsh compilation not working on macOS (fixes #13601) --- vlib/build/README.md | 6 +++--- vlib/v/help/build/build.txt | 3 +++ vlib/v/pref/pref.v | 10 +++++++++- vlib/v/pref/pref_test.v | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/vlib/build/README.md b/vlib/build/README.md index 8d716722e..8100b287a 100644 --- a/vlib/build/README.md +++ b/vlib/build/README.md @@ -46,10 +46,10 @@ That works on BusyBox and OpenBSD too, where `/usr/bin/env -S` is not available. Running VSH scripts requires V to compile the script before executing it, which can cause a delay between when you run `./build.vsh` and when the script actually starts executing. -If you want to fix this, you can "pre-compile" the buildscript by building the script, i.e, running -`v -skip-running build.vsh`. +If you want to fix this, you can "pre-compile" the buildscript by building the script with +`v build build.vsh` (or `v -skip-running build.vsh`). > You will need to rebuild every time you change the buildscript, and you should also add `/build` > to your `.gitignore` -> If you want maximum speed, you can also `v -prod -skip-running build.vsh` +> If you want maximum speed, you can also `v -prod build build.vsh` diff --git a/vlib/v/help/build/build.txt b/vlib/v/help/build/build.txt index fcecc89a8..a70861de0 100644 --- a/vlib/v/help/build/build.txt +++ b/vlib/v/help/build/build.txt @@ -15,6 +15,9 @@ then run `./myfile -param1 abcde` and exit with its exit code". When compiling packages, V ignores files that end in '_test.v'. +Use `v build script.vsh` to compile a `.vsh` script without running it. +`v -skip-running script.vsh` works too. + When compiling a single main package, V writes the resulting executable to an output file named after the build target. ('v abc.v' and 'v abc/' both write either 'abc' or 'abc.exe') The '.exe' suffix is added automatically, when writing a Windows executable. diff --git a/vlib/v/pref/pref.v b/vlib/v/pref/pref.v index b25210735..a1f86588f 100644 --- a/vlib/v/pref/pref.v +++ b/vlib/v/pref/pref.v @@ -387,6 +387,7 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin mut no_skip_unused := false mut command, mut command_idx := '', 0 + mut build_vsh_source := false for i := 0; i < args.len; i++ { arg := args[i] if inline_icon_path := inline_icon_option_value(arg) { @@ -1096,6 +1097,12 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin } else { if command == 'build' && is_source_file(arg) { + if arg.ends_with('.vsh') { + command, command_idx = arg, i + build_vsh_source = true + res.skip_running = true + continue + } eprintln_exit('Use `v ${arg}` instead.') } if is_source_file(arg) && arg.ends_with('.vsh') { @@ -1223,7 +1230,8 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin eprintln_cond(show_output && !res.is_quiet, '`-bare-builtin-dir` must be used with `-freestanding`') } - if command.ends_with('.vsh') || (res.raw_vsh_tmp_prefix != '' && !res.is_run) { + if !build_vsh_source + && (command.ends_with('.vsh') || (res.raw_vsh_tmp_prefix != '' && !res.is_run)) { // `v build.vsh gcc` is the same as `v run build.vsh gcc`, // i.e. compiling, then running the script, passing the args // after it to the script: diff --git a/vlib/v/pref/pref_test.v b/vlib/v/pref/pref_test.v index 6ca8e745b..f38caed5e 100644 --- a/vlib/v/pref/pref_test.v +++ b/vlib/v/pref/pref_test.v @@ -167,6 +167,39 @@ fn test_v_cmds_and_flags() { assert no_bm_files_res.output.trim_space() == 'v build-module: no module specified' } +fn test_build_command_compiles_vsh_without_running_it() { + test_dir := os.join_path(os.vtmp_dir(), 'v_pref_build_vsh_${os.getpid()}') + os.rmdir_all(test_dir) or {} + os.mkdir_all(test_dir)! + defer { + os.rmdir_all(test_dir) or {} + } + script_path := os.join_path(test_dir, 'build_only.vsh') + marker_path := os.join_path(test_dir, 'marker.txt') + mut exe_path := os.join_path(test_dir, 'build_only') + $if windows { + exe_path += '.exe' + } + os.write_file(script_path, "import os + +fn main() { + marker_path := os.join_path(@DIR, 'marker.txt') + os.write_file(marker_path, 'ran') or { panic(err) } + println('ran') +} +")! + build_res := os.execute('${os.quoted_path(vexe)} -silent build ${os.quoted_path(script_path)}') + assert build_res.exit_code == 0, build_res.output + assert build_res.output == '' + assert !os.exists(marker_path) + assert os.is_file(exe_path) + + run_res := os.execute(os.quoted_path(exe_path)) + assert run_res.exit_code == 0, run_res.output + assert run_res.output.trim_space() == 'ran' + assert os.read_file(marker_path)! == 'ran' +} + const tfile = os.join_path(os.vtmp_dir(), 'unknown_options_output.c') fn test_unknown_option_flags_no_run() { -- 2.39.5