Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,310 @@
|
||||
// Copyright (c) 2020-2024 Joe Conigliaro. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
module main
|
||||
|
||||
import os
|
||||
import time
|
||||
import v2.pref
|
||||
import v2.builder
|
||||
|
||||
fn main() {
|
||||
compile_args, runtime_args := split_eval_runtime_args(os.args[1..])
|
||||
|
||||
// Check for subcommands
|
||||
if compile_args.len > 0 && compile_args[0] == 'ast' {
|
||||
run_ast_command(compile_args[1..])
|
||||
return
|
||||
}
|
||||
if compile_args.len > 0 && compile_args[0] == 'test-self' {
|
||||
run_test_self()
|
||||
return
|
||||
}
|
||||
|
||||
mut prefs := pref.new_preferences_from_args(compile_args)
|
||||
|
||||
files := get_files(compile_args)
|
||||
if files.len == 0 {
|
||||
eprintln('At least 1 .v file expected')
|
||||
exit(1)
|
||||
}
|
||||
mut eval_runtime_args := [files[0]]
|
||||
eval_runtime_args << runtime_args
|
||||
prefs.eval_runtime_args = eval_runtime_args
|
||||
|
||||
mut b := builder.new_builder(&prefs)
|
||||
b.build(files)
|
||||
|
||||
// Auto-run test binaries after compilation (matching v1 behavior)
|
||||
if prefs.output_file == '' && is_test_file(files) && should_auto_run_test_binary(&prefs) {
|
||||
last := os.file_name(files.last())
|
||||
output_name := if last.ends_with('.vv2') {
|
||||
last.all_before_last('.vv2')
|
||||
} else {
|
||||
last.all_before_last('.v')
|
||||
}
|
||||
if os.exists(output_name) {
|
||||
ret := os.system('./' + output_name)
|
||||
os.rm(output_name) or {}
|
||||
c_file := output_name + '.c'
|
||||
if !prefs.keep_c && os.exists(c_file) {
|
||||
os.rm(c_file) or {}
|
||||
}
|
||||
exit(ret)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn split_eval_runtime_args(args []string) ([]string, []string) {
|
||||
for i, arg in args {
|
||||
if arg == '--' {
|
||||
return args[..i], args[i + 1..]
|
||||
}
|
||||
}
|
||||
return args, []string{}
|
||||
}
|
||||
|
||||
fn run_ast_command(args []string) {
|
||||
if args.len == 0 {
|
||||
eprintln('Usage: v2 ast <file.v>')
|
||||
eprintln('Dumps AST to <file>_ast.json and <file>_ast_transformed.json')
|
||||
exit(1)
|
||||
}
|
||||
|
||||
// Find the vast2 tool relative to vexe
|
||||
vroot := os.dir(@VEXE)
|
||||
vast2_path := os.join_path(vroot, 'cmd', 'tools', 'vast2', 'vast2')
|
||||
|
||||
// Build vast2 if it doesn't exist
|
||||
if !os.exists(vast2_path) {
|
||||
eprintln('Building vast2 tool...')
|
||||
vast2_source := os.join_path(vroot, 'cmd', 'tools', 'vast2', 'vast2.v')
|
||||
build_result := os.execute('${@VEXE} ${vast2_source}')
|
||||
if build_result.exit_code != 0 {
|
||||
eprintln('Failed to build vast2 tool:')
|
||||
eprintln(build_result.output)
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Run vast2 with the provided arguments
|
||||
cmd := '${vast2_path} ${args.join(' ')}'
|
||||
result := os.execute(cmd)
|
||||
print(result.output)
|
||||
if result.exit_code != 0 {
|
||||
exit(result.exit_code)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_test_file(files []string) bool {
|
||||
for file in files {
|
||||
if file.ends_with('_test.v') || file.ends_with('_test.vv2') {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fn should_auto_run_test_binary(prefs &pref.Preferences) bool {
|
||||
if prefs == unsafe { nil } {
|
||||
return true
|
||||
}
|
||||
if !prefs.can_run_target_binary_locally() {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// get_files extracts source files from args, excluding options and their values
|
||||
fn get_files(args []string) []string {
|
||||
return pref.source_files_from_args(args)
|
||||
}
|
||||
|
||||
fn resolve_own_path() string {
|
||||
arg0 := os.args[0]
|
||||
if os.is_abs_path(arg0) {
|
||||
return arg0
|
||||
}
|
||||
return os.join_path(os.getwd(), arg0)
|
||||
}
|
||||
|
||||
// detect_vroot walks up from `start` looking for a directory containing vlib/builtin.
|
||||
fn detect_vroot(start string) string {
|
||||
mut dir := start
|
||||
if !os.is_abs_path(dir) {
|
||||
dir = os.join_path(os.getwd(), dir)
|
||||
}
|
||||
for _ in 0 .. 10 {
|
||||
if os.is_dir(os.join_path(dir, 'vlib', 'builtin')) {
|
||||
return dir
|
||||
}
|
||||
parent := os.dir(dir)
|
||||
if parent == dir {
|
||||
break
|
||||
}
|
||||
dir = parent
|
||||
}
|
||||
return dir
|
||||
}
|
||||
|
||||
fn run_test_self() {
|
||||
t0 := time.now()
|
||||
// Resolve the v2 binary's own path. Cannot use @VEXE because when v1
|
||||
// compiles v2, @VEXE bakes in v1's path instead of v2's.
|
||||
vexe := resolve_own_path()
|
||||
// Walk up from the binary to find the repo root (directory containing vlib/builtin).
|
||||
// This allows running from subdirectories like cmd/v2/.
|
||||
vroot := detect_vroot(vexe)
|
||||
v2_dir := os.join_path(vroot, 'cmd', 'v2')
|
||||
|
||||
mut all_test_files := []string{}
|
||||
|
||||
// Builtin test files
|
||||
for name in ['array_test.v', 'string_test.v', 'map_test.v'] {
|
||||
all_test_files << os.join_path(vroot, 'vlib', 'builtin', name)
|
||||
}
|
||||
|
||||
// Math test
|
||||
all_test_files << os.join_path(vroot, 'vlib', 'math', 'math_test.v')
|
||||
|
||||
// Sumtype tests in cmd/v2/
|
||||
v2_files := os.ls(v2_dir) or { []string{} }
|
||||
for file in v2_files {
|
||||
if file.starts_with('test_sumtype') && file.ends_with('.v') {
|
||||
all_test_files << os.join_path(v2_dir, file)
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanc regression tests
|
||||
cleanc_tests_dir := os.join_path(vroot, 'vlib', 'v2', 'gen', 'cleanc', 'tests')
|
||||
cleanc_files := os.ls(cleanc_tests_dir) or { []string{} }
|
||||
for file in cleanc_files {
|
||||
if file.ends_with('.v') && file != 'run_tests.v' {
|
||||
all_test_files << os.join_path(cleanc_tests_dir, file)
|
||||
}
|
||||
}
|
||||
|
||||
total := all_test_files.len
|
||||
mut passed := 0
|
||||
mut failed := 0
|
||||
mut failed_files := []string{}
|
||||
|
||||
eprintln('---- v2 test-self: ${total} test files ----')
|
||||
|
||||
for i, test_file in all_test_files {
|
||||
short_name := test_file.replace(vroot + '/', '')
|
||||
t1 := time.now()
|
||||
|
||||
// Determine output binary path
|
||||
base := os.file_name(test_file).all_before_last('.v')
|
||||
out_bin := os.join_path(os.temp_dir(), 'v2_test_self_${base}')
|
||||
|
||||
// Compile (use os.system to avoid pipe deadlocks with popen/GC)
|
||||
compile_cmd := '${vexe} -o ${out_bin} "${test_file}" > /dev/null 2>&1'
|
||||
compile_ret := os.system(compile_cmd)
|
||||
compile_ms := f64(time.since(t1)) / f64(time.millisecond)
|
||||
|
||||
if compile_ret != 0 || !os.exists(out_bin) {
|
||||
failed++
|
||||
failed_files << short_name
|
||||
eprintln(' FAIL [${i + 1:4}/${total}] C: ${compile_ms:7.1} ms ${short_name}')
|
||||
os.rm(out_bin) or {}
|
||||
os.rm('${out_bin}.c') or {}
|
||||
continue
|
||||
}
|
||||
|
||||
// Run the compiled binary
|
||||
t2 := time.now()
|
||||
run_ret := os.system(out_bin + ' > /dev/null 2>&1')
|
||||
run_ms := f64(time.since(t2)) / f64(time.millisecond)
|
||||
|
||||
if run_ret != 0 {
|
||||
failed++
|
||||
failed_files << short_name
|
||||
eprintln(' FAIL [${i + 1:4}/${total}] C: ${compile_ms:7.1} ms, R: ${run_ms:7.1} ms ${short_name}')
|
||||
} else {
|
||||
passed++
|
||||
eprintln('OK [${i + 1:4}/${total}] C: ${compile_ms:7.1} ms, R: ${run_ms:7.1} ms ${short_name}')
|
||||
}
|
||||
|
||||
os.rm(out_bin) or {}
|
||||
os.rm('${out_bin}.c') or {}
|
||||
}
|
||||
|
||||
elapsed := time.since(t0)
|
||||
eprintln('------------------------------------------------------------------------')
|
||||
eprintln('${passed} passed, ${failed} failed, ${total} total (${elapsed})')
|
||||
if failed_files.len > 0 {
|
||||
eprintln('Failed:')
|
||||
for f in failed_files {
|
||||
eprintln(' ${f}')
|
||||
}
|
||||
}
|
||||
// Always run the self-compilation chain, even when some tests fail.
|
||||
// Self-compilation chain: v2 -> v3 -> v4 -> v5
|
||||
eprintln('')
|
||||
eprintln('---- v2 self-compilation chain ----')
|
||||
|
||||
v2_source := os.join_path(v2_dir, 'v2.v')
|
||||
backend := 'cleanc'
|
||||
tmpdir := os.temp_dir()
|
||||
v3_bin := os.join_path(tmpdir, 'v2_self_v3')
|
||||
v4_bin := os.join_path(tmpdir, 'v2_self_v4')
|
||||
v5_bin := os.join_path(tmpdir, 'v2_self_v5')
|
||||
|
||||
steps := [
|
||||
[vexe, v3_bin, 'v2 -> v3'],
|
||||
[v3_bin, v4_bin, 'v3 -> v4'],
|
||||
[v4_bin, v5_bin, 'v4 -> v5'],
|
||||
]
|
||||
|
||||
for step in steps {
|
||||
compiler := step[0]
|
||||
out := step[1]
|
||||
label := step[2]
|
||||
ts := time.now()
|
||||
cmd := '${compiler} -gc none -o ${out} -backend ${backend} "${v2_source}" > /dev/null 2>&1'
|
||||
ret := os.system(cmd)
|
||||
ms := f64(time.since(ts)) / f64(time.millisecond)
|
||||
if ret != 0 || !os.exists(out) {
|
||||
eprintln(' FAIL ${label} (${ms:.1} ms)')
|
||||
// Clean up
|
||||
for bin in [v3_bin, v4_bin, v5_bin] {
|
||||
os.rm(bin) or {}
|
||||
os.rm('${bin}.c') or {}
|
||||
}
|
||||
exit(1)
|
||||
}
|
||||
eprintln('OK ${label} (${ms:.1} ms)')
|
||||
}
|
||||
|
||||
// Verify that v3 runs and produces expected output
|
||||
result := os.execute('${v3_bin} 2>&1')
|
||||
expected := 'At least 1 .v file expected'
|
||||
if !result.output.contains(expected) {
|
||||
eprintln("FAIL v3 output check: expected '${expected}', got:")
|
||||
eprintln(result.output)
|
||||
for bin in [v3_bin, v4_bin, v5_bin] {
|
||||
os.rm(bin) or {}
|
||||
os.rm('${bin}.c') or {}
|
||||
}
|
||||
exit(1)
|
||||
}
|
||||
eprintln('OK v3 output verified')
|
||||
|
||||
// Clean up
|
||||
for bin in [v3_bin, v4_bin, v5_bin] {
|
||||
os.rm(bin) or {}
|
||||
os.rm('${bin}.c') or {}
|
||||
}
|
||||
|
||||
total_elapsed := time.since(t0)
|
||||
eprintln('')
|
||||
if failed > 0 {
|
||||
eprintln('=== SELF-COMPILATION OK, but ${failed} test(s) failed ===')
|
||||
eprintln('Total time: ${total_elapsed}')
|
||||
exit(1)
|
||||
}
|
||||
eprintln('=== SELF-COMPILATION TEST PASSED ===')
|
||||
eprintln('Total time: ${total_elapsed}')
|
||||
}
|
||||
Reference in new issue
Block a user