From 9332a83ce62f2246affda7fe2d3f9f0852591462 Mon Sep 17 00:00:00 2001 From: lutherwenxu Date: Sun, 9 Feb 2020 17:08:04 +0800 Subject: [PATCH] move v.v to cmd/v --- .github/workflows/ci.yml | 26 +- .github/workflows/periodic.yml | 10 +- .gitignore | 28 +- Dockerfile.cross | 2 +- Makefile | 8 +- {tools => cmd/tools}/.gitignore | 0 {tools => cmd/tools}/bench/hashmap.v | 0 {tools => cmd/tools}/bench/wyhash.v | 0 {tools => cmd/tools}/fast/.gitignore | 0 {tools => cmd/tools}/fast/fast.v | 12 +- {tools => cmd/tools}/fast/fast_main.js | 0 {tools => cmd/tools}/fast/footer.html | 0 {tools => cmd/tools}/fast/header.html | 2 +- {tools => cmd/tools}/gen1m.v | 0 {tools => cmd/tools}/gen_vc.v | 2 +- .../tools}/modules/scripting/scripting.v | 0 {tools => cmd/tools}/modules/testing/common.v | 12 +- {tools => cmd/tools}/modules/vgit/vgit.v | 8 +- {tools => cmd/tools}/oldv.v | 2 +- {tools => cmd/tools}/performance_compare.v | 18 +- {tools => cmd/tools}/preludes/README.md | 12 +- {tools => cmd/tools}/preludes/live_main.v | 0 {tools => cmd/tools}/preludes/live_shared.v | 0 .../tools}/preludes/tests_assertions.v | 0 .../tools}/preludes/tests_with_stats.v | 0 {tools => cmd/tools}/vbin2v.v | 0 {tools => cmd/tools}/vbuild-examples.v | 0 {tools => cmd/tools}/vbuild-tools.v | 2 +- {tools => cmd/tools}/vbuild-vbinaries.v | 0 {tools => cmd/tools}/vcreate.v | 0 {tools => cmd/tools}/vfmt.v | 76 +++- {tools => cmd/tools}/vnames.v | 9 +- {tools => cmd/tools}/vpm.v | 0 {tools => cmd/tools}/vrepl.v | 0 {tools => cmd/tools}/vtest-compiler.v | 6 +- {tools => cmd/tools}/vtest-fmt.v | 16 +- {tools => cmd/tools}/vtest.v | 0 {tools => cmd/tools}/vup.v | 0 cmd/v/compile.v | 67 +++ cmd/v/compile_options.v | 229 ++++++++++ cmd/v/flag.v | 78 ++++ vlib/compiler/vhelp.v => cmd/v/help.v | 12 +- vlib/compiler/vtools.v => cmd/v/simple_tool.v | 45 +- cmd/v/symlink.v | 31 ++ cmd/v/v.v | 88 ++++ make.bat | 8 +- v.v | 140 ------ vlib/compiler/aparser.v | 12 +- vlib/compiler/cc.v | 145 +++--- vlib/compiler/cflags.v | 6 +- vlib/compiler/cgen.v | 8 +- vlib/compiler/comptime.v | 4 +- vlib/compiler/fn.v | 4 +- vlib/compiler/live.v | 14 +- vlib/compiler/main.v | 413 +++--------------- vlib/compiler/module_header.v | 8 +- vlib/compiler/modules.v | 9 +- vlib/compiler/msvc.v | 12 +- vlib/compiler/vtmp.v | 4 +- vlib/hash/wyhash/wyhash.v | 2 +- vlib/v/builder/builder.v | 2 +- vlib/v/pref/default.v | 78 ++++ vlib/v/pref/os.v | 147 +++++++ vlib/v/pref/pref.v | 105 +++-- 64 files changed, 1123 insertions(+), 799 deletions(-) rename {tools => cmd/tools}/.gitignore (100%) rename {tools => cmd/tools}/bench/hashmap.v (100%) rename {tools => cmd/tools}/bench/wyhash.v (100%) rename {tools => cmd/tools}/fast/.gitignore (100%) rename {tools => cmd/tools}/fast/fast.v (85%) rename {tools => cmd/tools}/fast/fast_main.js (100%) rename {tools => cmd/tools}/fast/footer.html (100%) rename {tools => cmd/tools}/fast/header.html (94%) rename {tools => cmd/tools}/gen1m.v (100%) rename {tools => cmd/tools}/gen_vc.v (99%) rename {tools => cmd/tools}/modules/scripting/scripting.v (100%) rename {tools => cmd/tools}/modules/testing/common.v (95%) rename {tools => cmd/tools}/modules/vgit/vgit.v (96%) rename {tools => cmd/tools}/oldv.v (97%) rename {tools => cmd/tools}/performance_compare.v (94%) rename {tools => cmd/tools}/preludes/README.md (75%) rename {tools => cmd/tools}/preludes/live_main.v (100%) rename {tools => cmd/tools}/preludes/live_shared.v (100%) rename {tools => cmd/tools}/preludes/tests_assertions.v (100%) rename {tools => cmd/tools}/preludes/tests_with_stats.v (100%) rename {tools => cmd/tools}/vbin2v.v (100%) rename {tools => cmd/tools}/vbuild-examples.v (100%) rename {tools => cmd/tools}/vbuild-tools.v (91%) rename {tools => cmd/tools}/vbuild-vbinaries.v (100%) rename {tools => cmd/tools}/vcreate.v (100%) rename {tools => cmd/tools}/vfmt.v (80%) rename {tools => cmd/tools}/vnames.v (90%) rename {tools => cmd/tools}/vpm.v (100%) rename {tools => cmd/tools}/vrepl.v (100%) rename {tools => cmd/tools}/vtest-compiler.v (93%) rename {tools => cmd/tools}/vtest-fmt.v (83%) rename {tools => cmd/tools}/vtest.v (100%) rename {tools => cmd/tools}/vup.v (100%) create mode 100644 cmd/v/compile.v create mode 100644 cmd/v/compile_options.v create mode 100644 cmd/v/flag.v rename vlib/compiler/vhelp.v => cmd/v/help.v (92%) rename vlib/compiler/vtools.v => cmd/v/simple_tool.v (65%) create mode 100644 cmd/v/symlink.v create mode 100644 cmd/v/v.v delete mode 100644 v.v create mode 100644 vlib/v/pref/default.v create mode 100644 vlib/v/pref/os.v diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f21c6e0d09..df6b084c89 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,8 +12,8 @@ jobs: 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) run: make -j4 - - name: Build a production tools/vfmt - run: ./v -prod -d vfmt tools/vfmt.v + - name: Build a production cmd/tools/vfmt + run: ./v -prod -d vfmt cmd/tools/vfmt.v - 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 - name: Run v test-fmt @@ -28,12 +28,12 @@ jobs: - 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 - 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 run: | sudo ln -s /var/tmp/tcc/bin/tcc /usr/local/bin/tcc 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 - name: Test v binaries run: ./v -silent build-vbinaries @@ -71,9 +71,9 @@ jobs: brew install freetype glfw openssl postgres sdl2 sdl2_ttf sdl2_mixer sdl2_image export LIBRARY_PATH="$LIBRARY_PATH:/usr/local/opt/openssl/lib/" - 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 - 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 run: sudo ./v symlink - name: Set up pg database @@ -89,7 +89,7 @@ jobs: # - name: Test v->js # run: ./v -o hi.js examples/hello_v_js.v && node hi.js - name: Test symlink - run: ./v symlink && v -o v2 v.v + run: ./v symlink && v -o v2 cmd/v - name: Test vsh run: ./v examples/v_script.vsh - name: Test vid @@ -107,7 +107,7 @@ jobs: - 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 - 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 run: ./v -silent test-compiler - name: Test v binaries @@ -122,13 +122,13 @@ jobs: run: ./v -freestanding -o bare vlib/os/bare/bare_example_linux.v - name: x64 machine code generation run: | - ./v -o vprod -prod v.v - cd tools + ./v -o vprod -prod cmd/v + cd cmd/tools echo "Generating a 1m line V file..." - ../vprod gen1m.v + ../../vprod gen1m.v ./gen1m > 1m.v echo "Building it..." - ../vprod -x64 -o 1m 1m.v + ../../vprod -x64 -o 1m 1m.v echo "Running it..." ls # ./1m @@ -191,7 +191,7 @@ jobs: - 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 - 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 run: ./v -silent build-vbinaries # - name: Test v->js diff --git a/.github/workflows/periodic.yml b/.github/workflows/periodic.yml index 9cb753c5fe..93324e7e4d 100644 --- a/.github/workflows/periodic.yml +++ b/.github/workflows/periodic.yml @@ -4,7 +4,7 @@ on: schedule: - cron: '31 1,12 * * *' -jobs: +jobs: network-tests-ubuntu-tcc: runs-on: ubuntu-latest env: @@ -15,16 +15,16 @@ jobs: 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 - sudo apt-get install --quiet -y sqlite3 libsqlite3-dev + sudo apt-get install --quiet -y libglfw3 libglfw3-dev libfreetype6-dev libssl-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 - 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 run: | sudo ln -s /var/tmp/tcc/bin/tcc /usr/local/bin/tcc 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 run: ./v -d network test vlib/ diff --git a/.gitignore b/.gitignore index 19753860b0..0eed2ee247 100644 --- a/.gitignore +++ b/.gitignore @@ -7,20 +7,20 @@ fns.txt /v.c /v.*.c /v.c.out -/tools/performance_compare -/tools/oldv -/tools/vrepl -/tools/vtest -/tools/vtest-compiler -/tools/vtest-fmt -/tools/vfmt -/tools/vbin2v -/tools/vup -/tools/vpm -/tools/vcreate -/tools/vbuild-examples -/tools/vbuild-tools -/tools/vbuild-vbinaries +/cmd/tools/performance_compare +/cmd/tools/oldv +/cmd/tools/vrepl +/cmd/tools/vtest +/cmd/tools/vtest-compiler +/cmd/tools/vtest-fmt +/cmd/tools/vfmt +/cmd/tools/vbin2v +/cmd/tools/vup +/cmd/tools/vpm +/cmd/tools/vcreate +/cmd/tools/vbuild-examples +/cmd/tools/vbuild-tools +/cmd/tools/vbuild-vbinaries /v_g /v_cg /v_prod diff --git a/Dockerfile.cross b/Dockerfile.cross index 4e346898a6..22a0ff0ce1 100644 --- a/Dockerfile.cross +++ b/Dockerfile.cross @@ -3,7 +3,7 @@ FROM mstorsjo/llvm-mingw LABEL maintainer="Vitaly Takmazov " COPY . . 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 file v.exe diff --git a/Makefile b/Makefile index d42c9157e3..85dbf1ce39 100644 --- a/Makefile +++ b/Makefile @@ -42,8 +42,8 @@ endif all: latest_vc latest_tcc ifdef WIN32 $(CC) $(CFLAGS) -std=c99 -municode -w -o v2.exe $(TMPVC)/$(VCFILE) $(LDFLAGS) - ./v2.exe -o v3.exe v.v - ./v3.exe -o v.exe -prod v.v + ./v2.exe -o v3.exe cmd/v + ./v3.exe -o v.exe -prod cmd/v rm -f v2.exe v3.exe else $(CC) $(CFLAGS) -std=gnu11 -w -o v $(TMPVC)/$(VCFILE) $(LDFLAGS) -lm @@ -92,10 +92,10 @@ $(TMPVC)/.git/config: $(MAKE) fresh_vc selfcompile: - ./v -cg -o v v.v + ./v -cg -o v cmd/v 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 module_builtin: diff --git a/tools/.gitignore b/cmd/tools/.gitignore similarity index 100% rename from tools/.gitignore rename to cmd/tools/.gitignore diff --git a/tools/bench/hashmap.v b/cmd/tools/bench/hashmap.v similarity index 100% rename from tools/bench/hashmap.v rename to cmd/tools/bench/hashmap.v diff --git a/tools/bench/wyhash.v b/cmd/tools/bench/wyhash.v similarity index 100% rename from tools/bench/wyhash.v rename to cmd/tools/bench/wyhash.v diff --git a/tools/fast/.gitignore b/cmd/tools/fast/.gitignore similarity index 100% rename from tools/fast/.gitignore rename to cmd/tools/fast/.gitignore diff --git a/tools/fast/fast.v b/cmd/tools/fast/fast.v similarity index 85% rename from tools/fast/fast.v rename to cmd/tools/fast/fast.v index 8ae7c49dcd..5da8741d3c 100644 --- a/tools/fast/fast.v +++ b/cmd/tools/fast/fast.v @@ -11,9 +11,9 @@ import ( fn main() { exe := os.executable() 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') { - 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') // Fetch the last commit's hash @@ -36,11 +36,11 @@ fn main() { } // Build an optimized V println('Building vprod...') - exec('v -o $vdir/vprod -prod $vdir/v.v') + exec('v -o $vdir/vprod -prod $vdir/cmd/v') println('Measuring...') - diff1 := measure('$vdir/vprod -cc clang -o v.c $vdir/v.v') - diff2 := measure('$vdir/vprod -cc clang -o v2 $vdir/v.v') - diff3 := measure('$vdir/vprod -x64 $vdir/tools/1mil.v') + diff1 := measure('$vdir/vprod -cc clang -o v.c $vdir/cmd/v') + diff2 := measure('$vdir/vprod -cc clang -o v2 $vdir/cmd/v') + diff3 := measure('$vdir/vprod -x64 $vdir/cmd/tools/1mil.v') diff4 := measure('$vdir/vprod -cc clang $vdir/examples/hello_world.v') //println('Building V took ${diff}ms') commit_date := exec('git log -n1 --pretty="format:%at"') diff --git a/tools/fast/fast_main.js b/cmd/tools/fast/fast_main.js similarity index 100% rename from tools/fast/fast_main.js rename to cmd/tools/fast/fast_main.js diff --git a/tools/fast/footer.html b/cmd/tools/fast/footer.html similarity index 100% rename from tools/fast/footer.html rename to cmd/tools/fast/footer.html diff --git a/tools/fast/header.html b/cmd/tools/fast/header.html similarity index 94% rename from tools/fast/header.html rename to cmd/tools/fast/header.html index 6e3f4ae727..979ed24486 100644 --- a/tools/fast/header.html +++ b/cmd/tools/fast/header.html @@ -38,7 +38,7 @@ td {

