v / vlib / v2 / builder / cleanc_target_e2e_test.v
2510 lines · 2219 sloc · 72.55 KB · 7d6fd95995c08b27f8ddf5d469c9cdd889d10154
Raw
1module builder
2
3import os
4
5const target_linux_marker = '<target_marker_linux.h>'
6const target_macos_marker = '<target_marker_macos.h>'
7const target_windows_marker = '<target_marker_windows.h>'
8const target_cross_marker = '<target_marker_cross.h>'
9const target_freestanding_marker = '<target_marker_freestanding.h>'
10const inactive_windows_marker = '<inactive_windows_marker.h>'
11const inactive_freestanding_marker = '<inactive_freestanding_marker.h>'
12const active_comptime_marker = '<active_comptime_marker.h>'
13const e2e_freestanding_missing_alloc_hook_message = 'v2: freestanding target requires freestanding_alloc hook for heap allocation'
14const e2e_freestanding_missing_format_hook_message = 'v2: freestanding target cannot print non-string values without formatting support'
15const e2e_freestanding_missing_heap_runtime_message = 'v2: freestanding target cannot use V runtime heap helpers with --skip-builtin'
16const e2e_freestanding_missing_output_hook_message = 'v2: freestanding target requires freestanding_output hook for output'
17const e2e_freestanding_missing_panic_hook_message = 'v2: freestanding target requires freestanding_panic hook for panic'
18
19struct CleancCliResult {
20 exit_code int
21 output string
22 c_source string
23 out_path string
24 c_path string
25}
26
27fn e2e_repo_root() string {
28 mut dir := os.dir(@FILE)
29 for _ in 0 .. 10 {
30 if os.exists(os.join_path(dir, 'cmd', 'v2', 'v2.v'))
31 && os.exists(os.join_path(dir, 'vlib', 'builtin')) {
32 return dir
33 }
34 dir = os.dir(dir)
35 }
36 panic('could not locate repo root for cleanc target e2e')
37}
38
39fn normalize_e2e_os_name(target_os string) string {
40 return match target_os.to_lower() {
41 'darwin', 'mac' { 'macos' }
42 else { target_os.to_lower() }
43 }
44}
45
46fn e2e_v2_binary_name() string {
47 mut name := 'cleanc_target_e2e'
48 $if windows {
49 name += '.exe'
50 }
51 return name
52}
53
54fn build_v2_for_target_e2e(tmp_dir string) string {
55 vroot := e2e_repo_root()
56 v2_source := os.join_path(vroot, 'cmd', 'v2', 'v2.v')
57 v2_binary := os.join_path(tmp_dir, e2e_v2_binary_name())
58 res :=
59 os.execute('"${@VEXE}" -path "${os.join_path(vroot, 'vlib')}|@vlib|@vmodules" -gc none -nocache "${v2_source}" -o "${v2_binary}"')
60 if res.exit_code != 0 {
61 panic('failed to build cmd/v2 for cleanc target e2e:\n${res.output}')
62 }
63 return v2_binary
64}
65
66fn build_v1_for_target_e2e(tmp_dir string) string {
67 vroot := e2e_repo_root()
68 v1_source := os.join_path(vroot, 'cmd', 'v')
69 v1_binary := os.join_path(tmp_dir, 'v1_public_wrapper')
70 res :=
71 os.execute('"${@VEXE}" -path "${os.join_path(vroot, 'vlib')}|@vlib|@vmodules" -gc none -nocache "${v1_source}" -o "${v1_binary}"')
72 if res.exit_code != 0 {
73 panic('failed to build cmd/v for cleanc target e2e:\n${res.output}')
74 }
75 return v1_binary
76}
77
78fn run_v2_to_c(v2_binary string, tmp_dir string, name string, args []string, source string) CleancCliResult {
79 return run_v2_to_c_with_env(v2_binary, tmp_dir, name, args, source, '')
80}
81
82fn run_v2_to_c_with_env(v2_binary string, tmp_dir string, name string, args []string, source string, env_prefix string) CleancCliResult {
83 source_path := os.join_path(tmp_dir, '${name}.v')
84 out_path := os.join_path(tmp_dir, '${name}.c')
85 os.write_file(source_path, source) or { panic(err) }
86 cmd := 'cd "${e2e_repo_root()}" && ${env_prefix}"${v2_binary}" -gc none -nocache --no-parallel ${args.join(' ')} -o "${out_path}" "${source_path}"'
87 res := os.execute(cmd)
88 c_source := if os.exists(out_path) { os.read_file(out_path) or { '' } } else { '' }
89 return CleancCliResult{
90 exit_code: res.exit_code
91 output: res.output
92 c_source: c_source
93 out_path: out_path
94 c_path: out_path
95 }
96}
97
98fn run_v2_to_c_files(v2_binary string, tmp_dir string, name string, args []string, sources map[string]string) CleancCliResult {
99 case_dir := os.join_path(tmp_dir, name)
100 out_path := os.join_path(tmp_dir, '${name}.c')
101 os.mkdir_all(case_dir) or { panic(err) }
102 mut source_paths := []string{}
103 mut source_names := sources.keys()
104 source_names.sort()
105 for source_name in source_names {
106 source_path := os.join_path(case_dir, source_name)
107 os.mkdir_all(os.dir(source_path)) or { panic(err) }
108 os.write_file(source_path, sources[source_name]) or { panic(err) }
109 source_paths << source_path
110 }
111 cmd := 'cd "${e2e_repo_root()}" && "${v2_binary}" -gc none -nocache --no-parallel ${args.join(' ')} -o "${out_path}" ${source_paths.map('"${it}"').join(' ')}'
112 res := os.execute(cmd)
113 c_source := if os.exists(out_path) { os.read_file(out_path) or { '' } } else { '' }
114 return CleancCliResult{
115 exit_code: res.exit_code
116 output: res.output
117 c_source: c_source
118 out_path: out_path
119 c_path: out_path
120 }
121}
122
123fn run_v2_to_c_project_files(v2_binary string, tmp_dir string, name string, args []string, sources map[string]string, entry string) CleancCliResult {
124 case_dir := os.join_path(tmp_dir, name)
125 out_path := os.join_path(tmp_dir, '${name}.c')
126 os.mkdir_all(case_dir) or { panic(err) }
127 for source_name, source in sources {
128 source_path := os.join_path(case_dir, source_name)
129 os.mkdir_all(os.dir(source_path)) or { panic(err) }
130 os.write_file(source_path, source) or { panic(err) }
131 }
132 entry_path := os.join_path(case_dir, entry)
133 cmd := 'cd "${e2e_repo_root()}" && "${v2_binary}" -gc none -nocache --no-parallel ${args.join(' ')} -o "${out_path}" "${entry_path}"'
134 res := os.execute(cmd)
135 c_source := if os.exists(out_path) { os.read_file(out_path) or { '' } } else { '' }
136 return CleancCliResult{
137 exit_code: res.exit_code
138 output: res.output
139 c_source: c_source
140 out_path: out_path
141 c_path: out_path
142 }
143}
144
145fn generated_c_output_path(output_path string) string {
146 if output_path.ends_with('.c') {
147 return output_path
148 }
149 return output_path + '.c'
150}
151
152fn e2e_binary_output_path(tmp_dir string, name string) string {
153 mut path := os.join_path(tmp_dir, name)
154 $if windows {
155 path += '.exe'
156 }
157 return path
158}
159
160fn run_v2_to_binary(v2_binary string, tmp_dir string, name string, args []string, source string) CleancCliResult {
161 source_path := os.join_path(tmp_dir, '${name}.v')
162 out_path := e2e_binary_output_path(tmp_dir, name)
163 os.write_file(source_path, source) or { panic(err) }
164 env_prefix := if host_c_e2e_flags().len > 0 { 'V2CFLAGS="${host_c_e2e_flags()}" ' } else { '' }
165 cmd := 'cd "${e2e_repo_root()}" && ${env_prefix}"${v2_binary}" -gc none -nocache --no-parallel ${args.join(' ')} -o "${out_path}" "${source_path}"'
166 res := os.execute(cmd)
167 return CleancCliResult{
168 exit_code: res.exit_code
169 output: res.output
170 out_path: out_path
171 c_path: generated_c_output_path(out_path)
172 }
173}
174
175fn run_v2_to_output(v2_binary string, tmp_dir string, name string, args []string, source string, output_path string) CleancCliResult {
176 source_path := os.join_path(tmp_dir, '${name}.v')
177 os.write_file(source_path, source) or { panic(err) }
178 cmd := 'cd "${e2e_repo_root()}" && "${v2_binary}" -gc none -nocache --no-parallel ${args.join(' ')} -o "${output_path}" "${source_path}"'
179 res := os.execute(cmd)
180 c_path := generated_c_output_path(output_path)
181 c_source := if os.exists(c_path) { os.read_file(c_path) or { '' } } else { '' }
182 return CleancCliResult{
183 exit_code: res.exit_code
184 output: res.output
185 c_source: c_source
186 out_path: output_path
187 c_path: c_path
188 }
189}
190
191fn run_v2_without_output_in_dir(v2_binary string, tmp_dir string, name string, args []string, source string, work_dir string) CleancCliResult {
192 source_path := os.join_path(tmp_dir, '${name}.v')
193 os.write_file(source_path, source) or { panic(err) }
194 os.mkdir_all(work_dir) or { panic(err) }
195 env_prefix := if host_c_e2e_flags().len > 0 { 'V2CFLAGS="${host_c_e2e_flags()}" ' } else { '' }
196 cmd := 'cd "${work_dir}" && ${env_prefix}"${v2_binary}" -gc none -nocache --no-parallel ${args.join(' ')} "${source_path}"'
197 res := os.execute(cmd)
198 c_path := os.join_path(work_dir, '${name}.c')
199 c_source := if os.exists(c_path) { os.read_file(c_path) or { '' } } else { '' }
200 return CleancCliResult{
201 exit_code: res.exit_code
202 output: res.output
203 c_source: c_source
204 out_path: e2e_binary_output_path(work_dir, name)
205 c_path: c_path
206 }
207}
208
209fn run_v2_without_output(v2_binary string, tmp_dir string, name string, args []string, source string) CleancCliResult {
210 return run_v2_without_output_in_dir(v2_binary, tmp_dir, name, args, source, tmp_dir)
211}
212
213fn run_v1_v2_without_output_in_dir(v1_binary string, v2_binary string, tmp_dir string, name string, args []string, source string, work_dir string) CleancCliResult {
214 source_path := os.join_path(tmp_dir, '${name}.v')
215 os.write_file(source_path, source) or { panic(err) }
216 os.mkdir_all(work_dir) or { panic(err) }
217 repo_vexe := os.join_path(e2e_repo_root(), 'v')
218 env_prefix := if host_c_e2e_flags().len > 0 { 'V2CFLAGS="${host_c_e2e_flags()}" ' } else { '' }
219 cmd := 'cd "${work_dir}" && ${env_prefix}VEXE="${repo_vexe}" V_V2_EXE="${v2_binary}" "${v1_binary}" -v2 -gc none -nocache --no-parallel ${args.join(' ')} "${source_path}"'
220 res := os.execute(cmd)
221 c_path := os.join_path(work_dir, '${name}.c')
222 c_source := if os.exists(c_path) { os.read_file(c_path) or { '' } } else { '' }
223 return CleancCliResult{
224 exit_code: res.exit_code
225 output: res.output
226 c_source: c_source
227 out_path: e2e_binary_output_path(work_dir, name)
228 c_path: c_path
229 }
230}
231
232fn run_v1_v2_to_output(v1_binary string, v2_binary string, tmp_dir string, name string, args []string, source string, output_path string) CleancCliResult {
233 source_path := os.join_path(tmp_dir, '${name}.v')
234 os.write_file(source_path, source) or { panic(err) }
235 repo_vexe := os.join_path(e2e_repo_root(), 'v')
236 cmd := 'cd "${e2e_repo_root()}" && VEXE="${repo_vexe}" V_V2_EXE="${v2_binary}" "${v1_binary}" -v2 -gc none -nocache --no-parallel ${args.join(' ')} -o "${output_path}" "${source_path}"'
237 res := os.execute(cmd)
238 c_source := if os.exists(output_path) { os.read_file(output_path) or { '' } } else { '' }
239 return CleancCliResult{
240 exit_code: res.exit_code
241 output: res.output
242 c_source: c_source
243 out_path: output_path
244 c_path: output_path
245 }
246}
247
248fn run_v2_to_output_with_cache(v2_binary string, tmp_dir string, name string, args []string, source string, output_path string) CleancCliResult {
249 source_path := os.join_path(tmp_dir, '${name}.v')
250 os.write_file(source_path, source) or { panic(err) }
251 cmd := 'cd "${e2e_repo_root()}" && "${v2_binary}" -gc none --no-parallel ${args.join(' ')} -o "${output_path}" "${source_path}"'
252 res := os.execute(cmd)
253 c_path := generated_c_output_path(output_path)
254 c_source := if os.exists(c_path) { os.read_file(c_path) or { '' } } else { '' }
255 return CleancCliResult{
256 exit_code: res.exit_code
257 output: res.output
258 c_source: c_source
259 out_path: output_path
260 c_path: c_path
261 }
262}
263
264fn assert_cli_success(res CleancCliResult) {
265 assert res.exit_code == 0, res.output
266 assert os.exists(res.out_path), res.output
267 assert res.c_source.len > 0, res.output
268}
269
270fn assert_generated_c_static_assert_contains(res CleancCliResult, expected string) {
271 assert_cli_success(res)
272 assert res.c_source.contains('_Static_assert(0, "${expected}'), res.c_source
273}
274
275fn assert_generated_c_heap_runtime_static_assert_contains(res CleancCliResult, helper string) {
276 assert_generated_c_static_assert_contains(res,
277 '${e2e_freestanding_missing_heap_runtime_message}: ${helper}')
278}
279
280fn assert_binary_success(res CleancCliResult) {
281 assert res.exit_code == 0, res.output
282 assert os.exists(res.out_path), res.output
283}
284
285fn assert_generated_c_only(res CleancCliResult) {
286 assert res.exit_code == 0, res.output
287 assert os.exists(res.c_path), res.output
288 assert res.c_source.len > 0, res.output
289 assert !os.exists(res.out_path), 'unexpected local executable ${res.out_path}\n${res.output}'
290 assert res.output.contains('local C compilation disabled for this target'), res.output
291}
292
293fn assert_cli_failure_contains(res CleancCliResult, expected string) {
294 assert res.exit_code != 0, res.output
295 assert res.output.contains(expected), res.output
296 assert !os.exists(res.out_path), 'failed command wrote unexpected output ${res.out_path}\n${res.output}'
297 if res.c_path != res.out_path {
298 assert !os.exists(res.c_path), 'failed command wrote unexpected C output ${res.c_path}\n${res.output}'
299 }
300}
301
302fn host_cc_available() bool {
303 $if windows {
304 return false
305 }
306 return os.execute('cc --version').exit_code == 0
307}
308
309fn host_c_e2e_flags() string {
310 return '-D_GNU_SOURCE -Wno-error=incompatible-pointer-types -Wno-error=implicit-function-declaration'
311}
312
313fn concrete_non_host_e2e_os() string {
314 host_os := normalize_e2e_os_name(os.user_os())
315 return match host_os {
316 'windows' { 'linux' }
317 else { 'windows' }
318 }
319}
320
321fn target_fixture_source(name string) string {
322 path := os.join_path(e2e_repo_root(), 'vlib', 'v2', 'tests', 'target_codegen_example', name)
323 return os.read_file(path) or { panic('cannot read ${path}: ${err}') }
324}
325
326fn target_directive_source() string {
327 return target_fixture_source('target_directives.vv2')
328}
329
330fn cross_directive_source() string {
331 return target_fixture_source('cross_directives.vv2')
332}
333
334fn freestanding_directive_source() string {
335 return target_fixture_source('freestanding_directives.vv2')
336}
337
338fn freestanding_none_source() string {
339 return target_fixture_source('freestanding_none.vv2')
340}
341
342fn assert_concrete_target_markers(c_source string, target string) {
343 assert c_source.contains(match target {
344 'linux' { target_linux_marker }
345 'macos' { target_macos_marker }
346 'windows' { target_windows_marker }
347 else { '' }
348 })
349 if target != 'linux' {
350 assert !c_source.contains(target_linux_marker)
351 }
352 if target != 'macos' {
353 assert !c_source.contains(target_macos_marker)
354 }
355 if target != 'windows' {
356 assert !c_source.contains(target_windows_marker)
357 }
358 assert !c_source.contains(target_cross_marker)
359 assert !c_source.contains(target_freestanding_marker)
360}
361
362fn assert_no_os_runtime_headers(c_source string) {
363 for header in ['#include <unistd.h>', '#include <pthread.h>', '#include <dirent.h>',
364 '#include <windows.h>', '#include <mach/mach.h>', '#include <termios.h>',
365 '#include <sys/wait.h>'] {
366 assert !c_source.contains(header)
367 }
368}
369
370fn assert_no_obvious_hosted_headers(c_source string) {
371 for header in ['#include <stdio.h>', '#include <stdlib.h>', '#include <unistd.h>'] {
372 assert !c_source.contains(header)
373 }
374}
375
376fn c_source_line_matches(c_source string, idx int, line string) bool {
377 end_idx := idx + line.len
378 return (idx == 0 || c_source[idx - 1] == `\n`)
379 && (end_idx == c_source.len || c_source[end_idx] == `\n`)
380}
381
382fn c_source_find_complete_line(c_source string, line string) ?int {
383 mut search_from := 0
384 for {
385 idx := c_source.index_after(line, search_from) or { return none }
386 if c_source_line_matches(c_source, idx, line) {
387 return idx
388 }
389 search_from = idx + 1
390 }
391 return none
392}
393
394fn c_source_count_complete_lines(c_source string, line string) int {
395 mut count := 0
396 mut search_from := 0
397 for {
398 idx := c_source.index_after(line, search_from) or { break }
399 if c_source_line_matches(c_source, idx, line) {
400 count++
401 }
402 search_from = idx + 1
403 }
404 return count
405}
406
407fn c_source_find_line_prefix(c_source string, prefix string) ?int {
408 mut search_from := 0
409 for {
410 idx := c_source.index_after(prefix, search_from) or { return none }
411 if idx == 0 || c_source[idx - 1] == `\n` {
412 return idx
413 }
414 search_from = idx + 1
415 }
416 return none
417}
418
419fn c_source_find_line_prefix_after(c_source string, prefix string, start int) ?int {
420 mut search_from := start
421 for {
422 idx := c_source.index_after(prefix, search_from) or { return none }
423 if idx == 0 || c_source[idx - 1] == `\n` {
424 return idx
425 }
426 search_from = idx + 1
427 }
428 return none
429}
430
431fn assert_array_contains_fallback_decl_order(c_source string, fn_name string, prototype string) {
432 prototype_count := c_source_count_complete_lines(c_source, prototype)
433 assert prototype_count == 1, 'expected exactly one complete-line fallback prototype `${prototype}`, found ${prototype_count}'
434 proto_idx := c_source_find_complete_line(c_source, prototype) or {
435 assert false, 'missing array contains fallback prototype `${prototype}`'
436 return
437 }
438 symbol := '${fn_name}('
439 first_symbol_idx := c_source.index(symbol) or {
440 assert false, 'missing array contains fallback symbol `${symbol}`'
441 return
442 }
443 assert first_symbol_idx == proto_idx + 'bool '.len, '`${fn_name}` appears before its fallback prototype'
444
445 first_use_idx := c_source.index_after(symbol, proto_idx + prototype.len) or {
446 assert false, 'missing array contains fallback use `${symbol}` after prototype'
447 return
448 }
449 assert proto_idx < first_use_idx, '`${fn_name}` fallback prototype must precede first use'
450 body_prefix := 'bool ${fn_name}('
451 if body_idx := c_source_find_line_prefix_after(c_source, body_prefix, proto_idx + prototype.len) {
452 assert first_use_idx < body_idx, '`${fn_name}` first use should occur before generated body'
453 }
454 weak_body := '__attribute__((weak)) bool ${fn_name}('
455 if weak_idx := c_source_find_line_prefix(c_source, weak_body) {
456 assert first_use_idx < weak_idx, '`${fn_name}` first use should occur before fallback weak body'
457 assert proto_idx < weak_idx, '`${fn_name}` fallback prototype must precede weak body'
458 }
459}
460
461fn test_cleanc_cli_array_contains_fallback_decls_precede_pass5_uses() {
462 tmp_dir := os.join_path(os.vtmp_dir(), 'v2_cleanc_array_contains_fallback_${os.getpid()}')
463 os.rmdir_all(tmp_dir) or {}
464 os.mkdir_all(tmp_dir) or { panic(err) }
465 defer {
466 os.rmdir_all(tmp_dir) or {}
467 }
468 v2_binary := build_v2_for_target_e2e(tmp_dir)
469 res := run_v2_to_c_project_files(v2_binary, tmp_dir, 'array_contains_fallback', [], {
470 'ssa/ids.v': 'module ssa
471
472pub type ValueID = int
473pub type BlockID = int
474'
475 'types/types.v': 'module types
476
477pub type Type = int
478'
479 'main.v': 'module main
480
481import ssa
482import types
483
484fn main() {
485 values := [ssa.ValueID(1)]
486 blocks := [ssa.BlockID(2)]
487 type_ids := [types.Type(3)]
488 assert values.contains(ssa.ValueID(1))
489 assert blocks.contains(ssa.BlockID(2))
490 assert type_ids.contains(types.Type(3))
491}
492'
493 }, 'main.v')
494 assert_cli_success(res)
495 assert_array_contains_fallback_decl_order(res.c_source, 'Array_ssa__ValueID_contains',
496 'bool Array_ssa__ValueID_contains(Array_ssa__ValueID a, ssa__ValueID v);')
497 assert_array_contains_fallback_decl_order(res.c_source, 'Array_ssa__BlockID_contains',
498 'bool Array_ssa__BlockID_contains(Array_ssa__BlockID a, ssa__BlockID v);')
499 assert_array_contains_fallback_decl_order(res.c_source, 'Array_types__Type_contains',
500 'bool Array_types__Type_contains(Array_types__Type a, types__Type v);')
501}
502
503fn test_cleanc_cli_generated_c_target_matrix() {
504 tmp_dir := os.join_path(os.vtmp_dir(), 'v2_cleanc_target_e2e_${os.getpid()}')
505 os.rmdir_all(tmp_dir) or {}
506 os.mkdir_all(tmp_dir) or { panic(err) }
507 defer {
508 os.rmdir_all(tmp_dir) or {}
509 }
510 v2_binary := build_v2_for_target_e2e(tmp_dir)
511 source := target_directive_source()
512
513 default_res := run_v2_to_c(v2_binary, tmp_dir, 'default_host', [], source)
514 assert_cli_success(default_res)
515 assert !default_res.c_source.contains(target_cross_marker)
516 assert !default_res.c_source.contains(target_freestanding_marker)
517 host_os := normalize_e2e_os_name(os.user_os())
518 if host_os in ['linux', 'macos', 'windows'] {
519 assert_concrete_target_markers(default_res.c_source, host_os)
520 }
521
522 for target in ['linux', 'macos', 'windows'] {
523 res := run_v2_to_c(v2_binary, tmp_dir, 'target_${target}', ['-os', target], source)
524 assert_cli_success(res)
525 assert_concrete_target_markers(res.c_source, target)
526 }
527
528 cross_res := run_v2_to_c(v2_binary, tmp_dir, 'target_cross', ['-os', 'cross'],
529 cross_directive_source())
530 assert_cli_success(cross_res)
531 assert cross_res.c_source.contains(target_linux_marker)
532 assert cross_res.c_source.contains(target_macos_marker)
533 assert cross_res.c_source.contains(target_windows_marker)
534 assert cross_res.c_source.contains(target_cross_marker)
535 assert cross_res.c_source.contains('defined(__linux__)')
536 assert cross_res.c_source.contains('defined(_WIN32)')
537 assert cross_res.c_source.contains('defined(__APPLE__)')
538
539 free_res := run_v2_to_c(v2_binary, tmp_dir, 'target_freestanding', [
540 '-freestanding',
541 '-os',
542 'linux',
543 '--skip-builtin',
544 ], freestanding_directive_source())
545 assert_cli_success(free_res)
546 assert free_res.c_source.contains(target_linux_marker)
547 assert free_res.c_source.contains(target_freestanding_marker)
548 assert !free_res.c_source.contains(target_windows_marker)
549 assert_no_os_runtime_headers(free_res.c_source)
550}
551
552fn test_cleanc_cli_comptime_if_directives_follow_active_branch() {
553 tmp_dir := os.join_path(os.vtmp_dir(), 'v2_cleanc_comptime_directives_${os.getpid()}')
554 os.rmdir_all(tmp_dir) or {}
555 os.mkdir_all(tmp_dir) or { panic(err) }
556 defer {
557 os.rmdir_all(tmp_dir) or {}
558 }
559 v2_binary := build_v2_for_target_e2e(tmp_dir)
560
561 linux_res := run_v2_to_c(v2_binary, tmp_dir, 'linux_inactive_windows_directive', [
562 '-os',
563 'linux',
564 ], 'module main
565
566\$if windows {
567 #include <inactive_windows_marker.h>
568}
569
570#include <active_comptime_marker.h>
571
572fn main() {}
573')
574 assert_cli_success(linux_res)
575 assert !linux_res.c_source.contains(inactive_windows_marker)
576 assert linux_res.c_source.contains(active_comptime_marker)
577
578 freestanding_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_inactive_directive', [
579 '-freestanding',
580 '-os',
581 'linux',
582 '--skip-builtin',
583 ], 'module main
584
585\$if !freestanding {
586 #include <inactive_freestanding_marker.h>
587}
588
589#include freestanding <active_comptime_marker.h>
590
591fn main() {}
592')
593 assert_cli_success(freestanding_res)
594 assert !freestanding_res.c_source.contains(inactive_freestanding_marker)
595 assert freestanding_res.c_source.contains(active_comptime_marker)
596}
597
598fn test_cleanc_cli_freestanding_diagnostics_and_user_directives() {
599 tmp_dir := os.join_path(os.vtmp_dir(), 'v2_cleanc_target_diag_${os.getpid()}')
600 os.rmdir_all(tmp_dir) or {}
601 os.mkdir_all(tmp_dir) or { panic(err) }
602 defer {
603 os.rmdir_all(tmp_dir) or {}
604 }
605 v2_binary := build_v2_for_target_e2e(tmp_dir)
606
607 help_res := os.execute('"${v2_binary}" --definitely-unknown-freestanding-flag')
608 assert help_res.exit_code != 0, help_res.output
609 assert help_res.output.contains('-fhooks <values>'), help_res.output
610 assert help_res.output.contains('Advanced freestanding hooks for --skip-builtin --skip-type-check stubs'), help_res.output
611 assert help_res.output.contains('-b <name>'), help_res.output
612 assert help_res.output.contains('omit for cleanc'), help_res.output
613
614 assert help_res.output.contains('Override target OS (default: host OS)'), help_res.output
615
616 fixture_empty := target_fixture_source('freestanding_empty.vv2')
617 fixture_output := target_fixture_source('freestanding_output.vv2')
618 fixture_panic := target_fixture_source('freestanding_panic.vv2')
619 fixture_alloc := target_fixture_source('freestanding_alloc.vv2')
620 none_without_freestanding_res := run_v2_to_c(v2_binary, tmp_dir, 'none_without_freestanding', [
621 '-os',
622 'none',
623 ], fixture_empty)
624 assert_cli_failure_contains(none_without_freestanding_res, '-os none requires -freestanding')
625
626 freestanding_cross_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_cross', [
627 '-freestanding',
628 '-os',
629 'cross',
630 '--skip-builtin',
631 ], fixture_empty)
632 assert_cli_failure_contains(freestanding_cross_res, '-freestanding -os cross is not supported')
633
634 os_import_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_os_import', [
635 '-freestanding',
636 '-os',
637 'linux',
638 ], 'module main
639
640import os
641
642fn main() {
643 _ := os.args.len
644}
645 ')
646 assert_cli_failure_contains(os_import_res, 'freestanding target cannot use module os')
647
648 flat_os_import_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_flat_os_import', [
649 '-freestanding',
650 '-os',
651 'none',
652 '--skip-builtin',
653 '--skip-type-check',
654 ], 'module main
655
656import os
657
658fn main() {}
659 ')
660 assert_cli_failure_contains(flat_os_import_res, 'freestanding target cannot use module os')
661
662 for import_name in ['time', 'term', 'net', 'net.http', 'sync'] {
663 import_res := run_v2_to_c(v2_binary, tmp_dir,
664 'freestanding_${import_name.replace('.', '_')}_import', [
665 '-freestanding',
666 '-os',
667 'linux',
668 '--skip-builtin',
669 ], 'module main
670
671import ${import_name}
672
673fn main() {}
674')
675 assert_cli_failure_contains(import_res,
676 'freestanding target cannot use module ${import_name.all_before('.')}')
677 }
678
679 print_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_print', [
680 '-freestanding',
681 '-os',
682 'linux',
683 ], fixture_output)
684 assert_cli_failure_contains(print_res, 'freestanding target cannot use builtin println')
685
686 minimal_runtime_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_minimal_runtime', [
687 '-freestanding',
688 '-os',
689 'linux',
690 ], fixture_empty)
691 assert_cli_success(minimal_runtime_res)
692 assert_no_os_runtime_headers(minimal_runtime_res.c_source)
693
694 output_hook_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_output_hook', [
695 '-freestanding',
696 '-fhooks',
697 'output',
698 '-os',
699 'linux',
700 '--skip-builtin',
701 '--skip-type-check',
702 ], fixture_output)
703 assert_cli_success(output_hook_res)
704 assert output_hook_res.c_source.contains('isize v_platform_write(int stream, const u8* buf, isize len);')
705 assert output_hook_res.c_source.contains('v_platform_write(fd, ptr, remaining_bytes)')
706 assert !output_hook_res.c_source.contains('v_platform_panic'), output_hook_res.c_source
707 assert !output_hook_res.c_source.contains('v_platform_malloc'), output_hook_res.c_source
708 assert !output_hook_res.c_source.contains('v_platform_realloc'), output_hook_res.c_source
709 assert !output_hook_res.c_source.contains('v_platform_free'), output_hook_res.c_source
710
711 define_prealloc_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_define_prealloc', [
712 '-freestanding',
713 '-d',
714 'prealloc',
715 '-fhooks',
716 'output',
717 '-os',
718 'linux',
719 '--skip-builtin',
720 '--skip-type-check',
721 ], fixture_output)
722 assert_cli_success(define_prealloc_res)
723 assert !define_prealloc_res.output.contains('freestanding target cannot use -prealloc')
724
725 output_hook_builtin_res := run_v2_to_c(v2_binary, tmp_dir,
726 'freestanding_output_hook_builtin_runtime', [
727 '-freestanding',
728 '-fhooks',
729 'output',
730 '-os',
731 'linux',
732 ], fixture_output)
733 assert_cli_failure_contains(output_hook_builtin_res,
734 'freestanding target platform hooks currently require --skip-builtin and --skip-type-check')
735 assert !output_hook_builtin_res.output.contains('__malloc'), output_hook_builtin_res.output
736
737 output_hook_skip_builtin_typecheck_res := run_v2_to_c(v2_binary, tmp_dir,
738 'freestanding_output_hook_skip_builtin_typecheck_gate', [
739 '-freestanding',
740 '-fhooks',
741 'output',
742 '-os',
743 'linux',
744 '--skip-builtin',
745 ], fixture_output)
746 assert_cli_failure_contains(output_hook_skip_builtin_typecheck_res,
747 'freestanding target platform hooks currently require --skip-builtin and --skip-type-check')
748 assert !output_hook_skip_builtin_typecheck_res.output.contains('unknown ident'), output_hook_skip_builtin_typecheck_res.output
749
750 for hook_name in ['output', 'panic', 'alloc', 'minimal'] {
751 hook_runtime_res := run_v2_to_c(v2_binary, tmp_dir,
752 'freestanding_${hook_name}_builtin_runtime_gate', [
753 '-freestanding',
754 '-fhooks',
755 hook_name,
756 '-os',
757 'linux',
758 ], fixture_empty)
759 assert_cli_failure_contains(hook_runtime_res,
760 'freestanding target platform hooks currently require --skip-builtin and --skip-type-check')
761 assert !hook_runtime_res.output.contains('__malloc'), hook_runtime_res.output
762 }
763
764 output_missing_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_output_missing', [
765 '-freestanding',
766 '-fhooks',
767 'panic',
768 '-os',
769 'linux',
770 '--skip-builtin',
771 '--skip-type-check',
772 ], fixture_output)
773 assert_cli_failure_contains(output_missing_res,
774 'freestanding target cannot use builtin println without output platform hook')
775
776 spoofed_output_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_spoofed_output_define', [
777 '-freestanding',
778 '-d',
779 'freestanding_output',
780 '-os',
781 'linux',
782 '--skip-builtin',
783 '--skip-type-check',
784 ], fixture_output)
785 assert_cli_failure_contains(spoofed_output_res,
786 'freestanding target cannot use builtin println without output platform hook')
787
788 for helper_call in ['_write_buf_to_fd(1, 0, 0)', "_writeln_to_fd(1, '')", 'flush_stdout()',
789 'flush_stderr()'] {
790 helper_res := run_v2_to_c(v2_binary, tmp_dir,
791 'freestanding_output_helper_${helper_call.all_before('(')}', [
792 '-freestanding',
793 '-os',
794 'linux',
795 '--skip-builtin',
796 '--skip-type-check',
797 ], 'module main
798
799fn main() {
800 ${helper_call}
801}
802')
803 assert_generated_c_static_assert_contains(helper_res,
804 e2e_freestanding_missing_output_hook_message)
805 }
806
807 for print_call in ['println(123)', 'print(true)'] {
808 print_conversion_res := run_v2_to_c(v2_binary, tmp_dir,
809 'freestanding_output_only_${print_call.all_before('(')}_conversion', [
810 '-freestanding',
811 '-fhooks',
812 'output',
813 '-os',
814 'linux',
815 '--skip-builtin',
816 '--skip-type-check',
817 ], 'module main
818
819fn main() {
820 ${print_call}
821}
822')
823 assert_generated_c_static_assert_contains(print_conversion_res,
824 e2e_freestanding_missing_format_hook_message)
825 }
826
827 output_hook_string_literal_res := run_v2_to_c(v2_binary, tmp_dir,
828 'freestanding_output_hook_string_literal', [
829 '-freestanding',
830 '-fhooks',
831 'output',
832 '-os',
833 'linux',
834 '--skip-builtin',
835 '--skip-type-check',
836 ], "module main
837
838fn main() {
839 println('ok')
840}
841 ")
842 assert_cli_success(output_hook_string_literal_res)
843 assert output_hook_string_literal_res.c_source.contains('v_platform_write')
844 assert !output_hook_string_literal_res.c_source.contains(e2e_freestanding_missing_format_hook_message), output_hook_string_literal_res.c_source
845
846 output_hook_unknown_print_res := run_v2_to_c(v2_binary, tmp_dir,
847 'freestanding_output_hook_unknown_print_arg', [
848 '-freestanding',
849 '-fhooks',
850 'output',
851 '-os',
852 'linux',
853 '--skip-builtin',
854 '--skip-type-check',
855 ], 'module main
856
857fn C.platform_value() int
858
859fn main() {
860 x := C.platform_value()
861 println(x)
862}
863 ')
864 assert_generated_c_static_assert_contains(output_hook_unknown_print_res,
865 e2e_freestanding_missing_format_hook_message)
866
867 panic_hook_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_panic_hook', [
868 '-freestanding',
869 '-fhooks',
870 'panic',
871 '-os',
872 'linux',
873 '--skip-builtin',
874 '--skip-type-check',
875 ], fixture_panic)
876 assert_cli_success(panic_hook_res)
877 assert panic_hook_res.c_source.contains('void v_platform_panic(const u8* msg, isize len);')
878 assert !panic_hook_res.c_source.contains('v_platform_write'), panic_hook_res.c_source
879 assert !panic_hook_res.c_source.contains('v_platform_malloc'), panic_hook_res.c_source
880
881 panic_missing_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_panic_missing', [
882 '-freestanding',
883 '-fhooks',
884 'output',
885 '-os',
886 'linux',
887 ], fixture_panic)
888 assert_cli_failure_contains(panic_missing_res,
889 'freestanding target cannot use builtin panic without panic platform hook')
890
891 bang_missing_panic_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_bang_missing_panic', [
892 '-freestanding',
893 '-fhooks',
894 'output,alloc',
895 '-os',
896 'linux',
897 '--skip-builtin',
898 '--skip-type-check',
899 ], "module main
900
901fn fallible() !int {
902 return error('x')
903}
904
905fn main() {
906 _ := fallible()!
907}
908 ")
909 assert_generated_c_static_assert_contains(bang_missing_panic_res,
910 e2e_freestanding_missing_panic_hook_message)
911
912 bang_panic_hook_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_bang_panic_hook', [
913 '-freestanding',
914 '-fhooks',
915 'panic',
916 '-os',
917 'linux',
918 '--skip-builtin',
919 '--skip-type-check',
920 ], "module main
921
922fn fallible() !int {
923 return error('x')
924}
925
926fn main() {
927 _ := fallible()!
928}
929 ")
930 assert_generated_c_heap_runtime_static_assert_contains(bang_panic_hook_res, 'IError__str')
931
932 panic_ierror_hook_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_panic_ierror_hook', [
933 '-freestanding',
934 '-fhooks',
935 'output,panic,alloc',
936 '-os',
937 'linux',
938 '--skip-builtin',
939 '--skip-type-check',
940 ], "module main
941
942fn main() {
943 err := error('x')
944 panic(err)
945}
946 ")
947 assert_generated_c_heap_runtime_static_assert_contains(panic_ierror_hook_res, 'IError__str')
948
949 bang_result_propagation_res := run_v2_to_c(v2_binary, tmp_dir,
950 'freestanding_bang_result_propagation', [
951 '-freestanding',
952 '-fhooks',
953 'output,alloc',
954 '-os',
955 'linux',
956 '--skip-builtin',
957 '--skip-type-check',
958 ], "module main
959
960fn fallible() !int {
961 return error('x')
962}
963
964fn wrapper() !int {
965 return fallible()!
966}
967
968fn main() {}
969 ")
970 assert_cli_success(bang_result_propagation_res)
971
972 assert_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_assert', [
973 '-freestanding',
974 '-fhooks',
975 'output,panic',
976 '-os',
977 'linux',
978 '--skip-builtin',
979 '--skip-type-check',
980 ], 'module main
981
982fn main() {
983 assert true
984}
985 ')
986 assert_cli_failure_contains(assert_res,
987 'freestanding target cannot use assert because failed assertions need hosted output/exit support')
988
989 spoofed_panic_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_spoofed_panic_define', [
990 '-freestanding',
991 '-d',
992 'freestanding_panic',
993 '-os',
994 'linux',
995 '--skip-builtin',
996 '--skip-type-check',
997 ], fixture_panic)
998 assert_cli_failure_contains(spoofed_panic_res,
999 'freestanding target cannot use builtin panic without panic platform hook')
1000
1001 alloc_missing_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_alloc_missing', [
1002 '-freestanding',
1003 '-fhooks',
1004 'output',
1005 '-os',
1006 'linux',
1007 '--skip-builtin',
1008 '--skip-type-check',
1009 ], fixture_alloc)
1010 assert_generated_c_static_assert_contains(alloc_missing_res,
1011 e2e_freestanding_missing_alloc_hook_message)
1012
1013 spoofed_alloc_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_spoofed_alloc_define', [
1014 '-freestanding',
1015 '-d',
1016 'freestanding_alloc',
1017 '-os',
1018 'linux',
1019 '--skip-builtin',
1020 '--skip-type-check',
1021 ], fixture_alloc)
1022 assert_generated_c_static_assert_contains(spoofed_alloc_res,
1023 e2e_freestanding_missing_alloc_hook_message)
1024
1025 arguments_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_arguments', [
1026 '-freestanding',
1027 '-fhooks',
1028 'alloc',
1029 '-os',
1030 'linux',
1031 '--skip-builtin',
1032 '--skip-type-check',
1033 ], 'module main
1034
1035fn main() {
1036 _ := arguments()
1037}
1038')
1039 assert_generated_c_static_assert_contains(arguments_res,
1040 e2e_freestanding_missing_alloc_hook_message)
1041
1042 alloc_hook_string_interpolation_res := run_v2_to_c(v2_binary, tmp_dir,
1043 'freestanding_alloc_string_interpolation', [
1044 '-freestanding',
1045 '-fhooks',
1046 'alloc',
1047 '-os',
1048 'linux',
1049 '--skip-builtin',
1050 '--skip-type-check',
1051 ], "module main
1052
1053fn main() {
1054 _ := '\${1}'
1055}
1056 ")
1057 assert_generated_c_static_assert_contains(alloc_hook_string_interpolation_res,
1058 e2e_freestanding_missing_format_hook_message)
1059
1060 string_concat_missing_alloc_res := run_v2_to_c(v2_binary, tmp_dir,
1061 'freestanding_string_concat_missing_alloc', [
1062 '-freestanding',
1063 '-fhooks',
1064 'output,panic',
1065 '-os',
1066 'linux',
1067 '--skip-builtin',
1068 '--skip-type-check',
1069 ], "module main
1070
1071fn main() {
1072 s := 'left'
1073 _ := s + 'right'
1074}
1075 ")
1076 assert_generated_c_heap_runtime_static_assert_contains(string_concat_missing_alloc_res,
1077 'string__plus')
1078
1079 ambiguous_string_concat_missing_alloc_res := run_v2_to_c(v2_binary, tmp_dir,
1080 'freestanding_ambiguous_string_concat_missing_alloc', [
1081 '-freestanding',
1082 '-fhooks',
1083 'output,panic',
1084 '-os',
1085 'linux',
1086 '--skip-builtin',
1087 '--skip-type-check',
1088 ], 'module main
1089
1090fn C.platform_string() string
1091
1092fn main() {
1093 a := C.platform_string()
1094 b := C.platform_string()
1095 _ := a + b
1096}
1097 ')
1098 assert_generated_c_heap_runtime_static_assert_contains(ambiguous_string_concat_missing_alloc_res,
1099 'string__plus')
1100
1101 string_plus_assign_missing_alloc_res := run_v2_to_c(v2_binary, tmp_dir,
1102 'freestanding_string_plus_assign_missing_alloc', [
1103 '-freestanding',
1104 '-fhooks',
1105 'output,panic',
1106 '-os',
1107 'linux',
1108 '--skip-builtin',
1109 '--skip-type-check',
1110 ], "module main
1111
1112fn main() {
1113 mut s := 'left'
1114 s += 'right'
1115}
1116 ")
1117 assert_generated_c_heap_runtime_static_assert_contains(string_plus_assign_missing_alloc_res,
1118 'string__plus')
1119
1120 ambiguous_string_plus_assign_missing_alloc_res := run_v2_to_c(v2_binary, tmp_dir,
1121 'freestanding_ambiguous_string_plus_assign_missing_alloc', [
1122 '-freestanding',
1123 '-fhooks',
1124 'output,panic',
1125 '-os',
1126 'linux',
1127 '--skip-builtin',
1128 '--skip-type-check',
1129 ], 'module main
1130
1131fn C.platform_string() string
1132
1133fn main() {
1134 mut s := C.platform_string()
1135 s += C.platform_string()
1136}
1137 ')
1138 assert_generated_c_heap_runtime_static_assert_contains(ambiguous_string_plus_assign_missing_alloc_res,
1139 'string__plus')
1140
1141 numeric_plus_without_alloc_res := run_v2_to_c(v2_binary, tmp_dir,
1142 'freestanding_numeric_plus_without_alloc', [
1143 '-freestanding',
1144 '-fhooks',
1145 'output,panic',
1146 '-os',
1147 'linux',
1148 '--skip-builtin',
1149 '--skip-type-check',
1150 ], 'module main
1151
1152fn main() {
1153 _ := 1 + 2
1154 mut n := 1
1155 n += 2
1156}
1157 ')
1158 assert_cli_success(numeric_plus_without_alloc_res)
1159
1160 fixed_array_index_without_alloc_res := run_v2_to_c(v2_binary, tmp_dir,
1161 'freestanding_fixed_array_index_without_alloc', [
1162 '-freestanding',
1163 '-fhooks',
1164 'output,panic',
1165 '-os',
1166 'linux',
1167 '--skip-builtin',
1168 '--skip-type-check',
1169 ], 'module main
1170
1171fn main() {
1172 mut xs := [3]int{init: 0}
1173 xs[0] = 7
1174}
1175 ')
1176 assert_cli_success(fixed_array_index_without_alloc_res)
1177
1178 dynamic_array_literal_alloc_hook_res := run_v2_to_c(v2_binary, tmp_dir,
1179 'freestanding_dynamic_array_literal_alloc_hook', [
1180 '-freestanding',
1181 '-fhooks',
1182 'output,panic,alloc',
1183 '-os',
1184 'linux',
1185 '--skip-builtin',
1186 '--skip-type-check',
1187 ], 'module main
1188
1189fn main() {
1190 _ := [1, 2, 3]
1191}
1192 ')
1193 assert_generated_c_heap_runtime_static_assert_contains(dynamic_array_literal_alloc_hook_res,
1194 'new_array_from_c_array')
1195
1196 map_literal_alloc_hook_res := run_v2_to_c(v2_binary, tmp_dir,
1197 'freestanding_map_literal_alloc_hook', [
1198 '-freestanding',
1199 '-fhooks',
1200 'output,panic,alloc',
1201 '-os',
1202 'linux',
1203 '--skip-builtin',
1204 '--skip-type-check',
1205 ], 'module main
1206
1207fn main() {
1208 _ := {
1209 1: 2
1210 }
1211}
1212 ')
1213 assert_generated_c_heap_runtime_static_assert_contains(map_literal_alloc_hook_res, 'new_map')
1214
1215 numeric_shift_without_alloc_res := run_v2_to_c(v2_binary, tmp_dir,
1216 'freestanding_numeric_shift_without_alloc', [
1217 '-freestanding',
1218 '-fhooks',
1219 'output,panic',
1220 '-os',
1221 'linux',
1222 '--skip-builtin',
1223 '--skip-type-check',
1224 ], 'module main
1225
1226fn main() {
1227 mut flags := 1
1228 _ := flags << 1
1229 flags <<= 1
1230}
1231 ')
1232 assert_cli_success(numeric_shift_without_alloc_res)
1233
1234 user_map_method_without_alloc_res := run_v2_to_c(v2_binary, tmp_dir,
1235 'freestanding_user_map_method_without_alloc', [
1236 '-freestanding',
1237 '-fhooks',
1238 'output,panic',
1239 '-os',
1240 'linux',
1241 '--skip-builtin',
1242 '--skip-type-check',
1243 ], 'module main
1244
1245struct Device {}
1246
1247fn (d Device) map() int {
1248 return 7
1249}
1250
1251fn main() {
1252 d := Device{}
1253 _ := d.map()
1254}
1255 ')
1256 assert_cli_success(user_map_method_without_alloc_res)
1257
1258 user_clone_substr_methods_without_alloc_res := run_v2_to_c(v2_binary, tmp_dir,
1259 'freestanding_user_clone_substr_methods_without_alloc', [
1260 '-freestanding',
1261 '-fhooks',
1262 'output,panic',
1263 '-os',
1264 'linux',
1265 '--skip-builtin',
1266 '--skip-type-check',
1267 ], 'module main
1268
1269struct Device {}
1270
1271fn (d Device) clone() Device {
1272 return d
1273}
1274
1275fn (d Device) substr(start int, end int) Device {
1276 return d
1277}
1278
1279fn main() {
1280 d := Device{}
1281 _ := d.clone()
1282 _ := d.substr(0, 1)
1283}
1284 ')
1285 assert_cli_success(user_clone_substr_methods_without_alloc_res)
1286
1287 array_append_missing_alloc_res := run_v2_to_c(v2_binary, tmp_dir,
1288 'freestanding_array_append_missing_alloc', [
1289 '-freestanding',
1290 '-fhooks',
1291 'output,panic',
1292 '-os',
1293 'linux',
1294 '--skip-builtin',
1295 '--skip-type-check',
1296 ], 'module main
1297
1298fn C.platform_array() []int
1299
1300fn main() {
1301 mut nums := C.platform_array()
1302 nums << 2
1303}
1304 ')
1305 assert_generated_c_heap_runtime_static_assert_contains(array_append_missing_alloc_res,
1306 'array__push')
1307
1308 map_assign_missing_alloc_res := run_v2_to_c(v2_binary, tmp_dir,
1309 'freestanding_map_assign_missing_alloc', [
1310 '-freestanding',
1311 '-fhooks',
1312 'output,panic',
1313 '-os',
1314 'linux',
1315 '--skip-builtin',
1316 '--skip-type-check',
1317 ], 'module main
1318
1319fn C.platform_map() map[int]int
1320
1321fn main() {
1322 mut m := C.platform_map()
1323 m[1] = 1
1324}
1325 ')
1326 assert_generated_c_heap_runtime_static_assert_contains(map_assign_missing_alloc_res, 'map__set')
1327
1328 nested_map_assign_missing_runtime_res := run_v2_to_c(v2_binary, tmp_dir,
1329 'freestanding_nested_map_assign_missing_runtime', [
1330 '-freestanding',
1331 '-os',
1332 'linux',
1333 '--skip-builtin',
1334 ], "module main
1335
1336fn main() {
1337 mut res := map[string]map[string]string{}
1338 lang := 'en'
1339 key := 'msg'
1340 res[lang][key] = 'Hello'
1341}
1342 ")
1343 assert_generated_c_heap_runtime_static_assert_contains(nested_map_assign_missing_runtime_res,
1344 'map__get_and_set')
1345
1346 map_value_array_append_missing_runtime_res := run_v2_to_c(v2_binary, tmp_dir,
1347 'freestanding_map_value_array_append_missing_runtime', [
1348 '-freestanding',
1349 '-os',
1350 'linux',
1351 '--skip-builtin',
1352 ], 'module main
1353
1354fn C.platform_map() map[int][]int
1355
1356fn main() {
1357 mut m := C.platform_map()
1358 m[1] << 2
1359}
1360 ')
1361 assert_generated_c_heap_runtime_static_assert_contains(map_value_array_append_missing_runtime_res,
1362 'map__get_and_set')
1363 assert map_value_array_append_missing_runtime_res.c_source.contains('__new_array_with_default_noscan(0, 0, sizeof(int), NULL)'), map_value_array_append_missing_runtime_res.c_source
1364 assert !map_value_array_append_missing_runtime_res.c_source.contains('map__get(&m'), map_value_array_append_missing_runtime_res.c_source
1365 assert !map_value_array_append_missing_runtime_res.c_source.contains('map__get(&(m)'), map_value_array_append_missing_runtime_res.c_source
1366
1367 array_clone_missing_runtime_res := run_v2_to_c(v2_binary, tmp_dir,
1368 'freestanding_array_clone_missing_runtime', [
1369 '-freestanding',
1370 '-fhooks',
1371 'output,panic,alloc',
1372 '-os',
1373 'linux',
1374 '--skip-builtin',
1375 '--skip-type-check',
1376 ], 'module main
1377
1378fn C.platform_array() []int
1379
1380fn main() {
1381 nums := C.platform_array()
1382 _ := nums.clone()
1383}
1384 ')
1385 assert_generated_c_heap_runtime_static_assert_contains(array_clone_missing_runtime_res,
1386 'array__clone_to_depth')
1387
1388 string_clone_missing_alloc_res := run_v2_to_c(v2_binary, tmp_dir,
1389 'freestanding_string_clone_missing_alloc', [
1390 '-freestanding',
1391 '-fhooks',
1392 'output,panic',
1393 '-os',
1394 'linux',
1395 '--skip-builtin',
1396 '--skip-type-check',
1397 ], 'module main
1398
1399fn C.platform_string() string
1400
1401fn main() {
1402 s := C.platform_string()
1403 _ := s.clone()
1404}
1405 ')
1406 assert_generated_c_heap_runtime_static_assert_contains(string_clone_missing_alloc_res,
1407 'string__clone')
1408
1409 map_clone_missing_alloc_res := run_v2_to_c(v2_binary, tmp_dir,
1410 'freestanding_map_clone_missing_alloc', [
1411 '-freestanding',
1412 '-fhooks',
1413 'output,panic',
1414 '-os',
1415 'linux',
1416 '--skip-builtin',
1417 '--skip-type-check',
1418 ], 'module main
1419
1420fn C.platform_map() map[int]int
1421
1422fn main() {
1423 m := C.platform_map()
1424 _ := m.clone()
1425}
1426 ')
1427 assert_generated_c_heap_runtime_static_assert_contains(map_clone_missing_alloc_res,
1428 'map__clone')
1429
1430 range_slice_missing_alloc_res := run_v2_to_c(v2_binary, tmp_dir,
1431 'freestanding_range_slice_missing_alloc', [
1432 '-freestanding',
1433 '-fhooks',
1434 'output,panic',
1435 '-os',
1436 'linux',
1437 '--skip-builtin',
1438 '--skip-type-check',
1439 ], 'module main
1440
1441fn C.platform_string() string
1442
1443fn main() {
1444 s := C.platform_string()
1445 _ := s[0..1]
1446}
1447 ')
1448 assert_generated_c_heap_runtime_static_assert_contains(range_slice_missing_alloc_res,
1449 'string__substr')
1450
1451 string_substr_missing_alloc_res := run_v2_to_c(v2_binary, tmp_dir,
1452 'freestanding_string_substr_missing_alloc', [
1453 '-freestanding',
1454 '-fhooks',
1455 'output,panic',
1456 '-os',
1457 'linux',
1458 '--skip-builtin',
1459 '--skip-type-check',
1460 ], 'module main
1461
1462fn C.platform_string() string
1463
1464fn main() {
1465 s := C.platform_string()
1466 _ := s.substr(0, 1)
1467}
1468 ')
1469 assert_generated_c_heap_runtime_static_assert_contains(string_substr_missing_alloc_res,
1470 'string__substr')
1471
1472 split_map_assign_missing_alloc_res := run_v2_to_c_files(v2_binary, tmp_dir,
1473 'freestanding_split_map_assign_missing_alloc', [
1474 '-freestanding',
1475 '-fhooks',
1476 'output,panic',
1477 '-os',
1478 'linux',
1479 '--skip-builtin',
1480 '--skip-type-check',
1481 ], {
1482 'main.v': 'module main
1483
1484fn main() {
1485 mut m := C.platform_map()
1486 m[1] = 1
1487}
1488'
1489 'platform.v': 'module main
1490
1491fn C.platform_map() map[int]int
1492'
1493 })
1494 assert_generated_c_heap_runtime_static_assert_contains(split_map_assign_missing_alloc_res,
1495 'map__set')
1496
1497 alloc_hook_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_alloc_hook', [
1498 '-freestanding',
1499 '-fhooks',
1500 'alloc',
1501 '-os',
1502 'linux',
1503 '--skip-builtin',
1504 '--skip-type-check',
1505 ], fixture_alloc)
1506 assert_cli_success(alloc_hook_res)
1507 assert alloc_hook_res.c_source.contains('void* v_platform_malloc(isize n);')
1508 assert alloc_hook_res.c_source.contains('void* v_platform_realloc(void* ptr, isize n);')
1509 assert alloc_hook_res.c_source.contains('void v_platform_free(void* ptr);')
1510 assert !alloc_hook_res.c_source.contains('v_platform_write'), alloc_hook_res.c_source
1511 assert !alloc_hook_res.c_source.contains('v_platform_panic'), alloc_hook_res.c_source
1512
1513 explicit_array_runtime_res := run_v2_to_c(v2_binary, tmp_dir,
1514 'freestanding_explicit_array_runtime_helper', [
1515 '-freestanding',
1516 '-fhooks',
1517 'alloc',
1518 '-os',
1519 'none',
1520 '--skip-builtin',
1521 '--skip-type-check',
1522 ], 'module main
1523
1524fn main() {
1525 _ := new_array_from_c_array(0, 0, 0, 0)
1526}
1527 ')
1528 assert_generated_c_heap_runtime_static_assert_contains(explicit_array_runtime_res,
1529 'new_array_from_c_array')
1530
1531 explicit_array_noscan_runtime_res := run_v2_to_c(v2_binary, tmp_dir,
1532 'freestanding_explicit_array_noscan_runtime_helper', [
1533 '-freestanding',
1534 '-fhooks',
1535 'alloc',
1536 '-os',
1537 'none',
1538 '--skip-builtin',
1539 '--skip-type-check',
1540 ], 'module main
1541
1542fn main() {
1543 _ := new_array_from_c_array_noscan(0, 0, 0, 0)
1544}
1545 ')
1546 assert_generated_c_heap_runtime_static_assert_contains(explicit_array_noscan_runtime_res,
1547 'new_array_from_c_array_noscan')
1548
1549 explicit_array_no_alloc_runtime_res := run_v2_to_c(v2_binary, tmp_dir,
1550 'freestanding_explicit_array_no_alloc_runtime_helper', [
1551 '-freestanding',
1552 '-fhooks',
1553 'output,panic,alloc',
1554 '-os',
1555 'none',
1556 '--skip-builtin',
1557 '--skip-type-check',
1558 ], 'module main
1559
1560fn main() {
1561 _ := new_array_from_c_array_no_alloc(0, 0, 0, 0)
1562}
1563 ')
1564 assert_generated_c_heap_runtime_static_assert_contains(explicit_array_no_alloc_runtime_res,
1565 'new_array_from_c_array_no_alloc')
1566
1567 explicit_array_default_runtime_res := run_v2_to_c(v2_binary, tmp_dir,
1568 'freestanding_explicit_array_default_runtime_helper', [
1569 '-freestanding',
1570 '-fhooks',
1571 'alloc',
1572 '-os',
1573 'none',
1574 '--skip-builtin',
1575 '--skip-type-check',
1576 ], 'module main
1577
1578fn main() {
1579 _ := __new_array_with_default_noscan(0, 0, 0, 0)
1580}
1581 ')
1582 assert_generated_c_heap_runtime_static_assert_contains(explicit_array_default_runtime_res,
1583 '__new_array_with_default_noscan')
1584
1585 explicit_array_repeat_runtime_res := run_v2_to_c(v2_binary, tmp_dir,
1586 'freestanding_explicit_array_repeat_runtime_helper', [
1587 '-freestanding',
1588 '-fhooks',
1589 'alloc',
1590 '-os',
1591 'none',
1592 '--skip-builtin',
1593 '--skip-type-check',
1594 ], 'module main
1595
1596fn main() {
1597 _ := array__repeat()
1598}
1599 ')
1600 assert_generated_c_heap_runtime_static_assert_contains(explicit_array_repeat_runtime_res,
1601 'array__repeat')
1602
1603 explicit_map_runtime_res := run_v2_to_c(v2_binary, tmp_dir,
1604 'freestanding_explicit_map_runtime_helper', [
1605 '-freestanding',
1606 '-fhooks',
1607 'alloc',
1608 '-os',
1609 'none',
1610 '--skip-builtin',
1611 '--skip-type-check',
1612 ], 'module main
1613
1614fn main() {
1615 _ := new_map()
1616}
1617 ')
1618 assert_generated_c_heap_runtime_static_assert_contains(explicit_map_runtime_res, 'new_map')
1619
1620 explicit_array_spread_runtime_res := run_v2_to_c(v2_binary, tmp_dir,
1621 'freestanding_explicit_array_spread_runtime_helper', [
1622 '-freestanding',
1623 '-fhooks',
1624 'alloc',
1625 '-os',
1626 'none',
1627 '--skip-builtin',
1628 '--skip-type-check',
1629 ], 'module main
1630
1631fn main() {
1632 _ := new_array_from_array_and_c_array()
1633}
1634 ')
1635 assert_generated_c_heap_runtime_static_assert_contains(explicit_array_spread_runtime_res,
1636 'new_array_from_array_and_c_array')
1637
1638 explicit_builtin_array_spread_runtime_res := run_v2_to_c(v2_binary, tmp_dir,
1639 'freestanding_explicit_builtin_array_spread_runtime_helper', [
1640 '-freestanding',
1641 '-fhooks',
1642 'alloc',
1643 '-os',
1644 'none',
1645 '--skip-builtin',
1646 '--skip-type-check',
1647 ], 'module main
1648
1649fn main() {
1650 _ := builtin__new_array_from_array_and_c_array()
1651}
1652 ')
1653 assert_generated_c_heap_runtime_static_assert_contains(explicit_builtin_array_spread_runtime_res,
1654 'builtin__new_array_from_array_and_c_array')
1655
1656 tracked_heap_ops_alloc_hook_res := run_v2_to_c(v2_binary, tmp_dir,
1657 'freestanding_tracked_heap_ops_alloc_hook', [
1658 '-freestanding',
1659 '-fhooks',
1660 'output,panic,alloc',
1661 '-os',
1662 'linux',
1663 '--skip-builtin',
1664 '--skip-type-check',
1665 ], 'module main
1666
1667fn C.platform_array() []int
1668fn C.platform_map() map[int]int
1669fn C.platform_string() string
1670
1671fn main() {
1672 mut nums := C.platform_array()
1673 nums << 2
1674 mut m := C.platform_map()
1675 m[1] = 1
1676 _ := m.clone()
1677 s := C.platform_string()
1678 _ := s[0..1]
1679 _ := s.clone()
1680 _ := s.substr(0, 1)
1681}
1682 ')
1683 assert_generated_c_heap_runtime_static_assert_contains(tracked_heap_ops_alloc_hook_res,
1684 'array__push')
1685 assert_generated_c_heap_runtime_static_assert_contains(tracked_heap_ops_alloc_hook_res,
1686 'map__set')
1687 assert_generated_c_heap_runtime_static_assert_contains(tracked_heap_ops_alloc_hook_res,
1688 'map__clone')
1689 assert_generated_c_heap_runtime_static_assert_contains(tracked_heap_ops_alloc_hook_res,
1690 'string__clone')
1691 assert_generated_c_heap_runtime_static_assert_contains(tracked_heap_ops_alloc_hook_res,
1692 'string__substr')
1693
1694 string_concat_alloc_hook_res := run_v2_to_c(v2_binary, tmp_dir,
1695 'freestanding_string_concat_alloc_hook', [
1696 '-freestanding',
1697 '-fhooks',
1698 'alloc',
1699 '-os',
1700 'linux',
1701 '--skip-builtin',
1702 '--skip-type-check',
1703 ], "module main
1704
1705fn main() {
1706 s := 'left'
1707 _ := s + 'right'
1708}
1709 ")
1710 assert_generated_c_heap_runtime_static_assert_contains(string_concat_alloc_hook_res,
1711 'string__plus')
1712
1713 ambiguous_string_concat_alloc_hook_res := run_v2_to_c(v2_binary, tmp_dir,
1714 'freestanding_ambiguous_string_concat_alloc_hook', [
1715 '-freestanding',
1716 '-fhooks',
1717 'alloc',
1718 '-os',
1719 'linux',
1720 '--skip-builtin',
1721 '--skip-type-check',
1722 ], 'module main
1723
1724fn C.platform_string() string
1725
1726fn main() {
1727 a := C.platform_string()
1728 b := C.platform_string()
1729 _ := a + b
1730}
1731 ')
1732 assert_generated_c_heap_runtime_static_assert_contains(ambiguous_string_concat_alloc_hook_res,
1733 'string__plus')
1734
1735 prealloc_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_prealloc', [
1736 '-freestanding',
1737 '-prealloc',
1738 '-os',
1739 'linux',
1740 '--skip-builtin',
1741 ], fixture_empty)
1742 assert_cli_failure_contains(prealloc_res, 'freestanding target cannot use -prealloc')
1743
1744 shared_lib_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_shared_lib', [
1745 '-freestanding',
1746 '-shared',
1747 '-os',
1748 'linux',
1749 '--skip-builtin',
1750 ], 'module main
1751
1752fn main() {}
1753')
1754 assert_cli_failure_contains(shared_lib_res, 'freestanding target cannot use -shared')
1755
1756 hot_fn_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_hot_fn', [
1757 '-freestanding',
1758 '-hot-fn',
1759 'main',
1760 '-os',
1761 'linux',
1762 '--skip-builtin',
1763 ], 'module main
1764
1765fn main() {}
1766')
1767 assert_cli_failure_contains(hot_fn_res, 'freestanding target cannot use -hot-fn')
1768
1769 for call_name in ['eprint', 'eprintln', 'panic'] {
1770 call_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_${call_name}', [
1771 '-freestanding',
1772 '-os',
1773 'linux',
1774 '--skip-builtin',
1775 ], "module main
1776
1777fn main() {
1778 ${call_name}('x')
1779}
1780")
1781 assert_cli_failure_contains(call_res, 'freestanding target cannot use builtin ${call_name}')
1782 }
1783
1784 spawn_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_spawn', [
1785 '-freestanding',
1786 '-os',
1787 'linux',
1788 '--skip-builtin',
1789 ], 'module main
1790
1791fn work() {}
1792
1793fn main() {
1794 spawn work()
1795}
1796')
1797 assert_cli_failure_contains(spawn_res, 'freestanding target cannot use spawn')
1798
1799 lock_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_lock', [
1800 '-freestanding',
1801 '-os',
1802 'linux',
1803 '--skip-builtin',
1804 ], 'module main
1805
1806fn main() {
1807 lock {}
1808}
1809')
1810 assert_cli_failure_contains(lock_res, 'freestanding target cannot use lock/rlock')
1811
1812 shared_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_shared', [
1813 '-freestanding',
1814 '-os',
1815 'linux',
1816 '--skip-builtin',
1817 ], 'module main
1818
1819struct State {
1820 value shared int
1821}
1822
1823fn main() {}
1824')
1825 assert_cli_failure_contains(shared_res, 'freestanding target cannot use shared data')
1826
1827 live_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_live', [
1828 '-freestanding',
1829 '-os',
1830 'linux',
1831 '--skip-builtin',
1832 ], 'module main
1833
1834@[live]
1835fn step() {}
1836
1837fn main() {}
1838')
1839 assert_cli_failure_contains(live_res, 'freestanding target cannot use @[live]')
1840
1841 inactive_branch_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_inactive_branches', [
1842 '-freestanding',
1843 '-os',
1844 'linux',
1845 '--skip-builtin',
1846 '--skip-type-check',
1847 ], "module main
1848
1849fn main() {
1850 \$if !freestanding {
1851 println('inactive')
1852 }
1853 \$if windows {
1854 eprintln('inactive')
1855 }
1856}
1857 ")
1858 assert_cli_success(inactive_branch_res)
1859
1860 inactive_fn_attr_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_inactive_fn_attributes', [
1861 '-freestanding',
1862 '-os',
1863 'linux',
1864 '--skip-builtin',
1865 '--skip-type-check',
1866 ], "module main
1867
1868 @[if !freestanding]
1869 fn hosted_only() {
1870 println('inactive')
1871 }
1872
1873 @[if windows]
1874 fn windows_only() {
1875 eprintln('inactive')
1876 }
1877
1878 fn main() {}
1879 ")
1880 assert_cli_success(inactive_fn_attr_res)
1881
1882 fixed_array_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_fixed_array_no_alloc', [
1883 '-freestanding',
1884 '-os',
1885 'linux',
1886 '--skip-builtin',
1887 '--skip-type-check',
1888 ], 'module main
1889
1890 fn main() {
1891 _ := [3]int{init: 0}
1892 _ := [1, 2, 3]!
1893 }
1894 ')
1895 assert_cli_success(fixed_array_res)
1896
1897 inactive_import_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_inactive_import', [
1898 '-freestanding',
1899 '-os',
1900 'linux',
1901 '--skip-builtin',
1902 ], 'module main
1903
1904\$if !freestanding {
1905 import os
1906}
1907
1908fn main() {}
1909')
1910 assert_cli_success(inactive_import_res)
1911
1912 active_branch_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_active_branch', [
1913 '-freestanding',
1914 '-os',
1915 'linux',
1916 '--skip-builtin',
1917 '--skip-type-check',
1918 ], "module main
1919
1920fn main() {
1921 \$if freestanding {
1922 eprint('active')
1923 }
1924}
1925 ")
1926 assert_cli_failure_contains(active_branch_res, 'freestanding target cannot use builtin eprint')
1927
1928 hook_inactive_branch_res := run_v2_to_c(v2_binary, tmp_dir,
1929 'freestanding_hook_inactive_branch', [
1930 '-freestanding',
1931 '-fhooks',
1932 'output',
1933 '-os',
1934 'linux',
1935 '--skip-builtin',
1936 '--skip-type-check',
1937 ], "module main
1938
1939fn main() {
1940 \$if !freestanding_output {
1941 println('inactive')
1942 }
1943}
1944 ")
1945 assert_cli_success(hook_inactive_branch_res)
1946
1947 user_directive_res := run_v2_to_c(v2_binary, tmp_dir, 'freestanding_user_directives', [
1948 '-freestanding',
1949 '-os',
1950 'linux',
1951 '--skip-builtin',
1952 ], 'module main
1953
1954#include <platform_user_header.h>
1955#flag -DPLATFORM_USER_FLAG
1956fn C.platform_external() int
1957
1958fn main() {
1959 _ := C.platform_external()
1960}
1961 ')
1962 assert_cli_success(user_directive_res)
1963 assert user_directive_res.c_source.contains('#include <platform_user_header.h>')
1964 assert user_directive_res.c_source.contains('platform_external'), user_directive_res.c_source
1965}
1966
1967fn test_cleanc_cli_compiles_generated_c_on_host_when_cc_available() {
1968 if !host_cc_available() {
1969 eprintln('skip: cc is not available for cleanc host compile e2e')
1970 return
1971 }
1972 tmp_dir := os.join_path(os.vtmp_dir(), 'v2_cleanc_host_cc_${os.getpid()}')
1973 os.rmdir_all(tmp_dir) or {}
1974 os.mkdir_all(tmp_dir) or { panic(err) }
1975 defer {
1976 os.rmdir_all(tmp_dir) or {}
1977 }
1978 v2_binary := build_v2_for_target_e2e(tmp_dir)
1979 v1_binary := build_v1_for_target_e2e(tmp_dir)
1980 minimal_source := 'module main
1981
1982fn main() {}
1983'
1984
1985 minimal_res := run_v2_to_binary(v2_binary, tmp_dir, 'host_minimal', [
1986 '-cc',
1987 'cc',
1988 ], minimal_source)
1989 assert_binary_success(minimal_res)
1990 minimal_run := os.execute('"${minimal_res.out_path}"')
1991 assert minimal_run.exit_code == 0, minimal_run.output
1992 assert !os.exists(minimal_res.c_path), minimal_res.output
1993
1994 default_no_output_res := run_v2_without_output(v2_binary, tmp_dir, 'host_default_no_output', [
1995 '-cc',
1996 'cc',
1997 ], minimal_source)
1998 assert_binary_success(default_no_output_res)
1999 default_no_output_run := os.execute('"${default_no_output_res.out_path}"')
2000 assert default_no_output_run.exit_code == 0, default_no_output_run.output
2001 assert !os.exists(default_no_output_res.c_path), default_no_output_res.output
2002
2003 public_default_cwd := os.join_path(tmp_dir, 'public_default_cwd')
2004 public_default_no_output_res := run_v1_v2_without_output_in_dir(v1_binary, v2_binary, tmp_dir,
2005 'public_host_default_no_output', [
2006 '-cc',
2007 'cc',
2008 ], minimal_source, public_default_cwd)
2009 assert_binary_success(public_default_no_output_res)
2010 public_default_no_output_run := os.execute('"${public_default_no_output_res.out_path}"')
2011 assert public_default_no_output_run.exit_code == 0, public_default_no_output_run.output
2012 assert !os.exists(public_default_no_output_res.c_path), public_default_no_output_res.output
2013
2014 host_os := normalize_e2e_os_name(os.user_os())
2015 if host_os in ['linux', 'macos', 'windows'] {
2016 explicit_host_default_res := run_v2_without_output(v2_binary, tmp_dir,
2017 'host_explicit_default_no_output', [
2018 '-cc',
2019 'cc',
2020 '-os',
2021 host_os,
2022 ], minimal_source)
2023 assert_binary_success(explicit_host_default_res)
2024 explicit_host_default_run := os.execute('"${explicit_host_default_res.out_path}"')
2025 assert explicit_host_default_run.exit_code == 0, explicit_host_default_run.output
2026 assert !os.exists(explicit_host_default_res.c_path), explicit_host_default_res.output
2027
2028 explicit_host_res := run_v2_to_binary(v2_binary, tmp_dir, 'host_explicit_${host_os}', [
2029 '-cc',
2030 'cc',
2031 '-os',
2032 host_os,
2033 ], minimal_source)
2034 assert_binary_success(explicit_host_res)
2035 explicit_host_run := os.execute('"${explicit_host_res.out_path}"')
2036 assert explicit_host_run.exit_code == 0, explicit_host_run.output
2037 assert !os.exists(explicit_host_res.c_path), explicit_host_res.output
2038
2039 public_explicit_host_res := run_v1_v2_without_output_in_dir(v1_binary, v2_binary, tmp_dir,
2040 'public_host_explicit_default_no_output', [
2041 '-cc',
2042 'cc',
2043 '-os',
2044 host_os,
2045 ], minimal_source, public_default_cwd)
2046 assert_binary_success(public_explicit_host_res)
2047 public_explicit_host_run := os.execute('"${public_explicit_host_res.out_path}"')
2048 assert public_explicit_host_run.exit_code == 0, public_explicit_host_run.output
2049 assert !os.exists(public_explicit_host_res.c_path), public_explicit_host_res.output
2050 }
2051
2052 cross_res := run_v2_to_binary(v2_binary, tmp_dir, 'host_cross', [
2053 '-cc',
2054 'cc',
2055 '-os',
2056 'cross',
2057 ], minimal_source)
2058 assert_binary_success(cross_res)
2059 cross_run := os.execute('"${cross_res.out_path}"')
2060 assert cross_run.exit_code == 0, cross_run.output
2061 assert !os.exists(cross_res.c_path), cross_res.output
2062
2063 cross_default_res := run_v2_without_output(v2_binary, tmp_dir, 'host_cross_default_no_output', [
2064 '-cc',
2065 'cc',
2066 '-os',
2067 'cross',
2068 ], minimal_source)
2069 assert_binary_success(cross_default_res)
2070 cross_default_run := os.execute('"${cross_default_res.out_path}"')
2071 assert cross_default_run.exit_code == 0, cross_default_run.output
2072 assert !os.exists(cross_default_res.c_path), cross_default_res.output
2073
2074 public_cross_default_res := run_v1_v2_without_output_in_dir(v1_binary, v2_binary, tmp_dir,
2075 'public_host_cross_default_no_output', [
2076 '-cc',
2077 'cc',
2078 '-os',
2079 'cross',
2080 ], minimal_source, public_default_cwd)
2081 assert_binary_success(public_cross_default_res)
2082 public_cross_default_run := os.execute('"${public_cross_default_res.out_path}"')
2083 assert public_cross_default_run.exit_code == 0, public_cross_default_run.output
2084 assert !os.exists(public_cross_default_res.c_path), public_cross_default_res.output
2085
2086 cross_impl_path := os.join_path(tmp_dir, 'cross_flag_impl.c')
2087 os.write_file(cross_impl_path, '#ifndef CROSS_FLAG_FROM_V2
2088#error CROSS_FLAG_FROM_V2 missing
2089#endif
2090
2091int cross_platform_external(void) {
2092 return 9;
2093}
2094') or {
2095 panic(err)
2096 }
2097 cross_flag_res := run_v2_to_binary(v2_binary, tmp_dir, 'host_cross_flag_observable', [
2098 '-cc',
2099 'cc',
2100 '-os',
2101 'cross',
2102 ], 'module main
2103
2104#flag cross -DCROSS_FLAG_FROM_V2
2105#flag ${cross_impl_path}
2106
2107fn C.exit(code int)
2108fn C.cross_platform_external() int
2109
2110fn main() {
2111 if C.cross_platform_external() != 9 {
2112 C.exit(18)
2113 }
2114}
2115')
2116 assert_binary_success(cross_flag_res)
2117 cross_flag_run := os.execute('"${cross_flag_res.out_path}"')
2118 assert cross_flag_run.exit_code == 0, cross_flag_run.output
2119 assert !os.exists(cross_flag_res.c_path), cross_flag_res.output
2120
2121 c_impl_path := os.join_path(tmp_dir, 'platform_flag_impl.c')
2122 os.write_file(c_impl_path, '#ifndef PLATFORM_FLAG_FROM_V2
2123#error PLATFORM_FLAG_FROM_V2 missing
2124#endif
2125
2126int platform_external(void) {
2127 return 7;
2128}
2129') or {
2130 panic(err)
2131 }
2132 flag_res := run_v2_to_binary(v2_binary, tmp_dir, 'host_flag_observable', [
2133 '-cc',
2134 'cc',
2135 ], 'module main
2136
2137#flag -DPLATFORM_FLAG_FROM_V2
2138#flag ${c_impl_path}
2139
2140fn C.exit(code int)
2141fn C.platform_external() int
2142
2143fn main() {
2144 if C.platform_external() != 7 {
2145 C.exit(17)
2146 }
2147}
2148')
2149 assert_binary_success(flag_res)
2150 flag_run := os.execute('"${flag_res.out_path}"')
2151 assert flag_run.exit_code == 0, flag_run.output
2152 assert !os.exists(flag_res.c_path), flag_res.output
2153}
2154
2155fn test_cleanc_cli_writes_c_only_for_freestanding_and_concrete_non_host_targets() {
2156 tmp_dir := os.join_path(os.vtmp_dir(), 'v2_cleanc_c_only_${os.getpid()}')
2157 os.rmdir_all(tmp_dir) or {}
2158 os.mkdir_all(tmp_dir) or { panic(err) }
2159 defer {
2160 os.rmdir_all(tmp_dir) or {}
2161 }
2162 v2_binary := build_v2_for_target_e2e(tmp_dir)
2163 v1_binary := build_v1_for_target_e2e(tmp_dir)
2164 missing_cc := os.join_path(tmp_dir, 'cc_must_not_run')
2165 minimal_source := 'module main
2166
2167fn main() {}
2168'
2169
2170 default_cwd := os.join_path(tmp_dir, 'default_output_cwd')
2171 freestanding_default_res := run_v2_without_output_in_dir(v2_binary, tmp_dir,
2172 'freestanding_default_generation_only', [
2173 '-cc',
2174 missing_cc,
2175 '-freestanding',
2176 '-os',
2177 'linux',
2178 '--skip-builtin',
2179 ], minimal_source, default_cwd)
2180 assert_generated_c_only(freestanding_default_res)
2181 assert freestanding_default_res.c_path == os.join_path(default_cwd,
2182 'freestanding_default_generation_only.c')
2183 assert !os.exists(os.join_path(tmp_dir, 'freestanding_default_generation_only.c')), freestanding_default_res.output
2184
2185 host_target := normalize_e2e_os_name(os.user_os())
2186 public_freestanding_host_res := run_v1_v2_without_output_in_dir(v1_binary, v2_binary, tmp_dir,
2187 'public_freestanding_host_default_generation_only', [
2188 '-cc',
2189 missing_cc,
2190 '-freestanding',
2191 '-os',
2192 host_target,
2193 ], minimal_source, default_cwd)
2194 assert_generated_c_only(public_freestanding_host_res)
2195 assert public_freestanding_host_res.c_path == os.join_path(default_cwd,
2196 'public_freestanding_host_default_generation_only.c')
2197 assert !os.exists(os.join_path(tmp_dir, 'public_freestanding_host_default_generation_only.c')), public_freestanding_host_res.output
2198
2199 freestanding_none_default_res := run_v2_without_output_in_dir(v2_binary, tmp_dir,
2200 'freestanding_none_default_generation_only', [
2201 '-cc',
2202 missing_cc,
2203 '-freestanding',
2204 '-os',
2205 'none',
2206 ], freestanding_none_source(), default_cwd)
2207 assert_generated_c_only(freestanding_none_default_res)
2208 assert freestanding_none_default_res.c_path == os.join_path(default_cwd,
2209 'freestanding_none_default_generation_only.c')
2210 assert freestanding_none_default_res.c_source.contains('#include <platform_none.h>')
2211 assert_no_os_runtime_headers(freestanding_none_default_res.c_source)
2212
2213 public_freestanding_none_minimal_res := run_v1_v2_without_output_in_dir(v1_binary, v2_binary,
2214 tmp_dir, 'app', [
2215 '-cc',
2216 missing_cc,
2217 '-freestanding',
2218 '-os',
2219 'none',
2220 ], minimal_source, default_cwd)
2221 assert_generated_c_only(public_freestanding_none_minimal_res)
2222 assert public_freestanding_none_minimal_res.c_path == os.join_path(default_cwd, 'app.c')
2223 assert_no_os_runtime_headers(public_freestanding_none_minimal_res.c_source)
2224
2225 public_freestanding_none_default_res := run_v1_v2_without_output_in_dir(v1_binary, v2_binary,
2226 tmp_dir, 'public_freestanding_none_default_generation_only', [
2227 '-cc',
2228 missing_cc,
2229 '-freestanding',
2230 '-os',
2231 'none',
2232 ], freestanding_none_source(), default_cwd)
2233 assert_generated_c_only(public_freestanding_none_default_res)
2234 assert public_freestanding_none_default_res.c_path == os.join_path(default_cwd,
2235 'public_freestanding_none_default_generation_only.c')
2236 assert public_freestanding_none_default_res.c_source.contains('#include <platform_none.h>')
2237 assert_no_os_runtime_headers(public_freestanding_none_default_res.c_source)
2238
2239 freestanding_out := os.join_path(tmp_dir, 'freestanding_app')
2240 freestanding_res := run_v2_to_output(v2_binary, tmp_dir, 'freestanding_generation_only', [
2241 '-cc',
2242 missing_cc,
2243 '-freestanding',
2244 '-os',
2245 'linux',
2246 '--skip-builtin',
2247 ], minimal_source, freestanding_out)
2248 assert_generated_c_only(freestanding_res)
2249 assert freestanding_res.c_path == freestanding_out + '.c'
2250
2251 freestanding_none_out := os.join_path(tmp_dir, 'freestanding_none_app')
2252 freestanding_none_res := run_v2_to_output(v2_binary, tmp_dir,
2253 'freestanding_none_generation_only', [
2254 '-cc',
2255 missing_cc,
2256 '-freestanding',
2257 '-os',
2258 'none',
2259 '--skip-builtin',
2260 ], freestanding_none_source(), freestanding_none_out)
2261 assert_generated_c_only(freestanding_none_res)
2262 assert freestanding_none_res.c_path == freestanding_none_out + '.c'
2263 assert freestanding_none_res.c_source.contains('#include <platform_none.h>')
2264 assert freestanding_none_res.c_source.contains('platform_none_tick')
2265 assert_no_os_runtime_headers(freestanding_none_res.c_source)
2266
2267 public_wrapper_out := os.join_path(tmp_dir, 'public_wrapper_freestanding_none.c')
2268 public_wrapper_res := run_v1_v2_to_output(v1_binary, v2_binary, tmp_dir,
2269 'public_wrapper_freestanding_none', [
2270 '-cc',
2271 missing_cc,
2272 '-freestanding',
2273 '-os',
2274 'none',
2275 '--skip-builtin',
2276 ], freestanding_none_source(), public_wrapper_out)
2277 assert_cli_success(public_wrapper_res)
2278 assert public_wrapper_res.c_path == public_wrapper_out
2279 assert public_wrapper_res.c_source.contains('#include <platform_none.h>')
2280 assert public_wrapper_res.c_source.contains('platform_none_tick')
2281 assert_no_os_runtime_headers(public_wrapper_res.c_source)
2282
2283 freestanding_none_hooks_out := os.join_path(tmp_dir, 'freestanding_none_hooks_app')
2284 freestanding_none_hooks_res := run_v2_to_output(v2_binary, tmp_dir,
2285 'freestanding_none_hooks_generation_only', [
2286 '-cc',
2287 missing_cc,
2288 '-freestanding',
2289 '-os',
2290 'none',
2291 '-fhooks',
2292 'output,panic,alloc',
2293 '--skip-builtin',
2294 '--skip-type-check',
2295 ], "module main
2296
2297struct HeapBox {
2298 value int
2299}
2300
2301fn main() {
2302 println('hooked')
2303 _ := &HeapBox{
2304 value: 1
2305 }
2306 panic('hooked')
2307}
2308 ",
2309 freestanding_none_hooks_out)
2310 assert_generated_c_only(freestanding_none_hooks_res)
2311 assert freestanding_none_hooks_res.c_source.contains('isize v_platform_write(int stream, const u8* buf, isize len);')
2312 assert freestanding_none_hooks_res.c_source.contains('void v_platform_panic(const u8* msg, isize len);')
2313 assert freestanding_none_hooks_res.c_source.contains('void* v_platform_malloc(isize n);')
2314 assert freestanding_none_hooks_res.c_source.contains('void* v_platform_realloc(void* ptr, isize n);')
2315 assert freestanding_none_hooks_res.c_source.contains('void v_platform_free(void* ptr);')
2316 assert_no_os_runtime_headers(freestanding_none_hooks_res.c_source)
2317 assert_no_obvious_hosted_headers(freestanding_none_hooks_res.c_source)
2318
2319 non_host_target := concrete_non_host_e2e_os()
2320 non_host_default_res := run_v2_without_output_in_dir(v2_binary, tmp_dir,
2321 'concrete_non_host_default_generation_only', [
2322 '-cc',
2323 missing_cc,
2324 '-os',
2325 non_host_target,
2326 ], minimal_source, default_cwd)
2327 assert_generated_c_only(non_host_default_res)
2328 assert non_host_default_res.c_path == os.join_path(default_cwd,
2329 'concrete_non_host_default_generation_only.c')
2330 assert !os.exists(os.join_path(tmp_dir, 'concrete_non_host_default_generation_only.c')), non_host_default_res.output
2331
2332 public_non_host_default_res := run_v1_v2_without_output_in_dir(v1_binary, v2_binary, tmp_dir,
2333 'public_concrete_non_host_default_generation_only', [
2334 '-cc',
2335 missing_cc,
2336 '-os',
2337 non_host_target,
2338 ], minimal_source, default_cwd)
2339 assert_generated_c_only(public_non_host_default_res)
2340 assert public_non_host_default_res.c_path == os.join_path(default_cwd,
2341 'public_concrete_non_host_default_generation_only.c')
2342 assert !os.exists(os.join_path(tmp_dir, 'public_concrete_non_host_default_generation_only.c')), public_non_host_default_res.output
2343
2344 non_host_out := os.join_path(tmp_dir, 'target_${non_host_target}_app')
2345 non_host_res := run_v2_to_output(v2_binary, tmp_dir, 'concrete_non_host_generation_only', [
2346 '-cc',
2347 missing_cc,
2348 '-os',
2349 non_host_target,
2350 ], minimal_source, non_host_out)
2351 assert_generated_c_only(non_host_res)
2352 assert non_host_res.c_path == non_host_out + '.c'
2353
2354 non_host_cached_out := os.join_path(tmp_dir, 'target_${non_host_target}_cached_app')
2355 non_host_cached_res := run_v2_to_output_with_cache(v2_binary, tmp_dir,
2356 'concrete_non_host_generation_only_cached', [
2357 '-cc',
2358 missing_cc,
2359 '-os',
2360 non_host_target,
2361 ], 'module main
2362
2363fn main() {
2364 println(123)
2365}
2366', non_host_cached_out)
2367 assert_generated_c_only(non_host_cached_res)
2368 assert non_host_cached_res.c_path == non_host_cached_out + '.c'
2369 assert non_host_cached_res.c_source.contains('void println(string s) {'), non_host_cached_res.c_source
2370
2371 flag_obj := os.join_path(tmp_dir, 'generation_only_probe.o')
2372 flag_c := os.join_path(tmp_dir, 'generation_only_probe.c')
2373 os.write_file(flag_c, 'int generation_only_probe(void) { return 0; }\n') or { panic(err) }
2374 explicit_c_out := os.join_path(tmp_dir, 'explicit_generation_only.c')
2375 explicit_c_res := run_v2_to_output(v2_binary, tmp_dir, 'explicit_c_generation_only', [], 'module main
2376
2377#flag ${flag_obj}
2378
2379fn main() {}
2380',
2381 explicit_c_out)
2382 assert_cli_success(explicit_c_res)
2383 assert !os.exists(flag_obj), 'generation-only .c output compiled unexpected object ${flag_obj}\n${explicit_c_res.output}'
2384
2385 rejected_none_res := run_v2_without_output_in_dir(v2_binary, tmp_dir,
2386 'rejected_none_without_freestanding', [
2387 '-cc',
2388 missing_cc,
2389 '-os',
2390 'none',
2391 ], minimal_source, default_cwd)
2392 assert_cli_failure_contains(rejected_none_res, '-os none requires -freestanding')
2393 assert !os.exists(rejected_none_res.c_path), rejected_none_res.output
2394 assert !os.exists(rejected_none_res.out_path), rejected_none_res.output
2395
2396 rejected_freestanding_cross_res := run_v2_without_output_in_dir(v2_binary, tmp_dir,
2397 'rejected_freestanding_cross', [
2398 '-cc',
2399 missing_cc,
2400 '-freestanding',
2401 '-os',
2402 'cross',
2403 '--skip-builtin',
2404 ], minimal_source, default_cwd)
2405 assert_cli_failure_contains(rejected_freestanding_cross_res,
2406 '-freestanding -os cross is not supported')
2407 assert !os.exists(rejected_freestanding_cross_res.c_path), rejected_freestanding_cross_res.output
2408 assert !os.exists(rejected_freestanding_cross_res.out_path), rejected_freestanding_cross_res.output
2409}
2410
2411fn test_cleanc_cli_does_not_auto_run_stale_test_binary_for_generation_only_target() {
2412 $if windows {
2413 eprintln('skip: stale executable auto-run guard uses a POSIX shell script')
2414 return
2415 }
2416 tmp_dir := os.join_path(os.vtmp_dir(), 'v2_cleanc_no_stale_autorun_${os.getpid()}')
2417 os.rmdir_all(tmp_dir) or {}
2418 os.mkdir_all(tmp_dir) or { panic(err) }
2419 defer {
2420 os.rmdir_all(tmp_dir) or {}
2421 }
2422 v2_binary := build_v2_for_target_e2e(tmp_dir)
2423 source_path := os.join_path(tmp_dir, 'stale_autorun_test.v')
2424 stale_path := os.join_path(tmp_dir, 'stale_autorun_test')
2425 os.write_file(source_path, 'module main
2426
2427fn main() {}
2428') or { panic(err) }
2429 os.write_file(stale_path, '#!/bin/sh
2430echo STALE_AUTORUN_EXECUTED
2431exit 73
2432') or { panic(err) }
2433 os.chmod(stale_path, 0o755) or { panic(err) }
2434
2435 res :=
2436 os.execute('cd "${tmp_dir}" && "${v2_binary}" -gc none -nocache --no-parallel -freestanding -os none --skip-builtin "${source_path}"')
2437 assert res.exit_code == 0, res.output
2438 assert !res.output.contains('STALE_AUTORUN_EXECUTED'), res.output
2439 assert os.exists(os.join_path(tmp_dir, 'stale_autorun_test.c')), res.output
2440 assert os.exists(stale_path), res.output
2441
2442 non_host_target := concrete_non_host_e2e_os()
2443 cleanc_source_path := os.join_path(tmp_dir, 'cleanc_non_host_stale.v')
2444 cleanc_stale_path := os.join_path(tmp_dir, 'cleanc_non_host_stale')
2445 os.write_file(cleanc_source_path, 'module main
2446
2447fn main() {}
2448') or { panic(err) }
2449 os.write_file(cleanc_stale_path, '#!/bin/sh
2450echo CLEANC_NON_HOST_STALE_EXECUTED
2451exit 75
2452') or {
2453 panic(err)
2454 }
2455 os.chmod(cleanc_stale_path, 0o755) or { panic(err) }
2456 cleanc_res :=
2457 os.execute('cd "${tmp_dir}" && "${v2_binary}" -gc none -nocache --no-parallel -os ${non_host_target} "${cleanc_source_path}"')
2458 assert cleanc_res.exit_code == 0, cleanc_res.output
2459 assert !cleanc_res.output.contains('CLEANC_NON_HOST_STALE_EXECUTED'), cleanc_res.output
2460 assert os.exists(os.join_path(tmp_dir, 'cleanc_non_host_stale.c')), cleanc_res.output
2461 assert os.exists(cleanc_stale_path), cleanc_res.output
2462
2463 native_source_path := os.join_path(tmp_dir, 'native_cross_autorun_test.v')
2464 native_stale_path := os.join_path(tmp_dir, 'native_cross_autorun_test')
2465 os.write_file(native_source_path, 'module main
2466
2467fn main() {}
2468') or { panic(err) }
2469 os.write_file(native_stale_path, '#!/bin/sh
2470echo NATIVE_STALE_AUTORUN_EXECUTED
2471exit 74
2472') or {
2473 panic(err)
2474 }
2475 os.chmod(native_stale_path, 0o755) or { panic(err) }
2476 native_res :=
2477 os.execute('cd "${tmp_dir}" && "${v2_binary}" -gc none -nocache --no-parallel -b x64 -os ${non_host_target} --skip-builtin "${native_source_path}"')
2478 assert native_res.exit_code == 0, native_res.output
2479 assert !native_res.output.contains('NATIVE_STALE_AUTORUN_EXECUTED'), native_res.output
2480 assert os.exists(native_stale_path), native_res.output
2481}
2482
2483fn test_cleanc_cli_auto_runs_cross_test_binary_when_compiled_for_host() {
2484 if !host_cc_available() {
2485 eprintln('skip: cc is not available for cleanc cross auto-run e2e')
2486 return
2487 }
2488 tmp_dir := os.join_path(os.vtmp_dir(), 'v2_cleanc_cross_autorun_${os.getpid()}')
2489 os.rmdir_all(tmp_dir) or {}
2490 os.mkdir_all(tmp_dir) or { panic(err) }
2491 defer {
2492 os.rmdir_all(tmp_dir) or {}
2493 }
2494 v2_binary := build_v2_for_target_e2e(tmp_dir)
2495 source_path := os.join_path(tmp_dir, 'cross_autorun_test.v')
2496 os.write_file(source_path, 'module main
2497
2498fn main() {
2499 println("CROSS_AUTORUN_OK")
2500}
2501') or {
2502 panic(err)
2503 }
2504 env_prefix := if host_c_e2e_flags().len > 0 { 'V2CFLAGS="${host_c_e2e_flags()}" ' } else { '' }
2505 res :=
2506 os.execute('cd "${tmp_dir}" && ${env_prefix}"${v2_binary}" -gc none -nocache --no-parallel -cc cc -os cross "${source_path}"')
2507 assert res.exit_code == 0, res.output
2508 assert res.output.contains('CROSS_AUTORUN_OK'), res.output
2509 assert !os.exists(os.join_path(tmp_dir, 'cross_autorun_test')), res.output
2510}
2511