v2 / vlib / v / builder / cc_test.v
728 lines · 644 sloc · 26.28 KB · 7cd481e4c339f28f69ad5b947722243d87f2b8fc
Raw
1module builder
2
3import os
4import v.pref
5
6fn test_ccompiler_is_available_with_existing_absolute_path() {
7 assert ccompiler_is_available(@VEXE)
8}
9
10fn test_ccompiler_is_available_with_missing_compiler() {
11 assert !ccompiler_is_available('missing_compiler_17126_for_builder_test')
12}
13
14fn test_c_error_looks_like_cpp_header_with_clang_style_output() {
15 clang_output := "error: unknown type name 'namespace'\nerror: expected ';' after top level declarator"
16 assert c_error_looks_like_cpp_header(clang_output)
17}
18
19fn test_c_error_looks_like_cpp_header_with_source_excerpt() {
20 gcc_output := '/usr/include/H5File.h:18: error: \';\' expected (got "H5")\n| namespace H5 {\n'
21 assert c_error_looks_like_cpp_header(gcc_output)
22}
23
24fn test_c_error_looks_like_cpp_header_with_imgui_style_operator_overload() {
25 imgui_output := "/tmp/fake_imgui.h:3:16: error: 'operator' declared as array of functions of type 'float (unsigned long)'\n 3 | float operator[](unsigned long idx) const { return (&x)[idx]; }\n"
26 assert c_error_looks_like_cpp_header(imgui_output)
27}
28
29fn test_c_error_looks_like_cpp_header_with_class_keyword() {
30 class_output := "/usr/include/foo.hpp:4:1: error: unknown type name 'class'\n 4 | class Foo {\n"
31 assert c_error_looks_like_cpp_header(class_output)
32}
33
34fn test_c_error_looks_like_cpp_header_with_regular_c_error() {
35 c_output := "error: unknown type name 'my_missing_type'"
36 assert !c_error_looks_like_cpp_header(c_output)
37}
38
39fn test_c_output_suggests_missing_sokol_shader_symbol_with_clang_style_output() {
40 c_output := [
41 "/tmp/v_501/simple_shader.tmp.c:21250:43: error: use of undeclared identifier 'ATTR_vs_aposition'",
42 ' pipeline_desc.layout.attrs[v_fixed_index(ATTR_vs_aposition, 16)].format = 1;',
43 ].join('\n')
44 assert c_output_suggests_missing_sokol_shader_symbol(c_output) == 'ATTR_vs_aposition'
45}
46
47fn test_c_output_suggests_missing_sokol_shader_symbol_with_gcc_style_output() {
48 c_output := [
49 "/tmp/v_501/simple_shader.tmp.c:21250:43: error: 'SLOT_fs_params' undeclared (first use in this function)",
50 ' gfx_apply_uniforms(SLOT_fs_params);',
51 ].join('\n')
52 assert c_output_suggests_missing_sokol_shader_symbol(c_output) == 'SLOT_fs_params'
53}
54
55fn test_c_output_suggests_missing_sokol_shader_symbol_ignores_regular_c_errors() {
56 c_output := "error: use of undeclared identifier 'my_missing_type'"
57 assert c_output_suggests_missing_sokol_shader_symbol(c_output) == ''
58}
59
60fn test_macos_compile_args_do_not_force_version_min_by_default() {
61 compile_args := macos_compile_args(['-os', 'macos', '-cc', 'clang', hello_world_example()])
62 assert macos_version_min_flags(compile_args) == []string{}
63}
64
65fn test_macos_compile_args_keep_explicit_cflag_version_min() {
66 compile_args := macos_compile_args([
67 '-os',
68 'macos',
69 '-cc',
70 'clang',
71 '-cflags',
72 '-mmacosx-version-min=11.0',
73 hello_world_example(),
74 ])
75 assert macos_version_min_flags(compile_args) == ['-mmacosx-version-min=11.0']
76}
77
78fn test_macos_compile_args_append_macosx_version_min_after_cflags() {
79 compile_args := macos_compile_args([
80 '-os',
81 'macos',
82 '-cc',
83 'clang',
84 '-cflags',
85 '-mmacosx-version-min=10.7',
86 '-macosx-version-min',
87 '11.0',
88 hello_world_example(),
89 ])
90 assert macos_version_min_flags(compile_args) == [
91 '-mmacosx-version-min=10.7',
92 '-mmacosx-version-min=11.0',
93 ]
94}
95
96fn test_cc_from_string_detects_cl_as_msvc() {
97 assert pref.cc_from_string('cl') == .msvc
98 assert pref.cc_from_string('C:/Program Files/Microsoft Visual Studio/cl.exe') == .msvc
99}
100
101fn test_cc_from_string_detects_tiny_gcc_as_tinyc() {
102 assert pref.cc_from_string('tiny_gcc') == .tinyc
103 assert pref.cc_from_string('/usr/local/bin/tiny_gcc') == .tinyc
104}
105
106fn test_ccompiler_type_from_version_output_detects_openbsd_clang() {
107 detected := ccompiler_type_from_version_output('OpenBSD clang version 16.0.6') or { panic(err) }
108 assert detected == .clang
109}
110
111fn test_ccompiler_type_from_version_output_detects_tcc() {
112 detected := ccompiler_type_from_version_output('tcc version 0.9.27 (x86_64 Linux)') or {
113 panic(err)
114 }
115 assert detected == .tinyc
116}
117
118fn test_ccompiler_type_from_name_detects_tiny_gcc() {
119 detected := ccompiler_type_from_name('/opt/tiny_gcc') or { panic(err) }
120 assert detected == .tinyc
121}
122
123fn test_ccompiler_type_from_version_output_detects_tiny_gcc() {
124 detected := ccompiler_type_from_version_output('tiny_gcc version 0.9.27') or { panic(err) }
125 assert detected == .tinyc
126}
127
128fn test_is_tcc_compilation_failure_detects_tiny_gcc_compiler_name() {
129 assert is_tcc_compilation_failure('/opt/bin/tiny_gcc', .unknown, '')
130}
131
132fn test_resolve_ccompiler_type_detects_cc_alias_path_as_clang() {
133 $if windows {
134 return
135 }
136 test_root := os.join_path(os.vtmp_dir(), 'v_builder_cc_alias_${os.getpid()}')
137 alias_cc := prepare_test_ccompiler_alias(test_root, 'cc', 'OpenBSD clang version 16.0.6')
138 defer {
139 os.rmdir_all(test_root) or {}
140 }
141 assert resolve_ccompiler_type(alias_cc, pref.cc_from_string(alias_cc)) == .clang
142}
143
144fn test_resolve_ccompiler_type_detects_real_path_without_running_alias() {
145 $if windows {
146 return
147 }
148 test_root := os.join_path(os.vtmp_dir(), 'v_builder_cc_symlink_${os.getpid()}')
149 os.rmdir_all(test_root) or {}
150 os.mkdir_all(test_root) or { panic(err) }
151 defer {
152 os.rmdir_all(test_root) or {}
153 }
154 clang_path := os.join_path(test_root, 'clang')
155 os.write_file(clang_path, '#!/bin/sh
156exit 1
157') or { panic(err) }
158 os.chmod(clang_path, 0o700) or { panic(err) }
159 link_path := os.join_path(test_root, 'cc')
160 os.symlink(clang_path, link_path) or { panic(err) }
161 assert resolve_ccompiler_type(link_path, pref.cc_from_string(link_path)) == .clang
162}
163
164fn test_ccompiler_type_from_resolved_path_detects_macos_cc_wrapper_as_clang() {
165 $if macos {
166 detected := ccompiler_type_from_resolved_path('/usr/bin/cc') or { panic(err) }
167 assert detected == .clang
168 }
169}
170
171fn test_setup_ccompiler_options_detects_cl_path_as_msvc() {
172 mut full_args := ['']
173 full_args << hello_world_example()
174 mut prefs, _ := pref.parse_args_and_show_errors([], full_args, false)
175 prefs.ccompiler = 'C:/Program Files/Microsoft Visual Studio/cl.exe'
176 prefs.ccompiler_type = pref.cc_from_string(prefs.ccompiler)
177 mut builder := new_builder(prefs)
178 builder.out_name_c = os.join_path(os.vtmp_dir(), 'builder_cc_test.tmp.c')
179 builder.setup_ccompiler_options(prefs.ccompiler)
180 assert builder.ccoptions.cc == .msvc
181}
182
183fn test_msvc_thirdparty_obj_path_uses_cached_location_for_target_arch() {
184 obj_file := os.join_path(@VEXEROOT, 'thirdparty', 'mbedtls', 'library', 'bignum.o')
185 mut builder64 := new_builder_for_args(['-cc', 'msvc', '-m64', hello_world_example()])
186 mut builder32 := new_builder_for_args(['-cc', 'msvc', '-m32', hello_world_example()])
187 obj64 := builder64.msvc_thirdparty_obj_path('mbedtls', obj_file, '')
188 obj32 := builder32.msvc_thirdparty_obj_path('mbedtls', obj_file, '')
189 source_obj := os.real_path(obj_file.all_before_last('.o') + '.obj')
190 assert obj64 != obj32
191 assert obj64 != source_obj
192 assert obj32 != source_obj
193 assert obj64.ends_with('.obj')
194 assert obj32.ends_with('.obj')
195}
196
197fn test_msvc_thirdparty_obj_path_keeps_debug_objects_separate() {
198 obj_file := os.join_path(@VEXEROOT, 'thirdparty', 'mbedtls', 'library', 'bignum.o')
199 mut release_builder := new_builder_for_args(['-cc', 'msvc', '-m64', hello_world_example()])
200 mut debug_builder := new_builder_for_args(['-cc', 'msvc', '-m64', '-g', hello_world_example()])
201 release_obj := release_builder.msvc_thirdparty_obj_path('mbedtls', obj_file, '')
202 debug_obj := debug_builder.msvc_thirdparty_obj_path('mbedtls', obj_file, '')
203 assert debug_obj.ends_with('.debug.obj')
204 assert release_obj != debug_obj
205}
206
207fn test_sqlite_thirdparty_validation_error_for_missing_amalgamation() {
208 obj_path := os.join_path(@VEXEROOT, 'thirdparty', 'sqlite', 'sqlite3.o')
209 msg := sqlite_thirdparty_validation_error('db.sqlite', obj_path, '', .unknown)
210 assert msg.contains('sqlite3.c')
211 assert msg.contains('sqlite3.h')
212 assert msg.contains('install_thirdparty_sqlite.vsh')
213}
214
215fn test_sqlite_thirdparty_validation_error_for_sqlite3_cpp() {
216 obj_path := os.join_path(@VEXEROOT, 'thirdparty', 'sqlite', 'sqlite3.o')
217 source_path := obj_path.all_before_last('.o') + '.cpp'
218 msg := sqlite_thirdparty_validation_error('db.sqlite', obj_path, source_path, .cpp)
219 assert msg.contains('Do not rename `sqlite3.c` to `sqlite3.cpp`')
220 assert msg.contains('SQLite amalgamation package')
221}
222
223fn test_sqlite_thirdparty_validation_error_ignores_sqlite3_c() {
224 obj_path := os.join_path(@VEXEROOT, 'thirdparty', 'sqlite', 'sqlite3.o')
225 source_path := obj_path.all_before_last('.o') + '.c'
226 assert sqlite_thirdparty_validation_error('db.sqlite', obj_path, source_path, .c) == ''
227}
228
229fn test_sqlite_thirdparty_validation_error_ignores_other_modules() {
230 obj_path := os.join_path(@VEXEROOT, 'thirdparty', 'sqlite', 'sqlite3.o')
231 source_path := obj_path.all_before_last('.o') + '.cpp'
232 assert sqlite_thirdparty_validation_error('json.cjson', obj_path, source_path, .cpp) == ''
233}
234
235fn test_linux_cross_target_for_amd64() {
236 target := linux_cross_target_for_arch(.amd64) or { panic(err) }
237 assert target.triple == 'x86_64-linux-gnu'
238 assert target.lib_dir == 'x86_64-linux-gnu'
239 assert target.dynamic_linker == '/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2'
240 assert target.linker_emulation == 'elf_x86_64'
241}
242
243fn test_linux_cross_target_for_arm64_errors() {
244 if target := linux_cross_target_for_arch(.arm64) {
245 assert false, 'unexpected target: ${target}'
246 } else {
247 assert err.msg().contains('only `-arch amd64`')
248 assert err.msg().contains('linuxroot')
249 }
250}
251
252fn test_git_symlink_target_path_detects_placeholder_file() {
253 test_root := os.join_path(os.vtmp_dir(), 'v_builder_git_symlink_target_${os.getpid()}')
254 os.rmdir_all(test_root) or {}
255 defer {
256 os.rmdir_all(test_root) or {}
257 }
258 lib_dir := os.join_path(test_root, 'lib', 'x86_64-linux-gnu')
259 os.mkdir_all(lib_dir)!
260 target_file := os.join_path(lib_dir, 'libm-2.31.so')
261 placeholder := os.join_path(lib_dir, 'libm.so.6')
262 os.write_file(target_file, 'ELF PLACEHOLDER')!
263 os.write_file(placeholder, 'libm-2.31.so\n')!
264 assert git_symlink_target_path(placeholder) or { panic(err) } == target_file
265}
266
267fn test_git_symlink_target_path_ignores_linker_script() {
268 test_root := os.join_path(os.vtmp_dir(), 'v_builder_git_symlink_script_${os.getpid()}')
269 os.rmdir_all(test_root) or {}
270 defer {
271 os.rmdir_all(test_root) or {}
272 }
273 lib_dir := os.join_path(test_root, 'lib', 'x86_64-linux-gnu')
274 os.mkdir_all(lib_dir)!
275 linker_script := os.join_path(lib_dir, 'libm.so')
276 os.write_file(linker_script, '/* GNU ld script */\nGROUP ( /lib/x86_64-linux-gnu/libm.so.6 )\n')!
277 assert git_symlink_target_path(linker_script) == none
278}
279
280fn test_git_symlink_materialization_source_follows_placeholder_chain() {
281 test_root := os.join_path(os.vtmp_dir(), 'v_builder_git_symlink_chain_${os.getpid()}')
282 os.rmdir_all(test_root) or {}
283 defer {
284 os.rmdir_all(test_root) or {}
285 }
286 lib_dir := os.join_path(test_root, 'lib', 'x86_64-linux-gnu')
287 os.mkdir_all(lib_dir)!
288 final_target := os.join_path(lib_dir, 'libm-2.31.so')
289 first_link := os.join_path(lib_dir, 'libm.so.6')
290 second_link := os.join_path(lib_dir, 'libm.so')
291 os.write_file(final_target, 'ELF FINAL')!
292 os.write_file(first_link, 'libm-2.31.so\n')!
293 os.write_file(second_link, 'libm.so.6\n')!
294 assert git_symlink_materialization_source(second_link) or { panic(err) } == final_target
295}
296
297fn test_repair_cross_sysroot_git_symlink_placeholders_in_paths_materializes_target_copy() {
298 test_root := os.join_path(os.vtmp_dir(), 'v_builder_git_symlink_repair_${os.getpid()}')
299 os.rmdir_all(test_root) or {}
300 defer {
301 os.rmdir_all(test_root) or {}
302 }
303 lib_dir := os.join_path(test_root, 'lib', 'x86_64-linux-gnu')
304 os.mkdir_all(lib_dir)!
305 final_target := os.join_path(lib_dir, 'libm-2.31.so')
306 placeholder := os.join_path(lib_dir, 'libm.so.6')
307 final_bytes := 'ELF DATA BINARY'
308 os.write_file(final_target, final_bytes)!
309 os.write_file(placeholder, 'libm-2.31.so\n')!
310 repaired := repair_cross_sysroot_git_symlink_placeholders_in_paths([placeholder], true) or {
311 panic(err)
312 }
313 assert repaired == 1
314 assert os.read_file(placeholder)! == final_bytes
315}
316
317fn test_msvc_should_use_rsp_for_ascii_args() {
318 builder := new_builder_for_args(['-cc', 'msvc', hello_world_example()])
319 assert builder.msvc_should_use_rsp(['/OUT:"C:\\Users\\russo\\Desktop\\main.exe"'])
320}
321
322fn test_msvc_should_not_use_rsp_for_non_ascii_args() {
323 builder := new_builder_for_args(['-cc', 'msvc', hello_world_example()])
324 assert !builder.msvc_should_use_rsp([
325 '/OUT:"C:\\Users\\russo\\OneDrive\\Рабочий стол\\main.exe"',
326 ])
327}
328
329fn test_msvc_should_not_use_rsp_when_no_rsp_is_requested() {
330 builder := new_builder_for_args(['-cc', 'msvc', '-no-rsp', hello_world_example()])
331 assert !builder.msvc_should_use_rsp(['/OUT:"C:\\Users\\russo\\Desktop\\main.exe"'])
332}
333
334fn test_live_termux_linker_args_include_rdynamic_without_debug() {
335 linker_args := builder_linker_args([
336 '-os',
337 'termux',
338 '-cc',
339 'clang',
340 '-live',
341 hello_world_example(),
342 ])
343 assert linker_args.contains('-rdynamic')
344}
345
346fn test_thirdparty_cross_compile_config_for_linux_matches_target() {
347 builder := new_builder_for_args(['-os', 'linux', hello_world_example()])
348 cfg := builder.thirdparty_cross_compile_config()
349 if current_os == 'linux' {
350 assert cfg.sysroot == ''
351 assert cfg.target_args == []string{}
352 assert cfg.trailing_include_args == []string{}
353 return
354 }
355 assert cfg.target_args == ['-target x86_64-linux-gnu']
356 assert normalized_test_path(cfg.sysroot).ends_with('/linuxroot')
357 assert cfg.trailing_include_args == [
358 '-I',
359 os.quoted_path('${cfg.sysroot}/include'),
360 ]
361}
362
363fn test_thirdparty_cross_compile_config_for_freebsd_matches_target() {
364 builder := new_builder_for_args(['-os', 'freebsd', hello_world_example()])
365 cfg := builder.thirdparty_cross_compile_config()
366 if current_os == 'freebsd' {
367 assert cfg.sysroot == ''
368 assert cfg.target_args == []string{}
369 assert cfg.trailing_include_args == []string{}
370 return
371 }
372 assert cfg.target_args == ['-target x86_64-unknown-freebsd14.0']
373 assert normalized_test_path(cfg.sysroot).ends_with('/freebsdroot')
374 assert cfg.trailing_include_args == [
375 '-I',
376 os.quoted_path('${cfg.sysroot}/include'),
377 '-I',
378 os.quoted_path('${cfg.sysroot}/usr/include'),
379 ]
380}
381
382fn test_linux_tcc_arm64_stdatomic_does_not_add_atomic_s() {
383 $if !linux {
384 return
385 }
386 test_dir := os.join_path(os.vtmp_dir(), 'v_builder_stdatomic_tcc_arm64_${os.getpid()}')
387 os.mkdir_all(test_dir) or { panic(err) }
388 defer {
389 os.rmdir_all(test_dir) or {}
390 }
391 src_file := os.join_path(test_dir, 'main.v')
392 os.write_file(src_file, 'import sync.stdatomic
393fn main() {
394 mut x := u64(0)
395 stdatomic.store_u64(&x, 1)
396}
397') or {
398 panic(err)
399 }
400 res :=
401 os.execute('${os.quoted_path(@VEXE)} -dump-c-flags - -os linux -cc tcc -arch arm64 ${os.quoted_path(src_file)}')
402 assert res.exit_code == 0, res.output
403 assert !res.output.contains('thirdparty/stdatomic/nix/atomic.S')
404 // libatomic.so is only emitted when the host has the aarch64 cross-compile gcc toolchain installed.
405 matches := os.glob('/usr/lib/gcc/aarch64-linux-gnu/*/libatomic.so') or { []string{} }
406 if matches.len > 0 {
407 assert res.output.contains('libatomic.so')
408 }
409}
410
411fn test_live_windows_main_linker_args_export_host_symbols() {
412 linker_args := builder_linker_args_with_cc([
413 '-os',
414 'windows',
415 '-cc',
416 'gcc',
417 '-live',
418 hot_reload_graph_example(),
419 ], .gcc)
420 assert linker_args.contains('-Wl,--export-all-symbols')
421 assert linker_args.contains('-Wl,--out-implib,')
422 expected_import_lib := os.file_name(live_windows_import_lib_path(hot_reload_graph_example()))
423 assert linker_args.contains(expected_import_lib), 'linker_args should contain ${expected_import_lib}'
424}
425
426fn test_live_windows_shared_linker_args_include_host_import_lib() {
427 linker_args := builder_linker_args_with_cc([
428 '-os',
429 'windows',
430 '-cc',
431 'gcc',
432 '-sharedlive',
433 '-shared',
434 hot_reload_graph_example(),
435 ], .gcc)
436 expected_import_lib := os.file_name(live_windows_import_lib_path(hot_reload_graph_example()))
437 assert linker_args.contains(expected_import_lib), 'linker_args should contain ${expected_import_lib}'
438}
439
440fn test_windows_cross_compile_args_match_shared_prod_args() {
441 $if windows {
442 return
443 }
444 mut builder := new_test_builder(['-os', 'windows', '-prod', hello_world_example()])
445 all_args := builder.windows_cross_compile_args('')
446 assert all_args == builder.all_args(builder.ccoptions)
447 assert all_args.contains('-O3')
448 assert all_args.contains('-fwrapv')
449 assert all_args.contains('-fno-strict-aliasing')
450 assert all_args.contains('-DNDEBUG')
451 assert all_args.contains('-DNO_DEBUGGING')
452}
453
454fn test_shared_windows_builds_do_not_add_subsystem_flags() {
455 mut builder := new_test_builder(['-os', 'windows', '-shared', hello_world_example()])
456 assert builder.get_subsystem_flag() == ''
457 compile_args := builder.get_compile_args().join(' ')
458 assert !compile_args.contains('-municode')
459 assert !compile_args.contains('-mwindows')
460 assert !compile_args.contains('-mconsole')
461}
462
463fn test_windows_gcc_compile_args_force_generated_source_to_c_mode() {
464 compile_args := builder_compile_args(['-os', 'windows', '-cc', 'gcc', hello_world_example()])
465 assert compile_args.contains('-x c')
466}
467
468fn test_shared_tcc_compile_args_skip_bt25_after_late_compiler_resolution() {
469 mut full_args := ['']
470 full_args << ['-shared', hello_world_example()]
471 mut prefs, _ := pref.parse_args_and_show_errors([], full_args, false)
472 prefs.ccompiler = 'tcc'
473 prefs.ccompiler_type = .tinyc
474 prefs.normalize_gc_defaults_for_resolved_ccompiler()
475 assert 'no_backtrace' in prefs.compile_defines_all
476
477 mut builder := new_builder(prefs)
478 builder.out_name_c = os.join_path(os.vtmp_dir(), 'builder_cc_test.tmp.c')
479 builder.setup_ccompiler_options(prefs.ccompiler)
480
481 assert !builder.get_compile_args().contains('-bt25')
482}
483
484fn test_should_use_rsp_for_linux_by_default() {
485 builder := new_test_builder([hello_world_example()])
486 assert builder.should_use_rsp(['-o', builder.out_name_c])
487}
488
489fn test_should_not_use_rsp_for_termux() {
490 builder := new_test_builder(['-os', 'termux', hello_world_example()])
491 assert !builder.should_use_rsp(['-o', builder.out_name_c])
492}
493
494fn test_should_not_use_rsp_for_args_with_embedded_single_quotes() {
495 builder := new_test_builder([hello_world_example()])
496 assert !builder.should_use_rsp(["'\\''"])
497}
498
499fn test_setup_ccompiler_options_detects_cc_alias_path_as_clang() {
500 $if windows {
501 return
502 }
503 test_root := os.join_path(os.vtmp_dir(), 'v_builder_cc_setup_${os.getpid()}')
504 alias_cc := prepare_test_ccompiler_alias(test_root, 'cc', 'OpenBSD clang version 16.0.6')
505 defer {
506 os.rmdir_all(test_root) or {}
507 }
508 mut full_args := ['']
509 full_args << hello_world_example()
510 mut prefs, _ := pref.parse_args_and_show_errors([], full_args, false)
511 prefs.ccompiler = alias_cc
512 prefs.ccompiler_type = pref.cc_from_string(prefs.ccompiler)
513 mut builder := new_builder(prefs)
514 builder.out_name_c = os.join_path(os.vtmp_dir(), 'builder_cc_test.tmp.c')
515 builder.setup_ccompiler_options(prefs.ccompiler)
516 assert builder.pref.ccompiler_type == .clang
517 assert builder.ccoptions.cc == .clang
518}
519
520fn macos_compile_args(args []string) string {
521 return builder_compile_args(args)
522}
523
524fn builder_compile_args(args []string) string {
525 builder := new_test_builder(args)
526 return builder.get_compile_args().join(' ')
527}
528
529fn builder_linker_args(args []string) string {
530 builder := new_test_builder(args)
531 return builder.get_linker_args().join(' ')
532}
533
534fn builder_linker_args_with_cc(args []string, cc CC) string {
535 mut builder := new_test_builder_without_cc_setup(args)
536 ccompiler := ccompiler_name_for_test_cc(cc)
537 builder.pref.ccompiler = ccompiler
538 builder.pref.ccompiler_type = pref.cc_from_string(ccompiler)
539 builder.setup_ccompiler_options(ccompiler)
540 builder.setup_output_name()
541 return builder.get_linker_args().join(' ')
542}
543
544fn new_test_builder(args []string) Builder {
545 mut builder := new_test_builder_without_cc_setup(args)
546 builder.setup_ccompiler_options(builder.pref.ccompiler)
547 builder.setup_output_name()
548 return builder
549}
550
551fn new_test_builder_without_cc_setup(args []string) Builder {
552 mut full_args := ['']
553 full_args << args
554 prefs, _ := pref.parse_args_and_show_errors([], full_args, false)
555 mut builder := new_builder(prefs)
556 builder.out_name_c = os.join_path(os.vtmp_dir(), 'builder_cc_test.tmp.c')
557 return builder
558}
559
560fn ccompiler_name_for_test_cc(cc CC) string {
561 return match cc {
562 .tcc { 'tcc' }
563 .gcc { 'gcc' }
564 .icc { 'icc' }
565 .msvc { 'msvc' }
566 .clang { 'clang' }
567 .emcc { 'emcc' }
568 .unknown { '' }
569 }
570}
571
572fn new_builder_for_args(args []string) Builder {
573 mut full_args := ['']
574 full_args << args
575 prefs, _ := pref.parse_args_and_show_errors([], full_args, false)
576 return new_builder(prefs)
577}
578
579fn macos_version_min_flags(compile_args string) []string {
580 return compile_args.split(' ').filter(it.starts_with('-mmacosx-version-min='))
581}
582
583fn hello_world_example() string {
584 return os.join_path(@VEXEROOT, 'examples', 'hello_world.v')
585}
586
587fn hot_reload_graph_example() string {
588 return os.join_path(@VEXEROOT, 'examples', 'hot_reload', 'graph.v')
589}
590
591fn normalized_test_path(path string) string {
592 mut normalized := path.replace('\\', '/')
593 for normalized.contains('//') {
594 normalized = normalized.replace('//', '/')
595 }
596 return normalized
597}
598
599fn test_c_output_suggests_missing_typedef_for_c_struct_with_issue_19050_output() {
600 c_output := [
601 "/tmp/v_501/c_struct.6580681062929530137.tmp.c:12966:17: error: incomplete result type 'struct string_c' in function definition",
602 'struct string_c main__convert(string s) {',
603 ' ^',
604 "/tmp/v_501/c_struct.6580681062929530137.tmp.c:1962:8: note: forward declaration of 'struct string_c'",
605 'struct string_c main__convert(string s);',
606 ' ^',
607 "/tmp/v_501/c_struct.6580681062929530137.tmp.c:12967:25: error: variable has incomplete type 'struct string_c'",
608 ' struct string_c _t1 = ((struct string_c){.content = s.str,.len = ((u32)(s.len)),});',
609 ' ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~',
610 ].join('\n')
611 assert c_output_suggests_missing_typedef_for_c_struct(c_output, {
612 'string_c': true
613 }) == 'string_c'
614}
615
616fn test_c_output_suggests_missing_typedef_for_c_struct_requires_matching_redeclaration() {
617 c_output := [
618 "/tmp/v_501/c_struct.tmp.c:1:1: error: incomplete result type 'struct string_c' in function definition",
619 "/tmp/v_501/c_struct.tmp.c:2:1: note: forward declaration of 'struct string_c'",
620 ].join('\n')
621 assert c_output_suggests_missing_typedef_for_c_struct(c_output, {
622 'other_c_struct': true
623 }) == ''
624}
625
626fn test_c_output_suggests_missing_typedef_for_c_struct_with_issue_17101_output() {
627 c_output := [
628 "C:\\Users\\USER\\AppData\\Local\\Temp\\v_0\\main2.3589850650208729523.tmp.c:12338:99: error: 'tilengine__TLN_Affine' {aka 'struct TLN_Affine'} has no member named 'angle'",
629 '12338 | tilengine__TLN_Affine* affine = ((tilengine__TLN_Affine*)memdup(&(tilengine__TLN_Affine){.angle = 10,.dx = 1,.dy = 1,.sx = 1,.sy = 1,}, sizeof(tilengine__TLN_Affine)));',
630 ' | ^~~~~',
631 ].join('\n')
632 assert c_output_suggests_missing_typedef_for_c_struct(c_output, {
633 'TLN_Affine': true
634 }) == 'TLN_Affine'
635}
636
637fn test_extract_quoted_identifier_supports_double_quotes() {
638 assert extract_quoted_identifier('error: \';\' expected (got "glfw__GLFWwindow")') == 'glfw__GLFWwindow'
639}
640
641fn test_c_output_suggests_missing_header_for_typedef_c_struct_with_issue_25384_tcc_output() {
642 c_output := 'C:/Users/si_z_/AppData/Local/Temp/v_0/TestV.tmp.c:904: error: \';\' expected (got "glfw__GLFWwindow")'
643 assert c_output_suggests_missing_header_for_typedef_c_struct(c_output, {
644 'GLFWwindow': true
645 }, {
646 'glfw__GLFWwindow': 'GLFWwindow'
647 }) == 'GLFWwindow'
648}
649
650fn test_c_output_suggests_missing_header_for_typedef_c_struct_with_issue_25384_gcc_output() {
651 c_output := [
652 "/tmp/v_502/issue25384_windows.exe.tmp.c:1198:9: error: unknown type name 'GLFWwindow'",
653 ' 1198 | typedef GLFWwindow glfw__GLFWwindow;',
654 ].join('\n')
655 assert c_output_suggests_missing_header_for_typedef_c_struct(c_output, {
656 'GLFWwindow': true
657 }, {
658 'glfw__GLFWwindow': 'GLFWwindow'
659 }) == 'GLFWwindow'
660}
661
662fn test_c_output_suggests_missing_header_for_typedef_c_struct_requires_known_type() {
663 c_output := 'C:/Users/si_z_/AppData/Local/Temp/v_0/TestV.tmp.c:904: error: \';\' expected (got "glfw__GLFWwindow")'
664 assert c_output_suggests_missing_header_for_typedef_c_struct(c_output, {}, {}) == ''
665}
666
667fn test_c_output_suggests_missing_header_for_typedef_c_struct_with_issue_23648_tcc_output() {
668 c_output := 'D:/Temp/Temp/v_0/main.tmp.c:1028: error: \';\' expected (got "duarteroso__glfw__GLFWmonitor")'
669 assert c_output_suggests_missing_header_for_typedef_c_struct(c_output, {
670 'GLFWmonitor': true
671 }, {}) == 'GLFWmonitor'
672}
673
674fn test_c_error_missing_library_name_detects_tcc_output() {
675 tcc_output := "tcc: error: library 'pq' not found"
676 lib_name := c_error_missing_library_name(tcc_output)
677 assert lib_name == 'pq'
678}
679
680fn test_c_error_missing_libatomic_marker_with_tcc_output() {
681 c_output := "/tmp/v/vdoc.tmp.c:24184: warning: assignment makes pointer from integer without a cast\ntcc: error: library 'atomic' not found\n"
682 assert c_error_missing_libatomic_marker(c_output) == "library 'atomic' not found"
683 assert c_error_looks_like_missing_libatomic(c_output)
684}
685
686fn test_c_error_missing_libatomic_marker_with_ld_output() {
687 c_output := '/usr/bin/ld: cannot find -latomic\ncollect2: error: ld returned 1 exit status\n'
688 assert c_error_missing_libatomic_marker(c_output) == 'cannot find -latomic'
689 assert c_error_looks_like_missing_libatomic(c_output)
690}
691
692fn test_c_error_missing_libatomic_marker_with_regular_c_error() {
693 c_output := "error: unknown type name 'my_missing_type'"
694 assert c_error_missing_libatomic_marker(c_output) == ''
695 assert !c_error_looks_like_missing_libatomic(c_output)
696}
697
698fn test_c_error_missing_library_name_with_macos_ld_output() {
699 c_output := "ld: library 'mbedtls' not found\nclang: error: linker command failed with exit code 1\n"
700 assert c_error_missing_library_name(c_output) == 'mbedtls'
701}
702
703fn test_c_error_missing_library_name_with_gnu_ld_output() {
704 c_output := '/usr/bin/ld: cannot find -lssl\ncollect2: error: ld returned 1 exit status\n'
705 assert c_error_missing_library_name(c_output) == 'ssl'
706}
707
708fn test_c_error_missing_library_name_with_regular_c_error() {
709 c_output := "error: unknown type name 'my_missing_type'"
710 assert c_error_missing_library_name(c_output) == ''
711}
712
713fn prepare_test_ccompiler_alias(test_root string, compiler_name string, version_output string) string {
714 os.rmdir_all(test_root) or {}
715 os.mkdir_all(test_root) or { panic(err) }
716 compiler_path := os.join_path(test_root, compiler_name)
717 os.write_file(compiler_path, '#!/bin/sh
718if [ "\$1" = "--version" ] || [ "\$1" = "-v" ]; then
719 echo "${version_output}"
720 exit 0
721fi
722exit 1
723') or {
724 panic(err)
725 }
726 os.chmod(compiler_path, 0o700) or { panic(err) }
727 return compiler_path
728}
729