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

View File

@@ -5,7 +5,6 @@ module compiler
import (
os
os.cmdline
strings
filepath
v.pref
@@ -35,21 +34,15 @@ enum Pass {
main
}
struct V {
pub struct V {
pub mut:
os pref.OS // the OS to build for
out_name_c string // name of the temporary C file
files []string // all V files that need to be parsed and compiled
dir string // directory (or file) being compiled (TODO rename to path?)
compiled_dir string // contains os.realpath() of the dir of the final file beeing compiled, or the dir itself when doing `v .`
table &Table // table with types, vars, functions etc
cgen &CGen // C code generator
//x64 &x64.Gen
pref &pref.Preferences // all the preferences and settings extracted to a struct for reusability
lang_dir string // "~/code/v"
out_name string // "program.exe"
vroot string
mod string // module being built with -lib
parsers []Parser // file parsers
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
@@ -57,15 +50,33 @@ pub mut:
cached_mods []string
module_lookup_paths []string
// -d vfmt and -d another=0 for `$if vfmt { will execute }` and `$if another { will NOT get here }`
compile_defines []string // just ['vfmt']
compile_defines_all []string // contains both: ['vfmt','another']
v_fmt_all bool // << input set by tools/vfmt.v
v_fmt_file string // << file given by the user from tools/vfmt.v
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.realpath(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')
return &V{
compiled_dir: if os.is_dir(rdir) { rdir } else { filepath.dir(rdir) }
table: new_table(pref.obfuscate)
out_name_c: out_name_c
cgen: new_cgen(out_name_c)
//x64: x64.new_gen(out_name)
pref: pref
vgen_buf: vgen_buf
}
}
// Should be called by main at the end of the compilation process, to cleanup
pub fn (v &V) finalize_compilation() {
// TODO remove
@@ -166,7 +177,7 @@ pub fn (v mut V) compile() {
if v.pref.prealloc {
cgen.genln('#define VPREALLOC (1)')
}
if v.os == .js {
if v.pref.os == .js {
cgen.genln('#define _VJS (1) ')
}
v_hash := vhash()
@@ -189,11 +200,11 @@ pub fn (v mut V) compile() {
cgen.genln('#include <stdint.h>')
}
if v.compile_defines_all.len > 0 {
if v.pref.compile_defines_all.len > 0 {
cgen.genln('')
cgen.genln('// All custom defines : ' + v.compile_defines_all.join(','))
cgen.genln('// Turned ON custom defines: ' + v.compile_defines.join(','))
for cdefine in v.compile_defines {
cgen.genln('// All custom defines : ' + v.pref.compile_defines_all.join(','))
cgen.genln('// Turned ON custom defines: ' + v.pref.compile_defines.join(','))
for cdefine in v.pref.compile_defines {
cgen.genln('#define CUSTOM_DEFINE_${cdefine}')
}
cgen.genln('//')
@@ -234,7 +245,7 @@ pub fn (v mut V) compile() {
if '-debug_alloc' in os.args {
cgen.genln('#define DEBUG_ALLOC 1')
}
if v.pref.is_live && v.os != .windows {
if v.pref.is_live && v.pref.os != .windows {
cgen.includes << '#include <dlfcn.h>'
}
// cgen.genln('/*================================== FNS =================================*/')
@@ -261,7 +272,7 @@ pub fn (v mut V) compile() {
vgen_parser.parse(.main)
// Generate .vh if we are building a module
if v.pref.build_mode == .build_module {
generate_vh(v.dir)
generate_vh(v.pref.path)
}
// All definitions
mut def := strings.new_builder(10000) // Avoid unnecessary allocations
@@ -319,7 +330,7 @@ pub fn (v mut V) compile2() {
println(v.files)
}
mut b := v.new_v2()
b.build_c(v.files, v.out_name)
b.build_c(v.files, v.pref.out_name)
v.cc()
}
@@ -329,18 +340,18 @@ pub fn (v mut V) compile_x64() {
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(filepath.join(v.pref.vlib_path,'builtin','bare'))
v.files << v.dir
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.out_name)
b.build_x64(v.files, v.pref.out_name)
}
// make v2 from v1
fn (v &V) new_v2() builder.Builder {
mut b := builder.new_builder(v.pref)
b = { b|
os: v.os,
os: v.pref.os,
module_path: v_modules_path,
compiled_dir: v.compiled_dir,
module_search_paths: v.module_lookup_paths
@@ -356,7 +367,7 @@ fn (v mut V) generate_init() {
nogen := v.cgen.nogen
v.cgen.nogen = false
consts_init_body := v.cgen.consts_init.join_lines()
init_fn_name := mod_gen_name(v.mod) + '__init_consts'
init_fn_name := mod_gen_name(v.pref.mod) + '__init_consts'
v.cgen.genln('void ${init_fn_name}();\nvoid ${init_fn_name}() {\n$consts_init_body\n}')
v.cgen.nogen = nogen
}
@@ -482,7 +493,7 @@ pub fn (v mut V) generate_main() {
// Generate a C `main`, which calls every single test function
v.gen_main_start(false)
if v.pref.is_stats {
cgen.genln('BenchedTests bt = main__start_testing(${test_fn_names.len},tos3("$v.dir"));')
cgen.genln('BenchedTests bt = main__start_testing(${test_fn_names.len},tos3("$v.pref.path"));')
}
for tfname in test_fn_names {
if v.pref.is_stats {
@@ -513,7 +524,7 @@ pub fn (v mut V) generate_main() {
}
pub fn (v mut V) gen_main_start(add_os_args bool) {
if v.os == .windows {
if v.pref.os == .windows {
if 'glfw' in v.table.imports {
// GUI application
v.cgen.genln('int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, LPWSTR cmd_line, int show_cmd) { ')
@@ -531,7 +542,7 @@ pub fn (v mut V) gen_main_start(add_os_args bool) {
}
v.cgen.genln(' init();')
if add_os_args && 'os' in v.table.imports {
if v.os == .windows {
if v.pref.os == .windows {
v.cgen.genln(' os__args = os__init_os_args_wide(argc, argv);')
} else {
v.cgen.genln(' os__args = os__init_os_args(argc, (byteptr*)argv);')
@@ -547,45 +558,12 @@ pub fn (v mut V) gen_main_end(return_statement string) {
v.cgen.genln('}')
}
pub fn final_target_out_name(out_name string) string {
$if windows {
return out_name.replace('/', '\\') + '.exe'
}
return if out_name.starts_with('/') { out_name } else { './' + out_name }
}
pub fn (v V) run_compiled_executable_and_exit() {
args := env_vflags_and_os_args()
if v.pref.is_verbose {
println('============ running $v.out_name ============')
}
mut cmd := '"' + final_target_out_name(v.out_name).replace('.exe', '') + '"'
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)
}
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 v.v` instead of `v -o v compiler`')
println('use `v -o v cmd/v` instead of `v -o v compiler`')
}
verror("$dir doesn't exist")
}
@@ -606,27 +584,27 @@ pub fn (v &V) 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')) && v.os != .windows {
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.os != .linux {
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.os != .mac {
if (file.ends_with('_mac.v') || file.ends_with('_darwin.v')) && v.pref.os != .mac {
continue
}
if file.ends_with('_nix.v') && v.os == .windows {
if file.ends_with('_nix.v') && v.pref.os == .windows {
continue
}
if file.ends_with('_js.v') && v.os != .js {
if file.ends_with('_js.v') && v.pref.os != .js {
continue
}
if file.ends_with('_c.v') && v.os == .js {
if file.ends_with('_c.v') && v.pref.os == .js {
continue
}
if v.compile_defines_all.len > 0 && file.contains('_d_') {
if v.pref.compile_defines_all.len > 0 && file.contains('_d_') {
mut allowed := false
for cdefine in v.compile_defines {
for cdefine in v.pref.compile_defines {
file_postfix := '_d_${cdefine}.v'
if file.ends_with(file_postfix) {
allowed = true
@@ -740,15 +718,15 @@ pub fn (v &V) get_builtin_files() []string {
// get user files
pub fn (v &V) get_user_files() []string {
mut dir := v.dir
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 tools/preludes/README.md for more info about what preludes are
// See cmd/tools/preludes/README.md for more info about what preludes are
vroot := filepath.dir(vexe_path())
preludes_path := filepath.join(vroot,'tools','preludes')
preludes_path := filepath.join(vroot,'cmd','tools','preludes')
if v.pref.is_live {
user_files << filepath.join(preludes_path,'live_main.v')
}
@@ -858,284 +836,6 @@ pub fn (v &V) log(s string) {
println(s)
}
pub fn new_v(args []string) &V {
// Create modules dirs if they are missing
if !os.is_dir(v_modules_path) {
os.mkdir(v_modules_path)or{
panic(err)
}
os.mkdir('$v_modules_path${os.path_separator}cache')or{
panic(err)
}
}
// optional, custom modules search path
user_mod_path := cmdline.option(args, '-user_mod_path', '')
// Location of all vlib files
vroot := filepath.dir(vexe_path())
vlib_path := cmdline.option(args, '-vlib-path', filepath.join(vroot,'vlib'))
vpath := cmdline.option(args, '-vpath', v_modules_path)
mut vgen_buf := strings.new_builder(1000)
vgen_buf.writeln('module vgen\nimport strings')
target_os := cmdline.option(args, '-os', '')
mut out_name := cmdline.option(args, '-o', 'a.out')
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.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')
}
*/
}
is_test := dir.ends_with('_test.v')
is_script := dir.ends_with('.v') || dir.ends_with('.vsh')
if is_script && !os.exists(dir) {
println('`$dir` does not exist')
exit(1)
}
// No -o provided? foo.v => foo
if out_name == 'a.out' && dir.ends_with('.v') && dir != '.v' {
out_name = dir[..dir.len - 2]
// Building V? Use v2, since we can't overwrite a running
// executable on Windows + the precompiled V is more
// optimized.
if out_name == 'v' && os.is_dir('vlib/compiler') {
println('Saving the resulting V executable in `./v2`')
println('Use `v -o v v.v` if you want to replace current ' + 'V executable.')
out_name = 'v2'
}
}
// if we are in `/foo` and run `v .`, the executable should be `foo`
if dir == '.' && out_name == 'a.out' {
base := os.getwd().all_after(os.path_separator)
out_name = base.trim_space()
}
// `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)
}
}
}
mut _os := pref.OS.mac
// No OS specifed? Use current system
if target_os == '' {
$if linux {
_os = .linux
}
$if macos {
_os = .mac
}
$if windows {
_os = .windows
}
$if freebsd {
_os = .freebsd
}
$if openbsd {
_os = .openbsd
}
$if netbsd {
_os = .netbsd
}
$if dragonfly {
_os = .dragonfly
}
$if solaris {
_os = .solaris
}
$if haiku {
_os = .haiku
}
}
else {
_os = os_from_string(target_os)
}
// println('VROOT=$vroot')
// v.exe's parent directory should contain vlib
if !os.is_dir(vlib_path) || !os.is_dir(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=$vlib_path vexe_path=${vexe_path()}')
exit(1)
}
mut out_name_c := get_vtmp_filename(out_name, '.tmp.c')
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 {
verror('use -freestanding instead of -bare')
}
obfuscate := '-obf' in args
is_repl := '-repl' in args
pref := &pref.Preferences{
is_test: is_test
is_script: is_script
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: obfuscate
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: find_c_compiler()
building_v: !is_repl && (rdir_name == 'compiler' || rdir_name == 'v.v' || rdir_name == 'vfmt.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
}
if pref.is_verbose || pref.is_debug {
println('C compiler=$pref.ccompiler')
}
if pref.is_so {
out_name_c = get_vtmp_filename(out_name, '.tmp.so.c')
}
$if !linux {
if pref.is_bare && !out_name.ends_with('.c') {
verror('-freestanding only works on Linux for now')
}
}
return &V{
os: _os
out_name: out_name
dir: dir
compiled_dir: if os.is_dir(rdir) { rdir } else { filepath.dir(rdir) }
lang_dir: vroot
table: new_table(obfuscate)
out_name_c: out_name_c
cgen: new_cgen(out_name_c)
//x64: x64.new_gen(out_name)
vroot: vroot
pref: pref
mod: mod
vgen_buf: vgen_buf
compile_defines: compile_defines
compile_defines_all: compile_defines_all
}
}
fn non_empty(a []string) []string {
return a.filter(it.len != 0)
}
pub fn env_vflags_and_os_args() []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..]
}
}
else {
args << os.args
}
return non_empty(args)
}
pub 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.')
}
}
pub fn vexe_path() string {
vexe := os.getenv('VEXE')
if '' != vexe {
@@ -1223,12 +923,3 @@ pub fn set_vroot_folder(vroot_path string) {
vname := if os.user_os() == 'windows' { 'v.exe' } else { 'v' }
os.setenv('VEXE', os.realpath([vroot_path, vname].join(os.path_separator)), true)
}
pub fn new_v_compiler_with_args(args []string) &V {
vexe := vexe_path()
mut allargs := [vexe]
allargs << args
os.setenv('VOSARGS', allargs.join(' '), true)
return new_v(allargs)
}