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:
100
cmd/v/flag.v
100
cmd/v/flag.v
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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]
|
||||
}
|
@ -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(' ')
|
||||
}
|
||||
|
@ -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`')
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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; }')
|
||||
}
|
||||
*/
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
}
|
@ -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 []
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
50
cmd/v/v.v
50
cmd/v/v.v
@ -4,7 +4,7 @@
|
||||
module main
|
||||
|
||||
import (
|
||||
internal.compile
|
||||
//internal.compile
|
||||
internal.help
|
||||
os
|
||||
os.cmdline
|
||||
@ -12,6 +12,7 @@ import (
|
||||
v.doc
|
||||
v.pref
|
||||
v.util
|
||||
v.builder
|
||||
)
|
||||
|
||||
const (
|
||||
@ -22,6 +23,13 @@ const (
|
||||
'repl',
|
||||
'build-tools', 'build-examples', 'build-vbinaries',
|
||||
'setup-freetype']
|
||||
|
||||
list_of_flags_that_allow_duplicates = ['cc','d','define','cf','cflags']
|
||||
//list_of_flags contains a list of flags where an argument is expected past it.
|
||||
list_of_flags_with_param = [
|
||||
'o', 'output', 'd', 'define', 'b', 'backend', 'cc', 'os', 'target-os', 'arch',
|
||||
'csource', 'cf', 'cflags', 'path'
|
||||
]
|
||||
)
|
||||
|
||||
fn main() {
|
||||
@ -38,12 +46,12 @@ fn main() {
|
||||
println(util.full_v_version())
|
||||
return
|
||||
}
|
||||
prefs2, command := parse_args(args)
|
||||
//println('command = $command')
|
||||
if prefs2.is_verbose {
|
||||
prefs, command := parse_args(args)
|
||||
//println('main() command = $command')
|
||||
if prefs.is_verbose {
|
||||
println(util.full_v_version())
|
||||
}
|
||||
if prefs2.is_verbose {
|
||||
if prefs.is_verbose {
|
||||
//println('args= ')
|
||||
//println(args) // QTODO
|
||||
//println('prefs= ')
|
||||
@ -53,7 +61,7 @@ fn main() {
|
||||
// Note for future contributors: Please add new subcommands in the `match` block below.
|
||||
if command in simple_cmd {
|
||||
// External tools
|
||||
util.launch_tool(prefs2.is_verbose, 'v' + command)
|
||||
util.launch_tool(prefs.is_verbose, 'v' + command)
|
||||
return
|
||||
}
|
||||
match command {
|
||||
@ -61,7 +69,7 @@ fn main() {
|
||||
invoke_help_and_exit(args)
|
||||
}
|
||||
'create', 'init' {
|
||||
util.launch_tool(prefs2.is_verbose, 'vcreate')
|
||||
util.launch_tool(prefs.is_verbose, 'vcreate')
|
||||
return
|
||||
}
|
||||
'translate' {
|
||||
@ -69,7 +77,7 @@ fn main() {
|
||||
return
|
||||
}
|
||||
'search', 'install', 'update', 'remove' {
|
||||
util.launch_tool(prefs2.is_verbose, 'vpm')
|
||||
util.launch_tool(prefs.is_verbose, 'vpm')
|
||||
return
|
||||
}
|
||||
'get' {
|
||||
@ -96,8 +104,7 @@ fn main() {
|
||||
else {}
|
||||
}
|
||||
if command in ['run', 'build'] || command.ends_with('.v') || os.exists(command) {
|
||||
arg := join_flags_and_argument()
|
||||
compile.compile(command, arg)
|
||||
builder.compile(command, prefs)
|
||||
return
|
||||
}
|
||||
eprintln('v $command: unknown command\nRun "v help" for usage.')
|
||||
@ -113,6 +120,25 @@ fn parse_args(args []string) (&pref.Preferences, string) {
|
||||
match arg {
|
||||
'-v' { res.is_verbose = true }
|
||||
'-cg' { res.is_debug = true }
|
||||
'-live' { res.is_solive = true }
|
||||
'-autofree' { res.autofree = true }
|
||||
'-compress' { res.compress = true }
|
||||
'-freestanding' { res.is_bare = true }
|
||||
'-prod' { res.is_prod = true }
|
||||
'-stats' { res.is_stats = true }
|
||||
'-obfuscate' { res.obfuscate = true }
|
||||
'-translated' { res.translated = true }
|
||||
//'-x64' { res.translated = true }
|
||||
'-os' {
|
||||
//TODO Remove `tmp` variable when it doesn't error out in C.
|
||||
target_os := cmdline.option(args, '-os', '')
|
||||
tmp := pref.os_from_string(target_os) or {
|
||||
println('unknown operating system target `$target_os`')
|
||||
exit(1)
|
||||
}
|
||||
res.os = tmp
|
||||
i++
|
||||
}
|
||||
'-cc' {
|
||||
res.ccompiler = cmdline.option(args, '-cc', 'cc')
|
||||
i++
|
||||
@ -139,6 +165,10 @@ fn parse_args(args []string) (&pref.Preferences, string) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if command.ends_with('.v') || os.exists(command) {
|
||||
res.path = command
|
||||
}
|
||||
res.fill_with_defaults()
|
||||
return res, command
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user