From aab31f4b352a70cc96731cd8eb00ca05b506ffc4 Mon Sep 17 00:00:00 2001 From: lutherwenxu Date: Sat, 7 Mar 2020 01:53:29 +0800 Subject: [PATCH] cmd/v: rewrite flags --- .github/workflows/alpine.build.sh | 2 +- .github/workflows/ci.yml | 8 +- Dockerfile.alpine | 2 +- cmd/tools/modules/testing/common.v | 5 +- cmd/tools/performance_compare.v | 2 +- cmd/tools/vfmt.v | 33 +-- cmd/tools/vrepl.v | 6 +- cmd/v/compile_options.v | 227 ------------------- cmd/v/flag.v | 115 ++++++---- cmd/v/internal/compile/common_mistakes.v | 32 +++ cmd/v/{ => internal/compile}/compile.v | 47 ++-- cmd/v/internal/compile/compile_c_options.v | 81 +++++++ cmd/v/internal/compile/compile_js_options.v | 20 ++ cmd/v/internal/compile/compile_options.v | 222 ++++++++++++++++++ cmd/v/internal/compile/compile_x64_options.v | 83 +++++++ cmd/v/internal/compile/old_options.v | 107 +++++++++ cmd/v/internal/flag/flag.v | 123 ++++++++++ cmd/v/internal/flag/flag_workaround.v | 49 ++++ cmd/v/simple_tool.v | 33 ++- cmd/v/v.v | 149 ++++++++---- make.bat | 6 +- vlib/compiler/aparser.v | 2 +- vlib/compiler/cc.v | 8 +- vlib/compiler/compile_errors.v | 2 +- vlib/compiler/comptime.v | 2 +- vlib/compiler/expression.v | 2 +- vlib/compiler/fn.v | 12 +- vlib/compiler/for.v | 4 +- vlib/compiler/live.v | 4 +- vlib/compiler/main.v | 53 +++-- vlib/compiler/modules.v | 20 +- vlib/compiler/msvc.v | 2 +- vlib/compiler/tests/repl/repl_test.v | 4 +- vlib/v/builder/builder.v | 8 +- vlib/v/pref/default.v | 21 +- vlib/v/pref/os.v | 4 +- vlib/v/pref/pref.v | 51 ++++- 37 files changed, 1087 insertions(+), 464 deletions(-) delete mode 100644 cmd/v/compile_options.v create mode 100644 cmd/v/internal/compile/common_mistakes.v rename cmd/v/{ => internal/compile}/compile.v (54%) create mode 100644 cmd/v/internal/compile/compile_c_options.v create mode 100644 cmd/v/internal/compile/compile_js_options.v create mode 100644 cmd/v/internal/compile/compile_options.v create mode 100644 cmd/v/internal/compile/compile_x64_options.v create mode 100644 cmd/v/internal/compile/old_options.v create mode 100644 cmd/v/internal/flag/flag.v create mode 100644 cmd/v/internal/flag/flag_workaround.v diff --git a/.github/workflows/alpine.build.sh b/.github/workflows/alpine.build.sh index 775f78de9e..ea12b2f250 100755 --- a/.github/workflows/alpine.build.sh +++ b/.github/workflows/alpine.build.sh @@ -8,7 +8,7 @@ uname -a make -j4 -./v --version +./v -version du -s . diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ff7bfcdd74..ad1082aec2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -131,7 +131,7 @@ jobs: ../../vprod gen1m.v ./gen1m > 1m.v echo "Building it..." - ../../vprod -x64 -o 1m 1m.v + ../../vprod -backend x64 -o 1m 1m.v echo "Running it..." ls # ./1m @@ -144,7 +144,7 @@ jobs: - name: Install dependencies run: sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list; sudo apt-get update; sudo apt-get install --quiet -y postgresql libpq-dev libglfw3 libglfw3-dev libfreetype6-dev libssl-dev sqlite3 libsqlite3-dev libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libsdl2-image-dev - name: Download V - run: wget https://github.com/vlang/v/releases/latest/download/v_linux.zip && unzip v_linux.zip && ./v --version + run: wget https://github.com/vlang/v/releases/latest/download/v_linux.zip && unzip v_linux.zip && ./v -version - name: Test V run: ./v examples/hello_world.v && examples/hello_world #&& ./v -silent build-examples @@ -157,7 +157,7 @@ jobs: brew install freetype glfw openssl sdl2 sdl2_ttf sdl2_mixer sdl2_image export LIBRARY_PATH="$LIBRARY_PATH:/usr/local/opt/openssl/lib/" - name: Download V - run: wget https://github.com/vlang/v/releases/latest/download/v_macos.zip && unzip v_macos.zip && ./v --version + run: wget https://github.com/vlang/v/releases/latest/download/v_macos.zip && unzip v_macos.zip && ./v -version - name: Test V run: ./v examples/hello_world.v && examples/hello_world #&& ./v -silent build-examples @@ -171,7 +171,7 @@ jobs: curl -L https://github.com/vlang/v/releases/latest/download/v_windows.zip -o v_windows.zip echo Unzipping... unzip v_windows.zip - v.exe --version + v.exe -version echo Done dir - name: Test V diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 349bf5a8f1..77ae8a5d3e 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -17,4 +17,4 @@ RUN apk --no-cache add \ RUN apk --no-cache add --virtual sdl2deps sdl2-dev sdl2_ttf-dev sdl2_mixer-dev sdl2_image-dev -RUN git clone https://github.com/vlang/v /opt/vlang && make && v --version +RUN git clone https://github.com/vlang/v /opt/vlang && make && v -version diff --git a/cmd/tools/modules/testing/common.v b/cmd/tools/modules/testing/common.v index 0c6376dbaa..85885d9d40 100644 --- a/cmd/tools/modules/testing/common.v +++ b/cmd/tools/modules/testing/common.v @@ -19,11 +19,12 @@ pub mut: show_ok_tests bool } -pub fn new_test_session(vargs string) TestSession { +pub fn new_test_session(_vargs string) TestSession { + vargs := _vargs.replace('-silent', '') return TestSession{ vexe: pref.vexe_path() vargs: vargs - show_ok_tests: !vargs.contains('-silent') + show_ok_tests: !_vargs.contains('-silent') } } diff --git a/cmd/tools/performance_compare.v b/cmd/tools/performance_compare.v index 4400ba0b72..a6897b4c2a 100644 --- a/cmd/tools/performance_compare.v +++ b/cmd/tools/performance_compare.v @@ -123,7 +123,7 @@ fn (c &Context) prepare_v(cdir string, commit string) { scripting.show_sizes_of_files(['$cdir/cv', '$cdir/cv_stripped', '$cdir/cv_stripped_upxed']) scripting.show_sizes_of_files(['$cdir/v', '$cdir/v_stripped', '$cdir/v_stripped_upxed']) scripting.show_sizes_of_files(['$cdir/vprod', '$cdir/vprod_stripped', '$cdir/vprod_stripped_upxed']) - vversion := scripting.run('$cdir/v --version') + vversion := scripting.run('$cdir/v -version') vcommit := scripting.run('git rev-parse --short --verify HEAD') println('V version is: ${vversion} , local source commit: ${vcommit}') if vgit_context.vvlocation == 'cmd/v' { diff --git a/cmd/tools/vfmt.v b/cmd/tools/vfmt.v index 3c8415d08d..391daaab73 100644 --- a/cmd/tools/vfmt.v +++ b/cmd/tools/vfmt.v @@ -168,7 +168,12 @@ fn (foptions &FormatOptions) format_file(file string) { mut compiler_params := &pref.Preferences{} target_os := file_to_target_os(file) if target_os != '' { - compiler_params.os = pref.os_from_string(target_os) + //TODO Remove temporary variable once it compiles correctly in C + tmp := pref.os_from_string(target_os) or { + eprintln('unknown operating system $target_os') + return + } + compiler_params.os = tmp } mut cfile := file mut mod_folder_parent := tmpfolder @@ -189,7 +194,7 @@ fn (foptions &FormatOptions) format_file(file string) { } os.write_file(main_program_file, main_program_content) cfile = main_program_file - compiler_params.user_mod_path = mod_folder_parent + compiler_params.lookup_path = [mod_folder_parent, '@vlib', '@vmodule'] } if !is_test_file && mod_name == 'main' { // NB: here, file is guaranted to be a main. We do not know however @@ -229,19 +234,17 @@ fn (foptions &FormatOptions) format_file(file string) { } fn print_compiler_options( compiler_params &pref.Preferences ) { - eprintln(' os: ' + compiler_params.os.str() ) - eprintln(' ccompiler: $compiler_params.ccompiler' ) - eprintln(' mod: $compiler_params.mod ') - eprintln(' path: $compiler_params.path ') - eprintln(' out_name: $compiler_params.out_name ') - eprintln(' vroot: $compiler_params.vroot ') - eprintln(' vpath: $compiler_params.vpath ') - eprintln(' vlib_path: $compiler_params.vlib_path ') - eprintln(' out_name: $compiler_params.out_name ') - eprintln(' umpath: $compiler_params.user_mod_path ') - eprintln(' cflags: $compiler_params.cflags ') - eprintln(' is_test: $compiler_params.is_test ') - eprintln(' is_script: $compiler_params.is_script ') + eprintln(' os: ' + compiler_params.os.str() ) + eprintln(' ccompiler: $compiler_params.ccompiler' ) + eprintln(' mod: $compiler_params.mod ') + eprintln(' path: $compiler_params.path ') + eprintln(' out_name: $compiler_params.out_name ') + eprintln(' vroot: $compiler_params.vroot ') + eprintln('lookup_path: $compiler_params.lookup_path ') + eprintln(' out_name: $compiler_params.out_name ') + eprintln(' cflags: $compiler_params.cflags ') + eprintln(' is_test: $compiler_params.is_test ') + eprintln(' is_script: $compiler_params.is_script ') } fn (foptions &FormatOptions) post_process_file(file string, formatted_file_path string) { diff --git a/cmd/tools/vrepl.v b/cmd/tools/vrepl.v index 4255a18b35..5e93edf65d 100644 --- a/cmd/tools/vrepl.v +++ b/cmd/tools/vrepl.v @@ -144,7 +144,7 @@ pub fn run_repl(workdir string, vrepl_prefix string) []string { if r.line.starts_with('print') { source_code := r.functions.join('\n') + r.lines.join('\n') + '\n' + r.line os.write_file(file, source_code) - s := os.exec('"$vexe" run $file -repl') or { + s := os.exec('"$vexe" -repl run $file') or { rerror(err) return [] } @@ -166,7 +166,7 @@ pub fn run_repl(workdir string, vrepl_prefix string) []string { } temp_source_code := r.functions.join('\n') + r.lines.join('\n') + '\n' + r.temp_lines.join('\n') + '\n' + temp_line os.write_file(temp_file, temp_source_code) - s := os.exec('"$vexe" run $temp_file -repl') or { + s := os.exec('"$vexe" -repl run $temp_file') or { println("SDFSDF") rerror(err) return [] @@ -237,6 +237,6 @@ pub fn rerror(s string) { fn v_version() string { vexe := os.getenv('VEXE') - vversion_res := os.exec('$vexe --version') or { panic('"$vexe --version" is not working') } + vversion_res := os.exec('$vexe -version') or { panic('"$vexe -version" is not working') } return vversion_res.output } diff --git a/cmd/v/compile_options.v b/cmd/v/compile_options.v deleted file mode 100644 index 818dd0fed5..0000000000 --- a/cmd/v/compile_options.v +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved. -// Use of this source code is governed by an MIT license -// that can be found in the LICENSE file. -module main - -import ( - compiler - filepath - os - os.cmdline - v.pref -) - -//TODO Cleanup this file. This file ended up like a dump for functions that do not belong in `compiler`. -//Maybe restructure the functions below into different V files - -pub fn new_v(args []string) &compiler.V { - // Create modules dirs if they are missing - if !os.is_dir(compiler.v_modules_path) { - os.mkdir(compiler.v_modules_path)or{ - panic(err) - } - os.mkdir('$compiler.v_modules_path${filepath.separator}cache')or{ - panic(err) - } - } - vroot := filepath.dir(pref.vexe_path()) - // optional, custom modules search path - user_mod_path := cmdline.option(args, '-user_mod_path', '') - vlib_path := cmdline.option(args, '-vlib-path', '') - vpath := cmdline.option(args, '-vpath', '') - target_os := cmdline.option(args, '-os', '') - if target_os == 'msvc' { - // notice that `-os msvc` became `-cc msvc` - println('V error: use the flag `-cc msvc` to build using msvc') - os.flush() - exit(1) - } - mut out_name := cmdline.option(args, '-o', '') - mut dir := args[args.len-1]//.last() - if 'run' in args { - args_after_run := cmdline.only_non_options( cmdline.options_after(args,['run']) ) - dir = if args_after_run.len>0 { args_after_run[0] } else { '' } - } - if dir == 'v.v' { - println('looks like you are trying to build V with an old command') - println('use `v -o v cmd/v` instead of `v -o v v.v`') - exit(1) - } - if dir.ends_with(filepath.separator) { - dir = dir.all_before_last(filepath.separator) - } - if dir.starts_with('.$filepath.separator') { - dir = dir[2..] - } - if args.len < 2 { - dir = '' - } - - // build mode - mut build_mode := pref.BuildMode.default_mode - mut mod := '' - joined_args := args.join(' ') - if joined_args.contains('build module ') { - build_mode = .build_module - os.chdir(vroot) - // v build module ~/v/os => os.o - mod_path := if dir.contains('vlib') { dir.all_after('vlib' + filepath.separator) } else if dir.starts_with('.\\') || dir.starts_with('./') { dir[2..] } else if dir.starts_with(filepath.separator) { dir.all_after(filepath.separator) } else { dir } - mod = mod_path.replace(filepath.separator, '.') - println('Building module "${mod}" (dir="$dir")...') - // out_name = '$TmpPath/vlib/${base}.o' - if !out_name.ends_with('.c') { - out_name = mod - } - // Cross compiling? Use separate dirs for each os - /* - if target_os != os.user_os() { - os.mkdir('$TmpPath/vlib/$target_os') or { panic(err) } - out_name = '$TmpPath/vlib/$target_os/${base}.o' - println('target_os=$target_os user_os=${os.user_os()}') - println('!Cross compiling $out_name') - } - */ - - } - // `v -o dir/exec`, create "dir/" if it doesn't exist - if out_name.contains(filepath.separator) { - d := out_name.all_before_last(filepath.separator) - if !os.is_dir(d) { - println('creating a new directory "$d"') - os.mkdir(d)or{ - panic(err) - } - } - } - - // println('VROOT=$vroot') - cflags := cmdline.options(args, '-cflags').join(' ') - defines := cmdline.options(args, '-d') - compile_defines, compile_defines_all := parse_defines( defines ) - - rdir := os.realpath(dir) - rdir_name := filepath.filename(rdir) - if '-bare' in args { - println('V error: use -freestanding instead of -bare') - os.flush() - exit(1) - } - is_repl := '-repl' in args - ccompiler := cmdline.option(args, '-cc', '') - mut prefs := &pref.Preferences{ - os: pref.os_from_string(target_os) - is_so: '-shared' in args - is_solive: '-solive' in args - is_prod: '-prod' in args - is_verbose: '-verbose' in args || '--verbose' in args - is_debug: '-g' in args || '-cg' in args - is_vlines: '-g' in args && !('-cg' in args) - is_keep_c: '-keep_c' in args - is_pretty_c: '-pretty_c' in args - is_cache: '-cache' in args - is_stats: '-stats' in args - obfuscate: '-obf' in args - is_prof: '-prof' in args - is_live: '-live' in args - sanitize: '-sanitize' in args - // nofmt: '-nofmt' in args - - show_c_cmd: '-show_c_cmd' in args - translated: 'translated' in args - is_run: 'run' in args - autofree: '-autofree' in args - compress: '-compress' in args - enable_globals: '--enable-globals' in args - fast: '-fast' in args - is_bare: '-freestanding' in args - x64: '-x64' in args - output_cross_c: '-output-cross-platform-c' in args - prealloc: '-prealloc' in args - is_repl: is_repl - build_mode: build_mode - cflags: cflags - ccompiler: ccompiler - building_v: !is_repl && (rdir_name == 'compiler' || rdir_name == 'v' || rdir_name == 'vfmt.v' || rdir_name == 'cmd/v' || dir.contains('vlib')) - // is_fmt: comptime_define == 'vfmt' - - user_mod_path: user_mod_path - vlib_path: vlib_path - vpath: vpath - v2: '-v2' in args - vroot: vroot - out_name: out_name - path: dir - compile_defines: compile_defines - compile_defines_all: compile_defines_all - mod: mod - } - if prefs.is_verbose || prefs.is_debug { - println('C compiler=$prefs.ccompiler') - } - $if !linux { - if prefs.is_bare && !out_name.ends_with('.c') { - println('V error: -freestanding only works on Linux for now') - os.flush() - exit(1) - } - } - prefs.fill_with_defaults() - - // v.exe's parent directory should contain vlib - if !os.is_dir(prefs.vlib_path) || !os.is_dir(prefs.vlib_path + filepath.separator + 'builtin') { - // println('vlib not found, downloading it...') - /* - ret := os.system('git clone --depth=1 https://github.com/vlang/v .') - if ret != 0 { - println('failed to `git clone` vlib') - println('make sure you are online and have git installed') - exit(1) - } - */ - println('vlib not found. It should be next to the V executable.') - println('Go to https://vlang.io to install V.') - println('(os.executable=${os.executable()} vlib_path=$prefs.vlib_path vexe_path=${pref.vexe_path()}') - exit(1) - } - - if prefs.is_script && !os.exists(dir) { - println('`$dir` does not exist') - exit(1) - } - - return compiler.new_v(prefs) -} - -fn find_c_compiler_thirdparty_options(args []string) string { - mut cflags := cmdline.options(args, '-cflags') - $if !windows { - cflags << '-fPIC' - } - if '-m32' in args { - cflags << '-m32' - } - return cflags.join(' ') -} - -fn parse_defines(defines []string) ([]string,[]string) { - // '-d abc -d xyz=1 -d qwe=0' should produce: - // compile_defines: ['abc','xyz'] - // compile_defines_all ['abc','xyz','qwe'] - mut compile_defines := []string - mut compile_defines_all := []string - for dfn in defines { - dfn_parts := dfn.split('=') - if dfn_parts.len == 1 { - compile_defines << dfn - compile_defines_all << dfn - continue - } - if dfn_parts.len == 2 { - compile_defines_all << dfn_parts[0] - if dfn_parts[1] == '1' { - compile_defines << dfn_parts[0] - } - } - } - return compile_defines, compile_defines_all -} diff --git a/cmd/v/flag.v b/cmd/v/flag.v index 4f1f308fd4..790b5c2fe5 100644 --- a/cmd/v/flag.v +++ b/cmd/v/flag.v @@ -3,56 +3,25 @@ // that can be found in the LICENSE file. module main -import os +import ( + internal.flag + os +) const ( //list_of_flags contains a list of flags where an argument is expected past it. list_of_flags = [ - '-o', '-os', '-cc', '-cflags', '-d' + 'o', 'output', 'd', 'define', 'b', 'backend', 'cc', 'os', 'target-os', 'arch', + 'csource', 'cf', 'cflags', 'path' ] ) -fn get_basic_command_and_option(args []string) (string, []string) { - mut option := []string - for i, arg in args { - if i == 0 { - //Skip executable - continue - } - if arg == '--' { - //End of list of options. The next one is the command. - if i+1 < os.args.len { - return os.args[i+1], option - } - //There's no option past this - return '', option - } - if arg in list_of_flags { - i++ - continue - } - if arg[0] == `-` { - option << arg - continue - } - //It's not a flag. We did not skip it. It's a command. - return arg, option - } - - //There's no arguments that were not part of a flag. - return '', option -} - -fn non_empty(arg []string) []string { - return arg.filter(it != '') -} - fn join_flags_and_argument() []string { vosargs := os.getenv('VOSARGS') if vosargs != '' { - return non_empty(vosargs.split(' ')) + return vosargs.split(' ') } - + // No VOSARGS? Look for VFLAGS and concat it with command-line options. mut args := []string vflags := os.getenv('VFLAGS') if vflags != '' { @@ -61,8 +30,70 @@ fn join_flags_and_argument() []string { if os.args.len > 1 { args << os.args[1..] } - return non_empty(args) + return args + } + // No VFLAGS too? Just return command-line args. + return os.args +} + +fn parse_flags(flag string, f mut flag.Instance, prefs mut flag.MainCmdPreferences) { + match flag { + 'v' { + f.is_equivalent_to(['v', 'vv', 'vvv', 'verbose']) + prefs.verbosity = .level_one + } + 'vv' { + f.is_equivalent_to(['v', 'vv', 'vvv', 'verbose']) + prefs.verbosity = .level_two + } + 'vvv' { + f.is_equivalent_to(['v', 'vv', 'vvv', 'verbose']) + prefs.verbosity = .level_three + } + 'verbose' { + f.is_equivalent_to(['v', 'vv', 'vvv', 'verbose']) + level := f.int() or { + println('V error: Expected `0`, `1`, `2` or `3` as argument to `-verbose` to specify verbosity level.') + exit(1) + } + match level { + 0 {} //Zero verbosity is already default. + 1 { + prefs.verbosity = .level_one + } + 2 { + prefs.verbosity = .level_two + } + 3 { + prefs.verbosity = .level_three + } + else { + println('V error: Expected `0`, `1`, `2` or `3` as argument to `-verbose` to specify verbosity level.') + exit(1) + } + } + } + 'h', 'help' { + f.is_equivalent_to(['h', 'help']) + prefs.action = .help + } + 'v', 'version' { + f.is_equivalent_to(['v', 'version']) + prefs.action = .version + } + '-version', '-help' { + println('V error: `-$flag` has been deprecated. Use `$flag` instead.') + exit(1) + } + else { + prefs.unknown_flag = '-$flag' + if !(flag in list_of_flags) { + return + } + f.string() or { + println('V error: Error parsing flag. Expected value for `-$flag`.') + exit(1) + } + } } - - return non_empty(os.args) } diff --git a/cmd/v/internal/compile/common_mistakes.v b/cmd/v/internal/compile/common_mistakes.v new file mode 100644 index 0000000000..73d1212255 --- /dev/null +++ b/cmd/v/internal/compile/common_mistakes.v @@ -0,0 +1,32 @@ +// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module compile + +import ( + os + v.pref +) + +fn check_for_common_mistake(args []string, p &pref.Preferences) { + // Note: This feature is still currently commonly used. Uncomment this when + // proper deprecation detection is ready. + if p.out_name.ends_with('.c') && p.backend == .c { + //println('HINT: `-o $p.out_name` implies `-csource keep` and does not results in an executable currently.') + //println(' To overwrite this, specify `-csource drop` explicitly.') + } + if p.out_name.ends_with('.js') && p.backend != .js { + println('HINT: `-o $p.out_name` implies `-backend js` currently.') + //println(' To overwrite this, specify the intended backend explicitly.') + } + if p.path == 'vlib/compiler' || p.path == 'v.v' { + println('HINT: The V compiler is now located in `cmd/v`.') + println(' `$p.path` is no longer the correct path to compile if you are intending to do so.') + } + if !p.path.ends_with('.v') && !os.is_dir(p.path) && os.is_dir(p.path + os.path_separator) { + println('HINT: `$p.path` is not a directory nor a file suffixed with `.v`.') + println(' Did you perhaps accidentally reference the compiled executable?') + println(' To make sure V detects the directory correctly, add the path separator to the end of the path like so:') + println(' `v $p.path$os.path_separator`') + } +} diff --git a/cmd/v/compile.v b/cmd/v/internal/compile/compile.v similarity index 54% rename from cmd/v/compile.v rename to cmd/v/internal/compile/compile.v index d304e9355c..e7ca89a235 100644 --- a/cmd/v/compile.v +++ b/cmd/v/internal/compile/compile.v @@ -1,40 +1,29 @@ // Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. -module main +module compile import ( benchmark + compiler os - os.cmdline compiler ) -fn compile(command string, args []string) { +pub fn compile(command string, args []string) { // Construct the V object from command line arguments - mut v := new_v(args) - if v.pref.is_verbose { + parse_and_output_new_format(args) + prefs, remaining := parse_arguments(args) + check_for_common_mistake(args, &prefs) + mut v := compiler.new_v(prefs) + if v.pref.verbosity.is_higher_or_equal(.level_two) { println(args) } - if command == 'run' { - // always recompile for now, too error prone to skip recompilation otherwise - // for example for -repl usage, especially when piping lines to v - if v.pref.x64 { - v.compile_x64() - } - else if v.pref.v2 { - v.compile2() - } - else { - v.compile() - } - run_compiled_executable_and_exit(v, args) - } mut tmark := benchmark.new_benchmark() - if v.pref.x64 { + if v.pref.backend == .x64 { v.compile_x64() } - else if v.pref.v2 { + else if v.pref.backend == .experimental { v.compile2() } else { @@ -44,20 +33,22 @@ fn compile(command string, args []string) { tmark.stop() println('compilation took: ' + tmark.total_duration().str() + 'ms') } - if v.pref.is_test { - run_compiled_executable_and_exit(v, args) + if v.pref.is_test || v.pref.is_run { + run_compiled_executable_and_exit(v, remaining) } v.finalize_compilation() } -pub fn run_compiled_executable_and_exit(v &compiler.V, args []string) { - if v.pref.is_verbose { +pub fn run_compiled_executable_and_exit(v &compiler.V, remaining_args []string) { + if v.pref.verbosity.is_higher_or_equal(.level_two) { println('============ running $v.pref.out_name ============') } mut cmd := '"${v.pref.out_name}"' - args_after_no_options := cmdline.only_non_options( cmdline.options_after(args,['run','test']) ) - if args_after_no_options.len > 1 { - cmd += ' ' + args_after_no_options[1..].join(' ') + if remaining_args.len > 1 { + cmd += ' ' + remaining_args[1..].join(' ') + } + if v.pref.verbosity.is_higher_or_equal(.level_two) { + println('command to run executable: $cmd') } if v.pref.is_test { ret := os.system(cmd) diff --git a/cmd/v/internal/compile/compile_c_options.v b/cmd/v/internal/compile/compile_c_options.v new file mode 100644 index 0000000000..04295ce066 --- /dev/null +++ b/cmd/v/internal/compile/compile_c_options.v @@ -0,0 +1,81 @@ +// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module compile + +// This file contains the options specific to the C backend of V. +// To add a flag to all backends at once, please add the flag to `parse_options()` in `compile_options.v`. +// To add a flag to both x64 and C, please add the flag to `parse_executable_options()` in `compile_x64_options.v`. + +import ( + internal.flag + v.pref +) + +[inline] +fn parse_c_options(flag string, f mut flag.Instance, prefs mut pref.Preferences) { + match flag { + 'cc', 'compiler' { + f.is_equivalent_to(['cc', 'compiler']) + //TODO Remove `tmp` variable when it doesn't error out in C. + tmp := f.string() or { + println('V error: Expected argument after `-$flag`.') + exit(1) + } + prefs.ccompiler = tmp + } + 'cg', 'cdebug' { + f.is_equivalent_to(['cg', 'cdebug', 'g', 'debug']) + if f.bool() { + prefs.is_debug = true + prefs.is_vlines = false + } + } + 'live' { + prefs.is_live = f.bool() + } + 'csource' { + operation := f.string() or { + println('V error: Expected argument after `-csource`.') + exit(1) + } + match operation { + 'keep' { + prefs.is_keep_c = true + } + 'prettify' { + prefs.is_keep_c = true + prefs.is_pretty_c = true + } + 'drop' {} //Default + else { + println('V error: Unknown argument for `-csource` (`$operation`).') + println('Allowed options: `keep`, `prettify` and `drop`.') + exit(1) + } + } + } + 'sanitize' { + prefs.sanitize = f.bool() + } + 'cf', 'cflags' { + f.allow_duplicate() + cflag := f.string() or { + println('V error: Expected argument after `-$flag`.') + exit(1) + } + prefs.cflags += ' $cflag' + } + // TODO Deprecate these once v2 parser is live + 'repl' { + prefs.is_repl = f.bool() + } + 'solive' { + prefs.is_solive = f.bool() + prefs.is_so = prefs.is_solive + } + else { + parse_executable_options(flag, mut f, mut prefs) + } + } +} diff --git a/cmd/v/internal/compile/compile_js_options.v b/cmd/v/internal/compile/compile_js_options.v new file mode 100644 index 0000000000..f3c322df4a --- /dev/null +++ b/cmd/v/internal/compile/compile_js_options.v @@ -0,0 +1,20 @@ +// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module compile + +// This file contains the options specific to the JS backend of V. + +import ( + internal.flag + v.pref +) + +[inline] +fn parse_js_options(flag string, f flag.Instance, prefs pref.Preferences) { + // No notable flags for JS-only currently. Add them here when they are needed. + // For now, we just fail as this is meant to be a fallback. + println('V error: Unknown flag `-$flag` provided.') + println('Use `--` to terminate flag list if necessary.') + exit(1) +} diff --git a/cmd/v/internal/compile/compile_options.v b/cmd/v/internal/compile/compile_options.v new file mode 100644 index 0000000000..dbf38b7ad6 --- /dev/null +++ b/cmd/v/internal/compile/compile_options.v @@ -0,0 +1,222 @@ +// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module compile + +import ( + internal.flag + os.cmdline + v.pref +) + +fn parse_arguments(args []string) (pref.Preferences, []string) { + mut p := pref.Preferences{ + //TODO: Refactor away this preference. + // It's no longer controlled by a command-line flag. + enable_globals: true + } + mut backend := cmdline.options(args, '-b') + backend << cmdline.options(args, '-backend') + if backend.len > 1 { + println('V error: Only one backend may be enabled at once. (Multiple `-b`/`-backend` flags provided)') + exit(1) + } + if backend.len == 1 { + p.backend = pref.backend_from_string(backend[0]) or { + println('V error: Unknown backend ${backend[0]} provided.') + exit(1) + } + } else { + p.backend = .c + } + mut remaining := flag.parse_pref(args, parse_options, &p) or { + println('V error: Error while parsing flags.') + println(err) + exit(1) + } + match remaining[0] { + 'run' { + p.is_run = true + remaining = remaining[1..] + } + 'build' { + remaining = remaining[1..] + if remaining[0] == 'module' { + remaining = remaining[1..] + //TODO Figure out module + println('V error: Module compilation is not ready yet.') + exit(1) + } + } + else {} + } + if remaining.len == 0 { + println('V error: Expected file/directory to compile.') + exit(1) + } + if !p.is_run && remaining.len > 1 { + println('V error: Expected only one file/directory to compile.') + println('Did you perhaps put flags after the file/directory?') + exit(1) + } + p.path = remaining[0] + p.fill_with_defaults() + return p, remaining +} + +fn parse_options(flag string, f mut flag.Instance, prefs mut pref.Preferences) { + match flag { + 'path' { + // -path + path_str := f.string() or { + println('V error: Expected argument for `-path`.') + exit(1) + } + prefs.lookup_path = path_str.split('|') + } + 'o', 'output' { + f.is_equivalent_to(['o', 'output']) + //TODO Remove `tmp` variable when it doesn't error out in C. + tmp := f.string() or { + println('V error: Expected argument for `-$flag`.') + exit(1) + } + prefs.out_name = tmp + } + 'd', 'define' { + f.allow_duplicate() + define := f.string() or { + println('V error: Expected argument for `-$flag`.') + exit(1) + } + parse_define(mut prefs, define) + } + 'g', 'debug' { + f.is_equivalent_to(['g', 'debug', 'cg', 'cdebug']) + if f.bool() { + prefs.is_debug = true + prefs.is_vlines = true + } + } + 'e', 'experiments' { + f.allow_duplicate() + to_enable := f.string() or { + println('V error: Expected argument for `-$flag`.') + exit(1) + } + match to_enable { + 'prealloc' { + prefs.prealloc = true + } + else { + println('V error: Unknown experiment `$to_enable`.') + exit(1) + } + } + } + 'prod' { + prefs.is_prod = true + } + 'v' { + f.is_equivalent_to(['v', 'vv', 'vvv', 'verbose']) + prefs.verbosity = .level_one + } + 'vv' { + f.is_equivalent_to(['v', 'vv', 'vvv', 'verbose']) + prefs.verbosity = .level_two + } + 'vvv' { + f.is_equivalent_to(['v', 'vv', 'vvv', 'verbose']) + prefs.verbosity = .level_three + } + 'verbose' { + f.is_equivalent_to(['v', 'vv', 'vvv', 'verbose']) + level := f.int() or { + println('V error: Expected `0`, `1`, `2` or `3` as argument to `-verbose` to specify verbosity level.') + exit(1) + } + match level { + 0 {} //Zero verbosity is already default. + 1 { + prefs.verbosity = .level_one + } + 2 { + prefs.verbosity = .level_two + } + 3 { + prefs.verbosity = .level_three + } + else { + println('V error: Expected `0`, `1`, `2` or `3` as argument to `-verbose` to specify verbosity level.') + exit(1) + } + } + } + 'full-rebuild' { + prefs.is_cache = !f.bool() + } + 'stats' { + prefs.is_stats = f.bool() + } + 'obf', 'obfuscate' { + f.is_equivalent_to(['-obf', '-obfuscate']) + prefs.obfuscate = f.bool() + } + 'prof', 'profile' { + f.is_equivalent_to(['-prof', '-profile']) + prefs.is_prof = f.bool() + } + 'translated' { + prefs.translated = f.bool() + } + 'backend' { + // Just consume it. The option is handled outside of this function + f.string() or { return } + } + else { + match prefs.backend { + .c, .experimental { + parse_c_options(flag, mut f, mut prefs) + } + .x64 { + parse_x64_options(flag, mut f, mut prefs) + } + .js { + parse_js_options(flag, f, prefs) + } + else { + // TODO: Remove when compiler allows for it. + // This enum matching IS exhaustive. + panic('unexpected backend type: $prefs.backend') + } + } + } + } +} + +[inline] +fn parse_define(prefs mut pref.Preferences, define string) { + define_parts := define.split('=') + if define_parts.len == 1 { + prefs.compile_defines << define + prefs.compile_defines_all << define + return + } + if define_parts.len == 2 { + prefs.compile_defines_all << define_parts[0] + match define_parts[1] { + '0' {} + '1' { + prefs.compile_defines << define_parts[0] + } + else { + println('V error: Unknown define argument value `${define_parts[1]}` for ${define_parts[0]}.' + + 'Expected `0` or `1`.') + exit(1) + } + } + return + } + println('V error: Unknown define argument: ${define}. Expected at most one `=`.') + exit(1) +} diff --git a/cmd/v/internal/compile/compile_x64_options.v b/cmd/v/internal/compile/compile_x64_options.v new file mode 100644 index 0000000000..25c8e378ed --- /dev/null +++ b/cmd/v/internal/compile/compile_x64_options.v @@ -0,0 +1,83 @@ +// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module compile + +// This file contains the options specific to the x64 backend of V and backends that generate executable files. +// To add a flag to all backends at once, please add the flag to `parse_options()` in `compile_options.v`. +// To add a flag to the C backend-only, please add the flag to `parse_c_options()` in `compile_c_options.v`. + +import ( + internal.flag + v.pref +) + +[inline] +fn parse_x64_options(flag string, f mut flag.Instance, prefs mut pref.Preferences) { + // No notable flags for x64-only currently. Add them here when they are needed. + if flag == 'arch' { + println('V error: The `-arch` flag is not supported on the x64 backend.') + exit(1) + } + parse_executable_options(flag, mut f, mut prefs) +} + +[inline] +fn parse_executable_options(flag string, f mut flag.Instance, prefs mut pref.Preferences) { + match flag { + 'os', 'target-os' { + f.is_equivalent_to(['os', 'target-os']) + target_os := f.string() or { + println('V error: Expected argument after `-$flag`.') + exit(1) + } + if target_os == 'cross' { + prefs.output_cross_c = true + return + } + //TODO Remove `tmp` variable when it doesn't error out in C. + tmp := pref.os_from_string(target_os) or { + println('V error: Unknown operating system target `$target_os`.') + exit(1) + } + prefs.os = tmp + } + 'arch' { + target_arch := f.string() or { + println('V error: Expected argument after `-arch`.') + exit(1) + } + match target_arch { + 'x86' { + prefs.cflags += ' -m32' + } + 'x64' {} // Default. Do nothing. + else { + println('V error: Unknown architecture type. Only x86 and x64 are supported currently.') + exit(1) + } + } + } + 'freestanding' { + prefs.is_bare = f.bool() + } + 'shared' { + prefs.is_so = f.bool() + } + 'live' { + prefs.is_solive = f.bool() + } + 'manual-free' { + prefs.autofree = !f.bool() + } + 'compress' { + prefs.compress = f.bool() + } + else { + // No more fallback. We don't recognize this flag. + println('V error: Unknown flag `-$flag` provided.') + println('Use `--` to terminate flag list if necessary.') + exit(1) + } + } +} diff --git a/cmd/v/internal/compile/old_options.v b/cmd/v/internal/compile/old_options.v new file mode 100644 index 0000000000..2cc3f2c7b3 --- /dev/null +++ b/cmd/v/internal/compile/old_options.v @@ -0,0 +1,107 @@ +// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module compile + +import ( + os.cmdline +) + +struct Deprecated { + old string + new string + not_exactly bool +} + +//parse_and_output_new_format parses the old format and tells the user how to use the new options. +//This function exits if it detects the old format and returns if it doesn't. +fn parse_and_output_new_format(args []string) { + mut list := []Deprecated + mut obsolete := []string + //Check `-os msvc` + os := cmdline.option(args, '-os', '') + if os == 'msvc' { + list << Deprecated { + old: '-os msvc' + new: '-cc msvc' + } + } + //Check simple options + //TODO Refactor them to actually just modify mutable arrays + list << add_if_found_deprecated(args, '-cache', '-full-rebuild=false') + list << add_if_found_deprecated(args, 'translated', '-translated') + list << add_if_found_deprecated(args, '-x64', '-backend x64') + list << add_if_found_deprecated(args, '-v2', '-backend experimental') + list << add_if_found_deprecated(args, '-keep_c', '-csource keep') + list << add_if_found_deprecated(args, '-pretty_c', '-csource prettify') + list << add_if_found_deprecated(args, '-show_c_cmd', '-v') + list << add_if_found_deprecated(args, '-autofree', '-manual-free=false') + list << add_if_found_deprecated(args, '-fast', '-cc tcc') + list << add_if_found_deprecated(args, '-output-cross-platform-c', '-os cross') + list << add_if_found_deprecated(args, '-m32', '-arch x86') + list << add_if_found_deprecated(args, '-bare', '-freestanding') + obsolete << add_if_found_string(args, '--enable-globals') + list << add_if_found_deprecated(args, '-prealloc', '-e prealloc') + list << add_if_found_deprecated(args, '-user_mod_path', '-path*') + list << add_if_found_deprecated(args, '-vlib-path', '-path*') + list << add_if_found_deprecated(args, '-vpath', '-path*') + //Nothing to do here + if list.len == 0 && obsolete.len == 0 { + return + } + //Output messages + println('V has encountered deprecated/obsolete options. Please edit your command.\n') + if list.len > 0 { + println('Deprecated options that have been replaced:') + for deprecation in list { + if deprecation.not_exactly { + println(' `$deprecation.old` has been superseded by `$deprecation.new` (see help for more details)') + } else { + println(' use `$deprecation.new` instead of `$deprecation.old`') + } + } + println('') + } + if obsolete.len > 0 { + println('Obsolete options that are no longer supported:') + for obsoleted in obsolete { + println(' `$obsoleted` has been removed') + } + println('') + } + println('For more details, please use the command `v help build` for a list of options.') + exit(1) +} + +//===== +//HELPER FUNCTIONS +//===== + +[inline] +fn add_if_found_deprecated(args []string, deprecated string, alt string) []Deprecated { + if deprecated in args { + new := if alt.ends_with('*') { + Deprecated { + old: deprecated + new: alt[..alt.len-1] + not_exactly: true + } + } else { + Deprecated { + old: deprecated + new: alt + not_exactly: false + } + } + return [new] + } + return [] +} + +[inline] +fn add_if_found_string(args []string, deprecated string) []string { + if deprecated in args { + return [deprecated] + } + return [] +} diff --git a/cmd/v/internal/flag/flag.v b/cmd/v/internal/flag/flag.v new file mode 100644 index 0000000000..13864f635a --- /dev/null +++ b/cmd/v/internal/flag/flag.v @@ -0,0 +1,123 @@ +// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +// This module is designed to be general purpose. The only reason it currently only lives here is because there's no +// generics support and all types are defined manually. +// TODO Move to vlib once generics are implemented properly. +module flag + +const ( + // List as taken from `pkg.go.dev/flag` + truthy = ['1', 't', 'T', 'true', 'TRUE', 'True'] + falsey = ['0', 'f', 'F', 'false', 'FALSE', 'False'] +) + +pub struct Instance { + args []string +mut: + current_flag string + current_pos int + equal_val string + encountered map[string]bool +} + +fn (p mut Instance) parse_impl(args []string, value voidptr, callback void_cb) ?[]string { + for p.current_pos+1 < p.args.len { + p.current_pos++ + next := p.args[p.current_pos] + if !next.starts_with('-') || next == '-' { + // End of arguments. + // Note: - by itself is not considered a flag. + return args[p.current_pos..] + } + if next == '--' { + // Terminator. End of arguments. + return args[p.current_pos+1..] + } + // Start parsing flag by determining flag name + mut flag_name := '' + if idx := next.index('=') { + p.equal_val = next[idx+1..] + flag_name = next[1..idx] + } else { + p.equal_val = '' + flag_name = next[1..] + } + if p.encountered[flag_name] { + // Duplicate flags are opt-in + // This can be prevented with p.allow_duplicate() + return error('Duplicate flag: -$flag_name') + } + p.encountered[flag_name] = true + p.current_flag = flag_name + callback(flag_name, p, value) + } + // We didn't hit any exit condition. There's no argument left so nothing. + return []string +} + +pub fn (p mut Instance) string() ?string { + if p.equal_val != '' { + return p.equal_val + } + p.current_pos++ + if p.current_pos >= p.args.len { + return error('out of arguments') + } + return p.args[p.current_pos] +} + +pub fn (p mut Instance) int() ?int { + val := p.string() or { + return error(err) + } + return val.int() +} + +pub fn (p mut Instance) f32() ?f32 { + val := p.string() or { + return error(err) + } + return val.f32() +} + +pub fn (p mut Instance) f64() ?f64 { + val := p.string() or { + return error(err) + } + return val.f64() +} + +pub fn (p mut Instance) i64() ?i64 { + val := p.string() or { + return error(err) + } + return val.i64() +} + +pub fn (p mut Instance) bool() bool { + val := p.string() or { + // Could not fetch arguments? Parse it as `true`. + return true + } + if val in truthy { + return true + } + if val in falsey { + return false + } + // Unrecognized boolean type. Probably is not related to boolean. + p.current_pos-- + return true +} + +pub fn (p mut Instance) allow_duplicate() { + p.encountered[p.current_flag] = false +} + +pub fn (p mut Instance) is_equivalent_to(flags []string) { + for v in flags { + p.encountered[v] = true + } +} diff --git a/cmd/v/internal/flag/flag_workaround.v b/cmd/v/internal/flag/flag_workaround.v new file mode 100644 index 0000000000..031d78b167 --- /dev/null +++ b/cmd/v/internal/flag/flag_workaround.v @@ -0,0 +1,49 @@ +// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +module flag + +import ( + v.pref +) + +type void_cb fn(string, &Instance, voidptr) + +// This file contains all instance of usage in cmd/v. Should be replaced by generics. +pub fn parse_pref(args []string, callback fn(string, &Instance, &pref.Preferences), obj &pref.Preferences) ?[]string { + mut p := Instance { + args: args + current_pos: 0 + } + casted_callback := *(&void_cb(&callback)) + tmp := p.parse_impl(args, voidptr(obj), casted_callback) or { + return error(err) + } + return tmp +} + +pub enum MainCmdAction { + unspecified + version + help +} + +pub struct MainCmdPreferences { +pub mut: + verbosity pref.VerboseLevel + action MainCmdAction + unknown_flag string +} + +pub fn parse_main_cmd(args []string, callback fn(string, &Instance, &MainCmdPreferences), obj &MainCmdPreferences) ?[]string { + mut p := Instance { + args: args + current_pos: 0 + } + casted_callback := *(&void_cb(&callback)) + tmp := p.parse_impl(args, voidptr(obj), casted_callback) or { + return error(err) + } + return tmp +} diff --git a/cmd/v/simple_tool.v b/cmd/v/simple_tool.v index 9f4078ab51..fe9e34171e 100644 --- a/cmd/v/simple_tool.v +++ b/cmd/v/simple_tool.v @@ -10,27 +10,23 @@ import ( v.pref ) -fn launch_tool(is_verbose bool, tname string, cmdname string) { +fn launch_tool(verbosity pref.VerboseLevel, tool_name string) { vexe := pref.vexe_path() vroot := filepath.dir(vexe) compiler.set_vroot_folder(vroot) - mut tname_index := os.args.index(cmdname) - if tname_index == -1 { - tname_index = os.args.len - } - mut compilation_options := os.args[1..tname_index].clone() tool_args := os.args[1..].join(' ') - tool_exe := path_of_executable(os.realpath('$vroot/cmd/tools/$tname')) - tool_source := os.realpath('$vroot/cmd/tools/${tname}.v') + tool_exe := path_of_executable(os.realpath('$vroot/cmd/tools/$tool_name')) + tool_source := os.realpath('$vroot/cmd/tools/${tool_name}.v') tool_command := '"$tool_exe" $tool_args' - if is_verbose { + if verbosity.is_higher_or_equal(.level_two) { eprintln('launch_tool vexe : $vroot') eprintln('launch_tool vroot : $vroot') eprintln('launch_tool tool_args : $tool_args') eprintln('launch_tool tool_command: $tool_command') } + // TODO Caching should be done on the `vlib/v` level. mut should_compile := false if !os.exists(tool_exe) { should_compile = true @@ -40,7 +36,7 @@ fn launch_tool(is_verbose bool, tname string, cmdname string) { // rebuild the tool too just in case should_compile = true - if tname == 'vself' || tname == 'vup' { + if tool_name == 'vself' || tool_name == 'vup' { // The purpose of vself/up is to update and recompile v itself. // After the first 'v self' execution, v will be modified, so // then a second 'v self' will detect, that v is newer than the @@ -54,25 +50,26 @@ fn launch_tool(is_verbose bool, tname string, cmdname string) { should_compile = true } } - if is_verbose { + if verbosity.is_higher_or_equal(.level_two) { eprintln('launch_tool should_compile: $should_compile') } if should_compile { - if tname == 'vfmt' { - compilation_options << ['-d', 'vfmt'] + mut compilation_command := '"$vexe" ' + if tool_name == 'vfmt' { + // TODO Remove when it's no longer required by fmt + compilation_command += '-d vfmt ' } - compilation_args := compilation_options.join(' ') - compilation_command := '"$vexe" $compilation_args "$tool_source"' - if is_verbose { - eprintln('Compiling $tname with: "$compilation_command"') + compilation_command += '"$tool_source"' + if verbosity.is_higher_or_equal(.level_three) { + eprintln('Compiling $tool_name with: "$compilation_command"') } tool_compilation := os.exec(compilation_command) or { panic(err) } if tool_compilation.exit_code != 0 { panic('V tool "$tool_source" could not be compiled\n' + tool_compilation.output) } } - if is_verbose { + if verbosity.is_higher_or_equal(.level_three) { eprintln('launch_tool running tool command: $tool_command ...') } diff --git a/cmd/v/v.v b/cmd/v/v.v index b416a14613..ed39ec9853 100644 --- a/cmd/v/v.v +++ b/cmd/v/v.v @@ -5,6 +5,8 @@ module main import ( compiler + internal.compile + internal.flag internal.help os v.table @@ -23,69 +25,120 @@ const ( ) fn main() { - arg := join_flags_and_argument() - command,option := get_basic_command_and_option(arg) - is_verbose := '-verbose' in arg || '--verbose' in arg - if '-v' in option || '--version' in option || command == 'version' { - // Print the version and exit. - version_hash := compiler.vhash() - println('V $compiler.Version $version_hash') - return + prefs := flag.MainCmdPreferences{} + values := flag.parse_main_cmd(os.args, parse_flags, &prefs) or { + println('V Error: An error has occured while parsing flags: ') + println(err) + exit(1) } - if '-h' in option || '--help' in option || command == 'help' { - if is_verbose { - println(help.verbose_help_text) + if prefs.verbosity.is_higher_or_equal(.level_two) { + println('V $compiler.Version $compiler.vhash()') + } + if prefs.verbosity.is_higher_or_equal(.level_three) { + println('Parsed preferences: ') + println(prefs) + println('Remaining: $values') + } + // Do a quick check for `v -v`. Too much error has been made this way. + if prefs.verbosity == .level_one && values.len == 0 { + println("`v -v` now runs V with verbose mode set to level one which doesn't do anything.") + println('Did you mean `v -version` instead?') + exit(1) + } + // Start calling the correct functions/external tools + // Note for future contributors: Please add new subcommands in the `match` block below. + if prefs.action == .version { + disallow_unknown_flags(prefs) + print_version_and_exit() + } + if values.len == 0 && prefs.action == .help { + println('Use `v help` for usage information.') + exit(1) + } + if values.len == 0 || values[0] == '-' || values[0] == 'repl' { + // Check for REPL. + if values.len == 0 { + println('Running REPL as no arguments are provided.') + println('For usage information, quit V REPL using `exit` and use `v help`.') } - else { - println(help.help_text) - } - return - } - if is_verbose { - eprintln('v args: $arg') - eprintln('v command: $command') - eprintln('v options: $option') - } - if command == 'doc' { - mod := arg[arg.len-1] - table := table.new_table() - println(doc.doc(mod, table)) - return + launch_tool(prefs.verbosity, 'vrepl') } + command := values[0] if command in simple_cmd { // External tools - launch_tool(is_verbose, 'v' + command, command) - return - } - if command == 'run' || command == 'build' || command.ends_with('.v') || os.exists(command) { - compile(command, arg) + launch_tool(prefs.verbosity, 'v' + command) return } match command { - '', '-' { - if arg.len == 1 { - println('Running REPL as no arguments are provided. For usage information, use `v help`.') - } - launch_tool(is_verbose, 'vrepl', '') - } 'translate' { println('Translating C to V will be available in V 0.3') + return } 'search', 'install', 'update', 'remove' { - launch_tool(is_verbose, 'vpm', command) + launch_tool(prefs.verbosity, 'vpm') + return } 'get' { - println('Use `v install` to install modules from vpm.vlang.io') - } - 'symlink' { - create_symlink() - } - //'doc' { - //println('Currently unimplemented') - //} - else { - eprintln('v $command: unknown command\nRun "v help" for usage.') + println('V Error: Use `v install` to install modules from vpm.vlang.io') exit(1) } + 'symlink' { + disallow_unknown_flags(prefs) + create_symlink() + return + } + 'doc' { + disallow_unknown_flags(prefs) + if values.len == 1 { + println('V Error: Expected argument: Module name to output documentations for') + exit(1) + } + table := table.new_table() + println(doc.doc(values[1], table)) + return + } + 'help' { + // We check if the arguments are empty as we don't want to steal it from tools + // TODO Call actual help tool + disallow_unknown_flags(prefs) + if prefs.verbosity.is_higher_or_equal(.level_one) { + println(help.verbose_help_text) + } + else { + println(help.help_text) + } + if values.len > 1 { + println('Note: Actual help module is coming soon. Feel free to ask on the official channels for clarification.') + } + return + } + 'version' { + disallow_unknown_flags(prefs) + print_version_and_exit() + return + } + else {} } + if command == 'run' || command == 'build' || command.ends_with('.v') || os.exists(command) { + arg := join_flags_and_argument() + compile.compile(command, arg) + return + } + eprintln('v $command: unknown command\nRun "v help" for usage.') + exit(1) +} + +fn print_version_and_exit() { + version_hash := compiler.vhash() + println('V $compiler.Version $version_hash') + exit(0) +} + +[inline] +fn disallow_unknown_flags(prefs flag.MainCmdPreferences) { + if prefs.unknown_flag == '' { + return + } + println('V Error: Unexpected flag found: $prefs.unknown_flag') + exit(1) } diff --git a/make.bat b/make.bat index c3a18f13d3..dedc2d2d25 100644 --- a/make.bat +++ b/make.bat @@ -77,8 +77,8 @@ if %ERRORLEVEL% NEQ 0 ( ) echo rebuild from source (twice, in case of C definitions changes) -v2.exe -cc msvc -o v3.exe cmd/v -v3.exe -cc msvc -o v -prod cmd/v +v2.exe -o v3.exe cmd/v +v3.exe -o v -prod cmd/v if %ERRORLEVEL% NEQ 0 ( echo V failed to build itself with error %ERRORLEVEL% goto :compileerror @@ -115,4 +115,4 @@ exit /b 1 :success echo V build OK! -v -v +v -version diff --git a/vlib/compiler/aparser.v b/vlib/compiler/aparser.v index de9ae238bf..b8377da0b2 100644 --- a/vlib/compiler/aparser.v +++ b/vlib/compiler/aparser.v @@ -3186,7 +3186,7 @@ fn (p mut Parser) check_unused_imports() { } // the imports are usually at the start of the file // p.production_error_with_token_index('the following imports were never used: $output', 0) - if p.pref.is_verbose { + if p.pref.verbosity.is_higher_or_equal(.level_two) { eprintln('Used imports table: ${p.import_table.used_imports.str()}') } p.warn('the following imports were never used: $output') diff --git a/vlib/compiler/cc.v b/vlib/compiler/cc.v index e21eaba416..90bfa57fca 100644 --- a/vlib/compiler/cc.v +++ b/vlib/compiler/cc.v @@ -17,7 +17,7 @@ fn todo() { fn (v &V) no_cc_installed() bool { $if windows { os.exec('$v.pref.ccompiler -v')or{ - if v.pref.is_verbose { + if v.pref.verbosity.is_higher_or_equal(.level_one) { println('C compiler not found, trying to build with msvc...') } return true @@ -328,7 +328,7 @@ start: // TODO remove cmd := '${v.pref.ccompiler} $args' // Run - if v.pref.show_c_cmd || v.pref.is_verbose { + if v.pref.verbosity.is_higher_or_equal(.level_one) { println('\n==========') println(cmd) } @@ -402,7 +402,7 @@ If you're confident that all of the above is true, please try running V with the } diff := time.ticks() - ticks // Print the C command - if v.pref.show_c_cmd || v.pref.is_verbose { + if v.pref.verbosity.is_higher_or_equal(.level_one) { println('${v.pref.ccompiler} took $diff ms') println('=========\n') } @@ -513,7 +513,7 @@ fn (c mut V) cc_windows_cross() { println(cmd) //cmd := 'clang -o $obj_name -w $include -m32 -c -target x86_64-win32 $v_modules_path/$c.out_name_c' - if c.pref.show_c_cmd { + if c.pref.verbosity.is_higher_or_equal(.level_one) { println(cmd) } if os.system(cmd) != 0 { diff --git a/vlib/compiler/compile_errors.v b/vlib/compiler/compile_errors.v index 9493c1d83c..ea995a33da 100644 --- a/vlib/compiler/compile_errors.v +++ b/vlib/compiler/compile_errors.v @@ -176,7 +176,7 @@ fn (p mut Parser) print_error_context() { // os.write_to_file('/var/tmp/lang.types', '')//pes(p.table.types)) os.write_file('fns.txt', p.table.debug_fns()) } - if p.pref.is_verbose || p.pref.is_debug { + if p.pref.verbosity.is_higher_or_equal(.level_three) { println('pass=$p.pass fn=`$p.cur_fn.name`\n') } p.cgen.save() diff --git a/vlib/compiler/comptime.v b/vlib/compiler/comptime.v index 8a2888c7f0..af1e3f63ce 100644 --- a/vlib/compiler/comptime.v +++ b/vlib/compiler/comptime.v @@ -209,7 +209,7 @@ fn (p mut Parser) comp_time() { p.check(.lpar) p.check(.rpar) v_code := tmpl.compile_template(path) - if p.pref.is_verbose { + if p.pref.verbosity.is_higher_or_equal(.level_three) { println('\n\n') println('>>> vweb template for ${path}:') println(v_code) diff --git a/vlib/compiler/expression.v b/vlib/compiler/expression.v index 5f9db45316..ba6f974b9e 100644 --- a/vlib/compiler/expression.v +++ b/vlib/compiler/expression.v @@ -951,7 +951,7 @@ fn (p mut Parser) factor() string { return typ } else { - if p.pref.is_verbose || p.pref.is_debug { + if p.pref.verbosity.is_higher_or_equal(.level_three) { next := p.peek() println('prev=${p.prev_tok.str()}') println('next=${next.str()}') diff --git a/vlib/compiler/fn.v b/vlib/compiler/fn.v index 41bfe9a9bf..52a7d926e4 100644 --- a/vlib/compiler/fn.v +++ b/vlib/compiler/fn.v @@ -431,7 +431,7 @@ fn (p mut Parser) fn_decl() { str_args := f.str_args(p.table) // Special case for main() args if f.name == 'main__main' && !has_receiver { - if p.pref.x64 && !p.first_pass() { + if p.pref.backend == .x64 && !p.first_pass() { //p.x64.save_main_fn_addr() } if str_args != '' || typ != 'void' { @@ -581,7 +581,7 @@ fn (p mut Parser) fn_decl() { f.defer_text[f.scope_level] = ' ${cgen_name}_time += time__ticks() - _PROF_START;' } } - if p.pref.x64 { + if p.pref.backend == .x64 { //p.x64.register_function_address(f.name) } p.statements_no_rcbr() @@ -597,10 +597,10 @@ fn (p mut Parser) fn_decl() { if typ != 'void' && !p.returns { p.error_with_token_index('$f.name must return "$typ"', f.fn_name_token_idx) } - if p.pref.x64 && f.name == 'main__main' && !p.first_pass() { + if p.pref.backend == .x64 && f.name == 'main__main' && !p.first_pass() { //p.x64.gen_exit() } - if p.pref.x64 && !p.first_pass() { + if p.pref.backend == .x64 && !p.first_pass() { //p.x64.ret() } // {} closed correctly? scope_level should be 0 @@ -778,7 +778,7 @@ fn (p mut Parser) fn_call(f mut Fn, method_ph int, receiver_var, receiver_type s if is_comptime_define { p.cgen.nogen = true } - if p.pref.x64 && !p.first_pass() { + if p.pref.backend == .x64 && !p.first_pass() { //p.x64.call_fn(f.name) } p.calling_c = f.is_c @@ -1104,7 +1104,7 @@ fn (p mut Parser) fn_call_args(f mut Fn, generic_param_types []string) { p.gen('/*YY f=$f.name arg=$arg.name is_moved=$arg.is_moved*/string_clone(') } // x64 println gen - if p.pref.x64 && i == 0 && f.name == 'println' && p.tok == .str && p.peek() == .rpar { + if p.pref.backend == .x64 && i == 0 && f.name == 'println' && p.tok == .str && p.peek() == .rpar { //p.x64.gen_print(p.lit) } mut typ := p.bool_expression() diff --git a/vlib/compiler/for.v b/vlib/compiler/for.v index f72779d1ad..b5735cadc1 100644 --- a/vlib/compiler/for.v +++ b/vlib/compiler/for.v @@ -143,13 +143,13 @@ fn (p mut Parser) for_st() { if is_range { p.check_types(typ, 'int') p.check_space(.dotdot) - if p.pref.x64 { + if p.pref.backend == .x64 { to = p.lit.int() } range_typ,range_expr := p.tmp_expr() p.check_types(range_typ, 'int') range_end = range_expr - if p.pref.x64 { + if p.pref.backend == .x64 { //label = p.x64.gen_loop_start(expr.int()) // to = range_expr.int() // TODO why empty? } diff --git a/vlib/compiler/live.v b/vlib/compiler/live.v index 2fc7b61b8c..28e1991fb8 100644 --- a/vlib/compiler/live.v +++ b/vlib/compiler/live.v @@ -95,12 +95,12 @@ fn (v &V) generate_hot_reload_code() { } so_debug_flag := if v.pref.is_debug { '-g' } else { '' } cmd_compile_shared_library := '$vexe $msvc $so_debug_flag -o $file_base -solive -shared $file' - if v.pref.show_c_cmd { + if v.pref.verbosity.is_higher_or_equal(.level_one) { println(cmd_compile_shared_library) } ticks := time.ticks() os.system(cmd_compile_shared_library) - if v.pref.is_verbose { + if v.pref.verbosity.is_higher_or_equal(.level_two) { diff := time.ticks() - ticks println('compiling shared library took $diff ms') println('=========\n') diff --git a/vlib/compiler/main.v b/vlib/compiler/main.v index c60e2238b6..98ca3d3d20 100644 --- a/vlib/compiler/main.v +++ b/vlib/compiler/main.v @@ -142,12 +142,12 @@ pub fn (v mut V) compile() { } mut cgen := v.cgen cgen.genln('// Generated by V') - if v.pref.is_verbose { + if v.pref.verbosity.is_higher_or_equal(.level_three) { println('all .v files before:') println(v.files) } v.add_v_files_to_compile() - if v.pref.is_verbose { + if v.pref.verbosity.is_higher_or_equal(.level_three) { println('all .v files:') println(v.files) } @@ -303,7 +303,7 @@ pub fn (v mut V) compile() { v.generate_init() v.generate_main() v.generate_hot_reload_code() - if v.pref.is_verbose { + if v.pref.verbosity.is_higher_or_equal(.level_three) { v.log('flags=') for flag in v.get_os_cflags() { println(' * ' + flag.format()) @@ -324,7 +324,7 @@ pub fn (v mut V) compile2() { } //cgen.genln('// Generated by V') println('compile2()') - if v.pref.is_verbose { + if v.pref.verbosity.is_higher_or_equal(.level_three) { println('all .v files before:') println(v.files) } @@ -335,7 +335,7 @@ pub fn (v mut V) compile2() { v.files << v.get_builtin_files() v.files << v.get_user_files() v.set_module_lookup_paths() - if v.pref.is_verbose { + if v.pref.verbosity.is_higher_or_equal(.level_three) { println('all .v files:') println(v.files) } @@ -583,7 +583,7 @@ pub fn (v &V) v_files_from_dir(dir string) []string { mut files := os.ls(dir)or{ panic(err) } - if v.pref.is_verbose { + if v.pref.verbosity.is_higher_or_equal(.level_three) { println('v_files_from_dir ("$dir")') } files.sort() @@ -645,7 +645,7 @@ pub fn (v mut V) add_v_files_to_compile() { builtin_files = [builtin_vh] } } - if v.pref.is_verbose { + if v.pref.verbosity.is_higher_or_equal(.level_two) { v.log('v.add_v_files_to_compile > builtin_files: $builtin_files') } // Parse builtin imports @@ -674,7 +674,7 @@ pub fn (v mut V) add_v_files_to_compile() { } // Parse lib imports v.parse_lib_imports() - if v.pref.is_verbose { + if v.pref.verbosity.is_higher_or_equal(.level_three) { v.log('imports:') println(v.table.imports) } @@ -687,6 +687,8 @@ pub fn (v mut V) add_v_files_to_compile() { continue } // use cached built module if exists + // Cached modules are broken currently + /* if v.pref.vpath != '' && v.pref.build_mode != .build_module && !mod.contains('vweb') { mod_path := mod.replace('.', filepath.separator) vh_path := '$v_modules_path${filepath.separator}vlib${filepath.separator}${mod_path}.vh' @@ -697,6 +699,7 @@ pub fn (v mut V) add_v_files_to_compile() { continue } } + */ // standard module vfiles := v.get_imported_module_files(mod) for file in vfiles { @@ -716,14 +719,24 @@ pub fn (v mut V) add_v_files_to_compile() { } pub fn (v &V) get_builtin_files() []string { - // .vh cache exists? Use it - if v.pref.is_bare { - return v.v_files_from_dir(filepath.join(v.pref.vlib_path,'builtin','bare')) + // Lookup for built-in folder in lookup path. + // Assumption: `builtin/` folder implies usable implementation of builtin + for location in v.pref.lookup_path { + if !os.exists(filepath.join(location, 'builtin')) { + continue + } + if v.pref.is_bare { + return v.v_files_from_dir(filepath.join(location, 'builtin', 'bare')) + } + $if js { + return v.v_files_from_dir(filepath.join(location, 'builtin','js')) + } + return v.v_files_from_dir(filepath.join(location, 'builtin')) } - $if js { - return v.v_files_from_dir(filepath.join(v.pref.vlib_path,'builtin','js')) - } - return v.v_files_from_dir(filepath.join(v.pref.vlib_path,'builtin')) + // Panic. We couldn't find the folder. + verror('`builtin/` not included on module lookup path. +Did you forget to add vlib to the path? (Use @vlib for default vlib)') + panic('Unreachable code reached.') } // get user files @@ -763,7 +776,7 @@ pub fn (v &V) get_user_files() []string { if is_internal_module_test { // v volt/slack_test.v: compile all .v files to get the environment single_test_v_file := os.realpath(dir) - if v.pref.is_verbose { + if v.pref.verbosity.is_higher_or_equal(.level_two) { v.log('> Compiling an internal module _test.v file $single_test_v_file .') v.log('> That brings in all other ordinary .v files in the same module too .') } @@ -774,12 +787,12 @@ pub fn (v &V) get_user_files() []string { single_v_file := dir // Just compile one file and get parent dir user_files << single_v_file - if v.pref.is_verbose { + if v.pref.verbosity.is_higher_or_equal(.level_two) { v.log('> just compile one file: "${single_v_file}"') } } else { - if v.pref.is_verbose { + if v.pref.verbosity.is_higher_or_equal(.level_two) { v.log('> add all .v files from directory "${dir}" ...') } // Add .v files from the directory being compiled @@ -792,7 +805,7 @@ pub fn (v &V) get_user_files() []string { println('No input .v files') exit(1) } - if v.pref.is_verbose { + if v.pref.verbosity.is_higher_or_equal(.level_two) { v.log('user_files: $user_files') } return user_files @@ -840,7 +853,7 @@ pub fn (v mut V) parse_lib_imports() { pub fn (v &V) log(s string) { - if !v.pref.is_verbose { + if !v.pref.verbosity.is_higher_or_equal(.level_two) { return } println(s) diff --git a/vlib/compiler/modules.v b/vlib/compiler/modules.v index 2dadf6d6aa..d4b02fe54f 100644 --- a/vlib/compiler/modules.v +++ b/vlib/compiler/modules.v @@ -154,7 +154,6 @@ fn (v &V) module_path(mod string) string { // 'installed_mod' => '~/.vmodules/installed_mod' // 'local_mod' => '/path/to/current/dir/local_mod' fn (v mut V) set_module_lookup_paths() { - mlookup_path := if v.pref.vpath.len > 0 { v.pref.vpath } else { v_modules_path } // Module search order: // 0) V test files are very commonly located right inside the folder of the // module, which they test. Adding the parent folder of the module folder @@ -163,21 +162,18 @@ fn (v mut V) set_module_lookup_paths() { // 1) search in the *same* directory, as the compiled final v program source // (i.e. the . in `v .` or file.v in `v file.v`) // 2) search in the modules/ in the same directory. - // 3) search in vlib/ - // 4.1) search in -vpath (if given) - // 4.2) search in ~/.vmodules/ (i.e. modules installed with vpm) (no -vpath) + // 3) search in the provided paths + // By default, these are what (3) contains: + // 3.1) search in vlib/ + // 3.2) search in ~/.vmodules/ (i.e. modules installed with vpm) v.module_lookup_paths = [] if v.pref.is_test { v.module_lookup_paths << filepath.basedir(v.compiled_dir) // pdir of _test.v } v.module_lookup_paths << v.compiled_dir v.module_lookup_paths << filepath.join(v.compiled_dir,'modules') - v.module_lookup_paths << v.pref.vlib_path - v.module_lookup_paths << mlookup_path - if v.pref.user_mod_path.len > 0 { - v.module_lookup_paths << v.pref.user_mod_path - } - if v.pref.is_verbose { + v.module_lookup_paths << v.pref.lookup_path + if v.pref.verbosity.is_higher_or_equal(.level_two) { v.log('v.module_lookup_paths: $v.module_lookup_paths') } } @@ -195,11 +191,11 @@ fn (p mut Parser) find_module_path(mod string) ?string { mod_path := p.v.module_path(mod) for lookup_path in module_lookup_paths { try_path := filepath.join(lookup_path,mod_path) - if p.v.pref.is_verbose { + if p.v.pref.verbosity.is_higher_or_equal(.level_three) { println(' >> trying to find $mod in $try_path ...') } if os.is_dir(try_path) { - if p.v.pref.is_verbose { + if p.v.pref.verbosity.is_higher_or_equal(.level_three) { println(' << found $try_path .') } return try_path diff --git a/vlib/compiler/msvc.v b/vlib/compiler/msvc.v index 14443d5018..5bce7fee7f 100644 --- a/vlib/compiler/msvc.v +++ b/vlib/compiler/msvc.v @@ -280,7 +280,7 @@ pub fn (v mut V) cc_msvc() { cmd := '"$r.full_cl_exe_path" $args' // It is hard to see it at first, but the quotes above ARE balanced :-| ... // Also the double quotes at the start ARE needed. - if v.pref.show_c_cmd || v.pref.is_verbose { + if v.pref.verbosity.is_higher_or_equal(.level_one) { println('\n========== cl cmd line:') println(cmd) println('==========\n') diff --git a/vlib/compiler/tests/repl/repl_test.v b/vlib/compiler/tests/repl/repl_test.v index 36d94b19ba..6893095dea 100644 --- a/vlib/compiler/tests/repl/repl_test.v +++ b/vlib/compiler/tests/repl/repl_test.v @@ -10,7 +10,7 @@ fn test_the_v_compiler_can_be_invoked() { vexec := runner.full_path_to_v(5) println('vexecutable: $vexec') assert vexec != '' - vcmd := '"$vexec" --version' + vcmd := '"$vexec" -version' r := os.exec(vcmd) or { panic(err) } @@ -22,7 +22,7 @@ fn test_the_v_compiler_can_be_invoked() { } // println('"$vcmd_error" exit_code: $r_error.exit_code | output: $r_error.output') assert r_error.exit_code == 1 - assert r_error.output == '`nonexisting.v` does not exist' + assert r_error.output == "V error: nonexisting.v doesn't exist" } struct Session { diff --git a/vlib/v/builder/builder.v b/vlib/v/builder/builder.v index bd89840151..5087a2e7ab 100644 --- a/vlib/v/builder/builder.v +++ b/vlib/v/builder/builder.v @@ -138,7 +138,7 @@ pub fn (b &Builder) v_files_from_dir(dir string) []string { mut files := os.ls(dir) or { panic(err) } - if b.pref.is_verbose { + if b.pref.verbosity.is_higher_or_equal(.level_one) { println('v_files_from_dir ("$dir")') } files.sort() @@ -193,7 +193,7 @@ fn verror(err string) { } pub fn (b &Builder) log(s string) { - if b.pref.is_verbose { + if b.pref.verbosity.is_higher_or_equal(.level_two) { println(s) } } @@ -208,11 +208,11 @@ pub fn (b &Builder) find_module_path(mod string) ?string { mod_path := module_path(mod) for search_path in b.module_search_paths { try_path := filepath.join(search_path,mod_path) - if b.pref.is_verbose { + if b.pref.verbosity.is_higher_or_equal(.level_three) { println(' >> trying to find $mod in $try_path ..') } if os.is_dir(try_path) { - if b.pref.is_verbose { + if b.pref.verbosity.is_higher_or_equal(.level_three) { println(' << found $try_path .') } return try_path diff --git a/vlib/v/pref/default.v b/vlib/v/pref/default.v index ff648079b2..e02ac281ca 100644 --- a/vlib/v/pref/default.v +++ b/vlib/v/pref/default.v @@ -17,14 +17,15 @@ pub fn (p mut Preferences) fill_with_defaults() { // Location of all vlib files p.vroot = filepath.dir(vexe_path()) } - if p.vlib_path == '' { - p.vlib_path = filepath.join(p.vroot,'vlib') + vlib_path := filepath.join(p.vroot, 'vlib') + if p.lookup_path.len == 0 { + p.lookup_path = ['@vlib', '@vmodules'] } - if p.vpath == '' { - p.vpath = default_module_path + for i, path in p.lookup_path { + p.lookup_path[i] = path.replace('@vlib', vlib_path).replace('@vmodules', default_module_path) } + rpath := os.realpath(p.path) if p.out_name == ''{ - rpath := os.realpath(p.path) filename := filepath.filename(rpath).trim_space() mut base := filename.all_before_last('.') if base == '' { @@ -43,6 +44,8 @@ pub fn (p mut Preferences) fill_with_defaults() { p.out_name = 'v2' } } + rpath_name := filepath.filename(rpath) + p.building_v = !p.is_repl && (rpath_name == 'v' || rpath_name == 'vfmt.v') if p.os == ._auto { // No OS specifed? Use current system p.os = get_host_os() @@ -52,6 +55,14 @@ pub fn (p mut Preferences) fill_with_defaults() { } p.is_test = p.path.ends_with('_test.v') p.is_script = p.path.ends_with('.v') || p.path.ends_with('.vsh') + if p.third_party_option == '' { + p.third_party_option = p.cflags + $if !windows { + if !p.third_party_option.contains('-fPIC') { + p.third_party_option += ' -fPIC' + } + } + } } fn default_c_compiler() string { diff --git a/vlib/v/pref/os.v b/vlib/v/pref/os.v index 449ed1364e..dccc775c30 100644 --- a/vlib/v/pref/os.v +++ b/vlib/v/pref/os.v @@ -19,7 +19,7 @@ pub enum OS { } // Helper function to convert string names to OS enum -pub fn os_from_string(os_str string) OS { +pub fn os_from_string(os_str string) ?OS { match os_str { 'linux' { return .linux @@ -64,7 +64,7 @@ pub fn os_from_string(os_str string) OS { return ._auto } else { - panic('bad os $os_str') + return error('bad OS $os_str') } } } diff --git a/vlib/v/pref/pref.v b/vlib/v/pref/pref.v index 583d5803ed..e718cc7bdd 100644 --- a/vlib/v/pref/pref.v +++ b/vlib/v/pref/pref.v @@ -12,10 +12,26 @@ pub enum BuildMode { build_module } +pub enum Backend { + c // The (default) C backend + experimental // The experimental v2 backend + js // The JavaScript backend + x64 // The x64 backend +} + +pub enum VerboseLevel { + clean // `-verbose 0` or unspecified + level_one // `-v` or `-verbose 1` + level_two // `-vv` or `-verbose 2` + level_three // `-vvv` or `-verbose 3` +} + pub struct Preferences { pub mut: os OS // the OS to compile for + backend Backend build_mode BuildMode + verbosity VerboseLevel // nofmt bool // disable vfmt is_test bool // `v test string_test.v` is_script bool // single file mode (`v program.v`), main function can be skipped @@ -25,11 +41,9 @@ pub mut: is_prof bool // benchmark every function translated bool // `v translate doom.v` are we running V code translated from C? allow globals, ++ expressions, etc is_prod bool // use "-O2" - is_verbose bool // print extra information with `v.log()` obfuscate bool // `v -obf program.v`, renames functions to "f_XXX" is_repl bool is_run bool - show_c_cmd bool // `v -show_c_cmd` prints the C command to build program.v.c sanitize bool // use Clang's new "-fsanitize" option is_debug bool // false by default, turned on by -g or -cg, it tells v to pass -g to the C backend compiler. is_vlines bool // turned on by -g, false by default (it slows down .tmp.c generation slightly). @@ -40,6 +54,7 @@ pub mut: is_cache bool // turns on v usage of the module cache to speed up compilation. is_stats bool // `v -stats file_test.v` will produce more detailed statistics for the tests that were run no_auto_free bool // `v -nofree` disable automatic `free()` insertion for better performance in some applications (e.g. compilers) + // TODO Convert this into a []string cflags string // Additional options which will be passed to the C compiler. // For example, passing -cflags -Os will cause the C compiler to optimize the generated binaries for size. // You could pass several -cflags XXX arguments. They will be merged with each other. @@ -58,13 +73,9 @@ pub mut: enable_globals bool // allow __global for low level code // is_fmt bool is_bare bool - user_mod_path string // `v -user_mod_path /Users/user/modules` adds a new lookup path for imported modules - vlib_path string - vpath string - x64 bool + lookup_path []string output_cross_c bool prealloc bool - v2 bool vroot string out_name string path string // Path to file/folder to compile @@ -75,3 +86,29 @@ pub mut: mod string } + +pub fn backend_from_string(s string) ?Backend { + match s { + 'c' { + return .c + } + 'js' { + return .js + } + 'experimental', 'v2' { + //TODO Remove in the future once it's considered stable :) + return .experimental + } + 'x64' { + return .x64 + } + else { + return error('Unknown backend type $s') + } + } +} + +[inline] +pub fn (v VerboseLevel) is_higher_or_equal(other VerboseLevel) bool { + return int(v) >= int(other) +}