move v.v to cmd/v

pull/3700/head
lutherwenxu 2020-02-09 17:08:04 +08:00 committed by GitHub
parent 3f5e4c55b2
commit 9332a83ce6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 1123 additions and 799 deletions

View File

@ -12,8 +12,8 @@ jobs:
echo "Changed files compared to origin/master are:" && git diff --name-status origin/master HEAD -- '*.v' echo "Changed files compared to origin/master are:" && git diff --name-status origin/master HEAD -- '*.v'
- name: Build v (there is no need for dependencies for fmt) - name: Build v (there is no need for dependencies for fmt)
run: make -j4 run: make -j4
- name: Build a production tools/vfmt - name: Build a production cmd/tools/vfmt
run: ./v -prod -d vfmt tools/vfmt.v run: ./v -prod -d vfmt cmd/tools/vfmt.v
- name: Run v fmt -diff on only the changed files. Does NOT fail for now. - name: Run v fmt -diff on only the changed files. Does NOT fail for now.
run: git diff --name-status origin/master HEAD -- '*.v' |grep -v '^D'|rev|cut -f1|rev| xargs ./v fmt -noerror -diff run: git diff --name-status origin/master HEAD -- '*.v' |grep -v '^D'|rev|cut -f1|rev| xargs ./v fmt -noerror -diff
- name: Run v test-fmt - name: Run v test-fmt
@ -28,12 +28,12 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list; sudo apt-get update; sudo apt-get install --quiet -y libglfw3 libglfw3-dev libfreetype6-dev libssl-dev sqlite3 libsqlite3-dev libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libsdl2-image-dev run: sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list; sudo apt-get update; sudo apt-get install --quiet -y libglfw3 libglfw3-dev libfreetype6-dev libssl-dev sqlite3 libsqlite3-dev libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libsdl2-image-dev
- name: Build v - name: Build v
run: echo $VFLAGS && make -j4 && ./v -cg -o v v.v run: echo $VFLAGS && make -j4 && ./v -cg -o v cmd/v
- name: Test v->c - name: Test v->c
run: | run: |
sudo ln -s /var/tmp/tcc/bin/tcc /usr/local/bin/tcc sudo ln -s /var/tmp/tcc/bin/tcc /usr/local/bin/tcc
tcc -version tcc -version
./v -o v2 v.v # Make sure vtcc can build itself ./v -o v2 cmd/v # Make sure vtcc can build itself
./v -silent test-compiler ./v -silent test-compiler
- name: Test v binaries - name: Test v binaries
run: ./v -silent build-vbinaries run: ./v -silent build-vbinaries
@ -71,9 +71,9 @@ jobs:
brew install freetype glfw openssl postgres sdl2 sdl2_ttf sdl2_mixer sdl2_image brew install freetype glfw openssl postgres sdl2 sdl2_ttf sdl2_mixer sdl2_image
export LIBRARY_PATH="$LIBRARY_PATH:/usr/local/opt/openssl/lib/" export LIBRARY_PATH="$LIBRARY_PATH:/usr/local/opt/openssl/lib/"
- name: Build V - name: Build V
run: make -j4 && ./v -cg -o v v.v run: make -j4 && ./v -cg -o v cmd/v
- name: Build V using V - name: Build V using V
run: ./v -o v2 v.v && ./v2 -o v3 v.v run: ./v -o v2 cmd/v && ./v2 -o v3 cmd/v
- name: Test symlink - name: Test symlink
run: sudo ./v symlink run: sudo ./v symlink
- name: Set up pg database - name: Set up pg database
@ -89,7 +89,7 @@ jobs:
# - name: Test v->js # - name: Test v->js
# run: ./v -o hi.js examples/hello_v_js.v && node hi.js # run: ./v -o hi.js examples/hello_v_js.v && node hi.js
- name: Test symlink - name: Test symlink
run: ./v symlink && v -o v2 v.v run: ./v symlink && v -o v2 cmd/v
- name: Test vsh - name: Test vsh
run: ./v examples/v_script.vsh run: ./v examples/v_script.vsh
- name: Test vid - name: Test vid
@ -107,7 +107,7 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list; sudo apt-get update; sudo apt-get install --quiet -y postgresql libpq-dev libglfw3 libglfw3-dev libfreetype6-dev libssl-dev sqlite3 libsqlite3-dev libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libsdl2-image-dev run: sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list; sudo apt-get update; sudo apt-get install --quiet -y postgresql libpq-dev libglfw3 libglfw3-dev libfreetype6-dev libssl-dev sqlite3 libsqlite3-dev libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libsdl2-image-dev
- name: Build V - name: Build V
run: make -j4 && ./v -cc gcc -o v v.v run: make -j4 && ./v -cc gcc -o v cmd/v
- name: Test V - name: Test V
run: ./v -silent test-compiler run: ./v -silent test-compiler
- name: Test v binaries - name: Test v binaries
@ -122,13 +122,13 @@ jobs:
run: ./v -freestanding -o bare vlib/os/bare/bare_example_linux.v run: ./v -freestanding -o bare vlib/os/bare/bare_example_linux.v
- name: x64 machine code generation - name: x64 machine code generation
run: | run: |
./v -o vprod -prod v.v ./v -o vprod -prod cmd/v
cd tools cd cmd/tools
echo "Generating a 1m line V file..." echo "Generating a 1m line V file..."
../vprod gen1m.v ../../vprod gen1m.v
./gen1m > 1m.v ./gen1m > 1m.v
echo "Building it..." echo "Building it..."
../vprod -x64 -o 1m 1m.v ../../vprod -x64 -o 1m 1m.v
echo "Running it..." echo "Running it..."
ls ls
# ./1m # ./1m
@ -191,7 +191,7 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list; sudo apt-get update; sudo apt-get install --quiet -y musl musl-tools libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libsdl2-image-dev run: sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list; sudo apt-get update; sudo apt-get install --quiet -y musl musl-tools libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libsdl2-image-dev
- name: Build v - name: Build v
run: echo $VFLAGS && make -j4 && ./v -cg -o v v.v run: echo $VFLAGS && make -j4 && ./v -cg -o v cmd/v
- name: Test v binaries - name: Test v binaries
run: ./v -silent build-vbinaries run: ./v -silent build-vbinaries
# - name: Test v->js # - name: Test v->js

View File

@ -19,12 +19,12 @@ jobs:
sudo apt-get install --quiet -y sqlite3 libsqlite3-dev sudo apt-get install --quiet -y sqlite3 libsqlite3-dev
sudo apt-get install --quiet -y libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libsdl2-image-dev sudo apt-get install --quiet -y libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libsdl2-image-dev
- name: Build v - name: Build v
run: echo $VFLAGS && make -j4 && ./v -cg -o v v.v run: echo $VFLAGS && make -j4 && ./v -cg -o v cmd/v
- name: Test v->c - name: Test v->c
run: | run: |
sudo ln -s /var/tmp/tcc/bin/tcc /usr/local/bin/tcc sudo ln -s /var/tmp/tcc/bin/tcc /usr/local/bin/tcc
tcc -version tcc -version
./v -o v2 v.v # Make sure vtcc can build itself ./v -o v2 cmd/v # Make sure vtcc can build itself
- name: Run network tests - name: Run network tests
run: ./v -d network test vlib/ run: ./v -d network test vlib/

28
.gitignore vendored
View File

@ -7,20 +7,20 @@ fns.txt
/v.c /v.c
/v.*.c /v.*.c
/v.c.out /v.c.out
/tools/performance_compare /cmd/tools/performance_compare
/tools/oldv /cmd/tools/oldv
/tools/vrepl /cmd/tools/vrepl
/tools/vtest /cmd/tools/vtest
/tools/vtest-compiler /cmd/tools/vtest-compiler
/tools/vtest-fmt /cmd/tools/vtest-fmt
/tools/vfmt /cmd/tools/vfmt
/tools/vbin2v /cmd/tools/vbin2v
/tools/vup /cmd/tools/vup
/tools/vpm /cmd/tools/vpm
/tools/vcreate /cmd/tools/vcreate
/tools/vbuild-examples /cmd/tools/vbuild-examples
/tools/vbuild-tools /cmd/tools/vbuild-tools
/tools/vbuild-vbinaries /cmd/tools/vbuild-vbinaries
/v_g /v_g
/v_cg /v_cg
/v_prod /v_prod

View File

@ -3,7 +3,7 @@ FROM mstorsjo/llvm-mingw
LABEL maintainer="Vitaly Takmazov <vitalyster@gmail.com>" LABEL maintainer="Vitaly Takmazov <vitalyster@gmail.com>"
COPY . . COPY . .
RUN make RUN make
RUN ./v -os windows -o v.c v.v RUN ./v -os windows -o v.c cmd/v
RUN x86_64-w64-mingw32-gcc v.c -std=c99 -w -municode -o v.exe RUN x86_64-w64-mingw32-gcc v.c -std=c99 -w -municode -o v.exe
RUN file v.exe RUN file v.exe

View File

@ -42,8 +42,8 @@ endif
all: latest_vc latest_tcc all: latest_vc latest_tcc
ifdef WIN32 ifdef WIN32
$(CC) $(CFLAGS) -std=c99 -municode -w -o v2.exe $(TMPVC)/$(VCFILE) $(LDFLAGS) $(CC) $(CFLAGS) -std=c99 -municode -w -o v2.exe $(TMPVC)/$(VCFILE) $(LDFLAGS)
./v2.exe -o v3.exe v.v ./v2.exe -o v3.exe cmd/v
./v3.exe -o v.exe -prod v.v ./v3.exe -o v.exe -prod cmd/v
rm -f v2.exe v3.exe rm -f v2.exe v3.exe
else else
$(CC) $(CFLAGS) -std=gnu11 -w -o v $(TMPVC)/$(VCFILE) $(LDFLAGS) -lm $(CC) $(CFLAGS) -std=gnu11 -w -o v $(TMPVC)/$(VCFILE) $(LDFLAGS) -lm
@ -92,10 +92,10 @@ $(TMPVC)/.git/config:
$(MAKE) fresh_vc $(MAKE) fresh_vc
selfcompile: selfcompile:
./v -cg -o v v.v ./v -cg -o v cmd/v
selfcompile-static: selfcompile-static:
./v -cg -cflags '--static' -o v-static v.v ./v -cg -cflags '--static' -o v-static cmd/v
modules: module_builtin module_strings module_strconv modules: module_builtin module_strings module_strconv
module_builtin: module_builtin:

View File

