v: add support for `v crun examples/hello_world.v`, use crun mode for .vsh files by default. (#14554)
parent
c91b646372
commit
bf70f0b436
|
@ -7,6 +7,7 @@ Examples:
|
||||||
v hello.v Compile the file `hello.v` and output it as `hello` or `hello.exe`.
|
v hello.v Compile the file `hello.v` and output it as `hello` or `hello.exe`.
|
||||||
v run hello.v Same as above but also run the produced executable immediately after compilation.
|
v run hello.v Same as above but also run the produced executable immediately after compilation.
|
||||||
v -cg run hello.v Same as above, but make debugging easier (in case your program crashes).
|
v -cg run hello.v Same as above, but make debugging easier (in case your program crashes).
|
||||||
|
v crun hello.v Same as above, but do not recompile, if the executable already exists, and is newer than the sources.
|
||||||
v -o h.c hello.v Translate `hello.v` to `h.c`. Do not compile further.
|
v -o h.c hello.v Translate `hello.v` to `h.c`. Do not compile further.
|
||||||
v -o - hello.v Translate `hello.v` and output the C source code to stdout. Do not compile further.
|
v -o - hello.v Translate `hello.v` and output the C source code to stdout. Do not compile further.
|
||||||
|
|
||||||
|
@ -20,7 +21,10 @@ V supports the following commands:
|
||||||
init Setup the file structure for an already existing V project.
|
init Setup the file structure for an already existing V project.
|
||||||
|
|
||||||
* Ordinary development:
|
* Ordinary development:
|
||||||
run Compile and run a V program.
|
run Compile and run a V program. Delete the executable after the run.
|
||||||
|
crun Compile and run a V program without deleting the executable.
|
||||||
|
If you run the same program a second time, without changing the source files,
|
||||||
|
V will just run the executable, without recompilation. Suitable for scripting.
|
||||||
test Run all test files in the provided directory.
|
test Run all test files in the provided directory.
|
||||||
fmt Format the V code provided.
|
fmt Format the V code provided.
|
||||||
vet Report suspicious code constructs.
|
vet Report suspicious code constructs.
|
||||||
|
|
80
cmd/v/v.v
80
cmd/v/v.v
|
@ -93,17 +93,21 @@ fn main() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
match command {
|
match command {
|
||||||
|
'run', 'crun', 'build', 'build-module' {
|
||||||
|
rebuild(prefs)
|
||||||
|
return
|
||||||
|
}
|
||||||
'help' {
|
'help' {
|
||||||
invoke_help_and_exit(args)
|
invoke_help_and_exit(args)
|
||||||
}
|
}
|
||||||
|
'version' {
|
||||||
|
println(version.full_v_version(prefs.is_verbose))
|
||||||
|
return
|
||||||
|
}
|
||||||
'new', 'init' {
|
'new', 'init' {
|
||||||
util.launch_tool(prefs.is_verbose, 'vcreate', os.args[1..])
|
util.launch_tool(prefs.is_verbose, 'vcreate', os.args[1..])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
'translate' {
|
|
||||||
eprintln('Translating C to V will be available in V 0.3')
|
|
||||||
exit(1)
|
|
||||||
}
|
|
||||||
'install', 'list', 'outdated', 'remove', 'search', 'show', 'update', 'upgrade' {
|
'install', 'list', 'outdated', 'remove', 'search', 'show', 'update', 'upgrade' {
|
||||||
util.launch_tool(prefs.is_verbose, 'vpm', os.args[1..])
|
util.launch_tool(prefs.is_verbose, 'vpm', os.args[1..])
|
||||||
return
|
return
|
||||||
|
@ -118,42 +122,24 @@ fn main() {
|
||||||
eprintln('V Error: Use `v install` to install modules from vpm.vlang.io')
|
eprintln('V Error: Use `v install` to install modules from vpm.vlang.io')
|
||||||
exit(1)
|
exit(1)
|
||||||
}
|
}
|
||||||
'version' {
|
'translate' {
|
||||||
println(version.full_v_version(prefs.is_verbose))
|
eprintln('Translating C to V will be available in V 0.3')
|
||||||
return
|
exit(1)
|
||||||
}
|
}
|
||||||
else {}
|
else {
|
||||||
}
|
if command.ends_with('.v') || os.exists(command) {
|
||||||
if command in ['run', 'build', 'build-module'] || command.ends_with('.v') || os.exists(command) {
|
// println('command')
|
||||||
// println('command')
|
// println(prefs.path)
|
||||||
// println(prefs.path)
|
rebuild(prefs)
|
||||||
match prefs.backend {
|
return
|
||||||
.c {
|
|
||||||
$if no_bootstrapv ? {
|
|
||||||
// TODO: improve the bootstrapping with a split C backend here.
|
|
||||||
// C code generated by `VEXE=v cmd/tools/builders/c_builder -os cross -o c.c cmd/tools/builders/c_builder.v`
|
|
||||||
// is enough to bootstrap the C backend, and thus the rest, but currently bootstrapping relies on
|
|
||||||
// `v -os cross -o v.c cmd/v` having a functional C codegen inside instead.
|
|
||||||
util.launch_tool(prefs.is_verbose, 'builders/c_builder', os.args[1..])
|
|
||||||
}
|
|
||||||
builder.compile('build', prefs, cbuilder.compile_c)
|
|
||||||
}
|
|
||||||
.js_node, .js_freestanding, .js_browser {
|
|
||||||
util.launch_tool(prefs.is_verbose, 'builders/js_builder', os.args[1..])
|
|
||||||
}
|
|
||||||
.native {
|
|
||||||
util.launch_tool(prefs.is_verbose, 'builders/native_builder', os.args[1..])
|
|
||||||
}
|
|
||||||
.interpret {
|
|
||||||
util.launch_tool(prefs.is_verbose, 'builders/interpret_builder', os.args[1..])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if prefs.is_help {
|
if prefs.is_help {
|
||||||
invoke_help_and_exit(args)
|
invoke_help_and_exit(args)
|
||||||
}
|
}
|
||||||
eprintln('v $command: unknown command\nRun ${term.highlight_command('v help')} for usage.')
|
eprintln('v $command: unknown command')
|
||||||
|
eprintln('Run ${term.highlight_command('v help')} for usage.')
|
||||||
exit(1)
|
exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,7 +149,31 @@ fn invoke_help_and_exit(remaining []string) {
|
||||||
2 { help.print_and_exit(remaining[1]) }
|
2 { help.print_and_exit(remaining[1]) }
|
||||||
else {}
|
else {}
|
||||||
}
|
}
|
||||||
println('${term.highlight_command('v help')}: provide only one help topic.')
|
eprintln('${term.highlight_command('v help')}: provide only one help topic.')
|
||||||
println('For usage information, use ${term.highlight_command('v help')}.')
|
eprintln('For usage information, use ${term.highlight_command('v help')}.')
|
||||||
exit(1)
|
exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn rebuild(prefs &pref.Preferences) {
|
||||||
|
match prefs.backend {
|
||||||
|
.c {
|
||||||
|
$if no_bootstrapv ? {
|
||||||
|
// TODO: improve the bootstrapping with a split C backend here.
|
||||||
|
// C code generated by `VEXE=v cmd/tools/builders/c_builder -os cross -o c.c cmd/tools/builders/c_builder.v`
|
||||||
|
// is enough to bootstrap the C backend, and thus the rest, but currently bootstrapping relies on
|
||||||
|
// `v -os cross -o v.c cmd/v` having a functional C codegen inside instead.
|
||||||
|
util.launch_tool(prefs.is_verbose, 'builders/c_builder', os.args[1..])
|
||||||
|
}
|
||||||
|
builder.compile('build', prefs, cbuilder.compile_c)
|
||||||
|
}
|
||||||
|
.js_node, .js_freestanding, .js_browser {
|
||||||
|
util.launch_tool(prefs.is_verbose, 'builders/js_builder', os.args[1..])
|
||||||
|
}
|
||||||
|
.native {
|
||||||
|
util.launch_tool(prefs.is_verbose, 'builders/native_builder', os.args[1..])
|
||||||
|
}
|
||||||
|
.interpret {
|
||||||
|
util.launch_tool(prefs.is_verbose, 'builders/interpret_builder', os.args[1..])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ import v.dotgraph
|
||||||
|
|
||||||
pub struct Builder {
|
pub struct Builder {
|
||||||
pub:
|
pub:
|
||||||
compiled_dir string // contains os.real_path() of the dir of the final file beeing compiled, or the dir itself when doing `v .`
|
compiled_dir string // contains os.real_path() of the dir of the final file being compiled, or the dir itself when doing `v .`
|
||||||
module_path string
|
module_path string
|
||||||
pub mut:
|
pub mut:
|
||||||
checker &checker.Checker
|
checker &checker.Checker
|
||||||
|
@ -40,6 +40,7 @@ pub mut:
|
||||||
mod_invalidates_paths map[string][]string // changes in mod `os`, invalidate only .v files, that do `import os`
|
mod_invalidates_paths map[string][]string // changes in mod `os`, invalidate only .v files, that do `import os`
|
||||||
mod_invalidates_mods map[string][]string // changes in mod `os`, force invalidation of mods, that do `import os`
|
mod_invalidates_mods map[string][]string // changes in mod `os`, force invalidation of mods, that do `import os`
|
||||||
path_invalidates_mods map[string][]string // changes in a .v file from `os`, invalidates `os`
|
path_invalidates_mods map[string][]string // changes in a .v file from `os`, invalidates `os`
|
||||||
|
crun_cache_keys []string // target executable + top level source files; filled in by Builder.should_rebuild
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_builder(pref &pref.Preferences) Builder {
|
pub fn new_builder(pref &pref.Preferences) Builder {
|
||||||
|
|
|
@ -3,9 +3,7 @@
|
||||||
// that can be found in the LICENSE file.
|
// that can be found in the LICENSE file.
|
||||||
module builder
|
module builder
|
||||||
|
|
||||||
import time
|
|
||||||
import os
|
import os
|
||||||
import rand
|
|
||||||
import v.pref
|
import v.pref
|
||||||
import v.util
|
import v.util
|
||||||
import v.checker
|
import v.checker
|
||||||
|
@ -13,6 +11,19 @@ import v.checker
|
||||||
pub type FnBackend = fn (mut b Builder)
|
pub type FnBackend = fn (mut b Builder)
|
||||||
|
|
||||||
pub fn compile(command string, pref &pref.Preferences, backend_cb FnBackend) {
|
pub fn compile(command string, pref &pref.Preferences, backend_cb FnBackend) {
|
||||||
|
check_if_output_folder_is_writable(pref)
|
||||||
|
// Construct the V object from command line arguments
|
||||||
|
mut b := new_builder(pref)
|
||||||
|
if b.should_rebuild() {
|
||||||
|
b.rebuild(backend_cb)
|
||||||
|
}
|
||||||
|
b.exit_on_invalid_syntax()
|
||||||
|
// running does not require the parsers anymore
|
||||||
|
unsafe { b.myfree() }
|
||||||
|
b.run_compiled_executable_and_exit()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_if_output_folder_is_writable(pref &pref.Preferences) {
|
||||||
odir := os.dir(pref.out_name)
|
odir := os.dir(pref.out_name)
|
||||||
// When pref.out_name is just the name of an executable, i.e. `./v -o executable main.v`
|
// When pref.out_name is just the name of an executable, i.e. `./v -o executable main.v`
|
||||||
// without a folder component, just use the current folder instead:
|
// without a folder component, just use the current folder instead:
|
||||||
|
@ -24,56 +35,6 @@ pub fn compile(command string, pref &pref.Preferences, backend_cb FnBackend) {
|
||||||
// An early error here, is better than an unclear C error later:
|
// An early error here, is better than an unclear C error later:
|
||||||
verror(err.msg())
|
verror(err.msg())
|
||||||
}
|
}
|
||||||
// Construct the V object from command line arguments
|
|
||||||
mut b := new_builder(pref)
|
|
||||||
if pref.is_verbose {
|
|
||||||
println('builder.compile() pref:')
|
|
||||||
// println(pref)
|
|
||||||
}
|
|
||||||
mut sw := time.new_stopwatch()
|
|
||||||
backend_cb(mut b)
|
|
||||||
mut timers := util.get_timers()
|
|
||||||
timers.show_remaining()
|
|
||||||
if pref.is_stats {
|
|
||||||
compilation_time_micros := 1 + sw.elapsed().microseconds()
|
|
||||||
scompilation_time_ms := util.bold('${f64(compilation_time_micros) / 1000.0:6.3f}')
|
|
||||||
mut all_v_source_lines, mut all_v_source_bytes := 0, 0
|
|
||||||
for pf in b.parsed_files {
|
|
||||||
all_v_source_lines += pf.nr_lines
|
|
||||||
all_v_source_bytes += pf.nr_bytes
|
|
||||||
}
|
|
||||||
mut sall_v_source_lines := all_v_source_lines.str()
|
|
||||||
mut sall_v_source_bytes := all_v_source_bytes.str()
|
|
||||||
sall_v_source_lines = util.bold('${sall_v_source_lines:10s}')
|
|
||||||
sall_v_source_bytes = util.bold('${sall_v_source_bytes:10s}')
|
|
||||||
println(' V source code size: $sall_v_source_lines lines, $sall_v_source_bytes bytes')
|
|
||||||
//
|
|
||||||
mut slines := b.stats_lines.str()
|
|
||||||
mut sbytes := b.stats_bytes.str()
|
|
||||||
slines = util.bold('${slines:10s}')
|
|
||||||
sbytes = util.bold('${sbytes:10s}')
|
|
||||||
println('generated target code size: $slines lines, $sbytes bytes')
|
|
||||||
//
|
|
||||||
vlines_per_second := int(1_000_000.0 * f64(all_v_source_lines) / f64(compilation_time_micros))
|
|
||||||
svlines_per_second := util.bold(vlines_per_second.str())
|
|
||||||
println('compilation took: $scompilation_time_ms ms, compilation speed: $svlines_per_second vlines/s')
|
|
||||||
}
|
|
||||||
b.exit_on_invalid_syntax()
|
|
||||||
// running does not require the parsers anymore
|
|
||||||
unsafe { b.myfree() }
|
|
||||||
if pref.is_test || pref.is_run {
|
|
||||||
b.run_compiled_executable_and_exit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut b Builder) get_vtmp_filename(base_file_name string, postfix string) string {
|
|
||||||
vtmp := util.get_vtmp_folder()
|
|
||||||
mut uniq := ''
|
|
||||||
if !b.pref.reuse_tmpc {
|
|
||||||
uniq = '.$rand.u64()'
|
|
||||||
}
|
|
||||||
fname := os.file_name(os.real_path(base_file_name)) + '$uniq$postfix'
|
|
||||||
return os.real_path(os.join_path(vtmp, fname))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Temporary, will be done by -autofree
|
// Temporary, will be done by -autofree
|
||||||
|
@ -118,47 +79,45 @@ fn (mut b Builder) run_compiled_executable_and_exit() {
|
||||||
if b.pref.os == .ios {
|
if b.pref.os == .ios {
|
||||||
panic('Running iOS apps is not supported yet.')
|
panic('Running iOS apps is not supported yet.')
|
||||||
}
|
}
|
||||||
|
if !(b.pref.is_test || b.pref.is_run || b.pref.is_crun) {
|
||||||
|
exit(0)
|
||||||
|
}
|
||||||
|
compiled_file := os.real_path(b.pref.out_name)
|
||||||
|
run_file := if b.pref.backend.is_js() {
|
||||||
|
node_basename := $if windows { 'node.exe' } $else { 'node' }
|
||||||
|
os.find_abs_path_of_executable(node_basename) or {
|
||||||
|
panic('Could not find `node` in system path. Do you have Node.js installed?')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
compiled_file
|
||||||
|
}
|
||||||
|
mut run_args := []string{cap: b.pref.run_args.len + 1}
|
||||||
|
if b.pref.backend.is_js() {
|
||||||
|
run_args << compiled_file
|
||||||
|
}
|
||||||
|
run_args << b.pref.run_args
|
||||||
|
mut run_process := os.new_process(run_file)
|
||||||
|
run_process.set_args(run_args)
|
||||||
if b.pref.is_verbose {
|
if b.pref.is_verbose {
|
||||||
|
println('running $run_process.filename with arguments $run_process.args')
|
||||||
}
|
}
|
||||||
if b.pref.is_test || b.pref.is_run {
|
// Ignore sigint and sigquit while running the compiled file,
|
||||||
compiled_file := os.real_path(b.pref.out_name)
|
// so ^C doesn't prevent v from deleting the compiled file.
|
||||||
run_file := if b.pref.backend.is_js() {
|
// See also https://git.musl-libc.org/cgit/musl/tree/src/process/system.c
|
||||||
node_basename := $if windows { 'node.exe' } $else { 'node' }
|
prev_int_handler := os.signal_opt(.int, eshcb) or { serror('set .int', err) }
|
||||||
os.find_abs_path_of_executable(node_basename) or {
|
mut prev_quit_handler := os.SignalHandler(eshcb)
|
||||||
panic('Could not find `node` in system path. Do you have Node.js installed?')
|
$if !windows { // There's no sigquit on windows
|
||||||
}
|
prev_quit_handler = os.signal_opt(.quit, eshcb) or { serror('set .quit', err) }
|
||||||
} else {
|
|
||||||
compiled_file
|
|
||||||
}
|
|
||||||
mut run_args := []string{cap: b.pref.run_args.len + 1}
|
|
||||||
if b.pref.backend.is_js() {
|
|
||||||
run_args << compiled_file
|
|
||||||
}
|
|
||||||
run_args << b.pref.run_args
|
|
||||||
mut run_process := os.new_process(run_file)
|
|
||||||
run_process.set_args(run_args)
|
|
||||||
if b.pref.is_verbose {
|
|
||||||
println('running $run_process.filename with arguments $run_process.args')
|
|
||||||
}
|
|
||||||
// Ignore sigint and sigquit while running the compiled file,
|
|
||||||
// so ^C doesn't prevent v from deleting the compiled file.
|
|
||||||
// See also https://git.musl-libc.org/cgit/musl/tree/src/process/system.c
|
|
||||||
prev_int_handler := os.signal_opt(.int, eshcb) or { serror('set .int', err) }
|
|
||||||
mut prev_quit_handler := os.SignalHandler(eshcb)
|
|
||||||
$if !windows { // There's no sigquit on windows
|
|
||||||
prev_quit_handler = os.signal_opt(.quit, eshcb) or { serror('set .quit', err) }
|
|
||||||
}
|
|
||||||
run_process.wait()
|
|
||||||
os.signal_opt(.int, prev_int_handler) or { serror('restore .int', err) }
|
|
||||||
$if !windows {
|
|
||||||
os.signal_opt(.quit, prev_quit_handler) or { serror('restore .quit', err) }
|
|
||||||
}
|
|
||||||
ret := run_process.code
|
|
||||||
run_process.close()
|
|
||||||
b.cleanup_run_executable_after_exit(compiled_file)
|
|
||||||
exit(ret)
|
|
||||||
}
|
}
|
||||||
exit(0)
|
run_process.wait()
|
||||||
|
os.signal_opt(.int, prev_int_handler) or { serror('restore .int', err) }
|
||||||
|
$if !windows {
|
||||||
|
os.signal_opt(.quit, prev_quit_handler) or { serror('restore .quit', err) }
|
||||||
|
}
|
||||||
|
ret := run_process.code
|
||||||
|
run_process.close()
|
||||||
|
b.cleanup_run_executable_after_exit(compiled_file)
|
||||||
|
exit(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eshcb(_ os.Signal) {
|
fn eshcb(_ os.Signal) {
|
||||||
|
@ -171,6 +130,9 @@ fn serror(reason string, e IError) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut v Builder) cleanup_run_executable_after_exit(exefile string) {
|
fn (mut v Builder) cleanup_run_executable_after_exit(exefile string) {
|
||||||
|
if v.pref.is_crun {
|
||||||
|
return
|
||||||
|
}
|
||||||
if v.pref.reuse_tmpc {
|
if v.pref.reuse_tmpc {
|
||||||
v.pref.vrun_elog('keeping executable: $exefile , because -keepc was passed')
|
v.pref.vrun_elog('keeping executable: $exefile , because -keepc was passed')
|
||||||
return
|
return
|
||||||
|
|
|
@ -2,6 +2,8 @@ module builder
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import hash
|
import hash
|
||||||
|
import time
|
||||||
|
import rand
|
||||||
import strings
|
import strings
|
||||||
import v.util
|
import v.util
|
||||||
import v.pref
|
import v.pref
|
||||||
|
@ -11,11 +13,27 @@ pub fn (mut b Builder) rebuild_modules() {
|
||||||
if !b.pref.use_cache || b.pref.build_mode == .build_module {
|
if !b.pref.use_cache || b.pref.build_mode == .build_module {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
all_files := b.parsed_files.map(it.path)
|
||||||
|
$if trace_invalidations ? {
|
||||||
|
eprintln('> rebuild_modules all_files: $all_files')
|
||||||
|
}
|
||||||
|
invalidations := b.find_invalidated_modules_by_files(all_files)
|
||||||
|
$if trace_invalidations ? {
|
||||||
|
eprintln('> rebuild_modules invalidations: $invalidations')
|
||||||
|
}
|
||||||
|
if invalidations.len > 0 {
|
||||||
|
vexe := pref.vexe_path()
|
||||||
|
for imp in invalidations {
|
||||||
|
b.v_build_module(vexe, imp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut b Builder) find_invalidated_modules_by_files(all_files []string) []string {
|
||||||
util.timing_start('${@METHOD} source_hashing')
|
util.timing_start('${@METHOD} source_hashing')
|
||||||
mut new_hashes := map[string]string{}
|
mut new_hashes := map[string]string{}
|
||||||
mut old_hashes := map[string]string{}
|
mut old_hashes := map[string]string{}
|
||||||
mut sb_new_hashes := strings.new_builder(1024)
|
mut sb_new_hashes := strings.new_builder(1024)
|
||||||
all_files := b.parsed_files.map(it.path)
|
|
||||||
//
|
//
|
||||||
mut cm := vcache.new_cache_manager(all_files)
|
mut cm := vcache.new_cache_manager(all_files)
|
||||||
sold_hashes := cm.load('.hashes', 'all_files') or { ' ' }
|
sold_hashes := cm.load('.hashes', 'all_files') or { ' ' }
|
||||||
|
@ -31,8 +49,7 @@ pub fn (mut b Builder) rebuild_modules() {
|
||||||
old_hashes[cpath] = chash
|
old_hashes[cpath] = chash
|
||||||
}
|
}
|
||||||
// eprintln('old_hashes: $old_hashes')
|
// eprintln('old_hashes: $old_hashes')
|
||||||
for p in b.parsed_files {
|
for cpath in all_files {
|
||||||
cpath := p.path
|
|
||||||
ccontent := util.read_file(cpath) or { '' }
|
ccontent := util.read_file(cpath) or { '' }
|
||||||
chash := hash.sum64_string(ccontent, 7).hex_full()
|
chash := hash.sum64_string(ccontent, 7).hex_full()
|
||||||
new_hashes[cpath] = chash
|
new_hashes[cpath] = chash
|
||||||
|
@ -48,6 +65,7 @@ pub fn (mut b Builder) rebuild_modules() {
|
||||||
cm.save('.hashes', 'all_files', snew_hashes) or {}
|
cm.save('.hashes', 'all_files', snew_hashes) or {}
|
||||||
util.timing_measure('${@METHOD} source_hashing')
|
util.timing_measure('${@METHOD} source_hashing')
|
||||||
|
|
||||||
|
mut invalidations := []string{}
|
||||||
if new_hashes != old_hashes {
|
if new_hashes != old_hashes {
|
||||||
util.timing_start('${@METHOD} rebuilding')
|
util.timing_start('${@METHOD} rebuilding')
|
||||||
// eprintln('> b.mod_invalidates_paths: $b.mod_invalidates_paths')
|
// eprintln('> b.mod_invalidates_paths: $b.mod_invalidates_paths')
|
||||||
|
@ -148,13 +166,13 @@ pub fn (mut b Builder) rebuild_modules() {
|
||||||
}
|
}
|
||||||
if invalidated_mod_paths.len > 0 {
|
if invalidated_mod_paths.len > 0 {
|
||||||
impaths := invalidated_mod_paths.keys()
|
impaths := invalidated_mod_paths.keys()
|
||||||
vexe := pref.vexe_path()
|
|
||||||
for imp in impaths {
|
for imp in impaths {
|
||||||
b.v_build_module(vexe, imp)
|
invalidations << imp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
util.timing_measure('${@METHOD} rebuilding')
|
util.timing_measure('${@METHOD} rebuilding')
|
||||||
}
|
}
|
||||||
|
return invalidations
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut b Builder) v_build_module(vexe string, imp_path string) {
|
fn (mut b Builder) v_build_module(vexe string, imp_path string) {
|
||||||
|
@ -237,3 +255,114 @@ fn (mut b Builder) handle_usecache(vexe string) {
|
||||||
}
|
}
|
||||||
b.ccoptions.post_args << libs
|
b.ccoptions.post_args << libs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn (mut b Builder) should_rebuild() bool {
|
||||||
|
mut exe_name := b.pref.out_name
|
||||||
|
$if windows {
|
||||||
|
exe_name = exe_name + '.exe'
|
||||||
|
}
|
||||||
|
if !os.is_file(exe_name) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !b.pref.is_crun {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
mut v_program_files := []string{}
|
||||||
|
is_file := os.is_file(b.pref.path)
|
||||||
|
is_dir := os.is_dir(b.pref.path)
|
||||||
|
if is_file {
|
||||||
|
v_program_files << b.pref.path
|
||||||
|
} else if is_dir {
|
||||||
|
v_program_files << b.v_files_from_dir(b.pref.path)
|
||||||
|
}
|
||||||
|
v_program_files.sort() // ensure stable keys for the dependencies cache
|
||||||
|
b.crun_cache_keys = v_program_files
|
||||||
|
b.crun_cache_keys << exe_name
|
||||||
|
// just check the timestamps for now:
|
||||||
|
exe_stamp := os.file_last_mod_unix(exe_name)
|
||||||
|
source_stamp := most_recent_timestamp(v_program_files)
|
||||||
|
if exe_stamp <= source_stamp {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// The timestamps for the top level files were found ok,
|
||||||
|
// however we want to *also* make sure that a full rebuild will be done
|
||||||
|
// if any of the dependencies (if we know them) are changed.
|
||||||
|
mut cm := vcache.new_cache_manager(b.crun_cache_keys)
|
||||||
|
// always rebuild, when the compilation options changed between 2 sequential cruns:
|
||||||
|
sbuild_options := cm.load('.build_options', '.crun') or { return true }
|
||||||
|
if sbuild_options != b.pref.build_options.join('\n') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
sdependencies := cm.load('.dependencies', '.crun') or {
|
||||||
|
// empty/wiped out cache, we do not know what the dependencies are, so just
|
||||||
|
// rebuild, which will fill in the dependencies cache for the next crun
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
dependencies := sdependencies.split('\n')
|
||||||
|
// we have already compiled these source files, and have their dependencies
|
||||||
|
dependencies_stamp := most_recent_timestamp(dependencies)
|
||||||
|
if dependencies_stamp < exe_stamp {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn most_recent_timestamp(files []string) i64 {
|
||||||
|
mut res := i64(0)
|
||||||
|
for f in files {
|
||||||
|
f_stamp := os.file_last_mod_unix(f)
|
||||||
|
if res <= f_stamp {
|
||||||
|
res = f_stamp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut b Builder) rebuild(backend_cb FnBackend) {
|
||||||
|
mut sw := time.new_stopwatch()
|
||||||
|
backend_cb(mut b)
|
||||||
|
if b.pref.is_crun {
|
||||||
|
// save the dependencies after the first compilation, they will be used for subsequent ones:
|
||||||
|
mut cm := vcache.new_cache_manager(b.crun_cache_keys)
|
||||||
|
dependency_files := b.parsed_files.map(it.path)
|
||||||
|
cm.save('.dependencies', '.crun', dependency_files.join('\n')) or {}
|
||||||
|
cm.save('.build_options', '.crun', b.pref.build_options.join('\n')) or {}
|
||||||
|
}
|
||||||
|
mut timers := util.get_timers()
|
||||||
|
timers.show_remaining()
|
||||||
|
if b.pref.is_stats {
|
||||||
|
compilation_time_micros := 1 + sw.elapsed().microseconds()
|
||||||
|
scompilation_time_ms := util.bold('${f64(compilation_time_micros) / 1000.0:6.3f}')
|
||||||
|
mut all_v_source_lines, mut all_v_source_bytes := 0, 0
|
||||||
|
for pf in b.parsed_files {
|
||||||
|
all_v_source_lines += pf.nr_lines
|
||||||
|
all_v_source_bytes += pf.nr_bytes
|
||||||
|
}
|
||||||
|
mut sall_v_source_lines := all_v_source_lines.str()
|
||||||
|
mut sall_v_source_bytes := all_v_source_bytes.str()
|
||||||
|
sall_v_source_lines = util.bold('${sall_v_source_lines:10s}')
|
||||||
|
sall_v_source_bytes = util.bold('${sall_v_source_bytes:10s}')
|
||||||
|
println(' V source code size: $sall_v_source_lines lines, $sall_v_source_bytes bytes')
|
||||||
|
//
|
||||||
|
mut slines := b.stats_lines.str()
|
||||||
|
mut sbytes := b.stats_bytes.str()
|
||||||
|
slines = util.bold('${slines:10s}')
|
||||||
|
sbytes = util.bold('${sbytes:10s}')
|
||||||
|
println('generated target code size: $slines lines, $sbytes bytes')
|
||||||
|
//
|
||||||
|
vlines_per_second := int(1_000_000.0 * f64(all_v_source_lines) / f64(compilation_time_micros))
|
||||||
|
svlines_per_second := util.bold(vlines_per_second.str())
|
||||||
|
println('compilation took: $scompilation_time_ms ms, compilation speed: $svlines_per_second vlines/s')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut b Builder) get_vtmp_filename(base_file_name string, postfix string) string {
|
||||||
|
vtmp := util.get_vtmp_folder()
|
||||||
|
mut uniq := ''
|
||||||
|
if !b.pref.reuse_tmpc {
|
||||||
|
uniq = '.$rand.u64()'
|
||||||
|
}
|
||||||
|
fname := os.file_name(os.real_path(base_file_name)) + '$uniq$postfix'
|
||||||
|
return os.real_path(os.join_path(vtmp, fname))
|
||||||
|
}
|
||||||
|
|
|
@ -111,7 +111,8 @@ pub mut:
|
||||||
is_prof bool // benchmark every function
|
is_prof bool // benchmark every function
|
||||||
is_prod bool // use "-O2"
|
is_prod bool // use "-O2"
|
||||||
is_repl bool
|
is_repl bool
|
||||||
is_run bool
|
is_run bool // compile and run a v program, passing arguments to it, and deleting the executable afterwards
|
||||||
|
is_crun bool // similar to run, but does not recompile the executable, if there were no changes to the sources
|
||||||
is_debug bool // turned on by -g or -cg, it tells v to pass -g to the C backend compiler.
|
is_debug bool // turned on by -g or -cg, it tells v to pass -g to the C backend compiler.
|
||||||
is_vlines bool // turned on by -g (it slows down .tmp.c generation slightly).
|
is_vlines bool // turned on by -g (it slows down .tmp.c generation slightly).
|
||||||
is_stats bool // `v -stats file_test.v` will produce more detailed statistics for the tests that were run
|
is_stats bool // `v -stats file_test.v` will produce more detailed statistics for the tests that were run
|
||||||
|
@ -706,7 +707,7 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin
|
||||||
if command == '' {
|
if command == '' {
|
||||||
command = arg
|
command = arg
|
||||||
command_pos = i
|
command_pos = i
|
||||||
if command == 'run' {
|
if command in ['run', 'crun'] {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} else if is_source_file(command) && is_source_file(arg)
|
} else if is_source_file(command) && is_source_file(arg)
|
||||||
|
@ -734,6 +735,12 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin
|
||||||
if res.is_debug {
|
if res.is_debug {
|
||||||
res.parse_define('debug')
|
res.parse_define('debug')
|
||||||
}
|
}
|
||||||
|
if command == 'crun' {
|
||||||
|
res.is_crun = true
|
||||||
|
}
|
||||||
|
if command == 'run' {
|
||||||
|
res.is_run = true
|
||||||
|
}
|
||||||
if command == 'run' && res.is_prod && os.is_atty(1) > 0 {
|
if command == 'run' && res.is_prod && os.is_atty(1) > 0 {
|
||||||
eprintln_cond(show_output, "Note: building an optimized binary takes much longer. It shouldn't be used with `v run`.")
|
eprintln_cond(show_output, "Note: building an optimized binary takes much longer. It shouldn't be used with `v run`.")
|
||||||
eprintln_cond(show_output, 'Use `v run` without optimization, or build an optimized binary with -prod first, then run it separately.')
|
eprintln_cond(show_output, 'Use `v run` without optimization, or build an optimized binary with -prod first, then run it separately.')
|
||||||
|
@ -744,8 +751,7 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin
|
||||||
eprintln('Cannot save output binary in a .v file.')
|
eprintln('Cannot save output binary in a .v file.')
|
||||||
exit(1)
|
exit(1)
|
||||||
}
|
}
|
||||||
if command == 'run' {
|
if res.is_run || res.is_crun {
|
||||||
res.is_run = true
|
|
||||||
if command_pos + 2 > args.len {
|
if command_pos + 2 > args.len {
|
||||||
eprintln('v run: no v files listed')
|
eprintln('v run: no v files listed')
|
||||||
exit(1)
|
exit(1)
|
||||||
|
@ -798,7 +804,7 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin
|
||||||
// `v build.vsh gcc` is the same as `v run build.vsh gcc`,
|
// `v build.vsh gcc` is the same as `v run build.vsh gcc`,
|
||||||
// i.e. compiling, then running the script, passing the args
|
// i.e. compiling, then running the script, passing the args
|
||||||
// after it to the script:
|
// after it to the script:
|
||||||
res.is_run = true
|
res.is_crun = true
|
||||||
res.path = command
|
res.path = command
|
||||||
res.run_args = args[command_pos + 1..]
|
res.run_args = args[command_pos + 1..]
|
||||||
} else if command == 'interpret' {
|
} else if command == 'interpret' {
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
|
const crun_folder = os.join_path(os.temp_dir(), 'crun_folder')
|
||||||
|
|
||||||
|
const vprogram_file = os.join_path(crun_folder, 'vprogram.vv')
|
||||||
|
|
||||||
|
const vexe = os.getenv('VEXE')
|
||||||
|
|
||||||
|
fn testsuite_begin() {
|
||||||
|
os.setenv('VCACHE', crun_folder, true)
|
||||||
|
os.rmdir_all(crun_folder) or {}
|
||||||
|
os.mkdir_all(crun_folder) or {}
|
||||||
|
assert os.is_dir(crun_folder)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn testsuite_end() {
|
||||||
|
os.chdir(os.wd_at_startup) or {}
|
||||||
|
os.rmdir_all(crun_folder) or {}
|
||||||
|
assert !os.is_dir(crun_folder)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_saving_simple_v_program() ? {
|
||||||
|
os.write_file(vprogram_file, 'print("hello")')?
|
||||||
|
assert true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_crun_simple_v_program_several_times() ? {
|
||||||
|
mut sw := time.new_stopwatch()
|
||||||
|
mut times := []i64{}
|
||||||
|
for i in 0 .. 10 {
|
||||||
|
vcrun()
|
||||||
|
times << sw.elapsed().microseconds()
|
||||||
|
time.sleep(50 * time.millisecond)
|
||||||
|
sw.restart()
|
||||||
|
}
|
||||||
|
dump(times)
|
||||||
|
assert times.first() > times.last() * 5 // cruns compile just once, if the source file is not changed
|
||||||
|
$if !windows {
|
||||||
|
os.system('ls -la $crun_folder')
|
||||||
|
os.system('find $crun_folder')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vcrun() {
|
||||||
|
cmd := '${os.quoted_path(vexe)} crun ${os.quoted_path(vprogram_file)}'
|
||||||
|
eprintln('now: $time.now().format_ss_milli() | cmd: $cmd')
|
||||||
|
res := os.execute(cmd)
|
||||||
|
assert res.exit_code == 0
|
||||||
|
assert res.output == 'hello'
|
||||||
|
}
|
Loading…
Reference in New Issue