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

merge and simplify cmd/v and v.builder

This commit is contained in:
Alexander Medvednikov
2020-04-07 00:44:19 +02:00
parent 8da12eb8a7
commit 8d150d427a
21 changed files with 393 additions and 1283 deletions

View File

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

View File

@ -1,608 +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
time
v.pref
v.util
term
)
fn todo() {
}
fn (v &V) no_cc_installed() bool {
$if windows {
os.exec('$v.pref.ccompiler -v')or{
if v.pref.verbosity.is_higher_or_equal(.level_one) {
println('C compiler not found, trying to build with msvc...')
}
return true
}
}
return false
}
fn (v mut V) cc() {
if os.executable().contains('vfmt') {
return
}
v.build_thirdparty_obj_files()
vexe := pref.vexe_path()
vdir := os.dir(vexe)
// Just create a C/JavaScript file and exit
// for example: `v -o v.c compiler`
ends_with_c := v.pref.out_name.ends_with('.c')
ends_with_js := v.pref.out_name.ends_with('.js')
if ends_with_c || ends_with_js {
// Translating V code to JS by launching vjs.
// Using a separate process for V.js is for performance mostly,
// to avoid constant is_js checks.
$if !js {
if ends_with_js {
vjs_path := vexe + 'js'
if !os.exists(vjs_path) {
println('V.js compiler not found, building...')
// Build V.js. Specifying `-os js` makes V include
// only _js.v files and ignore _c.v files.
ret := os.system('$vexe -o $vjs_path -os js $vdir/cmd/v')
if ret == 0 {
println('Done.')
}
else {
println('Failed.')
exit(1)
}
}
ret := os.system('$vjs_path -o $v.pref.out_name $v.pref.path')
if ret == 0 {
println('Done. Run it with `node $v.pref.out_name`')
println('JS backend is at a very early stage.')
}
}
}
// v.out_name_c may be on a different partition than v.out_name
os.mv_by_cp(v.out_name_c, v.pref.out_name)or{
panic(err)
}
exit(0)
}
// Cross compiling for Windows
if v.pref.os == .windows {
$if !windows {
v.cc_windows_cross()
return
}
}
$if windows {
if v.pref.ccompiler == 'msvc' || v.no_cc_installed() {
v.cc_msvc()
return
}
}
// arguments for the C compiler
mut a := [v.pref.cflags, '-std=gnu11', '-Wall', '-Wextra',
// TODO : activate -Werror once no warnings remain
// '-Werror',
// TODO : try and remove the below workaround options when the corresponding
// warnings are totally fixed/removed
'-Wno-unused-variable',
// '-Wno-unused-but-set-variable',
'-Wno-unused-parameter', '-Wno-unused-result', '-Wno-unused-function', '-Wno-missing-braces', '-Wno-unused-label']
// TCC on Linux by default, unless -cc was provided
// TODO if -cc = cc, TCC is still used, default compiler should be
// used instead.
if v.pref.fast {
$if linux {
$if !android {
tcc_3rd := '$vdir/thirdparty/tcc/bin/tcc'
// println('tcc third "$tcc_3rd"')
tcc_path := '/var/tmp/tcc/bin/tcc'
if os.exists(tcc_3rd) && !os.exists(tcc_path) {
// println('moving tcc')
// if there's tcc in thirdparty/, that means this is
// a prebuilt V_linux.zip.
// Until the libtcc1.a bug is fixed, we neeed to move
// it to /var/tmp/
os.system('mv $vdir/thirdparty/tcc /var/tmp/')
}
if v.pref.ccompiler == 'cc' && os.exists(tcc_path) {
// TODO tcc bug, needs an empty libtcc1.a fila
// os.mkdir('/var/tmp/tcc/lib/tcc/') or { panic(err) }
// os.create('/var/tmp/tcc/lib/tcc/libtcc1.a')
v.pref.ccompiler = tcc_path
a << '-m64'
}
}
} $else {
verror('-fast is only supported on Linux right now')
}
}
if !v.pref.is_so
&& v.pref.build_mode != .build_module
&& os.user_os() == 'windows'
&& !v.pref.out_name.ends_with('.exe')
{
v.pref.out_name += '.exe'
}
// linux_host := os.user_os() == 'linux'
v.log('cc() isprod=$v.pref.is_prod outname=$v.pref.out_name')
if v.pref.is_so {
a << '-shared -fPIC ' // -Wl,-z,defs'
v.pref.out_name += '.so'
}
if v.pref.is_bare {
a << '-fno-stack-protector -static -ffreestanding -nostdlib'
}
if v.pref.build_mode == .build_module {
// Create the modules & out directory if it's not there.
mut out_dir := if v.pref.path.starts_with('vlib') { '${pref.default_module_path}${os.path_separator}cache${os.path_separator}$v.pref.path' } else { '${pref.default_module_path}${os.path_separator}$v.pref.path' }
pdir := out_dir.all_before_last(os.path_separator)
if !os.is_dir(pdir) {
os.mkdir_all(pdir)
}
v.pref.out_name = '${out_dir}.o' // v.out_name
println('Building ${v.pref.out_name}...')
}
debug_mode := v.pref.is_debug
mut debug_options := '-g'
mut optimization_options := '-O2'
mut guessed_compiler := v.pref.ccompiler
if guessed_compiler == 'cc' && v.pref.is_prod {
// deliberately guessing only for -prod builds for performance reasons
if ccversion:=os.exec('cc --version'){
if ccversion.exit_code == 0 {
if ccversion.output.contains('This is free software;') && ccversion.output.contains('Free Software Foundation, Inc.') {
guessed_compiler = 'gcc'
}
if ccversion.output.contains('clang version ') {
guessed_compiler = 'clang'
}
}
}
}
if v.pref.ccompiler.contains('clang') || guessed_compiler == 'clang' {
if debug_mode {
debug_options = '-g -O0 -no-pie'
}
optimization_options = '-O3'
mut have_flto := true
$if openbsd {
have_flto = false
}
if have_flto {
optimization_options += ' -flto'
}
}
if v.pref.ccompiler.contains('gcc') || guessed_compiler == 'gcc' {
if debug_mode {
debug_options = '-g3 -no-pie'
}
optimization_options = '-O3 -fno-strict-aliasing -flto'
}
if debug_mode {
a << debug_options
$if macos {
a << ' -ferror-limit=5000 '
}
}
if v.pref.is_prod {
a << optimization_options
}
if debug_mode && os.user_os() != 'windows' {
a << ' -rdynamic ' // needed for nicer symbolic backtraces
}
if v.pref.ccompiler != 'msvc' && v.pref.os != .freebsd {
a << '-Werror=implicit-function-declaration'
}
for f in v.generate_hotcode_reloading_compiler_flags() {
a << f
}
mut libs := '' // builtin.o os.o http.o etc
if v.pref.build_mode == .build_module {
a << '-c'
}
else if v.pref.is_cache {
/*
QTODO
builtin_o_path := os.join_path(pref.default_module_path, 'cache', 'vlib', 'builtin.o')
a << builtin_o_path.replace('builtin.o', 'strconv.o') // TODO hack no idea why this is needed
if os.exists(builtin_o_path) {
libs = builtin_o_path
}
else {
println('$builtin_o_path not found... building module builtin')
os.system('$vexe build module vlib${os.path_separator}builtin')
}
for imp in v.table.imports {
if imp.contains('vweb') {
continue
} // not working
if imp == 'webview' {
continue
}
imp_path := imp.replace('.', os.path_separator)
path := '${pref.default_module_path}${os.path_separator}cache${os.path_separator}vlib${os.path_separator}${imp_path}.o'
// println('adding ${imp_path}.o')
if os.exists(path) {
libs += ' ' + path
}
else {
println('$path not found... building module $imp')
if path.ends_with('vlib/ui.o') {
println('copying ui...')
os.cp('$vdir/thirdparty/ui/ui.o', path)or{
panic('error copying ui files')
}
os.cp('$vdir/thirdparty/ui/ui.vh', pref.default_module_path + '/vlib/ui.vh')or{
panic('error copying ui files')
}
}
else {
os.system('$vexe build module vlib${os.path_separator}$imp_path')
}
}
if path.ends_with('vlib/ui.o') {
a << '-framework Cocoa -framework Carbon'
}
}
*/
}
if v.pref.sanitize {
a << '-fsanitize=leak'
}
// Cross compiling linux TODO
/*
sysroot := '/tmp/lld/linuxroot/'
if v.os == .linux && !linux_host {
// Build file.o
a << '-c --sysroot=$sysroot -target x86_64-linux-gnu'
// Right now `out_name` can be `file`, not `file.o`
if !v.out_name.ends_with('.o') {
v.out_name = v.out_name + '.o'
}
}
*/
// Cross compiling windows
//
// Output executable name
a << '-o "$v.pref.out_name"'
if os.is_dir(v.pref.out_name) {
verror("\'$v.pref.out_name\' is a directory")
}
// macOS code can include objective C TODO remove once objective C is replaced with C
if v.pref.os == .mac {
a << '-x objective-c'
}
// The C file we are compiling
a << '"$v.out_name_c"'
if v.pref.os == .mac {
a << '-x none'
}
// Min macos version is mandatory I think?
if v.pref.os == .mac {
a << '-mmacosx-version-min=10.7'
}
if v.pref.os == .windows {
a << '-municode'
}
cflags := v.get_os_cflags()
// add .o files
a << cflags.c_options_only_object_files()
// add all flags (-I -l -L etc) not .o files
a << cflags.c_options_without_object_files()
a << libs
// Without these libs compilation will fail on Linux
// || os.user_os() == 'linux'
if !v.pref.is_bare && v.pref.build_mode != .build_module && v.pref.os in [.linux, .freebsd, .openbsd, .netbsd, .dragonfly, .solaris, .haiku] {
a << '-lm -lpthread '
// -ldl is a Linux only thing. BSDs have it in libc.
if v.pref.os == .linux {
a << ' -ldl '
}
if v.pref.os == .freebsd {
// FreeBSD: backtrace needs execinfo library while linking
a << ' -lexecinfo '
}
}
if !v.pref.is_bare && v.pref.os == .js && os.user_os() == 'linux' {
a << '-lm'
}
args := a.join(' ')
start:
todo()
// TODO remove
cmd := '${v.pref.ccompiler} $args'
// Run
if v.pref.verbosity.is_higher_or_equal(.level_one) {
println('\n==========')
println(cmd)
}
ticks := time.ticks()
res := os.exec(cmd)or{
// C compilation failed.
// If we are on Windows, try msvc
println('C compilation failed.')
/*
if os.user_os() == 'windows' && v.pref.ccompiler != 'msvc' {
println('Trying to build with MSVC')
v.cc_msvc()
return
}
*/
verror(err)
return
}
if res.exit_code != 0 {
// the command could not be found by the system
if res.exit_code == 127 {
$if linux {
// TCC problems on linux? Try GCC.
if v.pref.ccompiler.contains('tcc') {
v.pref.ccompiler = 'cc'
goto start
}
}
verror('C compiler error, while attempting to run: \n' + '-----------------------------------------------------------\n' + '$cmd\n' + '-----------------------------------------------------------\n' + 'Probably your C compiler is missing. \n' + 'Please reinstall it, or make it available in your PATH.\n\n' + missing_compiler_info())
}
if v.pref.is_debug {
eword := 'error:'
khighlight := if term.can_show_color_on_stdout() { term.red(eword) } else { eword }
println(res.output.replace(eword, khighlight))
verror("
==================
C error. This should never happen.
V compiler version: ${util.full_v_version()}
Host OS: ${pref.get_host_os().str()}
Target OS: $v.pref.os.str()
If you were not working with C interop and are not sure about what's happening,
please put the whole output in a pastebin and contact us through the following ways with a link to the pastebin:
- Raise an issue on GitHub: https://github.com/vlang/v/issues/new/choose
- Ask a question in #help on Discord: https://discord.gg/vlang")
}
else {
if res.output.len < 30 {
println(res.output)
} else {
elines := error_context_lines( res.output, 'error:', 1, 12 )
println('==================')
for eline in elines {
println(eline)
}
println('...')
println('==================')
println('(Use `v -cg` to print the entire error message)\n')
}
verror("C error.
Please make sure that:
- You have all V dependencies installed.
- You did not declare a C function that was not included. (Try commenting your code that involves C interop)
- You are running the latest version of V. (Try running `v up` and rerunning your command)
If you're confident that all of the above is true, please try running V with the `-cg` option which enables more debugging capabilities.\n")
}
}
diff := time.ticks() - ticks
// Print the C command
if v.pref.verbosity.is_higher_or_equal(.level_one) {
println('${v.pref.ccompiler} took $diff ms')
println('=========\n')
}
// Link it if we are cross compiling and need an executable
/*
if v.os == .linux && !linux_host && v.pref.build_mode != .build {
v.out_name = v.out_name.replace('.o', '')
obj_file := v.out_name + '.o'
println('linux obj_file=$obj_file out_name=$v.out_name')
ress := os.exec('/usr/local/Cellar/llvm/8.0.0/bin/ld.lld --sysroot=$sysroot ' +
'-v -o $v.out_name ' +
'-m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 ' +
'/usr/lib/x86_64-linux-gnu/crt1.o ' +
'$sysroot/lib/x86_64-linux-gnu/libm-2.28.a ' +
'/usr/lib/x86_64-linux-gnu/crti.o ' +
obj_file +
' /usr/lib/x86_64-linux-gnu/libc.so ' +
'/usr/lib/x86_64-linux-gnu/crtn.o') or {
verror(err)
return
}
println(ress.output)
println('linux cross compilation done. resulting binary: "$v.out_name"')
}
*/
if !v.pref.is_keep_c && v.out_name_c != 'v.c' {
os.rm(v.out_name_c)
}
if v.pref.compress {
$if windows {
println('-compress does not work on Windows for now')
return
}
ret := os.system('strip $v.pref.out_name')
if ret != 0 {
println('strip failed')
return
}
// NB: upx --lzma can sometimes fail with NotCompressibleException
// See https://github.com/vlang/v/pull/3528
mut ret2 := os.system('upx --lzma -qqq $v.pref.out_name')
if ret2 != 0 {
ret2 = os.system('upx -qqq $v.pref.out_name')
}
if ret2 != 0 {
println('upx failed')
$if macos {
println('install upx with `brew install upx`')
}
$if linux {
println('install upx\n' + 'for example, on Debian/Ubuntu run `sudo apt install upx`')
}
$if windows {
// :)
}
}
}
}
fn (c mut V) cc_windows_cross() {
/*
QTODO
println('Cross compiling for Windows...')
if !c.pref.out_name.ends_with('.exe') {
c.pref.out_name += '.exe'
}
mut args := '-o $c.pref.out_name -w -L. '
cflags := c.get_os_cflags()
// -I flags
args += if c.pref.ccompiler == 'msvc' { cflags.c_options_before_target_msvc() } else { cflags.c_options_before_target() }
mut libs := ''
if false && c.pref.build_mode == .default_mode {
libs = '"${pref.default_module_path}/vlib/builtin.o"'
if !os.exists(libs) {
println('`$libs` not found')
exit(1)
}
for imp in c.table.imports {
libs += ' "${pref.default_module_path}/vlib/${imp}.o"'
}
}
args += ' $c.out_name_c '
args += if c.pref.ccompiler == 'msvc' { cflags.c_options_after_target_msvc() } else { cflags.c_options_after_target() }
/*
winroot := '${pref.default_module_path}/winroot'
if !os.is_dir(winroot) {
winroot_url := 'https://github.com/vlang/v/releases/download/v0.1.10/winroot.zip'
println('"$winroot" not found.')
println('Download it from $winroot_url and save it in ${pref.default_module_path}')
println('Unzip it afterwards.\n')
println('winroot.zip contains all library and header files needed ' + 'to cross-compile for Windows.')
exit(1)
}
mut obj_name := c.out_name
obj_name = obj_name.replace('.exe', '')
obj_name = obj_name.replace('.o.o', '.o')
include := '-I $winroot/include '
*/
mut cmd := ''
cmd = ''
$if macos {
cmd = 'x86_64-w64-mingw32-gcc -std=gnu11 $args -municode'
}
$else {
panic('your platform is not supported yet')
}
println(cmd)
//cmd := 'clang -o $obj_name -w $include -m32 -c -target x86_64-win32 ${pref.default_module_path}/$c.out_name_c'
if c.pref.verbosity.is_higher_or_equal(.level_one) {
println(cmd)
}
if os.system(cmd) != 0 {
println('Cross compilation for Windows failed. Make sure you have clang installed.')
exit(1)
}
/*
if c.pref.build_mode != .build_module {
link_cmd := 'lld-link $obj_name $winroot/lib/libcmt.lib ' + '$winroot/lib/libucrt.lib $winroot/lib/kernel32.lib $winroot/lib/libvcruntime.lib ' + '$winroot/lib/uuid.lib'
if c.pref.show_c_cmd {
println(link_cmd)
}
if os.system(link_cmd) != 0 {
println('Cross compilation for Windows failed. Make sure you have lld linker installed.')
exit(1)
}
// os.rm(obj_name)
}
*/
println('Done!')
*/
}
fn (c &V) 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)
if c.pref.ccompiler == 'msvc' || c.no_cc_installed() {
build_thirdparty_obj_file_with_msvc(flag.value, rest_of_module_flags)
}
else {
c.build_thirdparty_obj_file(flag.value, rest_of_module_flags)
}
}
}
}
fn (v &V) build_thirdparty_obj_file(path string, moduleflags []CFlag) {
obj_path := os.real_path(path)
if os.exists(obj_path) {
return
}
println('$obj_path not found, building it...')
parent := os.dir(obj_path)
files := os.ls(parent)or{
panic(err)
}
mut cfiles := ''
for file in files {
if file.ends_with('.c') {
cfiles += '"' + os.real_path(parent + os.path_separator + file) + '" '
}
}
btarget := moduleflags.c_options_before_target()
atarget := moduleflags.c_options_after_target()
cmd := '$v.pref.ccompiler $v.pref.third_party_option $btarget -c -o "$obj_path" $cfiles $atarget '
res := os.exec(cmd)or{
println('failed thirdparty object build cmd: $cmd')
verror(err)
return
}
if res.exit_code != 0 {
println('failed thirdparty object build cmd: $cmd')
verror(res.output)
return
}
println(res.output)
}
fn missing_compiler_info() string {
$if windows {
return 'https://github.com/vlang/v/wiki/Installing-a-C-compiler-on-Windows'
}
$if linux {
return 'On Debian/Ubuntu, run `sudo apt install build-essential`'
}
$if macos {
return 'Install command line XCode tools with `xcode-select --install`'
}
return ''
}
fn error_context_lines(text, keyword string, before, after int) []string {
khighlight := if term.can_show_color_on_stdout() { term.red(keyword) } else { keyword }
mut eline_idx := 0
mut lines := text.split_into_lines()
for idx, eline in lines {
if eline.contains(keyword) {
lines[idx] = lines[idx].replace(keyword, khighlight)
if eline_idx == 0 {
eline_idx = idx
}
}
}
idx_s := if eline_idx - before >= 0 { eline_idx - before } else { 0 }
idx_e := if idx_s + after < lines.len { idx_s + after } else { lines.len }
return lines[idx_s..idx_e]
}

