v test v => v test-compiler

pull/2944/head
Delyan Angelov 2019-12-01 11:50:13 +02:00 committed by Alexander Medvednikov
parent 854309a7d8
commit ec15bfb7d1
29 changed files with 431 additions and 280 deletions

View File

@ -1,6 +1,7 @@
**Please delete this information after reading it.* **Please delete this information after reading it.*
Please title your PR as follows: `time: fix foo bar`. Always start with the thing you are fixing, then describe the fix. Please title your PR as follows: `time: fix foo bar`.
Always start with the thing you are fixing, then describe the fix.
Don't use past tense (e.g. "fixed foo bar"). Don't use past tense (e.g. "fixed foo bar").
Explain what your PR does and why. Explain what your PR does and why.
@ -20,10 +21,13 @@ fn test_foo() {
If you are fixing a bug, please add a test that covers it. If you are fixing a bug, please add a test that covers it.
Before submitting a PR, please run the tests with `v test v`, and make sure V can still compile itself. Run this twice: Before submitting a PR, please:
A) run the tests with `v test-compiler` .
B) make sure, that V can still compile itself:
```shell
./v -o v v.v ./v -o v v.v
./v -o v v.v ./v -o v v.v
```
I try to process PRs as soon as possible. They should be handled within 24 hours. I try to process PRs as soon as possible. They should be handled within 24 hours.

View File

@ -10,6 +10,6 @@ du -s .
ls -lat ls -lat
./v test v ./v test-compiler
echo "DONE" echo "DONE"

View File

@ -36,7 +36,7 @@ jobs:
- name: Build V using V - name: Build V using V
run: ./v -o v2 v.v && ./v2 -o v3 v.v run: ./v -o v2 v.v && ./v2 -o v3 v.v
- name: Test v->c - name: Test v->c
run: ./v test v run: ./v test-compiler
# - name: Test v->js # - name: Test v->js
# run: ./v -o hi.js examples/hello_v_js.v && node hi.js # run: ./v -o hi.js examples/hello_v_js.v && node hi.js
- name: Test symlink - name: Test symlink
@ -58,7 +58,7 @@ jobs:
- name: Build V - name: Build V
run: make && ./v -cc gcc -o v v.v run: make && ./v -cc gcc -o v v.v
- name: Test V - name: Test V
run: ./v test v run: ./v test-compiler
# - name: Test v->js # - name: Test v->js
# run: ./v -o hi.js examples/hello_v_js.v && node hi.js # run: ./v -o hi.js examples/hello_v_js.v && node hi.js
- name: Build Vorum - name: Build Vorum
@ -110,7 +110,7 @@ jobs:
sudo ln -s /var/tmp/tcc/bin/tcc /usr/local/bin/tcc sudo ln -s /var/tmp/tcc/bin/tcc /usr/local/bin/tcc
tcc -version tcc -version
./v -o v2 v.v # Make sure vtcc can build itself ./v -o v2 v.v # Make sure vtcc can build itself
./v test v ./v test-compiler
ubuntu-musl: ubuntu-musl:
runs-on: ubuntu-18.04 runs-on: ubuntu-18.04
@ -124,7 +124,7 @@ jobs:
- name: Build v - name: Build v
run: make && ./v -cc musl-gcc -o v v.v run: make && ./v -cc musl-gcc -o v v.v
# - name: Test v->c # - name: Test v->c
# run: ./v test v # run: ./v test-compiler
# - name: Test v->js # - name: Test v->js
# run: ./v -o hi.js examples/hello_v_js.v && node hi.js # run: ./v -o hi.js examples/hello_v_js.v && node hi.js
@ -142,7 +142,7 @@ jobs:
.\make.bat -gcc .\make.bat -gcc
- name: Test - name: Test
run: | run: |
.\v.exe test v .\v.exe test-compiler
## v.js dosent work on windows ## v.js dosent work on windows
#.\v.exe -o hi.js examples/hello_v_js.v #.\v.exe -o hi.js examples/hello_v_js.v
#node hi.js #node hi.js
@ -164,7 +164,7 @@ jobs:
env: env:
VFLAGS: -cc msvc VFLAGS: -cc msvc
run: | run: |
.\v.exe test v .\v.exe test-compiler
## v.js dosent work on windows ## v.js dosent work on windows
#.\v.exe -o hi.js examples/hello_v_js.v #.\v.exe -o hi.js examples/hello_v_js.v
#node hi.js #node hi.js

10
.gitignore vendored
View File

