From ec15bfb7d10a6a3fe654b8096854f80c7e005b93 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Sun, 1 Dec 2019 11:50:13 +0200 Subject: [PATCH] v test v => v test-compiler --- .github/PULL_REQUEST_TEMPLATE | 10 +- .github/workflows/alpine.test.sh | 2 +- .github/workflows/ci.yml | 12 +- .gitignore | 10 ++ examples/.gitignore | 5 + .../{ => modules}/some_module/some_module.v | 0 .../{ => modules}/automaton/automaton.v | 0 examples/log.v | 7 +- tools/.gitignore | 9 + tools/fast/fast.v | 4 +- tools/gen_vc.v | 30 ++-- tools/modules/testing/common.v | 126 ++++++++++++++ tools/vbuild-examples.v | 14 ++ tools/vbuild-tools.v | 14 ++ tools/vcreate.v | 4 +- tools/vtest-compiler.v | 77 +++++++++ tools/vtest.v | 156 +----------------- v.v | 20 +-- vlib/compiler/cgen.v | 2 +- vlib/compiler/module_header.v | 2 +- vlib/compiler/vfmt.v | 2 +- vlib/compiler/x64/elf.v | 2 +- vlib/flag/flag.v | 21 ++- vlib/flag/flag_test.v | 17 ++ vlib/log/log.v | 128 +++++++------- vlib/os/os.v | 31 +++- vlib/os/os_test.v | 4 +- vlib/ui/{ui_lin.v => ui_linux.v} | 0 vlib/vweb/assets/assets.v | 2 +- 29 files changed, 431 insertions(+), 280 deletions(-) rename examples/eventbus/{ => modules}/some_module/some_module.v (100%) rename examples/game_of_life/{ => modules}/automaton/automaton.v (100%) create mode 100644 tools/.gitignore create mode 100644 tools/modules/testing/common.v create mode 100644 tools/vbuild-examples.v create mode 100644 tools/vbuild-tools.v create mode 100644 tools/vtest-compiler.v rename vlib/ui/{ui_lin.v => ui_linux.v} (100%) diff --git a/.github/PULL_REQUEST_TEMPLATE b/.github/PULL_REQUEST_TEMPLATE index d08b8a9528..d32209bfd7 100644 --- a/.github/PULL_REQUEST_TEMPLATE +++ b/.github/PULL_REQUEST_TEMPLATE @@ -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. diff --git a/.github/workflows/alpine.test.sh b/.github/workflows/alpine.test.sh index 32f611ac80..784a849aa1 100755 --- a/.github/workflows/alpine.test.sh +++ b/.github/workflows/alpine.test.sh @@ -10,6 +10,6 @@ du -s . ls -lat -./v test v +./v test-compiler echo "DONE" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ec71d2ba3e..0ffda1dee8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/.gitignore b/.gitignore index cf118032f5..c47b41c540 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/examples/.gitignore b/examples/.gitignore index 45c94880f0..182d47571f 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -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 diff --git a/examples/eventbus/some_module/some_module.v b/examples/eventbus/modules/some_module/some_module.v similarity index 100% rename from examples/eventbus/some_module/some_module.v rename to examples/eventbus/modules/some_module/some_module.v diff --git a/examples/game_of_life/automaton/automaton.v b/examples/game_of_life/modules/automaton/automaton.v similarity index 100% rename from examples/game_of_life/automaton/automaton.v rename to examples/game_of_life/modules/automaton/automaton.v diff --git a/examples/log.v b/examples/log.v index 466439319c..cdcac94cde 100644 --- a/examples/log.v +++ b/examples/log.v @@ -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') diff --git a/tools/.gitignore b/tools/.gitignore new file mode 100644 index 0000000000..fcb39ffda3 --- /dev/null +++ b/tools/.gitignore @@ -0,0 +1,9 @@ +gen_vc +performance_compare +vcreate +vnames +vpm +vrepl +vtest +vtest-compiler +vup diff --git a/tools/fast/fast.v b/tools/fast/fast.v index d5eaefb2e9..468fcbe692 100644 --- a/tools/fast/fast.v +++ b/tools/fast/fast.v @@ -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 = ' @@ -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) diff --git a/tools/gen_vc.v b/tools/gen_vc.v index 267b054300..9404c5146c 100644 --- a/tools/gen_vc.v +++ b/tools/gen_vc.v @@ -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(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') } } diff --git a/tools/modules/testing/common.v b/tools/modules/testing/common.v new file mode 100644 index 0000000000..64b5077078 --- /dev/null +++ b/tools/modules/testing/common.v @@ -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 +} diff --git a/tools/vbuild-examples.v b/tools/vbuild-examples.v new file mode 100644 index 0000000000..a8db8f075f --- /dev/null +++ b/tools/vbuild-examples.v @@ -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) + } +} diff --git a/tools/vbuild-tools.v b/tools/vbuild-tools.v new file mode 100644 index 0000000000..3c8d2de68f --- /dev/null +++ b/tools/vbuild-tools.v @@ -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) + } +} diff --git a/tools/vcreate.v b/tools/vcreate.v index 3537383654..0a0806fef7 100644 --- a/tools/vcreate.v +++ b/tools/vcreate.v @@ -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() {' diff --git a/tools/vtest-compiler.v b/tools/vtest-compiler.v new file mode 100644 index 0000000000..1c34ef8811 --- /dev/null +++ b/tools/vtest-compiler.v @@ -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) + } + +} diff --git a/tools/vtest.v b/tools/vtest.v index 19d84d5ecc..cbd9d613b5 100644 --- a/tools/vtest.v +++ b/tools/vtest.v @@ -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') - */ -} diff --git a/v.v b/v.v index b34832ab11..33b7a1fbe6 100755 --- a/v.v +++ b/v.v @@ -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') diff --git a/vlib/compiler/cgen.v b/vlib/compiler/cgen.v index 288ff2d09d..0fb0c3a4ab 100644 --- a/vlib/compiler/cgen.v +++ b/vlib/compiler/cgen.v @@ -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{} } diff --git a/vlib/compiler/module_header.v b/vlib/compiler/module_header.v index 454c2ab780..2d02a0ebd1 100644 --- a/vlib/compiler/module_header.v +++ b/vlib/compiler/module_header.v @@ -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" diff --git a/vlib/compiler/vfmt.v b/vlib/compiler/vfmt.v index 582d15dc00..9d498918a1 100644 --- a/vlib/compiler/vfmt.v +++ b/vlib/compiler/vfmt.v @@ -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 } diff --git a/vlib/compiler/x64/elf.v b/vlib/compiler/x64/elf.v index bef5cac00d..3a97ab46fe 100644 --- a/vlib/compiler/x64/elf.v +++ b/vlib/compiler/x64/elf.v @@ -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() diff --git a/vlib/flag/flag.v b/vlib/flag/flag.v index c132ab6b4a..4e20128d26 100644 --- a/vlib/flag/flag.v +++ b/vlib/flag/flag.v @@ -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) diff --git a/vlib/flag/flag_test.v b/vlib/flag/flag_test.v index afea40be20..39364db85a 100644 --- a/vlib/flag/flag_test.v +++ b/vlib/flag/flag_test.v @@ -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') +} diff --git a/vlib/log/log.v b/vlib/log/log.v index 5ab445398d..ac36be27dd 100644 --- a/vlib/log/log.v +++ b/vlib/log/log.v @@ -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) +} + diff --git a/vlib/os/os.v b/vlib/os/os.v index e0f2c5e267..5206967719 100644 --- a/vlib/os/os.v +++ b/vlib/os/os.v @@ -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) diff --git a/vlib/os/os_test.v b/vlib/os/os_test.v index cd6e335d75..911cc5d506 100644 --- a/vlib/os/os_test.v +++ b/vlib/os/os_test.v @@ -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 } diff --git a/vlib/ui/ui_lin.v b/vlib/ui/ui_linux.v similarity index 100% rename from vlib/ui/ui_lin.v rename to vlib/ui/ui_linux.v diff --git a/vlib/vweb/assets/assets.v b/vlib/vweb/assets/assets.v index 07d56e4813..963653f4e9 100644 --- a/vlib/vweb/assets/assets.v +++ b/vlib/vweb/assets/assets.v @@ -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)