View File

@ -1,207 +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
// parsed cflag
struct CFlag {
mod string // the module in which the flag was given
os string // eg. windows | darwin | linux
name string // eg. -I
value string // eg. /path/to/include
}
pub fn (c &CFlag) str() string {
return 'CFlag{ name: "$c.name" value: "$c.value" mod: "$c.mod" os: "$c.os" }'
}
// get flags for current os
fn (v &V) get_os_cflags() []CFlag {
mut flags := []CFlag
mut ctimedefines := []string
if v.pref.compile_defines.len > 0 {
ctimedefines << v.pref.compile_defines
}
// QTODO
/*
for flag in v.table.cflags {
if flag.os == '' || (flag.os == 'linux' && v.pref.os == .linux) || (flag.os == 'darwin' && v.pref.os == .mac) || (flag.os == 'freebsd' && v.pref.os == .freebsd) || (flag.os == 'windows' && v.pref.os == .windows) || (flag.os == 'mingw' && v.pref.os == .windows && v.pref.ccompiler != 'msvc') || (flag.os == 'solaris' && v.pref.os == .solaris) {
flags << flag
}
if flag.os in ctimedefines {
flags << flag
}
}
*/
return flags
}
fn (v &V) get_rest_of_module_cflags(c &CFlag) []CFlag {
mut flags := []CFlag
cflags := v.get_os_cflags()
for flag in cflags {
if c.mod == flag.mod {
if c.name == flag.name && c.value == flag.value && c.os == flag.os {
continue
}
flags << flag
}
}
return flags
}
// format flag
fn (cf &CFlag) format() string {
mut value := cf.value
if cf.name in ['-l', '-Wa', '-Wl', '-Wp'] && value.len > 0 {
return '${cf.name}${value}'.trim_space()
}
// convert to absolute path
if cf.name == '-I' || cf.name == '-L' || value.ends_with('.o') {
value = '"' + os.real_path(value) + '"'
}
return '$cf.name $value'.trim_space()
}
// check if cflag is in table
/*
QTODO
fn (table &Table) has_cflag(cflag CFlag) bool {
for cf in table.cflags {
if cf.os == cflag.os && cf.name == cflag.name && cf.value == cflag.value {
return true
}
}
return false
}
// parse the flags to (table.cflags) []CFlag
// Note: clean up big time (joe-c)
fn (table mut Table) parse_cflag(cflag string, mod string, ctimedefines []string) ?bool {
allowed_flags := ['framework', 'library', 'Wa', 'Wl', 'Wp', 'I', 'l', 'L', ]
flag_orig := cflag.trim_space()
mut flag := flag_orig
if flag == '' {
return true
}
mut fos := ''
mut allowed_os_overrides := ['linux', 'darwin', 'freebsd', 'windows', 'mingw', 'solaris']
allowed_os_overrides << ctimedefines
for os_override in allowed_os_overrides {
if !flag.starts_with( os_override ) { continue }
pos := flag.index(' ') or {
return none
}
fos = flag[..pos].trim_space()
flag = flag[pos..].trim_space()
}
for {
mut name := ''
mut value := ''
if flag[0] == `-` {
for f in allowed_flags {
i := 1 + f.len
if i <= flag.len && f == flag[1..i] {
name = flag[..i].trim_space()
flag = flag[i..].trim_space()
break
}
}
}
mut index := flag.index(' -') or {
-1
}
for index > -1 {
mut has_next := false
for f in allowed_flags {
i := index + 2 + f.len
if i <= flag.len && f == flag[index + 2..i] {
value = flag[..index + 1].trim_space()
flag = flag[index + 1..].trim_space()
has_next = true
break
}
}
if has_next {
break
}
index = flag.index_after(' -', index + 1)
}
if index == -1 {
value = flag.trim_space()
}
if (name in ['-I', '-l', '-L']) && value == '' {
hint := if name == '-l' { 'library name' } else { 'path' }
return error('bad #flag `$flag_orig`: missing $hint after `$name`')
}
cf := CFlag{
mod: mod
os: fos
name: name
value: value
}
if !table.has_cflag(cf) {
table.cflags << cf
}
if index == -1 {
break
}
}
return true
}
*/
// TODO: implement msvc specific c_options_before_target and c_options_after_target ...
fn (cflags []CFlag) c_options_before_target_msvc() string {
return ''
}
fn (cflags []CFlag) c_options_after_target_msvc() string {
return ''
}
fn (cflags []CFlag) c_options_before_target() string {
// -I flags, optimization flags and so on
mut args := []string
for flag in cflags {
if flag.name != '-l' {
args << flag.format()
}
}
return args.join(' ')
}
fn (cflags []CFlag) c_options_after_target() string {
// -l flags (libs)
mut args := []string
for flag in cflags {
if flag.name == '-l' {
args << flag.format()
}
}
return args.join(' ')
}
fn (cflags []CFlag) c_options_without_object_files() string {
mut args := []string
for flag in cflags {
if flag.value.ends_with('.o') || flag.value.ends_with('.obj') {
continue
}
args << flag.format()
}
return args.join(' ')
}
fn (cflags []CFlag) c_options_only_object_files() string {
mut args := []string
for flag in cflags {
if flag.value.ends_with('.o') || flag.value.ends_with('.obj') {
args << flag.format()
}
}
return args.join(' ')
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,258 +0,0 @@
module compile
import (
os
time
)
fn (v &V) 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
if v.pref.os == .linux || os.user_os() == 'linux' {
a << '-rdynamic'
}
if v.pref.os == .mac || os.user_os() == 'mac' {
a << '-flat_namespace'
}
}
return a
}
fn (v &V) generate_hotcode_reloading_declarations() {
/*
QTODO
mut cgen := v.cgen
if v.pref.os != .windows {
if v.pref.is_so {
cgen.genln('pthread_mutex_t live_fn_mutex;')
}
if v.pref.is_live {
cgen.genln('pthread_mutex_t live_fn_mutex = PTHREAD_MUTEX_INITIALIZER;')
}
}
else {
if v.pref.is_so {
cgen.genln('HANDLE live_fn_mutex;')
cgen.genln('
void pthread_mutex_lock(HANDLE *m) {
WaitForSingleObject(*m, INFINITE);
}
void pthread_mutex_unlock(HANDLE *m) {
ReleaseMutex(*m);
}')
}
if v.pref.is_live {
cgen.genln('HANDLE live_fn_mutex = 0;')
}
}
*/
}
fn (v &V) generate_hotcode_reloading_main_caller() {
// QTODO
/*
if !v.pref.is_live {
return
}
// We are in live code reload mode, so start the .so loader in the background
mut cgen := v.cgen
cgen.genln('')
file_base := os.file_name(v.pref.path).replace('.v', '')
if v.pref.os != .windows {
// unix:
so_name := file_base + '.so'
cgen.genln(' char *live_library_name = "$so_name";')
cgen.genln(' load_so(live_library_name);')
cgen.genln(' pthread_t _thread_so;')
cgen.genln(' pthread_create(&_thread_so , NULL, (void *)&reload_so, live_library_name);')
}
else {
// windows:
so_name := file_base + if v.pref.ccompiler == 'msvc' { '.dll' } else { '.so' }
cgen.genln(' char *live_library_name = "$so_name";')
cgen.genln(' live_fn_mutex = CreateMutexA(0, 0, 0);')
cgen.genln(' load_so(live_library_name);')
cgen.genln(' unsigned long _thread_so;')
cgen.genln(' _thread_so = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&reload_so, 0, 0, 0);')
}
*/
}
fn (v &V) generate_hot_reload_code() {
/*
QTODO
mut cgen := v.cgen
// Hot code reloading
if v.pref.is_live {
mut file := os.real_path(v.pref.path)
file_base := os.file_name(file).replace('.v', '')
so_name := file_base + '.so'
// Need to build .so file before building the live application
// The live app needs to load this .so file on initialization.
mut vexe := os.args[0]
if os.user_os() == 'windows' {
vexe = cescaped_path(vexe)
file = cescaped_path(file)
}
mut msvc := ''
if v.pref.ccompiler == 'msvc' {
msvc = '-cc msvc'
}
so_debug_flag := if v.pref.is_debug { '-g' } else { '' }
cmd_compile_shared_library := '$vexe $msvc $so_debug_flag -o $file_base -solive -shared $file'
if v.pref.verbosity.is_higher_or_equal(.level_one) {
println(cmd_compile_shared_library)
}
ticks := time.ticks()
so_compilation_result := os.system(cmd_compile_shared_library)
if v.pref.verbosity.is_higher_or_equal(.level_two) {
diff := time.ticks() - ticks
println('compiling shared library took $diff ms')
println('=========\n')
}
if so_compilation_result != 0 {
exit(1)
}
cgen.genln('
void lfnmutex_print(char *s){
#if 0
fflush(stderr);
fprintf(stderr,">> live_fn_mutex: %p | %s\\n", &live_fn_mutex, s);
fflush(stderr);
#endif
}
')
if v.pref.os != .windows {
cgen.genln('
#define _POSIX_C_SOURCE 1
#include <limits.h>
#ifndef PATH_MAX
#define PATH_MAX 1024
#endif
void* live_lib = 0;
int load_so(byteptr path) {
char cpath[PATH_MAX] = {0};
int res = snprintf(cpath, sizeof (cpath), "./%s", path);
if (res >= sizeof (cpath)) {
fprintf (stderr, "path is too long");
return 0;
}
//printf("load_so %s\\n", cpath);
if (live_lib) dlclose(live_lib);
live_lib = dlopen(cpath, RTLD_LAZY);
if (!live_lib) {
fprintf(stderr, "open failed");
exit(1);
return 0;
}
')
for so_fn in cgen.so_fns {
cgen.genln('$so_fn = dlsym(live_lib, "$so_fn"); ')
}
}
else {
cgen.genln('
#ifndef PATH_MAX
#define PATH_MAX 1024
#endif
void pthread_mutex_lock(HANDLE *m) {
WaitForSingleObject(*m, INFINITE);
}
void pthread_mutex_unlock(HANDLE *m) {
ReleaseMutex(*m);
}
void* live_lib = NULL;
int load_so(byteptr path) {
char cpath[PATH_MAX];
int res = snprintf(cpath, sizeof (cpath), "./%s", path);
if (res >= sizeof(cpath)) {
puts("path is too long\\n");
exit(1);
return 0;
}
if (live_lib) FreeLibrary(live_lib);
live_lib = LoadLibraryA(cpath);
if (!live_lib) {
puts("open failed\\n");
exit(1);
return 0;
}
')
for so_fn in cgen.so_fns {
cgen.genln('$so_fn = (void *)GetProcAddress(live_lib, "$so_fn"); ')
}
}
cgen.genln('return 1;
}
int _live_reloads = 0;
void reload_so() {
char new_so_base[PATH_MAX] = {0};
char new_so_name[PATH_MAX] = {0};
char compile_cmd[PATH_MAX] = {0};
int last = os__file_last_mod_unix(tos2("$file"));
while (1) {
// TODO use inotify
int now = os__file_last_mod_unix(tos2("$file"));
if (now != last) {
last = now;
_live_reloads++;
//v -o bounce -shared bounce.v
snprintf(new_so_base, sizeof (new_so_base), ".tmp.%d.${file_base}", _live_reloads);
#ifdef _WIN32
// We have to make this directory becuase windows WILL NOT
// do it for us
os__mkdir(string_all_before_last(tos2(new_so_base), tos2("/")));
#endif
#ifdef _MSC_VER
snprintf(new_so_name, sizeof (new_so_name), "%s.dll", new_so_base);
#else
snprintf(new_so_name, sizeof (new_so_name), "%s.so", new_so_base);
#endif
snprintf(compile_cmd, sizeof (compile_cmd), "$vexe $msvc -o %s -solive -shared $file", new_so_base);
os__system(tos2(compile_cmd));
if( !os__exists(tos2(new_so_name)) ) {
puts("Errors while compiling $file\\n");
continue;
}
lfnmutex_print("reload_so locking...");
pthread_mutex_lock(&live_fn_mutex);
lfnmutex_print("reload_so locked");
load_so(new_so_name);
#ifndef _WIN32
unlink(new_so_name); // removing the .so file from the filesystem after dlopen-ing it is safe, since it will still be mapped in memory.
#else
_unlink(new_so_name);
#endif
//if(0 == rename(new_so_name, "${so_name}")){
// load_so("${so_name}");
//}
lfnmutex_print("reload_so unlocking...");
pthread_mutex_unlock(&live_fn_mutex);
lfnmutex_print("reload_so unlocked");
}
time__sleep_ms(100);
}
}
')
}
if v.pref.is_so {
cgen.genln(' int load_so(byteptr path) { return 0; }')
}
*/
}