Is V still fast?

Monitoring compilation speed for each commit.

-Source code: fast.v

+Source code: fast.v

diff --git a/tools/gen1m.v b/cmd/tools/gen1m.v similarity index 100% rename from tools/gen1m.v rename to cmd/tools/gen1m.v diff --git a/tools/gen_vc.v b/cmd/tools/gen_vc.v similarity index 99% rename from tools/gen_vc.v rename to cmd/tools/gen_vc.v index 06bfe3b5ca..0def01de25 100644 --- a/tools/gen_vc.v +++ b/cmd/tools/gen_vc.v @@ -291,7 +291,7 @@ fn (gen_vc mut GenVC) generate() { v_flags := if os_name == 'nix' { '-output-cross-platform-c' } else { '-os $os_name' } c_file := 'v${vc_suffix}.c' // 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 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 diff --git a/tools/modules/scripting/scripting.v b/cmd/tools/modules/scripting/scripting.v similarity index 100% rename from tools/modules/scripting/scripting.v rename to cmd/tools/modules/scripting/scripting.v diff --git a/tools/modules/testing/common.v b/cmd/tools/modules/testing/common.v similarity index 95% rename from tools/modules/testing/common.v rename to cmd/tools/modules/testing/common.v index f71f819e7e..fd6ffb9b4c 100644 --- a/tools/modules/testing/common.v +++ b/cmd/tools/modules/testing/common.v @@ -39,7 +39,7 @@ pub fn new_test_session(vargs string) TestSession { pub fn vexe_path() string { // NB: tools extracted from v require that the VEXE // 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. return os.getenv('VEXE') } @@ -238,11 +238,11 @@ pub fn building_any_v_binaries_failed() bool { testing.vlib_should_be_present(parent_dir) os.chdir(parent_dir) mut failed := false - v_build_commands := ['$vexe -o v_g -g v.v', - '$vexe -o v_prod_g -prod -g v.v', - '$vexe -o v_cg -cg v.v', - '$vexe -o v_prod_cg -prod -cg v.v', - '$vexe -o v_prod -prod v.v', + v_build_commands := ['$vexe -o v_g -g cmd/v', + '$vexe -o v_prod_g -prod -g cmd/v', + '$vexe -o v_cg -cg cmd/v', + '$vexe -o v_prod_cg -prod -cg cmd/v', + '$vexe -o v_prod -prod cmd/v', ] mut bmark := benchmark.new_benchmark() for cmd in v_build_commands { diff --git a/tools/modules/vgit/vgit.v b/cmd/tools/modules/vgit/vgit.v similarity index 96% rename from tools/modules/vgit/vgit.v rename to cmd/tools/modules/vgit/vgit.v index 25e847a94b..06ff40e411 100644 --- a/tools/modules/vgit/vgit.v +++ b/cmd/tools/modules/vgit/vgit.v @@ -92,7 +92,7 @@ pub mut: commit_vc_hash string // the git commit of the vc repo, corresponding to commit_v__hash vexename string // v or 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() { @@ -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) vgit_context.commit_v__hash = v_commithash 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) { // already compiled, so no need to compile v again return diff --git a/tools/oldv.v b/cmd/tools/oldv.v similarity index 97% rename from tools/oldv.v rename to cmd/tools/oldv.v index b5645e82cb..3ea69366e2 100644 --- a/tools/oldv.v +++ b/cmd/tools/oldv.v @@ -19,7 +19,7 @@ const ( git checkout known_good_commit git bisect 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: ... git bisect good ## ... or do: diff --git a/tools/performance_compare.v b/cmd/tools/performance_compare.v similarity index 94% rename from tools/performance_compare.v rename to cmd/tools/performance_compare.v index f567302e08..b204e4efa9 100644 --- a/tools/performance_compare.v +++ b/cmd/tools/performance_compare.v @@ -126,7 +126,9 @@ fn (c &Context) prepare_v(cdir string, commit string) { vversion := scripting.run('$cdir/v --version') vcommit := scripting.run('git rev-parse --short --verify HEAD') 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')) }else{ 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 { println('---------------------------------------------------------------------------------') println('Compare v performance when doing the following commands ($label):') - source_location_a := if os.exists('$c.a/v.v') { 'v.v ' } else { 'compiler/ ' } - source_location_b := if os.exists('$c.b/v.v') { 'v.v ' } else { 'compiler/ ' } + mut source_location_a := '' + 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_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 ' } diff --git a/tools/preludes/README.md b/cmd/tools/preludes/README.md similarity index 75% rename from tools/preludes/README.md rename to cmd/tools/preludes/README.md index b2cfb52005..f9be27150b 100644 --- a/tools/preludes/README.md +++ b/cmd/tools/preludes/README.md @@ -1,28 +1,28 @@ # 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. Each file is used in different situations (see below). 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: -### tools/preludes/live_main.v +### cmd/tools/preludes/live_main.v 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 needs module `os` and module `time`, in order for the background file change 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 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. 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. It specifies how the result will appear ('assert' vs 'asserts' and so on). diff --git a/tools/preludes/live_main.v b/cmd/tools/preludes/live_main.v similarity index 100% rename from tools/preludes/live_main.v rename to cmd/tools/preludes/live_main.v diff --git a/tools/preludes/live_shared.v b/cmd/tools/preludes/live_shared.v similarity index 100% rename from tools/preludes/live_shared.v rename to cmd/tools/preludes/live_shared.v diff --git a/tools/preludes/tests_assertions.v b/cmd/tools/preludes/tests_assertions.v similarity index 100% rename from tools/preludes/tests_assertions.v rename to cmd/tools/preludes/tests_assertions.v diff --git a/tools/preludes/tests_with_stats.v b/cmd/tools/preludes/tests_with_stats.v similarity index 100% rename from tools/preludes/tests_with_stats.v rename to cmd/tools/preludes/tests_with_stats.v diff --git a/tools/vbin2v.v b/cmd/tools/vbin2v.v similarity index 100% rename from tools/vbin2v.v rename to cmd/tools/vbin2v.v diff --git a/tools/vbuild-examples.v b/cmd/tools/vbuild-examples.v similarity index 100% rename from tools/vbuild-examples.v rename to cmd/tools/vbuild-examples.v diff --git a/tools/vbuild-tools.v b/cmd/tools/vbuild-tools.v similarity index 91% rename from tools/vbuild-tools.v rename to cmd/tools/vbuild-tools.v index 3c8d2de68f..4aaaf6b638 100644 --- a/tools/vbuild-tools.v +++ b/cmd/tools/vbuild-tools.v @@ -8,7 +8,7 @@ import ( fn main() { args := os.args 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) } } diff --git a/tools/vbuild-vbinaries.v b/cmd/tools/vbuild-vbinaries.v similarity index 100% rename from tools/vbuild-vbinaries.v rename to cmd/tools/vbuild-vbinaries.v diff --git a/tools/vcreate.v b/cmd/tools/vcreate.v similarity index 100% rename from tools/vcreate.v rename to cmd/tools/vcreate.v diff --git a/tools/vfmt.v b/cmd/tools/vfmt.v similarity index 80% rename from tools/vfmt.v rename to cmd/tools/vfmt.v index d4e1e6c482..3b18103f6a 100644 --- a/tools/vfmt.v +++ b/cmd/tools/vfmt.v @@ -8,6 +8,7 @@ import ( os.cmdline filepath compiler + v.pref ) struct FormatOptions { @@ -35,8 +36,8 @@ const ( fn main() { toolexe := os.executable() - compiler.set_vroot_folder(filepath.dir(filepath.dir(toolexe))) - args := compiler.env_vflags_and_os_args() + compiler.set_vroot_folder(filepath.dir(filepath.dir(filepath.dir(toolexe)))) + args := join_flags_and_argument() foptions := FormatOptions{ is_c: '-c' in args is_l: '-l' in args @@ -134,10 +135,10 @@ fn main() { fn (foptions &FormatOptions) format_file(file string) { tmpfolder := os.tmpdir() - mut compiler_params := []string + mut compiler_params := &pref.Preferences{} target_os := file_to_target_os(file) if target_os != '' { - compiler_params << ['-os', target_os] + compiler_params.os = pref.os_from_string(target_os) } mut cfile := file 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) 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' { // 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. 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 { eprintln('vfmt format_file: file: $file') 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_parent: $mod_folder_parent') 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('-------------------------------------------') } + 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) if use_tmp_main_program { if !foptions.is_debug { @@ -188,6 +198,22 @@ fn (foptions &FormatOptions) format_file(file string) { 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) { if formatted_file_path.len == 0 { return @@ -238,7 +264,7 @@ fn (foptions &FormatOptions) post_process_file(file string, formatted_file_path } 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. Options: -c check if file is already formatted. @@ -261,12 +287,13 @@ fn find_working_diff_command() ?string { 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 { - 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 file: $file') + 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 if foptions.is_all { v.v_fmt_all = true @@ -360,3 +387,28 @@ fn get_compile_name_of_potential_v_project(file string) string { } 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 != '') +} diff --git a/tools/vnames.v b/cmd/tools/vnames.v similarity index 90% rename from tools/vnames.v rename to cmd/tools/vnames.v index 89c7962731..519b2b85de 100644 --- a/tools/vnames.v +++ b/cmd/tools/vnames.v @@ -6,6 +6,7 @@ import ( strings filepath compiler + v.pref ) const ( @@ -32,7 +33,11 @@ fn analyze_v_file(file string) { println('$hash $file $hash') // 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() for f in v.files { v.parse(f, .decl) } fi := v.get_file_parser_index( file ) or { panic(err) } @@ -51,7 +56,7 @@ fn analyze_v_file(file string) { fn main(){ 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) fp.application(filepath.filename(toolexe)) diff --git a/tools/vpm.v b/cmd/tools/vpm.v similarity index 100% rename from tools/vpm.v rename to cmd/tools/vpm.v diff --git a/tools/vrepl.v b/cmd/tools/vrepl.v similarity index 100% rename from tools/vrepl.v rename to cmd/tools/vrepl.v diff --git a/tools/vtest-compiler.v b/cmd/tools/vtest-compiler.v similarity index 93% rename from tools/vtest-compiler.v rename to cmd/tools/vtest-compiler.v index 460b2cf298..6fc64590ca 100644 --- a/tools/vtest-compiler.v +++ b/cmd/tools/vtest-compiler.v @@ -33,8 +33,8 @@ fn v_test_compiler(vargs string) { // Make sure v.c can be compiled without warnings $if macos { - if os.exists('/v.v') { - os.system('$vexe -o v.c v.v') + if os.exists('/cmd/v') { + os.system('$vexe -o v.c cmd/v') if os.system('cc -Werror v.c') != 0 { eprintln('cc failed to build v.c without warnings') exit(1) @@ -42,7 +42,7 @@ fn v_test_compiler(vargs string) { 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('') testing.eheader('Testing all _test.v files...') mut compiler_test_session := testing.new_test_session(vargs) diff --git a/tools/vtest-fmt.v b/cmd/tools/vtest-fmt.v similarity index 83% rename from tools/vtest-fmt.v rename to cmd/tools/vtest-fmt.v index 03e652480b..4a6dcd27cf 100644 --- a/tools/vtest-fmt.v +++ b/cmd/tools/vtest-fmt.v @@ -7,14 +7,14 @@ import ( const ( known_failing_exceptions = ['./examples/vweb/vweb_example.v', - './tools/gen_vc.v', - './tools/modules/vgit/vgit.v', // generics - './tools/preludes/live_main.v', - './tools/preludes/live_shared.v', - './tools/preludes/tests_assertions.v', - './tools/preludes/tests_with_stats.v', - './tools/performance_compare.v', // generics - './tools/oldv.v', // generics + './cmd/tools/gen_vc.v', + './cmd/tools/modules/vgit/vgit.v', // generics + './cmd/tools/preludes/live_main.v', + './cmd/tools/preludes/live_shared.v', + './cmd/tools/preludes/tests_assertions.v', + './cmd/tools/preludes/tests_with_stats.v', + './cmd/tools/performance_compare.v', // generics + './cmd/tools/oldv.v', // generics './tutorials/code/blog/article.v', './tutorials/code/blog/blog.v', './vlib/arrays/arrays.v', diff --git a/tools/vtest.v b/cmd/tools/vtest.v similarity index 100% rename from tools/vtest.v rename to cmd/tools/vtest.v diff --git a/tools/vup.v b/cmd/tools/vup.v similarity index 100% rename from tools/vup.v rename to cmd/tools/vup.v diff --git a/cmd/v/compile.v b/cmd/v/compile.v new file mode 100644 index 0000000000..646b376fb5 --- /dev/null +++ b/cmd/v/compile.v @@ -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) +} diff --git a/cmd/v/compile_options.v b/cmd/v/compile_options.v new file mode 100644 index 0000000000..448b62803f --- /dev/null +++ b/cmd/v/compile_options.v @@ -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 +} + diff --git a/cmd/v/flag.v b/cmd/v/flag.v new file mode 100644 index 0000000000..9427b99b25 --- /dev/null +++ b/cmd/v/flag.v @@ -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 +} diff --git a/vlib/compiler/vhelp.v b/cmd/v/help.v similarity index 92% rename from vlib/compiler/vhelp.v rename to cmd/v/help.v index 35a45f2cae..ed7607616e 100644 --- a/vlib/compiler/vhelp.v +++ b/cmd/v/help.v @@ -1,8 +1,16 @@ -module compiler +module main -pub const ( +const ( help_text = 'Usage: v [options/commands] [file.v | directory] + To run V in REPL mode, run V without any arguments. + To compile a directory/file, pass it as the only argument. + + To run a directory/file, use `v run [file.v | directory]`. V will compile and run it for you. + + This help message is only intended to be a quick start guide. For a comprehensive help message, use `v help --verbose`.' + verbose_help_text = 'Usage: v [options/commands] [file.v | directory] + When V is run without any arguments, it is run in REPL mode. When given a .v file, it will be compiled. The executable will have the diff --git a/vlib/compiler/vtools.v b/cmd/v/simple_tool.v similarity index 65% rename from vlib/compiler/vtools.v rename to cmd/v/simple_tool.v index 2b255e3db2..211c01bcad 100644 --- a/vlib/compiler/vtools.v +++ b/cmd/v/simple_tool.v @@ -1,52 +1,57 @@ -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 ( - os + compiler filepath + os ) -pub fn launch_tool(tname string, cmdname string) { - is_verbose := '-verbose' in os.args || '--verbose' in os.args +fn launch_tool(is_verbose bool, tname string, cmdname string) { vexe := vexe_path() 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) if tname_index == -1 { tname_index = os.args.len } mut compilation_options := os.args[1..tname_index].clone() tool_args := os.args[1..].join(' ') - tool_exe := os.realpath('$vroot/tools/$tname') - tool_source := os.realpath('$vroot/tools/${tname}.v') + tool_exe := os.realpath('$vroot/cmd/tools/$tname') + tool_source := os.realpath('$vroot/cmd/tools/${tname}.v') tool_command := '"$tool_exe" $tool_args' - if is_verbose { + if is_verbose { eprintln('launch_tool vexe : $vroot') eprintln('launch_tool vroot : $vroot') eprintln('launch_tool tool_args : $tool_args') eprintln('launch_tool tool_command: $tool_command') - } - mut tool_should_be_recompiled := false + } + + mut should_compile := false if !os.exists(tool_exe) { - // fresh checkout - tool_should_be_recompiled = true + should_compile = true } else { if os.file_last_mod_unix(tool_exe) <= os.file_last_mod_unix(vexe) { // v was recompiled, maybe after v up ... // rebuild the tool too just in case - tool_should_be_recompiled = true + should_compile = true } if os.file_last_mod_unix(tool_exe) <= os.file_last_mod_unix(tool_source) { // the user changed the source code of the tool - tool_should_be_recompiled = true + should_compile = true } } - 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 tname == 'vfmt' { compilation_options << ['-d', 'vfmt'] } + + if should_compile { + if tname == 'vfmt' { + compilation_options << ['-d', 'vfmt'] + } compilation_args := compilation_options.join(' ') compilation_command := '"$vexe" $compilation_args "$tool_source"' if is_verbose { @@ -61,5 +66,5 @@ pub fn launch_tool(tname string, cmdname string) { eprintln('launch_tool running tool command: $tool_command ...') } - exit( os.system(tool_command) ) + exit(os.system(tool_command)) } diff --git a/cmd/v/symlink.v b/cmd/v/symlink.v new file mode 100644 index 0000000000..f35cc39d2b --- /dev/null +++ b/cmd/v/symlink.v @@ -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.') + } +} + diff --git a/cmd/v/v.v b/cmd/v/v.v new file mode 100644 index 0000000000..8e92662325 --- /dev/null +++ b/cmd/v/v.v @@ -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) + } + } +} diff --git a/make.bat b/make.bat index 3114e03ec2..c3a18f13d3 100644 --- a/make.bat +++ b/make.bat @@ -35,8 +35,8 @@ if %ERRORLEVEL% NEQ 0 ( ) echo Now using V to build V... -v2.exe -o v3.exe v.v -v3.exe -o v.exe -prod v.v +v2.exe -o v3.exe cmd/v +v3.exe -o v.exe -prod cmd/v if %ERRORLEVEL% NEQ 0 ( echo v.exe failed to compile itself - Create an issue at 'https://github.com/vlang' exit /b 1 @@ -77,8 +77,8 @@ if %ERRORLEVEL% NEQ 0 ( ) echo rebuild from source (twice, in case of C definitions changes) -v2.exe -cc msvc -o v3.exe v.v -v3.exe -cc msvc -o v -prod v.v +v2.exe -cc msvc -o v3.exe cmd/v +v3.exe -cc msvc -o v -prod cmd/v if %ERRORLEVEL% NEQ 0 ( echo V failed to build itself with error %ERRORLEVEL% goto :compileerror diff --git a/v.v b/v.v deleted file mode 100644 index e2476d6fda..0000000000 --- a/v.v +++ /dev/null @@ -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) -} diff --git a/vlib/compiler/aparser.v b/vlib/compiler/aparser.v index 844947d7cd..f943f6b62b 100644 --- a/vlib/compiler/aparser.v +++ b/vlib/compiler/aparser.v @@ -166,8 +166,8 @@ fn (v mut V) new_parser_from_file(path string) Parser { } } - if v.compile_defines.len > 0 { - for cdefine in v.compile_defines { + if v.pref.compile_defines.len > 0 { + for cdefine in v.pref.compile_defines { custom_path_ending := '_d_${cdefine}.v' if path.ends_with(custom_path_ending){ path_platform = custom_path_ending @@ -215,8 +215,8 @@ fn (v mut V) new_parser(scanner &Scanner) Parser { cgen: v.cgen //x64: v.x64 pref: v.pref - os: v.os - vroot: v.vroot + os: v.pref.os + vroot: v.pref.vroot local_vars: [Var{ }].repeat(MaxLocalVars) import_table: new_import_table() @@ -443,7 +443,7 @@ fn (p mut Parser) parse(pass Pass) { // p.fgen_nl() 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)') p.cgen.nogen = true // 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 // 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 - 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 else { p.table.qualify_module(p.mod, p.file_path) } p.table.register_module(fq_mod) diff --git a/vlib/compiler/cc.v b/vlib/compiler/cc.v index afb33ccdd9..c9b609a5dc 100644 --- a/vlib/compiler/cc.v +++ b/vlib/compiler/cc.v @@ -5,9 +5,9 @@ module compiler import ( os - os.cmdline time filepath + v.pref ) fn todo() { @@ -34,8 +34,8 @@ fn (v mut V) cc() { vdir := filepath.dir(vexe) // Just create a C/JavaScript file and exit // for example: `v -o v.c compiler` - ends_with_c := v.out_name.ends_with('.c') - ends_with_js := v.out_name.ends_with('.js') + ends_with_c := v.pref.out_name.ends_with('.c') + ends_with_js := v.pref.out_name.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 { @@ -59,7 +59,7 @@ fn (v mut V) cc() { println('V.js compiler not found, building...') // Build V.js. Specifying `-os js` makes V include // only _js.v files and ignore _c.v files. - ret := os.system('$vexe -o $vjs_path -os js $vdir/v.v') + ret := os.system('$vexe -o $vjs_path -os js $vdir/cmd/v') if ret == 0 { println('Done.') } @@ -68,21 +68,21 @@ fn (v mut V) cc() { 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 { - 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.') } } } // 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) } exit(0) } // Cross compiling for Windows - if v.os == .windows { + if v.pref.os == .windows { $if !windows { v.cc_windows_cross() return @@ -136,29 +136,29 @@ fn (v mut V) cc() { if !v.pref.is_so && v.pref.build_mode != .build_module && 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' - 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 { a << '-shared -fPIC ' // -Wl,-z,defs' - v.out_name = v.out_name + '.so' + v.pref.out_name += '.so' } if v.pref.is_bare { a << '-fno-stack-protector -static -ffreestanding -nostdlib' } if v.pref.build_mode == .build_module { // Create the modules & out directory if it's not there. - mut out_dir := if v.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) if !os.is_dir(pdir) { os.mkdir_all(pdir) } - v.out_name = '${out_dir}.o' // v.out_name - println('Building ${v.out_name}...') + v.pref.out_name = '${out_dir}.o' // v.out_name + println('Building ${v.pref.out_name}...') } debug_mode := v.pref.is_debug mut debug_options := '-g' @@ -198,7 +198,7 @@ fn (v mut V) cc() { if debug_mode && os.user_os() != 'windows' { 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' } for f in v.generate_hotcode_reloading_compiler_flags() { @@ -269,24 +269,24 @@ fn (v mut V) cc() { // Cross compiling windows // // Output executable name - a << '-o "$v.out_name"' - if os.is_dir(v.out_name) { - verror("\'$v.out_name\' is a directory") + a << '-o "$v.pref.out_name"' + if os.is_dir(v.pref.out_name) { + verror("\'$v.pref.out_name\' is a directory") } // macOS code can include objective C TODO remove once objective C is replaced with C - if v.os == .mac { + if v.pref.os == .mac { a << '-x objective-c' } // The C file we are compiling a << '"$v.out_name_c"' - if v.os == .mac { + if v.pref.os == .mac { a << '-x none' } // Min macos version is mandatory I think? - if v.os == .mac { + if v.pref.os == .mac { a << '-mmacosx-version-min=10.7' } - if v.os == .windows { + if v.pref.os == .windows { a << '-municode' } cflags := v.get_os_cflags() @@ -297,18 +297,18 @@ fn (v mut V) cc() { a << libs // Without these libs compilation will fail on Linux // || os.user_os() == 'linux' - if !v.pref.is_bare && v.pref.build_mode != .build_module && v.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 ' // -ldl is a Linux only thing. BSDs have it in libc. - if v.os == .linux { + if v.pref.os == .linux { a << ' -ldl ' } - if v.os == .freebsd { + if v.pref.os == .freebsd { // FreeBSD: backtrace needs execinfo library while linking 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' } args := a.join(' ') @@ -351,6 +351,18 @@ start: } if v.pref.is_debug { 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 { if res.output.len < 30 { @@ -359,11 +371,19 @@ start: q := res.output.all_after('error: ').limit(150) println('==================') println(q) + 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 // Print the C command @@ -402,16 +422,16 @@ start: println('-compress does not work on Windows for now') return } - ret := os.system('strip $v.out_name') + ret := os.system('strip $v.pref.out_name') if ret != 0 { println('strip failed') return } // NB: upx --lzma can sometimes fail with NotCompressibleException // See https://github.com/vlang/v/pull/3528 - mut ret2 := os.system('upx --lzma -qqq $v.out_name') + mut ret2 := os.system('upx --lzma -qqq $v.pref.out_name') if ret2 != 0 { - ret2 = os.system('upx -qqq $v.out_name') + ret2 = os.system('upx -qqq $v.pref.out_name') } if ret2 != 0 { println('upx failed') @@ -430,10 +450,10 @@ start: fn (c mut V) cc_windows_cross() { println('Cross compiling for Windows...') - if !c.out_name.ends_with('.exe') { - c.out_name = c.out_name + '.exe' + if !c.pref.out_name.ends_with('.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() // -I flags 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) } 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 { $if windows { return 'https://github.com/vlang/v/wiki/Installing-a-C-compiler-on-Windows' diff --git a/vlib/compiler/cflags.v b/vlib/compiler/cflags.v index a0129c0710..60fe77bb9d 100644 --- a/vlib/compiler/cflags.v +++ b/vlib/compiler/cflags.v @@ -20,12 +20,12 @@ pub fn (c &CFlag) str() string { fn (v &V) get_os_cflags() []CFlag { mut flags := []CFlag mut ctimedefines := []string - if v.compile_defines.len > 0 { - ctimedefines << v.compile_defines + if v.pref.compile_defines.len > 0 { + ctimedefines << v.pref.compile_defines } 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 } if flag.os in ctimedefines { diff --git a/vlib/compiler/cgen.v b/vlib/compiler/cgen.v index da87bd9fa0..00392fbe56 100644 --- a/vlib/compiler/cgen.v +++ b/vlib/compiler/cgen.v @@ -43,7 +43,7 @@ mut: cut_pos int } -fn new_cgen(out_name_c string) &CGen { +pub fn new_cgen(out_name_c string) &CGen { path := out_name_c out := os.create(path)or{ 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 } -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) if os.exists(obj_path) { return @@ -288,11 +288,9 @@ fn build_thirdparty_obj_file(path string, moduleflags []CFlag) { 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() 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{ println('failed thirdparty object build cmd: $cmd') verror(err) diff --git a/vlib/compiler/comptime.v b/vlib/compiler/comptime.v index 5ecff496ab..05fe2f9740 100644 --- a/vlib/compiler/comptime.v +++ b/vlib/compiler/comptime.v @@ -112,7 +112,7 @@ fn (p mut Parser) comp_time() { else if name == 'clang' { 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.: // // `[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('@VMOD', v_modules_path) // 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) return } diff --git a/vlib/compiler/fn.v b/vlib/compiler/fn.v index 84ae81b061..f8f6264b73 100644 --- a/vlib/compiler/fn.v +++ b/vlib/compiler/fn.v @@ -510,7 +510,7 @@ fn (p mut Parser) fn_decl() { } 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 { @@ -774,7 +774,7 @@ fn (p mut Parser) verify_fn_before_call(f &Fn) { // p.tok == fn_name fn (p mut Parser) fn_call(f mut Fn, method_ph int, receiver_var, receiver_type string) { 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 { p.cgen.nogen = true } diff --git a/vlib/compiler/live.v b/vlib/compiler/live.v index cf799a46e9..2fc7b61b8c 100644 --- a/vlib/compiler/live.v +++ b/vlib/compiler/live.v @@ -10,10 +10,10 @@ fn (v &V) generate_hotcode_reloading_compiler_flags() []string { mut a := []string if v.pref.is_live || v.pref.is_so { // See 'man dlopen', and test running a GUI program compiled with -live - if (v.os == .linux || os.user_os() == 'linux') { + if (v.pref.os == .linux || os.user_os() == 'linux') { a << '-rdynamic' } - if (v.os == .mac || os.user_os() == 'mac') { + if (v.pref.os == .mac || os.user_os() == 'mac') { a << '-flat_namespace' } } @@ -22,7 +22,7 @@ fn (v &V) generate_hotcode_reloading_compiler_flags() []string { fn (v &V) generate_hotcode_reloading_declarations() { mut cgen := v.cgen - if v.os != .windows { + if v.pref.os != .windows { if v.pref.is_so { 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 mut cgen := v.cgen cgen.genln('') - file_base := filepath.filename(v.dir).replace('.v', '') - if v.os != .windows { + file_base := filepath.filename(v.pref.path).replace('.v', '') + if v.pref.os != .windows { // unix: so_name := file_base + '.so' cgen.genln(' char *live_library_name = "$so_name";') @@ -79,7 +79,7 @@ fn (v &V) generate_hot_reload_code() { mut cgen := v.cgen // Hot code reloading 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', '') so_name := file_base + '.so' // 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(' void* live_lib=0; int load_so(byteptr path) { diff --git a/vlib/compiler/main.v b/vlib/compiler/main.v index b0598dec4d..18bf729d5a 100644 --- a/vlib/compiler/main.v +++ b/vlib/compiler/main.v @@ -5,7 +5,6 @@ module compiler import ( os - os.cmdline strings filepath v.pref @@ -35,21 +34,15 @@ enum Pass { main } -struct V { +pub struct V { pub mut: - os pref.OS // the OS to build for out_name_c string // name of the temporary C file files []string // all V files that need to be parsed and compiled - dir string // directory (or file) being compiled (TODO rename to path?) compiled_dir string // contains os.realpath() of the dir of the final file beeing compiled, or the dir itself when doing `v .` table &Table // table with types, vars, functions etc cgen &CGen // C code generator //x64 &x64.Gen pref &pref.Preferences // all the preferences and settings extracted to a struct for reusability - lang_dir string // "~/code/v" - out_name string // "program.exe" - vroot string - mod string // module being built with -lib parsers []Parser // file parsers vgen_buf strings.Builder // temporary buffer for generated V code (.str() etc) file_parser_idx map[string]int // map absolute file path to v.parsers index @@ -57,15 +50,33 @@ pub mut: cached_mods []string module_lookup_paths []string - // -d vfmt and -d another=0 for `$if vfmt { will execute }` and `$if another { will NOT get here }` - compile_defines []string // just ['vfmt'] - compile_defines_all []string // contains both: ['vfmt','another'] - - v_fmt_all bool // << input set by tools/vfmt.v - v_fmt_file string // << file given by the user from tools/vfmt.v + v_fmt_all bool // << input set by cmd/tools/vfmt.v + v_fmt_file string // << file given by the user from cmd/tools/vfmt.v v_fmt_file_result string // >> file with formatted output generated by vlib/compiler/vfmt.v } +pub fn new_v(pref &pref.Preferences) &V { + rdir := os.realpath(pref.path) + + mut out_name_c := get_vtmp_filename(pref.out_name, '.tmp.c') + if pref.is_so { + out_name_c = get_vtmp_filename(pref.out_name, '.tmp.so.c') + } + + mut vgen_buf := strings.new_builder(1000) + vgen_buf.writeln('module vgen\nimport strings') + + return &V{ + compiled_dir: if os.is_dir(rdir) { rdir } else { filepath.dir(rdir) } + table: new_table(pref.obfuscate) + out_name_c: out_name_c + cgen: new_cgen(out_name_c) + //x64: x64.new_gen(out_name) + pref: pref + vgen_buf: vgen_buf + } +} + // Should be called by main at the end of the compilation process, to cleanup pub fn (v &V) finalize_compilation() { // TODO remove @@ -166,7 +177,7 @@ pub fn (v mut V) compile() { if v.pref.prealloc { cgen.genln('#define VPREALLOC (1)') } - if v.os == .js { + if v.pref.os == .js { cgen.genln('#define _VJS (1) ') } v_hash := vhash() @@ -189,11 +200,11 @@ pub fn (v mut V) compile() { cgen.genln('#include ') } - if v.compile_defines_all.len > 0 { + if v.pref.compile_defines_all.len > 0 { cgen.genln('') - cgen.genln('// All custom defines : ' + v.compile_defines_all.join(',')) - cgen.genln('// Turned ON custom defines: ' + v.compile_defines.join(',')) - for cdefine in v.compile_defines { + cgen.genln('// All custom defines : ' + v.pref.compile_defines_all.join(',')) + cgen.genln('// Turned ON custom defines: ' + v.pref.compile_defines.join(',')) + for cdefine in v.pref.compile_defines { cgen.genln('#define CUSTOM_DEFINE_${cdefine}') } cgen.genln('//') @@ -234,7 +245,7 @@ pub fn (v mut V) compile() { if '-debug_alloc' in os.args { cgen.genln('#define DEBUG_ALLOC 1') } - if v.pref.is_live && v.os != .windows { + if v.pref.is_live && v.pref.os != .windows { cgen.includes << '#include ' } // cgen.genln('/*================================== FNS =================================*/') @@ -261,7 +272,7 @@ pub fn (v mut V) compile() { vgen_parser.parse(.main) // Generate .vh if we are building a module if v.pref.build_mode == .build_module { - generate_vh(v.dir) + generate_vh(v.pref.path) } // All definitions mut def := strings.new_builder(10000) // Avoid unnecessary allocations @@ -319,7 +330,7 @@ pub fn (v mut V) compile2() { println(v.files) } mut b := v.new_v2() - b.build_c(v.files, v.out_name) + b.build_c(v.files, v.pref.out_name) v.cc() } @@ -329,18 +340,18 @@ pub fn (v mut V) compile_x64() { println('You are not on a Linux system, so you will not ' + 'be able to run the resulting executable') } //v.files << v.v_files_from_dir(filepath.join(v.pref.vlib_path,'builtin','bare')) - v.files << v.dir + v.files << v.pref.path v.set_module_lookup_paths() mut b := v.new_v2() // move all this logic to v2 - b.build_x64(v.files, v.out_name) + b.build_x64(v.files, v.pref.out_name) } // make v2 from v1 fn (v &V) new_v2() builder.Builder { mut b := builder.new_builder(v.pref) b = { b| - os: v.os, + os: v.pref.os, module_path: v_modules_path, compiled_dir: v.compiled_dir, module_search_paths: v.module_lookup_paths @@ -356,7 +367,7 @@ fn (v mut V) generate_init() { nogen := v.cgen.nogen v.cgen.nogen = false consts_init_body := v.cgen.consts_init.join_lines() - init_fn_name := mod_gen_name(v.mod) + '__init_consts' + init_fn_name := mod_gen_name(v.pref.mod) + '__init_consts' v.cgen.genln('void ${init_fn_name}();\nvoid ${init_fn_name}() {\n$consts_init_body\n}') v.cgen.nogen = nogen } @@ -482,7 +493,7 @@ pub fn (v mut V) generate_main() { // Generate a C `main`, which calls every single test function v.gen_main_start(false) if v.pref.is_stats { - cgen.genln('BenchedTests bt = main__start_testing(${test_fn_names.len},tos3("$v.dir"));') + cgen.genln('BenchedTests bt = main__start_testing(${test_fn_names.len},tos3("$v.pref.path"));') } for tfname in test_fn_names { if v.pref.is_stats { @@ -513,7 +524,7 @@ pub fn (v mut V) generate_main() { } pub fn (v mut V) gen_main_start(add_os_args bool) { - if v.os == .windows { + if v.pref.os == .windows { if 'glfw' in v.table.imports { // GUI application v.cgen.genln('int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, LPWSTR cmd_line, int show_cmd) { ') @@ -531,7 +542,7 @@ pub fn (v mut V) gen_main_start(add_os_args bool) { } v.cgen.genln(' init();') if add_os_args && 'os' in v.table.imports { - if v.os == .windows { + if v.pref.os == .windows { v.cgen.genln(' os__args = os__init_os_args_wide(argc, argv);') } else { v.cgen.genln(' os__args = os__init_os_args(argc, (byteptr*)argv);') @@ -547,45 +558,12 @@ pub fn (v mut V) gen_main_end(return_statement string) { v.cgen.genln('}') } -pub fn final_target_out_name(out_name string) string { - $if windows { - return out_name.replace('/', '\\') + '.exe' - } - return if out_name.starts_with('/') { out_name } else { './' + out_name } -} - -pub fn (v V) run_compiled_executable_and_exit() { - args := env_vflags_and_os_args() - if v.pref.is_verbose { - println('============ running $v.out_name ============') - } - mut cmd := '"' + final_target_out_name(v.out_name).replace('.exe', '') + '"' - args_after_no_options := cmdline.only_non_options( cmdline.after(args,['run','test']) ) - if args_after_no_options.len > 1 { - cmd += ' ' + args_after_no_options[1..].join(' ') - } - if v.pref.is_test { - ret := os.system(cmd) - if ret != 0 { - exit(1) - } - } - if v.pref.is_run { - ret := os.system(cmd) - // TODO: make the runner wrapping as transparent as possible - // (i.e. use execve when implemented). For now though, the runner - // just returns the same exit code as the child process. - exit(ret) - } - exit(0) -} - pub fn (v &V) v_files_from_dir(dir string) []string { mut res := []string if !os.exists(dir) { if dir == 'compiler' && os.is_dir('vlib') { println('looks like you are trying to build V with an old command') - println('use `v -o v v.v` instead of `v -o v compiler`') + println('use `v -o v cmd/v` instead of `v -o v compiler`') } verror("$dir doesn't exist") } @@ -606,27 +584,27 @@ pub fn (v &V) v_files_from_dir(dir string) []string { if file.ends_with('_test.v') { continue } - if (file.ends_with('_win.v') || file.ends_with('_windows.v')) && v.os != .windows { + if (file.ends_with('_win.v') || file.ends_with('_windows.v')) && v.pref.os != .windows { continue } - if (file.ends_with('_lin.v') || file.ends_with('_linux.v')) && v.os != .linux { + if (file.ends_with('_lin.v') || file.ends_with('_linux.v')) && v.pref.os != .linux { continue } - if (file.ends_with('_mac.v') || file.ends_with('_darwin.v')) && v.os != .mac { + if (file.ends_with('_mac.v') || file.ends_with('_darwin.v')) && v.pref.os != .mac { continue } - if file.ends_with('_nix.v') && v.os == .windows { + if file.ends_with('_nix.v') && v.pref.os == .windows { continue } - if file.ends_with('_js.v') && v.os != .js { + if file.ends_with('_js.v') && v.pref.os != .js { continue } - if file.ends_with('_c.v') && v.os == .js { + if file.ends_with('_c.v') && v.pref.os == .js { continue } - if v.compile_defines_all.len > 0 && file.contains('_d_') { + if v.pref.compile_defines_all.len > 0 && file.contains('_d_') { mut allowed := false - for cdefine in v.compile_defines { + for cdefine in v.pref.compile_defines { file_postfix := '_d_${cdefine}.v' if file.ends_with(file_postfix) { allowed = true @@ -740,15 +718,15 @@ pub fn (v &V) get_builtin_files() []string { // get user files pub fn (v &V) get_user_files() []string { - mut dir := v.dir + mut dir := v.pref.path v.log('get_v_files($dir)') // Need to store user files separately, because they have to be added after // libs, but we dont know which libs need to be added yet mut user_files := []string - // See tools/preludes/README.md for more info about what preludes are + // See cmd/tools/preludes/README.md for more info about what preludes are vroot := filepath.dir(vexe_path()) - preludes_path := filepath.join(vroot,'tools','preludes') + preludes_path := filepath.join(vroot,'cmd','tools','preludes') if v.pref.is_live { user_files << filepath.join(preludes_path,'live_main.v') } @@ -858,284 +836,6 @@ pub fn (v &V) log(s string) { println(s) } -pub fn new_v(args []string) &V { - // Create modules dirs if they are missing - if !os.is_dir(v_modules_path) { - os.mkdir(v_modules_path)or{ - panic(err) - } - os.mkdir('$v_modules_path${os.path_separator}cache')or{ - panic(err) - } - } - // optional, custom modules search path - user_mod_path := cmdline.option(args, '-user_mod_path', '') - // Location of all vlib files - vroot := filepath.dir(vexe_path()) - vlib_path := cmdline.option(args, '-vlib-path', filepath.join(vroot,'vlib')) - vpath := cmdline.option(args, '-vpath', v_modules_path) - mut vgen_buf := strings.new_builder(1000) - vgen_buf.writeln('module vgen\nimport strings') - target_os := cmdline.option(args, '-os', '') - mut out_name := cmdline.option(args, '-o', 'a.out') - mut dir := args.last() - if 'run' in args { - args_after_run := cmdline.only_non_options( cmdline.after(args,['run']) ) - dir = if args_after_run.len>0 { args_after_run[0] } else { '' } - } - if dir.ends_with(os.path_separator) { - dir = dir.all_before_last(os.path_separator) - } - if dir.starts_with('.$os.path_separator') { - dir = dir[2..] - } - if args.len < 2 { - dir = '' - } - - // build mode - mut build_mode := pref.BuildMode.default_mode - mut mod := '' - joined_args := args.join(' ') - if joined_args.contains('build module ') { - build_mode = .build_module - os.chdir(vroot) - // v build module ~/v/os => os.o - mod_path := if dir.contains('vlib') { dir.all_after('vlib' + os.path_separator) } else if dir.starts_with('.\\') || dir.starts_with('./') { dir[2..] } else if dir.starts_with(os.path_separator) { dir.all_after(os.path_separator) } else { dir } - mod = mod_path.replace(os.path_separator, '.') - println('Building module "${mod}" (dir="$dir")...') - // out_name = '$TmpPath/vlib/${base}.o' - if !out_name.ends_with('.c') { - out_name = mod - } - // Cross compiling? Use separate dirs for each os - /* - if target_os != os.user_os() { - os.mkdir('$TmpPath/vlib/$target_os') or { panic(err) } - out_name = '$TmpPath/vlib/$target_os/${base}.o' - println('target_os=$target_os user_os=${os.user_os()}') - println('!Cross compiling $out_name') - } - */ - - } - is_test := dir.ends_with('_test.v') - is_script := dir.ends_with('.v') || dir.ends_with('.vsh') - if is_script && !os.exists(dir) { - println('`$dir` does not exist') - exit(1) - } - // No -o provided? foo.v => foo - if out_name == 'a.out' && dir.ends_with('.v') && dir != '.v' { - out_name = dir[..dir.len - 2] - // Building V? Use v2, since we can't overwrite a running - // executable on Windows + the precompiled V is more - // optimized. - if out_name == 'v' && os.is_dir('vlib/compiler') { - println('Saving the resulting V executable in `./v2`') - println('Use `v -o v v.v` if you want to replace current ' + 'V executable.') - out_name = 'v2' - } - } - // if we are in `/foo` and run `v .`, the executable should be `foo` - if dir == '.' && out_name == 'a.out' { - base := os.getwd().all_after(os.path_separator) - out_name = base.trim_space() - } - // `v -o dir/exec`, create "dir/" if it doesn't exist - if out_name.contains(os.path_separator) { - d := out_name.all_before_last(os.path_separator) - if !os.is_dir(d) { - println('creating a new directory "$d"') - os.mkdir(d)or{ - panic(err) - } - } - } - mut _os := pref.OS.mac - // No OS specifed? Use current system - if target_os == '' { - $if linux { - _os = .linux - } - $if macos { - _os = .mac - } - $if windows { - _os = .windows - } - $if freebsd { - _os = .freebsd - } - $if openbsd { - _os = .openbsd - } - $if netbsd { - _os = .netbsd - } - $if dragonfly { - _os = .dragonfly - } - $if solaris { - _os = .solaris - } - $if haiku { - _os = .haiku - } - } - else { - _os = os_from_string(target_os) - } - // println('VROOT=$vroot') - // v.exe's parent directory should contain vlib - if !os.is_dir(vlib_path) || !os.is_dir(vlib_path + os.path_separator + 'builtin') { - // println('vlib not found, downloading it...') - /* - ret := os.system('git clone --depth=1 https://github.com/vlang/v .') - if ret != 0 { - println('failed to `git clone` vlib') - println('make sure you are online and have git installed') - exit(1) - } - */ - println('vlib not found. It should be next to the V executable.') - println('Go to https://vlang.io to install V.') - println('(os.executable=${os.executable()} vlib_path=$vlib_path vexe_path=${vexe_path()}') - exit(1) - } - mut out_name_c := get_vtmp_filename(out_name, '.tmp.c') - cflags := cmdline.many_values(args, '-cflags').join(' ') - - defines := cmdline.many_values(args, '-d') - compile_defines, compile_defines_all := parse_defines( defines ) - - rdir := os.realpath(dir) - rdir_name := filepath.filename(rdir) - if '-bare' in args { - verror('use -freestanding instead of -bare') - } - obfuscate := '-obf' in args - is_repl := '-repl' in args - pref := &pref.Preferences{ - is_test: is_test - is_script: is_script - is_so: '-shared' in args - is_solive: '-solive' in args - is_prod: '-prod' in args - is_verbose: '-verbose' in args || '--verbose' in args - is_debug: '-g' in args || '-cg' in args - is_vlines: '-g' in args && !('-cg' in args) - is_keep_c: '-keep_c' in args - is_pretty_c: '-pretty_c' in args - is_cache: '-cache' in args - is_stats: '-stats' in args - obfuscate: obfuscate - is_prof: '-prof' in args - is_live: '-live' in args - sanitize: '-sanitize' in args - // nofmt: '-nofmt' in args - - show_c_cmd: '-show_c_cmd' in args - translated: 'translated' in args - is_run: 'run' in args - autofree: '-autofree' in args - compress: '-compress' in args - enable_globals: '--enable-globals' in args - fast: '-fast' in args - is_bare: '-freestanding' in args - x64: '-x64' in args - output_cross_c: '-output-cross-platform-c' in args - prealloc: '-prealloc' in args - is_repl: is_repl - build_mode: build_mode - cflags: cflags - ccompiler: find_c_compiler() - building_v: !is_repl && (rdir_name == 'compiler' || rdir_name == 'v.v' || rdir_name == 'vfmt.v' || dir.contains('vlib')) - // is_fmt: comptime_define == 'vfmt' - - user_mod_path: user_mod_path - vlib_path: vlib_path - vpath: vpath - v2: '-v2' in args - } - if pref.is_verbose || pref.is_debug { - println('C compiler=$pref.ccompiler') - } - if pref.is_so { - out_name_c = get_vtmp_filename(out_name, '.tmp.so.c') - } - $if !linux { - if pref.is_bare && !out_name.ends_with('.c') { - verror('-freestanding only works on Linux for now') - } - } - return &V{ - os: _os - out_name: out_name - dir: dir - compiled_dir: if os.is_dir(rdir) { rdir } else { filepath.dir(rdir) } - lang_dir: vroot - table: new_table(obfuscate) - out_name_c: out_name_c - cgen: new_cgen(out_name_c) - //x64: x64.new_gen(out_name) - vroot: vroot - pref: pref - mod: mod - vgen_buf: vgen_buf - compile_defines: compile_defines - compile_defines_all: compile_defines_all - } -} - -fn non_empty(a []string) []string { - return a.filter(it.len != 0) -} - -pub fn env_vflags_and_os_args() []string { - vosargs := os.getenv('VOSARGS') - if '' != vosargs { - return non_empty(vosargs.split(' ')) - } - mut args := []string - vflags := os.getenv('VFLAGS') - if '' != vflags { - args << os.args[0] - args << vflags.split(' ') - if os.args.len > 1 { - args << os.args[1..] - } - } - else { - args << os.args - } - return non_empty(args) -} - -pub fn create_symlink() { - $if windows { - return - } - vexe := vexe_path() - mut link_path := '/usr/local/bin/v' - mut ret := os.exec('ln -sf $vexe $link_path') or { panic(err) } - if ret.exit_code == 0 { - println('Symlink "$link_path" has been created') - } - else if os.system('uname -o | grep -q \'[A/a]ndroid\'') == 0 { - println('Failed to create symlink "$link_path". Trying again with Termux path for Android.') - link_path = '/data/data/com.termux/files/usr/bin/v' - ret = os.exec('ln -sf $vexe $link_path') or { panic(err) } - if ret.exit_code == 0 { - println('Symlink "$link_path" has been created') - } else { - println('Failed to create symlink "$link_path". Try again with sudo.') - } - } else { - println('Failed to create symlink "$link_path". Try again with sudo.') - } -} - pub fn vexe_path() string { vexe := os.getenv('VEXE') if '' != vexe { @@ -1223,12 +923,3 @@ pub fn set_vroot_folder(vroot_path string) { vname := if os.user_os() == 'windows' { 'v.exe' } else { 'v' } os.setenv('VEXE', os.realpath([vroot_path, vname].join(os.path_separator)), true) } - -pub fn new_v_compiler_with_args(args []string) &V { - vexe := vexe_path() - mut allargs := [vexe] - allargs << args - os.setenv('VOSARGS', allargs.join(' '), true) - return new_v(allargs) -} - diff --git a/vlib/compiler/module_header.v b/vlib/compiler/module_header.v index eb28293801..2ca22443eb 100644 --- a/vlib/compiler/module_header.v +++ b/vlib/compiler/module_header.v @@ -7,6 +7,7 @@ import ( strings os filepath + v.pref ) /* .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 // println('f:') // 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 mut g := VhGen{ consts: strings.new_builder(1000) @@ -166,4 +171,3 @@ fn (g mut VhGen) generate_type() { // g.i = old // g.i-- } - diff --git a/vlib/compiler/modules.v b/vlib/compiler/modules.v index 7cc1944a00..b347415793 100644 --- a/vlib/compiler/modules.v +++ b/vlib/compiler/modules.v @@ -3,11 +3,14 @@ // that can be found in the LICENSE file. module compiler -import os -import filepath +import ( + filepath + os + v.pref +) pub const ( - v_modules_path = os.home_dir() + '.vmodules' + v_modules_path = pref.default_module_path ) // Holds import information scoped to the parsed file struct ImportTable { diff --git a/vlib/compiler/msvc.v b/vlib/compiler/msvc.v index 3102fd927f..a34761ef92 100644 --- a/vlib/compiler/msvc.v +++ b/vlib/compiler/msvc.v @@ -206,16 +206,16 @@ pub fn (v mut V) cc_msvc() { a << '/MDd' } if v.pref.is_so { - if !v.out_name.ends_with('.dll') { - v.out_name = v.out_name + '.dll' + if !v.pref.out_name.ends_with('.dll') { + v.pref.out_name += '.dll' } // Build dll a << '/LD' } - else if !v.out_name.ends_with('.exe') { - v.out_name = v.out_name + '.exe' + else if !v.pref.out_name.ends_with('.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 if v.pref.build_mode == .build_module { // Compile only @@ -263,7 +263,7 @@ pub fn (v mut V) cc_msvc() { a << real_libs.join(' ') a << '/link' a << '/NOLOGO' - a << '/OUT:"$v.out_name"' + a << '/OUT:"$v.pref.out_name"' a << '/LIBPATH:"$r.ucrt_lib_path"' a << '/LIBPATH:"$r.um_lib_path"' a << '/LIBPATH:"$r.vs_lib_path"' diff --git a/vlib/compiler/vtmp.v b/vlib/compiler/vtmp.v index 1d602fd345..79939acc30 100644 --- a/vlib/compiler/vtmp.v +++ b/vlib/compiler/vtmp.v @@ -6,7 +6,7 @@ module compiler import os import filepath -pub fn get_vtmp_folder() string { +fn get_vtmp_folder() string { vtmp := filepath.join(os.tmpdir(),'v') if !os.is_dir(vtmp) { os.mkdir(vtmp)or{ @@ -16,7 +16,7 @@ pub fn get_vtmp_folder() string { 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() return os.realpath(filepath.join(vtmp,filepath.filename(os.realpath(base_file_name)) + postfix)) } diff --git a/vlib/hash/wyhash/wyhash.v b/vlib/hash/wyhash/wyhash.v index cd128fe3ee..3156251e99 100644 --- a/vlib/hash/wyhash/wyhash.v +++ b/vlib/hash/wyhash/wyhash.v @@ -11,7 +11,7 @@ // branch prediction hints. the C version will be // removed once the perfomance is matched. // 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 module wyhash diff --git a/vlib/v/builder/builder.v b/vlib/v/builder/builder.v index c49caa4558..c931236290 100644 --- a/vlib/v/builder/builder.v +++ b/vlib/v/builder/builder.v @@ -102,7 +102,7 @@ pub fn (b &Builder) v_files_from_dir(dir string) []string { if !os.exists(dir) { if dir == 'compiler' && os.is_dir('vlib') { println('looks like you are trying to build V with an old command') - println('use `v -o v v.v` instead of `v -o v compiler`') + println('use `v -o v cmd/v` instead of `v -o v compiler`') } verror("$dir doesn't exist") } diff --git a/vlib/v/pref/default.v b/vlib/v/pref/default.v new file mode 100644 index 0000000000..c56ffd159c --- /dev/null +++ b/vlib/v/pref/default.v @@ -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 +} diff --git a/vlib/v/pref/os.v b/vlib/v/pref/os.v new file mode 100644 index 0000000000..449ed1364e --- /dev/null +++ b/vlib/v/pref/os.v @@ -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') +} diff --git a/vlib/v/pref/pref.v b/vlib/v/pref/pref.v index 99ca8c4a19..583d5803ed 100644 --- a/vlib/v/pref/pref.v +++ b/vlib/v/pref/pref.v @@ -12,69 +12,66 @@ pub enum BuildMode { build_module } -pub enum OS { - mac - linux - windows - freebsd - openbsd - netbsd - dragonfly - js // TODO - android - solaris - haiku -} - pub struct Preferences { pub mut: - build_mode BuildMode - // nofmt bool // disable vfmt - is_test bool // `v test string_test.v` - is_script bool // single file mode (`v program.v`), main function can be skipped - is_live bool // main program that contains live/hot code - is_solive bool // a shared library, that will be used in a -live main program - is_so bool // an ordinary shared library, -shared, no matter if it is live or not - is_prof bool // benchmark every function - translated bool // `v translate doom.v` are we running V code translated from C? allow globals, ++ expressions, etc - is_prod bool // use "-O2" - is_verbose bool // print extra information with `v.log()` - obfuscate bool // `v -obf program.v`, renames functions to "f_XXX" - is_repl bool - is_run bool - show_c_cmd bool // `v -show_c_cmd` prints the C command to build program.v.c - sanitize bool // use Clang's new "-fsanitize" option - is_debug bool // false by default, turned on by -g or -cg, it tells v to pass -g to the C backend compiler. - 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) + os OS // the OS to compile for + build_mode BuildMode + // nofmt bool // disable vfmt + is_test bool // `v test string_test.v` + is_script bool // single file mode (`v program.v`), main function can be skipped + is_live bool // main program that contains live/hot code + is_solive bool // a shared library, that will be used in a -live main program + is_so bool // an ordinary shared library, -shared, no matter if it is live or not + is_prof bool // benchmark every function + translated bool // `v translate doom.v` are we running V code translated from C? allow globals, ++ expressions, etc + is_prod bool // use "-O2" + is_verbose bool // print extra information with `v.log()` + obfuscate bool // `v -obf program.v`, renames functions to "f_XXX" + is_repl bool + is_run bool + show_c_cmd bool // `v -show_c_cmd` prints the C command to build program.v.c + sanitize bool // use Clang's new "-fsanitize" option + is_debug bool // false by default, turned on by -g or -cg, it tells v to pass -g to the C backend compiler. + 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, // 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_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 - 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. + 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_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) + 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. // 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'. - ccompiler string // the name of the used C compiler - building_v bool - autofree bool - compress bool - // skip_builtin bool // Skips re-compilation of the builtin module + ccompiler string // the name of the used C compiler + third_party_option string + building_v bool + autofree bool + compress bool + // skip_builtin bool // Skips re-compilation of the builtin module // to increase compilation time. // This is on by default, since a vast majority of users do not // work on the builtin module itself. - // generating_vh bool - fast bool // use tcc/x64 codegen - enable_globals bool // allow __global for low level code + // generating_vh bool + fast bool // use tcc/x64 codegen + enable_globals bool // allow __global for low level code // is_fmt bool - is_bare bool - user_mod_path string // `v -user_mod_path /Users/user/modules` adds a new lookup path for imported modules - vlib_path string - vpath string - x64 bool - output_cross_c bool - prealloc bool - v2 bool + is_bare bool + user_mod_path string // `v -user_mod_path /Users/user/modules` adds a new lookup path for imported modules + vlib_path string + vpath string + x64 bool + output_cross_c bool + prealloc 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 }