v / cmd / tools / vbug.v
184 lines · 164 sloc · 5.18 KB · 4ab6d9031022fbcfd59a621ee64600abe13053dd
Raw
1import os
2import term
3import readline
4import vbugreport
5
6fn elog(msg string) {
7 eprintln(term.ecolorize(term.gray, msg))
8}
9
10fn olog(msg string) {
11 println(term.colorize(term.green, msg))
12}
13
14fn vversion() string {
15 vexe := os.getenv('VEXE')
16 return os.execute('${os.quoted_path(vexe)} version').output.trim_space()
17}
18
19// get output from `v doctor`
20fn get_vdoctor_output(is_verbose bool) string {
21 vexe := os.getenv('VEXE')
22 verbose_flag := if is_verbose { '-v' } else { '' }
23 result := os.execute('${os.quoted_path(vexe)} ${verbose_flag} doctor')
24 if result.exit_code != 0 {
25 elog('> unable to get `v doctor` output: ${result.output}')
26 return ''
27 }
28 return result.output
29}
30
31fn runv(label string, user_cmd string) os.Result {
32 mut result := os.Result{}
33 elog('> ${label} using: ${term.ecolorize(term.magenta, user_cmd)}')
34 result = os.execute(user_cmd)
35 print(result.output)
36 return result
37}
38
39// get output from `./v -g -o vdbg cmd/v && ./vdbg -user_args run file.v`
40fn get_v_build_output(is_verbose bool, is_yes bool, file_path string, user_args string, generated_file string) string {
41 mut result := os.Result{}
42 mut vexe := os.getenv('VEXE')
43
44 // prepare a V compiler with -g to have better backtraces if possible
45 wd := os.getwd()
46 vroot := @VMODROOT
47 os.chdir(vroot) or {}
48 verbose_flag := if is_verbose { '-v' } else { '' }
49 vdbg_path := $if windows { '${vroot}/vdbg.exe' } $else { '${vroot}/vdbg' }
50 vdbg_compilation_cmd := '${os.quoted_path(vexe)} ${verbose_flag} -g -o ${os.quoted_path(vdbg_path)} cmd/v'
51 result = runv('Prepare vdbg', vdbg_compilation_cmd)
52 os.chdir(wd) or {}
53
54 if result.exit_code == 0 {
55 vexe = vdbg_path
56 } else {
57 elog('> unable to compile V in debug mode: ${result.output}\ncommand: ${vdbg_compilation_cmd}\n')
58 }
59
60 result = runv('Compile',
61 '${os.quoted_path(vexe)} ${verbose_flag} ${user_args} ${os.quoted_path(file_path)}')
62 defer {
63 os.rm(vdbg_path) or {
64 if is_verbose {
65 elog('> unable to delete `vdbg`: ${err}')
66 }
67 }
68 }
69 if result.exit_code == 0 {
70 real_generated_file := os.real_path(generated_file)
71 defer(fn) {
72 os.rm(generated_file) or {
73 if is_verbose {
74 elog('> unable to delete generated file: ${err}')
75 }
76 }
77 }
78 run := is_yes
79 || ask('It looks like the compilation went well, do you want to run the file?')
80 if run {
81 result = runv('Run', real_generated_file)
82 if result.exit_code == 0 && !is_yes {
83 elog('> The file ran correctly as well.')
84 confirm_or_exit('Are you sure you want to continue?')
85 }
86 }
87 }
88 return result.output
89}
90
91fn ask(msg string) bool {
92 prompt := os.input_opt(term.colorize(term.bright_white, '${msg} [Y/n] ')) or { 'y' }
93 return prompt == '' || prompt[0].ascii_str().to_lower() != 'n'
94}
95
96fn confirm_or_exit(msg string) {
97 if !ask(msg) {
98 exit(1)
99 }
100}
101
102fn main() {
103 unbuffer_stdout()
104 mut compiler_args := []string{}
105 mut file_path := ''
106 is_verbose := '-v' in os.args
107 is_yes := '-y' in os.args
108
109 for arg in os.args[1..] {
110 if arg == 'bug' {
111 continue
112 }
113 if arg.ends_with('.v') || arg.ends_with('.vsh') || arg.ends_with('.vv') {
114 if file_path != '' {
115 elog('> v bug: only one V file can be submitted')
116 exit(1)
117 }
118 file_path = arg
119 } else {
120 if arg !in ['-y', '-v'] {
121 compiler_args << arg
122 }
123 }
124 }
125
126 if file_path == '' {
127 elog('> v bug: no v file listed to report')
128 exit(1)
129 }
130
131 os.unsetenv('VCOLORS')
132 // collect error information
133 // output from `v doctor`
134 vdoctor_output := get_vdoctor_output(is_verbose)
135 // file content
136 file_content := os.read_file(file_path) or {
137 elog('> unable to get file "${file_path}" content: ${err}')
138 ''
139 }
140
141 user_args := compiler_args.join(' ')
142 mut generated_file := file_path.all_before_last('.')
143 if os.user_os() == 'windows' {
144 generated_file += '.exe'
145 }
146 build_output := get_v_build_output(is_verbose, is_yes, file_path, user_args, generated_file)
147
148 // ask the user if he wants to submit even after an error
149 if !is_yes && (vdoctor_output == '' || file_content == '' || build_output == '') {
150 elog('> Error while retrieving the information.')
151 confirm_or_exit('Do you want to continue?')
152 }
153
154 expected_result := readline.read_line('What did you expect to see? ') or {
155 // Ctrl-C was pressed
156 elog('\nCanceled')
157 exit(1)
158 }
159 // open prefilled issue creation page, or print link as a fallback
160
161 if !is_yes && vdoctor_output.contains('behind V master') {
162 olog('> It looks like your installation of V is outdated.')
163 olog('> We advise you to run `v up` before submitting an issue.')
164 confirm_or_exit('Are you sure you want to continue?')
165 }
166
167 report := vbugreport.new_bug_report(file_path, generated_file, user_args, expected_result,
168 vversion(), vdoctor_output, file_content, build_output)
169 delivery := vbugreport.prepare_bug_report_delivery(report, file_path)
170 if delivery.mode == .local_report {
171 os.write_file(delivery.local_report_path, delivery.local_report_body) or {
172 elog('> unable to write the bug report file `${delivery.local_report_path}`: ${err}')
173 }
174 elog('> The full bug report is too large to prefill on GitHub.')
175 elog('> The full report was saved to `${delivery.local_report_path}`.')
176 elog('> Open the bug form below, then paste the saved markdown sections or attach the file:')
177 }
178 os.open_uri(delivery.uri) or {
179 if is_verbose {
180 elog(err.str())
181 }
182 olog(delivery.uri)
183 }
184}
185