1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

cmd/v: rewrite flags

This commit is contained in:
lutherwenxu
2020-03-07 01:53:29 +08:00
committed by GitHub
parent 522de0871a
commit aab31f4b35
37 changed files with 1087 additions and 464 deletions

View File

@@ -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')
}
}

View File

@@ -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' {

View File

@@ -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) {

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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`')
}
}

View File

@@ -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)

View File

@@ -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)
}
}
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}
}
}

View File

@@ -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 []
}

123
cmd/v/internal/flag/flag.v Normal file
View File

@@ -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
}
}

View File

@@ -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
}

View File

@@ -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 ...')
}

149
cmd/v/v.v
View File

@@ -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)
}