@ -11,9 +11,9 @@ import (
fn main() { fn main() {
exe := os.executable() exe := os.executable()
dir := filepath.dir(exe) dir := filepath.dir(exe)
vdir := filepath.dir(filepath.dir(dir)) vdir := filepath.dir(filepath.dir(filepath.dir(dir)))
if !os.exists('$vdir/v') && !os.is_dir('$vdir/vlib') { if !os.exists('$vdir/v') && !os.is_dir('$vdir/vlib') {
println('fast.html generator needs to be located in `v/tools/fast/`') println('fast.html generator needs to be located in `v/cmd/tools/fast`')
} }
println('fast.html generator\n') println('fast.html generator\n')
// Fetch the last commit's hash // Fetch the last commit's hash
@ -36,11 +36,11 @@ fn main() {
} }
// Build an optimized V // Build an optimized V
println('Building vprod...') println('Building vprod...')
exec('v -o $vdir/vprod -prod $vdir/v.v') exec('v -o $vdir/vprod -prod $vdir/cmd/v')
println('Measuring...') println('Measuring...')
diff1 := measure('$vdir/vprod -cc clang -o v.c $vdir/v.v') diff1 := measure('$vdir/vprod -cc clang -o v.c $vdir/cmd/v')
diff2 := measure('$vdir/vprod -cc clang -o v2 $vdir/v.v') diff2 := measure('$vdir/vprod -cc clang -o v2 $vdir/cmd/v')
diff3 := measure('$vdir/vprod -x64 $vdir/tools/1mil.v') diff3 := measure('$vdir/vprod -x64 $vdir/cmd/tools/1mil.v')
diff4 := measure('$vdir/vprod -cc clang $vdir/examples/hello_world.v') diff4 := measure('$vdir/vprod -cc clang $vdir/examples/hello_world.v')
//println('Building V took ${diff}ms') //println('Building V took ${diff}ms')
commit_date := exec('git log -n1 --pretty="format:%at"') commit_date := exec('git log -n1 --pretty="format:%at"')

View File

@ -38,7 +38,7 @@ td {
<h2>Is V still fast?</h2> <h2>Is V still fast?</h2>
Monitoring compilation speed for each commit. <br><br> Monitoring compilation speed for each commit. <br><br>
Source code: <a target=blank href='https://github.com/vlang/v/blob/master/tools/fast/fast.v'>fast.v</a> <br><br> Source code: <a target=blank href='https://github.com/vlang/v/blob/master/cmd/tools/fast/fast.v'>fast.v</a> <br><br>
<table> <table>
<tr> <tr>

View File

@ -291,7 +291,7 @@ fn (gen_vc mut GenVC) generate() {
v_flags := if os_name == 'nix' { '-output-cross-platform-c' } else { '-os $os_name' } v_flags := if os_name == 'nix' { '-output-cross-platform-c' } else { '-os $os_name' }
c_file := 'v${vc_suffix}.c' c_file := 'v${vc_suffix}.c'
// try generate .c file // try generate .c file
gen_vc.cmd_exec('$v_exec $v_flags -o $c_file $git_repo_dir_v/v.v') gen_vc.cmd_exec('$v_exec $v_flags -o $c_file $git_repo_dir_v/cmd/v')
// check if the c file seems ok // check if the c file seems ok
gen_vc.assert_file_exists_and_is_not_too_short(c_file, err_msg_gen_c) gen_vc.assert_file_exists_and_is_not_too_short(c_file, err_msg_gen_c)
// embed the latest v commit hash into the c file // embed the latest v commit hash into the c file

View File

@ -39,7 +39,7 @@ pub fn new_test_session(vargs string) TestSession {
pub fn vexe_path() string { pub fn vexe_path() string {
// NB: tools extracted from v require that the VEXE // NB: tools extracted from v require that the VEXE
// environment variable contains the path to the v executable location. // environment variable contains the path to the v executable location.
// They are usually launched by vlib/compiler/vtools.v, // They are usually launched by cmd/v/simple_tool.v,
// launch_tool/1 , which provides it. // launch_tool/1 , which provides it.
return os.getenv('VEXE') return os.getenv('VEXE')
} }
@ -238,11 +238,11 @@ pub fn building_any_v_binaries_failed() bool {
testing.vlib_should_be_present(parent_dir) testing.vlib_should_be_present(parent_dir)
os.chdir(parent_dir) os.chdir(parent_dir)
mut failed := false mut failed := false
v_build_commands := ['$vexe -o v_g -g v.v', v_build_commands := ['$vexe -o v_g -g cmd/v',
'$vexe -o v_prod_g -prod -g v.v', '$vexe -o v_prod_g -prod -g cmd/v',
'$vexe -o v_cg -cg v.v', '$vexe -o v_cg -cg cmd/v',
'$vexe -o v_prod_cg -prod -cg v.v', '$vexe -o v_prod_cg -prod -cg cmd/v',
'$vexe -o v_prod -prod v.v', '$vexe -o v_prod -prod cmd/v',
] ]
mut bmark := benchmark.new_benchmark() mut bmark := benchmark.new_benchmark()
for cmd in v_build_commands { for cmd in v_build_commands {

View File

@ -92,7 +92,7 @@ pub mut:
commit_vc_hash string // the git commit of the vc repo, corresponding to commit_v__hash commit_vc_hash string // the git commit of the vc repo, corresponding to commit_v__hash
vexename string // v or v.exe vexename string // v or v.exe
vexepath string // the full absolute path to the prepared v/v.exe vexepath string // the full absolute path to the prepared v/v.exe
vvlocation string // v.v or compiler/ , depending on v version vvlocation string // v.v or compiler/ or cmd/v, depending on v version
} }
pub fn (vgit_context mut VGitContext) compile_oldv_if_needed() { pub fn (vgit_context mut VGitContext) compile_oldv_if_needed() {
@ -117,7 +117,11 @@ pub fn (vgit_context mut VGitContext) compile_oldv_if_needed() {
v_commithash,vccommit_before := vgit.prepare_vc_source(vgit_context.path_vc, vgit_context.path_v, vgit_context.commit_v) v_commithash,vccommit_before := vgit.prepare_vc_source(vgit_context.path_vc, vgit_context.path_v, vgit_context.commit_v)
vgit_context.commit_v__hash = v_commithash vgit_context.commit_v__hash = v_commithash
vgit_context.commit_vc_hash = vccommit_before vgit_context.commit_vc_hash = vccommit_before
vgit_context.vvlocation = if os.exists('v.v') { 'v.v' } else { 'compiler' } if os.exists('cmd/v') {
vgit_context.vvlocation = 'cmd/v'
} else {
vgit_context.vvlocation = if os.exists('v.v') { 'v.v' } else { 'compiler' }
}
if os.is_dir(vgit_context.path_v) && os.exists(vgit_context.vexepath) { if os.is_dir(vgit_context.path_v) && os.exists(vgit_context.vexepath) {
// already compiled, so no need to compile v again // already compiled, so no need to compile v again
return return

View File

@ -19,7 +19,7 @@ const (
git checkout known_good_commit git checkout known_good_commit
git bisect good git bisect good
## Now git will automatically checkout a middle commit between the bad and the good ## Now git will automatically checkout a middle commit between the bad and the good
tools/oldv HEAD --command="run commands in oldv folder, to verify if the commit is good or bad" cmd/tools/oldv HEAD --command="run commands in oldv folder, to verify if the commit is good or bad"
## See what the result is, and either do: ... ## See what the result is, and either do: ...
git bisect good git bisect good
## ... or do: ## ... or do:

View File

@ -126,7 +126,9 @@ fn (c &Context) prepare_v(cdir string, commit string) {
vversion := scripting.run('$cdir/v --version') vversion := scripting.run('$cdir/v --version')
vcommit := scripting.run('git rev-parse --short --verify HEAD') vcommit := scripting.run('git rev-parse --short --verify HEAD')
println('V version is: ${vversion} , local source commit: ${vcommit}') println('V version is: ${vversion} , local source commit: ${vcommit}')
if vgit_context.vvlocation == 'v.v' { if vgit_context.vvlocation == 'cmd/v' {
println('Source lines of the compiler: ' + scripting.run('wc cmd/v/*.v vlib/compiler/*.v | tail -n -1'))
} else if vgit_context.vvlocation == 'v.v' {
println('Source lines of the compiler: ' + scripting.run('wc v.v vlib/compiler/*.v | tail -n -1')) println('Source lines of the compiler: ' + scripting.run('wc v.v vlib/compiler/*.v | tail -n -1'))
}else{ }else{
println('Source lines of the compiler: ' + scripting.run('wc compiler/*.v | tail -n -1')) println('Source lines of the compiler: ' + scripting.run('wc compiler/*.v | tail -n -1'))
@ -136,8 +138,18 @@ fn (c &Context) prepare_v(cdir string, commit string) {
fn (c Context) compare_v_performance(label string, commands []string) string { fn (c Context) compare_v_performance(label string, commands []string) string {
println('---------------------------------------------------------------------------------') println('---------------------------------------------------------------------------------')
println('Compare v performance when doing the following commands ($label):') println('Compare v performance when doing the following commands ($label):')
source_location_a := if os.exists('$c.a/v.v') { 'v.v ' } else { 'compiler/ ' } mut source_location_a := ''
source_location_b := if os.exists('$c.b/v.v') { 'v.v ' } else { 'compiler/ ' } mut source_location_b := ''
if os.exists('$c.a/cmd/v') {
source_location_a = 'cmd/v'
} else {
source_location_a = if os.exists('$c.a/v.v') { 'v.v ' } else { 'compiler/ ' }
}
if os.exists('$c.b/cmd/v') {
source_location_b = 'cmd/v'
} else {
source_location_b = if os.exists('$c.b/v.v') { 'v.v ' } else { 'compiler/ ' }
}
timestamp_a,_ := vgit.line_to_timestamp_and_commit(scripting.run('cd $c.a/ ; git rev-list -n1 --timestamp HEAD')) timestamp_a,_ := vgit.line_to_timestamp_and_commit(scripting.run('cd $c.a/ ; git rev-list -n1 --timestamp HEAD'))
timestamp_b,_ := vgit.line_to_timestamp_and_commit(scripting.run('cd $c.b/ ; git rev-list -n1 --timestamp HEAD')) timestamp_b,_ := vgit.line_to_timestamp_and_commit(scripting.run('cd $c.b/ ; git rev-list -n1 --timestamp HEAD'))
debug_option_a := if timestamp_a > 1570877641 { '-g ' } else { '-debug ' } debug_option_a := if timestamp_a > 1570877641 { '-g ' } else { '-debug ' }

View File

@ -1,28 +1,28 @@
# V preludes: # V preludes:
The tools/preludes/ contains small v code snippets, that V uses when The cmd/tools/preludes/ contains small v code snippets, that V uses when
compiling certain v programs. V adds the files below automatically itself. compiling certain v programs. V adds the files below automatically itself.
Each file is used in different situations (see below). Each file is used in different situations (see below).
NB: preludes are *NOT* intended to be used by user programs/modules. NB: preludes are *NOT* intended to be used by user programs/modules.
The folder tools/preludes/ is *NOT* a v module. The folder cmd/tools/preludes/ is *NOT* a v module.
## Details: ## Details:
### tools/preludes/live_main.v ### cmd/tools/preludes/live_main.v
Used when compiling live programs. This file is used by the main executable Used when compiling live programs. This file is used by the main executable
live program, that starts the file change monitoring thread. Each live program live program, that starts the file change monitoring thread. Each live program
needs module `os` and module `time`, in order for the background file change needs module `os` and module `time`, in order for the background file change
monitoring thread to work properly. monitoring thread to work properly.
### tools/preludes/live_shared.v ### cmd/tools/preludes/live_shared.v
Used when compiling live programs, for the shared library portion of the live Used when compiling live programs, for the shared library portion of the live
programs, that is reloaded each time the code is changed. programs, that is reloaded each time the code is changed.
### tools/preludes/tests_assertions.v ### cmd/tools/preludes/tests_assertions.v
Used when compiling `_test.v` programs. Used when compiling `_test.v` programs.
It specifies how failed assertions will look. It specifies how failed assertions will look.
### tools/preludes/tests_with_stats.v ### cmd/tools/preludes/tests_with_stats.v
Used when compiling `_test.v` programs with -stats option. Used when compiling `_test.v` programs with -stats option.
It specifies how the result will appear ('assert' vs 'asserts' and so on). It specifies how the result will appear ('assert' vs 'asserts' and so on).

View File

@ -8,7 +8,7 @@ import (
fn main() { fn main() {
args := os.args args := os.args
args_string := args[1..].join(' ') args_string := args[1..].join(' ')
if testing.v_build_failing(args_string.all_before('build-tools'), 'tools') { if testing.v_build_failing(args_string.all_before('build-tools'), 'cmd/tools') {
exit(1) exit(1)
} }
} }

View File

@ -8,6 +8,7 @@ import (
os.cmdline os.cmdline
filepath filepath
compiler compiler
v.pref
) )
struct FormatOptions { struct FormatOptions {
@ -35,8 +36,8 @@ const (
fn main() { fn main() {
toolexe := os.executable() toolexe := os.executable()
compiler.set_vroot_folder(filepath.dir(filepath.dir(toolexe))) compiler.set_vroot_folder(filepath.dir(filepath.dir(filepath.dir(toolexe))))
args := compiler.env_vflags_and_os_args() args := join_flags_and_argument()
foptions := FormatOptions{ foptions := FormatOptions{
is_c: '-c' in args is_c: '-c' in args
is_l: '-l' in args is_l: '-l' in args
@ -134,10 +135,10 @@ fn main() {
fn (foptions &FormatOptions) format_file(file string) { fn (foptions &FormatOptions) format_file(file string) {
tmpfolder := os.tmpdir() tmpfolder := os.tmpdir()
mut compiler_params := []string mut compiler_params := &pref.Preferences{}
target_os := file_to_target_os(file) target_os := file_to_target_os(file)
if target_os != '' { if target_os != '' {
compiler_params << ['-os', target_os] compiler_params.os = pref.os_from_string(target_os)
} }
mut cfile := file mut cfile := file
mut mod_folder_parent := tmpfolder mut mod_folder_parent := tmpfolder
@ -158,7 +159,7 @@ fn (foptions &FormatOptions) format_file(file string) {
} }
os.write_file(main_program_file, main_program_content) os.write_file(main_program_file, main_program_content)
cfile = main_program_file cfile = main_program_file
compiler_params << ['-user_mod_path', mod_folder_parent] compiler_params.user_mod_path = mod_folder_parent
} }
if !is_test_file && mod_name == 'main' { if !is_test_file && mod_name == 'main' {
// NB: here, file is guaranted to be a main. We do not know however // NB: here, file is guaranted to be a main. We do not know however
@ -166,7 +167,10 @@ fn (foptions &FormatOptions) format_file(file string) {
// project, like vorum or vid. // project, like vorum or vid.
cfile = get_compile_name_of_potential_v_project(cfile) cfile = get_compile_name_of_potential_v_project(cfile)
} }
compiler_params << cfile compiler_params.path = cfile
compiler_params.mod = mod_name
compiler_params.is_test = is_test_file
compiler_params.is_script = file.ends_with('.v') || file.ends_with('.vsh')
if foptions.is_verbose { if foptions.is_verbose {
eprintln('vfmt format_file: file: $file') eprintln('vfmt format_file: file: $file')
eprintln('vfmt format_file: cfile: $cfile') eprintln('vfmt format_file: cfile: $cfile')
@ -176,9 +180,15 @@ fn (foptions &FormatOptions) format_file(file string) {
eprintln('vfmt format_file: mod_folder: $mod_folder') eprintln('vfmt format_file: mod_folder: $mod_folder')
eprintln('vfmt format_file: mod_folder_parent: $mod_folder_parent') eprintln('vfmt format_file: mod_folder_parent: $mod_folder_parent')
eprintln('vfmt format_file: use_tmp_main_program: $use_tmp_main_program') eprintln('vfmt format_file: use_tmp_main_program: $use_tmp_main_program')
eprintln('vfmt format_file: compiler_params: $compiler_params') eprintln('vfmt format_file: compiler_params: ')
print_compiler_options( compiler_params )
eprintln('-------------------------------------------') eprintln('-------------------------------------------')
} }
compiler_params.fill_with_defaults()
if foptions.is_verbose {
eprintln('vfmt format_file: compiler_params: AFTER fill_with_defaults() ')
print_compiler_options( compiler_params )
}
formatted_file_path := foptions.compile_file(file, compiler_params) formatted_file_path := foptions.compile_file(file, compiler_params)
if use_tmp_main_program { if use_tmp_main_program {
if !foptions.is_debug { if !foptions.is_debug {
@ -188,6 +198,22 @@ fn (foptions &FormatOptions) format_file(file string) {
eprintln('${FORMATTED_FILE_TOKEN}${formatted_file_path}') eprintln('${FORMATTED_FILE_TOKEN}${formatted_file_path}')
} }
fn print_compiler_options( compiler_params &pref.Preferences ) {
eprintln(' os: ' + compiler_params.os.str() )
eprintln(' ccompiler: $compiler_params.ccompiler' )
eprintln(' mod: $compiler_params.mod ')
eprintln(' path: $compiler_params.path ')
eprintln(' out_name: $compiler_params.out_name ')
eprintln(' vroot: $compiler_params.vroot ')
eprintln(' vpath: $compiler_params.vpath ')
eprintln(' vlib_path: $compiler_params.vlib_path ')
eprintln(' out_name: $compiler_params.out_name ')
eprintln(' umpath: $compiler_params.user_mod_path ')
eprintln(' cflags: $compiler_params.cflags ')
eprintln(' is_test: $compiler_params.is_test ')
eprintln(' is_script: $compiler_params.is_script ')
}
fn (foptions &FormatOptions) post_process_file(file string, formatted_file_path string) { fn (foptions &FormatOptions) post_process_file(file string, formatted_file_path string) {
if formatted_file_path.len == 0 { if formatted_file_path.len == 0 {
return return
@ -238,7 +264,7 @@ fn (foptions &FormatOptions) post_process_file(file string, formatted_file_path
} }
fn usage() { fn usage() {
print('Usage: tools/vfmt [flags] fmt path_to_source.v [path_to_other_source.v] print('Usage: cmd/tools/vfmt [flags] fmt path_to_source.v [path_to_other_source.v]
Formats the given V source files, and prints their formatted source to stdout. Formats the given V source files, and prints their formatted source to stdout.
Options: Options:
-c check if file is already formatted. -c check if file is already formatted.
@ -261,12 +287,13 @@ fn find_working_diff_command() ?string {
return error('no working diff command found') return error('no working diff command found')
} }
fn (foptions &FormatOptions) compile_file(file string, compiler_params []string) string { fn (foptions &FormatOptions) compile_file(file string, compiler_params &pref.Preferences) string {
if foptions.is_verbose { if foptions.is_verbose {
eprintln('> new_v_compiler_with_args file: ' + file) eprintln('> new_v_compiler_with_args file: $file')
eprintln('> new_v_compiler_with_args compiler_params: ' + compiler_params.join(' ')) eprintln('> new_v_compiler_with_args compiler_params:')
print_compiler_options( compiler_params )
} }
mut v := compiler.new_v_compiler_with_args(compiler_params) mut v := compiler.new_v(compiler_params)
v.v_fmt_file = file v.v_fmt_file = file
if foptions.is_all { if foptions.is_all {
v.v_fmt_all = true v.v_fmt_all = true
@ -360,3 +387,28 @@ fn get_compile_name_of_potential_v_project(file string) string {
} }
return pfolder return pfolder
} }
//TODO Move join_flags_and_argument() and non_empty() into `cmd/internal` when v.mod work correctly
//to prevent code duplication with `cmd/v` (cmd/v/flag.v)
fn join_flags_and_argument() []string {
vosargs := os.getenv('VOSARGS')
if vosargs != '' {
return non_empty(vosargs.split(' '))
}
mut args := []string
vflags := os.getenv('VFLAGS')
if vflags != '' {
args << os.args[0]
args << vflags.split(' ')
if os.args.len > 1 {
args << os.args[1..]
}
return non_empty(args)
}
return non_empty(os.args)
}
fn non_empty(arg []string) []string {
return arg.filter(it != '')
}

View File

@ -6,6 +6,7 @@ import (
strings strings
filepath filepath
compiler compiler
v.pref
) )
const ( const (
@ -32,7 +33,11 @@ fn analyze_v_file(file string) {
println('$hash $file $hash') println('$hash $file $hash')
// main work: // main work:
mut v := compiler.new_v_compiler_with_args([file]) mut pref := &pref.Preferences{
path: file
}
pref.fill_with_defaults()
mut v := compiler.new_v(pref)
v.add_v_files_to_compile() v.add_v_files_to_compile()
for f in v.files { v.parse(f, .decl) } for f in v.files { v.parse(f, .decl) }
fi := v.get_file_parser_index( file ) or { panic(err) } fi := v.get_file_parser_index( file ) or { panic(err) }
@ -51,7 +56,7 @@ fn analyze_v_file(file string) {
fn main(){ fn main(){
toolexe := os.executable() toolexe := os.executable()
compiler.set_vroot_folder( filepath.dir(filepath.dir(toolexe)) ) compiler.set_vroot_folder(filepath.dir(filepath.dir(filepath.dir(toolexe))))
mut fp := flag.new_flag_parser(os.args) mut fp := flag.new_flag_parser(os.args)
fp.application(filepath.filename(toolexe)) fp.application(filepath.filename(toolexe))

View File

@ -33,8 +33,8 @@ fn v_test_compiler(vargs string) {
// Make sure v.c can be compiled without warnings // Make sure v.c can be compiled without warnings
$if macos { $if macos {
if os.exists('/v.v') { if os.exists('/cmd/v') {
os.system('$vexe -o v.c v.v') os.system('$vexe -o v.c cmd/v')
if os.system('cc -Werror v.c') != 0 { if os.system('cc -Werror v.c') != 0 {
eprintln('cc failed to build v.c without warnings') eprintln('cc failed to build v.c without warnings')
exit(1) exit(1)
@ -42,7 +42,7 @@ fn v_test_compiler(vargs string) {
eprintln('v.c can be compiled without warnings. This is good :)') eprintln('v.c can be compiled without warnings. This is good :)')
} }
} }
building_tools_failed := testing.v_build_failing(vargs, 'tools') building_tools_failed := testing.v_build_failing(vargs, 'cmd/tools')
eprintln('') eprintln('')
testing.eheader('Testing all _test.v files...') testing.eheader('Testing all _test.v files...')
mut compiler_test_session := testing.new_test_session(vargs) mut compiler_test_session := testing.new_test_session(vargs)

View File

@ -7,14 +7,14 @@ import (
const ( const (
known_failing_exceptions = ['./examples/vweb/vweb_example.v', known_failing_exceptions = ['./examples/vweb/vweb_example.v',
'./tools/gen_vc.v', './cmd/tools/gen_vc.v',
'./tools/modules/vgit/vgit.v', // generics './cmd/tools/modules/vgit/vgit.v', // generics
'./tools/preludes/live_main.v', './cmd/tools/preludes/live_main.v',
'./tools/preludes/live_shared.v', './cmd/tools/preludes/live_shared.v',
'./tools/preludes/tests_assertions.v', './cmd/tools/preludes/tests_assertions.v',
'./tools/preludes/tests_with_stats.v', './cmd/tools/preludes/tests_with_stats.v',
'./tools/performance_compare.v', // generics './cmd/tools/performance_compare.v', // generics
'./tools/oldv.v', // generics './cmd/tools/oldv.v', // generics
'./tutorials/code/blog/article.v', './tutorials/code/blog/article.v',
'./tutorials/code/blog/blog.v', './tutorials/code/blog/blog.v',
'./vlib/arrays/arrays.v', './vlib/arrays/arrays.v',

67
cmd/v/compile.v 100644
View File

@ -0,0 +1,67 @@
// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module main
import (
benchmark
os
os.cmdline
)
fn compile(command string, args []string) {
// Construct the V object from command line arguments
mut v := new_v(args)
if v.pref.is_verbose {
println(args)
}
if command == 'run' {
// always recompile for now, too error prone to skip recompilation otherwise
// for example for -repl usage, especially when piping lines to v
v.compile()
run_compiled_executable_and_exit(v, args)
}
mut tmark := benchmark.new_benchmark()
if v.pref.x64 {
v.compile_x64()
}
else if v.pref.v2 {
v.compile2()
}
else {
v.compile()
}
if v.pref.is_stats {
tmark.stop()
println('compilation took: ' + tmark.total_duration().str() + 'ms')
}
if v.pref.is_test {
run_compiled_executable_and_exit(v, args)
}
v.finalize_compilation()
}
pub fn run_compiled_executable_and_exit(v &compiler.V, args []string) {
if v.pref.is_verbose {
println('============ running $v.pref.out_name ============')
}
mut cmd := '"${v.pref.out_name}"'
args_after_no_options := cmdline.only_non_options( cmdline.after(args,['run','test']) )
if args_after_no_options.len > 1 {
cmd += ' ' + args_after_no_options[1..].join(' ')
}
if v.pref.is_test {
ret := os.system(cmd)
if ret != 0 {
exit(1)
}
}
if v.pref.is_run {
ret := os.system(cmd)
// TODO: make the runner wrapping as transparent as possible
// (i.e. use execve when implemented). For now though, the runner
// just returns the same exit code as the child process.
exit(ret)
}
exit(0)
}

View File

@ -0,0 +1,229 @@
// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module main
import (
compiler
filepath
os
os.cmdline
v.pref
)
//TODO Cleanup this file. This file ended up like a dump for functions that do not belong in `compiler`.
//Maybe restructure the functions below into different V files
pub fn new_v(args []string) &compiler.V {
// Create modules dirs if they are missing
if !os.is_dir(compiler.v_modules_path) {
os.mkdir(compiler.v_modules_path)or{
panic(err)
}
os.mkdir('$compiler.v_modules_path${os.path_separator}cache')or{
panic(err)
}
}
vroot := filepath.dir(vexe_path())
// optional, custom modules search path
user_mod_path := cmdline.option(args, '-user_mod_path', '')
vlib_path := cmdline.option(args, '-vlib-path', '')
vpath := cmdline.option(args, '-vpath', '')
target_os := cmdline.option(args, '-os', '')
if target_os == 'msvc' {
// notice that `-os msvc` became `-cc msvc`
println('V error: use the flag `-cc msvc` to build using msvc')
os.flush_stdout()
exit(1)
}
mut out_name := cmdline.option(args, '-o', '')
mut dir := args.last()
if 'run' in args {
args_after_run := cmdline.only_non_options( cmdline.after(args,['run']) )
dir = if args_after_run.len>0 { args_after_run[0] } else { '' }
}
if dir == 'v.v' {
println('looks like you are trying to build V with an old command')
println('use `v -o v cmd/v` instead of `v -o v v.v`')
exit(1)
}
if dir.ends_with(os.path_separator) {
dir = dir.all_before_last(os.path_separator)
}
if dir.starts_with('.$os.path_separator') {
dir = dir[2..]
}
if args.len < 2 {
dir = ''
}
// build mode
mut build_mode := pref.BuildMode.default_mode
mut mod := ''
joined_args := args.join(' ')
if joined_args.contains('build module ') {
build_mode = .build_module
os.chdir(vroot)
// v build module ~/v/os => os.o
mod_path := if dir.contains('vlib') { dir.all_after('vlib' + os.path_separator) } else if dir.starts_with('.\\') || dir.starts_with('./') { dir[2..] } else if dir.starts_with(os.path_separator) { dir.all_after(os.path_separator) } else { dir }
mod = mod_path.replace(os.path_separator, '.')
println('Building module "${mod}" (dir="$dir")...')
// out_name = '$TmpPath/vlib/${base}.o'
if !out_name.ends_with('.c') {
out_name = mod
}
// Cross compiling? Use separate dirs for each os
/*
if target_os != os.user_os() {
os.mkdir('$TmpPath/vlib/$target_os') or { panic(err) }
out_name = '$TmpPath/vlib/$target_os/${base}.o'
println('target_os=$target_os user_os=${os.user_os()}')
println('!Cross compiling $out_name')
}
*/
}
// `v -o dir/exec`, create "dir/" if it doesn't exist
if out_name.contains(os.path_separator) {
d := out_name.all_before_last(os.path_separator)
if !os.is_dir(d) {
println('creating a new directory "$d"')
os.mkdir(d)or{
panic(err)
}
}
}
// println('VROOT=$vroot')
cflags := cmdline.many_values(args, '-cflags').join(' ')
defines := cmdline.many_values(args, '-d')
compile_defines, compile_defines_all := parse_defines( defines )
rdir := os.realpath(dir)
rdir_name := filepath.filename(rdir)
if '-bare' in args {
println('V error: use -freestanding instead of -bare')
os.flush_stdout()
exit(1)
}
is_repl := '-repl' in args
ccompiler := cmdline.option(args, '-cc', '')
mut pref := &pref.Preferences{
os: pref.os_from_string(target_os)
is_so: '-shared' in args
is_solive: '-solive' in args
is_prod: '-prod' in args
is_verbose: '-verbose' in args || '--verbose' in args
is_debug: '-g' in args || '-cg' in args
is_vlines: '-g' in args && !('-cg' in args)
is_keep_c: '-keep_c' in args
is_pretty_c: '-pretty_c' in args
is_cache: '-cache' in args
is_stats: '-stats' in args
obfuscate: '-obf' in args
is_prof: '-prof' in args
is_live: '-live' in args
sanitize: '-sanitize' in args
// nofmt: '-nofmt' in args
show_c_cmd: '-show_c_cmd' in args
translated: 'translated' in args
is_run: 'run' in args
autofree: '-autofree' in args
compress: '-compress' in args
enable_globals: '--enable-globals' in args
fast: '-fast' in args
is_bare: '-freestanding' in args
x64: '-x64' in args
output_cross_c: '-output-cross-platform-c' in args
prealloc: '-prealloc' in args
is_repl: is_repl
build_mode: build_mode
cflags: cflags
ccompiler: ccompiler
building_v: !is_repl && (rdir_name == 'compiler' || rdir_name == 'v.v' || rdir_name == 'vfmt.v' || rdir_name == 'cmd/v' || dir.contains('vlib'))
// is_fmt: comptime_define == 'vfmt'
user_mod_path: user_mod_path
vlib_path: vlib_path
vpath: vpath
v2: '-v2' in args
vroot: vroot
out_name: out_name
path: dir
compile_defines: compile_defines
compile_defines_all: compile_defines_all
mod: mod
}
if pref.is_verbose || pref.is_debug {
println('C compiler=$pref.ccompiler')
}
$if !linux {
if pref.is_bare && !out_name.ends_with('.c') {
println('V error: -freestanding only works on Linux for now')
os.flush_stdout()
exit(1)
}
}
pref.fill_with_defaults()
// v.exe's parent directory should contain vlib
if !os.is_dir(pref.vlib_path) || !os.is_dir(pref.vlib_path + os.path_separator + 'builtin') {
// println('vlib not found, downloading it...')
/*
ret := os.system('git clone --depth=1 https://github.com/vlang/v .')
if ret != 0 {
println('failed to `git clone` vlib')
println('make sure you are online and have git installed')
exit(1)
}
*/
println('vlib not found. It should be next to the V executable.')
println('Go to https://vlang.io to install V.')
println('(os.executable=${os.executable()} vlib_path=$pref.vlib_path vexe_path=${vexe_path()}')
exit(1)
}
if pref.is_script && !os.exists(dir) {
println('`$dir` does not exist')
exit(1)
}
return compiler.new_v(pref)
}
fn find_c_compiler_thirdparty_options(args []string) string {
mut cflags := cmdline.many_values(args,'-cflags')
$if !windows {
cflags << '-fPIC'
}
if '-m32' in args {
cflags << '-m32'
}
return cflags.join(' ')
}
fn parse_defines(defines []string) ([]string,[]string) {
// '-d abc -d xyz=1 -d qwe=0' should produce:
// compile_defines: ['abc','xyz']
// compile_defines_all ['abc','xyz','qwe']
mut compile_defines := []string
mut compile_defines_all := []string
for dfn in defines {
dfn_parts := dfn.split('=')
if dfn_parts.len == 1 {
compile_defines << dfn
compile_defines_all << dfn
continue
}
if dfn_parts.len == 2 {
compile_defines_all << dfn_parts[0]
if dfn_parts[1] == '1' {
compile_defines << dfn_parts[0]
}
}
}
return compile_defines, compile_defines_all
}

78
cmd/v/flag.v 100644
View File

@ -0,0 +1,78 @@
// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module main
import os
const (
//list_of_flags contains a list of flags where an argument is expected past it.
list_of_flags = [
'-o', '-os', '-cc', '-cflags', '-d'
]
)
fn get_basic_command_and_option(args []string) (string, []string) {
mut option := []string
for i, arg in args {
if i == 0 {
//Skip executable
continue
}
if arg == '--' {
//End of list of options. The next one is the command.
if i+1 < os.args.len {
return os.args[i+1], option
}
//There's no option past this
return '', option
}
if arg in list_of_flags {
i++
continue
}
if arg[0] == `-` {
option << arg
continue
}
//It's not a flag. We did not skip it. It's a command.
return arg, option
}
//There's no arguments that were not part of a flag.
return '', option
}
fn non_empty(arg []string) []string {
return arg.filter(it != '')
}
fn join_flags_and_argument() []string {
vosargs := os.getenv('VOSARGS')
if vosargs != '' {
return non_empty(vosargs.split(' '))
}
mut args := []string
vflags := os.getenv('VFLAGS')
if vflags != '' {
args << os.args[0]
args << vflags.split(' ')
if os.args.len > 1 {
args << os.args[1..]
}
return non_empty(args)
}
return non_empty(os.args)
}
fn vexe_path() string {
vexe := os.getenv('VEXE')
if vexe != '' {
return vexe
}
real_vexe_path := os.realpath(os.executable())
os.setenv('VEXE', real_vexe_path, true)
return real_vexe_path
}

View File

@ -1,8 +1,16 @@
module compiler module main
pub const ( const (
help_text = 'Usage: v [options/commands] [file.v | directory] help_text = 'Usage: v [options/commands] [file.v | directory]
To run V in REPL mode, run V without any arguments.
To compile a directory/file, pass it as the only argument.
To run a directory/file, use `v run [file.v | directory]`. V will compile and run it for you.
This help message is only intended to be a quick start guide. For a comprehensive help message, use `v help --verbose`.'
verbose_help_text = 'Usage: v [options/commands] [file.v | directory]
When V is run without any arguments, it is run in REPL mode. When V is run without any arguments, it is run in REPL mode.
When given a .v file, it will be compiled. The executable will have the When given a .v file, it will be compiled. The executable will have the

View File

@ -1,23 +1,27 @@
module compiler // 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 ( import (
os compiler
filepath filepath
os
) )
pub fn launch_tool(tname string, cmdname string) { fn launch_tool(is_verbose bool, tname string, cmdname string) {
is_verbose := '-verbose' in os.args || '--verbose' in os.args
vexe := vexe_path() vexe := vexe_path()
vroot := filepath.dir(vexe) vroot := filepath.dir(vexe)
set_vroot_folder( vroot ) // needed by tools to find back v compiler.set_vroot_folder(vroot)
mut tname_index := os.args.index(cmdname) mut tname_index := os.args.index(cmdname)
if tname_index == -1 { if tname_index == -1 {
tname_index = os.args.len tname_index = os.args.len
} }
mut compilation_options := os.args[1..tname_index].clone() mut compilation_options := os.args[1..tname_index].clone()
tool_args := os.args[1..].join(' ') tool_args := os.args[1..].join(' ')
tool_exe := os.realpath('$vroot/tools/$tname') tool_exe := os.realpath('$vroot/cmd/tools/$tname')
tool_source := os.realpath('$vroot/tools/${tname}.v') tool_source := os.realpath('$vroot/cmd/tools/${tname}.v')
tool_command := '"$tool_exe" $tool_args' tool_command := '"$tool_exe" $tool_args'
if is_verbose { if is_verbose {
eprintln('launch_tool vexe : $vroot') eprintln('launch_tool vexe : $vroot')
@ -25,28 +29,29 @@ pub fn launch_tool(tname string, cmdname string) {
eprintln('launch_tool tool_args : $tool_args') eprintln('launch_tool tool_args : $tool_args')
eprintln('launch_tool tool_command: $tool_command') eprintln('launch_tool tool_command: $tool_command')
} }
mut tool_should_be_recompiled := false
mut should_compile := false
if !os.exists(tool_exe) { if !os.exists(tool_exe) {
// fresh checkout should_compile = true
tool_should_be_recompiled = true
} else { } else {
if os.file_last_mod_unix(tool_exe) <= os.file_last_mod_unix(vexe) { if os.file_last_mod_unix(tool_exe) <= os.file_last_mod_unix(vexe) {
// v was recompiled, maybe after v up ... // v was recompiled, maybe after v up ...
// rebuild the tool too just in case // rebuild the tool too just in case
tool_should_be_recompiled = true should_compile = true
} }
if os.file_last_mod_unix(tool_exe) <= os.file_last_mod_unix(tool_source) { if os.file_last_mod_unix(tool_exe) <= os.file_last_mod_unix(tool_source) {
// the user changed the source code of the tool // the user changed the source code of the tool
tool_should_be_recompiled = true should_compile = true
} }
} }
if is_verbose { if is_verbose {
eprintln('launch_tool tool_should_be_recompiled: $tool_should_be_recompiled') eprintln('launch_tool should_compile: $should_compile')
} }
if tool_should_be_recompiled { if should_compile {
if tname == 'vfmt' { compilation_options << ['-d', 'vfmt'] } if tname == 'vfmt' {
compilation_options << ['-d', 'vfmt']
}
compilation_args := compilation_options.join(' ') compilation_args := compilation_options.join(' ')
compilation_command := '"$vexe" $compilation_args "$tool_source"' compilation_command := '"$vexe" $compilation_args "$tool_source"'
if is_verbose { if is_verbose {
@ -61,5 +66,5 @@ pub fn launch_tool(tname string, cmdname string) {
eprintln('launch_tool running tool command: $tool_command ...') eprintln('launch_tool running tool command: $tool_command ...')
} }
exit( os.system(tool_command) ) exit(os.system(tool_command))
} }

31
cmd/v/symlink.v 100644
View File

@ -0,0 +1,31 @@
// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module main
import os
fn create_symlink() {
$if windows {
return
}
vexe := vexe_path()
mut link_path := '/usr/local/bin/v'
mut ret := os.exec('ln -sf $vexe $link_path') or { panic(err) }
if ret.exit_code == 0 {
println('Symlink "$link_path" has been created')
}
else if os.system('uname -o | grep -q \'[A/a]ndroid\'') == 0 {
println('Failed to create symlink "$link_path". Trying again with Termux path for Android.')
link_path = '/data/data/com.termux/files/usr/bin/v'
ret = os.exec('ln -sf $vexe $link_path') or { panic(err) }
if ret.exit_code == 0 {
println('Symlink "$link_path" has been created')
} else {
println('Failed to create symlink "$link_path". Try again with sudo.')
}
} else {
println('Failed to create symlink "$link_path". Try again with sudo.')
}
}

88
cmd/v/v.v 100644
View File

@ -0,0 +1,88 @@
// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module main
import (
compiler
os
)
const (
simple_cmd = [
'fmt',
'up',
'create',
'test', 'test-fmt', 'test-compiler',
'bin2v',
'repl',
'build-tools', 'build-examples', 'build-vbinaries'
]
)
fn main() {
arg := join_flags_and_argument()
command, option := get_basic_command_and_option(arg)
is_verbose := '-verbose' in arg || '--verbose' in arg
if '-v' in option || '--version' in option || command == 'version' {
// Print the version and exit.
version_hash := compiler.vhash()
println('V $compiler.Version $version_hash')
return
}
if '-h' in option || '--help' in option || command == 'help' {
if is_verbose {
println(verbose_help_text)
} else {
println(help_text)
}
return
}
if is_verbose {
eprintln('v args: $arg')
eprintln('v command: $command')
eprintln('v options: $option')
}
if command in simple_cmd {
//External tools
launch_tool(is_verbose, 'v' + command, command)
return
}
if command == 'run' || command == 'build' || command.ends_with('.v') || os.exists(command) {
compile(command, arg)
return
}
match command {
'', '-' {
if arg.len == 1 {
println('Running REPL as no arguments are provided. For usage information, use `v help`.')
}
launch_tool(is_verbose, 'vrepl', '')
}
'translate' {
println('Translating C to V will be available in V 0.3 (January)')
}
'search', 'install', 'update', 'remove' {
launch_tool(is_verbose, 'vpm', command)
}
'get' {
println('Use `v install` to install modules from vpm.vlang.io.')
}
'symlink' {
create_symlink()
}
'doc' {
println('Currently unimplemented')
}
else {
eprintln('v $command: unknown command\nRun "v help" for usage.')
exit(1)
}
}
}

View File

@ -35,8 +35,8 @@ if %ERRORLEVEL% NEQ 0 (
) )
echo Now using V to build V... echo Now using V to build V...
v2.exe -o v3.exe v.v v2.exe -o v3.exe cmd/v
v3.exe -o v.exe -prod v.v v3.exe -o v.exe -prod cmd/v
if %ERRORLEVEL% NEQ 0 ( if %ERRORLEVEL% NEQ 0 (
echo v.exe failed to compile itself - Create an issue at 'https://github.com/vlang' echo v.exe failed to compile itself - Create an issue at 'https://github.com/vlang'
exit /b 1 exit /b 1
@ -77,8 +77,8 @@ if %ERRORLEVEL% NEQ 0 (
) )
echo rebuild from source (twice, in case of C definitions changes) echo rebuild from source (twice, in case of C definitions changes)
v2.exe -cc msvc -o v3.exe v.v v2.exe -cc msvc -o v3.exe cmd/v
v3.exe -cc msvc -o v -prod v.v v3.exe -cc msvc -o v -prod cmd/v
if %ERRORLEVEL% NEQ 0 ( if %ERRORLEVEL% NEQ 0 (
echo V failed to build itself with error %ERRORLEVEL% echo V failed to build itself with error %ERRORLEVEL%
goto :compileerror goto :compileerror

140
v.v
View File

@ -1,140 +0,0 @@
// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module main
import (
compiler
benchmark
os
filepath
// v.types
// time
)
const (
known_commands = ['run', 'build', 'version', 'doc']
simple_tools = ['fmt',
'up',
'create',
'test', 'test-fmt', 'test-compiler',
'bin2v',
'repl',
'build-tools', 'build-examples', 'build-vbinaries']
)
fn main() {
is_verbose := '-verbose' in os.args || '--verbose' in os.args
// t := time.ticks()
// defer { println(time.ticks() - t) }
args := compiler.env_vflags_and_os_args()
options,command := compiler.get_v_options_and_main_command(args)
if is_verbose {
eprintln('v args: $args')
eprintln('v command: $command')
eprintln('v options: $options')
}
// external tool
if command in simple_tools {
compiler.launch_tool('v' + command, command)
return
}
// v run, v doc, etc
if !command.starts_with('-') && !command.ends_with('.v') && !os.exists(command) {
v_command(command, args)
}
// Print the version and exit.
if '-v' in options || '--version' in options {
version_hash := compiler.vhash()
println('V $compiler.Version $version_hash')
return
}
else if '-h' in options || '--help' in options {
println(compiler.help_text)
return
}
// No args? REPL
else if command == '' || (args.len == 2 && args[1] == '-') {
compiler.launch_tool('vrepl', '')
return
}
// Construct the V object from command line arguments
mut v := compiler.new_v(args)
if v.pref.is_verbose {
println(args)
}
if command == 'run' {
// always recompile for now, too error prone to skip recompilation otherwise
// for example for -repl usage, especially when piping lines to v
v.compile()
v.run_compiled_executable_and_exit()
}
mut tmark := benchmark.new_benchmark()
if v.pref.x64 {
v.compile_x64()
}
else if v.pref.v2 {
v.compile2()
}
else {
v.compile()
}
if v.pref.is_stats {
tmark.stop()
println('compilation took: ' + tmark.total_duration().str() + 'ms')
}
if v.pref.is_test {
v.run_compiled_executable_and_exit()
}
v.finalize_compilation()
}
fn v_command(command string, args []string) {
match command {
'', '.', 'run', 'build' {
// handled later in vlib/compiler/main.v
return
}
'version' {
println('V $compiler.Version $compiler.vhash()')
}
'help' {
println(compiler.help_text)
}
'translate' {
println('Translating C to V will be available in V 0.3 (January)')
}
'search', 'install', 'update', 'remove' {
compiler.launch_tool('vpm', command)
}
'get' {
println('use `v install` to install modules from vpm.vlang.io ')
}
'symlink' {
compiler.create_symlink()
}
'runrepl' {
// TODO: remove this after 2020/02/01
eprintln('`v runrepl` has been deprecated. Please use `v repl` instead.')
compiler.launch_tool('vrepl', 'runrepl')
}
'doc' {
vexe := os.executable()
vdir := filepath.dir(os.executable())
os.chdir(vdir)
mod := args.last()
os.system('$vexe build module vlib$os.path_separator' + args.last())
vhfile := filepath.join(compiler.v_modules_path,'vlib','${mod}.vh')
txt := os.read_file(vhfile) or {
panic(err)
}
println(txt)
// v.gen_doc_html_for_module(args.last())
}
else {
eprintln('v $command: unknown command\nRun "v help" for usage.')
exit(1)
}
}
exit(0)
}

View File

@ -166,8 +166,8 @@ fn (v mut V) new_parser_from_file(path string) Parser {
} }
} }
if v.compile_defines.len > 0 { if v.pref.compile_defines.len > 0 {
for cdefine in v.compile_defines { for cdefine in v.pref.compile_defines {
custom_path_ending := '_d_${cdefine}.v' custom_path_ending := '_d_${cdefine}.v'
if path.ends_with(custom_path_ending){ if path.ends_with(custom_path_ending){
path_platform = custom_path_ending path_platform = custom_path_ending
@ -215,8 +215,8 @@ fn (v mut V) new_parser(scanner &Scanner) Parser {
cgen: v.cgen cgen: v.cgen
//x64: v.x64 //x64: v.x64
pref: v.pref pref: v.pref
os: v.os os: v.pref.os
vroot: v.vroot vroot: v.pref.vroot
local_vars: [Var{ local_vars: [Var{
}].repeat(MaxLocalVars) }].repeat(MaxLocalVars)
import_table: new_import_table() import_table: new_import_table()
@ -443,7 +443,7 @@ fn (p mut Parser) parse(pass Pass) {
// //
p.fgen_nl() p.fgen_nl()
p.cgen.nogen = false p.cgen.nogen = false
if p.pref.build_mode == .build_module && p.mod != p.v.mod { if p.pref.build_mode == .build_module && p.mod != p.v.pref.mod {
// println('skipping $p.mod (v.mod = $p.v.mod)') // println('skipping $p.mod (v.mod = $p.v.mod)')
p.cgen.nogen = true p.cgen.nogen = true
// defer { p.cgen.nogen = false } // defer { p.cgen.nogen = false }
@ -453,7 +453,7 @@ fn (p mut Parser) parse(pass Pass) {
p.can_chash = p.mod in ['gg2', 'ui', 'uiold', 'darwin', 'clipboard', 'webview'] // TODO tmp remove p.can_chash = p.mod in ['gg2', 'ui', 'uiold', 'darwin', 'clipboard', 'webview'] // TODO tmp remove
// Import pass - the first and the smallest pass that only analyzes imports // Import pass - the first and the smallest pass that only analyzes imports
// if we are a building module get the full module name from v.mod // if we are a building module get the full module name from v.mod
fq_mod := if p.pref.build_mode == .build_module && p.v.mod.ends_with(p.mod) { p.v.mod } fq_mod := if p.pref.build_mode == .build_module && p.v.pref.mod.ends_with(p.mod) { p.v.pref.mod }
// fully qualify the module name, eg base64 to encoding.base64 // fully qualify the module name, eg base64 to encoding.base64
else { p.table.qualify_module(p.mod, p.file_path) } else { p.table.qualify_module(p.mod, p.file_path) }
p.table.register_module(fq_mod) p.table.register_module(fq_mod)

View File

@ -5,9 +5,9 @@ module compiler
import ( import (
os os
os.cmdline
time time
filepath filepath
v.pref
) )
fn todo() { fn todo() {
@ -34,8 +34,8 @@ fn (v mut V) cc() {
vdir := filepath.dir(vexe) vdir := filepath.dir(vexe)
// Just create a C/JavaScript file and exit // Just create a C/JavaScript file and exit
// for example: `v -o v.c compiler` // for example: `v -o v.c compiler`
ends_with_c := v.out_name.ends_with('.c') ends_with_c := v.pref.out_name.ends_with('.c')
ends_with_js := v.out_name.ends_with('.js') ends_with_js := v.pref.out_name.ends_with('.js')
if v.pref.is_pretty_c && !ends_with_js { if v.pref.is_pretty_c && !ends_with_js {
format_result := os.exec('clang-format -i -style=file "$v.out_name_c"') or { format_result := os.exec('clang-format -i -style=file "$v.out_name_c"') or {
@ -59,7 +59,7 @@ fn (v mut V) cc() {
println('V.js compiler not found, building...') println('V.js compiler not found, building...')
// Build V.js. Specifying `-os js` makes V include // Build V.js. Specifying `-os js` makes V include
// only _js.v files and ignore _c.v files. // only _js.v files and ignore _c.v files.
ret := os.system('$vexe -o $vjs_path -os js $vdir/v.v') ret := os.system('$vexe -o $vjs_path -os js $vdir/cmd/v')
if ret == 0 { if ret == 0 {
println('Done.') println('Done.')
} }
@ -68,21 +68,21 @@ fn (v mut V) cc() {
exit(1) exit(1)
} }
} }
ret := os.system('$vjs_path -o $v.out_name $v.dir') ret := os.system('$vjs_path -o $v.pref.out_name $v.pref.path')
if ret == 0 { if ret == 0 {
println('Done. Run it with `node $v.out_name`') println('Done. Run it with `node $v.pref.out_name`')
println('JS backend is at a very early stage.') println('JS backend is at a very early stage.')
} }
} }
} }
// v.out_name_c may be on a different partition than v.out_name // v.out_name_c may be on a different partition than v.out_name
os.mv_by_cp(v.out_name_c, v.out_name)or{ os.mv_by_cp(v.out_name_c, v.pref.out_name)or{
panic(err) panic(err)
} }
exit(0) exit(0)
} }
// Cross compiling for Windows // Cross compiling for Windows
if v.os == .windows { if v.pref.os == .windows {
$if !windows { $if !windows {
v.cc_windows_cross() v.cc_windows_cross()
return return
@ -136,29 +136,29 @@ fn (v mut V) cc() {
if !v.pref.is_so if !v.pref.is_so
&& v.pref.build_mode != .build_module && v.pref.build_mode != .build_module
&& os.user_os() == 'windows' && os.user_os() == 'windows'
&& !v.out_name.ends_with('.exe') && !v.pref.out_name.ends_with('.exe')
{ {
v.out_name += '.exe' v.pref.out_name += '.exe'
} }
// linux_host := os.user_os() == 'linux' // linux_host := os.user_os() == 'linux'
v.log('cc() isprod=$v.pref.is_prod outname=$v.out_name') v.log('cc() isprod=$v.pref.is_prod outname=$v.pref.out_name')
if v.pref.is_so { if v.pref.is_so {
a << '-shared -fPIC ' // -Wl,-z,defs' a << '-shared -fPIC ' // -Wl,-z,defs'
v.out_name = v.out_name + '.so' v.pref.out_name += '.so'
} }
if v.pref.is_bare { if v.pref.is_bare {
a << '-fno-stack-protector -static -ffreestanding -nostdlib' a << '-fno-stack-protector -static -ffreestanding -nostdlib'
} }
if v.pref.build_mode == .build_module { if v.pref.build_mode == .build_module {
// Create the modules & out directory if it's not there. // Create the modules & out directory if it's not there.
mut out_dir := if v.dir.starts_with('vlib') { '$v_modules_path${os.path_separator}cache${os.path_separator}$v.dir' } else { '$v_modules_path${os.path_separator}$v.dir' } mut out_dir := if v.pref.path.starts_with('vlib') { '$v_modules_path${os.path_separator}cache${os.path_separator}$v.pref.path' } else { '$v_modules_path${os.path_separator}$v.pref.path' }
pdir := out_dir.all_before_last(os.path_separator) pdir := out_dir.all_before_last(os.path_separator)
if !os.is_dir(pdir) { if !os.is_dir(pdir) {
os.mkdir_all(pdir) os.mkdir_all(pdir)
} }
v.out_name = '${out_dir}.o' // v.out_name v.pref.out_name = '${out_dir}.o' // v.out_name
println('Building ${v.out_name}...') println('Building ${v.pref.out_name}...')
} }
debug_mode := v.pref.is_debug debug_mode := v.pref.is_debug
mut debug_options := '-g' mut debug_options := '-g'
@ -198,7 +198,7 @@ fn (v mut V) cc() {
if debug_mode && os.user_os() != 'windows' { if debug_mode && os.user_os() != 'windows' {
a << ' -rdynamic ' // needed for nicer symbolic backtraces a << ' -rdynamic ' // needed for nicer symbolic backtraces
} }
if v.pref.ccompiler != 'msvc' && v.os != .freebsd { if v.pref.ccompiler != 'msvc' && v.pref.os != .freebsd {
a << '-Werror=implicit-function-declaration' a << '-Werror=implicit-function-declaration'
} }
for f in v.generate_hotcode_reloading_compiler_flags() { for f in v.generate_hotcode_reloading_compiler_flags() {
@ -269,24 +269,24 @@ fn (v mut V) cc() {
// Cross compiling windows // Cross compiling windows
// //
// Output executable name // Output executable name
a << '-o "$v.out_name"' a << '-o "$v.pref.out_name"'
if os.is_dir(v.out_name) { if os.is_dir(v.pref.out_name) {
verror("\'$v.out_name\' is a directory") verror("\'$v.pref.out_name\' is a directory")
} }
// macOS code can include objective C TODO remove once objective C is replaced with C // macOS code can include objective C TODO remove once objective C is replaced with C
if v.os == .mac { if v.pref.os == .mac {
a << '-x objective-c' a << '-x objective-c'
} }
// The C file we are compiling // The C file we are compiling
a << '"$v.out_name_c"' a << '"$v.out_name_c"'
if v.os == .mac { if v.pref.os == .mac {
a << '-x none' a << '-x none'
} }
// Min macos version is mandatory I think? // Min macos version is mandatory I think?
if v.os == .mac { if v.pref.os == .mac {
a << '-mmacosx-version-min=10.7' a << '-mmacosx-version-min=10.7'
} }
if v.os == .windows { if v.pref.os == .windows {
a << '-municode' a << '-municode'
} }
cflags := v.get_os_cflags() cflags := v.get_os_cflags()
@ -297,18 +297,18 @@ fn (v mut V) cc() {
a << libs a << libs
// Without these libs compilation will fail on Linux // Without these libs compilation will fail on Linux
// || os.user_os() == 'linux' // || os.user_os() == 'linux'
if !v.pref.is_bare && v.pref.build_mode != .build_module && v.os in [.linux, .freebsd, .openbsd, .netbsd, .dragonfly, .solaris, .haiku] { 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 ' a << '-lm -lpthread '
// -ldl is a Linux only thing. BSDs have it in libc. // -ldl is a Linux only thing. BSDs have it in libc.
if v.os == .linux { if v.pref.os == .linux {
a << ' -ldl ' a << ' -ldl '
} }
if v.os == .freebsd { if v.pref.os == .freebsd {
// FreeBSD: backtrace needs execinfo library while linking // FreeBSD: backtrace needs execinfo library while linking
a << ' -lexecinfo ' a << ' -lexecinfo '
} }
} }
if !v.pref.is_bare && v.os == .js && os.user_os() == 'linux' { if !v.pref.is_bare && v.pref.os == .js && os.user_os() == 'linux' {
a << '-lm' a << '-lm'
} }
args := a.join(' ') args := a.join(' ')
@ -351,6 +351,18 @@ start:
} }
if v.pref.is_debug { if v.pref.is_debug {
println(res.output) println(res.output)
verror("
==================
C error. This should never happen.
V compiler version: V $Version $vhash()
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 { else {
if res.output.len < 30 { if res.output.len < 30 {
@ -359,11 +371,19 @@ start:
q := res.output.all_after('error: ').limit(150) q := res.output.all_after('error: ').limit(150)
println('==================') println('==================')
println(q) println(q)
println('...')
println('==================') println('==================')
println('...\n(Use `v -cg` to print the entire error message)\n') 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")
} }
verror('C error. This should never happen. ' + '\nPlease create a GitHub issue: https://github.com/vlang/v/issues/new/choose')
} }
diff := time.ticks() - ticks diff := time.ticks() - ticks
// Print the C command // Print the C command
@ -402,16 +422,16 @@ start:
println('-compress does not work on Windows for now') println('-compress does not work on Windows for now')
return return
} }
ret := os.system('strip $v.out_name') ret := os.system('strip $v.pref.out_name')
if ret != 0 { if ret != 0 {
println('strip failed') println('strip failed')
return return
} }
// NB: upx --lzma can sometimes fail with NotCompressibleException // NB: upx --lzma can sometimes fail with NotCompressibleException
// See https://github.com/vlang/v/pull/3528 // See https://github.com/vlang/v/pull/3528
mut ret2 := os.system('upx --lzma -qqq $v.out_name') mut ret2 := os.system('upx --lzma -qqq $v.pref.out_name')
if ret2 != 0 { if ret2 != 0 {
ret2 = os.system('upx -qqq $v.out_name') ret2 = os.system('upx -qqq $v.pref.out_name')
} }
if ret2 != 0 { if ret2 != 0 {
println('upx failed') println('upx failed')
@ -430,10 +450,10 @@ start:
fn (c mut V) cc_windows_cross() { fn (c mut V) cc_windows_cross() {
println('Cross compiling for Windows...') println('Cross compiling for Windows...')
if !c.out_name.ends_with('.exe') { if !c.pref.out_name.ends_with('.exe') {
c.out_name = c.out_name + '.exe' c.pref.out_name += '.exe'
} }
mut args := '-o $c.out_name -w -L. ' mut args := '-o $c.pref.out_name -w -L. '
cflags := c.get_os_cflags() cflags := c.get_os_cflags()
// -I flags // -I flags
args += if c.pref.ccompiler == 'msvc' { cflags.c_options_before_target_msvc() } else { cflags.c_options_before_target() } args += if c.pref.ccompiler == 'msvc' { cflags.c_options_before_target_msvc() } else { cflags.c_options_before_target() }
@ -509,65 +529,12 @@ fn (c &V) build_thirdparty_obj_files() {
build_thirdparty_obj_file_with_msvc(flag.value, rest_of_module_flags) build_thirdparty_obj_file_with_msvc(flag.value, rest_of_module_flags)
} }
else { else {
build_thirdparty_obj_file(flag.value, rest_of_module_flags) c.build_thirdparty_obj_file(flag.value, rest_of_module_flags)
} }
} }
} }
} }
fn find_c_compiler() string {
args := env_vflags_and_os_args()
defaultcc := find_c_compiler_default()
return cmdline.option(args, '-cc', defaultcc)
}
fn find_c_compiler_default() string {
// fast_clang := '/usr/local/Cellar/llvm/8.0.0/bin/clang'
// if os.exists(fast_clang) {
// return fast_clang
// }
// TODO fix $if after 'string'
$if windows {
return 'gcc'
}
return 'cc'
}
fn find_c_compiler_thirdparty_options() string {
fullargs := env_vflags_and_os_args()
mut cflags := cmdline.many_values(fullargs,'-cflags')
$if !windows {
cflags << '-fPIC'
}
if '-m32' in fullargs {
cflags << '-m32'
}
return cflags.join(' ')
}
fn parse_defines(defines []string) ([]string,[]string) {
// '-d abc -d xyz=1 -d qwe=0' should produce:
// compile_defines: ['abc','xyz']
// compile_defines_all ['abc','xyz','qwe']
mut compile_defines := []string
mut compile_defines_all := []string
for dfn in defines {
dfn_parts := dfn.split('=')
if dfn_parts.len == 1 {
compile_defines << dfn
compile_defines_all << dfn
continue
}
if dfn_parts.len == 2 {
compile_defines_all << dfn_parts[0]
if dfn_parts[1] == '1' {
compile_defines << dfn_parts[0]
}
}
}
return compile_defines, compile_defines_all
}
fn missing_compiler_info() string { fn missing_compiler_info() string {
$if windows { $if windows {
return 'https://github.com/vlang/v/wiki/Installing-a-C-compiler-on-Windows' return 'https://github.com/vlang/v/wiki/Installing-a-C-compiler-on-Windows'

View File

@ -20,12 +20,12 @@ pub fn (c &CFlag) str() string {
fn (v &V) get_os_cflags() []CFlag { fn (v &V) get_os_cflags() []CFlag {
mut flags := []CFlag mut flags := []CFlag
mut ctimedefines := []string mut ctimedefines := []string
if v.compile_defines.len > 0 { if v.pref.compile_defines.len > 0 {
ctimedefines << v.compile_defines ctimedefines << v.pref.compile_defines
} }
for flag in v.table.cflags { for flag in v.table.cflags {
if flag.os == '' || (flag.os == 'linux' && v.os == .linux) || (flag.os == 'darwin' && v.os == .mac) || (flag.os == 'freebsd' && v.os == .freebsd) || (flag.os == 'windows' && v.os == .windows) { 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) {
flags << flag flags << flag
} }
if flag.os in ctimedefines { if flag.os in ctimedefines {

View File

@ -43,7 +43,7 @@ mut:
cut_pos int cut_pos int
} }
fn new_cgen(out_name_c string) &CGen { pub fn new_cgen(out_name_c string) &CGen {
path := out_name_c path := out_name_c
out := os.create(path)or{ out := os.create(path)or{
println('failed to create $path') println('failed to create $path')
@ -272,7 +272,7 @@ fn (g mut CGen) add_to_main(s string) {
g.fn_main = g.fn_main + s g.fn_main = g.fn_main + s
} }
fn build_thirdparty_obj_file(path string, moduleflags []CFlag) { fn (v &V) build_thirdparty_obj_file(path string, moduleflags []CFlag) {
obj_path := os.realpath(path) obj_path := os.realpath(path)
if os.exists(obj_path) { if os.exists(obj_path) {
return return
@ -288,11 +288,9 @@ fn build_thirdparty_obj_file(path string, moduleflags []CFlag) {
cfiles += '"' + os.realpath(parent + os.path_separator + file) + '" ' cfiles += '"' + os.realpath(parent + os.path_separator + file) + '" '
} }
} }
cc := find_c_compiler()
cc_thirdparty_options := find_c_compiler_thirdparty_options()
btarget := moduleflags.c_options_before_target() btarget := moduleflags.c_options_before_target()
atarget := moduleflags.c_options_after_target() atarget := moduleflags.c_options_after_target()
cmd := '$cc $cc_thirdparty_options $btarget -c -o "$obj_path" $cfiles $atarget ' cmd := '$v.pref.ccompiler $v.pref.third_party_option $btarget -c -o "$obj_path" $cfiles $atarget '
res := os.exec(cmd)or{ res := os.exec(cmd)or{
println('failed thirdparty object build cmd: $cmd') println('failed thirdparty object build cmd: $cmd')
verror(err) verror(err)

View File

@ -112,7 +112,7 @@ fn (p mut Parser) comp_time() {
else if name == 'clang' { else if name == 'clang' {
p.comptime_if_block('__clang__', not) p.comptime_if_block('__clang__', not)
} }
else if p.v.compile_defines_all.len > 0 && name in p.v.compile_defines_all { else if p.v.pref.compile_defines_all.len > 0 && name in p.v.pref.compile_defines_all {
// Support for *optional* custom compile defines, i.e.: // Support for *optional* custom compile defines, i.e.:
// //
// `[if custom]` => custom should be defined // `[if custom]` => custom should be defined
@ -249,7 +249,7 @@ fn (p mut Parser) chash() {
flag = flag.replace('@VLIB_PATH', p.pref.vlib_path) flag = flag.replace('@VLIB_PATH', p.pref.vlib_path)
flag = flag.replace('@VMOD', v_modules_path) flag = flag.replace('@VMOD', v_modules_path)
// p.log('adding flag "$flag"') // p.log('adding flag "$flag"')
_ = p.table.parse_cflag(flag, p.mod, p.v.compile_defines_all ) or { _ = p.table.parse_cflag(flag, p.mod, p.v.pref.compile_defines_all ) or {
p.error_with_token_index(err, p.cur_tok_index() - 1) p.error_with_token_index(err, p.cur_tok_index() - 1)
return return
} }

View File

@ -510,7 +510,7 @@ fn (p mut Parser) fn_decl() {
} }
if p.first_pass() && p.attr == 'live' && !(is_live || is_solive) { if p.first_pass() && p.attr == 'live' && !(is_live || is_solive) {
println('INFO: run `v -live $p.v.dir `, if you want to use [live] function $f.name .') println('INFO: run `v -live $p.v.pref.path `, if you want to use [live] function $f.name .')
} }
if p.is_vh || p.first_pass() || is_live || is_fn_header || skip_main_in_test { if p.is_vh || p.first_pass() || is_live || is_fn_header || skip_main_in_test {
@ -774,7 +774,7 @@ fn (p mut Parser) verify_fn_before_call(f &Fn) {
// p.tok == fn_name // p.tok == fn_name
fn (p mut Parser) fn_call(f mut Fn, method_ph int, receiver_var, receiver_type string) { fn (p mut Parser) fn_call(f mut Fn, method_ph int, receiver_var, receiver_type string) {
p.verify_fn_before_call(f) p.verify_fn_before_call(f)
is_comptime_define := f.comptime_define != '' && !(f.comptime_define in p.v.compile_defines ) is_comptime_define := f.comptime_define != '' && !(f.comptime_define in p.v.pref.compile_defines )
if is_comptime_define { if is_comptime_define {
p.cgen.nogen = true p.cgen.nogen = true
} }

View File

@ -10,10 +10,10 @@ fn (v &V) generate_hotcode_reloading_compiler_flags() []string {
mut a := []string mut a := []string
if v.pref.is_live || v.pref.is_so { if v.pref.is_live || v.pref.is_so {
// See 'man dlopen', and test running a GUI program compiled with -live // See 'man dlopen', and test running a GUI program compiled with -live
if (v.os == .linux || os.user_os() == 'linux') { if (v.pref.os == .linux || os.user_os() == 'linux') {
a << '-rdynamic' a << '-rdynamic'
} }
if (v.os == .mac || os.user_os() == 'mac') { if (v.pref.os == .mac || os.user_os() == 'mac') {
a << '-flat_namespace' a << '-flat_namespace'
} }
} }
@ -22,7 +22,7 @@ fn (v &V) generate_hotcode_reloading_compiler_flags() []string {
fn (v &V) generate_hotcode_reloading_declarations() { fn (v &V) generate_hotcode_reloading_declarations() {
mut cgen := v.cgen mut cgen := v.cgen
if v.os != .windows { if v.pref.os != .windows {
if v.pref.is_so { if v.pref.is_so {
cgen.genln('pthread_mutex_t live_fn_mutex;') cgen.genln('pthread_mutex_t live_fn_mutex;')
} }
@ -55,8 +55,8 @@ fn (v &V) generate_hotcode_reloading_main_caller() {
// We are in live code reload mode, so start the .so loader in the background // We are in live code reload mode, so start the .so loader in the background
mut cgen := v.cgen mut cgen := v.cgen
cgen.genln('') cgen.genln('')
file_base := filepath.filename(v.dir).replace('.v', '') file_base := filepath.filename(v.pref.path).replace('.v', '')
if v.os != .windows { if v.pref.os != .windows {
// unix: // unix:
so_name := file_base + '.so' so_name := file_base + '.so'
cgen.genln(' char *live_library_name = "$so_name";') cgen.genln(' char *live_library_name = "$so_name";')
@ -79,7 +79,7 @@ fn (v &V) generate_hot_reload_code() {
mut cgen := v.cgen mut cgen := v.cgen
// Hot code reloading // Hot code reloading
if v.pref.is_live { if v.pref.is_live {
mut file := os.realpath(v.dir) mut file := os.realpath(v.pref.path)
file_base := filepath.filename(file).replace('.v', '') file_base := filepath.filename(file).replace('.v', '')
so_name := file_base + '.so' so_name := file_base + '.so'
// Need to build .so file before building the live application // Need to build .so file before building the live application
@ -115,7 +115,7 @@ void lfnmutex_print(char *s){
} }
} }
') ')
if v.os != .windows { if v.pref.os != .windows {
cgen.genln(' cgen.genln('
void* live_lib=0; void* live_lib=0;
int load_so(byteptr path) { int load_so(byteptr path) {

View File

@ -5,7 +5,6 @@ module compiler
import ( import (
os os
os.cmdline
strings strings
filepath filepath
v.pref v.pref
@ -35,21 +34,15 @@ enum Pass {
main main
} }
struct V { pub struct V {
pub mut: pub mut:
os pref.OS // the OS to build for
out_name_c string // name of the temporary C file out_name_c string // name of the temporary C file
files []string // all V files that need to be parsed and compiled files []string // all V files that need to be parsed and compiled
dir string // directory (or file) being compiled (TODO rename to path?)
compiled_dir string // contains os.realpath() of the dir of the final file beeing compiled, or the dir itself when doing `v .` compiled_dir string // contains os.realpath() of the dir of the final file beeing compiled, or the dir itself when doing `v .`
table &Table // table with types, vars, functions etc table &Table // table with types, vars, functions etc
cgen &CGen // C code generator cgen &CGen // C code generator
//x64 &x64.Gen //x64 &x64.Gen
pref &pref.Preferences // all the preferences and settings extracted to a struct for reusability pref &pref.Preferences // all the preferences and settings extracted to a struct for reusability
lang_dir string // "~/code/v"
out_name string // "program.exe"
vroot string
mod string // module being built with -lib
parsers []Parser // file parsers parsers []Parser // file parsers
vgen_buf strings.Builder // temporary buffer for generated V code (.str() etc) 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 file_parser_idx map[string]int // map absolute file path to v.parsers index
@ -57,15 +50,33 @@ pub mut:
cached_mods []string cached_mods []string
module_lookup_paths []string module_lookup_paths []string
// -d vfmt and -d another=0 for `$if vfmt { will execute }` and `$if another { will NOT get here }` v_fmt_all bool // << input set by cmd/tools/vfmt.v
compile_defines []string // just ['vfmt'] v_fmt_file string // << file given by the user from cmd/tools/vfmt.v
compile_defines_all []string // contains both: ['vfmt','another']
v_fmt_all bool // << input set by tools/vfmt.v
v_fmt_file string // << file given by the user from tools/vfmt.v
v_fmt_file_result string // >> file with formatted output generated by vlib/compiler/vfmt.v v_fmt_file_result string // >> file with formatted output generated by vlib/compiler/vfmt.v
} }
pub fn new_v(pref &pref.Preferences) &V {
rdir := os.realpath(pref.path)
mut out_name_c := get_vtmp_filename(pref.out_name, '.tmp.c')
if pref.is_so {
out_name_c = get_vtmp_filename(pref.out_name, '.tmp.so.c')
}
mut vgen_buf := strings.new_builder(1000)
vgen_buf.writeln('module vgen\nimport strings')
return &V{
compiled_dir: if os.is_dir(rdir) { rdir } else { filepath.dir(rdir) }
table: new_table(pref.obfuscate)
out_name_c: out_name_c
cgen: new_cgen(out_name_c)
//x64: x64.new_gen(out_name)
pref: pref
vgen_buf: vgen_buf
}
}
// Should be called by main at the end of the compilation process, to cleanup // Should be called by main at the end of the compilation process, to cleanup
pub fn (v &V) finalize_compilation() { pub fn (v &V) finalize_compilation() {
// TODO remove // TODO remove
@ -166,7 +177,7 @@ pub fn (v mut V) compile() {
if v.pref.prealloc { if v.pref.prealloc {
cgen.genln('#define VPREALLOC (1)') cgen.genln('#define VPREALLOC (1)')
} }
if v.os == .js { if v.pref.os == .js {
cgen.genln('#define _VJS (1) ') cgen.genln('#define _VJS (1) ')
} }
v_hash := vhash() v_hash := vhash()
@ -189,11 +200,11 @@ pub fn (v mut V) compile() {
cgen.genln('#include <stdint.h>') cgen.genln('#include <stdint.h>')
} }
if v.compile_defines_all.len > 0 { if v.pref.compile_defines_all.len > 0 {
cgen.genln('') cgen.genln('')
cgen.genln('// All custom defines : ' + v.compile_defines_all.join(',')) cgen.genln('// All custom defines : ' + v.pref.compile_defines_all.join(','))
cgen.genln('// Turned ON custom defines: ' + v.compile_defines.join(',')) cgen.genln('// Turned ON custom defines: ' + v.pref.compile_defines.join(','))
for cdefine in v.compile_defines { for cdefine in v.pref.compile_defines {
cgen.genln('#define CUSTOM_DEFINE_${cdefine}') cgen.genln('#define CUSTOM_DEFINE_${cdefine}')
} }
cgen.genln('//') cgen.genln('//')
@ -234,7 +245,7 @@ pub fn (v mut V) compile() {
if '-debug_alloc' in os.args { if '-debug_alloc' in os.args {
cgen.genln('#define DEBUG_ALLOC 1') cgen.genln('#define DEBUG_ALLOC 1')
} }
if v.pref.is_live && v.os != .windows { if v.pref.is_live && v.pref.os != .windows {
cgen.includes << '#include <dlfcn.h>' cgen.includes << '#include <dlfcn.h>'
} }
// cgen.genln('/*================================== FNS =================================*/') // cgen.genln('/*================================== FNS =================================*/')
@ -261,7 +272,7 @@ pub fn (v mut V) compile() {
vgen_parser.parse(.main) vgen_parser.parse(.main)
// Generate .vh if we are building a module // Generate .vh if we are building a module
if v.pref.build_mode == .build_module { if v.pref.build_mode == .build_module {
generate_vh(v.dir) generate_vh(v.pref.path)
} }
// All definitions // All definitions
mut def := strings.new_builder(10000) // Avoid unnecessary allocations mut def := strings.new_builder(10000) // Avoid unnecessary allocations
@ -319,7 +330,7 @@ pub fn (v mut V) compile2() {
println(v.files) println(v.files)
} }
mut b := v.new_v2() mut b := v.new_v2()
b.build_c(v.files, v.out_name) b.build_c(v.files, v.pref.out_name)
v.cc() v.cc()
} }
@ -329,18 +340,18 @@ pub fn (v mut V) compile_x64() {
println('You are not on a Linux system, so you will not ' + 'be able to run the resulting executable') println('You are not on a Linux system, so you will not ' + 'be able to run the resulting executable')
} }
//v.files << v.v_files_from_dir(filepath.join(v.pref.vlib_path,'builtin','bare')) //v.files << v.v_files_from_dir(filepath.join(v.pref.vlib_path,'builtin','bare'))
v.files << v.dir v.files << v.pref.path
v.set_module_lookup_paths() v.set_module_lookup_paths()
mut b := v.new_v2() mut b := v.new_v2()
// move all this logic to v2 // move all this logic to v2
b.build_x64(v.files, v.out_name) b.build_x64(v.files, v.pref.out_name)
} }
// make v2 from v1 // make v2 from v1
fn (v &V) new_v2() builder.Builder { fn (v &V) new_v2() builder.Builder {
mut b := builder.new_builder(v.pref) mut b := builder.new_builder(v.pref)
b = { b| b = { b|
os: v.os, os: v.pref.os,
module_path: v_modules_path, module_path: v_modules_path,
compiled_dir: v.compiled_dir, compiled_dir: v.compiled_dir,
module_search_paths: v.module_lookup_paths module_search_paths: v.module_lookup_paths
@ -356,7 +367,7 @@ fn (v mut V) generate_init() {
nogen := v.cgen.nogen nogen := v.cgen.nogen
v.cgen.nogen = false v.cgen.nogen = false
consts_init_body := v.cgen.consts_init.join_lines() consts_init_body := v.cgen.consts_init.join_lines()
init_fn_name := mod_gen_name(v.mod) + '__init_consts' init_fn_name := mod_gen_name(v.pref.mod) + '__init_consts'
v.cgen.genln('void ${init_fn_name}();\nvoid ${init_fn_name}() {\n$consts_init_body\n}') v.cgen.genln('void ${init_fn_name}();\nvoid ${init_fn_name}() {\n$consts_init_body\n}')
v.cgen.nogen = nogen v.cgen.nogen = nogen
} }
@ -482,7 +493,7 @@ pub fn (v mut V) generate_main() {
// Generate a C `main`, which calls every single test function // Generate a C `main`, which calls every single test function
v.gen_main_start(false) v.gen_main_start(false)
if v.pref.is_stats { if v.pref.is_stats {
cgen.genln('BenchedTests bt = main__start_testing(${test_fn_names.len},tos3("$v.dir"));') cgen.genln('BenchedTests bt = main__start_testing(${test_fn_names.len},tos3("$v.pref.path"));')
} }
for tfname in test_fn_names { for tfname in test_fn_names {
if v.pref.is_stats { if v.pref.is_stats {
@ -513,7 +524,7 @@ pub fn (v mut V) generate_main() {
} }
pub fn (v mut V) gen_main_start(add_os_args bool) { pub fn (v mut V) gen_main_start(add_os_args bool) {
if v.os == .windows { if v.pref.os == .windows {
if 'glfw' in v.table.imports { if 'glfw' in v.table.imports {
// GUI application // GUI application
v.cgen.genln('int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, LPWSTR cmd_line, int show_cmd) { ') v.cgen.genln('int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, LPWSTR cmd_line, int show_cmd) { ')
@ -531,7 +542,7 @@ pub fn (v mut V) gen_main_start(add_os_args bool) {
} }
v.cgen.genln(' init();') v.cgen.genln(' init();')
if add_os_args && 'os' in v.table.imports { if add_os_args && 'os' in v.table.imports {
if v.os == .windows { if v.pref.os == .windows {
v.cgen.genln(' os__args = os__init_os_args_wide(argc, argv);') v.cgen.genln(' os__args = os__init_os_args_wide(argc, argv);')
} else { } else {
v.cgen.genln(' os__args = os__init_os_args(argc, (byteptr*)argv);') v.cgen.genln(' os__args = os__init_os_args(argc, (byteptr*)argv);')
@ -547,45 +558,12 @@ pub fn (v mut V) gen_main_end(return_statement string) {
v.cgen.genln('}') v.cgen.genln('}')
} }
pub fn final_target_out_name(out_name string) string {
$if windows {
return out_name.replace('/', '\\') + '.exe'
}
return if out_name.starts_with('/') { out_name } else { './' + out_name }
}
pub fn (v V) run_compiled_executable_and_exit() {
args := env_vflags_and_os_args()
if v.pref.is_verbose {
println('============ running $v.out_name ============')
}
mut cmd := '"' + final_target_out_name(v.out_name).replace('.exe', '') + '"'
args_after_no_options := cmdline.only_non_options( cmdline.after(args,['run','test']) )
if args_after_no_options.len > 1 {
cmd += ' ' + args_after_no_options[1..].join(' ')
}
if v.pref.is_test {
ret := os.system(cmd)
if ret != 0 {
exit(1)
}
}
if v.pref.is_run {
ret := os.system(cmd)
// TODO: make the runner wrapping as transparent as possible
// (i.e. use execve when implemented). For now though, the runner
// just returns the same exit code as the child process.
exit(ret)
}
exit(0)
}
pub fn (v &V) v_files_from_dir(dir string) []string { pub fn (v &V) v_files_from_dir(dir string) []string {
mut res := []string mut res := []string
if !os.exists(dir) { if !os.exists(dir) {
if dir == 'compiler' && os.is_dir('vlib') { if dir == 'compiler' && os.is_dir('vlib') {
println('looks like you are trying to build V with an old command') println('looks like you are trying to build V with an old command')
println('use `v -o v v.v` instead of `v -o v compiler`') println('use `v -o v cmd/v` instead of `v -o v compiler`')
} }
verror("$dir doesn't exist") verror("$dir doesn't exist")
} }
@ -606,27 +584,27 @@ pub fn (v &V) v_files_from_dir(dir string) []string {
if file.ends_with('_test.v') { if file.ends_with('_test.v') {
continue continue
} }
if (file.ends_with('_win.v') || file.ends_with('_windows.v')) && v.os != .windows { if (file.ends_with('_win.v') || file.ends_with('_windows.v')) && v.pref.os != .windows {
continue continue
} }
if (file.ends_with('_lin.v') || file.ends_with('_linux.v')) && v.os != .linux { if (file.ends_with('_lin.v') || file.ends_with('_linux.v')) && v.pref.os != .linux {
continue continue
} }
if (file.ends_with('_mac.v') || file.ends_with('_darwin.v')) && v.os != .mac { if (file.ends_with('_mac.v') || file.ends_with('_darwin.v')) && v.pref.os != .mac {
continue continue
} }
if file.ends_with('_nix.v') && v.os == .windows { if file.ends_with('_nix.v') && v.pref.os == .windows {
continue continue
} }
if file.ends_with('_js.v') && v.os != .js { if file.ends_with('_js.v') && v.pref.os != .js {
continue continue
} }
if file.ends_with('_c.v') && v.os == .js { if file.ends_with('_c.v') && v.pref.os == .js {
continue continue
} }
if v.compile_defines_all.len > 0 && file.contains('_d_') { if v.pref.compile_defines_all.len > 0 && file.contains('_d_') {
mut allowed := false mut allowed := false
for cdefine in v.compile_defines { for cdefine in v.pref.compile_defines {
file_postfix := '_d_${cdefine}.v' file_postfix := '_d_${cdefine}.v'
if file.ends_with(file_postfix) { if file.ends_with(file_postfix) {
allowed = true allowed = true
@ -740,15 +718,15 @@ pub fn (v &V) get_builtin_files() []string {
// get user files // get user files
pub fn (v &V) get_user_files() []string { pub fn (v &V) get_user_files() []string {
mut dir := v.dir mut dir := v.pref.path
v.log('get_v_files($dir)') v.log('get_v_files($dir)')
// Need to store user files separately, because they have to be added after // 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 // libs, but we dont know which libs need to be added yet
mut user_files := []string mut user_files := []string
// See tools/preludes/README.md for more info about what preludes are // See cmd/tools/preludes/README.md for more info about what preludes are
vroot := filepath.dir(vexe_path()) vroot := filepath.dir(vexe_path())
preludes_path := filepath.join(vroot,'tools','preludes') preludes_path := filepath.join(vroot,'cmd','tools','preludes')
if v.pref.is_live { if v.pref.is_live {
user_files << filepath.join(preludes_path,'live_main.v') user_files << filepath.join(preludes_path,'live_main.v')
} }
@ -858,284 +836,6 @@ pub fn (v &V) log(s string) {
println(s) println(s)
} }
pub fn new_v(args []string) &V {
// Create modules dirs if they are missing
if !os.is_dir(v_modules_path) {
os.mkdir(v_modules_path)or{
panic(err)
}
os.mkdir('$v_modules_path${os.path_separator}cache')or{
panic(err)
}
}
// optional, custom modules search path
user_mod_path := cmdline.option(args, '-user_mod_path', '')
// Location of all vlib files
vroot := filepath.dir(vexe_path())
vlib_path := cmdline.option(args, '-vlib-path', filepath.join(vroot,'vlib'))
vpath := cmdline.option(args, '-vpath', v_modules_path)
mut vgen_buf := strings.new_builder(1000)
vgen_buf.writeln('module vgen\nimport strings')
target_os := cmdline.option(args, '-os', '')
mut out_name := cmdline.option(args, '-o', 'a.out')
mut dir := args.last()
if 'run' in args {
args_after_run := cmdline.only_non_options( cmdline.after(args,['run']) )
dir = if args_after_run.len>0 { args_after_run[0] } else { '' }
}
if dir.ends_with(os.path_separator) {
dir = dir.all_before_last(os.path_separator)
}
if dir.starts_with('.$os.path_separator') {
dir = dir[2..]
}
if args.len < 2 {
dir = ''
}
// build mode
mut build_mode := pref.BuildMode.default_mode
mut mod := ''
joined_args := args.join(' ')
if joined_args.contains('build module ') {
build_mode = .build_module
os.chdir(vroot)
// v build module ~/v/os => os.o
mod_path := if dir.contains('vlib') { dir.all_after('vlib' + os.path_separator) } else if dir.starts_with('.\\') || dir.starts_with('./') { dir[2..] } else if dir.starts_with(os.path_separator) { dir.all_after(os.path_separator) } else { dir }
mod = mod_path.replace(os.path_separator, '.')
println('Building module "${mod}" (dir="$dir")...')
// out_name = '$TmpPath/vlib/${base}.o'
if !out_name.ends_with('.c') {
out_name = mod
}
// Cross compiling? Use separate dirs for each os
/*
if target_os != os.user_os() {
os.mkdir('$TmpPath/vlib/$target_os') or { panic(err) }
out_name = '$TmpPath/vlib/$target_os/${base}.o'
println('target_os=$target_os user_os=${os.user_os()}')
println('!Cross compiling $out_name')
}
*/
}
is_test := dir.ends_with('_test.v')
is_script := dir.ends_with('.v') || dir.ends_with('.vsh')
if is_script && !os.exists(dir) {
println('`$dir` does not exist')
exit(1)
}
// No -o provided? foo.v => foo
if out_name == 'a.out' && dir.ends_with('.v') && dir != '.v' {
out_name = dir[..dir.len - 2]
// Building V? Use v2, since we can't overwrite a running
// executable on Windows + the precompiled V is more
// optimized.
if out_name == 'v' && os.is_dir('vlib/compiler') {
println('Saving the resulting V executable in `./v2`')
println('Use `v -o v v.v` if you want to replace current ' + 'V executable.')
out_name = 'v2'
}
}
// if we are in `/foo` and run `v .`, the executable should be `foo`
if dir == '.' && out_name == 'a.out' {
base := os.getwd().all_after(os.path_separator)
out_name = base.trim_space()
}
// `v -o dir/exec`, create "dir/" if it doesn't exist
if out_name.contains(os.path_separator) {
d := out_name.all_before_last(os.path_separator)
if !os.is_dir(d) {
println('creating a new directory "$d"')
os.mkdir(d)or{
panic(err)
}
}
}
mut _os := pref.OS.mac
// No OS specifed? Use current system
if target_os == '' {
$if linux {
_os = .linux
}
$if macos {
_os = .mac
}
$if windows {
_os = .windows
}
$if freebsd {
_os = .freebsd
}
$if openbsd {
_os = .openbsd
}
$if netbsd {
_os = .netbsd
}
$if dragonfly {
_os = .dragonfly
}
$if solaris {
_os = .solaris
}
$if haiku {
_os = .haiku
}
}
else {
_os = os_from_string(target_os)
}
// println('VROOT=$vroot')
// v.exe's parent directory should contain vlib
if !os.is_dir(vlib_path) || !os.is_dir(vlib_path + os.path_separator + 'builtin') {
// println('vlib not found, downloading it...')
/*
ret := os.system('git clone --depth=1 https://github.com/vlang/v .')
if ret != 0 {
println('failed to `git clone` vlib')
println('make sure you are online and have git installed')
exit(1)
}
*/
println('vlib not found. It should be next to the V executable.')
println('Go to https://vlang.io to install V.')
println('(os.executable=${os.executable()} vlib_path=$vlib_path vexe_path=${vexe_path()}')
exit(1)
}
mut out_name_c := get_vtmp_filename(out_name, '.tmp.c')
cflags := cmdline.many_values(args, '-cflags').join(' ')
defines := cmdline.many_values(args, '-d')
compile_defines, compile_defines_all := parse_defines( defines )
rdir := os.realpath(dir)
rdir_name := filepath.filename(rdir)
if '-bare' in args {
verror('use -freestanding instead of -bare')
}
obfuscate := '-obf' in args
is_repl := '-repl' in args
pref := &pref.Preferences{
is_test: is_test
is_script: is_script
is_so: '-shared' in args
is_solive: '-solive' in args
is_prod: '-prod' in args
is_verbose: '-verbose' in args || '--verbose' in args
is_debug: '-g' in args || '-cg' in args
is_vlines: '-g' in args && !('-cg' in args)
is_keep_c: '-keep_c' in args
is_pretty_c: '-pretty_c' in args
is_cache: '-cache' in args
is_stats: '-stats' in args
obfuscate: obfuscate
is_prof: '-prof' in args
is_live: '-live' in args
sanitize: '-sanitize' in args
// nofmt: '-nofmt' in args
show_c_cmd: '-show_c_cmd' in args
translated: 'translated' in args
is_run: 'run' in args
autofree: '-autofree' in args
compress: '-compress' in args
enable_globals: '--enable-globals' in args
fast: '-fast' in args
is_bare: '-freestanding' in args
x64: '-x64' in args
output_cross_c: '-output-cross-platform-c' in args
prealloc: '-prealloc' in args
is_repl: is_repl
build_mode: build_mode
cflags: cflags
ccompiler: find_c_compiler()
building_v: !is_repl && (rdir_name == 'compiler' || rdir_name == 'v.v' || rdir_name == 'vfmt.v' || dir.contains('vlib'))
// is_fmt: comptime_define == 'vfmt'
user_mod_path: user_mod_path
vlib_path: vlib_path
vpath: vpath
v2: '-v2' in args
}
if pref.is_verbose || pref.is_debug {
println('C compiler=$pref.ccompiler')
}
if pref.is_so {
out_name_c = get_vtmp_filename(out_name, '.tmp.so.c')
}
$if !linux {
if pref.is_bare && !out_name.ends_with('.c') {
verror('-freestanding only works on Linux for now')
}
}
return &V{
os: _os
out_name: out_name
dir: dir
compiled_dir: if os.is_dir(rdir) { rdir } else { filepath.dir(rdir) }
lang_dir: vroot
table: new_table(obfuscate)
out_name_c: out_name_c
cgen: new_cgen(out_name_c)
//x64: x64.new_gen(out_name)
vroot: vroot
pref: pref
mod: mod
vgen_buf: vgen_buf
compile_defines: compile_defines
compile_defines_all: compile_defines_all
}
}
fn non_empty(a []string) []string {
return a.filter(it.len != 0)
}
pub fn env_vflags_and_os_args() []string {
vosargs := os.getenv('VOSARGS')
if '' != vosargs {
return non_empty(vosargs.split(' '))
}
mut args := []string
vflags := os.getenv('VFLAGS')
if '' != vflags {
args << os.args[0]
args << vflags.split(' ')
if os.args.len > 1 {
args << os.args[1..]
}
}
else {
args << os.args
}
return non_empty(args)
}
pub fn create_symlink() {
$if windows {
return
}
vexe := vexe_path()
mut link_path := '/usr/local/bin/v'
mut ret := os.exec('ln -sf $vexe $link_path') or { panic(err) }
if ret.exit_code == 0 {
println('Symlink "$link_path" has been created')
}
else if os.system('uname -o | grep -q \'[A/a]ndroid\'') == 0 {
println('Failed to create symlink "$link_path". Trying again with Termux path for Android.')
link_path = '/data/data/com.termux/files/usr/bin/v'
ret = os.exec('ln -sf $vexe $link_path') or { panic(err) }
if ret.exit_code == 0 {
println('Symlink "$link_path" has been created')
} else {
println('Failed to create symlink "$link_path". Try again with sudo.')
}
} else {
println('Failed to create symlink "$link_path". Try again with sudo.')
}
}
pub fn vexe_path() string { pub fn vexe_path() string {
vexe := os.getenv('VEXE') vexe := os.getenv('VEXE')
if '' != vexe { if '' != vexe {
@ -1223,12 +923,3 @@ pub fn set_vroot_folder(vroot_path string) {
vname := if os.user_os() == 'windows' { 'v.exe' } else { 'v' } vname := if os.user_os() == 'windows' { 'v.exe' } else { 'v' }
os.setenv('VEXE', os.realpath([vroot_path, vname].join(os.path_separator)), true) os.setenv('VEXE', os.realpath([vroot_path, vname].join(os.path_separator)), true)
} }
pub fn new_v_compiler_with_args(args []string) &V {
vexe := vexe_path()
mut allargs := [vexe]
allargs << args
os.setenv('VOSARGS', allargs.join(' '), true)
return new_v(allargs)
}

View File

@ -7,6 +7,7 @@ import (
strings strings
os os
filepath filepath
v.pref
) )
/* /*
.vh generation logic. .vh generation logic.
@ -52,7 +53,11 @@ fn generate_vh(mod string) {
filtered := vfiles.filter(it.ends_with('.v') && !it.ends_with('test.v') && !it.ends_with('_windows.v') && !it.ends_with('_win.v') && !it.ends_with('_lin.v') && !it.contains('${os.path_separator}examples') && !it.contains('_js.v') && !it.contains('_bare.v') && !it.contains('${os.path_separator}js')) // TODO merge once filter allows it filtered := vfiles.filter(it.ends_with('.v') && !it.ends_with('test.v') && !it.ends_with('_windows.v') && !it.ends_with('_win.v') && !it.ends_with('_lin.v') && !it.contains('${os.path_separator}examples') && !it.contains('_js.v') && !it.contains('_bare.v') && !it.contains('${os.path_separator}js')) // TODO merge once filter allows it
// println('f:') // println('f:')
// println(filtered) // println(filtered)
mut v := new_v(['foo.v']) mut pref := &pref.Preferences {
path: 'foo.v'
}
pref.fill_with_defaults()
mut v := new_v(pref)
// v.pref.generating_vh = true // v.pref.generating_vh = true
mut g := VhGen{ mut g := VhGen{
consts: strings.new_builder(1000) consts: strings.new_builder(1000)
@ -166,4 +171,3 @@ fn (g mut VhGen) generate_type() {
// g.i = old // g.i = old
// g.i-- // g.i--
} }

View File

@ -3,11 +3,14 @@
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
module compiler module compiler
import os import (
import filepath filepath
os
v.pref
)
pub const ( pub const (
v_modules_path = os.home_dir() + '.vmodules' v_modules_path = pref.default_module_path
) )
// Holds import information scoped to the parsed file // Holds import information scoped to the parsed file
struct ImportTable { struct ImportTable {

View File

@ -206,16 +206,16 @@ pub fn (v mut V) cc_msvc() {
a << '/MDd' a << '/MDd'
} }
if v.pref.is_so { if v.pref.is_so {
if !v.out_name.ends_with('.dll') { if !v.pref.out_name.ends_with('.dll') {
v.out_name = v.out_name + '.dll' v.pref.out_name += '.dll'
} }
// Build dll // Build dll
a << '/LD' a << '/LD'
} }
else if !v.out_name.ends_with('.exe') { else if !v.pref.out_name.ends_with('.exe') {
v.out_name = v.out_name + '.exe' v.pref.out_name += '.exe'
} }
v.out_name = os.realpath(v.out_name) v.pref.out_name = os.realpath(v.pref.out_name)
// alibs := []string // builtin.o os.o http.o etc // alibs := []string // builtin.o os.o http.o etc
if v.pref.build_mode == .build_module { if v.pref.build_mode == .build_module {
// Compile only // Compile only
@ -263,7 +263,7 @@ pub fn (v mut V) cc_msvc() {
a << real_libs.join(' ') a << real_libs.join(' ')
a << '/link' a << '/link'
a << '/NOLOGO' a << '/NOLOGO'
a << '/OUT:"$v.out_name"' a << '/OUT:"$v.pref.out_name"'
a << '/LIBPATH:"$r.ucrt_lib_path"' a << '/LIBPATH:"$r.ucrt_lib_path"'
a << '/LIBPATH:"$r.um_lib_path"' a << '/LIBPATH:"$r.um_lib_path"'
a << '/LIBPATH:"$r.vs_lib_path"' a << '/LIBPATH:"$r.vs_lib_path"'

View File

@ -6,7 +6,7 @@ module compiler
import os import os
import filepath import filepath
pub fn get_vtmp_folder() string { fn get_vtmp_folder() string {
vtmp := filepath.join(os.tmpdir(),'v') vtmp := filepath.join(os.tmpdir(),'v')
if !os.is_dir(vtmp) { if !os.is_dir(vtmp) {
os.mkdir(vtmp)or{ os.mkdir(vtmp)or{
@ -16,7 +16,7 @@ pub fn get_vtmp_folder() string {
return vtmp return vtmp
} }
pub fn get_vtmp_filename(base_file_name string, postfix string) string { fn get_vtmp_filename(base_file_name string, postfix string) string {
vtmp := get_vtmp_folder() vtmp := get_vtmp_folder()
return os.realpath(filepath.join(vtmp,filepath.filename(os.realpath(base_file_name)) + postfix)) return os.realpath(filepath.join(vtmp,filepath.filename(os.realpath(base_file_name)) + postfix))
} }

View File

@ -11,7 +11,7 @@
// branch prediction hints. the C version will be // branch prediction hints. the C version will be
// removed once the perfomance is matched. // removed once the perfomance is matched.
// you can test performance by running: // you can test performance by running:
// `v run tools/bench/wyhash.v` // `v run cmd/tools/bench/wyhash.v`
// try running with and without the `-prod` flag // try running with and without the `-prod` flag
module wyhash module wyhash

View File

@ -102,7 +102,7 @@ pub fn (b &Builder) v_files_from_dir(dir string) []string {
if !os.exists(dir) { if !os.exists(dir) {
if dir == 'compiler' && os.is_dir('vlib') { if dir == 'compiler' && os.is_dir('vlib') {
println('looks like you are trying to build V with an old command') println('looks like you are trying to build V with an old command')
println('use `v -o v v.v` instead of `v -o v compiler`') println('use `v -o v cmd/v` instead of `v -o v compiler`')
} }
verror("$dir doesn't exist") verror("$dir doesn't exist")
} }

View File

@ -0,0 +1,78 @@
// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module pref
import (
filepath
os
)
pub const (
default_module_path = os.home_dir() + '.vmodules'
)
pub fn (p mut Preferences) fill_with_defaults() {
if p.vroot == '' {
// Location of all vlib files
p.vroot = filepath.dir(vexe_path())
}
if p.vlib_path == '' {
p.vlib_path = filepath.join(p.vroot,'vlib')
}
if p.vpath == '' {
p.vpath = default_module_path
}
if p.out_name == ''{
rpath := os.realpath(p.path)
filename := filepath.filename(rpath).trim_space()
mut base := filename.all_before_last('.')
if base == '' {
// The file name is just `.v` or `.vsh` or `.*`
base = filename
}
target_dir := if os.is_dir(rpath) { rpath } else { filepath.dir(rpath) }
p.out_name = filepath.join(target_dir, base)
if rpath == '$p.vroot/cmd/v' && os.is_dir('vlib/compiler') {
// Building V? Use v2, since we can't overwrite a running
// executable on Windows + the precompiled V is more
// optimized.
println('Saving the resulting V executable in `./v2`')
println('Use `v -o v cmd/v` if you want to replace current ' + 'V executable.')
p.out_name = 'v2'
}
}
if p.os == ._auto {
// No OS specifed? Use current system
p.os = get_host_os()
}
if p.ccompiler == '' {
p.ccompiler = default_c_compiler()
}
p.is_test = p.path.ends_with('_test.v')
p.is_script = p.path.ends_with('.v') || p.path.ends_with('.vsh')
}
fn default_c_compiler() string {
// fast_clang := '/usr/local/Cellar/llvm/8.0.0/bin/clang'
// if os.exists(fast_clang) {
// return fast_clang
// }
// TODO fix $if after 'string'
$if windows {
return 'gcc'
}
return 'cc'
}
//TODO Remove code duplication
fn vexe_path() string {
vexe := os.getenv('VEXE')
if vexe != '' {
return vexe
}
real_vexe_path := os.realpath(os.executable())
os.setenv('VEXE', real_vexe_path, true)
return real_vexe_path
}

147
vlib/v/pref/os.v 100644
View File

@ -0,0 +1,147 @@
// 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 pref
pub enum OS {
_auto // Reserved so .mac cannot be misunderstood as auto
mac
linux
windows
freebsd
openbsd
netbsd
dragonfly
js // TODO
android
solaris
haiku
}
// Helper function to convert string names to OS enum
pub fn os_from_string(os_str string) OS {
match os_str {
'linux' {
return .linux
}
'windows' {
return .windows
}
'mac' {
return .mac
}
'macos' {
return .mac
}
'freebsd' {
return .freebsd
}
'openbsd' {
return .openbsd
}
'netbsd' {
return .netbsd
}
'dragonfly' {
return .dragonfly
}
'js' {
return .js
}
'solaris' {
return .solaris
}
'android' {
return .android
}
'haiku' {
return .haiku
}
'linux_or_macos' {
return .linux
}
'' {
return ._auto
}
else {
panic('bad os $os_str')
}
}
}
pub fn (o OS) str() string {
match o {
._auto {
return 'RESERVED: AUTO'
}
.mac {
return 'MacOS'
}
.linux {
return 'Linux'
}
.windows {
return 'Windows'
}
.freebsd {
return 'FreeBSD'
}
.openbsd {
return 'OpenBSD'
}
.netbsd {
return 'NetBSD'
}
.dragonfly {
return 'Dragonfly'
}
.js {
return 'JavaScript'
}
.android {
return 'Android'
}
.solaris {
return 'Solaris'
}
.haiku {
return 'Haiku'
}
else {
//TODO Remove when V is smart enough to know that there's no other possibilities
//should never be reached as all enum types have been enumerated
panic('unknown OS enum type: $o')
}
}
}
pub fn get_host_os() OS {
$if linux {
return .linux
}
$if macos {
return .mac
}
$if windows {
return .windows
}
$if freebsd {
return .freebsd
}
$if openbsd {
return .openbsd
}
$if netbsd {
return .netbsd
}
$if dragonfly {
return .dragonfly
}
$if solaris {
return .solaris
}
$if haiku {
return .haiku
}
panic('unknown host OS')
}

View File

@ -12,69 +12,66 @@ pub enum BuildMode {
build_module build_module
} }
pub enum OS {
mac
linux
windows
freebsd
openbsd
netbsd
dragonfly
js // TODO
android
solaris
haiku
}
pub struct Preferences { pub struct Preferences {
pub mut: pub mut:
build_mode BuildMode os OS // the OS to compile for
// nofmt bool // disable vfmt build_mode BuildMode
is_test bool // `v test string_test.v` // nofmt bool // disable vfmt
is_script bool // single file mode (`v program.v`), main function can be skipped is_test bool // `v test string_test.v`
is_live bool // main program that contains live/hot code is_script bool // single file mode (`v program.v`), main function can be skipped
is_solive bool // a shared library, that will be used in a -live main program is_live bool // main program that contains live/hot code
is_so bool // an ordinary shared library, -shared, no matter if it is live or not is_solive bool // a shared library, that will be used in a -live main program
is_prof bool // benchmark every function is_so bool // an ordinary shared library, -shared, no matter if it is live or not
translated bool // `v translate doom.v` are we running V code translated from C? allow globals, ++ expressions, etc is_prof bool // benchmark every function
is_prod bool // use "-O2" translated bool // `v translate doom.v` are we running V code translated from C? allow globals, ++ expressions, etc
is_verbose bool // print extra information with `v.log()` is_prod bool // use "-O2"
obfuscate bool // `v -obf program.v`, renames functions to "f_XXX" is_verbose bool // print extra information with `v.log()`
is_repl bool obfuscate bool // `v -obf program.v`, renames functions to "f_XXX"
is_run bool is_repl bool
show_c_cmd bool // `v -show_c_cmd` prints the C command to build program.v.c is_run bool
sanitize bool // use Clang's new "-fsanitize" option show_c_cmd bool // `v -show_c_cmd` prints the C command to build program.v.c
is_debug bool // false by default, turned on by -g or -cg, it tells v to pass -g to the C backend compiler. sanitize bool // use Clang's new "-fsanitize" option
is_vlines bool // turned on by -g, false by default (it slows down .tmp.c generation slightly). is_debug bool // false by default, turned on by -g or -cg, it tells v to pass -g to the C backend compiler.
is_keep_c bool // -keep_c , tell v to leave the generated .tmp.c alone (since by default v will delete them after c backend finishes) is_vlines bool // turned on by -g, false by default (it slows down .tmp.c generation slightly).
is_keep_c bool // -keep_c , tell v to leave the generated .tmp.c alone (since by default v will delete them after c backend finishes)
// NB: passing -cg instead of -g will set is_vlines to false and is_g to true, thus making v generate cleaner C files, // NB: passing -cg instead of -g will set is_vlines to false and is_g to true, thus making v generate cleaner C files,
// which are sometimes easier to debug / inspect manually than the .tmp.c files by plain -g (when/if v line number generation breaks). // which are sometimes easier to debug / inspect manually than the .tmp.c files by plain -g (when/if v line number generation breaks).
is_pretty_c bool // -pretty_c , tell v to run clang-format -i over the produced C file, before it is compiled. Use with -keep_c or with -o x.c . is_pretty_c bool // -pretty_c , tell v to run clang-format -i over the produced C file, before it is compiled. Use with -keep_c or with -o x.c .
is_cache bool // turns on v usage of the module cache to speed up compilation. is_cache bool // turns on v usage of the module cache to speed up compilation.
is_stats bool // `v -stats file_test.v` will produce more detailed statistics for the tests that were run is_stats bool // `v -stats file_test.v` will produce more detailed statistics for the tests that were run
no_auto_free bool // `v -nofree` disable automatic `free()` insertion for better performance in some applications (e.g. compilers) no_auto_free bool // `v -nofree` disable automatic `free()` insertion for better performance in some applications (e.g. compilers)
cflags string // Additional options which will be passed to the C compiler. cflags string // Additional options which will be passed to the C compiler.
// For example, passing -cflags -Os will cause the C compiler to optimize the generated binaries for size. // For example, passing -cflags -Os will cause the C compiler to optimize the generated binaries for size.
// You could pass several -cflags XXX arguments. They will be merged with each other. // You could pass several -cflags XXX arguments. They will be merged with each other.
// You can also quote several options at the same time: -cflags '-Os -fno-inline-small-functions'. // You can also quote several options at the same time: -cflags '-Os -fno-inline-small-functions'.
ccompiler string // the name of the used C compiler ccompiler string // the name of the used C compiler
building_v bool third_party_option string
autofree bool building_v bool
compress bool autofree bool
// skip_builtin bool // Skips re-compilation of the builtin module compress bool
// skip_builtin bool // Skips re-compilation of the builtin module
// to increase compilation time. // to increase compilation time.
// This is on by default, since a vast majority of users do not // This is on by default, since a vast majority of users do not
// work on the builtin module itself. // work on the builtin module itself.
// generating_vh bool // generating_vh bool
fast bool // use tcc/x64 codegen fast bool // use tcc/x64 codegen
enable_globals bool // allow __global for low level code enable_globals bool // allow __global for low level code
// is_fmt bool // is_fmt bool
is_bare bool is_bare bool
user_mod_path string // `v -user_mod_path /Users/user/modules` adds a new lookup path for imported modules user_mod_path string // `v -user_mod_path /Users/user/modules` adds a new lookup path for imported modules
vlib_path string vlib_path string
vpath string vpath string
x64 bool x64 bool
output_cross_c bool output_cross_c bool
prealloc bool prealloc bool
v2 bool v2 bool
vroot string
out_name string
path string // Path to file/folder to compile
// -d vfmt and -d another=0 for `$if vfmt { will execute }` and `$if another { will NOT get here }`
compile_defines []string // just ['vfmt']
compile_defines_all []string // contains both: ['vfmt','another']
mod string
} }