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

move v.v to cmd/v

This commit is contained in:
lutherwenxu
2020-02-09 17:08:04 +08:00
committed by GitHub
parent 3f5e4c55b2
commit 9332a83ce6
64 changed files with 1123 additions and 799 deletions

67
cmd/v/compile.v Normal file
View File

@@ -0,0 +1,67 @@
// 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 (
benchmark
os
os.cmdline
)
fn compile(command string, args []string) {
// Construct the V object from command line arguments
mut v := new_v(args)
if v.pref.is_verbose {
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
v.compile()
run_compiled_executable_and_exit(v, args)
}
mut tmark := benchmark.new_benchmark()
if v.pref.x64 {
v.compile_x64()
}
else if v.pref.v2 {
v.compile2()
}
else {
v.compile()
}
if v.pref.is_stats {
tmark.stop()
println('compilation took: ' + tmark.total_duration().str() + 'ms')
}
if v.pref.is_test {
run_compiled_executable_and_exit(v, args)
}
v.finalize_compilation()
}
pub fn run_compiled_executable_and_exit(v &compiler.V, args []string) {
if v.pref.is_verbose {
println('============ running $v.pref.out_name ============')
}
mut cmd := '"${v.pref.out_name}"'
args_after_no_options := cmdline.only_non_options( cmdline.after(args,['run','test']) )
if args_after_no_options.len > 1 {
cmd += ' ' + args_after_no_options[1..].join(' ')
}
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)
}

229
cmd/v/compile_options.v Normal file
View File

@@ -0,0 +1,229 @@
// 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${os.path_separator}cache')or{
panic(err)
}
}
vroot := filepath.dir(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_stdout()
exit(1)
}
mut out_name := cmdline.option(args, '-o', '')
mut dir := args.last()
if 'run' in args {
args_after_run := cmdline.only_non_options( cmdline.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(os.path_separator) {
dir = dir.all_before_last(os.path_separator)
}
if dir.starts_with('.$os.path_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' + os.path_separator) } else if dir.starts_with('.\\') || dir.starts_with('./') { dir[2..] } else if dir.starts_with(os.path_separator) { dir.all_after(os.path_separator) } else { dir }
mod = mod_path.replace(os.path_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(os.path_separator) {
d := out_name.all_before_last(os.path_separator)
if !os.is_dir(d) {
println('creating a new directory "$d"')
os.mkdir(d)or{
panic(err)
}
}
}
// println('VROOT=$vroot')
cflags := cmdline.many_values(args, '-cflags').join(' ')
defines := cmdline.many_values(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_stdout()
exit(1)
}
is_repl := '-repl' in args
ccompiler := cmdline.option(args, '-cc', '')
mut pref := &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.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 pref.is_verbose || pref.is_debug {
println('C compiler=$pref.ccompiler')
}
$if !linux {
if pref.is_bare && !out_name.ends_with('.c') {
println('V error: -freestanding only works on Linux for now')
os.flush_stdout()
exit(1)
}
}
pref.fill_with_defaults()
// v.exe's parent directory should contain vlib
if !os.is_dir(pref.vlib_path) || !os.is_dir(pref.vlib_path + os.path_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=$pref.vlib_path vexe_path=${vexe_path()}')
exit(1)
}
if pref.is_script && !os.exists(dir) {
println('`$dir` does not exist')
exit(1)
}
return compiler.new_v(pref)
}
fn find_c_compiler_thirdparty_options(args []string) string {
mut cflags := cmdline.many_values(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
}

78
cmd/v/flag.v Normal file
View File

@@ -0,0 +1,78 @@
// 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 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'
]
)
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(' '))
}
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 non_empty(args)
}
return non_empty(os.args)
}
fn vexe_path() string {
vexe := os.getenv('VEXE')
if vexe != '' {
return vexe
}
real_vexe_path := os.realpath(os.executable())
os.setenv('VEXE', real_vexe_path, true)
return real_vexe_path
}

101
cmd/v/help.v Normal file
View File

@@ -0,0 +1,101 @@
module main
const (
help_text = 'Usage: v [options/commands] [file.v | directory]
To run V in REPL mode, run V without any arguments.
To compile a directory/file, pass it as the only argument.
To run a directory/file, use `v run [file.v | directory]`. V will compile and run it for you.
This help message is only intended to be a quick start guide. For a comprehensive help message, use `v help --verbose`.'
verbose_help_text = 'Usage: v [options/commands] [file.v | directory]
When V is run without any arguments, it is run in REPL mode.
When given a .v file, it will be compiled. The executable will have the
same name as the input .v file: `v foo.v` produces `./foo` on *nix systems,
`foo.exe` on Windows.
You can use -o to specify a different output executable\'s name.
When given a directory, all .v files contained in it will be compiled as
part of a single main module.
By default the executable will have the same name as the directory.
To compile all V files in current directory, run `v .`
Any file ending in _test.v, will be treated as a test.
It will be compiled and run, evaluating the assert statements in every
function named test_xxx.
You can put common options inside an environment variable named VFLAGS, so that
you don\'t have to repeat them.
You can set it like this: `export VFLAGS="-cc clang -debug"` on *nix,
`set VFLAGS=-cc msvc` on Windows.
V respects the TMPDIR environment variable, and will put .tmp.c files in TMPDIR/v/ .
If you have not set it, a suitable platform specific folder (like /tmp) will be used.
Options/commands:
-h, help Display this information.
-o <file> Write output to <file>.
-o <file>.c Produce C source without compiling it.
-o <file>.js Produce JavaScript source.
-prod Build an optimized executable.
-v, version Display compiler version and git hash of the compiler source.
-verbose Produce a verbose log about what the compiler is doing, where it seeks for files and so on.
-live Enable hot code reloading (required by functions marked with [live]).
-os <OS> Produce an executable for the selected OS.
OS can be linux, mac, windows, msvc.
Use msvc if you want to use the MSVC compiler on Windows.
-shared Build a shared library.
-stats Show additional stats when compiling/running tests. Try `v -stats test .`
-cache Turn on usage of the precompiled module cache.
It very significantly speeds up secondary compilations.
-obf Obfuscate the resulting binary.
-compress Compress the resulting binary.
- Shorthand for `v repl`.
Options for debugging/troubleshooting v programs:
-g Generate debugging information in the backtraces. Add *V* line numbers to the generated executable.
-cg Same as -g, but add *C* line numbers to the generated executable instead of *V* line numbers.
-keep_c Do NOT remove the generated .tmp.c files after compilation.
It is useful when using debuggers like gdb/visual studio, when given after `-g` / `-cg`.
-pretty_c Run clang-format over the generated C file, so that it looks nicer. Requires you to have clang-format.
-show_c_cmd Print the full C compilation command and how much time it took. See also `-verbose`.
-cc <ccompiler> Specify which C compiler you want to use as a C backend.
The C backend compiler should be able to handle C99 compatible C code.
Common C compilers are gcc, clang, tcc, icc, cl...
-cflags <flags> Pass additional C flags to the C backend compiler.
Example: -cflags `sdl2-config --cflags`
Commands:
up Update V. Run `v up` at least once per day, since V development is rapid and features/bugfixes are added constantly.
run <file.v> Build and execute the V program in file.v. You can add arguments for the V program *after* the file name.
build <module> Compile a module into an object file.
repl Run the V REPL. If V is running in a tty terminal, the REPL is interactive, otherwise it just reads from stdin.
symlink Useful on Unix systems. Symlinks the current V executable to /usr/local/bin/v, so that V is globally available.
test-compiler Run all V test files, and compile all V examples.
test folder/ Run all V test files located in the folder and its subfolders. You can also pass individual _test.v files too.
fmt Run vfmt to format the source code. [wip]
doc Run vdoc over the source code and produce documentation.
translate Translates C to V. [wip, will be available in V 0.3]
create Create a new v project interactively. Answer the questions, and run it with `v run projectname`
V package management commands:
search keywords Search the https://vpm.vlang.io/ module repository for matching modules and shows their details.
install <module> Install a user module from https://vpm.vlang.io/.
update [module] Updates an already installed module, or ALL installed modules at once, when no module name is given.
remove [module] Removes an installed module, or ALL installed modules at once, when no module name is given.
'
)
/*
- To disable automatic formatting:
v -nofmt file.v
*/

70
cmd/v/simple_tool.v Normal file
View File

@@ -0,0 +1,70 @@
// 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
)
fn launch_tool(is_verbose bool, tname string, cmdname string) {
vexe := 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 := os.realpath('$vroot/cmd/tools/$tname')
tool_source := os.realpath('$vroot/cmd/tools/${tname}.v')
tool_command := '"$tool_exe" $tool_args'
if is_verbose {
eprintln('launch_tool vexe : $vroot')
eprintln('launch_tool vroot : $vroot')
eprintln('launch_tool tool_args : $tool_args')
eprintln('launch_tool tool_command: $tool_command')
}
mut should_compile := false
if !os.exists(tool_exe) {
should_compile = true
} else {
if os.file_last_mod_unix(tool_exe) <= os.file_last_mod_unix(vexe) {
// v was recompiled, maybe after v up ...
// rebuild the tool too just in case
should_compile = true
}
if os.file_last_mod_unix(tool_exe) <= os.file_last_mod_unix(tool_source) {
// the user changed the source code of the tool
should_compile = true
}
}
if is_verbose {
eprintln('launch_tool should_compile: $should_compile')
}
if should_compile {
if tname == 'vfmt' {
compilation_options << ['-d', 'vfmt']
}
compilation_args := compilation_options.join(' ')
compilation_command := '"$vexe" $compilation_args "$tool_source"'
if is_verbose {
eprintln('Compiling $tname 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 {
eprintln('launch_tool running tool command: $tool_command ...')
}
exit(os.system(tool_command))
}

31
cmd/v/symlink.v Normal file
View File

@@ -0,0 +1,31 @@
// 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 os
fn create_symlink() {
$if windows {
return
}
vexe := vexe_path()
mut link_path := '/usr/local/bin/v'
mut ret := os.exec('ln -sf $vexe $link_path') or { panic(err) }
if ret.exit_code == 0 {
println('Symlink "$link_path" has been created')
}
else if os.system('uname -o | grep -q \'[A/a]ndroid\'') == 0 {
println('Failed to create symlink "$link_path". Trying again with Termux path for Android.')
link_path = '/data/data/com.termux/files/usr/bin/v'
ret = os.exec('ln -sf $vexe $link_path') or { panic(err) }
if ret.exit_code == 0 {
println('Symlink "$link_path" has been created')
} else {
println('Failed to create symlink "$link_path". Try again with sudo.')
}
} else {
println('Failed to create symlink "$link_path". Try again with sudo.')
}
}

88
cmd/v/v.v Normal file
View File

@@ -0,0 +1,88 @@
// 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
os
)
const (
simple_cmd = [
'fmt',
'up',
'create',
'test', 'test-fmt', 'test-compiler',
'bin2v',
'repl',
'build-tools', 'build-examples', 'build-vbinaries'
]
)
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
}
if '-h' in option || '--help' in option || command == 'help' {
if is_verbose {
println(verbose_help_text)
} else {
println(help_text)
}
return
}
if is_verbose {
eprintln('v args: $arg')
eprintln('v command: $command')
eprintln('v options: $option')
}
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)
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 (January)')
}
'search', 'install', 'update', 'remove' {
launch_tool(is_verbose, 'vpm', command)
}
'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.')
exit(1)
}
}
}