diff --git a/cmd/tools/vtest-all.v b/cmd/tools/vtest-all.v index 0562d2f2d4..97d839b706 100644 --- a/cmd/tools/vtest-all.v +++ b/cmd/tools/vtest-all.v @@ -263,6 +263,22 @@ fn get_all_commands() []Command { rmfile: 'v.c' } } + $if !windows { + res << Command{ + line: '$vexe -raw-vsh-tmp-prefix tmp vlib/v/tests/script_with_no_extension' + okmsg: 'V can crun a script, that lacks a .vsh extension' + runcmd: .execute + expect: 'Test\n' + rmfile: 'vlib/v/tests/tmp.script_with_no_extension' + } + + res << Command{ + line: '$vexe -raw-vsh-tmp-prefix tmp run vlib/v/tests/script_with_no_extension' + okmsg: 'V can run a script, that lacks a .vsh extension' + runcmd: .execute + expect: 'Test\n' + } + } return res } diff --git a/doc/docs.md b/doc/docs.md index 969572c37a..50d34f97d7 100644 --- a/doc/docs.md +++ b/doc/docs.md @@ -127,6 +127,7 @@ To do so, run the command `v up`. * [Hot code reloading](#hot-code-reloading) * [Cross compilation](#cross-compilation) * [Cross-platform shell scripts in V](#cross-platform-shell-scripts-in-v) + * [Vsh scripts with no extension](#vsh-scripts-with-no-extension) * [Attributes](#attributes) * [Goto](#goto) * [Appendices](#appendices) @@ -5949,6 +5950,18 @@ Or just run it more like a traditional Bash script: On Unix-like platforms, the file can be run directly after making it executable using `chmod +x`: `./deploy.vsh` +## Vsh scripts with no extension + +Whilst V does normally not allow vsh scripts without the designated file extension, there is a way +to circumvent this rule and have a file with a fully custom name and shebang. Whilst this feature +exists it is only recommended for specific usecases like scripts that will be put in the path and +should **not** be used for things like build or deploy scripts. To access this feature start the +file with `#!/usr/bin/env -S v -raw-vsh-tmp-prefix tmp` where `tmp` is the prefix for +the built executable. This will run in crun mode so it will only rebuild if changes to the script +were made and keep the binary as `tmp.`. **Caution**: if this filename already +exists the file will be overriden. If you want to rebuild each time and not keep this binary instead +use `#!/usr/bin/env -S v -raw-vsh-tmp-prefix tmp run`. + ## Attributes V has several attributes that modify the behavior of functions and structs. diff --git a/vlib/v/builder/compile.v b/vlib/v/builder/compile.v index a470854522..91c3874332 100644 --- a/vlib/v/builder/compile.v +++ b/vlib/v/builder/compile.v @@ -309,7 +309,7 @@ pub fn (v &Builder) get_user_files() []string { is_real_file := does_exist && !os.is_dir(dir) resolved_link := if is_real_file && os.is_link(dir) { os.real_path(dir) } else { dir } if is_real_file && (dir.ends_with('.v') || resolved_link.ends_with('.vsh') - || dir.ends_with('.vv')) { + || v.pref.raw_vsh_tmp_prefix != '' || dir.ends_with('.vv')) { single_v_file := if resolved_link.ends_with('.vsh') { resolved_link } else { dir } // Just compile one file and get parent dir user_files << single_v_file diff --git a/vlib/v/pref/default.v b/vlib/v/pref/default.v index 0646b6d4dc..c80d250f74 100644 --- a/vlib/v/pref/default.v +++ b/vlib/v/pref/default.v @@ -50,7 +50,11 @@ pub fn (mut p Preferences) fill_with_defaults() { base = filename } target_dir := if os.is_dir(rpath) { rpath } else { os.dir(rpath) } - p.out_name = os.join_path(target_dir, base) + if p.raw_vsh_tmp_prefix != '' { + p.out_name = os.join_path(target_dir, p.raw_vsh_tmp_prefix + '.' + base) + } else { + p.out_name = os.join_path(target_dir, base) + } // Do *NOT* be tempted to generate binaries in the current work folder, // when -o is not given by default, like Go, Clang, GCC etc do. // @@ -119,7 +123,7 @@ pub fn (mut p Preferences) fill_with_defaults() { p.ccompiler_type = cc_from_string(p.ccompiler) p.is_test = p.path.ends_with('_test.v') || p.path.ends_with('_test.vv') || p.path.all_before_last('.v').all_before_last('.').ends_with('_test') - p.is_vsh = p.path.ends_with('.vsh') + p.is_vsh = p.path.ends_with('.vsh') || p.raw_vsh_tmp_prefix != '' p.is_script = p.is_vsh || p.path.ends_with('.v') || p.path.ends_with('.vv') if p.third_party_option == '' { p.third_party_option = p.cflags diff --git a/vlib/v/pref/pref.v b/vlib/v/pref/pref.v index 43deb35c60..e1e89b9a1b 100644 --- a/vlib/v/pref/pref.v +++ b/vlib/v/pref/pref.v @@ -102,37 +102,38 @@ pub mut: // verbosity VerboseLevel is_verbose bool // nofmt bool // disable vfmt - is_glibc bool // if GLIBC will be linked - is_musl bool // if MUSL will be linked - is_test bool // `v test string_test.v` - is_script bool // single file mode (`v program.v`), main function can be skipped - is_vsh bool // v script (`file.vsh`) file, the `os` module should be made global - is_livemain bool // main program that contains live/hot code - is_liveshared bool // a shared library, that will be used in a -live main program - is_shared bool // an ordinary shared library, -shared, no matter if it is live or not - is_o bool // building an .o file - is_prof bool // benchmark every function - is_prod bool // use "-O2" - is_repl bool - is_run bool // compile and run a v program, passing arguments to it, and deleting the executable afterwards - is_crun bool // similar to run, but does not recompile the executable, if there were no changes to the sources - is_debug bool // turned on by -g or -cg, it tells v to pass -g to the C backend compiler. - is_vlines bool // turned on by -g (it slows down .tmp.c generation slightly). - is_stats bool // `v -stats file_test.v` will produce more detailed statistics for the tests that were run - show_timings bool // show how much time each compiler stage took - is_fmt bool - is_vet bool - is_vweb bool // skip _ var warning in templates - is_ios_simulator bool - is_apk bool // build as Android .apk format - is_help bool // -h, -help or --help was passed - is_cstrict bool // turn on more C warnings; slightly slower - test_runner string // can be 'simple' (fastest, but much less detailed), 'tap', 'normal' - profile_file string // the profile results will be stored inside profile_file - profile_no_inline bool // when true, [inline] functions would not be profiled - profile_fns []string // when set, profiling will be off by default, but inside these functions (and what they call) it will be on. - translated bool // `v translate doom.v` are we running V code translated from C? allow globals, ++ expressions, etc - obfuscate bool // `v -obf program.v`, renames functions to "f_XXX" + is_glibc bool // if GLIBC will be linked + is_musl bool // if MUSL will be linked + is_test bool // `v test string_test.v` + is_script bool // single file mode (`v program.v`), main function can be skipped + is_vsh bool // v script (`file.vsh`) file, the `os` module should be made global + raw_vsh_tmp_prefix string // The prefix used for executables, when a script lacks the .vsh extension + is_livemain bool // main program that contains live/hot code + is_liveshared bool // a shared library, that will be used in a -live main program + is_shared bool // an ordinary shared library, -shared, no matter if it is live or not + is_o bool // building an .o file + is_prof bool // benchmark every function + is_prod bool // use "-O2" + is_repl bool + is_run bool // compile and run a v program, passing arguments to it, and deleting the executable afterwards + is_crun bool // similar to run, but does not recompile the executable, if there were no changes to the sources + is_debug bool // turned on by -g or -cg, it tells v to pass -g to the C backend compiler. + is_vlines bool // turned on by -g (it slows down .tmp.c generation slightly). + is_stats bool // `v -stats file_test.v` will produce more detailed statistics for the tests that were run + show_timings bool // show how much time each compiler stage took + is_fmt bool + is_vet bool + is_vweb bool // skip _ var warning in templates + is_ios_simulator bool + is_apk bool // build as Android .apk format + is_help bool // -h, -help or --help was passed + is_cstrict bool // turn on more C warnings; slightly slower + test_runner string // can be 'simple' (fastest, but much less detailed), 'tap', 'normal' + profile_file string // the profile results will be stored inside profile_file + profile_no_inline bool // when true, [inline] functions would not be profiled + profile_fns []string // when set, profiling will be off by default, but inside these functions (and what they call) it will be on. + translated bool // `v translate doom.v` are we running V code translated from C? allow globals, ++ expressions, etc + obfuscate bool // `v -obf program.v`, renames functions to "f_XXX" // Note: passing -cg instead of -g will set is_vlines to false and is_debug to true, thus making v generate cleaner C files, // which are sometimes easier to debug / inspect manually than the .tmp.c files by plain -g (when/if v line number generation breaks). sanitize bool // use Clang's new "-fsanitize" option @@ -705,6 +706,10 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin res.custom_prelude = prelude i++ } + '-raw-vsh-tmp-prefix' { + res.raw_vsh_tmp_prefix = cmdline.option(current_args, arg, '') + i++ + } '-cmain' { res.cmain = cmdline.option(current_args, '-cmain', '') i++ @@ -814,7 +819,7 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin if !res.is_bare && res.bare_builtin_dir != '' { eprintln_cond(show_output, '`-bare-builtin-dir` must be used with `-freestanding`') } - if command.ends_with('.vsh') { + if 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: