mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
move v.v to cmd/v
This commit is contained in:
9
cmd/tools/.gitignore
vendored
Normal file
9
cmd/tools/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
gen_vc
|
||||
performance_compare
|
||||
vcreate
|
||||
vnames
|
||||
vpm
|
||||
vrepl
|
||||
vtest
|
||||
vtest-compiler
|
||||
vup
|
||||
112
cmd/tools/bench/hashmap.v
Normal file
112
cmd/tools/bench/hashmap.v
Normal file
@@ -0,0 +1,112 @@
|
||||
import rand
|
||||
import time
|
||||
import builtin.hashmap
|
||||
|
||||
fn hashmap_set_bench(arr []string, repeat int) {
|
||||
start_time := time.ticks()
|
||||
for _ in 0..repeat {
|
||||
mut b := hashmap.new_hashmap()
|
||||
for x in arr {
|
||||
b.set(x, 1)
|
||||
}
|
||||
}
|
||||
end_time := time.ticks() - start_time
|
||||
println("* hashmap_set: ${end_time} ms")
|
||||
}
|
||||
|
||||
fn map_set_bench(arr []string, repeat int) {
|
||||
start_time := time.ticks()
|
||||
for _ in 0..repeat {
|
||||
mut b := map[string]int
|
||||
for x in arr {
|
||||
b[x] = 1
|
||||
}
|
||||
}
|
||||
end_time := time.ticks() - start_time
|
||||
println("* map_set: ${end_time} ms")
|
||||
}
|
||||
|
||||
fn hashmap_get_bench(arr []string, repeat int) {
|
||||
mut b := hashmap.new_hashmap()
|
||||
for x in arr {
|
||||
b.set(x, 1)
|
||||
}
|
||||
start_time := time.ticks()
|
||||
for _ in 0..repeat {
|
||||
for x in arr {
|
||||
b.get(x)
|
||||
}
|
||||
}
|
||||
end_time := time.ticks() - start_time
|
||||
println("* hashmap_get: ${end_time} ms")
|
||||
}
|
||||
|
||||
fn map_get_bench(arr []string, repeat int) {
|
||||
mut b := map[string]int
|
||||
for x in arr {
|
||||
b[x] = 1
|
||||
}
|
||||
start_time := time.ticks()
|
||||
for _ in 0..repeat {
|
||||
for x in arr {
|
||||
b[x]
|
||||
}
|
||||
}
|
||||
end_time := time.ticks() - start_time
|
||||
println("* map_get: ${end_time} ms")
|
||||
}
|
||||
|
||||
fn benchmark_many_keys() {
|
||||
key_len := 30
|
||||
repeat := 1
|
||||
for i := 2048; i <= 10000000; i = i * 2 {
|
||||
mut arr := []string
|
||||
for _ in 0..i {
|
||||
mut buf := []byte
|
||||
for j in 0..key_len {
|
||||
buf << byte(rand.next(int(`z`) - int(`a`)) + `a`)
|
||||
}
|
||||
s := string(buf)
|
||||
arr << s
|
||||
}
|
||||
println("$arr.len keys of length $key_len")
|
||||
// Uncomment the benchmark you would like to benchmark
|
||||
// Run one or two at a time while memory leaks is a thing
|
||||
hashmap_get_bench(arr, repeat)
|
||||
map_get_bench(arr, repeat)
|
||||
// hashmap_set_bench(arr, repeat)
|
||||
// map_set_bench(arr, repeat)
|
||||
println('')
|
||||
}
|
||||
}
|
||||
|
||||
fn benchmark_few_keys() {
|
||||
key_len := 30
|
||||
repeat := 10000
|
||||
println("Benchmarks are repeated $repeat times")
|
||||
for i := 16; i <= 2048; i = i * 2 {
|
||||
mut arr := []string
|
||||
for _ in 0..i {
|
||||
mut buf := []byte
|
||||
for j in 0..key_len {
|
||||
buf << byte(rand.next(int(`z`) - int(`a`)) + `a`)
|
||||
}
|
||||
s := string(buf)
|
||||
arr << s
|
||||
}
|
||||
println("$arr.len keys of length $key_len")
|
||||
// Uncomment the benchmark you would like to benchmark
|
||||
// Run one or two at a time while memory leaks is a thing
|
||||
hashmap_get_bench(arr, repeat)
|
||||
map_get_bench(arr, repeat)
|
||||
// hashmap_set_bench(arr, repeat)
|
||||
// map_set_bench(arr, repeat)
|
||||
println('')
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Uncomment below to benchmark on many keys
|
||||
// benchmark_many_keys()
|
||||
benchmark_few_keys()
|
||||
}
|
||||
55
cmd/tools/bench/wyhash.v
Normal file
55
cmd/tools/bench/wyhash.v
Normal file
@@ -0,0 +1,55 @@
|
||||
module main
|
||||
|
||||
import (
|
||||
hash.fnv1a
|
||||
hash.wyhash
|
||||
rand
|
||||
time
|
||||
)
|
||||
|
||||
fn main() {
|
||||
sample_size := 10000000
|
||||
min_str_len := 20
|
||||
max_str_len := 40
|
||||
println('Generating $sample_size strings between $min_str_len - $max_str_len chars long...')
|
||||
mut bytepile := []byte
|
||||
for _ in 0 .. sample_size * max_str_len {
|
||||
bytepile << byte(40 + rand.next(125 - 40))
|
||||
}
|
||||
mut str_lens := []int
|
||||
for _ in 0 .. sample_size {
|
||||
str_lens << min_str_len + rand.next(max_str_len - min_str_len)
|
||||
}
|
||||
println('Hashing each of the generated strings...')
|
||||
t0 := time.ticks()
|
||||
mut start_pos := 0
|
||||
for len in str_lens {
|
||||
end_pos := start_pos + len
|
||||
str := string(bytepile[start_pos..end_pos],len)
|
||||
_ = wyhash.wyhash_c(&str.str, u64(str.len), 1)
|
||||
start_pos = end_pos
|
||||
}
|
||||
t1 := time.ticks()
|
||||
d1 := t1 - t0
|
||||
println(' * wyhash4 C: ${d1}ms')
|
||||
start_pos = 0
|
||||
for len in str_lens {
|
||||
end_pos := start_pos + len
|
||||
str := string(bytepile[start_pos..end_pos],len)
|
||||
_ = wyhash.sum64_string(str, 1)
|
||||
start_pos = end_pos
|
||||
}
|
||||
t2 := time.ticks()
|
||||
d2 := t2 - t1
|
||||
println(' * wyhash4: ${d2}ms')
|
||||
start_pos = 0
|
||||
for len in str_lens {
|
||||
end_pos := start_pos + len
|
||||
str := string(bytepile[start_pos..end_pos],len)
|
||||
_ = fnv1a.sum64_string(str)
|
||||
start_pos = end_pos
|
||||
}
|
||||
t3 := time.ticks()
|
||||
d3 := t3 - t2
|
||||
println(' * fnv1a64: ${d3}ms')
|
||||
}
|
||||
3
cmd/tools/fast/.gitignore
vendored
Normal file
3
cmd/tools/fast/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
fast
|
||||
v2
|
||||
index.html
|
||||
89
cmd/tools/fast/fast.v
Normal file
89
cmd/tools/fast/fast.v
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
import (
|
||||
os
|
||||
time
|
||||
filepath
|
||||
)
|
||||
|
||||
fn main() {
|
||||
exe := os.executable()
|
||||
dir := filepath.dir(exe)
|
||||
vdir := filepath.dir(filepath.dir(filepath.dir(dir)))
|
||||
if !os.exists('$vdir/v') && !os.is_dir('$vdir/vlib') {
|
||||
println('fast.html generator needs to be located in `v/cmd/tools/fast`')
|
||||
}
|
||||
println('fast.html generator\n')
|
||||
// Fetch the last commit's hash
|
||||
println('Fetching updates...')
|
||||
ret := os.system('git pull --rebase')
|
||||
if ret != 0 {
|
||||
println('failed to git pull')
|
||||
return
|
||||
}
|
||||
mut commit_hash := exec('git rev-parse HEAD')
|
||||
commit_hash = commit_hash[..7]
|
||||
if !os.exists('table.html') {
|
||||
os.create('table.html') or { panic(err) }
|
||||
}
|
||||
mut table := os.read_file('table.html') or { panic(err) }
|
||||
// Do nothing if it's already been processed.
|
||||
if table.contains(commit_hash) {
|
||||
println('Commit $commit_hash has already been processed')
|
||||
return
|
||||
}
|
||||
// Build an optimized V
|
||||
println('Building vprod...')
|
||||
exec('v -o $vdir/vprod -prod $vdir/cmd/v')
|
||||
println('Measuring...')
|
||||
diff1 := measure('$vdir/vprod -cc clang -o v.c $vdir/cmd/v')
|
||||
diff2 := measure('$vdir/vprod -cc clang -o v2 $vdir/cmd/v')
|
||||
diff3 := measure('$vdir/vprod -x64 $vdir/cmd/tools/1mil.v')
|
||||
diff4 := measure('$vdir/vprod -cc clang $vdir/examples/hello_world.v')
|
||||
//println('Building V took ${diff}ms')
|
||||
commit_date := exec('git log -n1 --pretty="format:%at"')
|
||||
message := exec('git log -n1 --pretty="format:%s"')
|
||||
date := time.unix(commit_date.int())
|
||||
mut out := os.create('table.html') or { panic(err) }
|
||||
// Place the new row on top
|
||||
table =
|
||||
'<tr>
|
||||
<td>${date.format()}</td>
|
||||
<td><a target=_blank href="https://github.com/vlang/v/commit/$commit_hash">$commit_hash</a></td>
|
||||
<td>$message</td>
|
||||
<td>${diff1}ms</td>
|
||||
<td>${diff2}ms</td>
|
||||
<td>${diff3}ms</td>
|
||||
<td>${diff4}ms</td>
|
||||
</tr>\n' +
|
||||
table.trim_space()
|
||||
out.writeln(table)
|
||||
out.close()
|
||||
// Regenerate index.html
|
||||
header := os.read_file('header.html') or { panic(err) }
|
||||
footer := os.read_file('footer.html') or { panic(err) }
|
||||
mut res := os.create('index.html') or { panic(err) }
|
||||
res.writeln(header)
|
||||
res.writeln(table)
|
||||
res.writeln(footer)
|
||||
res.close()
|
||||
}
|
||||
|
||||
fn exec(s string) string {
|
||||
e := os.exec(s) or { panic(err) }
|
||||
return e.output
|
||||
}
|
||||
|
||||
// returns milliseconds
|
||||
fn measure(cmd string) int {
|
||||
println('Warming up...')
|
||||
for i in 0..3 {
|
||||
exec(cmd)
|
||||
}
|
||||
println('Building...')
|
||||
ticks := time.ticks()
|
||||
exec(cmd)
|
||||
return int(time.ticks() - ticks)
|
||||
}
|
||||
65
cmd/tools/fast/fast_main.js
Normal file
65
cmd/tools/fast/fast_main.js
Normal file
@@ -0,0 +1,65 @@
|
||||
(function () {
|
||||
var table = document.querySelector("table");
|
||||
var isTbody = table.children[0].nodeName == "TBODY";
|
||||
var trs = isTbody
|
||||
? table.children[0].querySelectorAll("tr")
|
||||
: table.querySelectorAll("tr");
|
||||
trs.forEach(function (tr, idx) {
|
||||
if (idx != 0 && idx + 1 < trs.length) {
|
||||
var vc = 3, vv = 4, vf = 5, vh = 6;
|
||||
var textContent = {
|
||||
vc: tr.children[vc].textContent,
|
||||
vv: tr.children[vv].textContent,
|
||||
vf: tr.children[vf].textContent,
|
||||
vh: tr.children[vh].textContent
|
||||
};
|
||||
var currentData = {
|
||||
vc: int(textContent.vc.slice(0, -2)),
|
||||
vv: int(textContent.vv.slice(0, -2)),
|
||||
vf: int(textContent.vf.slice(0, -2)),
|
||||
vh: int(textContent.vh.slice(0, -2))
|
||||
};
|
||||
var prevData = {
|
||||
vc: int(trs[idx + 1].children[vc].textContent.slice(0, -2)),
|
||||
vv: int(trs[idx + 1].children[vv].textContent.slice(0, -2)),
|
||||
vf: int(trs[idx + 1].children[vf].textContent.slice(0, -2)),
|
||||
vh: int(trs[idx + 1].children[vh].textContent.slice(0, -2))
|
||||
};
|
||||
var result = {
|
||||
vc: currentData.vc - prevData.vc,
|
||||
vv: currentData.vv - prevData.vv,
|
||||
vf: currentData.vf - prevData.vf,
|
||||
vh: currentData.vh - prevData.vh
|
||||
};
|
||||
if (Math.abs(result.vc) > 50)
|
||||
tr.children[vc].appendChild(createElement(result.vc));
|
||||
if (Math.abs(result.vv) > 50)
|
||||
tr.children[vv].appendChild(createElement(result.vv));
|
||||
if (Math.abs(result.vf) > 50)
|
||||
tr.children[vf].appendChild(createElement(result.vf));
|
||||
if (Math.abs(result.vh) > 50)
|
||||
tr.children[vh].appendChild(createElement(result.vh));
|
||||
}
|
||||
});
|
||||
function int(src) {
|
||||
return src - 0;
|
||||
}
|
||||
function getClassName(x) {
|
||||
if (x == 0)
|
||||
return "equal";
|
||||
return x < 0 ? "plus" : "minus";
|
||||
}
|
||||
function createElement(result) {
|
||||
var el = document.createElement("span");
|
||||
var parsedResult = parseResult(result);
|
||||
el.classList.add("diff");
|
||||
el.classList.add(getClassName(result));
|
||||
el.textContent = parsedResult;
|
||||
return el;
|
||||
}
|
||||
function parseResult(x) {
|
||||
if (x == 0)
|
||||
return "0";
|
||||
return x > 0 ? "+" + x : x;
|
||||
}
|
||||
})();
|
||||
4
cmd/tools/fast/footer.html
Normal file
4
cmd/tools/fast/footer.html
Normal file
@@ -0,0 +1,4 @@
|
||||
</table>
|
||||
<script src="main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
52
cmd/tools/fast/header.html
Normal file
52
cmd/tools/fast/header.html
Normal file
@@ -0,0 +1,52 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Is V still fast?</title>
|
||||
<style>
|
||||
*, body {
|
||||
font-family: Menlo, Monospace, 'Courier New';
|
||||
}
|
||||
table, td {
|
||||
border-collapse: collapse;
|
||||
border: 1px solid #dfdfdf;
|
||||
}
|
||||
td {
|
||||
padding: 5px;
|
||||
position: relative;
|
||||
}
|
||||
.diff {
|
||||
border-radius: 2.5px;
|
||||
color: #ffffff;
|
||||
padding: 0 5px 0 5px;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
}
|
||||
.minus {
|
||||
background-color: rgb(195, 74, 104);
|
||||
}
|
||||
.plus {
|
||||
background-color: #8BC34A;
|
||||
}
|
||||
.equal {
|
||||
background-color: rgb(113, 68, 172);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Is V still fast?</h2>
|
||||
|
||||
Monitoring compilation speed for each commit. <br><br>
|
||||
Source code: <a target=blank href='https://github.com/vlang/v/blob/master/cmd/tools/fast/fast.v'>fast.v</a> <br><br>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td style='width:400px'></td>
|
||||
<td style='width:120px'>v -o v.c</td>
|
||||
<td style='width:120px'>v -o v</td>
|
||||
<td style='width:130px'>v -x64 1mil.v</td>
|
||||
<td style='width:120px'>v hello.v</td>
|
||||
</tr>
|
||||
19
cmd/tools/gen1m.v
Normal file
19
cmd/tools/gen1m.v
Normal file
@@ -0,0 +1,19 @@
|
||||
fn main() {
|
||||
println('fn println(a int) {}')
|
||||
println('fn print(a string) {}')
|
||||
for i in 0..100000 {
|
||||
println('
|
||||
fn foo${i}() {
|
||||
x := $i
|
||||
mut a := x
|
||||
a += 2
|
||||
println(a)
|
||||
a = 0
|
||||
a = 1
|
||||
}
|
||||
')
|
||||
}
|
||||
//println('fn main() {foo1()} ')
|
||||
println('fn main() { print("1m DONE") } ')
|
||||
|
||||
}
|
||||
389
cmd/tools/gen_vc.v
Normal file
389
cmd/tools/gen_vc.v
Normal file
@@ -0,0 +1,389 @@
|
||||
// This tool regenerates V's bootstrap .c files
|
||||
// every time the V master branch is updated.
|
||||
|
||||
// if run with the --serve flag it will run in webhook
|
||||
// server mode awaiting a request to http://host:port/genhook
|
||||
|
||||
// available command line flags:
|
||||
// --work-dir gen_vc's working directory
|
||||
// --purge force purge the local repositories
|
||||
// --serve run in webhook server mode
|
||||
// --port port for http server to listen on
|
||||
// --log-to either 'file' or 'terminal'
|
||||
// --log-file path to log file used when --log-to is 'file'
|
||||
// --dry-run dont push anything to remote repo
|
||||
|
||||
module main
|
||||
|
||||
import (
|
||||
os
|
||||
log
|
||||
flag
|
||||
time
|
||||
vweb
|
||||
net.urllib
|
||||
)
|
||||
|
||||
// git credentials
|
||||
const(
|
||||
git_username = os.getenv('GITUSER')
|
||||
git_password = os.getenv('GITPASS')
|
||||
)
|
||||
|
||||
// repository
|
||||
const(
|
||||
// git repo
|
||||
git_repo_v = 'github.com/vlang/v'
|
||||
git_repo_vc = 'github.com/vlang/vc'
|
||||
// local repo directories
|
||||
git_repo_dir_v = 'v'
|
||||
git_repo_dir_vc = 'vc'
|
||||
)
|
||||
|
||||
// gen_vc
|
||||
const(
|
||||
// name
|
||||
app_name = 'gen_vc'
|
||||
// version
|
||||
app_version = '0.1.1'
|
||||
// description
|
||||
app_description = 'This tool regenerates V\'s bootstrap .c files every time the V master branch is updated.'
|
||||
// assume something went wrong if file size less than this
|
||||
too_short_file_limit = 5000
|
||||
// create a .c file for these os's
|
||||
vc_build_oses = [
|
||||
'nix', // all nix based os
|
||||
'windows'
|
||||
]
|
||||
)
|
||||
|
||||
// default options (overridden by flags)
|
||||
const(
|
||||
// gen_vc working directory
|
||||
work_dir = '/tmp/gen_vc'
|
||||
// dont push anything to remote repo
|
||||
dry_run = false
|
||||
// server port
|
||||
server_port = 7171
|
||||
// log file
|
||||
log_file = '$work_dir/log.txt'
|
||||
// log_to is either 'file' or 'terminal'
|
||||
log_to = 'terminal'
|
||||
)
|
||||
|
||||
// errors
|
||||
const(
|
||||
err_msg_build = 'error building'
|
||||
err_msg_make = 'make failed'
|
||||
err_msg_gen_c = 'failed to generate .c file'
|
||||
err_msg_cmd_x = 'error running cmd'
|
||||
)
|
||||
|
||||
struct GenVC {
|
||||
// logger
|
||||
logger &log.Log
|
||||
// flag options
|
||||
options FlagOptions
|
||||
mut:
|
||||
// true if error was experienced running generate
|
||||
gen_error bool
|
||||
}
|
||||
|
||||
// webhook server
|
||||
pub struct WebhookServer {
|
||||
pub mut:
|
||||
vweb vweb.Context
|
||||
gen_vc &GenVC
|
||||
}
|
||||
|
||||
// storage for flag options
|
||||
struct FlagOptions {
|
||||
work_dir string
|
||||
purge bool
|
||||
serve bool
|
||||
port int
|
||||
log_to string
|
||||
log_file string
|
||||
dry_run bool
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut fp := flag.new_flag_parser(os.args.clone())
|
||||
|
||||
fp.application(app_name)
|
||||
fp.version(app_version)
|
||||
fp.description(app_description)
|
||||
fp.skip_executable()
|
||||
|
||||
show_help:=fp.bool('help', false, 'Show this help screen\n')
|
||||
flag_options := parse_flags(mut fp)
|
||||
|
||||
if( show_help ){ println( fp.usage() ) exit(0) }
|
||||
|
||||
fp.finalize() or {
|
||||
eprintln(err)
|
||||
println(fp.usage())
|
||||
return
|
||||
}
|
||||
|
||||
// webhook server mode
|
||||
if flag_options.serve {
|
||||
vweb.run<WebhookServer>(flag_options.port)
|
||||
}
|
||||
// cmd mode
|
||||
else {
|
||||
mut gen_vc := new_gen_vc(flag_options)
|
||||
gen_vc.init()
|
||||
gen_vc.generate()
|
||||
}
|
||||
}
|
||||
|
||||
// new GenVC
|
||||
fn new_gen_vc(flag_options FlagOptions) &GenVC {
|
||||
mut logger := &log.Log{}
|
||||
logger.set_level(log.DEBUG)
|
||||
if flag_options.log_to == 'file' {
|
||||
logger.set_full_logpath( flag_options.log_file )
|
||||
}
|
||||
return &GenVC{
|
||||
options: flag_options
|
||||
logger: logger
|
||||
}
|
||||
}
|
||||
|
||||
// WebhookServer init
|
||||
pub fn (ws mut WebhookServer) init() {
|
||||
|
||||
mut fp := flag.new_flag_parser(os.args.clone())
|
||||
flag_options := parse_flags(mut fp)
|
||||
ws.gen_vc = new_gen_vc(flag_options)
|
||||
ws.gen_vc.init()
|
||||
//ws.gen_vc = new_gen_vc(flag_options)
|
||||
}
|
||||
|
||||
// gen webhook
|
||||
pub fn (ws mut WebhookServer) genhook() {
|
||||
ws.gen_vc.generate()
|
||||
// error in generate
|
||||
if ws.gen_vc.gen_error {
|
||||
ws.vweb.json('{status: "failed"}')
|
||||
return
|
||||
}
|
||||
ws.vweb.json('{status: "ok"}')
|
||||
}
|
||||
|
||||
pub fn (ws &WebhookServer) reset() {
|
||||
}
|
||||
|
||||
|
||||
// parse flags to FlagOptions struct
|
||||
fn parse_flags(fp mut flag.FlagParser) FlagOptions {
|
||||
return FlagOptions{
|
||||
serve : fp.bool('serve', false, 'run in webhook server mode')
|
||||
work_dir : fp.string('work-dir', work_dir, 'gen_vc working directory')
|
||||
purge : fp.bool('purge', false, 'force purge the local repositories')
|
||||
port : fp.int('port', server_port, 'port for web server to listen on')
|
||||
log_to : fp.string('log-to', log_to, 'log to is \'file\' or \'terminal\'')
|
||||
log_file : fp.string('log-file', log_file, 'log file to use when log-to is \'file\'')
|
||||
dry_run : fp.bool('dry-run', dry_run, 'when specified dont push anything to remote repo')
|
||||
}
|
||||
}
|
||||
|
||||
// init
|
||||
fn (gen_vc mut GenVC) init() {
|
||||
// purge repos if flag is passed
|
||||
if gen_vc.options.purge {
|
||||
gen_vc.purge_repos()
|
||||
}
|
||||
}
|
||||
|
||||
// regenerate
|
||||
fn (gen_vc mut GenVC) generate() {
|
||||
// set errors to false
|
||||
gen_vc.gen_error = false
|
||||
|
||||
// check if gen_vc dir exists
|
||||
if !os.is_dir(gen_vc.options.work_dir) {
|
||||
// try create
|
||||
os.mkdir(gen_vc.options.work_dir) or { panic(err) }
|
||||
// still dosen't exist... we have a problem
|
||||
if !os.is_dir(gen_vc.options.work_dir) {
|
||||
gen_vc.logger.error('error creating directory: $gen_vc.options.work_dir')
|
||||
gen_vc.gen_error = true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// cd to gen_vc dir
|
||||
os.chdir(gen_vc.options.work_dir)
|
||||
|
||||
// if we are not running with the --serve flag (webhook server)
|
||||
// rather than deleting and re-downloading the repo each time
|
||||
// first check to see if the local v repo is behind master
|
||||
// if it isn't behind theres no point continuing further
|
||||
if !gen_vc.options.serve && os.is_dir(git_repo_dir_v) {
|
||||
gen_vc.cmd_exec('git -C $git_repo_dir_v checkout master')
|
||||
// fetch the remote repo just in case there are newer commits there
|
||||
gen_vc.cmd_exec('git -C $git_repo_dir_v fetch')
|
||||
git_status := gen_vc.cmd_exec('git -C $git_repo_dir_v status')
|
||||
if !git_status.contains('behind') {
|
||||
gen_vc.logger.warn('v repository is already up to date.')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// delete repos
|
||||
gen_vc.purge_repos()
|
||||
|
||||
// clone repos
|
||||
gen_vc.cmd_exec('git clone --depth 1 https://$git_repo_v $git_repo_dir_v')
|
||||
gen_vc.cmd_exec('git clone --depth 1 https://$git_repo_vc $git_repo_dir_vc')
|
||||
|
||||
// get output of git log -1 (last commit)
|
||||
git_log_v := gen_vc.cmd_exec('git -C $git_repo_dir_v log -1 --format="commit %H%nDate: %ci%nDate Unix: %ct%nSubject: %s"')
|
||||
git_log_vc := gen_vc.cmd_exec('git -C $git_repo_dir_vc log -1 --format="Commit %H%nDate: %ci%nDate Unix: %ct%nSubject: %s"')
|
||||
|
||||
// date of last commit in each repo
|
||||
ts_v := git_log_v.find_between('Date:', '\n').trim_space()
|
||||
ts_vc := git_log_vc.find_between('Date:', '\n').trim_space()
|
||||
|
||||
// parse time as string to time.Time
|
||||
last_commit_time_v := time.parse(ts_v) or {
|
||||
panic(err)
|
||||
}
|
||||
last_commit_time_vc := time.parse(ts_vc) or {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// git dates are in users local timezone and v time.parse does not parse
|
||||
// timezones at the moment, so for now get unix timestamp from output also
|
||||
t_unix_v := git_log_v.find_between('Date Unix:', '\n').trim_space().int()
|
||||
t_unix_vc := git_log_vc.find_between('Date Unix:', '\n').trim_space().int()
|
||||
|
||||
// last commit hash in v repo
|
||||
last_commit_hash_v := git_log_v.find_between('commit', '\n').trim_space()
|
||||
last_commit_hash_v_short := last_commit_hash_v[..7]
|
||||
|
||||
// subject
|
||||
last_commit_subject := git_log_v.find_between('Subject:', '\n').trim_space()
|
||||
|
||||
// log some info
|
||||
gen_vc.logger.debug('last commit time ($git_repo_v): ' + last_commit_time_v.format_ss())
|
||||
gen_vc.logger.debug('last commit time ($git_repo_vc): ' + last_commit_time_vc.format_ss())
|
||||
gen_vc.logger.debug('last commit hash ($git_repo_v): $last_commit_hash_v')
|
||||
gen_vc.logger.debug('last commit subject ($git_repo_v): $last_commit_subject')
|
||||
|
||||
// if vc repo already has a newer commit than the v repo, assume it's up to date
|
||||
if t_unix_vc >= t_unix_v {
|
||||
gen_vc.logger.warn('vc repository is already up to date.')
|
||||
return
|
||||
}
|
||||
|
||||
// try build v for current os (linux in this case)
|
||||
gen_vc.cmd_exec('make -C $git_repo_dir_v')
|
||||
v_exec := '$git_repo_dir_v/v'
|
||||
// check if make was successful
|
||||
gen_vc.assert_file_exists_and_is_not_too_short(v_exec, err_msg_make)
|
||||
|
||||
// build v.c for each os
|
||||
for os_name in vc_build_oses {
|
||||
vc_suffix := if os_name == 'nix' { '' } else { '_${os_name[..3]}' }
|
||||
v_flags := if os_name == 'nix' { '-output-cross-platform-c' } else { '-os $os_name' }
|
||||
c_file := 'v${vc_suffix}.c'
|
||||
// try generate .c file
|
||||
gen_vc.cmd_exec('$v_exec $v_flags -o $c_file $git_repo_dir_v/cmd/v')
|
||||
// check if the c file seems ok
|
||||
gen_vc.assert_file_exists_and_is_not_too_short(c_file, err_msg_gen_c)
|
||||
// embed the latest v commit hash into the c file
|
||||
gen_vc.cmd_exec('sed -i \'1s/^/#define V_COMMIT_HASH "$last_commit_hash_v_short"\\n/\' $c_file')
|
||||
// run clang-format to make the c file more readable
|
||||
gen_vc.cmd_exec('clang-format -i $c_file')
|
||||
// move to vc repo
|
||||
gen_vc.cmd_exec('mv $c_file $git_repo_dir_vc/$c_file')
|
||||
// add new .c file to local vc repo
|
||||
gen_vc.cmd_exec('git -C $git_repo_dir_vc add $c_file')
|
||||
}
|
||||
|
||||
// check if the vc repo actually changed
|
||||
git_status := gen_vc.cmd_exec('git -C $git_repo_dir_vc status')
|
||||
if git_status.contains('nothing to commit') {
|
||||
gen_vc.logger.error('no changes to vc repo: something went wrong.')
|
||||
gen_vc.gen_error = true
|
||||
}
|
||||
// commit changes to local vc repo
|
||||
gen_vc.cmd_exec_safe('git -C $git_repo_dir_vc commit -m "[v:master] $last_commit_hash_v_short - $last_commit_subject"')
|
||||
// push changes to remote vc repo
|
||||
gen_vc.cmd_exec_safe('git -C $git_repo_dir_vc push https://${urllib.query_escape(git_username)}:${urllib.query_escape(git_password)}@$git_repo_vc master')
|
||||
}
|
||||
|
||||
// only execute when dry_run option is false, otherwise just log
|
||||
fn (gen_vc mut GenVC) cmd_exec_safe(cmd string) string {
|
||||
return gen_vc.command_execute(cmd, gen_vc.options.dry_run)
|
||||
}
|
||||
|
||||
// always execute command
|
||||
fn (gen_vc mut GenVC) cmd_exec(cmd string) string {
|
||||
return gen_vc.command_execute(cmd, false)
|
||||
}
|
||||
|
||||
// execute command
|
||||
fn (gen_vc mut GenVC) command_execute(cmd string, dry bool) string {
|
||||
// if dry is true then dont execute, just log
|
||||
if dry {
|
||||
return gen_vc.command_execute_dry(cmd)
|
||||
}
|
||||
gen_vc.logger.info('cmd: $cmd')
|
||||
r := os.exec(cmd) or {
|
||||
gen_vc.logger.error('$err_msg_cmd_x: "$cmd" could not start.')
|
||||
gen_vc.logger.error( err )
|
||||
// something went wrong, better start fresh next time
|
||||
gen_vc.purge_repos()
|
||||
gen_vc.gen_error = true
|
||||
return ''
|
||||
}
|
||||
if r.exit_code != 0 {
|
||||
gen_vc.logger.error('$err_msg_cmd_x: "$cmd" failed.')
|
||||
gen_vc.logger.error(r.output)
|
||||
// something went wrong, better start fresh next time
|
||||
gen_vc.purge_repos()
|
||||
gen_vc.gen_error = true
|
||||
return ''
|
||||
}
|
||||
return r.output
|
||||
}
|
||||
|
||||
// just log cmd, dont execute
|
||||
fn (gen_vc mut GenVC) command_execute_dry(cmd string) string {
|
||||
gen_vc.logger.info('cmd (dry): "$cmd"')
|
||||
return ''
|
||||
}
|
||||
|
||||
// delete repo directories
|
||||
fn (gen_vc mut GenVC) purge_repos() {
|
||||
// delete old repos (better to be fully explicit here, since these are destructive operations)
|
||||
mut repo_dir := '$gen_vc.options.work_dir/$git_repo_dir_v'
|
||||
if os.is_dir(repo_dir) {
|
||||
gen_vc.logger.info('purging local repo: "$repo_dir"')
|
||||
gen_vc.cmd_exec('rm -rf $repo_dir')
|
||||
}
|
||||
repo_dir = '$gen_vc.options.work_dir/$git_repo_dir_vc'
|
||||
if os.is_dir(repo_dir) {
|
||||
gen_vc.logger.info('purging local repo: "$repo_dir"')
|
||||
gen_vc.cmd_exec('rm -rf $repo_dir')
|
||||
}
|
||||
}
|
||||
|
||||
// check if file size is too short
|
||||
fn (gen_vc mut GenVC) assert_file_exists_and_is_not_too_short(f string, emsg string){
|
||||
if !os.exists(f) {
|
||||
gen_vc.logger.error('$err_msg_build: $emsg .')
|
||||
gen_vc.gen_error = true
|
||||
return
|
||||
}
|
||||
fsize := os.file_size(f)
|
||||
if fsize < too_short_file_limit {
|
||||
gen_vc.logger.error('$err_msg_build: $f exists, but is too short: only $fsize bytes.')
|
||||
gen_vc.gen_error = true
|
||||
return
|
||||
}
|
||||
}
|
||||
100
cmd/tools/modules/scripting/scripting.v
Normal file
100
cmd/tools/modules/scripting/scripting.v
Normal file
@@ -0,0 +1,100 @@
|
||||
module scripting
|
||||
|
||||
import os
|
||||
|
||||
pub fn set_verbose(on bool) {
|
||||
// setting a global here would be the obvious solution,
|
||||
// but V does not have globals normally.
|
||||
if on {
|
||||
os.setenv('VERBOSE', '1', true)
|
||||
}
|
||||
else {
|
||||
os.unsetenv('VERBOSE')
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verbose_trace(label string, message string) {
|
||||
if os.getenv('VERBOSE').len > 0 {
|
||||
slabel := 'scripting.${label}'
|
||||
println('# ${slabel:-25s} : $message')
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verbose_trace_exec_result(x os.Result) {
|
||||
if os.getenv('VERBOSE').len > 0 {
|
||||
println('# cmd.exit_code : ${x.exit_code.str():-4s} cmd.output:')
|
||||
println('# ----------------------------------- #')
|
||||
mut lnum := 1
|
||||
lines := x.output.split_into_lines()
|
||||
for line in lines {
|
||||
println('# ${lnum:3d}: $line')
|
||||
lnum++
|
||||
}
|
||||
println('# ----------------------------------- #')
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chdir(path string) {
|
||||
verbose_trace(@FN, 'cd $path')
|
||||
os.chdir(path)
|
||||
}
|
||||
|
||||
pub fn rmrf(path string) {
|
||||
verbose_trace(@FN, 'rm -rf $path')
|
||||
if os.exists(path) {
|
||||
if os.is_dir(path) {
|
||||
os.rmdir_recursive(path)
|
||||
}else{
|
||||
os.rm(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(cmd string) string {
|
||||
verbose_trace(@FN, cmd)
|
||||
x := os.exec(cmd) or {
|
||||
verbose_trace(@FN, '## failed.')
|
||||
return ''
|
||||
}
|
||||
verbose_trace_exec_result(x)
|
||||
if x.exit_code == 0 {
|
||||
return x.output
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
pub fn exit_0_status(cmd string) bool {
|
||||
verbose_trace(@FN, cmd)
|
||||
x := os.exec(cmd) or {
|
||||
verbose_trace(@FN, '## failed.')
|
||||
return false
|
||||
}
|
||||
verbose_trace_exec_result(x)
|
||||
if x.exit_code == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
pub fn tool_must_exist (toolcmd string) {
|
||||
verbose_trace(@FN, toolcmd)
|
||||
if exit_0_status('type $toolcmd') {
|
||||
return
|
||||
}
|
||||
eprintln('Missing tool: $toolcmd')
|
||||
eprintln('Please try again after you install it.')
|
||||
exit(1)
|
||||
}
|
||||
|
||||
pub fn used_tools_must_exist(tools []string) {
|
||||
for t in tools {
|
||||
tool_must_exist(t)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn show_sizes_of_files(files []string) {
|
||||
for f in files {
|
||||
size := os.file_size(f)
|
||||
println('${size:10d} $f')
|
||||
}
|
||||
}
|
||||
272
cmd/tools/modules/testing/common.v
Normal file
272
cmd/tools/modules/testing/common.v
Normal file
@@ -0,0 +1,272 @@
|
||||
module testing
|
||||
|
||||
import (
|
||||
os
|
||||
term
|
||||
benchmark
|
||||
filepath
|
||||
runtime
|
||||
sync
|
||||
)
|
||||
|
||||
pub struct TestSession {
|
||||
pub mut:
|
||||
files []string
|
||||
vexe string
|
||||
vargs string
|
||||
failed bool
|
||||
benchmark benchmark.Benchmark
|
||||
|
||||
ntask int // writing to this should be locked by mu.
|
||||
ntask_mtx &sync.Mutex
|
||||
waitgroup &sync.WaitGroup
|
||||
show_ok_tests bool
|
||||
}
|
||||
|
||||
pub fn new_test_session(vargs string) TestSession {
|
||||
return TestSession{
|
||||
vexe: vexe_path()
|
||||
vargs: vargs
|
||||
|
||||
ntask: 0
|
||||
ntask_mtx: sync.new_mutex()
|
||||
waitgroup: sync.new_waitgroup()
|
||||
|
||||
show_ok_tests: !vargs.contains('-silent')
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vexe_path() string {
|
||||
// NB: tools extracted from v require that the VEXE
|
||||
// environment variable contains the path to the v executable location.
|
||||
// They are usually launched by cmd/v/simple_tool.v,
|
||||
// launch_tool/1 , which provides it.
|
||||
return os.getenv('VEXE')
|
||||
}
|
||||
|
||||
pub fn (ts mut TestSession) init() {
|
||||
ts.benchmark = benchmark.new_benchmark()
|
||||
}
|
||||
|
||||
pub fn (ts mut TestSession) test() {
|
||||
ts.init()
|
||||
mut remaining_files := []string
|
||||
for dot_relative_file in ts.files {
|
||||
relative_file := dot_relative_file.replace('./', '')
|
||||
file := os.realpath(relative_file)
|
||||
$if windows {
|
||||
if file.contains('sqlite') || file.contains('httpbin') {
|
||||
continue
|
||||
}
|
||||
}
|
||||
$if !macos {
|
||||
if file.contains('customer') {
|
||||
continue
|
||||
}
|
||||
}
|
||||
$if msvc {
|
||||
if file.contains('asm') {
|
||||
continue
|
||||
}
|
||||
}
|
||||
$if tinyc {
|
||||
if file.contains('asm') {
|
||||
continue
|
||||
}
|
||||
}
|
||||
remaining_files << dot_relative_file
|
||||
}
|
||||
|
||||
ts.files = remaining_files
|
||||
ts.benchmark.set_total_expected_steps(remaining_files.len)
|
||||
|
||||
mut njobs := runtime.nr_jobs()
|
||||
$if msvc {
|
||||
// NB: MSVC can not be launched in parallel, without giving it
|
||||
// the option /FS because it uses a shared PDB file, which should
|
||||
// be locked, but that makes writing slower...
|
||||
// See: https://docs.microsoft.com/en-us/cpp/build/reference/fs-force-synchronous-pdb-writes?view=vs-2019
|
||||
// Instead, just run tests on 1 core for now.
|
||||
njobs = 1
|
||||
}
|
||||
ts.waitgroup.add( njobs )
|
||||
for i:=0; i < njobs; i++ {
|
||||
go process_in_thread(ts)
|
||||
}
|
||||
ts.waitgroup.wait()
|
||||
ts.benchmark.stop()
|
||||
eprintln(term.h_divider('-'))
|
||||
}
|
||||
|
||||
|
||||
fn process_in_thread(ts mut TestSession){
|
||||
ts.process_files()
|
||||
ts.waitgroup.done()
|
||||
}
|
||||
|
||||
fn (ts mut TestSession) process_files() {
|
||||
tmpd := os.tmpdir()
|
||||
show_stats := '-stats' in ts.vargs.split(' ')
|
||||
|
||||
mut tls_bench := benchmark.new_benchmark() // tls_bench is used to format the step messages/timings
|
||||
tls_bench.set_total_expected_steps( ts.benchmark.nexpected_steps )
|
||||
for {
|
||||
|
||||
ts.ntask_mtx.lock()
|
||||
ts.ntask++
|
||||
idx := ts.ntask-1
|
||||
ts.ntask_mtx.unlock()
|
||||
|
||||
if idx >= ts.files.len { break }
|
||||
tls_bench.cstep = idx
|
||||
|
||||
dot_relative_file := ts.files[ idx ]
|
||||
relative_file := dot_relative_file.replace('./', '')
|
||||
file := os.realpath(relative_file)
|
||||
// Ensure that the generated binaries will be stored in the temporary folder.
|
||||
// Remove them after a test passes/fails.
|
||||
fname := filepath.filename(file)
|
||||
generated_binary_fname := if os.user_os() == 'windows' { fname.replace('.v', '.exe') } else { fname.replace('.v', '') }
|
||||
generated_binary_fpath := filepath.join(tmpd,generated_binary_fname)
|
||||
if os.exists(generated_binary_fpath) {
|
||||
os.rm(generated_binary_fpath)
|
||||
}
|
||||
mut cmd_options := [ts.vargs]
|
||||
if !ts.vargs.contains('fmt') {
|
||||
cmd_options << ' -o "$generated_binary_fpath"'
|
||||
}
|
||||
cmd := '"${ts.vexe}" ' + cmd_options.join(' ') + ' "${file}"'
|
||||
// eprintln('>>> v cmd: $cmd')
|
||||
ts.benchmark.step()
|
||||
tls_bench.step()
|
||||
if show_stats {
|
||||
eprintln(term.h_divider('-'))
|
||||
status := os.system(cmd)
|
||||
if status == 0 {
|
||||
ts.benchmark.ok()
|
||||
tls_bench.ok()
|
||||
}
|
||||
else {
|
||||
ts.failed = true
|
||||
ts.benchmark.fail()
|
||||
tls_bench.fail()
|
||||
continue
|
||||
}
|
||||
}
|
||||
else {
|
||||
r := os.exec(cmd) or {
|
||||
ts.failed = true
|
||||
ts.benchmark.fail()
|
||||
tls_bench.fail()
|
||||
eprintln(tls_bench.step_message_fail(relative_file))
|
||||
continue
|
||||
}
|
||||
if r.exit_code != 0 {
|
||||
ts.failed = true
|
||||
ts.benchmark.fail()
|
||||
tls_bench.fail()
|
||||
eprintln(tls_bench.step_message_fail('${relative_file}\n`$file`\n (\n$r.output\n)'))
|
||||
}
|
||||
else {
|
||||
ts.benchmark.ok()
|
||||
tls_bench.ok()
|
||||
if ts.show_ok_tests {
|
||||
eprintln(tls_bench.step_message_ok(relative_file))
|
||||
}
|
||||
}
|
||||
}
|
||||
if os.exists(generated_binary_fpath) {
|
||||
os.rm(generated_binary_fpath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vlib_should_be_present(parent_dir string) {
|
||||
vlib_dir := filepath.join(parent_dir,'vlib')
|
||||
if !os.is_dir(vlib_dir) {
|
||||
eprintln('$vlib_dir is missing, it must be next to the V executable')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn v_build_failing(zargs string, folder string) bool {
|
||||
main_label := 'Building $folder ...'
|
||||
finish_label := 'building $folder'
|
||||
vexe := vexe_path()
|
||||
parent_dir := filepath.dir(vexe)
|
||||
vlib_should_be_present(parent_dir)
|
||||
vargs := zargs.replace(vexe, '')
|
||||
eheader(main_label)
|
||||
eprintln('v compiler args: "$vargs"')
|
||||
mut session := new_test_session(vargs)
|
||||
files := os.walk_ext(filepath.join(parent_dir,folder), '.v')
|
||||
mut mains := files.filter(!it.contains('modules') && !it.contains('preludes'))
|
||||
$if windows {
|
||||
// skip pico example on windows
|
||||
// there was a bug using filter here
|
||||
mut mains_filtered := []string
|
||||
for file in mains {
|
||||
if !file.ends_with('examples\\pico\\pico.v') {
|
||||
mains_filtered << file
|
||||
}
|
||||
}
|
||||
mains = mains_filtered
|
||||
}
|
||||
session.files << mains
|
||||
session.test()
|
||||
eprintln(session.benchmark.total_message(finish_label))
|
||||
return session.failed
|
||||
}
|
||||
|
||||
pub fn build_v_cmd_failed(cmd string) bool {
|
||||
res := os.exec(cmd) or {
|
||||
return true
|
||||
}
|
||||
if res.exit_code != 0 {
|
||||
eprintln('')
|
||||
eprintln(res.output)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
pub fn building_any_v_binaries_failed() bool {
|
||||
eheader('Building V binaries...')
|
||||
eprintln('VFLAGS is: "' + os.getenv('VFLAGS') + '"')
|
||||
vexe := testing.vexe_path()
|
||||
parent_dir := filepath.dir(vexe)
|
||||
testing.vlib_should_be_present(parent_dir)
|
||||
os.chdir(parent_dir)
|
||||
mut failed := false
|
||||
v_build_commands := ['$vexe -o v_g -g cmd/v',
|
||||
'$vexe -o v_prod_g -prod -g cmd/v',
|
||||
'$vexe -o v_cg -cg cmd/v',
|
||||
'$vexe -o v_prod_cg -prod -cg cmd/v',
|
||||
'$vexe -o v_prod -prod cmd/v',
|
||||
]
|
||||
mut bmark := benchmark.new_benchmark()
|
||||
for cmd in v_build_commands {
|
||||
bmark.step()
|
||||
if build_v_cmd_failed(cmd) {
|
||||
bmark.fail()
|
||||
failed = true
|
||||
eprintln(bmark.step_message_fail('command: ${cmd} . See details above ^^^^^^^'))
|
||||
eprintln('')
|
||||
continue
|
||||
}
|
||||
bmark.ok()
|
||||
eprintln(bmark.step_message_ok('command: ${cmd}'))
|
||||
}
|
||||
bmark.stop()
|
||||
eprintln(term.h_divider('-'))
|
||||
eprintln(bmark.total_message('building v binaries'))
|
||||
return failed
|
||||
}
|
||||
|
||||
pub fn eheader(msg string) {
|
||||
eprintln(term.header(msg,'-'))
|
||||
}
|
||||
|
||||
pub fn header(msg string) {
|
||||
println(term.header(msg,'-'))
|
||||
}
|
||||
178
cmd/tools/modules/vgit/vgit.v
Normal file
178
cmd/tools/modules/vgit/vgit.v
Normal file
@@ -0,0 +1,178 @@
|
||||
module vgit
|
||||
|
||||
import os
|
||||
import flag
|
||||
import filepath
|
||||
import scripting
|
||||
|
||||
const (
|
||||
remote_v_repo_url = 'https://github.com/vlang/v'
|
||||
remote_vc_repo_url = 'https://github.com/vlang/vc'
|
||||
)
|
||||
|
||||
pub fn check_v_commit_timestamp_before_self_rebuilding(v_timestamp int) {
|
||||
if v_timestamp >= 1561805697 {
|
||||
return
|
||||
}
|
||||
eprintln('##################################################################')
|
||||
eprintln('# WARNING: v self rebuilding, before 5b7a1e8 (2019-06-29 12:21) #')
|
||||
eprintln('# required the v executable to be built *inside* #')
|
||||
eprintln('# the toplevel compiler/ folder. #')
|
||||
eprintln('# #')
|
||||
eprintln('# That is not supported by this tool. #')
|
||||
eprintln('# You will have to build it manually there. #')
|
||||
eprintln('##################################################################')
|
||||
}
|
||||
|
||||
pub fn validate_commit_exists(commit string) {
|
||||
if commit.len == 0 {
|
||||
return
|
||||
}
|
||||
cmd := "git cat-file -t \'$commit\' "
|
||||
if !scripting.exit_0_status(cmd) {
|
||||
eprintln('Commit: "$commit" does not exist in the current repository.')
|
||||
exit(3)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn line_to_timestamp_and_commit(line string) (int,string) {
|
||||
parts := line.split(' ')
|
||||
return parts[0].int(),parts[1]
|
||||
}
|
||||
|
||||
pub fn normalized_workpath_for_commit(workdir string, commit string) string {
|
||||
nc := 'v_at_' + commit.replace('^', '_').replace('-', '_').replace('/', '_')
|
||||
return os.realpath(workdir + os.path_separator + nc)
|
||||
}
|
||||
|
||||
pub fn prepare_vc_source(vcdir string, cdir string, commit string) (string,string) {
|
||||
scripting.chdir(cdir)
|
||||
// Building a historic v with the latest vc is not always possible ...
|
||||
// It is more likely, that the vc *at the time of the v commit*,
|
||||
// or slightly before that time will be able to build the historic v:
|
||||
vline := scripting.run('git rev-list -n1 --timestamp "$commit" ')
|
||||
v_timestamp,v_commithash := vgit.line_to_timestamp_and_commit(vline)
|
||||
vgit.check_v_commit_timestamp_before_self_rebuilding(v_timestamp)
|
||||
scripting.chdir(vcdir)
|
||||
scripting.run('git checkout master')
|
||||
vcbefore := scripting.run('git rev-list HEAD -n1 --timestamp --before=$v_timestamp ')
|
||||
_,vccommit_before := vgit.line_to_timestamp_and_commit(vcbefore)
|
||||
scripting.run('git checkout "$vccommit_before" ')
|
||||
scripting.run('wc *.c')
|
||||
scripting.chdir(cdir)
|
||||
return v_commithash,vccommit_before
|
||||
}
|
||||
|
||||
pub fn clone_or_pull( remote_git_url string, local_worktree_path string ) {
|
||||
// NB: after clone_or_pull, the current repo branch is === HEAD === master
|
||||
if os.is_dir( local_worktree_path ) && os.is_dir(filepath.join(local_worktree_path,'.git')) {
|
||||
// Already existing ... Just pulling in this case is faster usually.
|
||||
scripting.run('git -C "$local_worktree_path" checkout --quiet master')
|
||||
scripting.run('git -C "$local_worktree_path" pull --quiet ')
|
||||
} else {
|
||||
// Clone a fresh
|
||||
scripting.run('git clone --quiet "$remote_git_url" "$local_worktree_path" ')
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
pub struct VGitContext {
|
||||
pub:
|
||||
cc string = 'cc' // what compiler to use
|
||||
workdir string = '/tmp' // the base working folder
|
||||
commit_v string = 'master' // the commit-ish that needs to be prepared
|
||||
path_v string // where is the local working copy v repo
|
||||
path_vc string // where is the local working copy vc repo
|
||||
v_repo_url string // the remote v repo URL
|
||||
vc_repo_url string // the remote vc repo URL
|
||||
pub mut:
|
||||
// these will be filled by vgitcontext.compile_oldv_if_needed()
|
||||
commit_v__hash string // the git commit of the v repo that should be prepared
|
||||
commit_vc_hash string // the git commit of the vc repo, corresponding to commit_v__hash
|
||||
vexename string // v or v.exe
|
||||
vexepath string // the full absolute path to the prepared v/v.exe
|
||||
vvlocation string // v.v or compiler/ or cmd/v, depending on v version
|
||||
}
|
||||
|
||||
pub fn (vgit_context mut VGitContext) compile_oldv_if_needed() {
|
||||
vgit_context.vexename = if os.user_os() == 'windows' { 'v.exe' } else { 'v' }
|
||||
vgit_context.vexepath = os.realpath( filepath.join(vgit_context.path_v, vgit_context.vexename) )
|
||||
mut command_for_building_v_from_c_source := ''
|
||||
mut command_for_selfbuilding := ''
|
||||
if 'windows' == os.user_os() {
|
||||
command_for_building_v_from_c_source = '$vgit_context.cc -std=c99 -municode -w -o cv.exe "$vgit_context.path_vc/v_win.c" '
|
||||
command_for_selfbuilding = './cv.exe -o $vgit_context.vexename {SOURCE}'
|
||||
}
|
||||
else {
|
||||
command_for_building_v_from_c_source = '$vgit_context.cc -std=gnu11 -w -o cv "$vgit_context.path_vc/v.c" -lm'
|
||||
command_for_selfbuilding = './cv -o $vgit_context.vexename {SOURCE}'
|
||||
}
|
||||
scripting.chdir(vgit_context.workdir)
|
||||
clone_or_pull( vgit_context.v_repo_url, vgit_context.path_v )
|
||||
clone_or_pull( vgit_context.vc_repo_url, vgit_context.path_vc )
|
||||
|
||||
scripting.chdir(vgit_context.path_v)
|
||||
scripting.run('git checkout $vgit_context.commit_v')
|
||||
v_commithash,vccommit_before := vgit.prepare_vc_source(vgit_context.path_vc, vgit_context.path_v, vgit_context.commit_v)
|
||||
vgit_context.commit_v__hash = v_commithash
|
||||
vgit_context.commit_vc_hash = vccommit_before
|
||||
if os.exists('cmd/v') {
|
||||
vgit_context.vvlocation = 'cmd/v'
|
||||
} else {
|
||||
vgit_context.vvlocation = if os.exists('v.v') { 'v.v' } else { 'compiler' }
|
||||
}
|
||||
if os.is_dir(vgit_context.path_v) && os.exists(vgit_context.vexepath) {
|
||||
// already compiled, so no need to compile v again
|
||||
return
|
||||
}
|
||||
// Recompilation is needed. Just to be sure, clean up everything first.
|
||||
scripting.run('git clean -xf')
|
||||
scripting.run(command_for_building_v_from_c_source)
|
||||
build_cmd := command_for_selfbuilding.replace('{SOURCE}', vgit_context.vvlocation)
|
||||
scripting.run(build_cmd)
|
||||
|
||||
// At this point, there exists a file vgit_context.vexepath
|
||||
// which should be a valid working V executable.
|
||||
}
|
||||
|
||||
pub fn add_common_tool_options<T>(context mut T, fp mut flag.FlagParser) []string {
|
||||
tdir := os.tmpdir()
|
||||
context.workdir = os.realpath(fp.string_('workdir', `w`, tdir, 'A writable base folder. Default: $tdir'))
|
||||
context.v_repo_url = fp.string('vrepo', vgit.remote_v_repo_url, 'The url of the V repository. You can clone it locally too. See also --vcrepo below.')
|
||||
context.vc_repo_url = fp.string('vcrepo', vgit.remote_vc_repo_url, 'The url of the vc repository. You can clone it
|
||||
${flag.SPACE}beforehand, and then just give the local folder
|
||||
${flag.SPACE}path here. That will eliminate the network ops
|
||||
${flag.SPACE}done by this tool, which is useful, if you want
|
||||
${flag.SPACE}to script it/run it in a restrictive vps/docker.
|
||||
')
|
||||
context.show_help = fp.bool_('help', `h`, false, 'Show this help screen.')
|
||||
context.verbose = fp.bool_('verbose', `v`, false, 'Be more verbose.')
|
||||
|
||||
if (context.show_help) {
|
||||
println(fp.usage())
|
||||
exit(0)
|
||||
}
|
||||
|
||||
if context.verbose {
|
||||
scripting.set_verbose(true)
|
||||
}
|
||||
|
||||
if os.is_dir(context.v_repo_url) {
|
||||
context.v_repo_url = os.realpath( context.v_repo_url )
|
||||
}
|
||||
|
||||
if os.is_dir(context.vc_repo_url) {
|
||||
context.vc_repo_url = os.realpath( context.vc_repo_url )
|
||||
}
|
||||
|
||||
commits := fp.finalize() or {
|
||||
eprintln('Error: ' + err)
|
||||
exit(1)
|
||||
}
|
||||
for commit in commits {
|
||||
vgit.validate_commit_exists(commit)
|
||||
}
|
||||
|
||||
return commits
|
||||
}
|
||||
120
cmd/tools/oldv.v
Normal file
120
cmd/tools/oldv.v
Normal file
@@ -0,0 +1,120 @@
|
||||
import (
|
||||
os
|
||||
flag
|
||||
filepath
|
||||
scripting
|
||||
vgit
|
||||
)
|
||||
|
||||
const (
|
||||
tool_version = '0.0.3'
|
||||
tool_description = ' Checkout an old V and compile it as it was on specific commit.
|
||||
This tool is useful, when you want to discover when something broke.
|
||||
It is also useful, when you just want to experiment with an older historic V.
|
||||
|
||||
The VCOMMIT argument can be a git commitish like HEAD or master and so on.
|
||||
When oldv is used with git bisect, you probably want to give HEAD. For example:
|
||||
git bisect start
|
||||
git bisect bad
|
||||
git checkout known_good_commit
|
||||
git bisect good
|
||||
## Now git will automatically checkout a middle commit between the bad and the good
|
||||
cmd/tools/oldv HEAD --command="run commands in oldv folder, to verify if the commit is good or bad"
|
||||
## See what the result is, and either do: ...
|
||||
git bisect good
|
||||
## ... or do:
|
||||
git bisect bad
|
||||
## Now you just repeat the above steps, each time running oldv with the same command, then mark the result as good or bad,
|
||||
## until you find the commit, where the problem first occured.
|
||||
## When you finish, do not forget to do:
|
||||
git bisect reset'
|
||||
)
|
||||
|
||||
struct Context {
|
||||
mut:
|
||||
v_repo_url string // the url of the V repository. It can be a local folder path, if you want to eliminate network operations...
|
||||
vc_repo_url string // the url of the vc repository. It can be a local folder path, if you want to eliminate network operations...
|
||||
workdir string // the working folder (typically /tmp), where the tool will write
|
||||
commit_v string='master' // the commit from which you want to produce a working v compiler (this may be a commit-ish too)
|
||||
commit_vc string='master' // this will be derived from commit_v
|
||||
commit_v_hash string // this will be filled from the commit-ish commit_v using rev-list. It IS a commit hash.
|
||||
path_v string // the full path to the v folder inside workdir.
|
||||
path_vc string // the full path to the vc folder inside workdir.
|
||||
cmd_to_run string // the command that you want to run *in* the oldv repo
|
||||
cc string='cc' // the C compiler to use for bootstrapping.
|
||||
cleanup bool // should the tool run a cleanup first
|
||||
verbose bool // should the tool be much more verbose
|
||||
show_help bool // whether to show the usage screen
|
||||
}
|
||||
|
||||
fn (c mut Context) compile_oldv_if_needed() {
|
||||
mut vgit_context := vgit.VGitContext{
|
||||
cc: c.cc
|
||||
workdir: c.workdir
|
||||
commit_v: c.commit_v
|
||||
path_v: c.path_v
|
||||
path_vc: c.path_vc
|
||||
v_repo_url: c.v_repo_url
|
||||
vc_repo_url: c.vc_repo_url
|
||||
}
|
||||
vgit_context.compile_oldv_if_needed()
|
||||
c.commit_v_hash = vgit_context.commit_v__hash
|
||||
if !os.exists(vgit_context.vexepath) && c.cmd_to_run.len > 0 {
|
||||
// NB: 125 is a special code, that git bisect understands as 'skip this commit'.
|
||||
// it is used to inform git bisect that the current commit leads to a build failure.
|
||||
exit(125)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
scripting.used_tools_must_exist(['git', 'cc'])
|
||||
mut context := Context{}
|
||||
mut fp := flag.new_flag_parser(os.args)
|
||||
fp.application(filepath.filename(os.executable()))
|
||||
fp.version(tool_version)
|
||||
fp.description(tool_description)
|
||||
fp.arguments_description('VCOMMIT')
|
||||
fp.skip_executable()
|
||||
fp.limit_free_args(1, 1)
|
||||
|
||||
context.cleanup = fp.bool('clean', true, 'Clean before running (slower).')
|
||||
context.cmd_to_run = fp.string_('command', `c`, '', 'Command to run in the old V repo.\n')
|
||||
|
||||
commits := vgit.add_common_tool_options(mut context, mut fp)
|
||||
if commits.len > 0 {
|
||||
context.commit_v = commits[0]
|
||||
} else {
|
||||
context.commit_v = scripting.run('git rev-list -n1 HEAD')
|
||||
}
|
||||
println('################# context.commit_v: $context.commit_v #####################')
|
||||
context.path_v = vgit.normalized_workpath_for_commit(context.workdir, context.commit_v)
|
||||
context.path_vc = vgit.normalized_workpath_for_commit(context.workdir, 'vc')
|
||||
if !os.is_dir(context.workdir) {
|
||||
msg := 'Work folder: ' + context.workdir + ' , does not exist.'
|
||||
eprintln(msg)
|
||||
exit(2)
|
||||
}
|
||||
ecc := os.getenv('CC')
|
||||
if ecc != '' {
|
||||
context.cc = ecc
|
||||
}
|
||||
if context.cleanup {
|
||||
scripting.rmrf(context.path_v)
|
||||
scripting.rmrf(context.path_vc)
|
||||
}
|
||||
|
||||
context.compile_oldv_if_needed()
|
||||
|
||||
scripting.chdir(context.path_v)
|
||||
println('# v commit hash: $context.commit_v_hash')
|
||||
println('# checkout folder: $context.path_v')
|
||||
if context.cmd_to_run.len > 0 {
|
||||
cmdres := os.exec(context.cmd_to_run) or {
|
||||
panic(err)
|
||||
}
|
||||
println('# command: ${context.cmd_to_run:-34s} exit code: ${cmdres.exit_code:-4d} result:')
|
||||
println(cmdres.output)
|
||||
exit(cmdres.exit_code)
|
||||
}
|
||||
|
||||
}
|
||||
212
cmd/tools/performance_compare.v
Normal file
212
cmd/tools/performance_compare.v
Normal file
@@ -0,0 +1,212 @@
|
||||
import (
|
||||
os
|
||||
flag
|
||||
filepath
|
||||
scripting
|
||||
vgit
|
||||
)
|
||||
|
||||
const (
|
||||
tool_version = '0.0.5'
|
||||
tool_description = ' Compares V executable size and performance,
|
||||
between 2 commits from V\'s local git history.
|
||||
When only one commit is given, it is compared to master.
|
||||
'
|
||||
)
|
||||
|
||||
struct Context {
|
||||
cwd string // current working folder
|
||||
mut:
|
||||
v_repo_url string // the url of the vc repository. It can be a local folder path, which is usefull to eliminate network operations...
|
||||
vc_repo_url string // the url of the vc repository. It can be a local folder path, which is usefull to eliminate network operations...
|
||||
workdir string // the working folder (typically /tmp), where the tool will write
|
||||
a string // the full path to the 'after' folder inside workdir
|
||||
b string // the full path to the 'before' folder inside workdir
|
||||
vc string // the full path to the vc folder inside workdir. It is used during bootstrapping v from the C source.
|
||||
commit_before string // the git commit for the 'before' state
|
||||
commit_after string // the git commit for the 'after' state
|
||||
warmups int // how many times to execute a command before gathering stats
|
||||
verbose bool // whether to print even more stuff
|
||||
show_help bool // whether to show the usage screen
|
||||
hyperfineopts string // use for additional CLI options that will be given to the hyperfine command
|
||||
vflags string // other v options to pass to compared v commands
|
||||
}
|
||||
|
||||
fn new_context() Context {
|
||||
return Context{
|
||||
cwd: os.getwd()
|
||||
commit_after: 'master'
|
||||
warmups: 4
|
||||
}
|
||||
}
|
||||
|
||||
fn (c Context) compare_versions() {
|
||||
// Input is validated at this point...
|
||||
// Cleanup artifacts from previous runs of this tool:
|
||||
scripting.chdir(c.workdir)
|
||||
scripting.run('rm -rf "$c.a" "$c.b" "$c.vc" ')
|
||||
// clone the VC source *just once per comparison*, and reuse it:
|
||||
scripting.run('git clone --quiet "$c.vc_repo_url" "$c.vc" ')
|
||||
println('Comparing V performance of commit $c.commit_before (before) vs commit $c.commit_after (after) ...')
|
||||
c.prepare_v(c.b, c.commit_before)
|
||||
c.prepare_v(c.a, c.commit_after)
|
||||
scripting.chdir(c.workdir)
|
||||
|
||||
if c.vflags.len > 0 {
|
||||
os.setenv('VFLAGS', c.vflags, true)
|
||||
}
|
||||
|
||||
// The first is the baseline, against which all the others will be compared.
|
||||
// It is the fastest, since hello_world.v has only a single println in it,
|
||||
mut perf_files := []string
|
||||
perf_files << c.compare_v_performance('source_hello', [
|
||||
'vprod @DEBUG@ -o source.c examples/hello_world.v',
|
||||
'vprod -o source.c examples/hello_world.v',
|
||||
'v @DEBUG@ -o source.c examples/hello_world.v',
|
||||
'v -o source.c examples/hello_world.v',
|
||||
])
|
||||
|
||||
perf_files << c.compare_v_performance('source_v', [
|
||||
'vprod @DEBUG@ -o source.c @COMPILER@',
|
||||
'vprod -o source.c @COMPILER@',
|
||||
'v @DEBUG@ -o source.c @COMPILER@',
|
||||
'v -o source.c @COMPILER@',
|
||||
])
|
||||
|
||||
perf_files << c.compare_v_performance('binary_hello', [
|
||||
'vprod -o hello examples/hello_world.v',
|
||||
'v -o hello examples/hello_world.v',
|
||||
])
|
||||
|
||||
perf_files << c.compare_v_performance('binary_v', [
|
||||
'vprod -o binary @COMPILER@',
|
||||
'v -o binary @COMPILER@',
|
||||
])
|
||||
|
||||
println('All performance files:')
|
||||
for f in perf_files {
|
||||
println(' $f')
|
||||
}
|
||||
}
|
||||
|
||||
fn (c &Context) prepare_v(cdir string, commit string) {
|
||||
mut cc := os.getenv('CC')
|
||||
if cc == '' {
|
||||
cc = 'cc'
|
||||
}
|
||||
mut vgit_context := vgit.VGitContext{
|
||||
cc: cc
|
||||
workdir: c.workdir
|
||||
commit_v: commit
|
||||
path_v: cdir
|
||||
path_vc: c.vc
|
||||
v_repo_url: c.v_repo_url
|
||||
vc_repo_url: c.vc_repo_url
|
||||
}
|
||||
vgit_context.compile_oldv_if_needed()
|
||||
scripting.chdir(cdir)
|
||||
println('Making a v compiler in $cdir')
|
||||
scripting.run('./v -cc ${cc} -o v $vgit_context.vvlocation')
|
||||
println('Making a vprod compiler in $cdir')
|
||||
scripting.run('./v -cc ${cc} -prod -o vprod $vgit_context.vvlocation')
|
||||
println('Stripping and compressing cv v and vprod binaries in $cdir')
|
||||
scripting.run('cp cv cv_stripped')
|
||||
scripting.run('cp v v_stripped')
|
||||
scripting.run('cp vprod vprod_stripped')
|
||||
scripting.run('strip *_stripped')
|
||||
scripting.run('cp cv_stripped cv_stripped_upxed')
|
||||
scripting.run('cp v_stripped v_stripped_upxed')
|
||||
scripting.run('cp vprod_stripped vprod_stripped_upxed')
|
||||
scripting.run('upx -qqq --lzma cv_stripped_upxed')
|
||||
scripting.run('upx -qqq --lzma v_stripped_upxed')
|
||||
scripting.run('upx -qqq --lzma vprod_stripped_upxed')
|
||||
scripting.show_sizes_of_files(['$cdir/cv', '$cdir/cv_stripped', '$cdir/cv_stripped_upxed'])
|
||||
scripting.show_sizes_of_files(['$cdir/v', '$cdir/v_stripped', '$cdir/v_stripped_upxed'])
|
||||
scripting.show_sizes_of_files(['$cdir/vprod', '$cdir/vprod_stripped', '$cdir/vprod_stripped_upxed'])
|
||||
vversion := scripting.run('$cdir/v --version')
|
||||
vcommit := scripting.run('git rev-parse --short --verify HEAD')
|
||||
println('V version is: ${vversion} , local source commit: ${vcommit}')
|
||||
if vgit_context.vvlocation == 'cmd/v' {
|
||||
println('Source lines of the compiler: ' + scripting.run('wc cmd/v/*.v vlib/compiler/*.v | tail -n -1'))
|
||||
} else if vgit_context.vvlocation == 'v.v' {
|
||||
println('Source lines of the compiler: ' + scripting.run('wc v.v vlib/compiler/*.v | tail -n -1'))
|
||||
}else{
|
||||
println('Source lines of the compiler: ' + scripting.run('wc compiler/*.v | tail -n -1'))
|
||||
}
|
||||
}
|
||||
|
||||
fn (c Context) compare_v_performance(label string, commands []string) string {
|
||||
println('---------------------------------------------------------------------------------')
|
||||
println('Compare v performance when doing the following commands ($label):')
|
||||
mut source_location_a := ''
|
||||
mut source_location_b := ''
|
||||
if os.exists('$c.a/cmd/v') {
|
||||
source_location_a = 'cmd/v'
|
||||
} else {
|
||||
source_location_a = if os.exists('$c.a/v.v') { 'v.v ' } else { 'compiler/ ' }
|
||||
}
|
||||
if os.exists('$c.b/cmd/v') {
|
||||
source_location_b = 'cmd/v'
|
||||
} else {
|
||||
source_location_b = if os.exists('$c.b/v.v') { 'v.v ' } else { 'compiler/ ' }
|
||||
}
|
||||
timestamp_a,_ := vgit.line_to_timestamp_and_commit(scripting.run('cd $c.a/ ; git rev-list -n1 --timestamp HEAD'))
|
||||
timestamp_b,_ := vgit.line_to_timestamp_and_commit(scripting.run('cd $c.b/ ; git rev-list -n1 --timestamp HEAD'))
|
||||
debug_option_a := if timestamp_a > 1570877641 { '-g ' } else { '-debug ' }
|
||||
debug_option_b := if timestamp_b > 1570877641 { '-g ' } else { '-debug ' }
|
||||
mut hyperfine_commands_arguments := []string
|
||||
for cmd in commands {
|
||||
println(cmd)
|
||||
}
|
||||
for cmd in commands {
|
||||
hyperfine_commands_arguments << " \'cd ${c.b:-34s} ; ./$cmd \' ".replace_each(['@COMPILER@', source_location_b, '@DEBUG@', debug_option_b])
|
||||
}
|
||||
for cmd in commands {
|
||||
hyperfine_commands_arguments << " \'cd ${c.a:-34s} ; ./$cmd \' ".replace_each(['@COMPILER@', source_location_a, '@DEBUG@', debug_option_a])
|
||||
}
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
cmd_stats_file := os.realpath([c.workdir, 'v_performance_stats_${label}.json'].join(os.path_separator))
|
||||
comparison_cmd := 'hyperfine $c.hyperfineopts ' + '--export-json ${cmd_stats_file} ' + '--time-unit millisecond ' + '--style full --warmup $c.warmups ' + hyperfine_commands_arguments.join(' ')
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
if c.verbose {
|
||||
println(comparison_cmd)
|
||||
}
|
||||
os.system(comparison_cmd)
|
||||
println('The detailed performance comparison report was saved to: $cmd_stats_file .')
|
||||
println('')
|
||||
return cmd_stats_file
|
||||
}
|
||||
|
||||
fn main() {
|
||||
scripting.used_tools_must_exist(['cp', 'rm', 'strip', 'make', 'git', 'upx', 'cc', 'wc', 'tail', 'hyperfine'])
|
||||
mut context := new_context()
|
||||
mut fp := flag.new_flag_parser(os.args)
|
||||
fp.application(filepath.filename(os.executable()))
|
||||
fp.version(tool_version)
|
||||
fp.description(tool_description)
|
||||
fp.arguments_description('COMMIT_BEFORE [COMMIT_AFTER]')
|
||||
fp.skip_executable()
|
||||
fp.limit_free_args(1, 2)
|
||||
|
||||
context.vflags = fp.string('vflags', '', 'Additional options to pass to the v commands, for example "-cc tcc"')
|
||||
context.hyperfineopts = fp.string('hyperfine_options', '',
|
||||
'Additional options passed to hyperfine.
|
||||
${flag.SPACE}For example on linux, you may want to pass:
|
||||
${flag.SPACE}--hyperfine_options "--prepare \'sync; echo 3 | sudo tee /proc/sys/vm/drop_caches\'"
|
||||
')
|
||||
commits := vgit.add_common_tool_options(mut context, mut fp)
|
||||
context.commit_before = commits[0]
|
||||
if commits.len > 1 {
|
||||
context.commit_after = commits[1]
|
||||
}
|
||||
context.b = vgit.normalized_workpath_for_commit(context.workdir, context.commit_before)
|
||||
context.a = vgit.normalized_workpath_for_commit(context.workdir, context.commit_after)
|
||||
context.vc = vgit.normalized_workpath_for_commit(context.workdir, 'vc')
|
||||
if !os.is_dir(context.workdir) {
|
||||
msg := 'Work folder: ' + context.workdir + ' , does not exist.'
|
||||
eprintln(msg)
|
||||
exit(2)
|
||||
}
|
||||
|
||||
context.compare_versions()
|
||||
}
|
||||
28
cmd/tools/preludes/README.md
Normal file
28
cmd/tools/preludes/README.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# V preludes:
|
||||
|
||||
The cmd/tools/preludes/ contains small v code snippets, that V uses when
|
||||
compiling certain v programs. V adds the files below automatically itself.
|
||||
Each file is used in different situations (see below).
|
||||
|
||||
NB: preludes are *NOT* intended to be used by user programs/modules.
|
||||
The folder cmd/tools/preludes/ is *NOT* a v module.
|
||||
|
||||
## Details:
|
||||
|
||||
### cmd/tools/preludes/live_main.v
|
||||
Used when compiling live programs. This file is used by the main executable
|
||||
live program, that starts the file change monitoring thread. Each live program
|
||||
needs module `os` and module `time`, in order for the background file change
|
||||
monitoring thread to work properly.
|
||||
|
||||
### cmd/tools/preludes/live_shared.v
|
||||
Used when compiling live programs, for the shared library portion of the live
|
||||
programs, that is reloaded each time the code is changed.
|
||||
|
||||
### cmd/tools/preludes/tests_assertions.v
|
||||
Used when compiling `_test.v` programs.
|
||||
It specifies how failed assertions will look.
|
||||
|
||||
### cmd/tools/preludes/tests_with_stats.v
|
||||
Used when compiling `_test.v` programs with -stats option.
|
||||
It specifies how the result will appear ('assert' vs 'asserts' and so on).
|
||||
9
cmd/tools/preludes/live_main.v
Normal file
9
cmd/tools/preludes/live_main.v
Normal file
@@ -0,0 +1,9 @@
|
||||
module main
|
||||
|
||||
import os
|
||||
import time
|
||||
|
||||
const (
|
||||
os_used = os.MAX_PATH
|
||||
time_used = time.now()
|
||||
)
|
||||
9
cmd/tools/preludes/live_shared.v
Normal file
9
cmd/tools/preludes/live_shared.v
Normal file
@@ -0,0 +1,9 @@
|
||||
module main
|
||||
|
||||
import os
|
||||
import time
|
||||
|
||||
const (
|
||||
os_used = os.MAX_PATH
|
||||
time_used = time.now()
|
||||
)
|
||||
36
cmd/tools/preludes/tests_assertions.v
Normal file
36
cmd/tools/preludes/tests_assertions.v
Normal file
@@ -0,0 +1,36 @@
|
||||
module main
|
||||
|
||||
import os
|
||||
import term
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// / This file will get compiled as part of the main program,
|
||||
// / for a _test.v file.
|
||||
// / The methods defined here are called back by the test program's
|
||||
// / assert statements, on each success/fail. The goal is to make
|
||||
// / customizing the look & feel of the assertions results easier,
|
||||
// / since it is done in normal V code, instead of in embedded C ...
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
fn cb_assertion_failed(filename string, line int, sourceline string, funcname string) {
|
||||
color_on := term.can_show_color_on_stderr()
|
||||
use_relative_paths := match os.getenv('VERROR_PATHS') {
|
||||
'absolute'{
|
||||
false
|
||||
}
|
||||
else {
|
||||
true}
|
||||
}
|
||||
final_filename := if use_relative_paths { filename } else { os.realpath(filename) }
|
||||
final_funcname := funcname.replace('main__', '').replace('__', '.')
|
||||
mut fail_message := 'FAILED assertion'
|
||||
if color_on {
|
||||
fail_message = term.bold(term.red(fail_message))
|
||||
}
|
||||
eprintln('$final_filename:$line: $fail_message')
|
||||
eprintln('Function: $final_funcname')
|
||||
eprintln('Source : $sourceline')
|
||||
}
|
||||
|
||||
fn cb_assertion_ok(filename string, line int, sourceline string, funcname string) {
|
||||
// do nothing for now on an OK assertion
|
||||
// println('OK ${line:5d}|$sourceline ')
|
||||
}
|
||||
104
cmd/tools/preludes/tests_with_stats.v
Normal file
104
cmd/tools/preludes/tests_with_stats.v
Normal file
@@ -0,0 +1,104 @@
|
||||
module main
|
||||
// /////////////////////////////////////////////////////////////////////
|
||||
// / This file will get compiled as a part of the same module,
|
||||
// / in which a given _test.v file is, when v is given -stats argument
|
||||
// / The methods defined here are called back by the test program's
|
||||
// / main function, generated by compiler/main.v so that customizing the
|
||||
// / look & feel of the results is easy, since it is done in normal V
|
||||
// / code, instead of in embedded C ...
|
||||
// /////////////////////////////////////////////////////////////////////
|
||||
import (
|
||||
filepath
|
||||
benchmark
|
||||
)
|
||||
|
||||
const (
|
||||
INNER_INDENT = ' '
|
||||
)
|
||||
|
||||
struct BenchedTests {
|
||||
mut:
|
||||
oks int
|
||||
fails int
|
||||
test_suit_file string
|
||||
step_func_name string
|
||||
bench benchmark.Benchmark
|
||||
}
|
||||
|
||||
// ///////////////////////////////////////////////////////////////////
|
||||
// Called at the start of the test program produced by `v -stats file_test.v`
|
||||
fn start_testing(total_number_of_tests int, vfilename string) BenchedTests {
|
||||
mut b := BenchedTests{
|
||||
bench: benchmark.new_benchmark()
|
||||
}
|
||||
b.bench.set_total_expected_steps(total_number_of_tests)
|
||||
b.test_suit_file = vfilename
|
||||
println('running tests in: $b.test_suit_file')
|
||||
return b
|
||||
}
|
||||
|
||||
// Called before each test_ function, defined in file_test.v
|
||||
fn (b mut BenchedTests) testing_step_start(stepfunc string) {
|
||||
b.step_func_name = stepfunc.replace('main__', '').replace('__', '.')
|
||||
b.oks = C.g_test_oks
|
||||
b.fails = C.g_test_fails
|
||||
b.bench.step()
|
||||
}
|
||||
|
||||
// Called after each test_ function, defined in file_test.v
|
||||
fn (b mut BenchedTests) testing_step_end() {
|
||||
ok_diff := C.g_test_oks - b.oks
|
||||
fail_diff := C.g_test_fails - b.fails
|
||||
// ////////////////////////////////////////////////////////////////
|
||||
if ok_diff == 0 && fail_diff == 0 {
|
||||
b.bench.neither_fail_nor_ok()
|
||||
println(INNER_INDENT + b.bench.step_message_ok('NO asserts | ') + b.fn_name())
|
||||
return
|
||||
}
|
||||
// ////////////////////////////////////////////////////////////////
|
||||
if ok_diff > 0 {
|
||||
b.bench.ok_many(ok_diff)
|
||||
}
|
||||
if fail_diff > 0 {
|
||||
b.bench.fail_many(fail_diff)
|
||||
}
|
||||
// ////////////////////////////////////////////////////////////////
|
||||
if ok_diff > 0 && fail_diff == 0 {
|
||||
println(INNER_INDENT + b.bench.step_message_ok(nasserts(ok_diff)) + b.fn_name())
|
||||
return
|
||||
}
|
||||
if fail_diff > 0 {
|
||||
println(INNER_INDENT + b.bench.step_message_fail(nasserts(fail_diff)) + b.fn_name())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
fn (b &BenchedTests) fn_name() string {
|
||||
return b.step_func_name + '()'
|
||||
}
|
||||
|
||||
// Called at the end of the test program produced by `v -stats file_test.v`
|
||||
fn (b mut BenchedTests) end_testing() {
|
||||
b.bench.stop()
|
||||
println(INNER_INDENT + b.bench.total_message('running V tests in "' + filepath.filename(b.test_suit_file) + '"'))
|
||||
}
|
||||
|
||||
// ///////////////////////////////////////////////////////////////////
|
||||
fn nasserts(n int) string {
|
||||
if n == 0 {
|
||||
return '${n:2d} asserts | '
|
||||
}
|
||||
if n == 1 {
|
||||
return '${n:2d} assert | '
|
||||
}
|
||||
if n < 10 {
|
||||
return '${n:2d} asserts | '
|
||||
}
|
||||
if n < 100 {
|
||||
return '${n:3d} asserts | '
|
||||
}
|
||||
if n < 1000 {
|
||||
return '${n:4d} asserts | '
|
||||
}
|
||||
return '${n:5d} asserts | '
|
||||
}
|
||||
95
cmd/tools/vbin2v.v
Normal file
95
cmd/tools/vbin2v.v
Normal file
@@ -0,0 +1,95 @@
|
||||
module main
|
||||
|
||||
import os
|
||||
import flag
|
||||
import filepath
|
||||
|
||||
const (
|
||||
tool_version = '0.0.3'
|
||||
tool_description = 'Converts a list of arbitrary files into a single v module file.'
|
||||
)
|
||||
|
||||
struct Context {
|
||||
mut:
|
||||
files []string
|
||||
prefix string
|
||||
show_help bool
|
||||
module_name string
|
||||
}
|
||||
|
||||
fn (context Context) header() {
|
||||
println('module ${context.module_name}')
|
||||
println('')
|
||||
allfiles := context.files.join(' ')
|
||||
mut options := []string
|
||||
if context.prefix.len > 0 {
|
||||
options << '-p ${context.prefix}'
|
||||
}
|
||||
if context.module_name.len > 0 {
|
||||
options << '-m ${context.module_name}'
|
||||
}
|
||||
soptions := options.join(' ')
|
||||
println('// File generated by:')
|
||||
println('// v bin2v ${allfiles} ${soptions}')
|
||||
println('// Please, do not edit this file.')
|
||||
println('// Your changes may be overwritten.')
|
||||
println('')
|
||||
println('const (')
|
||||
}
|
||||
|
||||
fn (context Context) footer() {
|
||||
println(')')
|
||||
}
|
||||
|
||||
fn (context Context) file2v(file string) {
|
||||
fname := filepath.filename(file)
|
||||
fname_no_dots := fname.replace('.', '_')
|
||||
byte_name := '${context.prefix}${fname_no_dots}'
|
||||
fbytes := os.read_bytes(file) or {
|
||||
eprintln('Error: $err')
|
||||
return
|
||||
}
|
||||
fbyte := fbytes[0]
|
||||
println(' ${byte_name}_len = ${fbytes.len}')
|
||||
print(' ${byte_name} = [ byte(${fbyte}), \n ')
|
||||
for i := 1; i < fbytes.len; i++ {
|
||||
b := int(fbytes[i]).str()
|
||||
print('${b:4s}, ')
|
||||
if 0 == i % 16 {
|
||||
print('\n ')
|
||||
}
|
||||
}
|
||||
println('\n]!!')
|
||||
println('')
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut context := Context{}
|
||||
mut fp := flag.new_flag_parser(os.args[1..])
|
||||
fp.application('v bin2v')
|
||||
fp.version(tool_version)
|
||||
fp.description(tool_description)
|
||||
fp.arguments_description('FILE [FILE]...')
|
||||
context.show_help = fp.bool_('help', `h`, false, 'Show this help screen.')
|
||||
context.module_name = fp.string_('module', `m`, 'binary', 'Name of the generated module.\n')
|
||||
context.prefix = fp.string_('prefix', `p`, '', 'A prefix put before each resource name.\n')
|
||||
if (context.show_help) {
|
||||
println(fp.usage())
|
||||
exit(0)
|
||||
}
|
||||
files := fp.finalize() or {
|
||||
eprintln('Error: ' + err)
|
||||
exit(1)
|
||||
}
|
||||
real_files := files.filter(it != 'bin2v')
|
||||
if real_files.len == 0 {
|
||||
println(fp.usage())
|
||||
exit(0)
|
||||
}
|
||||
context.files = real_files
|
||||
context.header()
|
||||
for file in real_files {
|
||||
context.file2v(file)
|
||||
}
|
||||
context.footer()
|
||||
}
|
||||
22
cmd/tools/vbuild-examples.v
Normal file
22
cmd/tools/vbuild-examples.v
Normal file
@@ -0,0 +1,22 @@
|
||||
module main
|
||||
|
||||
import (
|
||||
os
|
||||
testing
|
||||
filepath
|
||||
)
|
||||
|
||||
fn main() {
|
||||
args := os.args
|
||||
args_string := args[1..].join(' ')
|
||||
params := args_string.all_before('build-examples')
|
||||
|
||||
if testing.v_build_failing(params, 'examples'){
|
||||
exit(1)
|
||||
}
|
||||
|
||||
if testing.v_build_failing(params + '-live', filepath.join( 'examples', 'hot_reload')){
|
||||
exit(1)
|
||||
}
|
||||
|
||||
}
|
||||
14
cmd/tools/vbuild-tools.v
Normal file
14
cmd/tools/vbuild-tools.v
Normal file
@@ -0,0 +1,14 @@
|
||||
module main
|
||||
|
||||
import (
|
||||
os
|
||||
testing
|
||||
)
|
||||
|
||||
fn main() {
|
||||
args := os.args
|
||||
args_string := args[1..].join(' ')
|
||||
if testing.v_build_failing(args_string.all_before('build-tools'), 'cmd/tools') {
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
9
cmd/tools/vbuild-vbinaries.v
Normal file
9
cmd/tools/vbuild-vbinaries.v
Normal file
@@ -0,0 +1,9 @@
|
||||
module main
|
||||
|
||||
import testing
|
||||
|
||||
fn main() {
|
||||
if testing.building_any_v_binaries_failed() {
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
55
cmd/tools/vcreate.v
Normal file
55
cmd/tools/vcreate.v
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2019-2020 Alexander Medvednikov. 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
|
||||
)
|
||||
|
||||
struct Create {
|
||||
mut:
|
||||
name string
|
||||
description string
|
||||
}
|
||||
|
||||
fn cerror(e string){
|
||||
eprintln('\nerror: $e')
|
||||
}
|
||||
|
||||
fn (c Create)write_vmod() {
|
||||
mut vmod := os.create('${c.name}/v.mod') or { cerror(err) exit(1) }
|
||||
mut vmod_content := []string
|
||||
vmod_content << '#V Project#\n'
|
||||
vmod_content << 'Module {'
|
||||
vmod_content << ' name: \'${c.name}\','
|
||||
vmod_content << ' description: \'${c.description}\','
|
||||
vmod_content << ' dependencies: []'
|
||||
vmod_content << '}'
|
||||
vmod.write(vmod_content.join('\n'))
|
||||
}
|
||||
|
||||
fn (c Create)write_main() {
|
||||
mut main := os.create('${c.name}/${c.name}.v') or { cerror(err) exit(2) }
|
||||
mut main_content := []string
|
||||
main_content << 'module main\n'
|
||||
main_content << 'fn main() {'
|
||||
main_content << ' println(\'Hello World !\')'
|
||||
main_content << '}'
|
||||
main.write(main_content.join('\n'))
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut c := Create{}
|
||||
print('Choose your project name: ')
|
||||
c.name = os.get_line()
|
||||
print('Choose your project description: ')
|
||||
c.description = os.get_line()
|
||||
println('Initialising ...')
|
||||
if (os.is_dir(c.name)) { cerror('folder already exists') exit(3) }
|
||||
os.mkdir(c.name) or { panic(err) }
|
||||
c.write_vmod()
|
||||
c.write_main()
|
||||
println('Complete !')
|
||||
}
|
||||
414
cmd/tools/vfmt.v
Normal file
414
cmd/tools/vfmt.v
Normal file
@@ -0,0 +1,414 @@
|
||||
// Copyright (c) 2019-2020 Alexander Medvednikov. 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
|
||||
os.cmdline
|
||||
filepath
|
||||
compiler
|
||||
v.pref
|
||||
)
|
||||
|
||||
struct FormatOptions {
|
||||
is_l bool
|
||||
is_c bool
|
||||
is_w bool
|
||||
is_diff bool
|
||||
is_verbose bool
|
||||
is_all bool
|
||||
is_worker bool
|
||||
is_debug bool
|
||||
is_noerror bool
|
||||
}
|
||||
|
||||
const (
|
||||
platform_and_file_extensions = [['windows', '_win.v', '_windows.v'],
|
||||
['linux', '_lin.v', '_linux.v', '_nix.v'],
|
||||
['macos', '_mac.v', '_darwin.v'],
|
||||
['freebsd', '_bsd.v', '_freebsd.v'],
|
||||
['solaris', '_solaris.v'],
|
||||
['haiku', '_haiku.v'],
|
||||
]
|
||||
FORMATTED_FILE_TOKEN = '\@\@\@' + 'FORMATTED_FILE: '
|
||||
)
|
||||
|
||||
fn main() {
|
||||
toolexe := os.executable()
|
||||
compiler.set_vroot_folder(filepath.dir(filepath.dir(filepath.dir(toolexe))))
|
||||
args := join_flags_and_argument()
|
||||
foptions := FormatOptions{
|
||||
is_c: '-c' in args
|
||||
is_l: '-l' in args
|
||||
is_w: '-w' in args
|
||||
is_diff: '-diff' in args
|
||||
is_verbose: '-verbose' in args || '--verbose' in args
|
||||
is_all: '-all' in args || '--all' in args
|
||||
is_worker: '-worker' in args
|
||||
is_debug: '-debug' in args
|
||||
is_noerror: '-noerror' in args
|
||||
}
|
||||
if foptions.is_verbose {
|
||||
eprintln('vfmt foptions: $foptions')
|
||||
}
|
||||
if foptions.is_worker {
|
||||
// -worker should be added by a parent vfmt process.
|
||||
// We launch a sub process for each file because
|
||||
// the v compiler can do an early exit if it detects
|
||||
// a syntax error, but we want to process ALL passed
|
||||
// files if possible.
|
||||
foptions.format_file(cmdline.option(args, '-worker', ''))
|
||||
exit(0)
|
||||
}
|
||||
// we are NOT a worker at this stage, i.e. we are a parent vfmt process
|
||||
possible_files := cmdline.only_non_options(cmdline.after(args, ['fmt']))
|
||||
if foptions.is_verbose {
|
||||
eprintln('vfmt toolexe: $toolexe')
|
||||
eprintln('vfmt args: ' + os.args.str())
|
||||
eprintln('vfmt env_vflags_and_os_args: ' + args.str())
|
||||
eprintln('vfmt possible_files: ' + possible_files.str())
|
||||
}
|
||||
mut files := []string
|
||||
for file in possible_files {
|
||||
if !os.exists(file) {
|
||||
compiler.verror('"$file" does not exist.')
|
||||
}
|
||||
if !file.ends_with('.v') {
|
||||
compiler.verror('v fmt can only be used on .v files.\nOffending file: "$file" .')
|
||||
}
|
||||
files << file
|
||||
}
|
||||
if files.len == 0 {
|
||||
usage()
|
||||
exit(0)
|
||||
}
|
||||
mut cli_args_no_files := []string
|
||||
for a in os.args {
|
||||
if !a in files {
|
||||
cli_args_no_files << a
|
||||
}
|
||||
}
|
||||
mut errors := 0
|
||||
for file in files {
|
||||
fpath := os.realpath(file)
|
||||
mut worker_command_array := cli_args_no_files.clone()
|
||||
worker_command_array << ['-worker', fpath]
|
||||
worker_cmd := worker_command_array.join(' ')
|
||||
if foptions.is_verbose {
|
||||
eprintln('vfmt worker_cmd: $worker_cmd')
|
||||
}
|
||||
worker_result := os.exec(worker_cmd) or {
|
||||
errors++
|
||||
continue
|
||||
}
|
||||
if worker_result.exit_code != 0 {
|
||||
eprintln(worker_result.output)
|
||||
if worker_result.exit_code == 1 {
|
||||
eprintln('vfmt error while formatting file: $file .')
|
||||
}
|
||||
errors++
|
||||
continue
|
||||
}
|
||||
if worker_result.output.len > 0 {
|
||||
if worker_result.output.contains(FORMATTED_FILE_TOKEN) {
|
||||
wresult := worker_result.output.split(FORMATTED_FILE_TOKEN)
|
||||
formatted_warn_errs := wresult[0]
|
||||
formatted_file_path := wresult[1]
|
||||
foptions.post_process_file(fpath, formatted_file_path)
|
||||
if formatted_warn_errs.len > 0 {
|
||||
eprintln(formatted_warn_errs)
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
errors++
|
||||
}
|
||||
if errors > 0 {
|
||||
eprintln('Encountered a total of: ${errors} errors.')
|
||||
if foptions.is_noerror {
|
||||
exit(0)
|
||||
}
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
fn (foptions &FormatOptions) format_file(file string) {
|
||||
tmpfolder := os.tmpdir()
|
||||
mut compiler_params := &pref.Preferences{}
|
||||
target_os := file_to_target_os(file)
|
||||
if target_os != '' {
|
||||
compiler_params.os = pref.os_from_string(target_os)
|
||||
}
|
||||
mut cfile := file
|
||||
mut mod_folder_parent := tmpfolder
|
||||
is_test_file := file.ends_with('_test.v')
|
||||
mod_name,is_module_file := file_to_mod_name_and_is_module_file(file)
|
||||
use_tmp_main_program := is_module_file && !is_test_file
|
||||
mod_folder := filepath.basedir(file)
|
||||
if use_tmp_main_program {
|
||||
// TODO: remove the need for this
|
||||
// This makes a small program that imports the module,
|
||||
// so that the module files will get processed by the
|
||||
// vfmt implementation.
|
||||
mod_folder_parent = filepath.basedir(mod_folder)
|
||||
mut main_program_content := if mod_name == 'builtin' || mod_name == 'main' { 'fn main(){}\n' } else { 'import ${mod_name}\n' + 'fn main(){}\n' }
|
||||
main_program_file := filepath.join(tmpfolder,'vfmt_tmp_${mod_name}_program.v')
|
||||
if os.exists(main_program_file) {
|
||||
os.rm(main_program_file)
|
||||
}
|
||||
os.write_file(main_program_file, main_program_content)
|
||||
cfile = main_program_file
|
||||
compiler_params.user_mod_path = mod_folder_parent
|
||||
}
|
||||
if !is_test_file && mod_name == 'main' {
|
||||
// NB: here, file is guaranted to be a main. We do not know however
|
||||
// whether it is a standalone v program, or is it a part of a bigger
|
||||
// project, like vorum or vid.
|
||||
cfile = get_compile_name_of_potential_v_project(cfile)
|
||||
}
|
||||
compiler_params.path = cfile
|
||||
compiler_params.mod = mod_name
|
||||
compiler_params.is_test = is_test_file
|
||||
compiler_params.is_script = file.ends_with('.v') || file.ends_with('.vsh')
|
||||
if foptions.is_verbose {
|
||||
eprintln('vfmt format_file: file: $file')
|
||||
eprintln('vfmt format_file: cfile: $cfile')
|
||||
eprintln('vfmt format_file: is_test_file: $is_test_file')
|
||||
eprintln('vfmt format_file: is_module_file: $is_module_file')
|
||||
eprintln('vfmt format_file: mod_name: $mod_name')
|
||||
eprintln('vfmt format_file: mod_folder: $mod_folder')
|
||||
eprintln('vfmt format_file: mod_folder_parent: $mod_folder_parent')
|
||||
eprintln('vfmt format_file: use_tmp_main_program: $use_tmp_main_program')
|
||||
eprintln('vfmt format_file: compiler_params: ')
|
||||
print_compiler_options( compiler_params )
|
||||
eprintln('-------------------------------------------')
|
||||
}
|
||||
compiler_params.fill_with_defaults()
|
||||
if foptions.is_verbose {
|
||||
eprintln('vfmt format_file: compiler_params: AFTER fill_with_defaults() ')
|
||||
print_compiler_options( compiler_params )
|
||||
}
|
||||
formatted_file_path := foptions.compile_file(file, compiler_params)
|
||||
if use_tmp_main_program {
|
||||
if !foptions.is_debug {
|
||||
os.rm(cfile)
|
||||
}
|
||||
}
|
||||
eprintln('${FORMATTED_FILE_TOKEN}${formatted_file_path}')
|
||||
}
|
||||
|
||||
fn print_compiler_options( compiler_params &pref.Preferences ) {
|
||||
eprintln(' os: ' + compiler_params.os.str() )
|
||||
eprintln(' ccompiler: $compiler_params.ccompiler' )
|
||||
eprintln(' mod: $compiler_params.mod ')
|
||||
eprintln(' path: $compiler_params.path ')
|
||||
eprintln(' out_name: $compiler_params.out_name ')
|
||||
eprintln(' vroot: $compiler_params.vroot ')
|
||||
eprintln(' vpath: $compiler_params.vpath ')
|
||||
eprintln(' vlib_path: $compiler_params.vlib_path ')
|
||||
eprintln(' out_name: $compiler_params.out_name ')
|
||||
eprintln(' umpath: $compiler_params.user_mod_path ')
|
||||
eprintln(' cflags: $compiler_params.cflags ')
|
||||
eprintln(' is_test: $compiler_params.is_test ')
|
||||
eprintln(' is_script: $compiler_params.is_script ')
|
||||
}
|
||||
|
||||
fn (foptions &FormatOptions) post_process_file(file string, formatted_file_path string) {
|
||||
if formatted_file_path.len == 0 {
|
||||
return
|
||||
}
|
||||
if foptions.is_diff {
|
||||
diff_cmd := find_working_diff_command() or {
|
||||
eprintln('No working "diff" CLI command found.')
|
||||
return
|
||||
}
|
||||
os.system('$diff_cmd --minimal --text --unified=2 --show-function-line="fn " "$file" "$formatted_file_path" ')
|
||||
return
|
||||
}
|
||||
fc := os.read_file(file) or {
|
||||
eprintln('File $file could not be read')
|
||||
return
|
||||
}
|
||||
formatted_fc := os.read_file(formatted_file_path) or {
|
||||
eprintln('File $formatted_file_path could not be read')
|
||||
return
|
||||
}
|
||||
is_formatted_different := fc != formatted_fc
|
||||
if foptions.is_c {
|
||||
if is_formatted_different {
|
||||
eprintln('File is not formatted: $file')
|
||||
exit(2)
|
||||
}
|
||||
return
|
||||
}
|
||||
if foptions.is_l {
|
||||
if is_formatted_different {
|
||||
eprintln('File needs formatting: $file')
|
||||
}
|
||||
return
|
||||
}
|
||||
if foptions.is_w {
|
||||
if is_formatted_different {
|
||||
os.mv_by_cp(formatted_file_path, file) or {
|
||||
panic(err)
|
||||
}
|
||||
eprintln('Reformatted file: $file')
|
||||
}
|
||||
else {
|
||||
eprintln('Already formatted file: $file')
|
||||
}
|
||||
return
|
||||
}
|
||||
print(formatted_fc)
|
||||
}
|
||||
|
||||
fn usage() {
|
||||
print('Usage: cmd/tools/vfmt [flags] fmt path_to_source.v [path_to_other_source.v]
|
||||
Formats the given V source files, and prints their formatted source to stdout.
|
||||
Options:
|
||||
-c check if file is already formatted.
|
||||
If it is not, print filepath, and exit with code 2.
|
||||
-diff display only diffs between the formatted source and the original source.
|
||||
-l list files whose formatting differs from vfmt.
|
||||
-w write result to (source) file(s) instead of to stdout.
|
||||
')
|
||||
}
|
||||
|
||||
fn find_working_diff_command() ?string {
|
||||
for diffcmd in ['colordiff', 'diff', 'colordiff.exe', 'diff.exe'] {
|
||||
p := os.exec('$diffcmd --version') or {
|
||||
continue
|
||||
}
|
||||
if p.exit_code == 0 {
|
||||
return diffcmd
|
||||
}
|
||||
}
|
||||
return error('no working diff command found')
|
||||
}
|
||||
|
||||
fn (foptions &FormatOptions) compile_file(file string, compiler_params &pref.Preferences) string {
|
||||
if foptions.is_verbose {
|
||||
eprintln('> new_v_compiler_with_args file: $file')
|
||||
eprintln('> new_v_compiler_with_args compiler_params:')
|
||||
print_compiler_options( compiler_params )
|
||||
}
|
||||
mut v := compiler.new_v(compiler_params)
|
||||
v.v_fmt_file = file
|
||||
if foptions.is_all {
|
||||
v.v_fmt_all = true
|
||||
}
|
||||
v.compile()
|
||||
return v.v_fmt_file_result
|
||||
}
|
||||
|
||||
pub fn (f FormatOptions) str() string {
|
||||
return 'FormatOptions{ ' + ' is_l: $f.is_l' + ' is_w: $f.is_w' + ' is_diff: $f.is_diff' + ' is_verbose: $f.is_verbose' + ' is_all: $f.is_all' + ' is_worker: $f.is_worker' + ' is_debug: $f.is_debug' + ' }'
|
||||
}
|
||||
|
||||
fn file_to_target_os(file string) string {
|
||||
for extensions in platform_and_file_extensions {
|
||||
for ext in extensions {
|
||||
if file.ends_with(ext) {
|
||||
return extensions[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
fn file_to_mod_name_and_is_module_file(file string) (string,bool) {
|
||||
mut mod_name := 'main'
|
||||
mut is_module_file := false
|
||||
flines := read_source_lines(file) or {
|
||||
return mod_name,is_module_file
|
||||
}
|
||||
for fline in flines {
|
||||
line := fline.trim_space()
|
||||
if line.starts_with('module ') {
|
||||
if !line.starts_with('module main') {
|
||||
is_module_file = true
|
||||
mod_name = line.replace('module ', ' ').trim_space()
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return mod_name,is_module_file
|
||||
}
|
||||
|
||||
fn read_source_lines(file string) ?[]string {
|
||||
raw_fcontent := os.read_file(file) or {
|
||||
return error('can not read $file')
|
||||
}
|
||||
fcontent := raw_fcontent.replace('\r\n', '\n')
|
||||
return fcontent.split('\n')
|
||||
}
|
||||
|
||||
fn get_compile_name_of_potential_v_project(file string) string {
|
||||
// This function get_compile_name_of_potential_v_project returns:
|
||||
// a) the file's folder, if file is part of a v project
|
||||
// b) the file itself, if the file is a standalone v program
|
||||
pfolder := os.realpath(filepath.dir(file))
|
||||
// a .v project has many 'module main' files in one folder
|
||||
// if there is only one .v file, then it must be a standalone
|
||||
all_files_in_pfolder := os.ls(pfolder) or {
|
||||
panic(err)
|
||||
}
|
||||
mut vfiles := []string
|
||||
for f in all_files_in_pfolder {
|
||||
vf := filepath.join(pfolder,f)
|
||||
if f.starts_with('.') || !f.ends_with('.v') || os.is_dir(vf) {
|
||||
continue
|
||||
}
|
||||
vfiles << vf
|
||||
}
|
||||
if vfiles.len == 1 {
|
||||
return file
|
||||
}
|
||||
// /////////////////////////////////////////////////////////////
|
||||
// At this point, we know there are many .v files in the folder
|
||||
// We will have to read them all, and if there are more than one
|
||||
// containing `fn main` then the folder contains multiple standalone
|
||||
// v programs. If only one contains `fn main` then the folder is
|
||||
// a project folder, that should be compiled with `v pfolder`.
|
||||
mut main_fns := 0
|
||||
for f in vfiles {
|
||||
slines := read_source_lines(f) or {
|
||||
panic(err)
|
||||
}
|
||||
for line in slines {
|
||||
if line.contains('fn main()') {
|
||||
main_fns++
|
||||
if main_fns > 1 {
|
||||
return file
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return pfolder
|
||||
}
|
||||
|
||||
//TODO Move join_flags_and_argument() and non_empty() into `cmd/internal` when v.mod work correctly
|
||||
//to prevent code duplication with `cmd/v` (cmd/v/flag.v)
|
||||
fn join_flags_and_argument() []string {
|
||||
vosargs := os.getenv('VOSARGS')
|
||||
if vosargs != '' {
|
||||
return non_empty(vosargs.split(' '))
|
||||
}
|
||||
|
||||
mut args := []string
|
||||
vflags := os.getenv('VFLAGS')
|
||||
if vflags != '' {
|
||||
args << os.args[0]
|
||||
args << vflags.split(' ')
|
||||
if os.args.len > 1 {
|
||||
args << os.args[1..]
|
||||
}
|
||||
return non_empty(args)
|
||||
}
|
||||
|
||||
return non_empty(os.args)
|
||||
}
|
||||
fn non_empty(arg []string) []string {
|
||||
return arg.filter(it != '')
|
||||
}
|
||||
86
cmd/tools/vnames.v
Normal file
86
cmd/tools/vnames.v
Normal file
@@ -0,0 +1,86 @@
|
||||
module main
|
||||
|
||||
import (
|
||||
os
|
||||
flag
|
||||
strings
|
||||
filepath
|
||||
compiler
|
||||
v.pref
|
||||
)
|
||||
|
||||
const (
|
||||
tool_version = '0.0.1'
|
||||
tool_description = ' Extracts the function names declared in a v file.'
|
||||
)
|
||||
|
||||
fn f_to_string(fmod string, f compiler.Fn) ?string {
|
||||
svisibility := if f.is_public {
|
||||
'public'
|
||||
}else{
|
||||
'private'
|
||||
}
|
||||
if fmod != f.v_fn_module() { return none }
|
||||
if fmod == 'builtin' {
|
||||
return '$svisibility\t' + f.v_fn_name()
|
||||
}
|
||||
return '$svisibility\t' + f.v_fn_module() + '.' + f.v_fn_name()
|
||||
}
|
||||
|
||||
fn analyze_v_file(file string) {
|
||||
println('')
|
||||
hash := strings.repeat(`#`, (76 - file.len) / 2)
|
||||
println('$hash $file $hash')
|
||||
|
||||
// main work:
|
||||
mut pref := &pref.Preferences{
|
||||
path: file
|
||||
}
|
||||
pref.fill_with_defaults()
|
||||
mut v := compiler.new_v(pref)
|
||||
v.add_v_files_to_compile()
|
||||
for f in v.files { v.parse(f, .decl) }
|
||||
fi := v.get_file_parser_index( file ) or { panic(err) }
|
||||
fmod := v.parsers[fi].mod
|
||||
|
||||
// output:
|
||||
mut fns :=[]string
|
||||
for _, f in v.table.fns {
|
||||
fname := f_to_string(fmod, f) or { continue }
|
||||
fns << fname
|
||||
}
|
||||
fns.sort()
|
||||
for f in fns { println(f) }
|
||||
|
||||
}
|
||||
|
||||
fn main(){
|
||||
toolexe := os.executable()
|
||||
compiler.set_vroot_folder(filepath.dir(filepath.dir(filepath.dir(toolexe))))
|
||||
|
||||
mut fp := flag.new_flag_parser(os.args)
|
||||
fp.application(filepath.filename(toolexe))
|
||||
fp.version( tool_version )
|
||||
fp.description( tool_description )
|
||||
fp.arguments_description('FILE.v/FOLDER [FILE.v/FOLDER]...')
|
||||
fp.limit_free_args_to_at_least(1)
|
||||
fp.skip_executable()
|
||||
show_help:=fp.bool_('help', `h`, false, 'Show this help screen\n')
|
||||
if( show_help ){
|
||||
println( fp.usage() )
|
||||
exit(0)
|
||||
}
|
||||
|
||||
mut files := []string
|
||||
locations := fp.finalize() or { eprintln('Error: ' + err) exit(1) }
|
||||
for xloc in locations {
|
||||
loc := os.realpath(xloc)
|
||||
xfiles := if os.is_dir(loc){ os.walk_ext(loc,'.v') } else { [loc] }
|
||||
filtered_files := xfiles.filter(!it.ends_with('_js.v'))
|
||||
files << filtered_files
|
||||
}
|
||||
|
||||
for file in files {
|
||||
analyze_v_file(file)
|
||||
}
|
||||
}
|
||||
496
cmd/tools/vpm.v
Normal file
496
cmd/tools/vpm.v
Normal file
@@ -0,0 +1,496 @@
|
||||
module main
|
||||
|
||||
import (
|
||||
net.http
|
||||
os
|
||||
os.cmdline
|
||||
json
|
||||
filepath
|
||||
)
|
||||
|
||||
const (
|
||||
default_vpm_server_urls = ['https://vpm.best', 'https://vpm.vlang.io']
|
||||
valid_vpm_commands = ['help', 'search', 'install', 'update', 'remove']
|
||||
excluded_dirs = ['cache', 'vlib']
|
||||
supported_vcs_systems = ['git', 'hg']
|
||||
supported_vcs_folders = ['.git', '.hg']
|
||||
supported_vcs_update_cmds = {
|
||||
'git': 'git pull --depth=1'
|
||||
'hg': 'hg pull --update'
|
||||
}
|
||||
supported_vcs_install_cmds = {
|
||||
'git': 'git clone --depth=1'
|
||||
'hg': 'hg clone'
|
||||
}
|
||||
)
|
||||
|
||||
struct Mod {
|
||||
id int
|
||||
name string
|
||||
url string
|
||||
nr_downloads int
|
||||
vcs string
|
||||
}
|
||||
|
||||
struct Vmod {
|
||||
mut:
|
||||
name string
|
||||
version string
|
||||
deps []string
|
||||
}
|
||||
|
||||
fn main() {
|
||||
init_settings()
|
||||
// This tool is intended to be launched by the v frontend,
|
||||
// which provides the path to V inside os.getenv('VEXE')
|
||||
args := os.args // args are: vpm [options] SUBCOMMAND module names
|
||||
params := cmdline.only_non_options(args[1..])
|
||||
verbose_println('cli params: $params')
|
||||
if params.len < 1 {
|
||||
vpm_help([])
|
||||
exit(5)
|
||||
}
|
||||
vpm_command := params[0]
|
||||
module_names := params[1..]
|
||||
ensure_vmodules_dir_exist()
|
||||
// println('module names: ') println(module_names)
|
||||
match vpm_command {
|
||||
'help' {
|
||||
vpm_help(module_names)
|
||||
}
|
||||
'search' {
|
||||
vpm_search(module_names)
|
||||
}
|
||||
'install' {
|
||||
vpm_install(module_names)
|
||||
}
|
||||
'update' {
|
||||
vpm_update(module_names)
|
||||
}
|
||||
'remove' {
|
||||
vpm_remove(module_names)
|
||||
}
|
||||
else {
|
||||
println('Error: you tried to run "v $vpm_command"')
|
||||
println('... but the v package management tool vpm only knows about these commands:')
|
||||
for validcmd in valid_vpm_commands {
|
||||
println(' v $validcmd')
|
||||
}
|
||||
exit(3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn vpm_search(keywords []string) {
|
||||
if settings.is_help {
|
||||
println('Usage:')
|
||||
println(' v search keyword1 [keyword2] [...]')
|
||||
println(' ^^^^^^^^^^^^^^^^^ will search https://vpm.vlang.io/ for matching modules,')
|
||||
println(' and will show details about them')
|
||||
show_vpm_options()
|
||||
exit(0)
|
||||
}
|
||||
if keywords.len == 0 {
|
||||
println(' v search requires *at least one* keyword')
|
||||
exit(2)
|
||||
}
|
||||
modules := get_all_modules()
|
||||
joined := keywords.join(', ')
|
||||
mut index := 0
|
||||
for mod in modules {
|
||||
// TODO for some reason .filter results in substr error, so do it manually
|
||||
for k in keywords {
|
||||
if !mod.contains(k) {
|
||||
continue
|
||||
}
|
||||
if index == 0 {
|
||||
println('Search results for "$joined":\n')
|
||||
}
|
||||
index++
|
||||
mut parts := mod.split('.')
|
||||
// in case the author isn't present
|
||||
if parts.len == 1 {
|
||||
parts << parts[0]
|
||||
parts[0] = ''
|
||||
}
|
||||
println('${index}. ${parts[1]} by ${parts[0]} [$mod]')
|
||||
break
|
||||
}
|
||||
}
|
||||
println('\nUse "v install author.module_name" to install the module')
|
||||
if index == 0 {
|
||||
println('No module(s) found for "$joined"')
|
||||
}
|
||||
}
|
||||
|
||||
fn vpm_install(module_names []string) {
|
||||
if settings.is_help {
|
||||
println('Usage:')
|
||||
println(' v install module [module] [module] [...]')
|
||||
println(' ^^^^^^^^^^^^^ will install the modules you specified')
|
||||
show_vpm_options()
|
||||
exit(0)
|
||||
}
|
||||
if module_names.len == 0 {
|
||||
println(' v install requires *at least one* module name')
|
||||
exit(2)
|
||||
}
|
||||
mut errors := 0
|
||||
url := get_working_server_url()
|
||||
for n in module_names {
|
||||
name := n.trim_space()
|
||||
modurl := url + '/jsmod/$name'
|
||||
r := http.get(modurl) or {
|
||||
println('Http server did not respond to our request for ${modurl}.')
|
||||
println('Error details: $err')
|
||||
errors++
|
||||
continue
|
||||
}
|
||||
if r.status_code == 404 {
|
||||
println('Skipping module "$name", since $url reported that "$name" does not exist.')
|
||||
errors++
|
||||
continue
|
||||
}
|
||||
if r.status_code != 200 {
|
||||
println('Skipping module "$name", since $url responded with $r.status_code http status code. Please try again later.')
|
||||
errors++
|
||||
continue
|
||||
}
|
||||
s := r.text
|
||||
mod := json.decode(Mod,s) or {
|
||||
errors++
|
||||
println('Skipping module "$name", since its information is not in json format.')
|
||||
continue
|
||||
}
|
||||
if ('' == mod.url || '' == mod.name) {
|
||||
errors++
|
||||
// a possible 404 error, which means a missing module?
|
||||
println('Skipping module "$name", since it is missing name or url information.')
|
||||
continue
|
||||
}
|
||||
mut vcs := mod.vcs
|
||||
if vcs == '' {
|
||||
vcs = supported_vcs_systems[0]
|
||||
}
|
||||
if !vcs in supported_vcs_systems {
|
||||
errors++
|
||||
println('Skipping module "$name", since it uses an unsupported VCS {$vcs} .')
|
||||
continue
|
||||
}
|
||||
final_module_path := os.realpath(filepath.join(settings.vmodules_path,mod.name.replace('.', os.path_separator)))
|
||||
if os.exists(final_module_path) {
|
||||
vpm_update([name])
|
||||
continue
|
||||
}
|
||||
println('Installing module "$name" from $mod.url to $final_module_path ...')
|
||||
vcs_install_cmd := supported_vcs_install_cmds[vcs]
|
||||
cmd := '${vcs_install_cmd} "${mod.url}" "${final_module_path}"'
|
||||
verbose_println(' command: $cmd')
|
||||
cmdres := os.exec(cmd) or {
|
||||
errors++
|
||||
println('Could not install module "$name" to "$final_module_path" .')
|
||||
verbose_println('Error command: $cmd')
|
||||
verbose_println('Error details: $err')
|
||||
continue
|
||||
}
|
||||
if cmdres.exit_code != 0 {
|
||||
errors++
|
||||
println('Failed installing module "$name" to "$final_module_path" .')
|
||||
verbose_println('Failed command: ${cmd}')
|
||||
verbose_println('Failed command output:\n${cmdres.output}')
|
||||
continue
|
||||
}
|
||||
resolve_dependencies(name, final_module_path, module_names)
|
||||
}
|
||||
if errors > 0 {
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
fn vpm_update(m []string) {
|
||||
mut module_names := m
|
||||
if settings.is_help {
|
||||
println('Usage: ')
|
||||
println(' a) v update module [module] [module] [...]')
|
||||
println(' ^^^^^^^^^^^^ will update the listed modules to their latest versions')
|
||||
println(' b) v update')
|
||||
println(' ^^^^^^^^^^^^ will update ALL installed modules to their latest versions')
|
||||
show_vpm_options()
|
||||
exit(0)
|
||||
}
|
||||
if module_names.len == 0 {
|
||||
module_names = get_installed_modules()
|
||||
}
|
||||
mut errors := 0
|
||||
for name in module_names {
|
||||
final_module_path := valid_final_path_of_existing_module(name) or {
|
||||
continue
|
||||
}
|
||||
os.chdir(final_module_path)
|
||||
println('Updating module "$name"...')
|
||||
verbose_println(' work folder: $final_module_path')
|
||||
vcs := vcs_used_in_dir(final_module_path) or {
|
||||
continue
|
||||
}
|
||||
vcs_cmd := supported_vcs_update_cmds[vcs[0]]
|
||||
verbose_println(' command: $vcs_cmd')
|
||||
vcs_res := os.exec('${vcs_cmd}') or {
|
||||
errors++
|
||||
println('Could not update module "$name".')
|
||||
verbose_println('Error command: ${vcs_cmd}')
|
||||
verbose_println('Error details:\n$err')
|
||||
continue
|
||||
}
|
||||
if vcs_res.exit_code != 0 {
|
||||
errors++
|
||||
println('Failed updating module "${name}".')
|
||||
verbose_println('Failed command: ${vcs_cmd}')
|
||||
verbose_println('Failed details:\n${vcs_res.output}')
|
||||
continue
|
||||
}
|
||||
resolve_dependencies(name, final_module_path, module_names)
|
||||
}
|
||||
if errors > 0 {
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
fn vpm_remove(module_names []string) {
|
||||
if settings.is_help {
|
||||
println('Usage: ')
|
||||
println(' a) v remove module [module] [module] [...]')
|
||||
println(' ^^^^^^^^^^^^ will remove the listed modules')
|
||||
println(' b) v remove')
|
||||
println(' ^^^^^^^^^^^^ will remove ALL installed modules')
|
||||
show_vpm_options()
|
||||
exit(0)
|
||||
}
|
||||
if module_names.len == 0 {
|
||||
println(' v update requires *at least one* module name')
|
||||
exit(2)
|
||||
}
|
||||
for name in module_names {
|
||||
final_module_path := valid_final_path_of_existing_module(name) or {
|
||||
continue
|
||||
}
|
||||
println('Removing module "$name"...')
|
||||
verbose_println('removing folder $final_module_path')
|
||||
os.rmdir_recursive(final_module_path)
|
||||
// delete author directory if it is empty
|
||||
author := name.split('.')[0]
|
||||
author_dir := os.realpath(filepath.join(settings.vmodules_path,author))
|
||||
if os.is_dir_empty(author_dir) {
|
||||
verbose_println('removing author folder $author_dir')
|
||||
os.rmdir(author_dir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn valid_final_path_of_existing_module(name string) ?string {
|
||||
name_of_vmodules_folder := filepath.join(settings.vmodules_path,name.replace('.', os.path_separator))
|
||||
final_module_path := os.realpath(name_of_vmodules_folder)
|
||||
if !os.exists(final_module_path) {
|
||||
println('No module with name "$name" exists at $name_of_vmodules_folder')
|
||||
return none
|
||||
}
|
||||
if !os.is_dir(final_module_path) {
|
||||
println('Skipping "$name_of_vmodules_folder", since it is not a folder.')
|
||||
return none
|
||||
}
|
||||
vcs_used_in_dir(final_module_path) or {
|
||||
println('Skipping "$name_of_vmodules_folder", since it does not use a supported vcs.')
|
||||
return none
|
||||
}
|
||||
return final_module_path
|
||||
}
|
||||
|
||||
fn ensure_vmodules_dir_exist() {
|
||||
if !os.is_dir(settings.vmodules_path) {
|
||||
println('Creating $settings.vmodules_path/ ...')
|
||||
os.mkdir(settings.vmodules_path) or {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn vpm_help(module_names []string) {
|
||||
println('Usage:')
|
||||
println(' a) v install module [module] [module] [...]')
|
||||
println(' b) v update [module] [...]')
|
||||
println(' c) v remove [module] [...]')
|
||||
println(' d) v search keyword1 [keyword2] [...]')
|
||||
println('')
|
||||
println(' You can also pass -h or --help after each vpm command from the above, to see more details about it.')
|
||||
}
|
||||
|
||||
fn vcs_used_in_dir(dir string) ?[]string {
|
||||
mut vcs := []string
|
||||
for repo_subfolder in supported_vcs_folders {
|
||||
checked_folder := os.realpath(filepath.join(dir,repo_subfolder))
|
||||
if os.is_dir(checked_folder) {
|
||||
vcs << repo_subfolder.replace('.', '')
|
||||
}
|
||||
}
|
||||
if vcs.len == 0 {
|
||||
return none
|
||||
}
|
||||
return vcs
|
||||
}
|
||||
|
||||
fn get_installed_modules() []string {
|
||||
dirs := os.ls(settings.vmodules_path) or {
|
||||
return []
|
||||
}
|
||||
mut modules := []string
|
||||
for dir in dirs {
|
||||
adir := filepath.join(settings.vmodules_path,dir)
|
||||
if dir in excluded_dirs || !os.is_dir(adir) {
|
||||
continue
|
||||
}
|
||||
author := dir
|
||||
mods := os.ls(adir) or {
|
||||
continue
|
||||
}
|
||||
for m in mods {
|
||||
vcs_used_in_dir(filepath.join(adir,m)) or {
|
||||
continue
|
||||
}
|
||||
modules << '${author}.$m'
|
||||
}
|
||||
}
|
||||
return modules
|
||||
}
|
||||
|
||||
fn get_all_modules() []string {
|
||||
url := get_working_server_url()
|
||||
r := http.get(url) or {
|
||||
panic(err)
|
||||
}
|
||||
if r.status_code != 200 {
|
||||
println('Failed to search vpm.best. Status code: $r.status_code')
|
||||
exit(1)
|
||||
}
|
||||
s := r.text
|
||||
mut read_len := 0
|
||||
mut modules := []string
|
||||
for read_len < s.len {
|
||||
mut start_token := '<a href="/mod'
|
||||
end_token := '</a>'
|
||||
// get the start index of the module entry
|
||||
mut start_index := s.index_after(start_token, read_len)
|
||||
if start_index == -1 {
|
||||
break
|
||||
}
|
||||
// get the index of the end of anchor (a) opening tag
|
||||
// we use the previous start_index to make sure we are getting a module and not just a random 'a' tag
|
||||
start_token = '">'
|
||||
start_index = s.index_after(start_token, start_index) + start_token.len
|
||||
// get the index of the end of module entry
|
||||
end_index := s.index_after(end_token, start_index)
|
||||
if end_index == -1 {
|
||||
break
|
||||
}
|
||||
modules << s[start_index..end_index]
|
||||
read_len = end_index
|
||||
if read_len >= s.len {
|
||||
break
|
||||
}
|
||||
}
|
||||
return modules
|
||||
}
|
||||
|
||||
fn resolve_dependencies(name, module_path string, module_names []string) {
|
||||
vmod_path := filepath.join(module_path,'v.mod')
|
||||
if !os.exists(vmod_path) {
|
||||
return
|
||||
}
|
||||
data := os.read_file(vmod_path) or {
|
||||
return
|
||||
}
|
||||
vmod := parse_vmod(data)
|
||||
mut deps := []string
|
||||
// filter out dependencies that were already specified by the user
|
||||
for d in vmod.deps {
|
||||
if !(d in module_names) {
|
||||
deps << d
|
||||
}
|
||||
}
|
||||
if deps.len > 0 {
|
||||
println('Resolving ${deps.len} dependencies for module "$name"...')
|
||||
verbose_println('Found dependencies: $deps')
|
||||
vpm_install(deps)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_vmod(data string) Vmod {
|
||||
keys := ['name', 'version', 'deps']
|
||||
mut m := {
|
||||
'name': '',
|
||||
'version': '',
|
||||
'deps': ''
|
||||
}
|
||||
for key in keys {
|
||||
mut key_index := data.index('$key:') or {
|
||||
continue
|
||||
}
|
||||
key_index += key.len + 1
|
||||
m[key] = data[key_index..data.index_after('\n', key_index)].trim_space().replace("'", '').replace('[', '').replace(']', '')
|
||||
}
|
||||
mut vmod := Vmod{}
|
||||
vmod.name = m['name']
|
||||
vmod.version = m['version']
|
||||
if m['deps'].len > 0 {
|
||||
vmod.deps = m['deps'].split(',')
|
||||
}
|
||||
return vmod
|
||||
}
|
||||
|
||||
fn get_working_server_url() string {
|
||||
server_urls := if settings.server_urls.len > 0 { settings.server_urls } else { default_vpm_server_urls }
|
||||
for url in server_urls {
|
||||
verbose_println('Trying server url: $url')
|
||||
http.head(url) or {
|
||||
verbose_println(' $url failed.')
|
||||
continue
|
||||
}
|
||||
return url
|
||||
}
|
||||
panic('No responding vpm server found. Please check your network connectivity and try again later.')
|
||||
}
|
||||
|
||||
// settings context:
|
||||
struct VpmSettings {
|
||||
mut:
|
||||
is_help bool
|
||||
is_verbose bool
|
||||
server_urls []string
|
||||
vmodules_path string
|
||||
}
|
||||
|
||||
const (
|
||||
settings = &VpmSettings{}
|
||||
)
|
||||
|
||||
fn init_settings() {
|
||||
mut s := &VpmSettings(0)
|
||||
unsafe{
|
||||
s = settings
|
||||
}
|
||||
s.is_help = '-h' in os.args || '--help' in os.args || 'help' in os.args
|
||||
s.is_verbose = '-verbose' in os.args || '--verbose' in os.args
|
||||
s.server_urls = cmdline.many_values(os.args, '-server-url')
|
||||
s.vmodules_path = os.home_dir() + '.vmodules'
|
||||
}
|
||||
|
||||
fn show_vpm_options() {
|
||||
println('Options:')
|
||||
println(' -help - Show usage info')
|
||||
println(' -verbose - Print more details about the performed operation')
|
||||
println(' -server-url - When doing network operations, use this vpm server. Can be given multiple times.')
|
||||
}
|
||||
|
||||
fn verbose_println(s string) {
|
||||
if settings.is_verbose {
|
||||
println(s)
|
||||
}
|
||||
}
|
||||
245
cmd/tools/vrepl.v
Normal file
245
cmd/tools/vrepl.v
Normal file
@@ -0,0 +1,245 @@
|
||||
// Copyright (c) 2019-2020 Alexander Medvednikov. 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
|
||||
term
|
||||
readline
|
||||
os.cmdline
|
||||
filepath
|
||||
)
|
||||
|
||||
struct Repl {
|
||||
mut:
|
||||
indent int
|
||||
in_func bool
|
||||
line string
|
||||
lines []string
|
||||
temp_lines []string
|
||||
functions_name []string
|
||||
functions []string
|
||||
}
|
||||
|
||||
fn (r mut Repl) checks() bool {
|
||||
mut in_string := false
|
||||
mut is_cut := false
|
||||
was_indent := r.indent > 0
|
||||
|
||||
for i := 0; i < r.line.len; i++ {
|
||||
if r.line[i] == `\'` && (i == 0 || r.line[i - 1] != `\\`) {
|
||||
in_string = !in_string
|
||||
}
|
||||
if r.line[i] == `{` && !in_string {
|
||||
r.line = r.line[..i + 1] + '\n' + r.line[i + 1..]
|
||||
is_cut = true
|
||||
i++
|
||||
r.indent++
|
||||
}
|
||||
if r.line[i] == `}` && !in_string {
|
||||
r.line = r.line[..i] + '\n' + r.line[i..]
|
||||
is_cut = true
|
||||
i++
|
||||
r.indent--
|
||||
if r.indent == 0 {
|
||||
r.in_func = false
|
||||
}
|
||||
}
|
||||
if i + 2 < r.line.len && r.indent == 0 && r.line[i + 1] == `f` && r.line[i + 2] == `n` {
|
||||
r.in_func = true
|
||||
}
|
||||
}
|
||||
return r.in_func || (was_indent && r.indent <= 0) || r.indent > 0 || is_cut
|
||||
}
|
||||
|
||||
fn (r &Repl) function_call(line string) bool {
|
||||
for function in r.functions_name {
|
||||
if line.starts_with(function) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
pub fn repl_help() {
|
||||
version := v_version()
|
||||
println(version)
|
||||
println('
|
||||
help Displays this information.
|
||||
Ctrl-C, Ctrl-D, exit Exits the REPL.
|
||||
clear Clears the screen.
|
||||
')
|
||||
}
|
||||
|
||||
pub fn run_repl(workdir string, vrepl_prefix string) []string {
|
||||
version := v_version()
|
||||
println(version)
|
||||
println('Use Ctrl-C or `exit` to exit')
|
||||
|
||||
file := filepath.join( workdir, '.${vrepl_prefix}vrepl.v' )
|
||||
temp_file := filepath.join( workdir, '.${vrepl_prefix}vrepl_temp.v')
|
||||
mut prompt := '>>> '
|
||||
defer {
|
||||
os.rm(file)
|
||||
os.rm(temp_file)
|
||||
os.rm(file[..file.len - 2])
|
||||
os.rm(temp_file[..temp_file.len - 2])
|
||||
}
|
||||
mut r := Repl{}
|
||||
mut readline := readline.Readline{}
|
||||
vexe := os.getenv('VEXE')
|
||||
for {
|
||||
if r.indent == 0 {
|
||||
prompt = '>>> '
|
||||
}
|
||||
else {
|
||||
prompt = '... '
|
||||
}
|
||||
mut line := readline.read_line(prompt) or {
|
||||
break
|
||||
}
|
||||
if line.trim_space() == '' && line.ends_with('\n') {
|
||||
continue
|
||||
}
|
||||
line = line.trim_space()
|
||||
if line.len <= -1 || line == '' || line == 'exit' {
|
||||
break
|
||||
}
|
||||
r.line = line
|
||||
if r.line == '\n' {
|
||||
continue
|
||||
}
|
||||
if r.line == 'clear' {
|
||||
term.erase_display('2')
|
||||
continue
|
||||
}
|
||||
if r.line == 'help' {
|
||||
repl_help()
|
||||
continue
|
||||
}
|
||||
if r.line.starts_with('fn') {
|
||||
r.in_func = true
|
||||
r.functions_name << r.line.all_after('fn').all_before('(').trim_space()
|
||||
}
|
||||
was_func := r.in_func
|
||||
if r.checks() {
|
||||
for rline in r.line.split('\n') {
|
||||
if r.in_func || was_func {
|
||||
r.functions << rline
|
||||
}
|
||||
else {
|
||||
r.temp_lines << rline
|
||||
}
|
||||
}
|
||||
if r.indent > 0 {
|
||||
continue
|
||||
}
|
||||
r.line = ''
|
||||
}
|
||||
// Save the source only if the user is printing something,
|
||||
// but don't add this print call to the `lines` array,
|
||||
// so that it doesn't get called during the next print.
|
||||
if r.line.starts_with('print') {
|
||||
source_code := r.functions.join('\n') + r.lines.join('\n') + '\n' + r.line
|
||||
os.write_file(file, source_code)
|
||||
s := os.exec('"$vexe" run $file -repl') or {
|
||||
rerror(err)
|
||||
return []
|
||||
}
|
||||
print_output(s)
|
||||
}
|
||||
else {
|
||||
mut temp_line := r.line
|
||||
mut temp_flag := false
|
||||
func_call := r.function_call(r.line)
|
||||
if !(
|
||||
r.line.contains(' ') ||
|
||||
r.line.contains(':') ||
|
||||
r.line.contains('=') ||
|
||||
r.line.contains(',') ||
|
||||
r.line.ends_with('++') ||
|
||||
r.line.ends_with('--') ||
|
||||
r.line == '') && !func_call {
|
||||
temp_line = 'println($r.line)'
|
||||
temp_flag = true
|
||||
}
|
||||
temp_source_code := r.functions.join('\n') + r.lines.join('\n') + '\n' + r.temp_lines.join('\n') + '\n' + temp_line
|
||||
os.write_file(temp_file, temp_source_code)
|
||||
s := os.exec('"$vexe" run $temp_file -repl') or {
|
||||
println("SDFSDF")
|
||||
rerror(err)
|
||||
return []
|
||||
}
|
||||
if !func_call && s.exit_code == 0 && !temp_flag {
|
||||
for r.temp_lines.len > 0 {
|
||||
if !r.temp_lines[0].starts_with('print') {
|
||||
r.lines << r.temp_lines[0]
|
||||
}
|
||||
r.temp_lines.delete(0)
|
||||
}
|
||||
r.lines << r.line
|
||||
}
|
||||
else {
|
||||
for r.temp_lines.len > 0 {
|
||||
r.temp_lines.delete(0)
|
||||
}
|
||||
}
|
||||
print_output(s)
|
||||
}
|
||||
}
|
||||
return r.lines
|
||||
}
|
||||
|
||||
fn print_output(s os.Result) {
|
||||
lines := s.output.split('\n')
|
||||
for line in lines {
|
||||
if line.contains('.vrepl_temp.v:') {
|
||||
// Hide the temporary file name
|
||||
sline := line.all_after('.vrepl_temp.v:')
|
||||
idx := sline.index(' ') or {
|
||||
println(sline)
|
||||
return
|
||||
}
|
||||
println(sline[idx+1..])
|
||||
} else if line.contains('.vrepl.v:') {
|
||||
// Ensure that .vrepl.v: is at the start, ignore the path
|
||||
// This is needed to have stable .repl tests.
|
||||
idx := line.index('.vrepl.v:') or { return }
|
||||
println(line[idx..])
|
||||
} else {
|
||||
println(line)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Support for the parameters replfolder and replprefix is needed
|
||||
// so that the repl can be launched in parallel by several different
|
||||
// threads by the REPL test runner.
|
||||
args := cmdline.after(os.args, ['repl'])
|
||||
replfolder := os.realpath( cmdline.option(args, '-replfolder', '.') )
|
||||
replprefix := cmdline.option(args, '-replprefix', 'noprefix.')
|
||||
os.chdir( replfolder )
|
||||
if !os.exists(os.getenv('VEXE')) {
|
||||
println('Usage:')
|
||||
println(' VEXE=vexepath vrepl\n')
|
||||
println(' ... where vexepath is the full path to the v executable file')
|
||||
return
|
||||
}
|
||||
run_repl( replfolder, replprefix )
|
||||
}
|
||||
|
||||
pub fn rerror(s string) {
|
||||
println('V repl error: $s')
|
||||
os.flush_stdout()
|
||||
exit(1)
|
||||
}
|
||||
|
||||
fn v_version() string {
|
||||
vexe := os.getenv('VEXE')
|
||||
vversion_res := os.exec('$vexe --version') or { panic('"$vexe --version" is not working') }
|
||||
return vversion_res.output
|
||||
}
|
||||
73
cmd/tools/vtest-compiler.v
Normal file
73
cmd/tools/vtest-compiler.v
Normal file
@@ -0,0 +1,73 @@
|
||||
module main
|
||||
|
||||
import (
|
||||
os
|
||||
testing
|
||||
benchmark
|
||||
filepath
|
||||
)
|
||||
|
||||
pub const (
|
||||
v_modules_path = os.home_dir() + '.vmodules'
|
||||
)
|
||||
|
||||
fn main() {
|
||||
args := os.args
|
||||
args_string := args[1..].join(' ')
|
||||
v_test_compiler(args_string.all_before('test-compiler'))
|
||||
}
|
||||
|
||||
fn v_test_compiler(vargs string) {
|
||||
vexe := testing.vexe_path()
|
||||
parent_dir := filepath.dir(vexe)
|
||||
testing.vlib_should_be_present(parent_dir)
|
||||
// Changing the current directory is needed for some of the compiler tests,
|
||||
// compiler/tests/local_test.v and compiler/tests/repl/repl_test.v
|
||||
os.chdir(parent_dir)
|
||||
/*
|
||||
if !os.exists(parent_dir + '/v.v') {
|
||||
eprintln('v.v is missing, it must be next to the V executable')
|
||||
exit(1)
|
||||
}
|
||||
*/
|
||||
|
||||
// Make sure v.c can be compiled without warnings
|
||||
$if macos {
|
||||
if os.exists('/cmd/v') {
|
||||
os.system('$vexe -o v.c cmd/v')
|
||||
if os.system('cc -Werror v.c') != 0 {
|
||||
eprintln('cc failed to build v.c without warnings')
|
||||
exit(1)
|
||||
}
|
||||
eprintln('v.c can be compiled without warnings. This is good :)')
|
||||
}
|
||||
}
|
||||
building_tools_failed := testing.v_build_failing(vargs, 'cmd/tools')
|
||||
eprintln('')
|
||||
testing.eheader('Testing all _test.v files...')
|
||||
mut compiler_test_session := testing.new_test_session(vargs)
|
||||
compiler_test_session.files << os.walk_ext(parent_dir, '_test.v')
|
||||
compiler_test_session.test()
|
||||
eprintln(compiler_test_session.benchmark.total_message('running V tests'))
|
||||
eprintln('')
|
||||
building_examples_failed := testing.v_build_failing(vargs, 'examples')
|
||||
eprintln('')
|
||||
building_live_failed := testing.v_build_failing(vargs + '-live', filepath.join('examples','hot_reload'))
|
||||
eprintln('')
|
||||
v_module_install_cmd := '$vexe install nedpals.args'
|
||||
eprintln('')
|
||||
testing.eheader('Installing a v module with: $v_module_install_cmd')
|
||||
mut vmark := benchmark.new_benchmark()
|
||||
ret := os.system(v_module_install_cmd)
|
||||
if ret != 0 {
|
||||
eprintln('failed to run v install')
|
||||
}
|
||||
if !os.exists(v_modules_path + '/nedpals/args') {
|
||||
eprintln('v failed to install a test module')
|
||||
}
|
||||
vmark.stop()
|
||||
eprintln('Installing a v module took: ' + vmark.total_duration().str() + 'ms')
|
||||
if building_tools_failed || compiler_test_session.failed || building_examples_failed || building_live_failed {
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
71
cmd/tools/vtest-fmt.v
Normal file
71
cmd/tools/vtest-fmt.v
Normal file
@@ -0,0 +1,71 @@
|
||||
module main
|
||||
|
||||
import (
|
||||
os
|
||||
testing
|
||||
)
|
||||
|
||||
const (
|
||||
known_failing_exceptions = ['./examples/vweb/vweb_example.v',
|
||||
'./cmd/tools/gen_vc.v',
|
||||
'./cmd/tools/modules/vgit/vgit.v', // generics
|
||||
'./cmd/tools/preludes/live_main.v',
|
||||
'./cmd/tools/preludes/live_shared.v',
|
||||
'./cmd/tools/preludes/tests_assertions.v',
|
||||
'./cmd/tools/preludes/tests_with_stats.v',
|
||||
'./cmd/tools/performance_compare.v', // generics
|
||||
'./cmd/tools/oldv.v', // generics
|
||||
'./tutorials/code/blog/article.v',
|
||||
'./tutorials/code/blog/blog.v',
|
||||
'./vlib/arrays/arrays.v',
|
||||
'./vlib/arrays/arrays_test.v',
|
||||
'./vlib/builtin/js/hashmap.v',
|
||||
'./vlib/compiler/tests/fn_variadic_test.v',
|
||||
'./vlib/compiler/tests/generic_test.v',
|
||||
'./vlib/crypto/aes/aes.v',
|
||||
'./vlib/crypto/aes/aes_cbc.v',
|
||||
'./vlib/crypto/aes/block_generic.v',
|
||||
'./vlib/crypto/aes/const.v',
|
||||
'./vlib/crypto/aes/cypher_generic.v',
|
||||
'./vlib/crypto/rc4/rc4.v',
|
||||
'./vlib/eventbus/eventbus_test.v',
|
||||
'./vlib/os/bare/bare_example_linux.v',
|
||||
'./vlib/szip/szip.v',
|
||||
'./vlib/uiold/examples/users_gui/users.v',
|
||||
'./vlib/vweb/assets/assets.v',
|
||||
'./vlib/vweb/vweb.v',
|
||||
]
|
||||
)
|
||||
|
||||
fn main() {
|
||||
args := os.args
|
||||
args_string := args[1..].join(' ')
|
||||
v_test_formatting(args_string.all_before('test-fmt'))
|
||||
}
|
||||
|
||||
fn v_test_formatting(vargs string) {
|
||||
all_v_files := v_files()
|
||||
testing.eheader('Run "v fmt" over all .v files')
|
||||
mut vfmt_test_session := testing.new_test_session('$vargs fmt -worker')
|
||||
vfmt_test_session.files << all_v_files
|
||||
vfmt_test_session.test()
|
||||
eprintln(vfmt_test_session.benchmark.total_message('running vfmt over V files'))
|
||||
if vfmt_test_session.benchmark.nfail > 0 {
|
||||
panic('\nWARNING: v fmt failed ${vfmt_test_session.benchmark.nfail} times.\n')
|
||||
}
|
||||
}
|
||||
|
||||
fn v_files() []string {
|
||||
mut files_that_can_be_formatted := []string
|
||||
all_test_files := os.walk_ext('.', '.v')
|
||||
for tfile in all_test_files {
|
||||
if tfile in known_failing_exceptions {
|
||||
continue
|
||||
}
|
||||
if tfile.starts_with('./vlib/v/cgen/tests') {
|
||||
continue
|
||||
}
|
||||
files_that_can_be_formatted << tfile
|
||||
}
|
||||
return files_that_can_be_formatted
|
||||
}
|
||||
56
cmd/tools/vtest.v
Normal file
56
cmd/tools/vtest.v
Normal file
@@ -0,0 +1,56 @@
|
||||
module main
|
||||
|
||||
import (
|
||||
os
|
||||
os.cmdline
|
||||
testing
|
||||
)
|
||||
|
||||
pub fn main() {
|
||||
args := os.args
|
||||
if args.last() == 'test' {
|
||||
println('Usage:')
|
||||
println(' A)')
|
||||
println(' v test folder/ : run all v tests in the given folder.')
|
||||
println(' v -stats test folder/ : the same, but print more stats.')
|
||||
println(' B)')
|
||||
println(' v test file_test.v : run test functions in a given test file.')
|
||||
println(' v -stats test file_test.v : as above, but with more stats.')
|
||||
println(' NB: you can also give many and mixed folder/ file_test.v arguments after test.')
|
||||
println('')
|
||||
return
|
||||
}
|
||||
|
||||
args_to_executable := args[1..]
|
||||
args_before := cmdline.before(args_to_executable, ['test'])
|
||||
args_after := cmdline.after(args_to_executable, ['test'])
|
||||
|
||||
if args_after.join(' ') == 'v' {
|
||||
eprintln('`v test v` has been deprecated.')
|
||||
eprintln('Use `v test-compiler` instead.')
|
||||
exit(1)
|
||||
}
|
||||
|
||||
mut ts := testing.new_test_session(args_before.join(' '))
|
||||
for targ in args_after {
|
||||
if os.exists(targ) && targ.ends_with('_test.v') {
|
||||
ts.files << targ
|
||||
continue
|
||||
}
|
||||
if os.is_dir(targ) {
|
||||
// Fetch all tests from the directory
|
||||
ts.files << os.walk_ext( targ.trim_right(os.path_separator), '_test.v')
|
||||
continue
|
||||
}
|
||||
println('Unrecognized test file $targ .')
|
||||
}
|
||||
|
||||
testing.header('Testing...')
|
||||
ts.test()
|
||||
|
||||
println( ts.benchmark.total_message('running V _test.v files') )
|
||||
if ts.failed {
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
26
cmd/tools/vup.v
Normal file
26
cmd/tools/vup.v
Normal file
@@ -0,0 +1,26 @@
|
||||
module main
|
||||
|
||||
import (
|
||||
os
|
||||
filepath
|
||||
)
|
||||
|
||||
fn main() {
|
||||
println('Updating V...')
|
||||
vroot := filepath.dir(os.getenv('VEXE'))
|
||||
os.chdir(vroot)
|
||||
s := os.exec('git -C "$vroot" pull --rebase origin master') or { panic(err) }
|
||||
println(s.output)
|
||||
$if windows {
|
||||
v_backup_file := '$vroot/v_old.exe'
|
||||
if os.exists( v_backup_file ) {
|
||||
os.rm( v_backup_file )
|
||||
}
|
||||
os.mv('$vroot/v.exe', v_backup_file)
|
||||
s2 := os.exec('"$vroot/make.bat"') or { panic(err) }
|
||||
println(s2.output)
|
||||
} $else {
|
||||
s2 := os.exec('make -C "$vroot"') or { panic(err) }
|
||||
println(s2.output)
|
||||
}
|
||||
}
|
||||
67
cmd/v/compile.v
Normal file
67
cmd/v/compile.v
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright (c) 2019-2020 Alexander Medvednikov. 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 (
|
||||
benchmark
|
||||
os
|
||||
os.cmdline
|
||||
)
|
||||
|
||||
fn compile(command string, args []string) {
|
||||
// Construct the V object from command line arguments
|
||||
mut v := new_v(args)
|
||||
if v.pref.is_verbose {
|
||||
println(args)
|
||||
}
|
||||
if command == 'run' {
|
||||
// always recompile for now, too error prone to skip recompilation otherwise
|
||||
// for example for -repl usage, especially when piping lines to v
|
||||
v.compile()
|
||||
run_compiled_executable_and_exit(v, args)
|
||||
}
|
||||
mut tmark := benchmark.new_benchmark()
|
||||
if v.pref.x64 {
|
||||
v.compile_x64()
|
||||
}
|
||||
else if v.pref.v2 {
|
||||
v.compile2()
|
||||
}
|
||||
else {
|
||||
v.compile()
|
||||
}
|
||||
if v.pref.is_stats {
|
||||
tmark.stop()
|
||||
println('compilation took: ' + tmark.total_duration().str() + 'ms')
|
||||
}
|
||||
if v.pref.is_test {
|
||||
run_compiled_executable_and_exit(v, args)
|
||||
}
|
||||
v.finalize_compilation()
|
||||
}
|
||||
|
||||
pub fn run_compiled_executable_and_exit(v &compiler.V, args []string) {
|
||||
if v.pref.is_verbose {
|
||||
println('============ running $v.pref.out_name ============')
|
||||
}
|
||||
mut cmd := '"${v.pref.out_name}"'
|
||||
args_after_no_options := cmdline.only_non_options( cmdline.after(args,['run','test']) )
|
||||
if args_after_no_options.len > 1 {
|
||||
cmd += ' ' + args_after_no_options[1..].join(' ')
|
||||
}
|
||||
if v.pref.is_test {
|
||||
ret := os.system(cmd)
|
||||
if ret != 0 {
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
if v.pref.is_run {
|
||||
ret := os.system(cmd)
|
||||
// TODO: make the runner wrapping as transparent as possible
|
||||
// (i.e. use execve when implemented). For now though, the runner
|
||||
// just returns the same exit code as the child process.
|
||||
exit(ret)
|
||||
}
|
||||
exit(0)
|
||||
}
|
||||
229
cmd/v/compile_options.v
Normal file
229
cmd/v/compile_options.v
Normal file
@@ -0,0 +1,229 @@
|
||||
// Copyright (c) 2019-2020 Alexander Medvednikov. 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 (
|
||||
compiler
|
||||
filepath
|
||||
os
|
||||
os.cmdline
|
||||
v.pref
|
||||
)
|
||||
|
||||
//TODO Cleanup this file. This file ended up like a dump for functions that do not belong in `compiler`.
|
||||
//Maybe restructure the functions below into different V files
|
||||
|
||||
pub fn new_v(args []string) &compiler.V {
|
||||
// Create modules dirs if they are missing
|
||||
if !os.is_dir(compiler.v_modules_path) {
|
||||
os.mkdir(compiler.v_modules_path)or{
|
||||
panic(err)
|
||||
}
|
||||
os.mkdir('$compiler.v_modules_path${os.path_separator}cache')or{
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
vroot := filepath.dir(vexe_path())
|
||||
// optional, custom modules search path
|
||||
user_mod_path := cmdline.option(args, '-user_mod_path', '')
|
||||
vlib_path := cmdline.option(args, '-vlib-path', '')
|
||||
vpath := cmdline.option(args, '-vpath', '')
|
||||
target_os := cmdline.option(args, '-os', '')
|
||||
if target_os == 'msvc' {
|
||||
// notice that `-os msvc` became `-cc msvc`
|
||||
println('V error: use the flag `-cc msvc` to build using msvc')
|
||||
os.flush_stdout()
|
||||
exit(1)
|
||||
}
|
||||
mut out_name := cmdline.option(args, '-o', '')
|
||||
mut dir := args.last()
|
||||
if 'run' in args {
|
||||
args_after_run := cmdline.only_non_options( cmdline.after(args,['run']) )
|
||||
dir = if args_after_run.len>0 { args_after_run[0] } else { '' }
|
||||
}
|
||||
if dir == 'v.v' {
|
||||
println('looks like you are trying to build V with an old command')
|
||||
println('use `v -o v cmd/v` instead of `v -o v v.v`')
|
||||
exit(1)
|
||||
}
|
||||
if dir.ends_with(os.path_separator) {
|
||||
dir = dir.all_before_last(os.path_separator)
|
||||
}
|
||||
if dir.starts_with('.$os.path_separator') {
|
||||
dir = dir[2..]
|
||||
}
|
||||
if args.len < 2 {
|
||||
dir = ''
|
||||
}
|
||||
|
||||
// build mode
|
||||
mut build_mode := pref.BuildMode.default_mode
|
||||
mut mod := ''
|
||||
joined_args := args.join(' ')
|
||||
if joined_args.contains('build module ') {
|
||||
build_mode = .build_module
|
||||
os.chdir(vroot)
|
||||
// v build module ~/v/os => os.o
|
||||
mod_path := if dir.contains('vlib') { dir.all_after('vlib' + os.path_separator) } else if dir.starts_with('.\\') || dir.starts_with('./') { dir[2..] } else if dir.starts_with(os.path_separator) { dir.all_after(os.path_separator) } else { dir }
|
||||
mod = mod_path.replace(os.path_separator, '.')
|
||||
println('Building module "${mod}" (dir="$dir")...')
|
||||
// out_name = '$TmpPath/vlib/${base}.o'
|
||||
if !out_name.ends_with('.c') {
|
||||
out_name = mod
|
||||
}
|
||||
// Cross compiling? Use separate dirs for each os
|
||||
/*
|
||||
if target_os != os.user_os() {
|
||||
os.mkdir('$TmpPath/vlib/$target_os') or { panic(err) }
|
||||
out_name = '$TmpPath/vlib/$target_os/${base}.o'
|
||||
println('target_os=$target_os user_os=${os.user_os()}')
|
||||
println('!Cross compiling $out_name')
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
// `v -o dir/exec`, create "dir/" if it doesn't exist
|
||||
if out_name.contains(os.path_separator) {
|
||||
d := out_name.all_before_last(os.path_separator)
|
||||
if !os.is_dir(d) {
|
||||
println('creating a new directory "$d"')
|
||||
os.mkdir(d)or{
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// println('VROOT=$vroot')
|
||||
cflags := cmdline.many_values(args, '-cflags').join(' ')
|
||||
|
||||
defines := cmdline.many_values(args, '-d')
|
||||
compile_defines, compile_defines_all := parse_defines( defines )
|
||||
|
||||
rdir := os.realpath(dir)
|
||||
rdir_name := filepath.filename(rdir)
|
||||
if '-bare' in args {
|
||||
println('V error: use -freestanding instead of -bare')
|
||||
os.flush_stdout()
|
||||
exit(1)
|
||||
}
|
||||
is_repl := '-repl' in args
|
||||
ccompiler := cmdline.option(args, '-cc', '')
|
||||
mut pref := &pref.Preferences{
|
||||
os: pref.os_from_string(target_os)
|
||||
is_so: '-shared' in args
|
||||
is_solive: '-solive' in args
|
||||
is_prod: '-prod' in args
|
||||
is_verbose: '-verbose' in args || '--verbose' in args
|
||||
is_debug: '-g' in args || '-cg' in args
|
||||
is_vlines: '-g' in args && !('-cg' in args)
|
||||
is_keep_c: '-keep_c' in args
|
||||
is_pretty_c: '-pretty_c' in args
|
||||
is_cache: '-cache' in args
|
||||
is_stats: '-stats' in args
|
||||
obfuscate: '-obf' in args
|
||||
is_prof: '-prof' in args
|
||||
is_live: '-live' in args
|
||||
sanitize: '-sanitize' in args
|
||||
// nofmt: '-nofmt' in args
|
||||
|
||||
show_c_cmd: '-show_c_cmd' in args
|
||||
translated: 'translated' in args
|
||||
is_run: 'run' in args
|
||||
autofree: '-autofree' in args
|
||||
compress: '-compress' in args
|
||||
enable_globals: '--enable-globals' in args
|
||||
fast: '-fast' in args
|
||||
is_bare: '-freestanding' in args
|
||||
x64: '-x64' in args
|
||||
output_cross_c: '-output-cross-platform-c' in args
|
||||
prealloc: '-prealloc' in args
|
||||
is_repl: is_repl
|
||||
build_mode: build_mode
|
||||
cflags: cflags
|
||||
ccompiler: ccompiler
|
||||
building_v: !is_repl && (rdir_name == 'compiler' || rdir_name == 'v.v' || rdir_name == 'vfmt.v' || rdir_name == 'cmd/v' || dir.contains('vlib'))
|
||||
// is_fmt: comptime_define == 'vfmt'
|
||||
|
||||
user_mod_path: user_mod_path
|
||||
vlib_path: vlib_path
|
||||
vpath: vpath
|
||||
v2: '-v2' in args
|
||||
vroot: vroot
|
||||
out_name: out_name
|
||||
path: dir
|
||||
compile_defines: compile_defines
|
||||
compile_defines_all: compile_defines_all
|
||||
mod: mod
|
||||
}
|
||||
if pref.is_verbose || pref.is_debug {
|
||||
println('C compiler=$pref.ccompiler')
|
||||
}
|
||||
$if !linux {
|
||||
if pref.is_bare && !out_name.ends_with('.c') {
|
||||
println('V error: -freestanding only works on Linux for now')
|
||||
os.flush_stdout()
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
pref.fill_with_defaults()
|
||||
|
||||
// v.exe's parent directory should contain vlib
|
||||
if !os.is_dir(pref.vlib_path) || !os.is_dir(pref.vlib_path + os.path_separator + 'builtin') {
|
||||
// println('vlib not found, downloading it...')
|
||||
/*
|
||||
ret := os.system('git clone --depth=1 https://github.com/vlang/v .')
|
||||
if ret != 0 {
|
||||
println('failed to `git clone` vlib')
|
||||
println('make sure you are online and have git installed')
|
||||
exit(1)
|
||||
}
|
||||
*/
|
||||
println('vlib not found. It should be next to the V executable.')
|
||||
println('Go to https://vlang.io to install V.')
|
||||
println('(os.executable=${os.executable()} vlib_path=$pref.vlib_path vexe_path=${vexe_path()}')
|
||||
exit(1)
|
||||
}
|
||||
|
||||
if pref.is_script && !os.exists(dir) {
|
||||
println('`$dir` does not exist')
|
||||
exit(1)
|
||||
}
|
||||
|
||||
return compiler.new_v(pref)
|
||||
}
|
||||
|
||||
fn find_c_compiler_thirdparty_options(args []string) string {
|
||||
mut cflags := cmdline.many_values(args,'-cflags')
|
||||
$if !windows {
|
||||
cflags << '-fPIC'
|
||||
}
|
||||
if '-m32' in args {
|
||||
cflags << '-m32'
|
||||
}
|
||||
return cflags.join(' ')
|
||||
}
|
||||
|
||||
fn parse_defines(defines []string) ([]string,[]string) {
|
||||
// '-d abc -d xyz=1 -d qwe=0' should produce:
|
||||
// compile_defines: ['abc','xyz']
|
||||
// compile_defines_all ['abc','xyz','qwe']
|
||||
mut compile_defines := []string
|
||||
mut compile_defines_all := []string
|
||||
for dfn in defines {
|
||||
dfn_parts := dfn.split('=')
|
||||
if dfn_parts.len == 1 {
|
||||
compile_defines << dfn
|
||||
compile_defines_all << dfn
|
||||
continue
|
||||
}
|
||||
if dfn_parts.len == 2 {
|
||||
compile_defines_all << dfn_parts[0]
|
||||
if dfn_parts[1] == '1' {
|
||||
compile_defines << dfn_parts[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
return compile_defines, compile_defines_all
|
||||
}
|
||||
|
||||
78
cmd/v/flag.v
Normal file
78
cmd/v/flag.v
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright (c) 2019-2020 Alexander Medvednikov. 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
|
||||
|
||||
const (
|
||||
//list_of_flags contains a list of flags where an argument is expected past it.
|
||||
list_of_flags = [
|
||||
'-o', '-os', '-cc', '-cflags', '-d'
|
||||
]
|
||||
)
|
||||
|
||||
fn get_basic_command_and_option(args []string) (string, []string) {
|
||||
mut option := []string
|
||||
for i, arg in args {
|
||||
if i == 0 {
|
||||
//Skip executable
|
||||
continue
|
||||
}
|
||||
if arg == '--' {
|
||||
//End of list of options. The next one is the command.
|
||||
if i+1 < os.args.len {
|
||||
return os.args[i+1], option
|
||||
}
|
||||
//There's no option past this
|
||||
return '', option
|
||||
}
|
||||
if arg in list_of_flags {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if arg[0] == `-` {
|
||||
option << arg
|
||||
continue
|
||||
}
|
||||
//It's not a flag. We did not skip it. It's a command.
|
||||
return arg, option
|
||||
}
|
||||
|
||||
//There's no arguments that were not part of a flag.
|
||||
return '', option
|
||||
}
|
||||
|
||||
fn non_empty(arg []string) []string {
|
||||
return arg.filter(it != '')
|
||||
}
|
||||
|
||||
fn join_flags_and_argument() []string {
|
||||
vosargs := os.getenv('VOSARGS')
|
||||
if vosargs != '' {
|
||||
return non_empty(vosargs.split(' '))
|
||||
}
|
||||
|
||||
mut args := []string
|
||||
vflags := os.getenv('VFLAGS')
|
||||
if vflags != '' {
|
||||
args << os.args[0]
|
||||
args << vflags.split(' ')
|
||||
if os.args.len > 1 {
|
||||
args << os.args[1..]
|
||||
}
|
||||
return non_empty(args)
|
||||
}
|
||||
|
||||
return non_empty(os.args)
|
||||
}
|
||||
|
||||
fn vexe_path() string {
|
||||
vexe := os.getenv('VEXE')
|
||||
if vexe != '' {
|
||||
return vexe
|
||||
}
|
||||
real_vexe_path := os.realpath(os.executable())
|
||||
os.setenv('VEXE', real_vexe_path, true)
|
||||
return real_vexe_path
|
||||
}
|
||||
101
cmd/v/help.v
Normal file
101
cmd/v/help.v
Normal file
@@ -0,0 +1,101 @@
|
||||
module main
|
||||
|
||||
const (
|
||||
help_text = 'Usage: v [options/commands] [file.v | directory]
|
||||
|
||||
To run V in REPL mode, run V without any arguments.
|
||||
To compile a directory/file, pass it as the only argument.
|
||||
|
||||
To run a directory/file, use `v run [file.v | directory]`. V will compile and run it for you.
|
||||
|
||||
This help message is only intended to be a quick start guide. For a comprehensive help message, use `v help --verbose`.'
|
||||
verbose_help_text = 'Usage: v [options/commands] [file.v | directory]
|
||||
|
||||
When V is run without any arguments, it is run in REPL mode.
|
||||
|
||||
When given a .v file, it will be compiled. The executable will have the
|
||||
same name as the input .v file: `v foo.v` produces `./foo` on *nix systems,
|
||||
`foo.exe` on Windows.
|
||||
|
||||
You can use -o to specify a different output executable\'s name.
|
||||
|
||||
When given a directory, all .v files contained in it will be compiled as
|
||||
part of a single main module.
|
||||
|
||||
By default the executable will have the same name as the directory.
|
||||
|
||||
To compile all V files in current directory, run `v .`
|
||||
|
||||
Any file ending in _test.v, will be treated as a test.
|
||||
It will be compiled and run, evaluating the assert statements in every
|
||||
function named test_xxx.
|
||||
|
||||
You can put common options inside an environment variable named VFLAGS, so that
|
||||
you don\'t have to repeat them.
|
||||
|
||||
You can set it like this: `export VFLAGS="-cc clang -debug"` on *nix,
|
||||
`set VFLAGS=-cc msvc` on Windows.
|
||||
|
||||
V respects the TMPDIR environment variable, and will put .tmp.c files in TMPDIR/v/ .
|
||||
If you have not set it, a suitable platform specific folder (like /tmp) will be used.
|
||||
|
||||
Options/commands:
|
||||
-h, help Display this information.
|
||||
-o <file> Write output to <file>.
|
||||
-o <file>.c Produce C source without compiling it.
|
||||
-o <file>.js Produce JavaScript source.
|
||||
-prod Build an optimized executable.
|
||||
-v, version Display compiler version and git hash of the compiler source.
|
||||
-verbose Produce a verbose log about what the compiler is doing, where it seeks for files and so on.
|
||||
-live Enable hot code reloading (required by functions marked with [live]).
|
||||
-os <OS> Produce an executable for the selected OS.
|
||||
OS can be linux, mac, windows, msvc.
|
||||
Use msvc if you want to use the MSVC compiler on Windows.
|
||||
-shared Build a shared library.
|
||||
-stats Show additional stats when compiling/running tests. Try `v -stats test .`
|
||||
|
||||
-cache Turn on usage of the precompiled module cache.
|
||||
It very significantly speeds up secondary compilations.
|
||||
|
||||
-obf Obfuscate the resulting binary.
|
||||
-compress Compress the resulting binary.
|
||||
- Shorthand for `v repl`.
|
||||
|
||||
Options for debugging/troubleshooting v programs:
|
||||
-g Generate debugging information in the backtraces. Add *V* line numbers to the generated executable.
|
||||
-cg Same as -g, but add *C* line numbers to the generated executable instead of *V* line numbers.
|
||||
-keep_c Do NOT remove the generated .tmp.c files after compilation.
|
||||
It is useful when using debuggers like gdb/visual studio, when given after `-g` / `-cg`.
|
||||
-pretty_c Run clang-format over the generated C file, so that it looks nicer. Requires you to have clang-format.
|
||||
-show_c_cmd Print the full C compilation command and how much time it took. See also `-verbose`.
|
||||
-cc <ccompiler> Specify which C compiler you want to use as a C backend.
|
||||
The C backend compiler should be able to handle C99 compatible C code.
|
||||
Common C compilers are gcc, clang, tcc, icc, cl...
|
||||
-cflags <flags> Pass additional C flags to the C backend compiler.
|
||||
Example: -cflags `sdl2-config --cflags`
|
||||
|
||||
Commands:
|
||||
up Update V. Run `v up` at least once per day, since V development is rapid and features/bugfixes are added constantly.
|
||||
run <file.v> Build and execute the V program in file.v. You can add arguments for the V program *after* the file name.
|
||||
build <module> Compile a module into an object file.
|
||||
repl Run the V REPL. If V is running in a tty terminal, the REPL is interactive, otherwise it just reads from stdin.
|
||||
symlink Useful on Unix systems. Symlinks the current V executable to /usr/local/bin/v, so that V is globally available.
|
||||
test-compiler Run all V test files, and compile all V examples.
|
||||
test folder/ Run all V test files located in the folder and its subfolders. You can also pass individual _test.v files too.
|
||||
fmt Run vfmt to format the source code. [wip]
|
||||
doc Run vdoc over the source code and produce documentation.
|
||||
translate Translates C to V. [wip, will be available in V 0.3]
|
||||
create Create a new v project interactively. Answer the questions, and run it with `v run projectname`
|
||||
|
||||
V package management commands:
|
||||
search keywords Search the https://vpm.vlang.io/ module repository for matching modules and shows their details.
|
||||
install <module> Install a user module from https://vpm.vlang.io/.
|
||||
update [module] Updates an already installed module, or ALL installed modules at once, when no module name is given.
|
||||
remove [module] Removes an installed module, or ALL installed modules at once, when no module name is given.
|
||||
'
|
||||
)
|
||||
/*
|
||||
- To disable automatic formatting:
|
||||
v -nofmt file.v
|
||||
*/
|
||||
|
||||
70
cmd/v/simple_tool.v
Normal file
70
cmd/v/simple_tool.v
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright (c) 2019-2020 Alexander Medvednikov. 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 (
|
||||
compiler
|
||||
filepath
|
||||
os
|
||||
)
|
||||
|
||||
fn launch_tool(is_verbose bool, tname string, cmdname string) {
|
||||
vexe := vexe_path()
|
||||
vroot := filepath.dir(vexe)
|
||||
compiler.set_vroot_folder(vroot)
|
||||
|
||||
mut tname_index := os.args.index(cmdname)
|
||||
if tname_index == -1 {
|
||||
tname_index = os.args.len
|
||||
}
|
||||
mut compilation_options := os.args[1..tname_index].clone()
|
||||
tool_args := os.args[1..].join(' ')
|
||||
tool_exe := os.realpath('$vroot/cmd/tools/$tname')
|
||||
tool_source := os.realpath('$vroot/cmd/tools/${tname}.v')
|
||||
tool_command := '"$tool_exe" $tool_args'
|
||||
if is_verbose {
|
||||
eprintln('launch_tool vexe : $vroot')
|
||||
eprintln('launch_tool vroot : $vroot')
|
||||
eprintln('launch_tool tool_args : $tool_args')
|
||||
eprintln('launch_tool tool_command: $tool_command')
|
||||
}
|
||||
|
||||
mut should_compile := false
|
||||
if !os.exists(tool_exe) {
|
||||
should_compile = true
|
||||
} else {
|
||||
if os.file_last_mod_unix(tool_exe) <= os.file_last_mod_unix(vexe) {
|
||||
// v was recompiled, maybe after v up ...
|
||||
// rebuild the tool too just in case
|
||||
should_compile = true
|
||||
}
|
||||
if os.file_last_mod_unix(tool_exe) <= os.file_last_mod_unix(tool_source) {
|
||||
// the user changed the source code of the tool
|
||||
should_compile = true
|
||||
}
|
||||
}
|
||||
if is_verbose {
|
||||
eprintln('launch_tool should_compile: $should_compile')
|
||||
}
|
||||
|
||||
if should_compile {
|
||||
if tname == 'vfmt' {
|
||||
compilation_options << ['-d', 'vfmt']
|
||||
}
|
||||
compilation_args := compilation_options.join(' ')
|
||||
compilation_command := '"$vexe" $compilation_args "$tool_source"'
|
||||
if is_verbose {
|
||||
eprintln('Compiling $tname with: "$compilation_command"')
|
||||
}
|
||||
tool_compilation := os.exec(compilation_command) or { panic(err) }
|
||||
if tool_compilation.exit_code != 0 {
|
||||
panic('V tool "$tool_source" could not be compiled\n' + tool_compilation.output)
|
||||
}
|
||||
}
|
||||
if is_verbose {
|
||||
eprintln('launch_tool running tool command: $tool_command ...')
|
||||
}
|
||||
|
||||
exit(os.system(tool_command))
|
||||
}
|
||||
31
cmd/v/symlink.v
Normal file
31
cmd/v/symlink.v
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) 2019-2020 Alexander Medvednikov. 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
|
||||
|
||||
fn create_symlink() {
|
||||
$if windows {
|
||||
return
|
||||
}
|
||||
vexe := vexe_path()
|
||||
mut link_path := '/usr/local/bin/v'
|
||||
mut ret := os.exec('ln -sf $vexe $link_path') or { panic(err) }
|
||||
if ret.exit_code == 0 {
|
||||
println('Symlink "$link_path" has been created')
|
||||
}
|
||||
else if os.system('uname -o | grep -q \'[A/a]ndroid\'') == 0 {
|
||||
println('Failed to create symlink "$link_path". Trying again with Termux path for Android.')
|
||||
link_path = '/data/data/com.termux/files/usr/bin/v'
|
||||
ret = os.exec('ln -sf $vexe $link_path') or { panic(err) }
|
||||
if ret.exit_code == 0 {
|
||||
println('Symlink "$link_path" has been created')
|
||||
} else {
|
||||
println('Failed to create symlink "$link_path". Try again with sudo.')
|
||||
}
|
||||
} else {
|
||||
println('Failed to create symlink "$link_path". Try again with sudo.')
|
||||
}
|
||||
}
|
||||
|
||||
88
cmd/v/v.v
Normal file
88
cmd/v/v.v
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright (c) 2019-2020 Alexander Medvednikov. 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 (
|
||||
compiler
|
||||
os
|
||||
)
|
||||
|
||||
const (
|
||||
simple_cmd = [
|
||||
'fmt',
|
||||
'up',
|
||||
'create',
|
||||
'test', 'test-fmt', 'test-compiler',
|
||||
'bin2v',
|
||||
'repl',
|
||||
'build-tools', 'build-examples', 'build-vbinaries'
|
||||
]
|
||||
)
|
||||
|
||||
fn main() {
|
||||
arg := join_flags_and_argument()
|
||||
command, option := get_basic_command_and_option(arg)
|
||||
|
||||
is_verbose := '-verbose' in arg || '--verbose' in arg
|
||||
|
||||
if '-v' in option || '--version' in option || command == 'version' {
|
||||
// Print the version and exit.
|
||||
version_hash := compiler.vhash()
|
||||
println('V $compiler.Version $version_hash')
|
||||
return
|
||||
}
|
||||
if '-h' in option || '--help' in option || command == 'help' {
|
||||
if is_verbose {
|
||||
println(verbose_help_text)
|
||||
} else {
|
||||
println(help_text)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if is_verbose {
|
||||
eprintln('v args: $arg')
|
||||
eprintln('v command: $command')
|
||||
eprintln('v options: $option')
|
||||
}
|
||||
|
||||
if command in simple_cmd {
|
||||
//External tools
|
||||
launch_tool(is_verbose, 'v' + command, command)
|
||||
return
|
||||
}
|
||||
|
||||
if command == 'run' || command == 'build' || command.ends_with('.v') || os.exists(command) {
|
||||
compile(command, arg)
|
||||
return
|
||||
}
|
||||
|
||||
match command {
|
||||
'', '-' {
|
||||
if arg.len == 1 {
|
||||
println('Running REPL as no arguments are provided. For usage information, use `v help`.')
|
||||
}
|
||||
launch_tool(is_verbose, 'vrepl', '')
|
||||
}
|
||||
'translate' {
|
||||
println('Translating C to V will be available in V 0.3 (January)')
|
||||
}
|
||||
'search', 'install', 'update', 'remove' {
|
||||
launch_tool(is_verbose, 'vpm', command)
|
||||
}
|
||||
'get' {
|
||||
println('Use `v install` to install modules from vpm.vlang.io.')
|
||||
}
|
||||
'symlink' {
|
||||
create_symlink()
|
||||
}
|
||||
'doc' {
|
||||
println('Currently unimplemented')
|
||||
}
|
||||
else {
|
||||
eprintln('v $command: unknown command\nRun "v help" for usage.')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user