v2 / vlib / v / builder / builder_test.v
384 lines · 334 sloc · 12.21 KB · da7e85cbec7fd73d9d26db033850648c49120c9f
Raw
1module main
2
3import os
4import v.builder
5import v.pref
6
7const vexe = @VEXE
8const test_path = os.join_path(os.vtmp_dir(), 'run_check')
9
10fn testsuite_begin() {
11 os.mkdir_all(test_path) or {}
12}
13
14fn testsuite_end() {
15 os.rmdir_all(test_path) or {}
16}
17
18fn run_v_ok(command string) string {
19 res := os.execute(command)
20 if res.exit_code != 0 {
21 eprintln('> failing cmd: ${command}')
22 eprintln('> output:\n${res.output}')
23 assert res.exit_code == 0
24 }
25 return res.output
26}
27
28fn test_conditional_executable_removal() {
29 os.chdir(test_path)!
30 os.write_file('main.v', 'fn main(){\n\tprintln("Hello World!")\n}\n')!
31
32 mut executable := 'run_check'
33 $if windows {
34 executable += '.exe'
35 }
36
37 original_file_list_ := os.ls(test_path)!
38 dump(original_file_list_)
39 assert executable !in original_file_list_
40
41 assert os.execute('${os.quoted_path(vexe)} run .').output.trim_space() == 'Hello World!'
42 after_run_file_list := os.ls(test_path)!.filter(os.exists(it))
43 dump(after_run_file_list)
44 assert executable !in after_run_file_list
45
46 assert os.execute('${os.quoted_path(vexe)} .').exit_code == 0
47 assert os.execute('./${executable}').output.trim_space() == 'Hello World!'
48 after_compilation__ := os.ls(test_path)!
49 dump(after_compilation__)
50 assert executable in after_compilation__
51
52 assert os.execute('${os.quoted_path(vexe)} run .').output.trim_space() == 'Hello World!'
53 after_second_run___ := os.ls(test_path)!
54 dump(after_second_run___)
55 assert executable in after_second_run___
56}
57
58fn test_run_from_workdir_with_spaces() {
59 project_dir := os.join_path(test_path, 'issue 16501 path with spaces')
60 os.rmdir_all(project_dir) or {}
61 os.mkdir_all(project_dir)!
62 defer {
63 os.chdir(test_path) or {}
64 os.rmdir_all(project_dir) or {}
65 }
66 os.write_file(os.join_path(project_dir, 'main.v'),
67 'fn main(){\n\tprintln("Hello from a spaced path")\n}\n')!
68 os.write_file(os.join_path(project_dir, 'Test.vsh'),
69 "println('Hello from a spaced script path')\n")!
70 os.chdir(project_dir)!
71
72 run_file_res := os.execute('${os.quoted_path(vexe)} run Test.vsh')
73 assert run_file_res.exit_code == 0, run_file_res.output
74 assert run_file_res.output.trim_space() == 'Hello from a spaced script path'
75
76 run_dir_res := os.execute('${os.quoted_path(vexe)} run .')
77 assert run_dir_res.exit_code == 0, run_dir_res.output
78 assert run_dir_res.output.trim_space() == 'Hello from a spaced path'
79}
80
81fn test_existing_vsh_executable_uses_cache_until_source_is_newer() {
82 project_dir := os.join_path(test_path, 'existing vsh executable')
83 os.rmdir_all(project_dir) or {}
84 os.mkdir_all(project_dir)!
85 defer {
86 os.chdir(test_path) or {}
87 os.rmdir_all(project_dir) or {}
88 }
89 script_path := os.join_path(project_dir, 'script.vsh')
90 os.write_file(script_path, "println('Hello from cached vsh')\n")!
91 os.chdir(project_dir)!
92
93 cmd := '${os.quoted_path(vexe)} script.vsh'
94 first_res := os.execute(cmd)
95 assert first_res.exit_code == 0, first_res.output
96 assert first_res.output.trim_space() == 'Hello from cached vsh'
97
98 mut executable := os.join_path(project_dir, 'script')
99 $if windows {
100 executable += '.exe'
101 }
102 assert os.is_file(executable)
103
104 warm_cache_res := os.execute(cmd)
105 assert warm_cache_res.exit_code == 0, warm_cache_res.output
106 assert warm_cache_res.output.trim_space() == 'Hello from cached vsh'
107
108 cache_stamp := os.file_last_mod_unix(executable) + 3600
109 os.utime(executable, cache_stamp, cache_stamp)!
110
111 os.write_file(script_path, "println('Hello from rebuilt vsh')\n")!
112 os.utime(script_path, cache_stamp - 1, cache_stamp - 1)!
113 assert os.file_last_mod_unix(script_path) < cache_stamp
114
115 cached_res := os.execute(cmd)
116 assert cached_res.exit_code == 0, cached_res.output
117 assert cached_res.output.trim_space() == 'Hello from cached vsh'
118
119 os.utime(script_path, cache_stamp + 60, cache_stamp + 60)!
120 assert os.file_last_mod_unix(script_path) > cache_stamp
121
122 rebuilt_res := os.execute(cmd)
123 assert rebuilt_res.exit_code == 0, rebuilt_res.output
124 assert rebuilt_res.output.trim_space() == 'Hello from rebuilt vsh'
125}
126
127fn test_file_list() {
128 os.chdir(test_path)!
129 os.mkdir_all('filelist')!
130 os.write_file('filelist/main.v', 'module main
131fn main() {
132 part_a := AS{}
133 part_b := BS{}
134 println("\${part_a}=>\${part_b}")
135}')!
136 os.write_file('filelist/part_a.v', 'module main
137pub struct AS{}
138')!
139
140 os.write_file('filelist/part_b.v', 'module main
141pub struct BS{}
142')!
143
144 // `part_c.v` is not included in the compilation
145 // or there will be a conflit definition of `struct BS`
146 os.write_file('filelist/part_c.v', 'module main
147pub struct BS{}
148')!
149 mut executable := 'filelist_check'
150 $if windows {
151 executable += '.exe'
152 }
153
154 original_file_list_ := os.ls(test_path)!
155 dump(original_file_list_)
156 assert executable !in original_file_list_
157
158 cmd := '${os.quoted_path(vexe)} -o ${executable} filelist/main.v -file-list "filelist/part_a.v,filelist/part_b.v,"'
159 os.execute(cmd)
160 after_compile_file_list := os.ls(test_path)!.filter(os.exists(it))
161 dump(after_compile_file_list)
162 assert executable in after_compile_file_list
163 assert os.execute('./${executable}').output.trim_space() == 'AS{}=>BS{}'
164}
165
166fn test_run_custom_base_url_uses_project_root_lookup() {
167 os.chdir(test_path)!
168 project_dir := os.join_path(test_path, 'run_base_url_project')
169 defer {
170 os.chdir(test_path) or {}
171 }
172 os.mkdir_all(os.join_path(project_dir, 'source', 'foo'))!
173 os.mkdir_all(os.join_path(project_dir, 'source', 'modules', 'dep'))!
174 os.write_file(os.join_path(project_dir, 'v.mod'),
175 "Module {\n\tname: 'run_base_url_project'\n\tbase_url: 'source'\n\tdescription: ''\n\tversion: ''\n\tlicense: ''\n\tdependencies: []\n}\n")!
176 os.write_file(os.join_path(project_dir, 'source', 'main.v'), 'module main
177import foo
178import dep
179
180fn main() {
181 println(foo.name() + "+" + dep.name())
182}
183')!
184 os.write_file(os.join_path(project_dir, 'source', 'foo', 'foo.v'), 'module foo
185
186pub fn name() string {
187 return "foo"
188}
189')!
190 os.write_file(os.join_path(project_dir, 'source', 'modules', 'dep', 'dep.v'), 'module dep
191
192pub fn name() string {
193 return "dep"
194}
195')!
196 os.chdir(project_dir)!
197 assert run_v_ok('${os.quoted_path(vexe)} run .').trim_space() == 'foo+dep'
198 assert run_v_ok('${os.quoted_path(vexe)} run source').trim_space() == 'foo+dep'
199 assert run_v_ok('${os.quoted_path(vexe)} run ./source').trim_space() == 'foo+dep'
200}
201
202fn test_empty_local_dir_does_not_shadow_vlib_module() {
203 os.chdir(test_path)!
204 project_dir := os.join_path(test_path, 'run_empty_local_module_dir')
205 defer {
206 os.chdir(test_path) or {}
207 }
208 os.mkdir_all(os.join_path(project_dir, 'os'))!
209 os.write_file(os.join_path(project_dir, 'main.v'),
210 "module main\n\nimport os\n\nfn main() {\n\tprintln(os.is_dir('.'))\n}\n")!
211 os.chdir(project_dir)!
212
213 res := os.execute('${os.quoted_path(vexe)} run main.v')
214
215 assert res.exit_code == 0, res.output
216 assert res.output.trim_space() == 'true'
217}
218
219fn test_removed_src_layout_error_mentions_vmod_subdirs() {
220 os.chdir(test_path)!
221 project_dir := os.join_path(test_path, 'run_removed_src_project')
222 defer {
223 os.chdir(test_path) or {}
224 }
225 os.mkdir_all(os.join_path(project_dir, 'src'))!
226 os.write_file(os.join_path(project_dir, 'src', 'main.v'),
227 'fn main() {\n\tprintln("Hello from src")\n}\n')!
228 os.chdir(project_dir)!
229
230 res := os.execute('${os.quoted_path(vexe)} run .')
231 normalized_output := res.output.replace('\r\n', '\n')
232
233 assert res.exit_code != 0
234 assert normalized_output.contains('the virtual `src/` module directory is no longer supported.')
235 assert !normalized_output.contains('base_url')
236 assert normalized_output.contains('add `subdirs` to v.mod')
237 assert normalized_output.contains("subdirs: ['admin', 'repo', 'commit', 'ci', 'security', 'ssh', 'user']")
238}
239
240fn test_thirdparty_object_build_with_multiline_cflags() {
241 mut env := os.environ()
242 existing_cflags := if 'CFLAGS' in env { env['CFLAGS'] } else { '' }
243 env['CFLAGS'] = if existing_cflags == '' {
244 '-DTHIRDPARTY_MULTILINE_1=1\n-DTHIRDPARTY_MULTILINE_2=1'
245 } else {
246 '${existing_cflags}\n-DTHIRDPARTY_MULTILINE_1=1\n-DTHIRDPARTY_MULTILINE_2=1'
247 }
248 mut p := os.new_process(vexe)
249 p.set_work_folder(@VEXEROOT)
250 p.set_args(['run', os.join_path(@VEXEROOT, 'vlib', 'v', 'tests', 'project_with_c_code', 'main.v')])
251 p.set_environment(env)
252 p.set_redirect_stdio()
253 p.wait()
254 stdout := p.stdout_slurp()
255 stderr := p.stderr_slurp()
256 p.close()
257 assert p.code == 0, 'stdout:\n${stdout}\nstderr:\n${stderr}'
258}
259
260fn test_missing_library_is_reported_without_compiler_bug_hint() {
261 if os.user_os() == 'windows' && os.getenv('VFLAGS').contains('msvc') {
262 return
263 }
264 os.chdir(test_path)!
265 os.mkdir_all('missing_library')!
266 lib_name := 'v_missing_lib_25499'
267 src_file := os.join_path('missing_library', 'main.v')
268 os.write_file(src_file, '#flag -l${lib_name}\nfn main() {}\n')!
269
270 res := os.execute('${os.quoted_path(vexe)} ${os.quoted_path(src_file)}')
271 normalized_output := res.output.replace('\r\n', '\n')
272
273 assert res.exit_code != 0
274 assert normalized_output.contains('builder error:')
275 assert normalized_output.contains('C library `${lib_name}` was not found while linking the generated program.')
276 assert normalized_output.contains('Please install the corresponding development package/libraries')
277 assert !normalized_output.contains('This is a V compiler bug')
278}
279
280fn test_windows_host_c_compiler_probe_is_skipped_for_non_windows_targets() {
281 assert builder.should_find_windows_host_c_compiler(&pref.Preferences{
282 backend: .c
283 os: .windows
284 })
285 assert !builder.should_find_windows_host_c_compiler(&pref.Preferences{
286 backend: .c
287 os: .linux
288 })
289 assert !builder.should_find_windows_host_c_compiler(&pref.Preferences{
290 backend: .c
291 os: .windows
292 output_cross_c: true
293 })
294 assert !builder.should_find_windows_host_c_compiler(&pref.Preferences{
295 backend: .js_browser
296 os: .windows
297 })
298}
299
300fn test_message_limit_notices_do_not_fail_build() {
301 os.chdir(test_path)!
302 src_file := os.join_path(test_path, 'message_limit_notices.v')
303 mut exe_file := os.join_path(test_path, 'message_limit_notices')
304 $if windows {
305 exe_file += '.exe'
306 }
307
308 mut lines := []string{}
309 for i in 0 .. 10 {
310 lines << '@[deprecated]'
311 lines << "@[deprecated_after: '3000-12-30']"
312 lines << 'fn future_${i}() {}'
313 }
314 lines << 'fn main() {'
315 for i in 0 .. 10 {
316 lines << '\tfuture_${i}()'
317 }
318 lines << '}'
319 os.write_file(src_file, lines.join('\n') + '\n')!
320
321 res :=
322 os.execute('${os.quoted_path(vexe)} -stats -message-limit 5 -o ${os.quoted_path(exe_file)} ${os.quoted_path(src_file)}')
323 normalized_output := res.output.replace('\r\n', '\n')
324
325 assert res.exit_code == 0, normalized_output
326 assert os.exists(exe_file), normalized_output
327 assert !normalized_output.contains('builder error: too many errors/warnings/notices')
328 assert normalized_output.contains('checker summary: 0 V errors, 0 V warnings, 10 V notices'), normalized_output
329}
330
331fn test_run_with_obscure_source_filenames() {
332 if os.user_os() == 'windows' {
333 return
334 }
335 obscure_dir := os.join_path(test_path, 'obscure_filenames')
336 os.rmdir_all(obscure_dir) or {}
337 os.mkdir_all(obscure_dir)!
338 source := "println('hi')\n"
339 for idx, file_name in [
340 "quote's.v",
341 '"hi".v',
342 "'.v",
343 '".v',
344 '.v',
345 '..v',
346 '...v',
347 '-.v',
348 '.c.v',
349 'line\nfeed.v',
350 ] {
351 src_file := os.join_path(obscure_dir, file_name)
352 os.write_file(src_file, source)!
353 out_file := os.join_path(obscure_dir, 'obscure_output_${idx}')
354 display_name := file_name.replace('\n', '\\n')
355 res :=
356 os.execute('${os.quoted_path(vexe)} -o ${os.quoted_path(out_file)} run ${os.quoted_path(src_file)}')
357 assert res.exit_code == 0, '${display_name}: ${res.output}'
358 assert res.output.trim_space() == 'hi', '${display_name}: ${res.output}'
359 assert os.read_file(src_file)! == source
360 os.rm(out_file) or {}
361 }
362}
363
364fn test_macos_2048_build_does_not_force_deployment_target() {
365 $if !macos {
366 return
367 }
368 game_path := os.join_path(@VEXEROOT, 'examples', '2048', '2048.v')
369 flags_output := run_v_ok('${os.quoted_path(vexe)} -dump-c-flags - ${os.quoted_path(game_path)}')
370 assert flags_output.contains('-fobjc-arc')
371 assert !flags_output.contains('-mmacosx-version-min=')
372}
373
374fn test_macos_arch_flag_is_forwarded_to_c_compiler() {
375 $if !macos {
376 return
377 }
378 src_file := os.join_path(test_path, 'macos_arch_forwarding.v')
379 out_file := os.join_path(test_path, 'macos_arch_forwarding')
380 os.write_file(src_file, 'fn main() {\n\tprintln("hello")\n}\n')!
381 flags_output :=
382 run_v_ok('${os.quoted_path(vexe)} -cc clang -gc none -showcc -skip-running -arch amd64 -o ${os.quoted_path(out_file)} ${os.quoted_path(src_file)}')
383 assert flags_output.contains('-arch x86_64')
384}
385