@ -1,3 +1,4 @@
fns.txt
*.dSYM *.dSYM
*_test *_test
/v /v
@ -12,12 +13,18 @@
/tools/vrepl.exe /tools/vrepl.exe
/tools/vtest /tools/vtest
/tools/vtest.exe /tools/vtest.exe
/tools/vtest-compiler
/tools/vtest-compiler.exe
/tools/vup /tools/vup
/tools/vup.exe /tools/vup.exe
/tools/vpm /tools/vpm
/tools/vpm.exe /tools/vpm.exe
/tools/vcreate /tools/vcreate
/tools/vcreate.exe /tools/vcreate.exe
/tools/vbuild-examples
/tools/vbuild-examples.exe
/tools/vbuild-tools
/tools/vbuild-tools.exe
*.exe *.exe
*.o *.o
.*.c .*.c
@ -38,3 +45,6 @@ vjs
._* ._*
.vrepl_temp.v .vrepl_temp.v
a.out a.out
vlib/os/bare/bare_example_linux
info.log

5
examples/.gitignore vendored
View File

@ -12,3 +12,8 @@
/hello_v_js /hello_v_js
/fibonacci /fibonacci
/sqlite /sqlite
empty_gg_freetype
game_of_life/life_gg
random_ips
vweb/vweb_example
x64/hello_world

View File

@ -1,7 +1,12 @@
import log import log
fn main() { fn main() {
mut l := log.Log { log.LogLevel.info, 'info', true } mut l := log.Log{}
l.set_level(log.INFO)
// Make a new file called info.log in the current folder
l.set_full_logpath('./info.log')
println('Please check the file: ${l.output_file_name} after this example crashes.')
l.info('info') l.info('info')
l.warn('warn') l.warn('warn')
l.error('error') l.error('error')

9
tools/.gitignore vendored 100644
View File

@ -0,0 +1,9 @@
gen_vc
performance_compare
vcreate
vnames
vpm
vrepl
vtest
vtest-compiler
vup

View File

@ -39,7 +39,7 @@ fn main() {
commit_date := exec('git log -n1 --pretty="format:%at"') commit_date := exec('git log -n1 --pretty="format:%at"')
message := exec('git log -n1 --pretty="format:%s"') message := exec('git log -n1 --pretty="format:%s"')
date := time.unix(commit_date.int()) date := time.unix(commit_date.int())
out := os.create('table.html') or { panic(err) } mut out := os.create('table.html') or { panic(err) }
// Place the new row on top // Place the new row on top
table = table =
'<tr> '<tr>
@ -57,7 +57,7 @@ fn main() {
// Regenerate index.html // Regenerate index.html
header := os.read_file('header.html') or { panic(err) } header := os.read_file('header.html') or { panic(err) }
footer := os.read_file('footer.html') or { panic(err) } footer := os.read_file('footer.html') or { panic(err) }
res := os.create('index.html') or { panic(err) } mut res := os.create('index.html') or { panic(err) }
res.writeln(header) res.writeln(header)
res.writeln(table) res.writeln(table)
res.writeln(footer) res.writeln(footer)

View File

@ -45,7 +45,7 @@ const(
// name // name
app_name = 'gen_vc' app_name = 'gen_vc'
// version // version
app_version = '0.1.0' app_version = '0.1.1'
// description // description
app_description = 'This tool regenerates V\'s bootstrap .c files every time the V master branch is updated.' 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 // assume something went wrong if file size less than this
@ -114,17 +114,22 @@ fn main() {
fp.version(app_version) fp.version(app_version)
fp.description(app_description) fp.description(app_description)
fp.skip_executable() fp.skip_executable()
show_help:=fp.bool('help', false, 'Show this help screen\n')
flag_options := parse_flags(mut fp) flag_options := parse_flags(mut fp)
_ = fp.finalize() or { if( show_help ){ println( fp.usage() ) exit(0) }
fp.finalize() or {
eprintln(err) eprintln(err)
println(fp.usage()) println(fp.usage())
return return
} }
// webhook server mode // webhook server mode
if flag_options.serve { if flag_options.serve {
vweb.run<WebhookServer>(flag_options.port) app := WebhookServer{ gen_vc: new_gen_vc(flag_options) }
vweb.run(mut app, flag_options.port)
} }
// cmd mode // cmd mode
else { else {
@ -136,15 +141,14 @@ fn main() {
// new GenVC // new GenVC
fn new_gen_vc(flag_options FlagOptions) &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{ return &GenVC{
// options
options: flag_options options: flag_options
// logger logger: logger
logger: if flag_options.log_to == 'file' {
&log.Log{log.DEBUG, flag_options.log_file}
} else {
&log.Log{log.DEBUG, 'terminal'}
}
} }
} }
@ -175,7 +179,7 @@ fn parse_flags(fp mut flag.FlagParser) FlagOptions {
purge : fp.bool('purge', false, 'force purge the local repositories') purge : fp.bool('purge', false, 'force purge the local repositories')
port : fp.int('port', int(server_port), 'port for web server to listen on') port : fp.int('port', int(server_port), 'port for web server to listen on')
log_to : fp.string('log-to', log_to, 'log to is \'file\' or \'terminal\'') 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\'') 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') dry_run : fp.bool('dry-run', dry_run, 'when specified dont push anything to remote repo')
} }
} }

View File

@ -0,0 +1,126 @@
module testing
import (
os
term
benchmark
filepath
)
pub struct TestSession {
pub mut:
files []string
vexe string
vargs string
failed bool
benchmark benchmark.Benchmark
ok string
fail string
}
pub fn new_test_sesion(vargs string) TestSession {
return TestSession{
vexe: vexe_path()
vargs: vargs
}
}
pub fn vexe_path() string {
// NB: tools extracted from v require that the first
// argument to them to be the v executable location.
// They are usually launched by vlib/compiler/vtools.v,
// launch_tool/1 , which provides it.
return os.args[1]
}
pub fn (ts mut TestSession) init() {
ts.ok = term.ok_message('OK')
ts.fail = term.fail_message('FAIL')
ts.benchmark = benchmark.new_benchmark()
}
pub fn (ts mut TestSession) test() {
ts.init()
show_stats := '-stats' in ts.vargs.split(' ')
for dot_relative_file in ts.files {
relative_file := dot_relative_file.replace('./', '')
file := os.realpath( relative_file )
$if windows {
if file.contains('sqlite') { continue }
}
$if msvc {
if file.contains('asm') { continue }
}
$if tinyc {
if file.contains('asm') { continue }
}
tmpc_filepath := file.replace('.v', '.tmp.c')
cmd := '"$ts.vexe" $ts.vargs "$file"'
ts.benchmark.step()
if show_stats {
println('-------------------------------------------------')
status := os.system(cmd)
if status == 0 {
ts.benchmark.ok()
}else{
ts.benchmark.fail()
ts.failed = true
continue
}
}else{
r := os.exec(cmd) or {
ts.benchmark.fail()
ts.failed = true
println(ts.benchmark.step_message('$relative_file ${ts.fail}'))
continue
}
if r.exit_code != 0 {
ts.benchmark.fail()
ts.failed = true
println(ts.benchmark.step_message('$relative_file ${ts.fail}\n`$file`\n (\n$r.output\n)'))
} else {
ts.benchmark.ok()
println(ts.benchmark.step_message('$relative_file ${ts.ok}'))
}
}
os.rm( tmpc_filepath )
}
ts.benchmark.stop()
}
pub fn vlib_should_be_present( parent_dir string ) {
vlib_dir := filepath.join( parent_dir, 'vlib' )
if !os.dir_exists( vlib_dir ){
println('$vlib_dir is missing, it must be next to the V executable')
exit(1)
}
}
pub fn v_build_failing(vargs string, folder string) bool {
main_label := 'Building $folder ...'
finish_label := 'building $folder'
vexe := vexe_path()
parent_dir := os.dir(vexe)
vlib_should_be_present( parent_dir )
println(main_label)
mut session := new_test_sesion( vargs )
files := os.walk_ext(filepath.join(parent_dir, folder),'.v')
mains := files.filter(!it.contains('modules'))
mut rebuildable_mains := mains
if os.user_os() == 'windows' {
// on windows, an executable can not be rebuilt, while it is running
myself := os.executable().replace('.exe', '') + '.v'
mains_without_myself := mains.filter(!it.contains(myself))
rebuildable_mains = mains_without_myself // workaround a bug in it.contains generation
}
session.files << rebuildable_mains
session.test()
println( session.benchmark.total_message( finish_label ) )
return session.failed
}

View 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-examples'), 'examples') {
exit(1)
}
}

View 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'), 'tools') {
exit(1)
}
}

