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 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").
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.
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
```
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
./v test v
./v test-compiler
echo "DONE"

View File

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

10
.gitignore vendored
View File

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

5
examples/.gitignore vendored
View File

@ -12,3 +12,8 @@
/hello_v_js
/fibonacci
/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
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.warn('warn')
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"')
message := exec('git log -n1 --pretty="format:%s"')
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
table =
'<tr>
@ -57,7 +57,7 @@ fn main() {
// Regenerate index.html
header := os.read_file('header.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(table)
res.writeln(footer)

View File

@ -45,7 +45,7 @@ const(
// name
app_name = 'gen_vc'
// version
app_version = '0.1.0'
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
@ -114,17 +114,22 @@ fn main() {
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)
_ = fp.finalize() or {
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)
app := WebhookServer{ gen_vc: new_gen_vc(flag_options) }
vweb.run(mut app, flag_options.port)
}
// cmd mode
else {
@ -136,15 +141,14 @@ fn main() {
// 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
options: flag_options
// logger
logger: if flag_options.log_to == 'file' {
&log.Log{log.DEBUG, flag_options.log_file}
} else {
&log.Log{log.DEBUG, 'terminal'}
}
logger: logger
}
}
@ -175,7 +179,7 @@ fn parse_flags(fp mut flag.FlagParser) FlagOptions {
purge : fp.bool('purge', false, 'force purge the local repositories')
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_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')
}
}

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() {
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
vmod_content << '#V Project#\n'
vmod_content << 'Module {'
@ -31,7 +31,7 @@ fn (c Create)write_vmod() {
}
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
main_content << 'module main\n'
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 (
os
term
benchmark
testing
)
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() {
args := os.args
if args.last() == 'test' {
println('Usage:')
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 -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 -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.')
@ -52,11 +25,12 @@ pub fn main() {
args_after := args_string.all_after('test ')
if args_after == 'v' {
v_test_v(args_before)
return
eprintln('`v test v` has been deprecated.')
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(' ') {
if os.file_exists(targ) && targ.ends_with('_test.v') {
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..]
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.
if '-v' in options || '--version' in options || 'version' in commands {
version_hash := compiler.vhash()
@ -39,10 +47,6 @@ fn main() {
println('Translating C to V will be available in V 0.3')
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 {
compiler.launch_tool('vpm')
return
@ -55,10 +59,6 @@ fn main() {
compiler.create_symlink()
return
}
else if 'create' in commands {
compiler.launch_tool('vcreate')
return
}
// TODO quit if the v compiler is too old
// 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
@ -68,10 +68,6 @@ fn main() {
compiler.vfmt(args)
return
}
else if 'test' in commands {
compiler.launch_tool('vtest')
return
}
// No args? REPL
else if 'runrepl' in commands || commands.len == 0 || (args.len == 2 && args[1] == '-') {
compiler.launch_tool('vrepl')

View File

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

View File

@ -38,7 +38,7 @@ fn generate_vh(mod string) {
os.mkdir_all(pdir)
// 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("\\", "/")
out.writeln('// $mod_path module header\n')
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
}
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')
return
}

View File

@ -96,7 +96,7 @@ pub fn (g mut Gen) generate_elf_footer() {
// -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)
// 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)
f.write_bytes(g.buf.data, g.buf.len)
f.close()

View File

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

View File

@ -247,3 +247,20 @@ fn test_allow_abreviations() {
u := fp.usage()
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 time
import term
import filepath
pub enum LogLevel {
fatal
fatal = 1
error
warning
warn
info
debug
}
@ -16,8 +17,8 @@ fn tag(l LogLevel) string {
return match l {
.fatal { term.red('F') }
.error { term.red('E') }
.warning { term.yellow('W') }
.info { term.white('I') }
.warn { term.yellow('W') }
.info { term.white('I') }
.debug { term.blue('D') }
else { ' ' }
}
@ -40,17 +41,21 @@ interface Logger {
}
pub struct Log {
mut:
mut:
level LogLevel
output_label string
ofile os.File
output_to_file bool
pub:
output_file_name string
}
pub fn (l mut Log) set_level(level int){
l.level = match level {
FATAL { LogLevel.fatal }
ERROR { LogLevel.error }
WARN { LogLevel.warning }
WARN { LogLevel.warn }
INFO { LogLevel.info }
DEBUG { LogLevel.debug }
else { .debug }
@ -61,79 +66,74 @@ pub fn (l mut Log) set_output_level(level LogLevel){
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
}
pub fn (l mut Log) set_output(output string){
l.output_label = output
pub fn (l mut Log) set_output_path(output_file_path string) {
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) {
filename := '${l.output_label}.log'.replace(' ', '')
f := os.open_append(filename) or {
panic('error reading file $filename')
}
pub fn (l mut Log) close(){
l.ofile.close()
}
fn (l mut Log) log_file(s string, level LogLevel) {
timestamp := time.now().format_ss()
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)
t := time.now()
println('[$f ${t.format_ss()}] $s')
}
pub fn (l Log) fatal(s string){
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
}
fn (l mut Log) send_output(s &string, level LogLevel){
if l.output_to_file {
l.log_file(s, .debug)
l.log_file(s, level)
} 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 {
cfile voidptr // Using void* instead of FILE*
mut:
opened bool
}
struct FileInfo {
@ -68,14 +70,17 @@ fn C.getenv(byteptr) &char
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
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)
}
// 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)
C.fseek(f.cfile, pos, C.SEEK_SET)
nreadbytes := C.fread(arr.data, 1, size, f.cfile)
@ -281,6 +286,7 @@ pub fn open(path string) ?File {
if isnil(file.cfile) {
return error('failed to open file "$path"')
}
file.opened = true
return file
}
@ -302,6 +308,7 @@ pub fn create(path string) ?File {
if isnil(file.cfile) {
return error('failed to create file "$path"')
}
file.opened = true
return file
}
@ -322,10 +329,11 @@ pub fn open_append(path string) ?File {
if isnil(file.cfile) {
return error('failed to create(append) file "$path"')
}
file.opened = true
return file
}
pub fn (f File) write(s string) {
pub fn (f mut File) write(s string) {
C.fputs(s.str, 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
// for example if we have write(7, 4), "07 00 00 00" gets written
// 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)
}
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.fwrite(data, 1, size, f.cfile)
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)
// ss := s.clone()
// TODO perf
@ -352,11 +361,15 @@ pub fn (f File) writeln(s string) {
C.fputs('\n', f.cfile)
}
pub fn (f File) flush() {
pub fn (f mut File) flush() {
if !f.opened { return }
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)
}
@ -707,7 +720,7 @@ pub fn home_dir() string {
// write_file writes `text` data to a file in `path`.
pub fn write_file(path, text string) {
f := os.create(path) or {
mut f := os.create(path) or {
return
}
f.write(text)

View File

@ -43,7 +43,7 @@ fn test_write_and_read_bytes() {
file_name := './byte_reader_writer.tst'
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')
return
}
@ -56,7 +56,7 @@ fn test_write_and_read_bytes() {
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')
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) {
os.mkdir(am.cache_dir) or { panic(err) }
}
file := os.create(out_file) or {
mut file := os.create(out_file) or {
panic(err)
}
file.write(out)