From 159932d59b238e7eec4e923a5b0fbea83297f095 Mon Sep 17 00:00:00 2001 From: Nicolas Sauzede Date: Fri, 20 Nov 2020 09:25:59 +0100 Subject: [PATCH] v: run code from stdin `echo println(2+2) | v run -`, with no repl limits (#6884) --- cmd/v/help/run.txt | 5 ++- vlib/v/pref/pref.v | 45 +++++++++++++++++++++++ vlib/v/tests/run_v_code_from_stdin_test.v | 23 ++++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 vlib/v/tests/run_v_code_from_stdin_test.v diff --git a/cmd/v/help/run.txt b/cmd/v/help/run.txt index 53a147c06f..d214ceeb87 100644 --- a/cmd/v/help/run.txt +++ b/cmd/v/help/run.txt @@ -1,8 +1,11 @@ -Usage: v [build flags] run [arguments...] +Usage: v [build flags] run [arguments...] This command is equivalent to running `v build` and running the compiled executable. The executable is passed the arguments as provided in [arguments...]. +If the target is '-', it means that the V source code to build comes from stdin. +If the '-o' option is not specified, and the target is '-', a temporary base name for the executable will be used. + The exit status of run will be: * `1` if the compilation failed. * The exit code of the compiled executable otherwise. diff --git a/vlib/v/pref/pref.v b/vlib/v/pref/pref.v index f2016c0a52..195254dc6b 100644 --- a/vlib/v/pref/pref.v +++ b/vlib/v/pref/pref.v @@ -6,6 +6,7 @@ module pref import os.cmdline import os import v.vcache +import rand pub enum BuildMode { // `v program.v' @@ -408,6 +409,44 @@ pub fn parse_args(args []string) (&Preferences, string) { } res.path = args[command_pos + 1] res.run_args = args[command_pos + 2..] + if res.path == '-' { + tmp_file_path := rand.ulid() + mut tmp_exe_file_path := res.out_name + mut output_option := '' + if tmp_exe_file_path == '' { + tmp_exe_file_path = '${tmp_file_path}.exe' + output_option = '-o "$tmp_exe_file_path"' + } + tmp_v_file_path := '${tmp_file_path}.v' + mut lines := []string{} + for { + iline := os.get_raw_line() + if iline.len == 0 { + break + } + lines << iline + } + contents := lines.join('') + os.write_file(tmp_v_file_path, contents) or { + panic('Failed to create temporary file $tmp_v_file_path') + } + run_options := cmdline.options_before(args, ['run']).join(' ') + command_options := cmdline.options_after(args, ['run'])[1..].join(' ') + vexe := vexe_path() + tmp_cmd := '"$vexe" $output_option $run_options run "$tmp_v_file_path" $command_options' + // + res.vrun_elog('tmp_cmd: $tmp_cmd') + tmp_result := os.system(tmp_cmd) + res.vrun_elog('exit code: $tmp_result') + // + if output_option.len != 0 { + res.vrun_elog('remove tmp exe file: $tmp_exe_file_path') + os.rm(tmp_exe_file_path) + } + res.vrun_elog('remove tmp v file: $tmp_v_file_path') + os.rm(tmp_v_file_path) + exit(tmp_result) + } must_exist(res.path) if !res.path.ends_with('.v') && os.is_executable(res.path) && os.is_file(res.path) && os.is_file(res.path + '.v') { @@ -430,6 +469,12 @@ pub fn parse_args(args []string) (&Preferences, string) { return res, command } +fn (pref &Preferences) vrun_elog(s string) { + if pref.is_verbose { + eprintln('> v run -, $s') + } +} + fn must_exist(path string) { if !os.exists(path) { eprintln('v expects that `$path` exists, but it does not') diff --git a/vlib/v/tests/run_v_code_from_stdin_test.v b/vlib/v/tests/run_v_code_from_stdin_test.v new file mode 100644 index 0000000000..4e175c612c --- /dev/null +++ b/vlib/v/tests/run_v_code_from_stdin_test.v @@ -0,0 +1,23 @@ +import os + +const ( + vexe = os.getenv('VEXE') +) + +fn test_vexe_is_set() { + assert vexe != '' +} + +fn test_pipe_to_v_run() ? { + cat_cmd := if os.user_os() == 'windows' { 'type' } else { 'cat' } + tmp_v_file := os.join_path(os.temp_dir(), 'generated_piped_program.v') + os.write_file(tmp_v_file, 'println(1 + 3)\nprintln("hello")\n') ? + assert os.is_file(tmp_v_file) + cmd := '"$cat_cmd" "$tmp_v_file" | "$vexe" run -' + res := os.exec(cmd) ? + // eprintln('>> cmd: $cmd | res: $res') + assert res.exit_code == 0 + assert res.output.trim_space().split('\n') == ['4', 'hello'] + os.rm(tmp_v_file) + assert !os.exists(tmp_v_file) +}