View File

@ -19,7 +19,7 @@ fn cerror(e string){
} }
fn (c Create)write_vmod() { fn (c Create)write_vmod() {
vmod := os.create('${c.name}/v.mod') or { cerror(err) exit(1) } mut vmod := os.create('${c.name}/v.mod') or { cerror(err) exit(1) }
mut vmod_content := []string mut vmod_content := []string
vmod_content << '#V Project#\n' vmod_content << '#V Project#\n'
vmod_content << 'Module {' vmod_content << 'Module {'
@ -31,7 +31,7 @@ fn (c Create)write_vmod() {
} }
fn (c Create)write_main() { fn (c Create)write_main() {
main := os.create('${c.name}/${c.name}.v') or { cerror(err) exit(2) } mut main := os.create('${c.name}/${c.name}.v') or { cerror(err) exit(2) }
mut main_content := []string mut main_content := []string
main_content << 'module main\n' main_content << 'module main\n'
main_content << 'fn main() {' main_content << 'fn main() {'

View File

@ -0,0 +1,77 @@
module main
import (
os
testing
benchmark
)
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 := os.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.file_exists(parent_dir + '/v.v') {
println('v.v is missing, it must be next to the V executable')
exit(1)
}
*/
// Make sure v.c can be compiled without warnings
$if mac {
if os.file_exists('/v.v') {
os.system('$vexe -o v.c v.v')
if os.system('cc -Werror v.c') != 0 {
println('cc failed to build v.c without warnings')
exit(1)
}
println('v.c can be compiled without warnings. This is good :)')
}
}
building_tools_failed := testing.v_build_failing(vargs, 'tools')
println('\nTesting all _test.v files...')
mut compiler_test_session := testing.new_test_sesion( vargs )
compiler_test_session.files << os.walk_ext(parent_dir, '_test.v')
compiler_test_session.test()
println( compiler_test_session.benchmark.total_message('running V tests') )
println('')
building_examples_failed := testing.v_build_failing(vargs, 'examples')
v_module_install_cmd := '$vexe install nedpals.args'
println('\nInstalling a v module with: $v_module_install_cmd ')
mut vmark := benchmark.new_benchmark()
ret := os.system(v_module_install_cmd)
if ret != 0 {
println('failed to run v install')
exit(1)
}
if !os.file_exists(v_modules_path + '/nedpals/args') {
println('v failed to install a test module')
exit(1)
}
vmark.stop()
println( 'Installing a v module took: ' + vmark.total_duration().str() + 'ms')
if building_tools_failed || compiler_test_session.failed || building_examples_failed {
exit(1)
}
}

View File

@ -2,44 +2,17 @@ module main
import ( import (
os os
term testing
benchmark
) )
struct TestSession {
mut:
files []string
vexe string
vargs string
failed bool
benchmark benchmark.Benchmark
}
pub fn new_test_sesion(vargs string) TestSession {
return TestSession{
vexe: vexe_path()
vargs: vargs
}
}
fn vexe_path() string {
// NB: tools extracted from v require that the first
// argument to them to be the v executable location.
// They are usually launched by vlib/compiler/vtools.v,
// launch_tool/1 , which provides it.
return os.args[1]
}
pub fn main() { pub fn main() {
args := os.args args := os.args
if args.last() == 'test' { if args.last() == 'test' {
println('Usage:') println('Usage:')
println(' A)') println(' A)')
println(' v test v : run all v tests and build all the examples')
println(' B)')
println(' v test folder/ : run all v tests in the given folder.') println(' v test folder/ : run all v tests in the given folder.')
println(' v -stats test folder/ : the same, but print more stats.') println(' v -stats test folder/ : the same, but print more stats.')
println(' C)') println(' B)')
println(' v test file_test.v : run test functions in a given test file.') 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(' 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(' NB: you can also give many and mixed folder/ file_test.v arguments after test.')
@ -52,11 +25,12 @@ pub fn main() {
args_after := args_string.all_after('test ') args_after := args_string.all_after('test ')
if args_after == 'v' { if args_after == 'v' {
v_test_v(args_before) eprintln('`v test v` has been deprecated.')
return eprintln('Use `v test-compiler` instead.')
exit(1)
} }
mut ts := new_test_sesion(args_before) mut ts := testing.new_test_sesion(args_before)
for targ in args_after.split(' ') { for targ in args_after.split(' ') {
if os.file_exists(targ) && targ.ends_with('_test.v') { if os.file_exists(targ) && targ.ends_with('_test.v') {
ts.files << targ ts.files << targ
@ -79,119 +53,3 @@ pub fn main() {
} }
} }
pub fn (ts mut TestSession) test() {
ok := term.ok_message('OK')
fail := term.fail_message('FAIL')
show_stats := '-stats' in ts.vargs.split(' ')
ts.benchmark = benchmark.new_benchmark()
for dot_relative_file in ts.files {
relative_file := dot_relative_file.replace('./', '')
file := os.realpath( relative_file )
$if windows {
if file.contains('sqlite') { continue }
}
$if msvc {
if file.contains('asm') { continue }
}
$if tinyc {
if file.contains('asm') { continue }
}
tmpc_filepath := file.replace('.v', '.tmp.c')
cmd := '"$ts.vexe" $ts.vargs "$file"'
ts.benchmark.step()
if show_stats {
println('-------------------------------------------------')
status := os.system(cmd)
if status == 0 {
ts.benchmark.ok()
}else{
ts.benchmark.fail()
ts.failed = true
continue
}
}else{
r := os.exec(cmd) or {
ts.benchmark.fail()
ts.failed = true
println(ts.benchmark.step_message('$relative_file $fail'))
continue
}
if r.exit_code != 0 {
ts.benchmark.fail()
ts.failed = true
println(ts.benchmark.step_message('$relative_file $fail\n`$file`\n (\n$r.output\n)'))
} else {
ts.benchmark.ok()
println(ts.benchmark.step_message('$relative_file $ok'))
}
}
os.rm( tmpc_filepath )
}
ts.benchmark.stop()
}
pub fn v_test_v(args_before_test string){
vexe := vexe_path()
parent_dir := os.dir(vexe)
// 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.dir_exists(parent_dir + '/vlib') {
println('vlib/ is missing, it must be next to the V executable')
exit(1)
}
/*
if !os.file_exists(parent_dir + '/v.v') {
println('v.v is missing, it must be next to the V executable')
exit(1)
}
*/
// Make sure v.c can be compiled without warnings
$if mac {
if os.file_exists('/v.v') {
os.system('$vexe -o v.c v.v')
if os.system('cc -Werror v.c') != 0 {
println('cc failed to build v.c without warnings')
exit(1)
}
println('v.c can be compiled without warnings. This is good :)')
}
}
//
println('Testing...')
mut ts := new_test_sesion( args_before_test )
ts.files << os.walk_ext(parent_dir, '_test.v')
ts.test()
println( ts.benchmark.total_message('running V tests') )
//
println('\nBuilding examples...')
mut es := new_test_sesion( args_before_test )
files := os.walk_ext(parent_dir+'/examples','.v')
stable := files.filter(!it.contains('automaton.v') && !it.contains('some_module.v'))
es.files << stable
es.test()
println( es.benchmark.total_message('building examples') )
//
test_vget()
if ts.failed || es.failed {
exit(1)
}
}
pub fn test_vget() {
/*
vexe := vexe_path()
ret := os.system('$vexe install nedpals.args')
if ret != 0 {
println('failed to run v install')
exit(1)
}
if !os.file_exists(v_modules_path + '/nedpals/args') {
println('v failed to install a test module')
exit(1)
}
println('vget is OK')
*/
}

20
v.v
View File

@ -25,6 +25,14 @@ fn main() {
stuff_after_executable := os.args[1..] stuff_after_executable := os.args[1..]
commands := stuff_after_executable.filter(!it.starts_with('-')) commands := stuff_after_executable.filter(!it.starts_with('-'))
simple_tools := ['up', 'create', 'test', 'test-compiler', 'build-tools', 'build-examples']
for tool in simple_tools {
if tool in commands {
compiler.launch_tool('v$tool')
return
}
}
// Print the version and exit. // Print the version and exit.
if '-v' in options || '--version' in options || 'version' in commands { if '-v' in options || '--version' in options || 'version' in commands {
version_hash := compiler.vhash() version_hash := compiler.vhash()
@ -39,10 +47,6 @@ fn main() {
println('Translating C to V will be available in V 0.3') println('Translating C to V will be available in V 0.3')
return return
} }
else if 'up' in commands {
compiler.launch_tool('vup')
return
}
else if 'search' in commands || 'install' in commands || 'update' in commands || 'remove' in commands { else if 'search' in commands || 'install' in commands || 'update' in commands || 'remove' in commands {
compiler.launch_tool('vpm') compiler.launch_tool('vpm')
return return
@ -55,10 +59,6 @@ fn main() {
compiler.create_symlink() compiler.create_symlink()
return return
} }
else if 'create' in commands {
compiler.launch_tool('vcreate')
return
}
// TODO quit if the v compiler is too old // TODO quit if the v compiler is too old
// u := os.file_last_mod_unix('v') // u := os.file_last_mod_unix('v')
// If there's no tmp path with current version yet, the user must be using a pre-built package // If there's no tmp path with current version yet, the user must be using a pre-built package
@ -68,10 +68,6 @@ fn main() {
compiler.vfmt(args) compiler.vfmt(args)
return return
} }
else if 'test' in commands {
compiler.launch_tool('vtest')
return
}
// No args? REPL // No args? REPL
else if 'runrepl' in commands || commands.len == 0 || (args.len == 2 && args[1] == '-') { else if 'runrepl' in commands || commands.len == 0 || (args.len == 2 && args[1] == '-') {
compiler.launch_tool('vrepl') compiler.launch_tool('vrepl')

View File

@ -42,7 +42,7 @@ mut:
fn new_cgen(out_name_c string) &CGen { fn new_cgen(out_name_c string) &CGen {
path := out_name_c path := out_name_c
out := os.create(path) or { mut out := os.create(path) or {
println('failed to create $path') println('failed to create $path')
return &CGen{} return &CGen{}
} }

View File

@ -38,7 +38,7 @@ fn generate_vh(mod string) {
os.mkdir_all(pdir) os.mkdir_all(pdir)
// os.mkdir(os.realpath(dir)) or { panic(err) } // os.mkdir(os.realpath(dir)) or { panic(err) }
} }
out := os.create(path) or { panic(err) } mut out := os.create(path) or { panic(err) }
mod_path := mod.replace("\\", "/") mod_path := mod.replace("\\", "/")
out.writeln('// $mod_path module header\n') out.writeln('// $mod_path module header\n')
mod_def := if mod_path.contains('/') { mod_path.all_after('/') } else { mod_path } // "os" mod_def := if mod_path.contains('/') { mod_path.all_after('/') } else { mod_path } // "os"

View File

@ -200,7 +200,7 @@ fn (p mut Parser) gen_fmt() {
return return
} }
println('generating ${p.file_name}.v') println('generating ${p.file_name}.v')
out := os.create('/var/tmp/fmt/' + p.file_name) or { mut out := os.create('/var/tmp/fmt/' + p.file_name) or {
verror('failed to create fmt.v') verror('failed to create fmt.v')
return return
} }

View File

@ -96,7 +96,7 @@ pub fn (g mut Gen) generate_elf_footer() {
// -5 is for "e8 00 00 00 00" // -5 is for "e8 00 00 00 00"
g.write64_at(int(g.main_fn_addr - g.code_start_pos) - 5, g.code_start_pos+1) g.write64_at(int(g.main_fn_addr - g.code_start_pos) - 5, g.code_start_pos+1)
// Create the binary // Create the binary
f := os.create(g.out_name) or { panic(err) } mut f := os.create(g.out_name) or { panic(err) }
os.chmod(g.out_name, 0775) os.chmod(g.out_name, 0775)
f.write_bytes(g.buf.data, g.buf.len) f.write_bytes(g.buf.data, g.buf.len)
f.close() f.close()

View File

@ -45,7 +45,7 @@ module flag
// ``` // ```
// data object storing information about a defined flag // data object storing information about a defined flag
struct Flag { pub struct Flag {
pub: pub:
name string // name as it appears on command line name string // name as it appears on command line
abbr byte // shortcut abbr byte // shortcut
@ -55,7 +55,7 @@ pub:
} }
// //
struct FlagParser { pub struct FlagParser {
pub mut: pub mut:
args []string // the arguments to be parsed args []string // the arguments to be parsed
flags []Flag // registered flags flags []Flag // registered flags
@ -69,7 +69,7 @@ pub mut:
args_description string args_description string
} }
const ( pub const (
// used for formating usage message // used for formating usage message
SPACE = ' ' SPACE = ' '
UNDERLINE = '-----------------------------------------------' UNDERLINE = '-----------------------------------------------'
@ -125,14 +125,13 @@ fn (fs mut FlagParser) parse_value(n string, ab byte) ?string {
c := '--$n' c := '--$n'
for i, a in fs.args { for i, a in fs.args {
if a == c || (a.len == 2 && a[1] == ab) { if a == c || (a.len == 2 && a[1] == ab) {
if fs.args.len > i+1 && fs.args[i+1].left(2) != '--' { if i+1 > fs.args.len { panic('Missing argument for \'$n\'') }
val := fs.args[i+1] nextarg := fs.args[i+1]
fs.args.delete(i+1) if nextarg.limit(2) == '--' { panic('Missing argument for \'$n\'') }
fs.args.delete(i) val := fs.args[i+1]
return val fs.args.delete(i+1)
} else { fs.args.delete(i)
panic('Missing argument for \'$n\'') return val
}
} else if a.len > c.len && c == a[..c.len] && a[c.len..c.len+1] == '=' { } else if a.len > c.len && c == a[..c.len] && a[c.len..c.len+1] == '=' {
val := a[c.len+1..] val := a[c.len+1..]
fs.args.delete(i) fs.args.delete(i)

View File

@ -247,3 +247,20 @@ fn test_allow_abreviations() {
u := fp.usage() u := fp.usage()
assert u.contains(' -v') && u.contains(' -o') && u.contains(' -i') && u.contains(' -f') assert u.contains(' -v') && u.contains(' -o') && u.contains(' -i') && u.contains(' -f')
} }
fn test_allow_kebab_options() {
default_value := 'this_is_the_default_value_of_long_option'
long_option_value := 'this_is_a_long_option_value_as_argument'
mut fp := flag.new_flag_parser(['--my-long-flag', 'true', '--my-long-option', long_option_value ])
my_flag := fp.bool('my-long-flag', false, 'flag with long-kebab-name')
my_option := fp.string('my-long-option', default_value, 'string with long-kebab-name')
assert my_flag == true
assert my_option == long_option_value
u := fp.usage()
assert u.contains(' --my-long-flag')
assert u.contains(' --my-long-option')
}

View File

@ -3,11 +3,12 @@ module log
import os import os
import time import time
import term import term
import filepath
pub enum LogLevel { pub enum LogLevel {
fatal fatal = 1
error error
warning warn
info info
debug debug
} }
@ -16,8 +17,8 @@ fn tag(l LogLevel) string {
return match l { return match l {
.fatal { term.red('F') } .fatal { term.red('F') }
.error { term.red('E') } .error { term.red('E') }
.warning { term.yellow('W') } .warn { term.yellow('W') }
.info { term.white('I') } .info { term.white('I') }
.debug { term.blue('D') } .debug { term.blue('D') }
else { ' ' } else { ' ' }
} }
@ -40,17 +41,21 @@ interface Logger {
} }
pub struct Log { pub struct Log {
mut: mut:
level LogLevel level LogLevel
output_label string output_label string
ofile os.File
output_to_file bool output_to_file bool
pub:
output_file_name string
} }
pub fn (l mut Log) set_level(level int){ pub fn (l mut Log) set_level(level int){
l.level = match level { l.level = match level {
FATAL { LogLevel.fatal } FATAL { LogLevel.fatal }
ERROR { LogLevel.error } ERROR { LogLevel.error }
WARN { LogLevel.warning } WARN { LogLevel.warn }
INFO { LogLevel.info } INFO { LogLevel.info }
DEBUG { LogLevel.debug } DEBUG { LogLevel.debug }
else { .debug } else { .debug }
@ -61,79 +66,74 @@ pub fn (l mut Log) set_output_level(level LogLevel){
l.level = level l.level = level
} }
pub fn (l mut Log) set_output_label(label string) { pub fn (l mut Log) set_full_logpath(full_log_path string) {
rlog_file := os.realpath( full_log_path )
l.set_output_label( os.filename( rlog_file ) )
l.set_output_path( os.basedir( rlog_file ) )
}
pub fn (l mut Log) set_output_label(label string){
l.output_label = label l.output_label = label
} }
pub fn (l mut Log) set_output(output string){ pub fn (l mut Log) set_output_path(output_file_path string) {
l.output_label = output if l.ofile.is_opened() { l.ofile.close() }
l.output_to_file = true
l.output_file_name = filepath.join( os.realpath( output_file_path ) , l.output_label )
mut ofile := os.open_append( l.output_file_name ) or {
panic('error while opening log file ${l.output_file_name} for appending')
}
l.ofile = ofile
} }
fn (l Log) log_file(s string, level LogLevel) { pub fn (l mut Log) close(){
filename := '${l.output_label}.log'.replace(' ', '') l.ofile.close()
f := os.open_append(filename) or { }
panic('error reading file $filename')
} fn (l mut Log) log_file(s string, level LogLevel) {
timestamp := time.now().format_ss() timestamp := time.now().format_ss()
e := tag(level) e := tag(level)
f.writeln('$timestamp [$e] $s') l.ofile.writeln('$timestamp [$e] $s')
} }
fn (l Log) log_cli(s string, level LogLevel) { fn (l mut Log) log_cli(s string, level LogLevel) {
f := tag(level) f := tag(level)
t := time.now() t := time.now()
println('[$f ${t.format_ss()}] $s') println('[$f ${t.format_ss()}] $s')
} }
pub fn (l Log) fatal(s string){ fn (l mut Log) send_output(s &string, level LogLevel){
if l.level == .fatal {
if l.output_to_file {
l.log_file(s, .fatal)
} else {
l.log_cli(s, .fatal)
}
panic('$l.output_label: $s')
}
}
pub fn (l Log) error(s string){
if l.level in [.info, .debug, .warning, .error] {
if l.output_to_file {
l.log_file(s, .error)
} else {
l.log_cli(s, .error)
}
}
}
pub fn (l Log) warn(s string){
if l.level in [.info, .debug, .warning] {
if l.output_to_file {
l.log_file(s, .warning)
} else {
l.log_cli(s, .warning)
}
}
}
pub fn (l Log) info(s string){
if l.level in [.info, .debug] {
if l.output_to_file {
l.log_file(s, .info)
} else {
l.log_cli(s, .info)
}
}
}
pub fn (l Log) debug(s string){
if l.level != .debug {
return
}
if l.output_to_file { if l.output_to_file {
l.log_file(s, .debug) l.log_file(s, level)
} else { } else {
l.log_cli(s, .debug) l.log_cli(s, level)
} }
} }
pub fn (l mut Log) fatal(s string){
if l.level < .fatal { return }
l.send_output(s, .fatal)
l.ofile.close()
panic('$l.output_label: $s')
}
pub fn (l mut Log) error(s string){
if l.level < .error { return }
l.send_output(s, .error)
}
pub fn (l mut Log) warn(s string){
if l.level < .warn { return }
l.send_output(s, .warn)
}
pub fn (l mut Log) info(s string){
if l.level < .info { return }
l.send_output(s, .info)
}
pub fn (l mut Log) debug(s string){
if l.level < .debug { return }
l.send_output(s, .debug)
}

View File

@ -33,6 +33,8 @@ pub const (
pub struct File { pub struct File {
cfile voidptr // Using void* instead of FILE* cfile voidptr // Using void* instead of FILE*
mut:
opened bool
} }
struct FileInfo { struct FileInfo {
@ -68,14 +70,17 @@ fn C.getenv(byteptr) &char
fn C.sigaction(int, voidptr, int) fn C.sigaction(int, voidptr, int)
pub fn (f File) is_opened() bool {
return f.opened
}
// read_bytes reads an amount of bytes from the beginning of the file // read_bytes reads an amount of bytes from the beginning of the file
pub fn (f File) read_bytes(size int) []byte { pub fn (f mut File) read_bytes(size int) []byte {
return f.read_bytes_at(size, 0) return f.read_bytes_at(size, 0)
} }
// read_bytes_at reads an amount of bytes at the given position in the file // read_bytes_at reads an amount of bytes at the given position in the file
pub fn (f File) read_bytes_at(size, pos int) []byte { pub fn (f mut File) read_bytes_at(size, pos int) []byte {
mut arr := [`0`].repeat(size) mut arr := [`0`].repeat(size)
C.fseek(f.cfile, pos, C.SEEK_SET) C.fseek(f.cfile, pos, C.SEEK_SET)
nreadbytes := C.fread(arr.data, 1, size, f.cfile) nreadbytes := C.fread(arr.data, 1, size, f.cfile)
@ -281,6 +286,7 @@ pub fn open(path string) ?File {
if isnil(file.cfile) { if isnil(file.cfile) {
return error('failed to open file "$path"') return error('failed to open file "$path"')
} }
file.opened = true
return file return file
} }
@ -302,6 +308,7 @@ pub fn create(path string) ?File {
if isnil(file.cfile) { if isnil(file.cfile) {
return error('failed to create file "$path"') return error('failed to create file "$path"')
} }
file.opened = true
return file return file
} }
@ -322,10 +329,11 @@ pub fn open_append(path string) ?File {
if isnil(file.cfile) { if isnil(file.cfile) {
return error('failed to create(append) file "$path"') return error('failed to create(append) file "$path"')
} }
file.opened = true
return file return file
} }
pub fn (f File) write(s string) { pub fn (f mut File) write(s string) {
C.fputs(s.str, f.cfile) C.fputs(s.str, f.cfile)
// C.fwrite(s.str, 1, s.len, f.cfile) // C.fwrite(s.str, 1, s.len, f.cfile)
} }
@ -333,17 +341,18 @@ pub fn (f File) write(s string) {
// convert any value to []byte (LittleEndian) and write it // convert any value to []byte (LittleEndian) and write it
// for example if we have write(7, 4), "07 00 00 00" gets written // for example if we have write(7, 4), "07 00 00 00" gets written
// write(0x1234, 2) => "34 12" // write(0x1234, 2) => "34 12"
pub fn (f File) write_bytes(data voidptr, size int) { pub fn (f mut File) write_bytes(data voidptr, size int) {
C.fwrite(data, 1, size, f.cfile) C.fwrite(data, 1, size, f.cfile)
} }
pub fn (f File) write_bytes_at(data voidptr, size, pos int) { pub fn (f mut File) write_bytes_at(data voidptr, size, pos int) {
C.fseek(f.cfile, pos, C.SEEK_SET) C.fseek(f.cfile, pos, C.SEEK_SET)
C.fwrite(data, 1, size, f.cfile) C.fwrite(data, 1, size, f.cfile)
C.fseek(f.cfile, 0, C.SEEK_END) C.fseek(f.cfile, 0, C.SEEK_END)
} }
pub fn (f File) writeln(s string) { pub fn (f mut File) writeln(s string) {
if !f.opened { return }
// C.fwrite(s.str, 1, s.len, f.cfile) // C.fwrite(s.str, 1, s.len, f.cfile)
// ss := s.clone() // ss := s.clone()
// TODO perf // TODO perf
@ -352,11 +361,15 @@ pub fn (f File) writeln(s string) {
C.fputs('\n', f.cfile) C.fputs('\n', f.cfile)
} }
pub fn (f File) flush() { pub fn (f mut File) flush() {
if !f.opened { return }
C.fflush(f.cfile) C.fflush(f.cfile)
} }
pub fn (f File) close() { pub fn (f mut File) close() {
if !f.opened { return }
f.opened = false
C.fflush(f.cfile)
C.fclose(f.cfile) C.fclose(f.cfile)
} }
@ -707,7 +720,7 @@ pub fn home_dir() string {
// write_file writes `text` data to a file in `path`. // write_file writes `text` data to a file in `path`.
pub fn write_file(path, text string) { pub fn write_file(path, text string) {
f := os.create(path) or { mut f := os.create(path) or {
return return
} }
f.write(text) f.write(text)

View File

@ -43,7 +43,7 @@ fn test_write_and_read_bytes() {
file_name := './byte_reader_writer.tst' file_name := './byte_reader_writer.tst'
payload := [`I`, `D`, `D`, `Q`, `D`] payload := [`I`, `D`, `D`, `Q`, `D`]
file_write := os.create(os.realpath(file_name)) or { mut file_write := os.create(os.realpath(file_name)) or {
eprintln('failed to create file $file_name') eprintln('failed to create file $file_name')
return return
} }
@ -56,7 +56,7 @@ fn test_write_and_read_bytes() {
assert payload.len == os.file_size(file_name) assert payload.len == os.file_size(file_name)
file_read := os.open(os.realpath(file_name)) or { mut file_read := os.open(os.realpath(file_name)) or {
eprintln('failed to open file $file_name') eprintln('failed to open file $file_name')
return return
} }

View File

@ -105,7 +105,7 @@ fn (am mut AssetManager) combine(asset_type string, to_file bool) string {
if !os.dir_exists(am.cache_dir) { if !os.dir_exists(am.cache_dir) {
os.mkdir(am.cache_dir) or { panic(err) } os.mkdir(am.cache_dir) or { panic(err) }
} }
file := os.create(out_file) or { mut file := os.create(out_file) or {
panic(err) panic(err)
} }
file.write(out) file.write(out)