diff --git a/cmd/v/flag.v b/cmd/v/flag.v deleted file mode 100644 index a323c033c7..0000000000 --- a/cmd/v/flag.v +++ /dev/null @@ -1,100 +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 ( - internal.flag - os -) - -const ( - list_of_flags_that_allow_duplicates = ['cc','d','define','cf','cflags'] - //list_of_flags contains a list of flags where an argument is expected past it. - list_of_flags_with_param = [ - 'o', 'output', 'd', 'define', 'b', 'backend', 'cc', 'os', 'target-os', 'arch', - 'csource', 'cf', 'cflags', 'path' - ] -) - -fn join_flags_and_argument() []string { - vosargs := os.getenv('VOSARGS') - if vosargs != '' { - return vosargs.split(' ') - } - // No VOSARGS? Look for VFLAGS and concat it with command-line options. - mut args := []string - vflags := os.getenv('VFLAGS') - if vflags != '' { - args << os.args[0] - args << vflags.split(' ') - if os.args.len > 1 { - args << os.args[1..] - } - return 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 - } - '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_with_param) { - return - } - f.string() or { - println('V error: Error parsing flag. Expected value for `-$flag`.') - exit(1) - } - return - } - } -} diff --git a/cmd/v/internal/compile/common_mistakes.v b/cmd/v/internal/compile/common_mistakes.v deleted file mode 100644 index 73d1212255..0000000000 --- a/cmd/v/internal/compile/common_mistakes.v +++ /dev/null @@ -1,32 +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 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/internal/compile/compile.v b/cmd/v/internal/compile/compile.v deleted file mode 100644 index 069e5cf636..0000000000 --- a/cmd/v/internal/compile/compile.v +++ /dev/null @@ -1,404 +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 compile - -import ( - benchmark - os - v.builder - v.pref - v.util - strings -) - -pub struct V { -pub mut: - mod_file_cacher &builder.ModFileCacher // used during lookup for v.mod to support @VROOT - out_name_c string // name of the temporary C file - files []string // all V files that need to be parsed and compiled - compiled_dir string // contains os.real_path() of the dir of the final file beeing compiled, or the dir itself when doing `v .` - pref &pref.Preferences // all the preferences and settings extracted to a struct for reusability - vgen_buf strings.Builder // temporary buffer for generated V code (.str() etc) - file_parser_idx map[string]int // map absolute file path to v.parsers index - gen_parser_idx map[string]int - cached_mods []string - module_lookup_paths []string - - v_fmt_all bool // << input set by cmd/tools/vfmt.v - v_fmt_file string // << file given by the user from cmd/tools/vfmt.v - v_fmt_file_result string // >> file with formatted output generated by vlib/compiler/vfmt.v -} - -pub fn new_v(pref &pref.Preferences) &V { - rdir := os.real_path(pref.path) - - mut out_name_c := get_vtmp_filename(pref.out_name, '.tmp.c') - if pref.is_so { - out_name_c = get_vtmp_filename(pref.out_name, '.tmp.so.c') - } - - mut vgen_buf := strings.new_builder(1000) - vgen_buf.writeln('module vgen\nimport strings') - compiled_dir:=if os.is_dir(rdir) { rdir } else { os.dir(rdir) } - - return &V{ - mod_file_cacher: builder.new_mod_file_cacher() - compiled_dir:compiled_dir// if os.is_dir(rdir) { rdir } else { os.dir(rdir) } - out_name_c: out_name_c - pref: pref - vgen_buf: vgen_buf - } -} - - -// make v2 from v1 -fn (v &V) new_v2() builder.Builder { - mut b := builder.new_builder(v.pref) - b = { b| - os: v.pref.os, - module_path: pref.default_module_path, - compiled_dir: v.compiled_dir, - module_search_paths: v.module_lookup_paths - } - return b -} - -fn get_vtmp_folder() string { - vtmp := os.join_path(os.temp_dir(), 'v') - if !os.is_dir(vtmp) { - os.mkdir(vtmp) or { - panic(err) - } - } - return vtmp -} - -fn get_vtmp_filename(base_file_name string, postfix string) string { - vtmp := get_vtmp_folder() - return os.real_path(os.join_path(vtmp, os.file_name(os.real_path(base_file_name)) + postfix)) -} - - -pub fn (v mut V) compile_x64() { - $if !linux { - println('v -x64 can only generate Linux binaries for now') - println('You are not on a Linux system, so you will not ' + 'be able to run the resulting executable') - } - //v.files << v.v_files_from_dir(os.join_path(v.pref.vlib_path,'builtin','bare')) - v.files << v.pref.path - v.set_module_lookup_paths() - mut b := v.new_v2() - // move all this logic to v2 - b.build_x64(v.files, v.pref.out_name) -} - - -pub fn (v mut V) compile2() { - if os.user_os() != 'windows' && v.pref.ccompiler == 'msvc' { - verror('Cannot build with msvc on ${os.user_os()}') - } - //cgen.genln('// Generated by V') - //println('compile2()') - if v.pref.verbosity.is_higher_or_equal(.level_three) { - println('all .v files before:') - println(v.files) - } - // v1 compiler files - //v.add_v_files_to_compile() - //v.files << v.dir - // v2 compiler - v.files << v.get_builtin_files() - v.files << v.get_user_files() - v.set_module_lookup_paths() - if v.pref.verbosity.is_higher_or_equal(.level_three) { - println('all .v files:') - println(v.files) - } - mut b := v.new_v2() - b.build_c(v.files, v.out_name_c)// v.pref.out_name + '.c') - v.cc() -} - - -pub fn compile(command string, args []string) { - // Construct the V object from command line arguments - parse_and_output_new_format(args) - prefs, remaining := parse_arguments(args) - check_for_common_mistake(args, prefs) - mut v := new_v(prefs) - if v.pref.verbosity.is_higher_or_equal(.level_two) { - println(args) - } - mut tmark := benchmark.new_benchmark() - if v.pref.backend == .x64 { - v.compile_x64() - } - else if v.pref.backend == .experimental { - v.compile2() - } - else { - //v.compile() - v.compile2() - } - if v.pref.is_stats { - tmark.stop() - println('compilation took: ' + tmark.total_duration().str() + 'ms') - } - 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 &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}"' - for i in 1..remaining_args.len { - // Determine if there are spaces in the parameters - if remaining_args[i].index_byte(` `) > 0 { - cmd += ' "' + remaining_args[i] + '"' - } - else { - cmd += ' ' + remaining_args[i] - } - } - 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) - if ret != 0 { - exit(1) - } - } - if v.pref.is_run { - ret := os.system(cmd) - // TODO: make the runner wrapping as transparent as possible - // (i.e. use execve when implemented). For now though, the runner - // just returns the same exit code as the child process. - exit(ret) - } - exit(0) -} - -// 'strings' => 'VROOT/vlib/strings' -// 'installed_mod' => '~/.vmodules/installed_mod' -// 'local_mod' => '/path/to/current/dir/local_mod' -fn (v mut V) set_module_lookup_paths() { - // 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 - // with the _test.v files, *guarantees* that the tested module can be found - // without needing to set custom options/flags. - // 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 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 << os.base_dir(v.compiled_dir) // pdir of _test.v - } - v.module_lookup_paths << v.compiled_dir - x := os.join_path(v.compiled_dir, 'modules') - if v.pref.verbosity.is_higher_or_equal(.level_two) { - println('x: "$x"') - } - v.module_lookup_paths << os.join_path(v.compiled_dir, 'modules') - 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') - println(v.module_lookup_paths) - } -} - -pub fn (v &V) get_builtin_files() []string { - // 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(os.join_path(location, 'builtin')) { - continue - } - if v.pref.is_bare { - return v.v_files_from_dir(os.join_path(location, 'builtin', 'bare')) - } - $if js { - return v.v_files_from_dir(os.join_path(location, 'builtin', 'js')) - } - return v.v_files_from_dir(os.join_path(location, '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.') -} - - -pub fn (v &V) get_user_files() []string { - mut dir := v.pref.path - v.log('get_v_files($dir)') - // Need to store user files separately, because they have to be added after - // libs, but we dont know which libs need to be added yet - mut user_files := []string - - // See cmd/tools/preludes/README.md for more info about what preludes are - vroot := os.dir(pref.vexe_path()) - preludes_path := os.join_path(vroot, 'cmd', 'tools', 'preludes') - if v.pref.is_live { - user_files << os.join_path(preludes_path, 'live_main.v') - } - if v.pref.is_solive { - user_files << os.join_path(preludes_path, 'live_shared.v') - } - if v.pref.is_test { - user_files << os.join_path(preludes_path, 'tests_assertions.v') - } - if v.pref.is_test && v.pref.is_stats { - user_files << os.join_path(preludes_path, 'tests_with_stats.v') - } - - is_test := dir.ends_with('_test.v') - mut is_internal_module_test := false - if is_test { - tcontent := os.read_file(dir)or{ - panic('$dir does not exist') - } - slines := tcontent.trim_space().split_into_lines() - for sline in slines { - line := sline.trim_space() - if line.len > 2 { - if line[0] == `/` && line[1] == `/` { - continue - } - if line.starts_with('module ') && !line.starts_with('module main') { - is_internal_module_test = true - break - } - } - } - } - if is_internal_module_test { - // v volt/slack_test.v: compile all .v files to get the environment - single_test_v_file := os.real_path(dir) - 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 .') - } - user_files << single_test_v_file - dir = os.base_dir(single_test_v_file) - } - is_real_file := os.exists(dir) && !os.is_dir(dir) - if is_real_file && ( dir.ends_with('.v') || dir.ends_with('.vsh') ) { - single_v_file := dir - // Just compile one file and get parent dir - user_files << single_v_file - if v.pref.verbosity.is_higher_or_equal(.level_two) { - v.log('> just compile one file: "${single_v_file}"') - } - } - else { - 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 - files := v.v_files_from_dir(dir) - for file in files { - user_files << file - } - } - if user_files.len == 0 { - println('No input .v files') - exit(1) - } - if v.pref.verbosity.is_higher_or_equal(.level_two) { - v.log('user_files: $user_files') - } - return user_files -} -pub fn (v &V) log(s string) { - if !v.pref.verbosity.is_higher_or_equal(.level_two) { - return - } - println(s) -} - - -pub fn (v &V) v_files_from_dir(dir string) []string { - mut res := []string - if !os.exists(dir) { - if dir == 'compiler' && os.is_dir('vlib') { - 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 compiler`') - } - verror("$dir doesn't exist") - } - else if !os.is_dir(dir) { - verror("$dir isn't a directory!") - } - mut files := os.ls(dir)or{ - panic(err) - } - if v.pref.verbosity.is_higher_or_equal(.level_three) { - println('v_files_from_dir ("$dir")') - } - files.sort() - for file in files { - if !file.ends_with('.v') && !file.ends_with('.vh') { - continue - } - if file.ends_with('_test.v') { - continue - } - if (file.ends_with('_win.v') || file.ends_with('_windows.v')) && v.pref.os != .windows { - continue - } - if (file.ends_with('_lin.v') || file.ends_with('_linux.v')) && v.pref.os != .linux { - continue - } - if (file.ends_with('_mac.v') || file.ends_with('_darwin.v')) && v.pref.os != .mac { - continue - } - if file.ends_with('_nix.v') && v.pref.os == .windows { - continue - } - if file.ends_with('_android.v') && v.pref.os != .android { - continue - } - if file.ends_with('_freebsd.v') && v.pref.os != .freebsd { - continue - } - if file.ends_with('_solaris.v') && v.pref.os != .solaris { - continue - } - if file.ends_with('_js.v') && v.pref.os != .js { - continue - } - if file.ends_with('_c.v') && v.pref.os == .js { - continue - } - if v.pref.compile_defines_all.len > 0 && file.contains('_d_') { - mut allowed := false - for cdefine in v.pref.compile_defines { - file_postfix := '_d_${cdefine}.v' - if file.ends_with(file_postfix) { - allowed = true - break - } - } - if !allowed { - continue - } - } - res << os.join_path(dir, file) - } - return res -} - -pub fn verror(s string) { - util.verror('compiler error', s) -} diff --git a/cmd/v/internal/compile/compile_c_options.v b/cmd/v/internal/compile/compile_c_options.v deleted file mode 100644 index 0065838559..0000000000 --- a/cmd/v/internal/compile/compile_c_options.v +++ /dev/null @@ -1,75 +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 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 -) - -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 - } - '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' { - 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 deleted file mode 100644 index f3c322df4a..0000000000 --- a/cmd/v/internal/compile/compile_js_options.v +++ /dev/null @@ -1,20 +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 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 deleted file mode 100644 index 982592da5e..0000000000 --- a/cmd/v/internal/compile/compile_options.v +++ /dev/null @@ -1,235 +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 compile - -import ( - internal.flag - os.cmdline - v.pref - v.util -) - -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 { - -// TODO remove tmp var after cgen optional bug is fixed - x := pref.backend_from_string(backend[0]) or { - println('V error: Unknown backend ${backend[0]} provided.') - exit(1) - } - p.backend = x - } else { - p.backend = .c - } - remaining2 := flag.parse_pref(args, parse_options, p) or { - println('V error: Error while parsing flags.') - println(err) - println('Args:') - println(args) - exit(1) - } - mut remaining := remaining2 // TODO cgen bug - match remaining[0] { - 'run' { - p.is_run = true - remaining = remaining[1..] - } - 'build' { - remaining = remaining[1..] - if remaining.len > 0 && 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 { - 'color' { - f.is_equivalent_to(['color','nocolor']) - util.emanager.set_support_color(true) - } - 'nocolor' { - f.is_equivalent_to(['color','nocolor']) - util.emanager.set_support_color(false) - } - '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' { - 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' { - 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() - } - 'b', '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 deleted file mode 100644 index 25c8e378ed..0000000000 --- a/cmd/v/internal/compile/compile_x64_options.v +++ /dev/null @@ -1,83 +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 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 deleted file mode 100644 index 2cc3f2c7b3..0000000000 --- a/cmd/v/internal/compile/old_options.v +++ /dev/null @@ -1,107 +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 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 deleted file mode 100644 index b57a380bf3..0000000000 --- a/cmd/v/internal/flag/flag.v +++ /dev/null @@ -1,117 +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. - -// 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..] - } - 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) - } - if !val[0].is_digit() { - return error('an integer number was expected, but "$val" was found instead.') - } - 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) 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 deleted file mode 100644 index 709e2dc7e3..0000000000 --- a/cmd/v/internal/flag/flag_workaround.v +++ /dev/null @@ -1,48 +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 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 - } - //tmp := p.parse_impl(args, voidptr(obj), void_cb(callback)) or { - tmp := p.parse_impl(args, obj, void_cb(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 - } - tmp := p.parse_impl(args, obj, void_cb(callback)) or { - return error(err) - } - return tmp -} diff --git a/cmd/v/v.v b/cmd/v/v.v index 90708e4c82..b3b5fa38e8 100644 --- a/cmd/v/v.v +++ b/cmd/v/v.v @@ -4,7 +4,7 @@ module main import ( - internal.compile + //internal.compile internal.help os os.cmdline @@ -12,6 +12,7 @@ import ( v.doc v.pref v.util + v.builder ) const ( @@ -22,6 +23,13 @@ const ( 'repl', 'build-tools', 'build-examples', 'build-vbinaries', 'setup-freetype'] + + list_of_flags_that_allow_duplicates = ['cc','d','define','cf','cflags'] + //list_of_flags contains a list of flags where an argument is expected past it. + list_of_flags_with_param = [ + 'o', 'output', 'd', 'define', 'b', 'backend', 'cc', 'os', 'target-os', 'arch', + 'csource', 'cf', 'cflags', 'path' + ] ) fn main() { @@ -38,12 +46,12 @@ fn main() { println(util.full_v_version()) return } - prefs2, command := parse_args(args) - //println('command = $command') - if prefs2.is_verbose { + prefs, command := parse_args(args) + //println('main() command = $command') + if prefs.is_verbose { println(util.full_v_version()) } - if prefs2.is_verbose { + if prefs.is_verbose { //println('args= ') //println(args) // QTODO //println('prefs= ') @@ -53,7 +61,7 @@ fn main() { // Note for future contributors: Please add new subcommands in the `match` block below. if command in simple_cmd { // External tools - util.launch_tool(prefs2.is_verbose, 'v' + command) + util.launch_tool(prefs.is_verbose, 'v' + command) return } match command { @@ -61,7 +69,7 @@ fn main() { invoke_help_and_exit(args) } 'create', 'init' { - util.launch_tool(prefs2.is_verbose, 'vcreate') + util.launch_tool(prefs.is_verbose, 'vcreate') return } 'translate' { @@ -69,7 +77,7 @@ fn main() { return } 'search', 'install', 'update', 'remove' { - util.launch_tool(prefs2.is_verbose, 'vpm') + util.launch_tool(prefs.is_verbose, 'vpm') return } 'get' { @@ -96,8 +104,7 @@ fn main() { else {} } if command in ['run', 'build'] || command.ends_with('.v') || os.exists(command) { - arg := join_flags_and_argument() - compile.compile(command, arg) + builder.compile(command, prefs) return } eprintln('v $command: unknown command\nRun "v help" for usage.') @@ -113,6 +120,25 @@ fn parse_args(args []string) (&pref.Preferences, string) { match arg { '-v' { res.is_verbose = true } '-cg' { res.is_debug = true } + '-live' { res.is_solive = true } + '-autofree' { res.autofree = true } + '-compress' { res.compress = true } + '-freestanding' { res.is_bare = true } + '-prod' { res.is_prod = true } + '-stats' { res.is_stats = true } + '-obfuscate' { res.obfuscate = true } + '-translated' { res.translated = true } + //'-x64' { res.translated = true } + '-os' { + //TODO Remove `tmp` variable when it doesn't error out in C. + target_os := cmdline.option(args, '-os', '') + tmp := pref.os_from_string(target_os) or { + println('unknown operating system target `$target_os`') + exit(1) + } + res.os = tmp + i++ + } '-cc' { res.ccompiler = cmdline.option(args, '-cc', 'cc') i++ @@ -139,6 +165,10 @@ fn parse_args(args []string) (&pref.Preferences, string) { } } } + if command.ends_with('.v') || os.exists(command) { + res.path = command + } + res.fill_with_defaults() return res, command } diff --git a/vlib/v/builder/builder.v b/vlib/v/builder/builder.v index 6a5c44c75d..f7289b8ec5 100644 --- a/vlib/v/builder/builder.v +++ b/vlib/v/builder/builder.v @@ -26,11 +26,14 @@ mut: module_search_paths []string parsed_files []ast.File global_scope &ast.Scope + out_name_c string } pub fn new_builder(pref &pref.Preferences) Builder { + rdir := os.real_path(pref.path) + compiled_dir := if os.is_dir(rdir) { rdir } else { os.dir(rdir) } table := table.new_table() - return Builder{ + return builder.Builder{ mod_file_cacher: new_mod_file_cacher() pref: pref table: table @@ -38,6 +41,7 @@ pub fn new_builder(pref &pref.Preferences) Builder { global_scope: &ast.Scope{ parent: 0 } + compiled_dir: compiled_dir } } @@ -67,6 +71,7 @@ pub fn (b mut Builder) gen_c(v_files []string) string { } pub fn (b mut Builder) build_c(v_files []string, out_file string) { + b.out_name_c = out_file b.info('build_c($out_file)') mut f := os.create(out_file) or { panic(err) @@ -77,6 +82,10 @@ pub fn (b mut Builder) build_c(v_files []string, out_file string) { } pub fn (b mut Builder) build_x64(v_files []string, out_file string) { + $if !linux { + println('v -x64 can only generate Linux binaries for now') + println('You are not on a Linux system, so you will not ' + 'be able to run the resulting executable') + } t0 := time.ticks() b.parsed_files = parser.parse_files(v_files, b.table, b.pref, b.global_scope) b.parse_imports() @@ -129,7 +138,7 @@ pub fn (b mut Builder) parse_imports() { } } -pub fn (b &Builder) v_files_from_dir(dir string) []string { +pub fn (b Builder) v_files_from_dir(dir string) []string { mut res := []string if !os.exists(dir) { if dir == 'compiler' && os.is_dir('vlib') { @@ -137,14 +146,13 @@ pub fn (b &Builder) v_files_from_dir(dir string) []string { println('use `v -o v cmd/v` instead of `v -o v compiler`') } verror("$dir doesn't exist") - } - else if !os.is_dir(dir) { + } else if !os.is_dir(dir) { verror("$dir isn't a directory!") } mut files := os.ls(dir) or { panic(err) } - if b.pref.verbosity.is_higher_or_equal(.level_one) { + if b.pref.is_verbose { println('v_files_from_dir ("$dir")') } files.sort() @@ -155,7 +163,8 @@ pub fn (b &Builder) v_files_from_dir(dir string) []string { if file.ends_with('_test.v') { continue } - if (file.ends_with('_win.v') || file.ends_with('_windows.v')) && b.os != .windows { + if (file.ends_with('_win.v') || file.ends_with('_windows.v')) && b.os != + .windows { continue } if (file.ends_with('_lin.v') || file.ends_with('_linux.v')) && b.os != .linux { @@ -195,19 +204,19 @@ pub fn (b &Builder) v_files_from_dir(dir string) []string { continue } } - res << os.join_path(dir,file) + res << os.join_path(dir, file) } return res } -pub fn (b &Builder) log(s string) { - if b.pref.verbosity.is_higher_or_equal(.level_two) { +pub fn (b Builder) log(s string) { + if b.pref.is_verbose { println(s) } } -pub fn (b &Builder) info(s string) { - if b.pref.verbosity.is_higher_or_equal(.level_one) { +pub fn (b Builder) info(s string) { + if b.pref.is_verbose { println(s) } } @@ -218,15 +227,15 @@ fn module_path(mod string) string { return mod.replace('.', os.path_separator) } -pub fn (b &Builder) find_module_path(mod string) ?string { +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 := os.join_path(search_path,mod_path) - if b.pref.verbosity.is_higher_or_equal(.level_three) { + try_path := os.join_path(search_path, mod_path) + if b.pref.is_verbose { println(' >> trying to find $mod in $try_path ..') } if os.is_dir(try_path) { - if b.pref.verbosity.is_higher_or_equal(.level_three) { + if b.pref.is_verbose { println(' << found $try_path .') } return try_path diff --git a/cmd/v/internal/compile/cc.v b/vlib/v/builder/cc.v similarity index 97% rename from cmd/v/internal/compile/cc.v rename to vlib/v/builder/cc.v index 25c46920ed..6391ac2973 100644 --- a/cmd/v/internal/compile/cc.v +++ b/vlib/v/builder/cc.v @@ -1,7 +1,7 @@ // 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 +module builder import ( os @@ -14,10 +14,10 @@ import ( fn todo() { } -fn (v &V) no_cc_installed() bool { +fn (v &Builder) no_cc_installed() bool { $if windows { os.exec('$v.pref.ccompiler -v')or{ - if v.pref.verbosity.is_higher_or_equal(.level_one) { + if v.pref.is_verbose { println('C compiler not found, trying to build with msvc...') } return true @@ -26,10 +26,13 @@ fn (v &V) no_cc_installed() bool { return false } -fn (v mut V) cc() { +fn (v mut Builder) cc() { if os.executable().contains('vfmt') { return } + if v.pref.is_verbose { + println('builder.cc() pref.out_name="$v.pref.out_name"') + } v.build_thirdparty_obj_files() vexe := pref.vexe_path() vdir := os.dir(vexe) @@ -320,7 +323,7 @@ start: // TODO remove cmd := '${v.pref.ccompiler} $args' // Run - if v.pref.verbosity.is_higher_or_equal(.level_one) { + if v.pref.is_verbose { println('\n==========') println(cmd) } @@ -394,7 +397,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.verbosity.is_higher_or_equal(.level_one) { + if v.pref.is_verbose { println('${v.pref.ccompiler} took $diff ms') println('=========\n') } @@ -455,7 +458,7 @@ If you're confident that all of the above is true, please try running V with the } } -fn (c mut V) cc_windows_cross() { +fn (c mut Builder) cc_windows_cross() { /* QTODO println('Cross compiling for Windows...') @@ -531,7 +534,7 @@ fn (c mut V) cc_windows_cross() { */ } -fn (c &V) build_thirdparty_obj_files() { +fn (c &Builder) build_thirdparty_obj_files() { for flag in c.get_os_cflags() { if flag.value.ends_with('.o') { rest_of_module_flags := c.get_rest_of_module_cflags(flag) @@ -545,7 +548,7 @@ fn (c &V) build_thirdparty_obj_files() { } } -fn (v &V) build_thirdparty_obj_file(path string, moduleflags []CFlag) { +fn (v &Builder) build_thirdparty_obj_file(path string, moduleflags []CFlag) { obj_path := os.real_path(path) if os.exists(obj_path) { return diff --git a/cmd/v/internal/compile/cflags.v b/vlib/v/builder/cflags.v similarity index 97% rename from cmd/v/internal/compile/cflags.v rename to vlib/v/builder/cflags.v index 45b6e3c843..9df94c0612 100644 --- a/cmd/v/internal/compile/cflags.v +++ b/vlib/v/builder/cflags.v @@ -1,7 +1,7 @@ // 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 +module builder import os // parsed cflag @@ -17,7 +17,7 @@ pub fn (c &CFlag) str() string { } // get flags for current os -fn (v &V) get_os_cflags() []CFlag { +fn (v &Builder) get_os_cflags() []CFlag { mut flags := []CFlag mut ctimedefines := []string if v.pref.compile_defines.len > 0 { @@ -38,7 +38,7 @@ fn (v &V) get_os_cflags() []CFlag { return flags } -fn (v &V) get_rest_of_module_cflags(c &CFlag) []CFlag { +fn (v &Builder) get_rest_of_module_cflags(c &CFlag) []CFlag { mut flags := []CFlag cflags := v.get_os_cflags() for flag in cflags { diff --git a/vlib/v/builder/compile.v b/vlib/v/builder/compile.v new file mode 100644 index 0000000000..e9a7e34f95 --- /dev/null +++ b/vlib/v/builder/compile.v @@ -0,0 +1,300 @@ +// 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 builder + +import ( + benchmark + os + v.pref + v.util + strings +) + +/* +pub struct V { +pub mut: + mod_file_cacher &builder.ModFileCacher // used during lookup for v.mod to support @VROOT + out_name_c string // name of the temporary C file + files []string // all V files that need to be parsed and compiled + compiled_dir string // contains os.real_path() of the dir of the final file beeing compiled, or the dir itself when doing `v .` + pref &pref.Preferences // all the preferences and settings extracted to a struct for reusability + vgen_buf strings.Builder // temporary buffer for generated V code (.str() etc) + file_parser_idx map[string]int // map absolute file path to v.parsers index + gen_parser_idx map[string]int + cached_mods []string + module_lookup_paths []string + + v_fmt_all bool // << input set by cmd/tools/vfmt.v + v_fmt_file string // << file given by the user from cmd/tools/vfmt.v + v_fmt_file_result string // >> file with formatted output generated by vlib/compiler/vfmt.v +} + +pub fn new_v(pref &pref.Preferences) &V { + rdir := os.real_path(pref.path) + + mut out_name_c := get_vtmp_filename(pref.out_name, '.tmp.c') + if pref.is_so { + out_name_c = get_vtmp_filename(pref.out_name, '.tmp.so.c') + } + + mut vgen_buf := strings.new_builder(1000) + vgen_buf.writeln('module vgen\nimport strings') + compiled_dir:=if os.is_dir(rdir) { rdir } else { os.dir(rdir) } + + return &V{ + mod_file_cacher: builder.new_mod_file_cacher() + compiled_dir:compiled_dir// if os.is_dir(rdir) { rdir } else { os.dir(rdir) } + out_name_c: out_name_c + pref: pref + vgen_buf: vgen_buf + } +} +*/ +/* +// make v2 from v1 +fn (v &V) new_v2() builder.Builder { + mut b := builder.new_builder(v.pref) + b = { b| + os: v.pref.os, + module_path: pref.default_module_path, + compiled_dir: v.compiled_dir, + module_search_paths: v.module_lookup_paths + } + return b +} +*/ +fn get_vtmp_folder() string { + vtmp := os.join_path(os.temp_dir(), 'v') + if !os.is_dir(vtmp) { + os.mkdir(vtmp) or { + panic(err) + } + } + return vtmp +} + +fn get_vtmp_filename(base_file_name, postfix string) string { + vtmp := get_vtmp_folder() + return os.real_path(os.join_path(vtmp, os.file_name(os.real_path(base_file_name)) + + postfix)) +} + +pub fn compile(command string, pref &pref.Preferences) { + mut remaining := []string + // Construct the V object from command line arguments + mut b := new_builder(pref) + if pref.is_verbose { + println('builder.compile() pref:') + println(pref) + } + mut tmark := benchmark.new_benchmark() + mut files := []string + if pref.backend == .x64 { + // v.files << v.v_files_from_dir(os.join_path(v.pref.vlib_path,'builtin','bare')) + files << pref.path + b.set_module_lookup_paths() + b.build_x64(files, pref.out_name) + } else { + if os.user_os() != 'windows' && pref.ccompiler == 'msvc' { + verror('Cannot build with msvc on ${os.user_os()}') + } + // cgen.genln('// Generated by V') + // println('compile2()') + if pref.is_verbose { + println('all .v files before:') + println(files) + } + // v1 compiler files + // v.add_v_files_to_compile() + // v.files << v.dir + // v2 compiler + //b.set_module_lookup_paths() + files << b.get_builtin_files() + files << b.get_user_files() + b.set_module_lookup_paths() + if pref.is_verbose { + println('all .v files:') + println(files) + } + //b.build_c(files, b.out_name_c) // v.pref.out_name + '.c') + b.build_c(files, pref.out_name + '.c') + b.cc() + } + if pref.is_stats { + tmark.stop() + println('compilation took: ' + tmark.total_duration().str() + 'ms') + } + if pref.is_test || pref.is_run { + b.run_compiled_executable_and_exit(remaining) + } + // v.finalize_compilation() +} + +fn (b mut Builder) run_compiled_executable_and_exit(remaining []string) { + if b.pref.is_verbose { + println('============ running $b.pref.out_name ============') + } + mut cmd := '"${b.pref.out_name}"' + for i in 1 .. remaining.len { + // Determine if there are spaces in the parameters + if remaining[i].index_byte(` `) > 0 { + cmd += ' "' + remaining[i] + '"' + } else { + cmd += ' ' + remaining[i] + } + } + if b.pref.is_verbose { + println('command to run executable: $cmd') + } + if b.pref.is_test { + ret := os.system(cmd) + if ret != 0 { + exit(1) + } + } + if b.pref.is_run { + ret := os.system(cmd) + // TODO: make the runner wrapping as transparent as possible + // (i.e. use execve when implemented). For now though, the runner + // just returns the same exit code as the child process. + exit(ret) + } + exit(0) +} + +// 'strings' => 'VROOT/vlib/strings' +// 'installed_mod' => '~/.vmodules/installed_mod' +// 'local_mod' => '/path/to/current/dir/local_mod' +fn (v mut Builder) set_module_lookup_paths() { + // 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 + // with the _test.v files, *guarantees* that the tested module can be found + // without needing to set custom options/flags. + // 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 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_search_paths = [] + if v.pref.is_test { + v.module_search_paths << os.base_dir(v.compiled_dir) // pdir of _test.v + } + v.module_search_paths << v.compiled_dir + x := os.join_path(v.compiled_dir, 'modules') + if v.pref.is_verbose { + println('x: "$x"') + } + v.module_search_paths << os.join_path(v.compiled_dir, 'modules') + v.module_search_paths << v.pref.lookup_path + if v.pref.is_verbose { + v.log('v.module_lookup_paths') // : $v.module_lookup_paths') + println(v.module_search_paths) + } +} + +pub fn (v &Builder) get_builtin_files() []string { + //println('get_builtin_files() lookuppath:') + //println(v.pref.lookup_path) + // 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(os.join_path(location, 'builtin')) { + continue + } + if v.pref.is_bare { + return v.v_files_from_dir(os.join_path(location, 'builtin', 'bare')) + } + $if js { + return v.v_files_from_dir(os.join_path(location, 'builtin', 'js')) + } + return v.v_files_from_dir(os.join_path(location, '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.') +} + +pub fn (v &Builder) get_user_files() []string { + mut dir := v.pref.path + v.log('get_v_files($dir)') + // Need to store user files separately, because they have to be added after + // libs, but we dont know which libs need to be added yet + mut user_files := []string + // See cmd/tools/preludes/README.md for more info about what preludes are + vroot := os.dir(pref.vexe_path()) + preludes_path := os.join_path(vroot, 'cmd', 'tools', 'preludes') + if v.pref.is_live { + user_files << os.join_path(preludes_path, 'live_main.v') + } + if v.pref.is_solive { + user_files << os.join_path(preludes_path, 'live_shared.v') + } + if v.pref.is_test { + user_files << os.join_path(preludes_path, 'tests_assertions.v') + } + if v.pref.is_test && v.pref.is_stats { + user_files << os.join_path(preludes_path, 'tests_with_stats.v') + } + is_test := dir.ends_with('_test.v') + mut is_internal_module_test := false + if is_test { + tcontent := os.read_file(dir) or { + panic('$dir does not exist') + } + slines := tcontent.trim_space().split_into_lines() + for sline in slines { + line := sline.trim_space() + if line.len > 2 { + if line[0] == `/` && line[1] == `/` { + continue + } + if line.starts_with('module ') && !line.starts_with('module main') { + is_internal_module_test = true + break + } + } + } + } + if is_internal_module_test { + // v volt/slack_test.v: compile all .v files to get the environment + single_test_v_file := os.real_path(dir) + if v.pref.is_verbose { + 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 .') + } + user_files << single_test_v_file + dir = os.base_dir(single_test_v_file) + } + is_real_file := os.exists(dir) && !os.is_dir(dir) + if is_real_file && (dir.ends_with('.v') || dir.ends_with('.vsh')) { + single_v_file := dir + // Just compile one file and get parent dir + user_files << single_v_file + if v.pref.is_verbose { + v.log('> just compile one file: "${single_v_file}"') + } + } else { + if v.pref.is_verbose { + v.log('> add all .v files from directory "${dir}" ...') + } + // Add .v files from the directory being compiled + files := v.v_files_from_dir(dir) + for file in files { + user_files << file + } + } + if user_files.len == 0 { + println('No input .v files') + exit(1) + } + if v.pref.is_verbose { + v.log('user_files: $user_files') + } + return user_files +} + diff --git a/cmd/v/internal/compile/live.v b/vlib/v/builder/live.v similarity index 96% rename from cmd/v/internal/compile/live.v rename to vlib/v/builder/live.v index b58a06492a..17e385b2ab 100644 --- a/cmd/v/internal/compile/live.v +++ b/vlib/v/builder/live.v @@ -1,11 +1,11 @@ -module compile +module builder import ( os time ) -fn (v &V) generate_hotcode_reloading_compiler_flags() []string { +fn (v &Builder) generate_hotcode_reloading_compiler_flags() []string { mut a := []string if v.pref.is_live || v.pref.is_so { // See 'man dlopen', and test running a GUI program compiled with -live @@ -19,7 +19,7 @@ fn (v &V) generate_hotcode_reloading_compiler_flags() []string { return a } -fn (v &V) generate_hotcode_reloading_declarations() { +fn (v &Builder) generate_hotcode_reloading_declarations() { /* QTODO mut cgen := v.cgen @@ -50,7 +50,7 @@ void pthread_mutex_unlock(HANDLE *m) { */ } -fn (v &V) generate_hotcode_reloading_main_caller() { +fn (v &Builder) generate_hotcode_reloading_main_caller() { // QTODO /* if !v.pref.is_live { @@ -80,7 +80,7 @@ fn (v &V) generate_hotcode_reloading_main_caller() { */ } -fn (v &V) generate_hot_reload_code() { +fn (v &Builder) generate_hot_reload_code() { /* QTODO mut cgen := v.cgen diff --git a/cmd/v/internal/compile/msvc.v b/vlib/v/builder/msvc.v similarity index 99% rename from cmd/v/internal/compile/msvc.v rename to vlib/v/builder/msvc.v index b882e3c818..bf0cd16395 100644 --- a/cmd/v/internal/compile/msvc.v +++ b/vlib/v/builder/msvc.v @@ -1,4 +1,4 @@ -module compile +module builder import os import v.pref @@ -182,7 +182,7 @@ fn find_msvc() ?MsvcResult { } } -pub fn (v mut V) cc_msvc() { +pub fn (v mut Builder) cc_msvc() { r := find_msvc()or{ // TODO: code reuse if !v.pref.is_keep_c && v.out_name_c != 'v.c' && v.out_name_c != 'v_macos.c' { @@ -281,7 +281,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.verbosity.is_higher_or_equal(.level_one) { + if v.pref.is_verbose { println('\n========== cl cmd line:') println(cmd) println('==========\n') diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 9b9c10740e..c1576285be 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1207,11 +1207,11 @@ fn (c mut Checker) warn_or_error(s string, pos token.Position, warn bool) { c.nr_errors++ } //if c.pref.is_verbose { - if c.pref.verbosity.is_higher_or_equal(.level_one) { + if c.pref.is_verbose { print_backtrace() } typ := if warn { 'warning' } else { 'error' } - kind := if c.pref.verbosity.is_higher_or_equal(.level_one) { + kind := if c.pref.is_verbose { 'checker $typ #$c.nr_errors:' } else { '$typ:' @@ -1228,7 +1228,7 @@ fn (c mut Checker) warn_or_error(s string, pos token.Position, warn bool) { if !warn { c.error_lines << pos.line_nr } - if c.pref.verbosity.is_higher_or_equal(.level_one) { + if c.pref.is_verbose { println('\n\n') } if c.nr_errors >= max_nr_errors { diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index e11b5902f8..92dbf4f07a 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -12,7 +12,7 @@ import ( const ( tabs = ['', '\t', '\t\t', '\t\t\t', '\t\t\t\t', '\t\t\t\t\t', '\t\t\t\t\t\t', '\t\t\t\t\t\t\t'] - max_len = 80 + max_len = 100 ) struct Fmt { diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 72b4ea60a9..62634bd272 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -508,7 +508,7 @@ fn (p mut Parser) range_expr(low ast.Expr) ast.Expr { pub fn (p &Parser) error(s string) { mut kind := 'error:' - if p.pref.verbosity.is_higher_or_equal(.level_one) { + if p.pref.is_verbose { print_backtrace() kind = 'parser error:' } diff --git a/vlib/v/pref/pref.v b/vlib/v/pref/pref.v index 60f1fe566a..a5ea5b43d7 100644 --- a/vlib/v/pref/pref.v +++ b/vlib/v/pref/pref.v @@ -19,19 +19,12 @@ pub enum 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 + //verbosity VerboseLevel is_verbose bool // nofmt bool // disable vfmt is_test bool // `v test string_test.v` @@ -108,7 +101,3 @@ pub fn backend_from_string(s string) ?Backend { } } -[inline] -pub fn (v VerboseLevel) is_higher_or_equal(other VerboseLevel) bool { - return int(v) >= int(other) -}