View File

@ -1,408 +0,0 @@
module compile
import os
import v.pref
#flag windows -l shell32
#flag windows -l dbghelp
#flag windows -l advapi32
struct MsvcResult {
full_cl_exe_path string
exe_path string
um_lib_path string
ucrt_lib_path string
vs_lib_path string
um_include_path string
ucrt_include_path string
vs_include_path string
shared_include_path string
}
// shell32 for RegOpenKeyExW etc
// Mimics a HKEY
type RegKey voidptr
// Taken from the windows SDK
const (
HKEY_LOCAL_MACHINE = RegKey(0x80000002)// as RegKey
KEY_QUERY_VALUE = (0x0001)
KEY_WOW64_32KEY = (0x0200)
KEY_ENUMERATE_SUB_KEYS = (0x0008)
)
// Given a root key look for one of the subkeys in 'versions' and get the path
fn find_windows_kit_internal(key RegKey, versions []string) ?string {
$if windows {
unsafe {
for version in versions {
required_bytes := 0 // TODO mut
result := C.RegQueryValueEx(key, version.to_wide(), 0, 0, 0, &required_bytes)
length := required_bytes / 2
if result != 0 {
continue
}
alloc_length := (required_bytes + 2)
mut value := &u16(malloc(alloc_length))
if isnil(value) {
continue
}
result2 := C.RegQueryValueEx(key, version.to_wide(), 0, 0, value, &alloc_length)
if result2 != 0 {
continue
}
// We might need to manually null terminate this thing
// So just make sure that we do that
if value[length - 1] != u16(0) {
value[length] = u16(0)
}
res := string_from_wide(value)
return res
}
}
}
return error('windows kit not found')
}
struct WindowsKit {
um_lib_path string
ucrt_lib_path string
um_include_path string
ucrt_include_path string
shared_include_path string
}
// Try and find the root key for installed windows kits
fn find_windows_kit_root(host_arch string) ?WindowsKit {
$if windows {
root_key := RegKey(0)
path := 'SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots'
rc := C.RegOpenKeyEx(HKEY_LOCAL_MACHINE, path.to_wide(), 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &root_key)
defer {
C.RegCloseKey(root_key)
}
if rc != 0 {
return error('Unable to open root key')
}
// Try and find win10 kit
kit_root := find_windows_kit_internal(root_key, ['KitsRoot10', 'KitsRoot81'])or{
return error('Unable to find a windows kit')
}
kit_lib := kit_root + 'Lib'
// println(kit_lib)
files := os.ls(kit_lib)or{
panic(err)
}
mut highest_path := ''
mut highest_int := 0
for f in files {
no_dot := f.replace('.', '')
v_int := no_dot.int()
if v_int > highest_int {
highest_int = v_int
highest_path = f
}
}
kit_lib_highest := kit_lib + '\\$highest_path'
kit_include_highest := kit_lib_highest.replace('Lib', 'Include')
// println('$kit_lib_highest $kit_include_highest')
return WindowsKit{
um_lib_path: kit_lib_highest + '\\um\\$host_arch'
ucrt_lib_path: kit_lib_highest + '\\ucrt\\$host_arch'
um_include_path: kit_include_highest + '\\um'
ucrt_include_path: kit_include_highest + '\\ucrt'
shared_include_path: kit_include_highest + '\\shared'
}
}
return error('Host OS does not support funding a windows kit')
}
struct VsInstallation {
include_path string
lib_path string
exe_path string
}
fn find_vs(vswhere_dir string, host_arch string) ?VsInstallation {
$if !windows {
return error('Host OS does not support finding a Vs installation')
}
// Emily:
// VSWhere is guaranteed to be installed at this location now
// If its not there then end user needs to update their visual studio
// installation!
res := os.exec('"$vswhere_dir\\Microsoft Visual Studio\\Installer\\vswhere.exe" -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath')or{
return error(err)
}
// println('res: "$res"')
version := os.read_file('$res.output\\VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt')or{
println('Unable to find msvc version')
return error('Unable to find vs installation')
}
version2 := version // TODO remove. cgen option bug if expr
// println('version: $version')
v := if version.ends_with('\n') { version2[..version.len - 2] } else {version2 }
lib_path := '$res.output\\VC\\Tools\\MSVC\\$v\\lib\\$host_arch'
include_path := '$res.output\\VC\\Tools\\MSVC\\$v\\include'
if os.exists('$lib_path\\vcruntime.lib') {
p := '$res.output\\VC\\Tools\\MSVC\\$v\\bin\\Host$host_arch\\$host_arch'
// println('$lib_path $include_path')
return VsInstallation{
exe_path: p
lib_path: lib_path
include_path: include_path
}
}
println('Unable to find vs installation (attempted to use lib path "$lib_path")')
return error('Unable to find vs exe folder')
}
fn find_msvc() ?MsvcResult {
$if windows {
processor_architecture := os.getenv('PROCESSOR_ARCHITECTURE')
vswhere_dir := if processor_architecture == 'x86' { '%ProgramFiles%' } else { '%ProgramFiles(x86)%' }
host_arch := if processor_architecture == 'x86' { 'X86' } else { 'X64' }
wk := find_windows_kit_root(host_arch)or{
return error('Unable to find windows sdk')
}
vs := find_vs(vswhere_dir, host_arch)or{
return error('Unable to find visual studio')
}
return MsvcResult{
full_cl_exe_path: os.real_path(vs.exe_path + os.path_separator + 'cl.exe')
exe_path: vs.exe_path
um_lib_path: wk.um_lib_path
ucrt_lib_path: wk.ucrt_lib_path
vs_lib_path: vs.lib_path
um_include_path: wk.um_include_path
ucrt_include_path: wk.ucrt_include_path
vs_include_path: vs.include_path
shared_include_path: wk.shared_include_path
}
} $else {
verror('Cannot find msvc on this OS')
return error('msvc not found')
}
}
pub fn (v mut V) 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' {
os.rm(v.out_name_c)
}
verror('Cannot find MSVC on this OS')
return
}
out_name_obj := os.real_path(v.out_name_c + '.obj')
// Default arguments
// volatile:ms enables atomic volatile (gcc _Atomic)
// -w: no warnings
// 2 unicode defines
// /Fo sets the object file name - needed so we can clean up after ourselves properly
mut a := ['-w', '/we4013', '/volatile:ms', '/Fo"$out_name_obj"']
if v.pref.is_prod {
a << '/O2'
a << '/MD'
a << '/Zi'
a << '/DNDEBUG'
}
else {
a << '/Zi'
a << '/MDd'
}
if v.pref.is_so {
if !v.pref.out_name.ends_with('.dll') {
v.pref.out_name += '.dll'
}
// Build dll
a << '/LD'
}
else if !v.pref.out_name.ends_with('.exe') {
v.pref.out_name += '.exe'
}
v.pref.out_name = os.real_path(v.pref.out_name)
// alibs := []string // builtin.o os.o http.o etc
if v.pref.build_mode == .build_module {
// Compile only
a << '/c'
}
else if v.pref.build_mode == .default_mode {
/*
b := os.real_path( '${pref.default_module_path}/vlib/builtin.obj' )
alibs << '"$b"'
if !os.exists(b) {
println('`builtin.obj` not found')
exit(1)
}
for imp in v.table.imports {
if imp == 'webview' {
continue
}
alibs << '"' + os.real_path( '${pref.default_module_path}/vlib/${imp}.obj' ) + '"'
}
*/
}
if v.pref.sanitize {
println('Sanitize not supported on msvc.')
}
// The C file we are compiling
// a << '"$TmpPath/$v.out_name_c"'
a << '"' + os.real_path(v.out_name_c) + '"'
// Emily:
// Not all of these are needed (but the compiler should discard them if they are not used)
// these are the defaults used by msbuild and visual studio
mut real_libs := ['kernel32.lib', 'user32.lib', 'advapi32.lib']
sflags := v.get_os_cflags().msvc_string_flags()
real_libs << sflags.real_libs
inc_paths := sflags.inc_paths
lib_paths := sflags.lib_paths
other_flags := sflags.other_flags
// Include the base paths
a << '-I "$r.ucrt_include_path"'
a << '-I "$r.vs_include_path"'
a << '-I "$r.um_include_path"'
a << '-I "$r.shared_include_path"'
a << inc_paths
a << other_flags
// Libs are passed to cl.exe which passes them to the linker
a << real_libs.join(' ')
a << '/link'
a << '/NOLOGO'
a << '/OUT:"$v.pref.out_name"'
a << '/LIBPATH:"$r.ucrt_lib_path"'
a << '/LIBPATH:"$r.um_lib_path"'
a << '/LIBPATH:"$r.vs_lib_path"'
a << '/DEBUG:FULL' // required for prod builds to generate PDB
if v.pref.is_prod {
a << '/INCREMENTAL:NO' // Disable incremental linking
a << '/OPT:REF'
a << '/OPT:ICF'
}
a << lib_paths
args := a.join(' ')
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) {
println('\n========== cl cmd line:')
println(cmd)
println('==========\n')
}
// println('$cmd')
res := os.exec(cmd)or{
println(err)
verror('msvc error')
return
}
if res.exit_code != 0 {
verror(res.output)
}
// println(res)
// println('C OUTPUT:')
if !v.pref.is_keep_c && v.out_name_c != 'v.c' && v.out_name_c != 'v_macos.c' {
os.rm(v.out_name_c)
}
// Always remove the object file - it is completely unnecessary
os.rm(out_name_obj)
}
fn build_thirdparty_obj_file_with_msvc(path string, moduleflags []CFlag) {
msvc := find_msvc()or{
println('Could not find visual studio')
return
}
// msvc expects .obj not .o
mut obj_path := '${path}bj'
obj_path = os.real_path(obj_path)
if os.exists(obj_path) {
println('$obj_path already built.')
return
}
println('$obj_path not found, building it (with msvc)...')
parent := os.dir(obj_path)
files := os.ls(parent)or{
panic(err)
}
mut cfiles := ''
for file in files {
if file.ends_with('.c') {
cfiles += '"' + os.real_path(parent + os.path_separator + file) + '" '
}
}
include_string := '-I "$msvc.ucrt_include_path" -I "$msvc.vs_include_path" -I "$msvc.um_include_path" -I "$msvc.shared_include_path"'
// println('cfiles: $cfiles')
btarget := moduleflags.c_options_before_target_msvc()
atarget := moduleflags.c_options_after_target_msvc()
cmd := '"$msvc.full_cl_exe_path" /volatile:ms /Zi /DNDEBUG $include_string /c $btarget $cfiles $atarget /Fo"$obj_path"'
// NB: the quotes above ARE balanced.
println('thirdparty cmd line: $cmd')
res := os.exec(cmd)or{
println('msvc: failed thirdparty object build cmd: $cmd')
verror(err)
return
}
if res.exit_code != 0 {
println('msvc: failed thirdparty object build cmd: $cmd')
verror(res.output)
return
}
println(res.output)
}
struct MsvcStringFlags {
mut:
real_libs []string
inc_paths []string
lib_paths []string
other_flags []string
}
fn (cflags []CFlag) msvc_string_flags() MsvcStringFlags {
mut real_libs := []string
mut inc_paths := []string
mut lib_paths := []string
mut other_flags := []string
for flag in cflags {
// println('fl: $flag.name | flag arg: $flag.value')
// We need to see if the flag contains -l
// -l isnt recognised and these libs will be passed straight to the linker
// by the compiler
if flag.name == '-l' {
if flag.value.ends_with('.dll') {
verror('MSVC cannot link against a dll (`#flag -l $flag.value`)')
}
// MSVC has no method of linking against a .dll
// TODO: we should look for .defs aswell
lib_lib := flag.value + '.lib'
real_libs << lib_lib
}
else if flag.name == '-I' {
inc_paths << flag.format()
}
else if flag.name == '-L' {
lib_paths << flag.value
lib_paths << flag.value + os.path_separator + 'msvc'
// The above allows putting msvc specific .lib files in a subfolder msvc/ ,
// where gcc will NOT find them, but cl will do...
// NB: gcc is smart enough to not need .lib files at all in most cases, the .dll is enough.
// When both a msvc .lib file and .dll file are present in the same folder,
// as for example for glfw3, compilation with gcc would fail.
}
else if flag.value.ends_with('.o') {
// msvc expects .obj not .o
other_flags << '"${flag.value}bj"'
}
else {
other_flags << flag.value
}
}
mut lpaths := []string
for l in lib_paths {
lpaths << '/LIBPATH:"' + os.real_path(l) + '"'
}
return MsvcStringFlags{
real_libs:real_libs
inc_paths:inc_paths
lib_paths:lpaths
other_flags:other_flags
}
}

View File

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

View File

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

View File

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

View File

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