mirror of
https://github.com/bearlanguageorg/bear.git
synced 2026-08-26 14:17:18 +00:00
vrmm build files
This commit is contained in:
@@ -33,6 +33,9 @@ vr run <file.vr|file.vbin> compile+link+run, or run a binary
|
|||||||
vr debug <file.vr|file.vbin> run with an instruction trace
|
vr debug <file.vr|file.vbin> run with an instruction trace
|
||||||
vr test <file.vr> run every test_* function
|
vr test <file.vr> run every test_* function
|
||||||
vr bench <file.vr> [iterations] benchmark main()
|
vr bench <file.vr> [iterations] benchmark main()
|
||||||
|
vr make [target] [args...] run build.vrmm (target = main)
|
||||||
|
vr make -f <file.vrmm> [target] [args...] run another build module
|
||||||
|
vr build [target] [args...] alias for make
|
||||||
vr clean remove .vobj/.vbin artifacts
|
vr clean remove .vobj/.vbin artifacts
|
||||||
vr up rebuild bin/vr
|
vr up rebuild bin/vr
|
||||||
vr symlink link bin/vr into your PATH
|
vr symlink link bin/vr into your PATH
|
||||||
@@ -71,6 +74,64 @@ Quick start:
|
|||||||
./bin/vr run use_lib.vbin
|
./bin/vr run use_lib.vbin
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Build modules (.vrmm)
|
||||||
|
|
||||||
|
A **VuurRaaf Make Module** (`.vrmm`) is build instructions for the toolchain,
|
||||||
|
written in VuurRaaf itself — the same idea as V's `.vsh` scripts. The toolchain
|
||||||
|
compiles the module and runs one of its functions (a *target*) with the
|
||||||
|
`build_*` builtins available, so the script can drive every stage of the
|
||||||
|
pipeline: compile, assemble, link, run, test, bench, clean, and shell out to
|
||||||
|
the host.
|
||||||
|
|
||||||
|
```
|
||||||
|
# build.vrmm
|
||||||
|
fn main() {
|
||||||
|
build_compile("main.vr", "main.vobj")
|
||||||
|
build_link(["main.vobj"], "main.vbin")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clean() {
|
||||||
|
build_clean()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
vr make # runs main() from build.vrmm
|
||||||
|
vr make clean # runs the clean() target
|
||||||
|
vr make deploy --prod # runs deploy() with args() == ["--prod"]
|
||||||
|
vr make -f x.vrmm t # run target t from another module
|
||||||
|
```
|
||||||
|
|
||||||
|
A target that returns a nonzero integer, calls `exit(n)` with `n > 0`, or
|
||||||
|
`throw`s fails the build. Paths are relative to the working directory;
|
||||||
|
`build_root()` returns the module's own directory for absolute paths.
|
||||||
|
|
||||||
|
Build builtins:
|
||||||
|
|
||||||
|
| builtin | description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `build_compile(src, out)` | source → object (out defaults to `src.vobj`); returns the out path |
|
||||||
|
| `build_assemble(src, out)` | `.vasm` → object; returns the out path |
|
||||||
|
| `build_link(objs, out)` | objects → executable; returns the out path |
|
||||||
|
| `build_run(file)` | compile+link+run a `.vr`, or run a `.vbin`; returns the exit code |
|
||||||
|
| `build_test(file)` | run every `test_*` function; throws if any fail |
|
||||||
|
| `build_bench(file, n)` | benchmark `main()` n times |
|
||||||
|
| `build_clean()` | remove `.vobj`/`.vbin` in the cwd; returns the count |
|
||||||
|
| `build_exec(cmd)` | run a shell command; returns its output (throws on nonzero exit) |
|
||||||
|
| `build_exec_status(cmd)` | run a shell command; returns its exit code |
|
||||||
|
| `build_exists(path)` | 1 if the path exists, else 0 |
|
||||||
|
| `build_mkdir(path)` | create a directory (and parents) |
|
||||||
|
| `build_rm(path)` | remove a file or directory tree; returns 1 if something was removed |
|
||||||
|
| `build_copy(src, dst)` | copy a file or a whole directory tree |
|
||||||
|
| `build_glob(pattern)` | list files matching a glob (e.g. `"src/*.vr"`) |
|
||||||
|
| `build_ls(dir)` | list a directory's entries |
|
||||||
|
| `build_base(path)` / `build_dir(path)` / `build_join(a, b)` | path helpers |
|
||||||
|
| `build_root()` | absolute directory of the running `.vrmm` |
|
||||||
|
|
||||||
|
`vr init` scaffolds a project with a working `build.vrmm`; see
|
||||||
|
`examples/build.vrmm` for a tour (targets: `main`, `multi`, `test`, `bench`,
|
||||||
|
`deploy`, `clean`).
|
||||||
|
|
||||||
## The VuurRaaf language
|
## The VuurRaaf language
|
||||||
|
|
||||||
A small, V-flavored language. Values are 64-bit integers, 64-bit floats,
|
A small, V-flavored language. Values are 64-bit integers, 64-bit floats,
|
||||||
@@ -261,6 +322,7 @@ main.v CLI entry point (vr <command> ...)
|
|||||||
repl.v interactive REPL
|
repl.v interactive REPL
|
||||||
fmt.v source formatter
|
fmt.v source formatter
|
||||||
pkg.v package manager (init/get/install/list)
|
pkg.v package manager (init/get/install/list)
|
||||||
|
vm/native.v host builtins incl. the build_* (.vrmm) builtins
|
||||||
v.mod module definition
|
v.mod module definition
|
||||||
compiler/ assembler/ linker/ vm/ obj/ the toolchain itself
|
compiler/ assembler/ linker/ vm/ obj/ the toolchain itself
|
||||||
bin/ built binary + standalone tools
|
bin/ built binary + standalone tools
|
||||||
|
|||||||
@@ -552,6 +552,12 @@ fn builtin_result_type(name string) TypeInfo {
|
|||||||
'args', 'keys' { TypeInfo{ kind: .array_t } }
|
'args', 'keys' { TypeInfo{ kind: .array_t } }
|
||||||
'len' { TypeInfo{ kind: .int_t } }
|
'len' { TypeInfo{ kind: .int_t } }
|
||||||
'write_file', 'setenv', 'exit', 'sleep', 'eprint' { TypeInfo{ kind: .unknown } }
|
'write_file', 'setenv', 'exit', 'sleep', 'eprint' { TypeInfo{ kind: .unknown } }
|
||||||
|
// build-module builtins (.vrmm)
|
||||||
|
'build_compile', 'build_assemble', 'build_link', 'build_exec', 'build_base',
|
||||||
|
'build_dir', 'build_join', 'build_root' { TypeInfo{ kind: .string_t } }
|
||||||
|
'build_glob', 'build_ls' { TypeInfo{ kind: .array_t } }
|
||||||
|
'build_run', 'build_test', 'build_bench', 'build_clean', 'build_exec_status',
|
||||||
|
'build_exists', 'build_mkdir', 'build_rm', 'build_copy' { TypeInfo{ kind: .int_t } }
|
||||||
else { TypeInfo{ kind: .unknown } }
|
else { TypeInfo{ kind: .unknown } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -903,6 +903,26 @@ fn builtin_spec(name string) (int, int) {
|
|||||||
'read_file' { native_read_file, 1 }
|
'read_file' { native_read_file, 1 }
|
||||||
'write_file' { native_write_file, 2 }
|
'write_file' { native_write_file, 2 }
|
||||||
'eprint' { native_eprint, 1 }
|
'eprint' { native_eprint, 1 }
|
||||||
|
// build-module builtins (.vrmm) — see vm/native.v
|
||||||
|
'build_compile' { native_build_compile, 2 }
|
||||||
|
'build_assemble' { native_build_assemble, 2 }
|
||||||
|
'build_link' { native_build_link, 2 }
|
||||||
|
'build_run' { native_build_run, 1 }
|
||||||
|
'build_test' { native_build_test, 1 }
|
||||||
|
'build_bench' { native_build_bench, 2 }
|
||||||
|
'build_clean' { native_build_clean, 0 }
|
||||||
|
'build_exec' { native_build_exec, 1 }
|
||||||
|
'build_exec_status' { native_build_exec_status, 1 }
|
||||||
|
'build_exists' { native_build_exists, 1 }
|
||||||
|
'build_mkdir' { native_build_mkdir, 1 }
|
||||||
|
'build_rm' { native_build_rm, 1 }
|
||||||
|
'build_copy' { native_build_copy, 2 }
|
||||||
|
'build_glob' { native_build_glob, 1 }
|
||||||
|
'build_ls' { native_build_ls, 1 }
|
||||||
|
'build_base' { native_build_base, 1 }
|
||||||
|
'build_dir' { native_build_dir, 1 }
|
||||||
|
'build_join' { native_build_join, 2 }
|
||||||
|
'build_root' { native_build_root, 0 }
|
||||||
else { -1, 0 }
|
else { -1, 0 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1131,6 +1151,8 @@ fn (mut g Gen) expr_type(e Expr) string {
|
|||||||
if e.kind == .call {
|
if e.kind == .call {
|
||||||
return match e.name {
|
return match e.name {
|
||||||
'upper', 'lower', 'trim', 'str', 'getenv', 'read_file', 'join' { 'string' }
|
'upper', 'lower', 'trim', 'str', 'getenv', 'read_file', 'join' { 'string' }
|
||||||
|
'build_compile', 'build_assemble', 'build_link', 'build_exec', 'build_base',
|
||||||
|
'build_dir', 'build_join', 'build_root' { 'string' }
|
||||||
else { '' }
|
else { '' }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,3 +105,25 @@ const native_sleep = 134
|
|||||||
const native_read_file = 135
|
const native_read_file = 135
|
||||||
const native_write_file = 136
|
const native_write_file = 136
|
||||||
const native_eprint = 137
|
const native_eprint = 137
|
||||||
|
|
||||||
|
// build-module builtins (.vrmm) — available to `vr make` scripts and to any
|
||||||
|
// program that wants to drive the toolchain
|
||||||
|
const native_build_compile = 138
|
||||||
|
const native_build_assemble = 139
|
||||||
|
const native_build_link = 140
|
||||||
|
const native_build_run = 141
|
||||||
|
const native_build_test = 142
|
||||||
|
const native_build_bench = 143
|
||||||
|
const native_build_clean = 144
|
||||||
|
const native_build_exec = 145
|
||||||
|
const native_build_exec_status = 146
|
||||||
|
const native_build_exists = 147
|
||||||
|
const native_build_mkdir = 148
|
||||||
|
const native_build_rm = 149
|
||||||
|
const native_build_copy = 150
|
||||||
|
const native_build_glob = 151
|
||||||
|
const native_build_ls = 152
|
||||||
|
const native_build_base = 153
|
||||||
|
const native_build_dir = 154
|
||||||
|
const native_build_join = 155
|
||||||
|
const native_build_root = 156
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
// build.vrmm — a VuurRaaf Make Module: build instructions for the toolchain,
|
||||||
|
// written in VuurRaaf itself (like V's .vsh, but for this language).
|
||||||
|
//
|
||||||
|
// ./bin/vr make -f examples/build.vrmm build + run hello
|
||||||
|
// ./bin/vr make -f examples/build.vrmm multi compile/link/run a 2-file program
|
||||||
|
// ./bin/vr make -f examples/build.vrmm test run tests.vr
|
||||||
|
// ./bin/vr make -f examples/build.vrmm clean remove artifacts
|
||||||
|
// ./bin/vr make -f examples/build.vrmm deploy --prod deploy() with args()
|
||||||
|
//
|
||||||
|
// Paths are relative to the working directory; build_root() returns this
|
||||||
|
// module's own directory for scripts that want absolute paths.
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// compile + link + run in one step
|
||||||
|
build_run("examples/hello.vr")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn multi() {
|
||||||
|
// drive each toolchain stage explicitly
|
||||||
|
let root = build_root()
|
||||||
|
let lib = build_compile(build_join(root, "lib.vr"), build_join(root, "lib.vobj"))
|
||||||
|
let use = build_compile(build_join(root, "use_lib.vr"), build_join(root, "use_lib.vobj"))
|
||||||
|
let bin = build_link([lib, use], build_join(root, "use_lib.vbin"))
|
||||||
|
build_run(bin)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test() {
|
||||||
|
// run every test_* function; fails the build if any test fails
|
||||||
|
build_test("examples/tests.vr")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bench() {
|
||||||
|
// benchmark main() of fib.vr 2000 times
|
||||||
|
build_bench("examples/fib.vr", 2000)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deploy() {
|
||||||
|
// arguments after the target arrive via args()
|
||||||
|
let args = args()
|
||||||
|
if len(args) == 0 {
|
||||||
|
throw "deploy needs a flag: vr make deploy --prod"
|
||||||
|
}
|
||||||
|
println("deploying with args: " + join(args, " "))
|
||||||
|
let out = build_exec("echo 'deploy step ok'")
|
||||||
|
println(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clean() {
|
||||||
|
// remove this module's artifacts (build_rm returns 1 if it removed something)
|
||||||
|
let root = build_root()
|
||||||
|
for f in ["lib.vobj", "lib.vbin", "use_lib.vobj", "use_lib.vbin", "hello.vbin"] {
|
||||||
|
build_rm(build_join(root, f))
|
||||||
|
}
|
||||||
|
println("cleaned")
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// debug: does the function work at all?
|
||||||
|
|
||||||
|
fn double(x) {
|
||||||
|
return x * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println(double(21))
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// debug: generic function
|
||||||
|
|
||||||
|
fn first[T](arr) {
|
||||||
|
return arr[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let nums = [10, 20, 30]
|
||||||
|
let x = first(nums)
|
||||||
|
println(x)
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// debug: trace function execution
|
||||||
|
|
||||||
|
fn first[T](arr) {
|
||||||
|
let x = arr[0]
|
||||||
|
println(x)
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let nums = [10, 20, 30]
|
||||||
|
let x = first(nums)
|
||||||
|
println("main got: ${x}")
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// debug: trace everything
|
||||||
|
|
||||||
|
fn first[T](arr) {
|
||||||
|
println("inside first")
|
||||||
|
println(arr)
|
||||||
|
let x = arr[0]
|
||||||
|
println("x = ${x}")
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println("calling first")
|
||||||
|
let nums = [10, 20, 30]
|
||||||
|
let result = first(nums)
|
||||||
|
println("result = ${result}")
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// debug: test arr[0] in function body
|
||||||
|
|
||||||
|
fn first(arr) {
|
||||||
|
println(arr[0])
|
||||||
|
return arr[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let nums = [10, 20, 30]
|
||||||
|
let result = first(nums)
|
||||||
|
println("result = ${result}")
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// debug: basic arr[0]
|
||||||
|
|
||||||
|
fn get_first(arr) {
|
||||||
|
return arr[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let nums = [10, 20, 30]
|
||||||
|
println(get_first(nums))
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// debug: inline arr[0]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let nums = [10, 20, 30]
|
||||||
|
println(nums[0])
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// simple generic test
|
||||||
|
|
||||||
|
fn first[T](arr) {
|
||||||
|
return arr[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let nums = [10, 20, 30]
|
||||||
|
println(first[int](nums))
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// simple generic test - no type args
|
||||||
|
|
||||||
|
fn first[T](arr) {
|
||||||
|
return arr[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let nums = [10, 20, 30]
|
||||||
|
println(first(nums))
|
||||||
|
}
|
||||||
@@ -91,6 +91,9 @@ fn main() {
|
|||||||
'clean' {
|
'clean' {
|
||||||
toolchain_clean()
|
toolchain_clean()
|
||||||
}
|
}
|
||||||
|
'make', 'm', 'build' {
|
||||||
|
toolchain_make(rest) or { die('make', err) }
|
||||||
|
}
|
||||||
'up' {
|
'up' {
|
||||||
toolchain_up() or { die('up', err) }
|
toolchain_up() or { die('up', err) }
|
||||||
}
|
}
|
||||||
@@ -177,6 +180,9 @@ fn toolchain_help() {
|
|||||||
println(' get <owner/repo | url | ./path> fetch a package into vendor/')
|
println(' get <owner/repo | url | ./path> fetch a package into vendor/')
|
||||||
println(' install install deps from vr.mod')
|
println(' install install deps from vr.mod')
|
||||||
println(' list show the project manifest')
|
println(' list show the project manifest')
|
||||||
|
println(' make [target] [args...] run build.vrmm (target = main)')
|
||||||
|
println(' make -f <file.vrmm> [target] [args...] run another build module (.vrmm)')
|
||||||
|
println(' build alias for make')
|
||||||
println(' clean remove build artifacts')
|
println(' clean remove build artifacts')
|
||||||
println(' up rebuild the vr binary into bin/')
|
println(' up rebuild the vr binary into bin/')
|
||||||
println(' symlink link bin/vr into your PATH')
|
println(' symlink link bin/vr into your PATH')
|
||||||
@@ -468,6 +474,73 @@ fn run_src_with_args(src string, entry string, trace bool, args []string) ! {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// housekeeping
|
// housekeeping
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// make — run a .vrmm build module
|
||||||
|
|
||||||
|
// toolchain_make compiles a .vrmm build module and runs one of its targets.
|
||||||
|
// A build module is a VuurRaaf program that drives the toolchain through the
|
||||||
|
// build_* builtins (build_compile, build_link, build_run, build_exec, ...).
|
||||||
|
//
|
||||||
|
// vr make runs main() (or build()) from build.vrmm
|
||||||
|
// vr make clean runs the clean() target
|
||||||
|
// vr make deploy --prod runs deploy() with args() == ["--prod"]
|
||||||
|
// vr make -f x.vrmm t runs target t from x.vrmm
|
||||||
|
//
|
||||||
|
// A target that returns nonzero (or calls exit(n>0) / throws) fails the build.
|
||||||
|
fn toolchain_make(args []string) ! {
|
||||||
|
mut file := 'build.vrmm'
|
||||||
|
mut rest := []string{}
|
||||||
|
mut i := 0
|
||||||
|
for i < args.len {
|
||||||
|
if args[i] == '-f' && i + 1 < args.len {
|
||||||
|
file = args[i + 1]
|
||||||
|
i += 2
|
||||||
|
} else {
|
||||||
|
rest << args[i]
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !os.exists(file) {
|
||||||
|
return error('no build module "${file}" found (write one, or run `vr init` to scaffold it)')
|
||||||
|
}
|
||||||
|
mut target := 'main'
|
||||||
|
if rest.len > 0 {
|
||||||
|
target = rest[0]
|
||||||
|
rest = rest[1..].clone()
|
||||||
|
}
|
||||||
|
// compile and link the build module itself
|
||||||
|
tmp_obj := os.join_path(os.temp_dir(), 'vr_make_${os.getpid()}.vobj')
|
||||||
|
tmp_bin := os.join_path(os.temp_dir(), 'vr_make_${os.getpid()}.vbin')
|
||||||
|
defer {
|
||||||
|
os.rm(tmp_obj) or {}
|
||||||
|
os.rm(tmp_bin) or {}
|
||||||
|
}
|
||||||
|
o := compiler.compile_file(file)!
|
||||||
|
obj.write(tmp_obj, o)!
|
||||||
|
linker.link([tmp_obj], tmp_bin)!
|
||||||
|
bin := obj.read_bin(tmp_bin)!
|
||||||
|
// pick the entry: an explicit target, else main, else build
|
||||||
|
mut names := []string{}
|
||||||
|
for f in bin.fns {
|
||||||
|
names << f.name
|
||||||
|
}
|
||||||
|
mut entry := ''
|
||||||
|
if target == 'main' && 'main' in names {
|
||||||
|
entry = 'main'
|
||||||
|
} else if target == 'main' && 'build' in names {
|
||||||
|
entry = 'build'
|
||||||
|
} else if target in names {
|
||||||
|
entry = target
|
||||||
|
} else {
|
||||||
|
return error('no target function "${target}" in ${file} (available: ${names.join(', ')} or main)')
|
||||||
|
}
|
||||||
|
println('vr make: ${file} [${entry}]')
|
||||||
|
code := vm.run_build(bin, entry, rest, os.abs_path(os.dir(file)))!
|
||||||
|
if code != 0 {
|
||||||
|
return error('target ${entry} finished with exit code ${code}')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn toolchain_clean() {
|
fn toolchain_clean() {
|
||||||
mut n := 0
|
mut n := 0
|
||||||
if files := os.ls('.') {
|
if files := os.ls('.') {
|
||||||
|
|||||||
@@ -24,7 +24,29 @@ fn toolchain_init(args []string) ! {
|
|||||||
if !os.exists('main.vr') {
|
if !os.exists('main.vr') {
|
||||||
os.write_file('main.vr', 'fn main() {\n\tprintln("hello from ${pkg}")\n}\n')!
|
os.write_file('main.vr', 'fn main() {\n\tprintln("hello from ${pkg}")\n}\n')!
|
||||||
}
|
}
|
||||||
println('initialized project "${pkg}" (${manifest_file}, main.vr)')
|
if !os.exists('build.vrmm') {
|
||||||
|
os.write_file('build.vrmm', '// build.vrmm — build instructions for the VuurRaaf toolchain.
|
||||||
|
//
|
||||||
|
// vr make runs main()
|
||||||
|
// vr make clean runs the clean() target
|
||||||
|
//
|
||||||
|
// Build builtins: build_compile, build_assemble, build_link, build_run,
|
||||||
|
// build_test, build_bench, build_clean, build_exec, build_exec_status,
|
||||||
|
// build_exists, build_mkdir, build_rm, build_copy, build_glob, build_ls,
|
||||||
|
// build_base, build_dir, build_join, build_root.
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
build_compile("main.vr", "main.vobj")
|
||||||
|
build_link(["main.vobj"], "main.vbin")
|
||||||
|
println("built main.vbin — run it with: vr run main.vbin")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clean() {
|
||||||
|
build_clean()
|
||||||
|
}
|
||||||
|
')!
|
||||||
|
}
|
||||||
|
println('initialized project "${pkg}" (${manifest_file}, main.vr, build.vrmm)')
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toolchain_get(args []string) ! {
|
fn toolchain_get(args []string) ! {
|
||||||
|
|||||||
+252
@@ -11,6 +11,10 @@ import os
|
|||||||
import math
|
import math
|
||||||
import rand
|
import rand
|
||||||
import time
|
import time
|
||||||
|
import obj
|
||||||
|
import compiler
|
||||||
|
import assembler
|
||||||
|
import linker
|
||||||
|
|
||||||
fn (mut v Vm) native(id int, _argc int) ! {
|
fn (mut v Vm) native(id int, _argc int) ! {
|
||||||
match id {
|
match id {
|
||||||
@@ -346,12 +350,260 @@ fn (mut v Vm) native(id int, _argc int) ! {
|
|||||||
x := v.pop()!
|
x := v.pop()!
|
||||||
eprintln(v.val_str(x, 0))
|
eprintln(v.val_str(x, 0))
|
||||||
}
|
}
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// build-module builtins (.vrmm) — these let a VuurRaaf program drive
|
||||||
|
// the toolchain itself, the way V's .vsh scripts drive `v build`.
|
||||||
|
native_build_compile {
|
||||||
|
out := v.pop_str()!
|
||||||
|
src := v.pop_str()!
|
||||||
|
o := compiler.compile_file(src) or { return error('build_compile: ${err.msg()}') }
|
||||||
|
o_path := if out.len == 0 { src.all_before_last('.') + '.vobj' } else { out }
|
||||||
|
obj.write(o_path, o) or { return error('build_compile: cannot write ${o_path}: ${err.msg()}') }
|
||||||
|
println('compiled ${src} -> ${o_path} (${o.code.len} bytes code, ${o.symbols.len} symbols)')
|
||||||
|
v.push(v.alloc_str(o_path))!
|
||||||
|
}
|
||||||
|
native_build_assemble {
|
||||||
|
out := v.pop_str()!
|
||||||
|
src := v.pop_str()!
|
||||||
|
o := assembler.assemble_file(src) or { return error('build_assemble: ${err.msg()}') }
|
||||||
|
o_path := if out.len == 0 { src.all_before_last('.') + '.vobj' } else { out }
|
||||||
|
obj.write(o_path, o) or { return error('build_assemble: cannot write ${o_path}: ${err.msg()}') }
|
||||||
|
println('assembled ${src} -> ${o_path} (${o.code.len} bytes code)')
|
||||||
|
v.push(v.alloc_str(o_path))!
|
||||||
|
}
|
||||||
|
native_build_link {
|
||||||
|
out := v.pop_str()!
|
||||||
|
h := v.pop()!
|
||||||
|
if !v.is_arr(h) || !v.valid_arr_handle(h) {
|
||||||
|
return error('build_link() expects an array of object files as its first argument')
|
||||||
|
}
|
||||||
|
mut objs := []string{}
|
||||||
|
for x in v.arrays[v.hand(h)] {
|
||||||
|
if !v.is_str(x) || !v.valid_handle(x) {
|
||||||
|
return error('build_link() expects string paths inside the object array')
|
||||||
|
}
|
||||||
|
objs << v.strings[v.hand(x)]
|
||||||
|
}
|
||||||
|
if objs.len == 0 {
|
||||||
|
return error('build_link() needs at least one object file')
|
||||||
|
}
|
||||||
|
o_path := if out.len == 0 { os.base(objs[0]).all_before_last('.') + '.vbin' } else { out }
|
||||||
|
linker.link(objs, o_path) or { return error('build_link: ${err.msg()}') }
|
||||||
|
println('linked ${objs.len} object file(s) -> ${o_path}')
|
||||||
|
v.push(v.alloc_str(o_path))!
|
||||||
|
}
|
||||||
|
native_build_run {
|
||||||
|
f := v.pop_str()!
|
||||||
|
if f.ends_with('.vbin') {
|
||||||
|
bin := obj.read_bin(f) or { return error('build_run: ${err.msg()}') }
|
||||||
|
code := run_with_args(bin, 'main', false, []string{}) or {
|
||||||
|
return error('build_run: ${err.msg()}')
|
||||||
|
}
|
||||||
|
v.push(v.enc_int(code))!
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !f.ends_with('.vr') {
|
||||||
|
return error('build_run() expects a .vr or .vbin file')
|
||||||
|
}
|
||||||
|
tmp_obj := os.join_path(os.temp_dir(), 'vr_build_${os.getpid()}.vobj')
|
||||||
|
tmp_bin := os.join_path(os.temp_dir(), 'vr_build_${os.getpid()}.vbin')
|
||||||
|
defer {
|
||||||
|
os.rm(tmp_obj) or {}
|
||||||
|
os.rm(tmp_bin) or {}
|
||||||
|
}
|
||||||
|
o := compiler.compile_file(f) or { return error('build_run: ${err.msg()}') }
|
||||||
|
obj.write(tmp_obj, o) or { return error('build_run: ${err.msg()}') }
|
||||||
|
linker.link([tmp_obj], tmp_bin) or { return error('build_run: ${err.msg()}') }
|
||||||
|
bin := obj.read_bin(tmp_bin) or { return error('build_run: ${err.msg()}') }
|
||||||
|
code := run_with_args(bin, 'main', false, []string{}) or {
|
||||||
|
return error('build_run: ${err.msg()}')
|
||||||
|
}
|
||||||
|
v.push(v.enc_int(code))!
|
||||||
|
}
|
||||||
|
native_build_test {
|
||||||
|
src := v.pop_str()!
|
||||||
|
o := compiler.compile_file(src) or { return error('build_test: ${err.msg()}') }
|
||||||
|
mut tests := []string{}
|
||||||
|
for s in o.symbols {
|
||||||
|
if s.name.starts_with('test_') {
|
||||||
|
tests << s.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if tests.len == 0 {
|
||||||
|
return error('build_test: no test_* functions found in ${src}')
|
||||||
|
}
|
||||||
|
tmp_obj := os.join_path(os.temp_dir(), 'vr_build_${os.getpid()}.vobj')
|
||||||
|
tmp_bin := os.join_path(os.temp_dir(), 'vr_build_${os.getpid()}.vbin')
|
||||||
|
defer {
|
||||||
|
os.rm(tmp_obj) or {}
|
||||||
|
os.rm(tmp_bin) or {}
|
||||||
|
}
|
||||||
|
obj.write(tmp_obj, o) or { return error('build_test: ${err.msg()}') }
|
||||||
|
linker.link([tmp_obj], tmp_bin) or { return error('build_test: ${err.msg()}') }
|
||||||
|
bin := obj.read_bin(tmp_bin) or { return error('build_test: ${err.msg()}') }
|
||||||
|
mut passes := 0
|
||||||
|
mut fails := 0
|
||||||
|
for t in tests {
|
||||||
|
run(bin, t, false) or {
|
||||||
|
eprintln(' FAIL ${t} — ${err}')
|
||||||
|
fails++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
println(' PASS ${t}')
|
||||||
|
passes++
|
||||||
|
}
|
||||||
|
if fails > 0 {
|
||||||
|
return error('build_test: ${fails} of ${tests.len} test(s) failed in ${src}')
|
||||||
|
}
|
||||||
|
println('${passes} test(s) passed')
|
||||||
|
v.push(v.enc_int(i64(passes)))!
|
||||||
|
}
|
||||||
|
native_build_bench {
|
||||||
|
n := int(v.dec_int(v.pop()!))
|
||||||
|
src := v.pop_str()!
|
||||||
|
if n < 1 {
|
||||||
|
return error('build_bench() expects a positive iteration count')
|
||||||
|
}
|
||||||
|
tmp_obj := os.join_path(os.temp_dir(), 'vr_build_${os.getpid()}.vobj')
|
||||||
|
tmp_bin := os.join_path(os.temp_dir(), 'vr_build_${os.getpid()}.vbin')
|
||||||
|
defer {
|
||||||
|
os.rm(tmp_obj) or {}
|
||||||
|
os.rm(tmp_bin) or {}
|
||||||
|
}
|
||||||
|
o := compiler.compile_file(src) or { return error('build_bench: ${err.msg()}') }
|
||||||
|
obj.write(tmp_obj, o) or { return error('build_bench: ${err.msg()}') }
|
||||||
|
linker.link([tmp_obj], tmp_bin) or { return error('build_bench: ${err.msg()}') }
|
||||||
|
bin := obj.read_bin(tmp_bin) or { return error('build_bench: ${err.msg()}') }
|
||||||
|
start := time.now().unix_milli()
|
||||||
|
for _ in 0..n {
|
||||||
|
run(bin, 'main', false) or { return error('build_bench: ${err.msg()}') }
|
||||||
|
}
|
||||||
|
ms := time.now().unix_milli() - start
|
||||||
|
rate := if ms > 0 { f64(n) / (f64(ms) / 1000.0) } else { f64(0) }
|
||||||
|
println('bench: ${n} runs of main() in ${ms}ms (${rate:.0} runs/s)')
|
||||||
|
v.push(v.enc_int(0))!
|
||||||
|
}
|
||||||
|
native_build_clean {
|
||||||
|
mut n := 0
|
||||||
|
if files := os.ls('.') {
|
||||||
|
for f in files {
|
||||||
|
if f.ends_with('.vobj') || f.ends_with('.vbin') {
|
||||||
|
os.rm(f) or {}
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println('cleaned ${n} artifact(s)')
|
||||||
|
v.push(v.enc_int(i64(n)))!
|
||||||
|
}
|
||||||
|
native_build_exec {
|
||||||
|
cmd := v.pop_str()!
|
||||||
|
res := os.execute(cmd)
|
||||||
|
if res.exit_code != 0 {
|
||||||
|
return error('build_exec: "${cmd}" failed with exit code ${res.exit_code}: ${res.output}')
|
||||||
|
}
|
||||||
|
v.push(v.alloc_str(res.output))!
|
||||||
|
}
|
||||||
|
native_build_exec_status {
|
||||||
|
cmd := v.pop_str()!
|
||||||
|
res := os.execute(cmd)
|
||||||
|
v.push(v.enc_int(i64(res.exit_code)))!
|
||||||
|
}
|
||||||
|
native_build_exists {
|
||||||
|
p := v.pop_str()!
|
||||||
|
v.push(v.enc_int(bool_i64(os.exists(p))))!
|
||||||
|
}
|
||||||
|
native_build_mkdir {
|
||||||
|
p := v.pop_str()!
|
||||||
|
os.mkdir_all(p) or { return error('build_mkdir: cannot create ${p}: ${err.msg()}') }
|
||||||
|
v.push(v.enc_int(0))!
|
||||||
|
}
|
||||||
|
native_build_rm {
|
||||||
|
p := v.pop_str()!
|
||||||
|
if !os.exists(p) {
|
||||||
|
v.push(v.enc_int(0))!
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if os.is_dir(p) {
|
||||||
|
os.rmdir_all(p) or { return error('build_rm: cannot remove ${p}: ${err.msg()}') }
|
||||||
|
} else {
|
||||||
|
os.rm(p) or { return error('build_rm: cannot remove ${p}: ${err.msg()}') }
|
||||||
|
}
|
||||||
|
v.push(v.enc_int(1))!
|
||||||
|
}
|
||||||
|
native_build_copy {
|
||||||
|
dst := v.pop_str()!
|
||||||
|
src := v.pop_str()!
|
||||||
|
if os.is_dir(src) {
|
||||||
|
v.copy_tree(src, dst) or { return error('build_copy: ${err.msg()}') }
|
||||||
|
} else {
|
||||||
|
os.cp(src, dst, os.CopyParams{}) or {
|
||||||
|
return error('build_copy: cannot copy ${src} -> ${dst}: ${err.msg()}')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v.push(v.enc_int(0))!
|
||||||
|
}
|
||||||
|
native_build_glob {
|
||||||
|
pat := v.pop_str()!
|
||||||
|
files := os.glob(pat) or { return error('build_glob: ${err.msg()}') }
|
||||||
|
mut arr := []i64{}
|
||||||
|
for f in files {
|
||||||
|
v.strings << f
|
||||||
|
arr << v.mkstr(v.strings.len - 1)
|
||||||
|
}
|
||||||
|
v.arrays << arr
|
||||||
|
v.push(v.mkarr(v.arrays.len - 1))!
|
||||||
|
}
|
||||||
|
native_build_ls {
|
||||||
|
dir := v.pop_str()!
|
||||||
|
entries := os.ls(dir) or { return error('build_ls: cannot read ${dir}: ${err.msg()}') }
|
||||||
|
mut arr := []i64{}
|
||||||
|
for f in entries {
|
||||||
|
v.strings << f
|
||||||
|
arr << v.mkstr(v.strings.len - 1)
|
||||||
|
}
|
||||||
|
v.arrays << arr
|
||||||
|
v.push(v.mkarr(v.arrays.len - 1))!
|
||||||
|
}
|
||||||
|
native_build_base {
|
||||||
|
p := v.pop_str()!
|
||||||
|
v.push(v.alloc_str(os.base(p)))!
|
||||||
|
}
|
||||||
|
native_build_dir {
|
||||||
|
p := v.pop_str()!
|
||||||
|
v.push(v.alloc_str(os.dir(p)))!
|
||||||
|
}
|
||||||
|
native_build_join {
|
||||||
|
b := v.pop_str()!
|
||||||
|
a := v.pop_str()!
|
||||||
|
v.push(v.alloc_str(os.join_path(a, b)))!
|
||||||
|
}
|
||||||
|
native_build_root {
|
||||||
|
v.push(v.alloc_str(v.build_root))!
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
return error('unknown native builtin ${id}')
|
return error('unknown native builtin ${id}')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// copy_tree recursively copies a directory tree (used by build_copy).
|
||||||
|
fn (mut v Vm) copy_tree(src string, dst string) ! {
|
||||||
|
if !os.is_dir(src) {
|
||||||
|
return error('${src} is not a directory')
|
||||||
|
}
|
||||||
|
os.mkdir_all(dst) or { return error('cannot create ${dst}: ${err.msg()}') }
|
||||||
|
for entry in os.ls(src) or { return error('cannot read ${src}: ${err.msg()}') } {
|
||||||
|
sp := os.join_path(src, entry)
|
||||||
|
dp := os.join_path(dst, entry)
|
||||||
|
if os.is_dir(sp) {
|
||||||
|
v.copy_tree(sp, dp)!
|
||||||
|
} else if os.is_file(sp) {
|
||||||
|
os.cp(sp, dp, os.CopyParams{}) or { return error('cannot copy ${sp}: ${err.msg()}') }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// pop_str pops the top value and requires it to be a string.
|
// pop_str pops the top value and requires it to be a string.
|
||||||
fn (mut v Vm) pop_str() !string {
|
fn (mut v Vm) pop_str() !string {
|
||||||
x := v.pop()!
|
x := v.pop()!
|
||||||
|
|||||||
@@ -105,3 +105,24 @@ const native_sleep = 134
|
|||||||
const native_read_file = 135
|
const native_read_file = 135
|
||||||
const native_write_file = 136
|
const native_write_file = 136
|
||||||
const native_eprint = 137
|
const native_eprint = 137
|
||||||
|
|
||||||
|
// build-module builtins (.vrmm) — driven by `vr make`
|
||||||
|
const native_build_compile = 138
|
||||||
|
const native_build_assemble = 139
|
||||||
|
const native_build_link = 140
|
||||||
|
const native_build_run = 141
|
||||||
|
const native_build_test = 142
|
||||||
|
const native_build_bench = 143
|
||||||
|
const native_build_clean = 144
|
||||||
|
const native_build_exec = 145
|
||||||
|
const native_build_exec_status = 146
|
||||||
|
const native_build_exists = 147
|
||||||
|
const native_build_mkdir = 148
|
||||||
|
const native_build_rm = 149
|
||||||
|
const native_build_copy = 150
|
||||||
|
const native_build_glob = 151
|
||||||
|
const native_build_ls = 152
|
||||||
|
const native_build_base = 153
|
||||||
|
const native_build_dir = 154
|
||||||
|
const native_build_join = 155
|
||||||
|
const native_build_root = 156
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ mut:
|
|||||||
lines []obj.LineInfo // debug info: code offset -> source line
|
lines []obj.LineInfo // debug info: code offset -> source line
|
||||||
const_strs int // strings[0..const_strs] are bytecode constants, never collected
|
const_strs int // strings[0..const_strs] are bytecode constants, never collected
|
||||||
last_heap int // heap size at the last GC check (allocation trigger)
|
last_heap int // heap size at the last GC check (allocation trigger)
|
||||||
|
build_root string // directory of the .vrmm build module (build_root() builtin)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bool_i64(b bool) i64 {
|
fn bool_i64(b bool) i64 {
|
||||||
|
|||||||
@@ -17,6 +17,17 @@ pub fn run(bin obj.Bin, entry string, trace bool) !i64 {
|
|||||||
// run_with_args is run() with command-line arguments exposed to the program
|
// run_with_args is run() with command-line arguments exposed to the program
|
||||||
// via the `args()` builtin.
|
// via the `args()` builtin.
|
||||||
pub fn run_with_args(bin obj.Bin, entry string, trace bool, args []string) !i64 {
|
pub fn run_with_args(bin obj.Bin, entry string, trace bool, args []string) !i64 {
|
||||||
|
return run_internal(bin, entry, trace, args, '')!
|
||||||
|
}
|
||||||
|
|
||||||
|
// run_build executes a .vrmm build module: the entry target receives the
|
||||||
|
// extra CLI arguments via `args()`, and `build_root()` reports the module's
|
||||||
|
// own directory so scripts can find files regardless of the working directory.
|
||||||
|
pub fn run_build(bin obj.Bin, entry string, args []string, root string) !i64 {
|
||||||
|
return run_internal(bin, entry, false, args, root)!
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_internal(bin obj.Bin, entry string, trace bool, args []string, root string) !i64 {
|
||||||
mut v := Vm{
|
mut v := Vm{
|
||||||
code: bin.code
|
code: bin.code
|
||||||
strings: bin.strings.clone()
|
strings: bin.strings.clone()
|
||||||
@@ -25,6 +36,7 @@ pub fn run_with_args(bin obj.Bin, entry string, trace bool, args []string) !i64
|
|||||||
prog_args: args
|
prog_args: args
|
||||||
lines: bin.lines
|
lines: bin.lines
|
||||||
const_strs: bin.strings.len
|
const_strs: bin.strings.len
|
||||||
|
build_root: root
|
||||||
}
|
}
|
||||||
mut entry_ip := -1
|
mut entry_ip := -1
|
||||||
for f in bin.fns {
|
for f in bin.fns {
|
||||||
|
|||||||
Reference in New Issue
Block a user