vxx2 / vlib / v / pref / pref_test.v
770 lines · 676 sloc · 26.78 KB · db39f2e0bc5bb212975a164c3536f441d7e9efbe
Raw
1import v.pref
2import v.vmod
3import os
4
5const vexe = @VEXE
6const vroot = os.dir(vexe)
7
8fn test_check_parametes() {
9 // reproducing issue https://github.com/vlang/v/issues/13983
10 _, cmd := pref.parse_args_and_show_errors(['help'], [''], true)
11 // no command found from args
12 assert cmd == ''
13}
14
15fn test_version_flag() {
16 v_ver := vmod.from_file(os.join_path(vroot, 'v.mod'))!.version
17 v_ver_cmd_res := os.execute_opt('${vexe} --version')!.output
18 assert v_ver_cmd_res.starts_with('V ${v_ver}'), v_ver_cmd_res
19
20 v_retry_ver_cmd_res := os.execute_opt('${vexe} retry --version')!.output
21 assert v_retry_ver_cmd_res != v_ver_cmd_res
22
23 v_git_ver_subcmd_res := os.execute_opt('${vexe} retry -- git --version')!.output
24 assert v_git_ver_subcmd_res !in [v_ver_cmd_res, v_retry_ver_cmd_res]
25
26 // Test version / verbosity toggle.
27 assert os.execute_opt('${vexe} -v')!.output == v_ver_cmd_res
28 assert os.execute_opt('${vexe} -cc tcc -v')!.output == v_ver_cmd_res
29
30 example_path := os.join_path(vroot, 'examples', 'hello_world.v')
31 v_verbose_cmd_res := os.execute_opt('${vexe} -v run ${example_path}')!.output
32 assert v_verbose_cmd_res != v_ver_cmd_res
33 assert v_verbose_cmd_res.contains('v.pref.lookup_path:')
34
35 v_verbose_cmd_with_additional_args_res := os.execute_opt('${vexe} -g -v run ${example_path}')!.output
36 assert v_verbose_cmd_with_additional_args_res != v_ver_cmd_res
37 assert v_verbose_cmd_with_additional_args_res.contains('v.pref.lookup_path:')
38}
39
40fn test_cross_compile_keeps_explicit_cc() {
41 target_os := if pref.get_host_os() == .linux { 'macos' } else { 'linux' }
42 custom_cc := 'cosmocc'
43
44 first, _ := pref.parse_args_and_show_errors(['help'], ['', '-cc', custom_cc, '-os', target_os],
45 false)
46 assert first.ccompiler_set_by_flag
47 assert first.ccompiler == custom_cc
48
49 second, _ := pref.parse_args_and_show_errors(['help'],
50 ['', '-os', target_os, '-cc', custom_cc], false)
51 assert second.ccompiler_set_by_flag
52 assert second.ccompiler == custom_cc
53}
54
55fn test_v2_only_flags_are_forwarded_by_v1_wrapper() {
56 target := os.join_path(vroot, 'examples', 'hello_world.v')
57 prefs, command := pref.parse_args_and_show_errors(['help'], [
58 '',
59 '-v2',
60 '-freestanding',
61 '-os',
62 'none',
63 '--skip-builtin',
64 '--skip-type-check',
65 '--debug',
66 '--showcc',
67 '--stats',
68 '-print-parsed-files',
69 '--profile-alloc',
70 '--single-backend',
71 '-O0',
72 '-fhooks',
73 'output,panic,alloc',
74 target,
75 ], false)
76 assert command == target
77 assert prefs.use_v2
78 assert prefs.is_bare
79 assert prefs.build_options.contains('-os none')
80 assert prefs.build_options.contains('-fhooks output,panic,alloc')
81}
82
83fn test_vexe_path_normalizes_relative_env_path() {
84 old_wd := os.getwd()
85 old_vexe := os.getenv_opt('VEXE')
86 test_root := os.join_path(os.vtmp_dir(), 'pref_vexe_path_relative_env_test')
87 os.rmdir_all(test_root) or {}
88 os.mkdir_all(test_root)!
89 fake_vexe := os.join_path(test_root, 'v')
90 os.write_file(fake_vexe, '')!
91 defer {
92 os.chdir(old_wd) or {}
93 if vexe_env := old_vexe {
94 os.setenv('VEXE', vexe_env, true)
95 } else {
96 os.unsetenv('VEXE')
97 }
98 os.rmdir_all(test_root) or {}
99 }
100 os.chdir(test_root)!
101 os.setenv('VEXE', './v', true)
102 expected_vexe := os.real_path(fake_vexe)
103 assert pref.vexe_path() == expected_vexe
104 assert os.getenv('VEXE') == expected_vexe
105}
106
107fn test_mac_is_alias_for_macos() {
108 os_kind := pref.os_from_string('mac') or {
109 assert false, err.msg()
110 return
111 }
112 assert os_kind == .macos
113 assert pref.OS.macos.is_target_of('mac')
114 assert !pref.OS.linux.is_target_of('mac')
115}
116
117fn test_bsd_target_matches_macos_and_bsd_systems() {
118 for os_kind in [pref.OS.macos, .freebsd, .openbsd, .netbsd, .dragonfly] {
119 assert os_kind.is_target_of('bsd')
120 }
121 assert !pref.OS.linux.is_target_of('bsd')
122 assert !pref.OS.windows.is_target_of('bsd')
123}
124
125fn test_disable_explicit_mutability_flag() {
126 target := os.join_path(vroot, 'examples', 'hello_world.v')
127 prefs, _ := pref.parse_args_and_show_errors([], ['-disable-explicit-mutability', target], false)
128 assert prefs.disable_explicit_mutability
129 assert prefs.build_options.contains('-disable-explicit-mutability')
130
131 prefs2, _ := pref.parse_args_and_show_errors([], ['--disable-explicit-mutability', target],
132 false)
133 assert prefs2.disable_explicit_mutability
134 assert prefs2.build_options.contains('--disable-explicit-mutability')
135}
136
137fn test_profile_flag_does_not_consume_direct_compile_target() {
138 target := os.join_path(vroot, 'examples', 'hello_world.v')
139 prefs, command := pref.parse_args_and_show_errors([], ['-profile', target], false)
140 assert command == target
141 assert prefs.path == target
142 assert prefs.is_prof
143 assert prefs.profile_file == '-'
144}
145
146fn test_profile_flag_does_not_consume_run_command() {
147 target := os.join_path(vroot, 'examples', 'hello_world.v')
148 prefs, command := pref.parse_args_and_show_errors([], ['-profile', 'run', target], false)
149 assert command == 'run'
150 assert prefs.path == target
151 assert prefs.is_run
152 assert prefs.is_prof
153 assert prefs.profile_file == '-'
154}
155
156fn test_profile_flag_still_accepts_explicit_output_file() {
157 target := os.join_path(vroot, 'examples', 'hello_world.v')
158 prefs, command := pref.parse_args_and_show_errors([], ['-profile', 'profile.txt', target],
159 false)
160 assert command == target
161 assert prefs.path == target
162 assert prefs.is_prof
163 assert prefs.profile_file == 'profile.txt'
164}
165
166fn new_wasm_preferences() pref.Preferences {
167 return pref.Preferences{
168 backend: .wasm
169 os: .browser
170 arch: .wasm32
171 }
172}
173
174fn new_c_preferences() pref.Preferences {
175 return pref.Preferences{
176 backend: .c
177 os: .linux
178 arch: .amd64
179 }
180}
181
182fn test_c_backend_filters_backend_specific_files() {
183 prefs := new_c_preferences()
184 dir := os.join_path(os.vtmp_dir(), 'c_backend_filters')
185 filtered := prefs.should_compile_filtered_files(dir, [
186 'mod.c.v',
187 'mod.js.v',
188 'mod.v',
189 'mod.wasm.v',
190 ])
191 assert filtered == [
192 os.join_path(dir, 'mod.c.v'),
193 os.join_path(dir, 'mod.v'),
194 ]
195}
196
197fn test_c_backend_skips_modules_with_only_non_c_variants() {
198 prefs := new_c_preferences()
199 filtered := prefs.should_compile_filtered_files('sus', ['sus.js.v', 'sus.wasm.v'])
200 assert filtered.len == 0
201}
202
203fn test_wasm_backend_filters_backend_specific_files() {
204 prefs := new_wasm_preferences()
205 dir := os.join_path(os.vtmp_dir(), 'wasm_backend_filters')
206 filtered := prefs.should_compile_filtered_files(dir, [
207 'mod.c.v',
208 'mod.js.v',
209 'mod.v',
210 'mod.wasm.v',
211 ])
212 assert filtered == [
213 os.join_path(dir, 'mod.v'),
214 os.join_path(dir, 'mod.wasm.v'),
215 ]
216}
217
218fn test_wasm_backend_skips_modules_with_only_c_and_js_variants() {
219 prefs := new_wasm_preferences()
220 filtered := prefs.should_compile_filtered_files('sus', ['sus.c.v', 'sus.js.v'])
221 assert filtered.len == 0
222}
223
224fn filtered_file_names_for_os(os_kind pref.OS, files []string) []string {
225 prefs := pref.Preferences{
226 os: os_kind
227 }
228 dir := os.join_path(os.vtmp_dir(), 'environment_specific_files')
229 mut res := []string{}
230 for file in prefs.should_compile_filtered_files(dir, files) {
231 res << os.base(file)
232 }
233 return res
234}
235
236fn test_bsd_specific_files_are_filtered_by_target_os() {
237 for os_kind in [pref.OS.macos, .freebsd, .openbsd, .netbsd, .dragonfly] {
238 assert filtered_file_names_for_os(os_kind, ['mod_bsd.c.v', 'mod_bsd.v']) == [
239 'mod_bsd.c.v',
240 'mod_bsd.v',
241 ]
242 }
243 assert filtered_file_names_for_os(.linux, ['mod_bsd.c.v', 'mod_bsd.v']).len == 0
244 assert filtered_file_names_for_os(.windows, ['mod_bsd.c.v', 'mod_bsd.v']).len == 0
245}
246
247fn test_bsd_specific_files_prefer_more_specific_variants() {
248 mut files := [
249 'main.v',
250 'something_default.c.v',
251 'something_windows.c.v',
252 ]
253 assert filtered_file_names_for_os(.freebsd, files) == ['main.v', 'something_default.c.v']
254
255 files << 'something_nix.c.v'
256 assert filtered_file_names_for_os(.freebsd, files) == ['main.v', 'something_nix.c.v']
257
258 files << 'something_bsd.c.v'
259 assert filtered_file_names_for_os(.freebsd, files) == ['main.v', 'something_bsd.c.v']
260
261 files << 'something_freebsd.c.v'
262 assert filtered_file_names_for_os(.freebsd, files) == ['main.v', 'something_freebsd.c.v']
263}
264
265fn test_bsd_specific_files_prefer_darwin_on_macos() {
266 files := [
267 'main.v',
268 'something_nix.c.v',
269 'something_bsd.v',
270 'something_darwin.v',
271 ]
272 assert filtered_file_names_for_os(.macos, files) == ['main.v', 'something_darwin.v']
273}
274
275fn test_explicit_gc_mode_is_forwarded_to_build_module() {
276 target := os.join_path(vroot, 'examples', 'hello_world.v')
277 for gc_mode in ['none', 'boehm', 'boehm_full', 'boehm_incr', 'boehm_full_opt', 'boehm_incr_opt',
278 'boehm_leak'] {
279 prefs, _ := pref.parse_args_and_show_errors([], ['-usecache', '-gc', gc_mode, target],
280 false)
281 assert prefs.build_options.contains('-gc ${gc_mode}')
282 }
283}
284
285fn test_v_compiler_targets_default_to_no_gc() {
286 for target in [
287 os.join_path(vroot, 'cmd', 'v'),
288 os.join_path(vroot, 'cmd', 'v') + os.path_separator,
289 os.join_path(vroot, 'cmd', 'v', 'v.v'),
290 os.join_path(vroot, 'cmd', 'v2'),
291 os.join_path(vroot, 'cmd', 'v2') + os.path_separator,
292 os.join_path(vroot, 'cmd', 'v2', 'v2.v'),
293 os.join_path(vroot, 'cmd', 'tools', 'vfmt.v'),
294 ] {
295 prefs, _ := pref.parse_args_and_show_errors([], ['', target], false)
296 assert prefs.gc_mode == .no_gc
297 assert prefs.build_options.join(' ').contains('-gc none')
298 }
299}
300
301fn test_v_compiler_targets_keep_explicit_gc_selection() {
302 target := os.join_path(vroot, 'cmd', 'v2', 'v2.v')
303 prefs, _ := pref.parse_args_and_show_errors([], ['', '-gc', 'boehm', target], false)
304 assert prefs.gc_mode == .boehm_full_opt
305 assert prefs.build_options.contains('-gc boehm')
306}
307
308fn test_cross_compile_defaults_windows_to_the_cross_compiler_arch() {
309 if pref.get_host_os() == .windows {
310 return
311 }
312 target := os.join_path(vroot, 'examples', 'hello_world.v')
313 prefs, _ := pref.parse_args_and_show_errors([], ['', '-os', 'windows', target], false)
314 assert prefs.arch == .amd64
315 assert prefs.ccompiler == 'x86_64-w64-mingw32-gcc'
316}
317
318fn test_cross_compile_windows_m32_uses_i386_arch_and_compiler() {
319 if pref.get_host_os() == .windows {
320 return
321 }
322 target := os.join_path(vroot, 'examples', 'hello_world.v')
323 prefs, _ := pref.parse_args_and_show_errors([], ['', '-os', 'windows', '-m32', target], false)
324 assert !prefs.m64
325 assert prefs.arch == .i386
326 assert prefs.ccompiler == 'i686-w64-mingw32-gcc'
327 assert prefs.build_options.contains('-m32')
328}
329
330fn test_cross_compile_defaults_linux_to_amd64() {
331 if pref.get_host_os() == .linux {
332 return
333 }
334 target := os.join_path(vroot, 'examples', 'hello_world.v')
335 prefs, _ := pref.parse_args_and_show_errors([], ['', '-os', 'linux', target], false)
336 assert prefs.arch == .amd64
337 assert prefs.ccompiler == 'clang'
338}
339
340fn test_cross_compile_infers_android_arch_from_vcross_compiler_name() {
341 target := os.join_path(vroot, 'examples', 'hello_world.v')
342 old_cross_compiler := os.getenv('VCROSS_COMPILER_NAME')
343 defer {
344 os.setenv('VCROSS_COMPILER_NAME', old_cross_compiler, true)
345 }
346 for compiler_name, expected_arch in {
347 'aarch64-linux-android21-clang': pref.Arch.arm64
348 'armv7a-linux-androideabi21-clang': pref.Arch.arm32
349 'i686-linux-android21-clang': pref.Arch.i386
350 'x86_64-linux-android21-clang': pref.Arch.amd64
351 } {
352 os.setenv('VCROSS_COMPILER_NAME', compiler_name, true)
353 prefs, _ := pref.parse_args_and_show_errors([], ['', '-os', 'android', target], false)
354 assert prefs.arch == expected_arch
355 assert prefs.ccompiler == compiler_name
356 }
357}
358
359fn test_musl_still_defaults_to_boehm_gc() {
360 // Regression test for https://github.com/vlang/v/issues/27090 .
361 // Alpine/musl programs must keep the boehm GC by default; otherwise
362 // long-running allocations grow without bound (see the issue's repro).
363 target := os.join_path(vroot, 'examples', 'hello_world.v')
364 prefs, _ := pref.parse_args_and_show_errors([], ['', '-musl', target], false)
365 assert prefs.is_musl
366 assert prefs.gc_mode == .boehm_full_opt
367}
368
369fn test_prealloc_defaults_to_no_gc() {
370 target := os.join_path(vroot, 'examples', 'hello_world.v')
371 prefs, _ := pref.parse_args_and_show_errors([], ['', '-prealloc', target], false)
372 assert prefs.prealloc
373 assert prefs.gc_mode == .no_gc
374}
375
376fn test_prealloc_overrides_explicit_gc_selection() {
377 target := os.join_path(vroot, 'examples', 'hello_world.v')
378 prefs, _ := pref.parse_args_and_show_errors([], ['', '-gc', 'boehm', '-prealloc', target],
379 false)
380 assert prefs.prealloc
381 assert prefs.gc_mode == .no_gc
382 assert 'gcboehm' !in prefs.compile_defines
383 assert prefs.build_options.join(' ').contains('-gc none')
384}
385
386fn test_no_gc_thread_local_alloc_prefers_source_bundled_boehm() {
387 target := os.join_path(vroot, 'examples', 'hello_world.v')
388 prefs, _ := pref.parse_args_and_show_errors([], ['', '-d', 'no_gc_thread_local_alloc', target],
389 false)
390 assert prefs.gc_mode == .boehm_full_opt
391 assert 'no_gc_thread_local_alloc' in prefs.compile_defines_all
392 assert 'use_bundled_libgc' in prefs.compile_defines_all
393 assert !prefs.ccompiler.contains('tcc')
394 assert prefs.build_options.contains('-d use_bundled_libgc')
395}
396
397fn test_no_gc_thread_local_alloc_does_not_force_boehm_with_gc_none() {
398 target := os.join_path(vroot, 'examples', 'hello_world.v')
399 prefs, _ := pref.parse_args_and_show_errors([], ['', '-gc', 'none', '-d',
400 'no_gc_thread_local_alloc', target], false)
401 assert prefs.gc_mode == .no_gc
402 assert 'no_gc_thread_local_alloc' in prefs.compile_defines_all
403 assert 'use_bundled_libgc' !in prefs.compile_defines_all
404}
405
406fn test_no_gc_thread_local_alloc_keeps_explicit_dynamic_boehm() {
407 target := os.join_path(vroot, 'examples', 'hello_world.v')
408 prefs, _ := pref.parse_args_and_show_errors([], ['', '-d', 'dynamic_boehm', '-d',
409 'no_gc_thread_local_alloc', target], false)
410 assert prefs.gc_mode == .boehm_full_opt
411 assert 'dynamic_boehm' in prefs.compile_defines_all
412 assert 'use_bundled_libgc' !in prefs.compile_defines_all
413}
414
415fn stale_windows_gc_prefs(gc_set_by_flag bool) pref.Preferences {
416 mut prefs := pref.Preferences{
417 os: .windows
418 ccompiler_type: .msvc
419 gc_mode: .boehm_full_opt
420 gc_set_by_flag: gc_set_by_flag
421 compile_defines: ['gcboehm', 'gcboehm_full', 'gcboehm_opt', 'custom']
422 compile_defines_all: ['gcboehm', 'gcboehm_full', 'gcboehm_opt', 'custom']
423 compile_values: map[string]string{}
424 build_options: ['-prod', '-d gcboehm', '-d gcboehm_full', '-d gcboehm_opt']
425 }
426 prefs.compile_values['gcboehm'] = 'true'
427 prefs.compile_values['gcboehm_full'] = 'true'
428 prefs.compile_values['gcboehm_opt'] = 'true'
429 prefs.compile_values['custom'] = 'true'
430 return prefs
431}
432
433fn test_windows_msvc_gc_defaults_are_cleared_after_compiler_resolution() {
434 mut prefs := stale_windows_gc_prefs(false)
435
436 prefs.normalize_gc_defaults_for_resolved_ccompiler()
437
438 assert prefs.gc_mode == .no_gc
439 assert prefs.build_options == ['-prod', '-gc', 'none']
440 assert prefs.compile_defines == ['custom']
441 assert prefs.compile_defines_all == ['custom']
442 assert prefs.compile_values == {
443 'custom': 'true'
444 }
445}
446
447fn test_windows_msvc_gc_defaults_keep_explicit_gc_selection() {
448 mut prefs := stale_windows_gc_prefs(true)
449 prefs.build_options = ['-prod', '-gc', 'boehm', '-d gcboehm', '-d gcboehm_full', '-d gcboehm_opt']
450
451 prefs.normalize_gc_defaults_for_resolved_ccompiler()
452
453 assert prefs.gc_mode == .boehm_full_opt
454 assert prefs.build_options == ['-prod', '-gc', 'boehm', '-d gcboehm', '-d gcboehm_full',
455 '-d gcboehm_opt']
456 assert prefs.compile_defines == ['gcboehm', 'gcboehm_full', 'gcboehm_opt', 'custom']
457 assert prefs.compile_defines_all == ['gcboehm', 'gcboehm_full', 'gcboehm_opt', 'custom']
458 assert prefs.compile_values == {
459 'custom': 'true'
460 'gcboehm': 'true'
461 'gcboehm_full': 'true'
462 'gcboehm_opt': 'true'
463 }
464}
465
466fn test_m32_sets_i386_arch_when_not_explicitly_set() {
467 target := os.join_path(vroot, 'examples', 'hello_world.v')
468 prefs, _ := pref.parse_args_and_show_errors([], ['', '-m32', target], false)
469 assert !prefs.m64
470 assert prefs.arch == .i386
471 assert prefs.build_options.contains('-m32')
472}
473
474fn test_m32_does_not_override_explicit_arch() {
475 target := os.join_path(vroot, 'examples', 'hello_world.v')
476 prefs, _ := pref.parse_args_and_show_errors([], ['', '-arch', 'amd64', '-m32', target], false)
477 assert !prefs.m64
478 assert prefs.arch == .amd64
479 assert prefs.build_options.contains('-m32')
480}
481
482fn test_v_cmds_and_flags() {
483 build_cmd_res := os.execute('${vexe} build ${vroot}/examples/hello_world.v')
484 assert build_cmd_res.output.trim_space() == 'Use `v ${vroot}/examples/hello_world.v` instead.'
485
486 too_many_targets_res :=
487 os.execute('${vexe} ${vroot}/examples/hello_world.v ${vroot}/examples/fizz_buzz.v')
488 assert too_many_targets_res.output.trim_space() == 'Too many targets. Specify just one target: <target.v|target_directory>.'
489
490 unknown_arg_res := os.execute('${vexe} -xyz')
491 assert unknown_arg_res.output.trim_space() == 'Unknown argument `-xyz`'
492
493 unknown_arg_for_cmd_res := os.execute('${vexe} build-module -xyz ${vroot}/vlib/math')
494 assert unknown_arg_for_cmd_res.output.trim_space() == 'Unknown argument `-xyz` for command `build-module`'
495
496 v2_only_flag_without_v2_res :=
497 os.execute('${vexe} --skip-builtin ${vroot}/examples/hello_world.v')
498 assert v2_only_flag_without_v2_res.exit_code == 1
499 assert v2_only_flag_without_v2_res.output.trim_space() == 'Unknown argument `--skip-builtin`'
500
501 v2_hooks_without_v2_res := os.execute('${vexe} -fhooks output ${vroot}/examples/hello_world.v')
502 assert v2_hooks_without_v2_res.exit_code == 1
503 assert v2_hooks_without_v2_res.output.trim_space() == 'Unknown argument `-fhooks`'
504
505 v2_os_none_without_v2_res := os.execute('${vexe} -os none ${vroot}/examples/hello_world.v')
506 assert v2_os_none_without_v2_res.exit_code == 1
507 assert v2_os_none_without_v2_res.output.trim_space() == 'unknown operating system target `none`'
508
509 late_v2_skip_builtin_res :=
510 os.execute('${vexe} --skip-builtin run ${vroot}/examples/hello_world.v -v2')
511 assert late_v2_skip_builtin_res.exit_code == 1
512 assert late_v2_skip_builtin_res.output.trim_space() == 'Unknown argument `--skip-builtin`'
513
514 late_v2_hooks_res :=
515 os.execute('${vexe} -fhooks output run ${vroot}/examples/hello_world.v -v2')
516 assert late_v2_hooks_res.exit_code == 1
517 assert late_v2_hooks_res.output.trim_space() == 'Unknown argument `-fhooks`'
518
519 eval_removed_message := 'use v -v2 -eval file.v'
520 eval_flag_res := os.execute('${vexe} -eval ${vroot}/examples/hello_world.v')
521 assert eval_flag_res.exit_code == 1
522 assert eval_flag_res.output.trim_space() == eval_removed_message
523
524 eval_backend_res := os.execute('${vexe} -backend eval ${vroot}/examples/hello_world.v')
525 assert eval_backend_res.exit_code == 1
526 assert eval_backend_res.output.trim_space() == eval_removed_message
527
528 interpret_flag_res := os.execute('${vexe} -interpret ${vroot}/examples/hello_world.v')
529 assert interpret_flag_res.exit_code == 1
530 assert interpret_flag_res.output.trim_space() == eval_removed_message
531
532 interpret_command_res := os.execute('${vexe} interpret ${vroot}/examples/hello_world.v')
533 assert interpret_command_res.exit_code == 1
534 assert interpret_command_res.output.trim_space() == eval_removed_message
535
536 no_run_files_res := os.execute('${vexe} run')
537 assert no_run_files_res.output.trim_space() == 'v run: no v files listed'
538
539 no_bm_files_res := os.execute('${vexe} build-module')
540 assert no_bm_files_res.output.trim_space() == 'v build-module: no module specified'
541}
542
543fn test_build_command_compiles_vsh_without_running_it() {
544 test_dir := os.join_path(os.vtmp_dir(), 'v_pref_build_vsh_${os.getpid()}')
545 os.rmdir_all(test_dir) or {}
546 os.mkdir_all(test_dir)!
547 defer {
548 os.rmdir_all(test_dir) or {}
549 }
550 script_path := os.join_path(test_dir, 'build_only.vsh')
551 marker_path := os.join_path(test_dir, 'marker.txt')
552 mut exe_path := os.join_path(test_dir, 'build_only')
553 $if windows {
554 exe_path += '.exe'
555 }
556 os.write_file(script_path, "import os
557
558fn main() {
559 marker_path := os.join_path(@DIR, 'marker.txt')
560 os.write_file(marker_path, 'ran') or { panic(err) }
561 println('ran')
562}
563")!
564 build_res := os.execute('${os.quoted_path(vexe)} -silent build ${os.quoted_path(script_path)}')
565 assert build_res.exit_code == 0, build_res.output
566 assert build_res.output == ''
567 assert !os.exists(marker_path)
568 assert os.is_file(exe_path)
569
570 run_res := os.execute(os.quoted_path(exe_path))
571 assert run_res.exit_code == 0, run_res.output
572 assert run_res.output.trim_space() == 'ran'
573 assert os.read_file(marker_path)! == 'ran'
574}
575
576const tfile = os.join_path(os.vtmp_dir(), 'unknown_options_output.c')
577
578fn test_unknown_option_flags_no_run() {
579 os.chdir(os.dir(@VEXE))!
580 os.rm(tfile) or {}
581
582 res1 :=
583 os.execute('${os.quoted_path(@VEXE)} -o ${os.quoted_path(tfile)} examples/hello_world.v --an-unknown-option')
584 assert res1.exit_code == 1, res1.output
585 assert res1.output.starts_with('Unknown argument')
586 assert res1.output.contains('--an-unknown-option')
587 assert !os.exists(tfile)
588
589 res2 :=
590 os.execute('${os.quoted_path(@VEXE)} -o ${os.quoted_path(tfile)} --an-unknown-option examples/hello_world.v')
591 assert res2.exit_code == 1, res2.output
592 assert res2.output.starts_with('Unknown argument')
593 assert res2.output.contains('--an-unknown-option')
594 assert !os.exists(tfile)
595}
596
597fn test_unknown_option_flags_with_run() {
598 res_run_o :=
599 os.execute('${os.quoted_path(@VEXE)} -o ${os.quoted_path(tfile)} run examples/hello_world.v --an-unknown-option')
600 assert res_run_o.exit_code == 0, res_run_o.output
601 assert res_run_o.output == '' // because of -o, there should not be an actual run, since compilation stopped after generating the .c file
602 assert os.exists(tfile)
603 os.rm(tfile) or {}
604
605 res_run_no_o_unknown_before_run :=
606 os.execute('${os.quoted_path(@VEXE)} --an-unknown-option run examples/hello_world.v ')
607 assert res_run_no_o_unknown_before_run.exit_code == 1, res_run_no_o_unknown_before_run.output
608 assert res_run_no_o_unknown_before_run.output.starts_with('Unknown argument')
609 assert res_run_no_o_unknown_before_run.output.contains('--an-unknown-option')
610 assert !os.exists(tfile)
611
612 res_run_no_o :=
613 os.execute('${os.quoted_path(@VEXE)} run examples/hello_world.v --an-unknown-option')
614 assert res_run_no_o.exit_code == 0, res_run_no_o.output
615 assert res_run_no_o.output.trim_space() == 'Hello, World!'
616 assert !os.exists(tfile)
617}
618
619fn test_missing_explicit_ccompiler_reports_error() {
620 target := os.join_path(vroot, 'examples', 'hello_world.v')
621 missing_cc := 'missing_compiler_17126_for_pref_test'
622 output := os.join_path(os.vtmp_dir(), 'missing_explicit_ccompiler_output')
623 mut expected_output := output
624 $if windows {
625 expected_output += '.exe'
626 }
627 os.rm(expected_output) or {}
628 res :=
629 os.execute('${os.quoted_path(@VEXE)} -cc ${missing_cc} -o ${os.quoted_path(output)} ${os.quoted_path(target)}')
630 assert res.exit_code != 0
631 assert res.output.contains(missing_cc), res.output
632 assert res.output.to_lower().contains('not found') || res.output.to_lower().contains('missing'), res.output
633
634 assert !os.exists(expected_output)
635}
636
637fn test_generate_c_project_flag_parsing() {
638 target := os.join_path(vroot, 'examples', 'hello_world.v')
639 prefs, _ := pref.parse_args_and_show_errors([], ['-generate-c-project', 'cproj', target], false)
640 assert prefs.generate_c_project == 'cproj'
641 assert prefs.use_cache == false
642}
643
644fn test_generate_c_project_creates_build_files() {
645 output_dir := os.join_path(os.vtmp_dir(), 'v_generate_c_project_json')
646 os.rmdir_all(output_dir) or {}
647 defer {
648 os.rmdir_all(output_dir) or {}
649 }
650 target := os.join_path(vroot, 'examples', 'json.v')
651 cmd := '${os.quoted_path(vexe)} -generate-c-project ${os.quoted_path(output_dir)} ${os.quoted_path(target)}'
652 res := os.execute(cmd)
653 assert res.exit_code == 0, res.output
654 for rel_path in ['json.c', 'build_command.txt', 'build.sh', 'build.bat', 'Makefile'] {
655 assert os.is_file(os.join_path(output_dir, rel_path))
656 }
657 build_command := os.read_file(os.join_path(output_dir, 'build_command.txt')) or { panic(err) }
658 generated_c_path := os.join_path(output_dir, 'json.c')
659 normalized_build_command := normalized_build_path(build_command)
660 assert normalized_build_command.contains(normalized_build_path(generated_c_path))
661 || normalized_build_command.contains(normalized_build_path(os.short_path(generated_c_path)))
662 assert build_command.contains('cJSON.c')
663 assert !build_command.contains('.tmp.c')
664 assert !build_command.contains('.module.')
665}
666
667fn normalized_build_path(path string) string {
668 mut normalized := path.replace('\\', '/')
669 for normalized.contains('//') {
670 normalized = normalized.replace('//', '/')
671 }
672 return normalized
673}
674
675fn test_output_flag_accepts_directory_path() {
676 output_dir := os.join_path(os.vtmp_dir(), 'v_output_flag_directory')
677 os.rmdir_all(output_dir) or {}
678 defer {
679 os.rmdir_all(output_dir) or {}
680 }
681 target := os.join_path(vroot, 'examples', 'hello_world.v')
682 output_arg := output_dir + os.path_separator
683 res :=
684 os.execute('${os.quoted_path(vexe)} -o ${os.quoted_path(output_arg)} ${os.quoted_path(target)}')
685 assert res.exit_code == 0, res.output
686 assert os.is_dir(output_dir)
687 mut expected_output := os.join_path(output_dir, 'hello_world')
688 $if windows {
689 expected_output += '.exe'
690 }
691 assert os.is_file(expected_output)
692}
693
694fn test_tcc_shared_builds_disable_backtraces() {
695 mut shared_prefs := &pref.Preferences{
696 path: 'libfoo.v'
697 is_shared: true
698 ccompiler: 'tinyc'
699 }
700 shared_prefs.fill_with_defaults()
701 assert 'no_backtrace' in shared_prefs.compile_defines_all
702
703 mut regular_prefs := &pref.Preferences{
704 path: 'main.v'
705 ccompiler: 'tinyc'
706 }
707 regular_prefs.fill_with_defaults()
708 assert 'no_backtrace' !in regular_prefs.compile_defines_all
709}
710
711fn test_bsd_tinyc_defaults_to_openssl() {
712 mut bsd_tinyc_prefs := &pref.Preferences{
713 path: 'main.v'
714 os: .freebsd
715 ccompiler: 'tinyc'
716 ccompiler_set_by_flag: true
717 }
718 bsd_tinyc_prefs.fill_with_defaults()
719 assert 'use_openssl' in bsd_tinyc_prefs.compile_defines
720 assert 'use_openssl' in bsd_tinyc_prefs.compile_defines_all
721
722 mut bsd_clang_prefs := &pref.Preferences{
723 path: 'main.v'
724 os: .freebsd
725 ccompiler: 'clang'
726 }
727 bsd_clang_prefs.fill_with_defaults()
728 assert 'use_openssl' !in bsd_clang_prefs.compile_defines_all
729}
730
731fn test_late_resolved_tcc_shared_builds_disable_backtraces() {
732 mut shared_prefs := &pref.Preferences{
733 path: 'libfoo.v'
734 is_shared: true
735 ccompiler: 'gcc'
736 }
737 shared_prefs.fill_with_defaults()
738 assert 'no_backtrace' !in shared_prefs.compile_defines_all
739
740 shared_prefs.ccompiler_type = .tinyc
741 shared_prefs.normalize_gc_defaults_for_resolved_ccompiler()
742
743 assert 'no_backtrace' in shared_prefs.compile_defines_all
744 assert shared_prefs.build_options.contains('-d no_backtrace')
745}
746
747fn test_wayland_only_linux_session_surfaces_a_v_error_for_gg() {
748 if os.user_os() == 'windows' {
749 return
750 }
751 pid := os.getpid()
752 test_dir := os.join_path(os.vtmp_dir(), 'v_issue_18030_gg_wayland_${pid}')
753 source_path := os.join_path(test_dir, 'main.v')
754 exe_path := os.join_path(test_dir, 'app')
755 source := 'import gg as _\n\nfn main() {}\n'
756 os.mkdir_all(test_dir) or { panic(err) }
757 os.write_file(source_path, source) or { panic(err) }
758 defer {
759 os.rmdir_all(test_dir) or {}
760 }
761 cmd := 'DISPLAY= WAYLAND_DISPLAY=wayland-0 XDG_SESSION_TYPE=wayland ${os.quoted_path(vexe)} -os linux -o ${os.quoted_path(exe_path)} ${os.quoted_path(source_path)}'
762 res := os.execute(cmd)
763 output := res.output.replace('\r', '')
764 if res.exit_code == 0 {
765 eprintln('> failed command: ${cmd}')
766 }
767 assert res.exit_code != 0
768 assert output.contains('Wayland-only Linux session without `-d sokol_wayland`')
769 assert !output.contains('C error. This should never happen.')
770}
771