Compare commits

...

No commits in common. "v0.0.12" and "master" have entirely different histories.

4827 changed files with 502076 additions and 687 deletions

15
.cirrus.yml 100644
View File

@ -0,0 +1,15 @@
freebsd_instance:
image_family: freebsd-13-0
freebsd_task:
name: Code CI / freebsd
install_script: pkg install -y git
script: |
echo 'Building V'
git clone https://github.com/vlang/v
cd v
make
##.github/workflows/freebsd_build_tcc.sh
##tcc -v -v
echo 'Build cmd/tools/fast'
cd cmd/tools/fast && ../../../v fast.v && ./fast -clang

44
.ctags.d/v.ctags 100644
View File

@ -0,0 +1,44 @@
## TODO: support more precise struct/const/enum fields
--langdef=V
--map-V=+.v
--map-V=+.vv
--map-V=+.vsh
--kinddef-V=m,imodule,imported modules
--kinddef-V=M,module,modules
--kinddef-V=C,cfunction,cfunctions
--kinddef-V=f,function,functions
--kinddef-V=h,method,functions
--kinddef-V=c,const,constants
--kinddef-V=v,variable,variables
--kinddef-V=s,struct,structs
--kinddef-V=e,enum,enums
--kinddef-V=i,interface,interfaces
--kinddef-V=S,sfield,struct field
--kinddef-V=E,efield,enum field
--_roledef-V.m=imported,imported module
--_roledef-V.M=declared,module declaration
--regex-V=/^module[[:blank:]]+([0-9a-zA-Z]+)[[:blank:]]*$/\1/M/{_role=declared}{scope=push}
--regex-V=/^import[[:blank:]]+(([0-9a-zA-Z]+)?|.*\.([a-zA-Z_][0-9a-zA-Z]+))[[:blank:]]*$/\2\3/m/{_role=imported}{scope=ref}
--regex-V=/^[[:blank:]]*fn[[:blank:]]+C\.([a-zA-Z_][0-9a-zA-Z_]*)\(/C.\1/C/
--regex-V=/^(pub)?[[:blank:]]*fn[[:blank:]]+([a-zA-Z_][0-9a-zA-Z_]*)\(/\2/f/
--regex-V=/^(pub)?[[:blank:]]*fn[[:blank:]]+\(.*\)[[:blank:]]*([a-zA-Z_][0-9a-zA-Z_]*)\(/\2/h/
--regex-V=/^(pub)?[[:blank:]]*struct[[:blank:]]+([a-zA-Z_][0-9a-zA-Z_]*)[[:blank:]]*\{/\2/s/{scope=push}
--regex-V=/^(pub)?[[:blank:]]*enum[[:blank:]]+([a-zA-Z_][0-9a-zA-Z_]*)[[:blank:]]*\{/\2/e/{scope=push}
--regex-V=/^(pub)?[[:blank:]]*interface[[:blank:]]+([a-zA-Z_][0-9a-zA-Z_]*)[[:blank:]]*\{/\2/i/{scope=push}
--regex-V=/^[[:blank:]]*([a-zA-Z_][0-9a-zA-Z_]+)[[:blank:]]*(,)?[[:blank:]]*(\/\/.*)?$/\1/E/{scope=ref}
--regex-V=/^[[:blank:]]*([a-zA-Z_][0-9a-zA-Z_]+)[[:blank:]]+\??\&?(\[[0-9]*\])?([a-zA-Z_][0-9a-zA-Z_.]+)[[:blank:]]*(\/\/.*)?$/\1/S/{scope=ref}
--regex-V=/^[[:blank:]]*\}[[:blank:]]*$//{scope=pop}{placeholder}
## Variables:
--regex-V=/^[[:blank:]]*(mut[[:blank:]]+)?([a-zA-Z_][0-9a-zA-Z_]+)[[:blank:]]*:=/\2/v/
## Consts:
--regex-V=/^(pub)?[[:blank:]]*const[[:blank:]]+\([[:blank:]]*/const/c/{scope=push}
##NB: the next variable regexp should work only inside const ( ), but currently works for ordinary assignments too:
--regex-V=/^[[:blank:]]*([a-zA-Z_][0-9a-zA-Z_]+)[[:blank:]]*=/\1/v/{scope=ref}
--regex-V=/^[[:blank:]]*\)[[:blank:]]*$//{scope=pop}{placeholder}
--extras=+q
--extras=+r
--fields=+r

29
.editorconfig 100644
View File

@ -0,0 +1,29 @@
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.v]
indent_style = tab
indent_size = 4
[*.{yml,yaml}]
indent_style = space
indent_size = 2
[*.md]
trim_trailing_whitespace = false
# lines that are too long will trigger an error in cmd/tools/vcheck-md.v
# run v check-md [folder/file] to test markdown files
# the longest normal line is specified with this constant:
# `too_long_line_length_other = 100`
max_line_length = 100
[*.{txt,out}]
insert_final_newline = false
[{Makefile,GNUmakefile}]
indent_style = tab

9
.gitattributes vendored
View File

@ -1,2 +1,7 @@
*.v linguist-language=Go
website/* linguist-vendored
*.v linguist-language=V text=auto eol=lf
*.vv linguist-language=V text=auto eol=lf
*.vsh linguist-language=V text=auto eol=lf
**/v.mod linguist-language=V text=auto eol=lf
*.bat text=auto eol=crlf
Dockerfile.* linguist-language=Dockerfile
*.toml text eol=lf

1
.github/FUNDING.yml vendored
View File

@ -1,3 +1,4 @@
# These are supported funding model platforms
patreon: vlang
github: [medvednikov]

View File

@ -0,0 +1,25 @@
---
name: Bug report for V
about: Please use the appropriate label when submitting an issue: bug/feature request/question.
title: "New issue"
labels: ''
assignees: ''
---
<!-- You can use `v doctor` to fill up the next fields -->
#### V version:
#### OS:
#### C Compiler:
## What did you do?
### What did you expect to see?
### What did you see instead?

View File

@ -0,0 +1,25 @@
---
name: Bug report
about: Bug report
title: ''
labels: 'Bug'
assignees: ''
# When updating, make sure to update the template in 'cmd/tools/vbug.v' too
---
<!-- Please make sure to run `v up` before reporting any issues as it may have already been fixed.
It's also advisable to update all relevant modules using `v outdated` and `v install` -->
<!-- You can use `v doctor` to fill up the next fields -->
**V version:**
**OS:**
<!-- Please include the standalone minimal reproducible code.
If not, it may be closed by the administrators. -->
**What did you do?**
**What did you expect to see?**
**What did you see instead?**

View File

@ -0,0 +1,10 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: 'Feature Request'
assignees: ''
---

35
.github/PULL_REQUEST_TEMPLATE vendored 100644
View File

@ -0,0 +1,35 @@
<!--
Please title your PR as follows: `time: fix foo bar`.
Always start with the thing you are fixing, then describe the fix.
Don't use past tense (e.g. "fixed foo bar").
Explain what your PR does and why.
If you are adding a new function, please document it and add tests:
```
// foo does foo and bar
fn foo() {
// file_test.v
fn test_foo() {
assert foo() == ...
...
}
```
If you are fixing a bug, please add a test that covers it.
Before submitting a PR, please run `v test-all` .
See also `TESTS.md`.
I try to process PRs as soon as possible. They should be handled within 24 hours.
Applying labels to PRs is not needed.
Thanks a lot for your contribution!
-->

View File

@ -0,0 +1,15 @@
#!/bin/sh -l
set -e
pwd
uname -a
make -j4
./v version
du -s .
echo "DONE"

View File

@ -0,0 +1,24 @@
#!/bin/sh -l
set -e
pwd
uname -a
du -s .
ls -lat
##./v test-all
## try running the known failing tests first to get faster feedback
./v test vlib/builtin/string_test.v vlib/strings/builder_test.v
./v test-cleancode
./v test-self
./v build-vbinaries
echo "DONE"

View File

@ -0,0 +1,67 @@
#!/usr/bin/env -S v
module main
import os
import vab.vxt
import vab.android.ndk
fn main() {
assert ndk.found()
assert vxt.found()
work_dir := os.join_path(os.temp_dir(), 'android_cross_compile_test')
os.rm(work_dir) or {}
os.mkdir_all(work_dir) or { panic(err) }
vexe := vxt.vexe()
examples_dir := os.join_path(vxt.home(), 'examples')
v_example := os.join_path(examples_dir, 'toml.v')
ndk_version := ndk.default_version()
sysroot_path := ndk.sysroot_path(ndk_version) or { panic(err) }
include_path := os.join_path(sysroot_path, 'usr', 'include')
android_include_path := os.join_path(include_path, 'android')
//'-I"$include_path"'
cflags := ['-I"$android_include_path"', '-Wno-unused-value', '-Wno-implicit-function-declaration',
'-Wno-int-conversion']
for arch in ndk.supported_archs {
for level in ['min', 'max'] {
compiler_api := match level {
'min' {
ndk.compiler_min_api(.c, ndk_version, arch) or { panic(err) }
}
'max' {
ndk.compiler_max_api(.c, ndk_version, arch) or { panic(err) }
}
else {
panic('invalid min/max level')
}
}
os.setenv('VCROSS_COMPILER_NAME', compiler_api, true)
c_file := os.join_path(work_dir, arch + '-' + level + '.c')
o_file := os.join_path(work_dir, arch + '-' + level + '.o')
// x.v -> x.c
v_compile_cmd := '$vexe -o $c_file -os android -gc none $v_example'
vres := os.execute(v_compile_cmd)
if vres.exit_code != 0 {
panic('"$v_compile_cmd" failed: $vres.output')
}
assert os.exists(c_file)
// x.c -> x.o
compile_cmd := '$compiler_api ${cflags.join(' ')} -c $c_file -o $o_file'
cres := os.execute(compile_cmd)
if cres.exit_code != 0 {
panic('"$compile_cmd" failed: $cres.output')
}
assert os.exists(o_file)
compiler_exe_name := os.file_name(compiler_api)
println('Compiled examples/toml.v successfully for ($level) $arch $compiler_exe_name')
}
}
}

View File

@ -0,0 +1,163 @@
name: Build binary artifacts
on:
push:
tags:
- weekly.**
- 0.**
jobs:
build-linux:
runs-on: ubuntu-20.04
env:
CC: gcc
ZIPNAME: v_linux.zip
steps:
- uses: actions/checkout@v1
- name: Compile
run: |
make -j4
./v -cc $CC -prod -o v cmd/v
./v -cc $CC -prod cmd/tools/vup.v
./v -cc $CC -prod cmd/tools/vdoctor.v
- name: Remove excluded
run: |
rm -rf .git
rm -rf vc/
rm -rf v_old
- name: Create ZIP archive
run: |
cd ..
zip -r9 --symlinks $ZIPNAME v/
mv $ZIPNAME v/
cd v/
- name: Create artifact
uses: actions/upload-artifact@v2
with:
name: linux
path: v_linux.zip
build-macos:
runs-on: macos-latest
env:
CC: clang
ZIPNAME: v_macos.zip
steps:
- uses: actions/checkout@v1
- name: Compile
run: |
make -j4
./v -cc $CC -prod -o v cmd/v
./v -cc $CC -prod cmd/tools/vup.v
./v -cc $CC -prod cmd/tools/vdoctor.v
- name: Remove excluded
run: |
rm -rf .git
rm -rf vc/
rm -rf v_old
- name: Create ZIP archive
run: |
cd ..
zip -r9 --symlinks $ZIPNAME v/
mv $ZIPNAME v/
cd v/
- name: Create artifact
uses: actions/upload-artifact@v2
with:
name: macos
path: v_macos.zip
build-windows:
runs-on: windows-latest
env:
CC: msvc
ZIPNAME: v_windows.zip
steps:
- uses: actions/checkout@v1
- uses: msys2/setup-msys2@v2
- name: Compile
run: |
.\make.bat -tcc
.\v.exe cmd\tools\vup.v
.\v.exe cmd\tools\vdoctor.v
- name: Remove excluded
shell: msys2 {0}
run: |
rm -rf .git
rm -rf vc/
rm -rf v_old.exe
- name: Create archive
shell: msys2 {0}
run: |
cd ..
powershell Compress-Archive v $ZIPNAME
mv $ZIPNAME v/
cd v/
# NB: the powershell Compress-Archive line is from:
# https://superuser.com/a/1336434/194881
# It is needed, because `zip` is not installed by default :-|
- name: Create artifact
uses: actions/upload-artifact@v2
with:
name: windows
path: v_windows.zip
release:
name: Create Github Release
needs: [build-linux, build-windows, build-macos]
runs-on: ubuntu-20.04
steps:
- name: Get short tag name
uses: jungwinter/split@v1
id: split
with:
msg: ${{ github.ref }}
seperator: /
- name: Create Release
id: create_release
uses: ncipollo/release-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ steps.split.outputs._2 }}
name: ${{ steps.split.outputs._2 }}
commit: ${{ github.sha }}
draft: false
prerelease: false
publish:
needs: [release]
runs-on: ubuntu-20.04
strategy:
matrix:
version: [linux, macos, windows]
steps:
- uses: actions/checkout@v1
- name: Fetch artifacts
uses: actions/download-artifact@v1
with:
name: ${{ matrix.version }}
path: ./${{ matrix.version }}
- name: Get short tag name
uses: jungwinter/split@v1
id: split
with:
msg: ${{ github.ref }}
seperator: /
- name: Get release
id: get_release_info
uses: leahlundqvist/get-release@v1.3.1
env:
GITHUB_TOKEN: ${{ github.token }}
with:
tag_name: ${{ steps.split.outputs._2 }}
- name: Upload Release Asset
id: upload-release-asset
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.get_release_info.outputs.upload_url }}
asset_path: ${{ matrix.version }}/v_${{ matrix.version }}.zip
asset_name: v_${{ matrix.version }}.zip
asset_content_type: application/zip

View File

@ -0,0 +1,56 @@
#!/bin/bash
set -e
V=$PWD/v
if [[ -x "$V" ]]
then
echo "The v executable exists."
else
echo "This script should be run from the top level folder of a V repository"
echo "i.e. the folder where your V executable is."
exit 1
fi
BUILD=$PWD/vinix_build
echo "Creating $BUILD folder..."
rm -rf $BUILD
mkdir -p $BUILD
cd $BUILD
echo "Clone current Vinix"
git clone https://github.com/vlang/vinix.git --depth=1
cd $BUILD
echo "Clone current mlibc"
git clone https://github.com/managarm/mlibc.git --depth=1
cd $BUILD
echo "Patch mlibc for Vinix"
cd mlibc
patch -p3 < ../vinix/patches/mlibc/mlibc.patch
cd $BUILD
echo "Install mlibc headers"
mkdir mlibc-build
cd mlibc-build
meson --cross-file ../vinix/cross_file.txt --prefix=/ -Dheaders_only=true ../mlibc
ninja
mkdir ../mlibc-headers
DESTDIR=`realpath ../mlibc-headers` ninja install
cd $BUILD
echo "Attempt to build the Vinix kernel (debug)"
cd vinix/kernel
make PROD=false CFLAGS="-D__vinix__ -O2 -g -pipe -I../../mlibc-headers/include"
make clean
cd $BUILD
echo "Attempt to build the Vinix kernel (prod)"
cd vinix/kernel
make PROD=true CFLAGS="-D__vinix__ -O2 -g -pipe -I../../mlibc-headers/include"
make clean
rm -rf $BUILD

18
.github/workflows/cancel.yml vendored 100644
View File

@ -0,0 +1,18 @@
name: Cancel previous workflows
on:
workflow_run:
workflows: ["Code CI"]
types:
- requested
jobs:
cancel-previous-workflows:
runs-on: ubuntu-latest
steps:
- uses: spaceface777/cancel-workflow-action@0.9.1
# don't cancel CI for commits pushed to vlang/v#master (if ci is still too slow, this can be removed safely)
if: ${{ github.event.workflow_run.head_repository.full_name != 'vlang/v' || github.event.workflow_run.head_branch != 'master' }}
with:
# workflow ids for `Code CI` and `Sanitized CI` (from https://api.github.com/repos/vlang/v/actions/workflows):
workflow_id: 4577,7940868 # the other workflows finish quickly - no need to skip them

680
.github/workflows/ci.yml vendored 100644
View File

@ -0,0 +1,680 @@
name: Code CI
on:
push:
paths-ignore:
- "**.md"
pull_request:
paths-ignore:
- "**.md"
concurrency:
group: build-ci-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
jobs:
ubuntu-tcc:
runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 121
env:
VFLAGS: -cc tcc -no-retry-compilation
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install --quiet -y libssl-dev sqlite3 libsqlite3-dev valgrind
sudo apt-get install --quiet -y libfreetype6-dev libxi-dev libxcursor-dev libgl-dev
## The following is needed for examples/wkhtmltopdf.v
wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.focal_amd64.deb
sudo apt-get install --quiet -y xfonts-75dpi xfonts-base
sudo dpkg -i wkhtmltox_0.12.6-1.focal_amd64.deb
- name: Build v
run: |
echo $VFLAGS
make
./v test-cleancode
./v -d debug_malloc -d debug_realloc -o v cmd/v
./v -cg -cstrict -o v cmd/v
# Test v -realloc arena allocation
./v -o vrealloc -prealloc cmd/v && ./vrealloc -o v3 cmd/v && ./v3 -o v4 cmd/v
- name: Test v->c
run: |
thirdparty/tcc/tcc.exe -version
./v -cg -o v cmd/v # Make sure vtcc can build itself twice
# ./v test-all
- name: v self compilation
run: ./v -o v2 cmd/v && ./v2 -o v3 cmd/v && ./v3 -o v4 cmd/v
- name: v self compilation with -skip-unused
run: ./v -skip-unused -o v2 cmd/v && ./v2 -skip-unused -o v3 cmd/v && ./v3 -skip-unused -o v4 cmd/v
- name: v doctor
run: |
./v doctor
- name: Verify `v test` works
run: |
echo $VFLAGS
./v cmd/tools/test_if_v_test_system_works.v
./cmd/tools/test_if_v_test_system_works
- name: All code is formatted
run: ./v test-cleancode
- name: Self tests
run: ./v test-self
# - name: Self tests (-cstrict)
# run: V_CI_CSTRICT=1 ./v -cstrict test-self
- name: Test time functions in a timezone UTC-12
run: TZ=Etc/GMT+12 ./v test vlib/time/
- name: Test time functions in a timezone UTC-3
run: TZ=Etc/GMT+3 ./v test vlib/time/
- name: Test time functions in a timezone UTC+3
run: TZ=Etc/GMT-3 ./v test vlib/time/
- name: Test time functions in a timezone UTC+12
run: TZ=Etc/GMT-12 ./v test vlib/time/
- name: Test time functions in a timezone using daylight saving (Europe/Paris)
run: TZ=Europe/Paris ./v test vlib/time/
- name: Build examples
run: ./v -W build-examples
- name: Test building v tools
run: ./v -W build-tools
- name: Test v binaries
run: ./v build-vbinaries
- name: Run a VSH script
run: ./v run examples/v_script.vsh
- name: Test v tutorials
run: ./v tutorials/building_a_simple_web_blog_with_vweb/code/blog
- name: Build cmd/tools/fast
run: cd cmd/tools/fast && ../../../v fast.v && ./fast
- name: V self compilation with -usecache
run: |
unset VFLAGS
./v -usecache examples/hello_world.v && examples/hello_world
./v -o v2 -usecache cmd/v
./v2 -o v3 -usecache cmd/v
./v3 version
./v3 -o tetris -usecache examples/tetris/tetris.v
ubuntu-tcc-boehm-gc:
runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 121
env:
VFLAGS: -cc tcc -no-retry-compilation
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install --quiet -y libssl-dev sqlite3 libsqlite3-dev valgrind
sudo apt-get install --quiet -y libfreetype6-dev libxi-dev libxcursor-dev libgl-dev
sudo apt-get install --quiet -y libgc-dev
## The following is needed for examples/wkhtmltopdf.v
wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.focal_amd64.deb
sudo apt-get install --quiet -y xfonts-75dpi xfonts-base
sudo dpkg -i wkhtmltox_0.12.6-1.focal_amd64.deb
- name: Build v
run: |
echo $VFLAGS
make
- name: Test v->c
run: |
thirdparty/tcc/tcc.exe -version
./v -cg -o v cmd/v # Make sure vtcc can build itself twice
- name: v self compilation with -gc boehm
run: |
./v -gc boehm -o v2 cmd/v && ./v2 -gc boehm -o v3 cmd/v && ./v3 -gc boehm -o v4 cmd/v
mv v4 v
- name: v doctor
run: |
./v doctor
- name: Verify `v -gc boehm test` works
run: |
./v -gc boehm cmd/tools/test_if_v_test_system_works.v
./cmd/tools/test_if_v_test_system_works
- name: All code is formatted
run: ./v test-cleancode
- name: Self tests with `-gc boehm` with V compiler using Boehm-GC itself
run: ./v -gc boehm test-self
- name: Test leak detector
run: |
./v -gc boehm_leak -o testcase_leak vlib/v/tests/testcase_leak.vv
./testcase_leak 2>leaks.txt
grep "Found 1 leaked object" leaks.txt && grep -P ", sz=\s?1000," leaks.txt
- name: Test leak detector not being active for `-gc boehm`
run: |
./v -gc boehm -o testcase_leak vlib/v/tests/testcase_leak.vv
./testcase_leak 2>leaks.txt
[ "$(stat -c %s leaks.txt)" = "0" ]
- name: Test leak detector not being active for normal compile
run: |
./v -o testcase_leak vlib/v/tests/testcase_leak.vv
./testcase_leak 2>leaks.txt
[ "$(stat -c %s leaks.txt)" = "0" ]
macos:
runs-on: macOS-12
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 121
env:
VFLAGS: -cc clang
PKG_CONFIG_PATH: /usr/local/opt/openssl@3/lib/pkgconfig
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12.x
- name: Install dependencies
run: |
##brew install libpq openssl freetype ### these are *already installed* on Catalina ...
brew uninstall --ignore-dependencies libpq ## libpq is a dependency of PHP
brew install postgresql openssl
export LIBRARY_PATH="$LIBRARY_PATH:/usr/local/opt/openssl/lib/"
echo "PKG_CONFIG_PATH is '$PKG_CONFIG_PATH'"
- name: Build V
run: make -j4 && ./v -cg -cstrict -o v cmd/v
- name: Run sanitizers
run: |
./v -o v2 cmd/v -cflags -fsanitize=undefined
UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 ./v2 -o v.c cmd/v
- name: Build V using V
run: ./v -o v2 cmd/v && ./v2 -o v3 cmd/v
- name: Test symlink
run: ./v symlink
# - name: Set up pg database
# run: |
# pg_ctl -D /usr/local/var/postgres -l /usr/local/var/postgres/server.log start
# psql -d postgres -c 'select rolname from pg_roles'
# psql -d postgres -c 'create database customerdb;'
# psql -d customerdb -f examples/database/pg/mydb.sql
# - name: Test v->c
# run: ./v test-all
# - name: Test v binaries
# run: ./v build-vbinaries
## - name: Test v->js
## run: ./v -o hi.js examples/hello_v_js.v && node hi.js
- name: Verify `v test` works
run: |
echo $VFLAGS
./v cmd/tools/test_if_v_test_system_works.v
./cmd/tools/test_if_v_test_system_works
- name: All code is formatted
run: VJOBS=1 ./v test-cleancode
- name: Self tests
run: VJOBS=1 ./v test-self
- name: Build examples
run: ./v build-examples
- name: Build examples with -autofree
run: |
./v -autofree -o tetris examples/tetris/tetris.v
./v -autofree -o blog tutorials/building_a_simple_web_blog_with_vweb/code/blog
- name: v doctor
run: |
./v doctor
- name: Test ved
run: |
git clone --depth 1 https://github.com/vlang/ved
cd ved && ../v -o ved .
../v -autofree .
cd ..
# - name: Test c2v
# run: |
# git clone --depth 1 https://github.com/vlang/c2v
# cd c2v && ../v -o c2v .
# ../v .
# ../v run tests/run_tests.vsh
# ../v -experimental -w c2v_test.v
# cd ..
- name: Build V UI examples
run: |
git clone --depth 1 https://github.com/vlang/ui
cd ui
mkdir -p ~/.vmodules
ln -s $(pwd) ~/.vmodules/ui
../v examples/rectangles.v
## ../v run examples/build_examples.vsh
- name: V self compilation with -usecache
run: |
unset VFLAGS
./v -usecache examples/hello_world.v && examples/hello_world
./v -o v2 -usecache cmd/v
./v2 -o v3 -usecache cmd/v
./v3 version
./v3 -o tetris -usecache examples/tetris/tetris.v
ubuntu:
runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 121
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12.x
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install --quiet -y postgresql libpq-dev libssl-dev sqlite3 libsqlite3-dev valgrind
sudo apt-get install --quiet -y libfreetype6-dev libxi-dev libxcursor-dev libgl-dev
- name: Build V
run: make -j4 && ./v -cc gcc -cg -cstrict -o v cmd/v
- name: Valgrind v.c
run: valgrind --error-exitcode=1 ./v -o v.c cmd/v
- name: Run sanitizers
run: |
./v -o v2 cmd/v -cflags -fsanitize=thread
./v -o v3 cmd/v -cflags "-fsanitize=undefined -fno-sanitize=alignment"
UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 ./v2 -o v.c cmd/v
UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 ./v3 -o v.c cmd/v
# - name: Test V
# run: ./v test-all
# - name: Test v binaries
# run: ./v build-vbinaries
## - name: Test v->js
## run: ./v -o hi.js examples/hello_v_js.v && node hi.js
# - name: Build Vorum
# run: git clone --depth 1 https://github.com/vlang/vorum && cd vorum && ../v . && cd ..
- name: Build vpm
run: git clone --depth 1 https://github.com/vlang/vpm && cd vpm && ../v . && cd ..
- name: Freestanding
run: ./v -freestanding run vlib/os/bare/bare_example_linux.v
- name: V self compilation
run: ./v -o v2 cmd/v && ./v2 -o v3 cmd/v && ./v3 -o v4 cmd/v
- name: V self compilation with -usecache
run: |
unset VFLAGS
./v -usecache examples/hello_world.v && examples/hello_world
./v -o v2 -usecache cmd/v
./v2 -o v3 -usecache cmd/v
./v3 version
./v3 -o tetris -usecache examples/tetris/tetris.v
- name: Verify `v test` works
run: |
echo $VFLAGS
./v cmd/tools/test_if_v_test_system_works.v
./cmd/tools/test_if_v_test_system_works
- name: All code is formatted
run: ./v test-cleancode
- name: Self tests
run: ./v test-self
- name: Self tests (-prod)
run: ./v -o vprod -prod cmd/v && ./vprod test-self
- name: Self tests (-cstrict)
run: VTEST_JUST_ESSENTIAL=1 V_CI_CSTRICT=1 ./v -cc gcc -cstrict test-self
- name: Build examples
run: ./v build-examples
- name: Build tetris.v with -autofree
run: ./v -autofree -experimental -o tetris examples/tetris/tetris.v
- name: Build option_test.v with -autofree
run: ./v -autofree vlib/v/tests/option_test.v
- name: Build modules
run: |
./v build-module vlib/os
./v build-module vlib/builtin
./v build-module vlib/strconv
./v build-module vlib/time
./v build-module vlib/term
./v build-module vlib/math
./v build-module vlib/strings
./v build-module vlib/v/token
./v build-module vlib/v/ast
./v build-module vlib/v/parser
./v build-module vlib/v/gen/c
./v build-module vlib/v/depgraph
./v build-module vlib/os/cmdline
- name: native machine code generation
run: |
exit
./v -o vprod -prod cmd/v
cd cmd/tools
echo "Generating a 1m line V file..."
../../vprod gen1m.v
./gen1m > 1m.v
echo "Building it..."
../../vprod -backend native -o 1m 1m.v
echo "Running it..."
ls
# ./1m
# run: echo "TODO" #cd examples/native && ../../v -native hello_world.v && ./hello_world
# - name: Coveralls GitHub Action
# uses: coverallsapp/github-action@v1.0.1
# with:
# github-token: ${{ secrets.GITHUB_TOKEN }}
ubuntu-clang:
runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 121
env:
VFLAGS: -cc clang
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12.x
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install --quiet -y postgresql libpq-dev libssl-dev sqlite3 libsqlite3-dev valgrind
sudo apt-get install --quiet -y libfreetype6-dev libxi-dev libxcursor-dev libgl-dev
sudo apt-get install --quiet -y clang
- name: Build V
run: make -j4 && ./v -cc clang -cg -cstrict -o v cmd/v
- name: Valgrind
run: valgrind --error-exitcode=1 ./v -o v.c cmd/v
- name: Run sanitizers
run: |
./v -o v2 cmd/v -cflags -fsanitize=memory
./v -o v3 cmd/v -cflags -fsanitize=thread
./v -o v4 cmd/v -cflags -fsanitize=undefined
./v -o v5 cmd/v -cflags -fsanitize=address,pointer-compare,pointer-subtract
UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 ./v2 -o v.c cmd/v
UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 ./v3 -o v.c cmd/v
UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 ./v4 -o v.c cmd/v
ASAN_OPTIONS=detect_leaks=0 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 ./v5 -o v.c cmd/v
- name: v self compilation
run: ./v -o v2 cmd/v && ./v2 -o v3 cmd/v && ./v3 -o v4 cmd/v
- name: v self compilation with -usecache
run: |
unset VFLAGS
./v -usecache examples/hello_world.v && examples/hello_world
./v -o v2 -usecache cmd/v
./v2 -o v3 -usecache cmd/v
./v3 version
./v3 -o tetris -usecache examples/tetris/tetris.v
- name: Verify `v test` works
run: |
echo $VFLAGS
./v cmd/tools/test_if_v_test_system_works.v
./cmd/tools/test_if_v_test_system_works
- name: All code is formatted
run: ./v test-cleancode
- name: Self tests
run: ./v test-self
- name: Self tests (vprod)
run: |
./v -o vprod -prod cmd/v
./vprod test-self
- name: Self tests (-cstrict)
run: VTEST_JUST_ESSENTIAL=1 V_CI_CSTRICT=1 ./vprod -cstrict test-self
- name: Build examples
run: ./v build-examples
- name: Build examples with -autofree
run: |
./v -autofree -experimental -o tetris examples/tetris/tetris.v
- name: Build modules
run: |
./v build-module vlib/os
./v build-module vlib/builtin
./v build-module vlib/strconv
./v build-module vlib/time
./v build-module vlib/term
./v build-module vlib/math
./v build-module vlib/strings
./v build-module vlib/v/token
./v build-module vlib/v/ast
./v build-module vlib/v/parser
./v build-module vlib/v/gen/c
./v build-module vlib/v/depgraph
./v build-module vlib/os/cmdline
- name: native machine code generation
run: |
exit
./v -o vprod -prod cmd/v
cd cmd/tools
echo "Generating a 1m line V file..."
../../vprod gen1m.v
./gen1m > 1m.v
echo "Building it..."
../../vprod -backend native -o 1m 1m.v
echo "Running it..."
ls
windows-gcc:
runs-on: windows-2019
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 121
env:
VFLAGS: -cc gcc
VERBOSE_MAKE: 1
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12.x
- name: Build
run: |
gcc --version
.\make.bat -gcc
- name: Test new v.c
run: |
.\v.exe -o v.c cmd/v
gcc -Werror -municode -w v.c
- name: Install dependencies
run: |
.\v.exe setup-freetype
.\.github\workflows\windows-install-sqlite.bat
- name: v doctor
run: |
./v doctor
- name: Verify `v test` works
run: |
echo $VFLAGS
./v cmd/tools/test_if_v_test_system_works.v
./cmd/tools/test_if_v_test_system_works
- name: All code is formatted
run: ./v test-cleancode
- name: Self tests
run: .\v.exe test-self
# - name: Test
# run: .\v.exe test-all
- name: Test time functions in a timezone UTC-12
run: |
tzutil /s "Dateline Standard Time"
./v test vlib/time/
- name: Test time functions in a timezone UTC-3
run: |
tzutil /s "Greenland Standard Time"
./v test vlib/time/
- name: Test time functions in a timezone UTC+3
run: |
tzutil /s "Russian Standard Time"
./v test vlib/time/
- name: Test time functions in a timezone UTC+12
run: |
tzutil /s "New Zealand Standard Time"
./v test vlib/time/
- name: Test v->js
run: ./v -o hi.js examples/hello_v_js.v && node hi.js
- name: Test v binaries
run: ./v build-vbinaries
- name: Build examples
run: ./v build-examples
- name: v2 self compilation
run: .\v.exe -o v2.exe cmd/v && .\v2.exe -o v3.exe cmd/v
windows-msvc:
runs-on: windows-2019
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 121
env:
VFLAGS: -cc msvc
VERBOSE_MAKE: 1
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12.x
- name: Build
run: |
echo %VFLAGS%
echo $VFLAGS
.\make.bat -msvc
.\v.exe -cflags /WX self
- name: Install dependencies
run: |
.\v.exe setup-freetype
.\.github\workflows\windows-install-sqlite.bat
- name: v doctor
run: |
./v doctor
- name: Verify `v test` works
run: |
echo $VFLAGS
./v cmd/tools/test_if_v_test_system_works.v
./cmd/tools/test_if_v_test_system_works
### TODO: test-cleancode fails with msvc. Investigate why???
## - name: All code is formatted
## run: ./v test-cleancode
- name: Self tests
run: |
./v -cg cmd\tools\vtest-self.v
./v test-self
# - name: Test
# run: .\v.exe test-all
- name: Test v->js
run: ./v -o hi.js examples/hello_v_js.v && node hi.js
- name: Test v binaries
run: ./v build-vbinaries
- name: Build examples
run: ./v build-examples
- name: v2 self compilation
run: .\v.exe -o v2.exe cmd/v && .\v2.exe -o v3.exe cmd/v
windows-tcc:
runs-on: windows-2019
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 121
env:
VFLAGS: -cc tcc -no-retry-compilation
VJOBS: 1
VTEST_SHOW_START: 1
VERBOSE_MAKE: 1
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12.x
- name: Build with make.bat -tcc
run: |
.\make.bat -tcc
- name: Test new v.c
run: |
.\v.exe -o v.c cmd/v
.\thirdparty\tcc\tcc.exe -Werror -w -ladvapi32 -bt10 v.c
- name: Install dependencies
run: |
.\v.exe setup-freetype
.\.github\workflows\windows-install-sqlite.bat
- name: v doctor
run: |
./v doctor
- name: Verify `v test` works
run: |
.\v.exe cmd/tools/test_if_v_test_system_works.v
.\cmd\tools\test_if_v_test_system_works.exe
- name: Verify `v vlib/v/gen/c/coutput_test.v` works
run: |
.\v.exe vlib/v/gen/c/coutput_test.v
- name: Make sure running TCC64 instead of TCC32
run: ./v test .github\workflows\make_sure_ci_run_with_64bit_compiler_test.v
- name: Test ./v doc -v clipboard *BEFORE building tools*
run: ./v doc -v clipboard
- name: Test v build-tools
run: ./v -W build-tools
- name: All code is formatted
run: ./v test-cleancode
- name: Self tests
run: ./v test-self
- name: Test v->js
run: ./v -o hi.js examples/hello_v_js.v && node hi.js
- name: Test v binaries
run: ./v build-vbinaries
- name: Build examples
run: ./v build-examples
- name: v2 self compilation
run: .\v.exe -o v2.exe cmd/v && .\v2.exe -o v3.exe cmd/v && .\v3.exe -o v4.exe cmd/v
- name: v2 self compilation with -gc boehm
run: .\v.exe -o v2.exe -gc boehm cmd/v && .\v2.exe -o v3.exe -gc boehm cmd/v && .\v3.exe -o v4.exe -gc boehm cmd/v
## ## tcc32
## - name: Build with make.bat -tcc32
## run: |
## Remove-Item -Recurse -Force .\thirdparty\tcc
## .\v.exe wipe-cache
## .\make.bat -tcc32
## - name: Test new v.c
## run: .\v.exe -o v.c cmd/v && .\thirdparty\tcc\tcc.exe -Werror -g -w -ladvapi32 -bt10 v.c
## - name: v doctor
## run: ./v doctor
##
## - name: Verify `v test` works
## run: |
## .\v.exe cmd/tools/test_if_v_test_system_works.v
## .\cmd\tools\test_if_v_test_system_works.exe
##
## - name: Verify `v vlib/v/gen/c/coutput_test.v` works
## run: |
## .\v.exe vlib/v/gen/c/coutput_test.v
##
## - name: Make sure running TCC32 instead of TCC64
## run: ./v -stats .github\workflows\make_sure_ci_run_with_32bit_compiler_test.v
##
## - name: Test v build-tools
## run: ./v -W build-tools
##
## - name: Test ./v doc clipboard
## run: ./v doc clipboard
##
## - name: Self tests
## run: ./v test-self
## - name: Test v->js
## run: ./v -o hi.js examples/hello_v_js.v && node hi.js
## - name: Test v binaries
## run: ./v build-vbinaries
## - name: Build examples
## run: ./v build-examples
## - name: v2 self compilation
## run: .\v.exe -o v2.exe cmd/v && .\v2.exe -o v3.exe cmd/v
# ubuntu-autofree-selfcompile:
# runs-on: ubuntu-20.04
# if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
# timeout-minutes: 121
# env:
# VFLAGS: -cc gcc
# steps:
# - uses: actions/checkout@v2
# - name: Build V
# run: make -j4
# - name: V self compilation with -autofree
# run: ./v -o v2 -autofree cmd/v && ./v2 -o v3 -autofree cmd/v && ./v3 -o v4 -autofree cmd/v
# ubuntu-musl:
# runs-on: ubuntu-20.04
# if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
# timeout-minutes: 121
# env:
# VFLAGS: -cc musl-gcc
# V_CI_MUSL: 1
# steps:
# - uses: actions/checkout@v2
# - uses: actions/setup-node@v1
# with:
# node-version: 12.x
# - name: Install dependencies
# run: |
# sudo apt-get install --quiet -y musl musl-tools libssl-dev sqlite3 libsqlite3-dev valgrind
# - name: Build v
# run: echo $VFLAGS && make -j4 && ./v -cg -o v cmd/v
# # - name: Test v binaries
# # run: ./v build-vbinaries
# ## - name: Test v->js
# ## run: ./v -o hi.js examples/hello_v_js.v && node hi.js
# - name: quick debug
# run: ./v -stats vlib/strconv/format_test.v
# - name: Self tests
# run: ./v test-self

View File

@ -0,0 +1,79 @@
name: Bootstraping works
on:
push:
paths-ignore:
- "**.md"
pull_request:
paths-ignore:
- "**.md"
jobs:
ubuntu:
runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 30
env:
VFLAGS: -cc tcc -no-retry-compilation
B_CFLAGS: -g -std=gnu11 -w
B_LFLAGS: -lm -lpthread
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 10
- name: Build v
run: make
- name: v.c can be compiled and run with -os cross (bootstrapping works)
run: |
ls -la v vc/v.c
./v -os cross -o vc/v.c cmd/v
gcc $B_CFLAGS -o v_from_vc vc/v.c $B_LFLAGS
ls -lart v_from_vc
./v_from_vc version
./v_from_vc run examples/hello_world.v
./v_from_vc -o v_from_vc_produced_native_v cmd/v
./v_from_vc_produced_native_v run examples/hello_world.v
make local=1
ls -la v vc/v.c v_from_vc v_from_vc_produced_native_v
- name: Ensure v up works
run: |
./v cmd/tools/oldv.v
./cmd/tools/oldv -v HEAD^^^^^
cd ~/.cache/oldv/v_at_HEAD_____/
./v version
./v -v up
./v version
./v -o v2 cmd/v
./v2 -o v3 cmd/v
macos:
runs-on: macos-11
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 30
env:
VFLAGS: -cc clang
B_CFLAGS: -g -std=gnu11 -w
B_LFLAGS: -lm -lpthread
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 10
- name: Build V
run: make && ./v -cg -cstrict -o v cmd/v
- name: v.c can be compiled and run with -os cross (bootstrapping works)
run: |
ls -la v vc/v.c
./v -os cross -o vc/v.c cmd/v
cc $B_CFLAGS -o v_from_vc vc/v.c $B_LFLAGS
ls -lart v_from_vc
./v_from_vc version
./v_from_vc run examples/hello_world.v
./v_from_vc -o v_from_vc_produced_native_v cmd/v
./v_from_vc_produced_native_v run examples/hello_world.v
### the next make invocation will simulate building V from scratch,
### using this commit
make local=1
ls -la v vc/v.c v_from_vc v_from_vc_produced_native_v

111
.github/workflows/ci_cross.yml vendored 100644
View File

@ -0,0 +1,111 @@
name: Cross CI
on:
push:
paths-ignore:
- "**.md"
pull_request:
paths-ignore:
- "**.md"
jobs:
macos-cross:
runs-on: macOS-latest
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 25
env:
VFLAGS: -cc clang
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 10
- name: Install dependencies
run: |
brew install mingw-w64
export LIBRARY_PATH="$LIBRARY_PATH:/usr/local/opt/openssl/lib/"
- name: Build V
run: make
- name: Test symlink
run: ./v symlink
- name: Cross-compilation to Linux
run: |
./v -os linux cmd/v
# TODO: fix this: ./v -os linux examples/2048/2048.v
- name: Cross-compilation to Windows
run: |
./v -os windows cmd/v
./v -os windows examples/2048/2048.v
linux-cross:
runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 25
env:
VFLAGS: -cc tcc -no-retry-compilation
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 10
- name: Install dependencies
run: |
## sudo dpkg --add-architecture i386
sudo apt update
sudo apt-get install --quiet -y libssl-dev sqlite3 libsqlite3-dev
sudo apt-get install --quiet -y mingw-w64 wine-stable winetricks
## sudo apt-get install --quiet -y wine32
- name: Turn off the wine crash dialog
run: winetricks nocrashdialog
- name: Build v
run: make
- name: v.c can be compiled and run with -os cross
run: |
./v -os cross -o /tmp/v.c cmd/v
gcc -g -std=gnu11 -w -o v_from_vc /tmp/v.c -lm -lpthread
ls -lart v_from_vc
./v_from_vc version
- name: v_win.c can be compiled and run with -os windows
run: |
./v -cc msvc -os windows -o /tmp/v_win.c cmd/v
x86_64-w64-mingw32-gcc /tmp/v_win.c -std=c99 -w -municode -o v_from_vc.exe
ls -lart v_from_vc.exe
wine64 ./v_from_vc.exe version
- name: hello_world.v can be cross compiled to hello_world.exe
run: |
./v -os windows examples/hello_world.v
ls -lart examples/hello_world.exe
wine64 examples/hello_world.exe
- name: 2048.v can be cross compiled to 2048.exe
run: |
./v -os windows examples/2048/2048.v
ls -lart examples/2048/2048.exe
windows-cross:
runs-on: windows-2019
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 25
steps:
- uses: actions/checkout@v2
- name: Build
run: |
echo %VFLAGS%
echo $VFLAGS
.\make.bat -msvc
- name: TODO v_win.c can be compiled and run with -os windows
run: |
.\v.exe -os windows -cc msvc -showcc -o v2.exe cmd\v
.\v.exe -os windows -cc msvc -o v_win.c cmd\v
dir v2.exe
dir v_win.c
.\v2.exe version

View File

@ -0,0 +1,256 @@
name: Sanitized CI
## Running these jobs is slow (over ~1 hour, sometimes even 2)
## so we run them only when there is a chance that the generated
## C code could have changed, or for some critical vlib modules,
## like `builtin`, `os`, `sync`, where they have demonstrated
## their usefulness by catching actual very hard to find bugs.
## The cost of this selective running is delayed feedback when
## there are bugs in other V modules.
## The positive is *much faster CI runs* for most V contributors,
## that make PRs that are not concerning V itself, or the critical
## V modules.
on:
push:
paths:
- '!**'
- 'cmd/tools/vtest*'
- 'cmd/tools/builders/**.v'
- 'vlib/builtin/**.v'
- 'vlib/strconv/**.v'
- 'vlib/strings/**.v'
- 'vlib/math/**.v'
- 'vlib/math/big/**.v'
- 'vlib/arrays/**.v'
- 'vlib/datatypes/**.v'
- 'vlib/os/**.v'
- 'vlib/sync/**.v'
- 'vlib/v/tests/**.v'
- 'vlib/v/ast/**.v'
- 'vlib/v/scanner/**.v'
- 'vlib/v/parser/**.v'
- 'vlib/v/checker/**.v'
- 'vlib/v/gen/c/**.v'
- 'vlib/v/builder/**.v'
- 'vlib/v/cflag/**.v'
- 'vlib/v/live/**.v'
- 'vlib/v/util/**.v'
- 'vlib/v/markused/**.v'
- 'vlib/v/preludes/**.v'
- 'vlib/v/embed_file/**.v'
pull_request:
paths:
- '!**'
- 'cmd/tools/vtest*'
- 'cmd/tools/builders/**.v'
- 'vlib/builtin/**.v'
- 'vlib/strconv/**.v'
- 'vlib/strings/**.v'
- 'vlib/math/**.v'
- 'vlib/math/big/**.v'
- 'vlib/arrays/**.v'
- 'vlib/datatypes/**.v'
- 'vlib/os/**.v'
- 'vlib/sync/**.v'
- 'vlib/v/tests/**.v'
- 'vlib/v/ast/**.v'
- 'vlib/v/scanner/**.v'
- 'vlib/v/parser/**.v'
- 'vlib/v/checker/**.v'
- 'vlib/v/gen/c/**.v'
- 'vlib/v/builder/**.v'
- 'vlib/v/cflag/**.v'
- 'vlib/v/live/**.v'
- 'vlib/v/util/**.v'
- 'vlib/v/markused/**.v'
- 'vlib/v/preludes/**.v'
- 'vlib/v/embed_file/**.v'
concurrency:
group: build-sanitized-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
jobs:
tests-sanitize-undefined-clang:
runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 180
env:
VFLAGS: -cc clang
VJOBS: 1
VTEST_SHOW_START: 1
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12.x
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install --quiet -y postgresql libpq-dev libssl-dev sqlite3 libsqlite3-dev valgrind
sudo apt-get install --quiet -y libfreetype6-dev libxi-dev libxcursor-dev libgl-dev
sudo apt-get install clang
- name: Build V
run: make && ./v -cg -cstrict -o v cmd/v
- name: Ensure code is well formatted
run: ./v test-cleancode
- name: Self tests (-fsanitize=undefined)
run: ./v -cflags "-fsanitize=undefined" -o v2 cmd/v && ./v2 -cflags -fsanitize=undefined test-self
- name: Build examples (V compiled with -fsanitize=undefined)
run: ./v2 build-examples
tests-sanitize-undefined-gcc:
runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 180
env:
VFLAGS: -cc gcc
VJOBS: 1
VTEST_SHOW_START: 1
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12.x
- name: Install dependencies
run: |
sudo apt update
sudo apt-get install --quiet -y postgresql libpq-dev libssl-dev sqlite3 libsqlite3-dev valgrind
sudo apt-get install --quiet -y libfreetype6-dev libxi-dev libxcursor-dev libgl-dev
- name: Build V
run: make && ./v -cg -cstrict -o v cmd/v
- name: Ensure code is well formatted
run: ./v test-cleancode
- name: Self tests (-fsanitize=undefined)
run: ./v -cflags "-fsanitize=undefined" -o v2 cmd/v && ./v2 -cflags -fsanitize=undefined test-self
- name: Build examples (V compiled with -fsanitize=undefined)
run: ./v2 build-examples
tests-sanitize-address-clang:
runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 180
env:
VFLAGS: -cc clang
VJOBS: 1
VTEST_SHOW_START: 1
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12.x
- name: Install dependencies
run: |
sudo apt update
sudo apt-get install --quiet -y postgresql libpq-dev libssl-dev sqlite3 libsqlite3-dev valgrind
sudo apt-get install --quiet -y libfreetype6-dev libxi-dev libxcursor-dev libgl-dev
sudo apt-get install clang
- name: Build V
run: make && ./v -cg -cstrict -o v cmd/v
- name: Ensure code is well formatted
run: ./v test-cleancode
- name: Self tests (-fsanitize=address)
run: ASAN_OPTIONS=detect_leaks=0 ./v -cflags "-fsanitize=address,pointer-compare,pointer-subtract" test-self
- name: Self tests (V compiled with -fsanitize=address)
run:
./v -cflags -fsanitize=address -o v cmd/v &&
ASAN_OPTIONS=detect_leaks=0 ./v -cc tcc test-self -asan-compiler
- name: Build examples (V compiled with -fsanitize=address)
run: ASAN_OPTIONS=detect_leaks=0 ./v build-examples
tests-sanitize-address-msvc:
runs-on: windows-2019
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 180
env:
VFLAGS: -cc msvc
VJOBS: 1
VTEST_SHOW_START: 1
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12.x
- name: Build
run: |
echo %VFLAGS%
echo $VFLAGS
.\make.bat -msvc
.\v.exe self
- name: Ensure code is well formatted
run: |
.\v.exe fmt -verify vlib/builtin/ vlib/v/scanner/ vlib/v/parser/ vlib/v/gen/
## TODO: check to see why `v test-cleancode` does not work with msvc on windows
## - name: Install dependencies
## run: |
## .\v.exe setup-freetype
## .\.github\workflows\windows-install-sqlite.bat
## - name: Self tests (TODO: /fsanitize=address)
## run: |
## .\v.exe -cflags "/fsanitize=address" test-self
tests-sanitize-address-gcc:
runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 180
env:
VFLAGS: -cc gcc
VJOBS: 1
VTEST_SHOW_START: 1
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12.x
- name: Install dependencies
run: |
sudo apt update
sudo apt-get install --quiet -y postgresql libpq-dev libssl-dev sqlite3 libsqlite3-dev valgrind
sudo apt-get install --quiet -y libfreetype6-dev libxi-dev libxcursor-dev libgl-dev
sudo apt-get install clang
- name: Build V
run: make && ./v -cg -cstrict -o v cmd/v
- name: Ensure code is well formatted
run: ./v test-cleancode
- name: Self tests (-fsanitize=address)
run: ASAN_OPTIONS=detect_leaks=0 ./v -cflags -fsanitize=address test-self
- name: Self tests (V compiled with -fsanitize=address)
run:
./v -cflags -fsanitize=address,pointer-compare,pointer-subtract -o v cmd/v &&
ASAN_OPTIONS=detect_leaks=0 ./v -cc tcc test-self -asan-compiler
- name: Build examples (V compiled with -fsanitize=address)
run: ASAN_OPTIONS=detect_leaks=0 ./v build-examples
tests-sanitize-memory-clang:
runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 180
env:
VFLAGS: -cc clang -gc none
VJOBS: 1
VTEST_SHOW_START: 1
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12.x
- name: Install dependencies
run: |
sudo apt update
sudo apt-get install --quiet -y postgresql libpq-dev libssl-dev sqlite3 libsqlite3-dev valgrind
sudo apt-get install --quiet -y libfreetype6-dev libxi-dev libxcursor-dev libgl-dev
sudo apt-get install clang
- name: Build V
run: make && ./v -cc clang -cg -cstrict -o v cmd/v
- name: Ensure code is well formatted
run: ./v test-cleancode
- name: Self tests (-fsanitize=memory)
run: ./v -cflags -fsanitize=memory test-self
- name: Self tests (V compiled with -fsanitize=memory)
run: |
./v -cflags -fsanitize=memory -o v cmd/v
./v -cc tcc test-self -msan-compiler
- name: Build examples (V compiled with -fsanitize=memory)
run: ./v build-examples

View File

@ -0,0 +1,36 @@
name: vlang benchmarks
on:
push:
paths-ignore:
- "**.md"
pull_request:
paths-ignore:
- "**.md"
jobs:
run:
name: Run
runs-on: ubuntu-latest
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install google benchmark
run: |
git clone https://github.com/google/benchmark.git
cd benchmark
cmake -E make_directory "build"
cmake -E chdir "build" cmake -DBENCHMARK_DOWNLOAD_DEPENDENCIES=on -DCMAKE_BUILD_TYPE=Release ../
sudo cmake --build "build" --config Release --target install
- name: Run V benchmark
run: |
make
sudo ./v symlink
git clone https://github.com/vincenzopalazzo/benchmarks.git
cd benchmarks
make vdep && make v
- uses: actions/upload-artifact@v3
with:
name: vlang-benchmark
path: benchmarks/vlang/*.json

View File

@ -0,0 +1,82 @@
name: Containers CI
on:
push:
paths-ignore:
- "**.md"
pull_request:
paths-ignore:
- "**.md"
concurrency:
group: build-containers-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
jobs:
alpine-docker-musl-gcc:
runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 181
container:
# Alpine docker pre-built container
image: thevlang/vlang:alpine-build
env:
V_CI_MUSL: 1
VFLAGS: -cc gcc
volumes:
- ${{github.workspace}}:/opt/vlang
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Show Environment
run: |
echo "PWD:"
pwd
echo "ENVIRONMENT:"
env
echo "C Compiler:"
gcc --version
- name: Build V
run: CC=gcc make
- name: All code is formatted
run: ./v test-cleancode
- name: Run only essential tests
run: VTEST_JUST_ESSENTIAL=1 ./v test-self
ubuntu-docker-musl:
runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 121
container:
image: thevlang/vlang:ubuntu-build
env:
V_CI_MUSL: 1
V_CI_UBUNTU_MUSL: 1
VFLAGS: -cc musl-gcc -gc none
volumes:
- ${{github.workspace}}:/opt/vlang
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Build V
run: |
echo $VFLAGS && make -j4 && ./v -cg -o v cmd/v
- name: Verify `v test` works
run: |
echo $VFLAGS
./v cmd/tools/test_if_v_test_system_works.v
./cmd/tools/test_if_v_test_system_works
- name: All code is formatted
run: ./v test-cleancode
- name: Test V fixed tests
run: ./v test-self

46
.github/workflows/debug_ci.yml vendored 100644
View File

@ -0,0 +1,46 @@
name: Debug CI
on: [workflow_dispatch]
jobs:
debug-msvc:
runs-on: windows-2019
timeout-minutes: 121
env:
VFLAGS: -cc msvc
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12.x
- name: Build
run: |
echo %VFLAGS%
echo $VFLAGS
.\make.bat -msvc
.\v.exe -cflags /WX self
- name: Install dependencies
run: |
.\v.exe setup-freetype
.\.github\workflows\windows-install-sqlite.bat
- name: v doctor
run: ./v doctor
- name: Verify `v test` works
run: |
./v cmd/tools/test_if_v_test_system_works.v
./cmd/tools/test_if_v_test_system_works
- name: All code is formatted
run: ./v test-cleancode
- name: Self tests
run: |
./v -cg cmd\tools\vtest-self.v
./v test-self
- name: Test v->js
run: ./v -o hi.js examples/hello_v_js.v && node hi.js
- name: Test v binaries
run: ./v build-vbinaries
- name: Build examples
run: ./v build-examples
- name: v2 self compilation
run: .\v.exe -o v2.exe cmd/v && .\v2.exe -o v3.exe cmd/v

38
.github/workflows/docs_ci.yml vendored 100644
View File

@ -0,0 +1,38 @@
name: Docs CI
### Run on *EVERY* commit. The documentation *SHOULD* stay valid, and
### the developers should receive early warning if they break it.
on: [push, pull_request]
jobs:
check-markdown:
runs-on: ubuntu-20.04
timeout-minutes: 5
steps:
- uses: actions/checkout@v2
- name: Build V
run: make
- name: Check markdown line length & code examples
run: ./v check-md -hide-warnings .
## NB: -hide-warnings is used here, so that the output is less noisy,
## thus real errors are easier to spot.
report-missing-fn-doc:
runs-on: ubuntu-20.04
timeout-minutes: 5
env:
MOPTIONS: --relative-paths --exclude /vlib/v/ --exclude /builtin/linux_bare/ --exclude /testdata/ --exclude /tests/
steps:
- uses: actions/checkout@v2
- name: Build V
run: make
- name: Checkout previous v
uses: actions/checkout@v2
with:
repository: vlang/v
path: pv
- name: Check against parent commit
run: |
./v missdoc --diff $MOPTIONS pv/vlib vlib

View File

@ -0,0 +1,33 @@
#!/usr/local/bin/bash
## should be run in V's main repo folder!
rm -rf tinycc/
rm -rf thirdparty/tcc/
pushd .
git clone git://repo.or.cz/tinycc.git
cd tinycc
export CC=clang
./configure \
--cc=clang \
--prefix=thirdparty/tcc \
--bindir=thirdparty/tcc \
--crtprefix=thirdparty/tcc/lib:/usr/lib \
--libpaths=thirdparty/tcc/lib:/usr/lib:/lib:/usr/local/lib \
--debug
gmake
gmake install
popd
mv tinycc/thirdparty/tcc thirdparty/tcc
mv thirdparty/tcc/tcc thirdparty/tcc/tcc.exe
sudo ln -s $(pwd)/thirdparty/tcc/tcc.exe /usr/local/bin/tcc
thirdparty/tcc/tcc.exe -v -v

49
.github/workflows/gen_vc.yml vendored 100644
View File

@ -0,0 +1,49 @@
name: VC gen
on:
push:
branches:
- master
paths-ignore:
- "**.md"
jobs:
build-vc:
runs-on: ubuntu-latest
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
env:
VREPO: github.com/vlang/vc.git
steps:
- uses: actions/checkout@v2
- name: Build V
run: make
- name: Regenerate v.c and v_win.c
run: |
git config --global user.email "vlang-bot@users.noreply.github.com"
git config --global user.name "vlang-bot"
COMMIT_HASH=$(git rev-parse --short HEAD)
COMMIT_MSG=$(git log -1 --oneline --pretty='%s' HEAD)
rm -rf vc
git clone --depth=1 \
https://vlang-bot:${{ secrets.VLANG_BOT_SECRET }}@$VREPO
rm -rf vc/v.c vc/v_win.c
./v -o vc/v.c -os cross cmd/v
./v -o vc/v_win.c -os windows -cc msvc cmd/v
sed -i "1s/^/#define V_COMMIT_HASH \"$COMMIT_HASH\"\n/" vc/v.c
sed -i "1s/^/#define V_COMMIT_HASH \"$COMMIT_HASH\"\n/" vc/v_win.c
# ensure the C files are over 5000 lines long, as a safety measure
[ $(wc -l < vc/v.c) -gt 5000 ]
[ $(wc -l < vc/v_win.c) -gt 5000 ]
git -C vc add v.c v_win.c
git -C vc commit -m "[v:master] $COMMIT_HASH - $COMMIT_MSG"
# in case there are recent commits:
git -C vc pull --rebase origin master
git -C vc push

View File

@ -0,0 +1,54 @@
name: Graphics CI
on:
push:
paths-ignore:
- "**.md"
pull_request:
paths-ignore:
- "**.md"
jobs:
gg-regressions:
runs-on: ubuntu-18.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 10
env:
VFLAGS: -cc tcc
DISPLAY: :99
steps:
- name: Checkout V
uses: actions/checkout@v2
- name: Build local v
run: make
- uses: openrndr/setup-opengl@v1.1
- name: Setup dependencies
run: |
# imagemagick : convert, mogrify
# xvfb : xvfb (installed by openrndr/setup-opengl@v1.1)
# openimageio-tools : idiff
# libxcursor-dev libxi-dev : V gfx deps
# mesa-common-dev : For headless rendering
# freeglut3-dev : Fixes graphic apps compilation with tcc
sudo apt-get update
sudo apt-get install imagemagick openimageio-tools mesa-common-dev libxcursor-dev libxi-dev freeglut3-dev
wget https://raw.githubusercontent.com/tremby/imgur.sh/c98345d/imgur.sh
git clone https://github.com/Larpon/gg-regression-images gg-regression-images
chmod +x ./imgur.sh
- name: Sample and compare
id: compare
continue-on-error: true
run: |
Xvfb $DISPLAY -screen 0 1280x1024x24 &
./v gret -t ./gg-regression-images/vgret.v_examples.toml -v ./gg-sample_images ./gg-regression-images
- name: Upload regression to imgur
if: steps.compare.outcome != 'success'
run: |
./imgur.sh /tmp/fail.png
./imgur.sh /tmp/diff.png
exit 1

View File

@ -0,0 +1,5 @@
fn test_ci_run_with_32bit_compiler() {
$if x64 {
assert false
}
}

View File

@ -0,0 +1,5 @@
fn test_ci_run_with_64bit_compiler() {
$if x32 {
assert false
}
}

View File

@ -0,0 +1,20 @@
name: vlib modules CI
on:
pull_request:
push:
branches:
- master
jobs:
build-module-docs:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Build V
run: make
- name: Build module documentation
run: ./v doc -m -f html vlib/
- name: Deploy docs to vercel
if: ${{ github.event_name == 'push' }}
run: npx vercel --confirm --prod --name vmodules --token ${{ secrets.VERCEL_TOKEN }} vlib/_docs/ || true

View File

@ -0,0 +1,36 @@
name: native backend CI
on:
push:
paths-ignore:
- "**.md"
pull_request:
paths-ignore:
- "**.md"
concurrency:
group: native-backend-ci-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
jobs:
native-backend:
strategy:
matrix:
os: [ubuntu-18.04, ubuntu-20.04, macos-10.15, macos-11, macos-12, windows-2016, windows-2019, windows-2022]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- name: Build V with make.bat
if: ${{ startsWith(matrix.os, 'windows') }}
run: |
.\make.bat
.\v.exe symlink -githubci
- name: Build V with make
if: ${{ !startsWith(matrix.os, 'windows') }}
run: |
make
./v symlink -githubci
- name: Test the native backend
run: v test vlib/v/gen/native/

153
.github/workflows/other_ci.yml vendored 100644
View File

@ -0,0 +1,153 @@
name: Other CI
on:
push:
paths-ignore:
- "**.md"
pull_request:
paths-ignore:
- "**.md"
concurrency:
group: build-other-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
jobs:
no-gpl-by-accident:
runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 15
steps:
- uses: actions/checkout@v2
- name: No GPL licensed code, should be added accidentally
run: |
! grep -r --exclude="*.yml" "a GPL license" .
code-formatting:
runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 15
env:
VFLAGS: -cc gcc
steps:
- uses: actions/checkout@v2
- name: Environment info
run: echo $VFLAGS $GITHUB_SHA $GITHUB_REF
- name: Build local v
run: make -j4
- name: v test-cleancode
run: ./v test-cleancode
- name: v test-fmt
run: ./v test-fmt
performance-regressions:
runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 15
env:
VFLAGS: -cc gcc
steps:
- uses: actions/checkout@v2
- name: Environment info
run: echo $VFLAGS $GITHUB_SHA $GITHUB_REF
- name: Build local v
run: make -j4
- name: Clone & Build previous vmaster/v
run: |
git clone --depth=1 https://github.com/vlang/v vmaster/
(cd vmaster; make -j4)
- name: V versions
run: ./v version && ./vmaster/v version
- name: Build the repeat tool
run: ./v cmd/tools/repeat.v
- name: Repeat -o hw.c examples/hello_world.v
run: cmd/tools/repeat --max_time 251 --series 3 --count 20 --nmins 2 --nmaxs 5 --warmup 3 --fail_percent 10 -t 'cd {T} ; ./v -show-timings -o hw.c examples/hello_world.v' . ./vmaster
- name: Repeat -o v.c cmd/v
run: cmd/tools/repeat --max_time 1501 --series 3 --count 20 --nmins 2 --nmaxs 5 --warmup 3 --fail_percent 10 -t 'cd {T} ; ./v -show-timings -o v.c cmd/v' . ./vmaster
misc-tooling:
runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 121
env:
VFLAGS: -cc tcc -no-retry-compilation
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 10
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install --quiet -y libsodium-dev libssl-dev sqlite3 libsqlite3-dev postgresql libpq-dev valgrind
sudo apt-get install --quiet -y libfreetype6-dev libxi-dev libxcursor-dev libgl-dev xfonts-75dpi xfonts-base
sudo apt-get install --quiet -y g++-9
- name: Build v
run: make
- name: g++ version
run: g++-9 --version
- name: V self compilation with g++
run: ./v -cc g++-9 -no-std -cflags -std=c++11 -o v2 cmd/v && ./v2 -cc g++-9 -no-std -cflags -std=c++11 -o v3 cmd/v
## - name: Running tests with g++
## run: ./v -cc g++-9 test-self
- name: Ensure V can be compiled with -autofree
run: ./v -autofree -o v2 cmd/v ## NB: this does not mean it runs, but at least keeps it from regressing
- name: Shader examples can be build
run: |
wget https://github.com/floooh/sokol-tools-bin/raw/33d2e4cc26088c6c28eaef5467990f8940d15aab/bin/linux/sokol-shdc
chmod +x ./sokol-shdc
for f in examples/sokol/02_cubes_glsl/cube_glsl \
examples/sokol/03_march_tracing_glsl/rt_glsl \
examples/sokol/04_multi_shader_glsl/rt_glsl_puppy \
examples/sokol/04_multi_shader_glsl/rt_glsl_march \
examples/sokol/05_instancing_glsl/rt_glsl_instancing \
examples/sokol/06_obj_viewer/gouraud \
; do \
echo "compiling shader $f.glsl ..."; \
./sokol-shdc --input $f.glsl --output $f.h --slang glsl330 ; \
done
./v should-compile-all examples/sokol/*.v examples/sokol/0?*/*.v
parser-silent:
runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 121
steps:
- uses: actions/checkout@v2
- name: Install zzuf
run: sudo apt install -qq zzuf
- name: Build local v
run: |
make -j4
./v -g cmd/tools/vtest-parser.v
- name: Run test-parser
run: |
./v test-parser -S examples/hello_world.v
./v test-parser -S examples/hanoi.v
./v test-parser -S examples/fibonacci.v
./v test-parser -S examples/cli.v
./v test-parser -S examples/json.v
./v test-parser -S examples/vmod.v
./v test-parser -S examples/regex/regex_example.v
./v test-parser -S examples/2048/2048.v
- name: Run test-parser over fuzzed files
run: |
zzuf -R '\x00-\x20\x7f-\xff' -r0.01 < examples/hello_world.v > examples/hello_world_fuzz.v
zzuf -R '\x00-\x20\x7f-\xff' -r0.01 < examples/hanoi.v > examples/hanoi_fuzz.v
zzuf -R '\x00-\x20\x7f-\xff' -r0.01 < examples/fibonacci.v > examples/fibonacci_fuzz.v
zzuf -R '\x00-\x20\x7f-\xff' -r0.01 < examples/cli.v > examples/cli_fuzz.v
zzuf -R '\x00-\x20\x7f-\xff' -r0.01 < examples/json.v > examples/json_fuzz.v
zzuf -R '\x00-\x20\x7f-\xff' -r0.01 < examples/vmod.v > examples/vmod_fuzz.v
zzuf -R '\x00-\x20\x7f-\xff' -r0.01 < examples/regex/regex_example.v > examples/regex_example_fuzz.v
zzuf -R '\x00-\x20\x7f-\xff' -r0.01 < examples/2048/2048.v > examples/2048/2048_fuzz.v
./v test-parser -S examples/hello_world_fuzz.v
./v test-parser -S examples/hanoi_fuzz.v
./v test-parser -S examples/cli_fuzz.v
./v test-parser -S examples/regex_example_fuzz.v
./v test-parser -S examples/2048/2048_fuzz.v

107
.github/workflows/paths_ci.yml vendored 100644
View File

@ -0,0 +1,107 @@
name: Path Testing CI
on:
push:
paths-ignore:
- "**.md"
pull_request:
paths-ignore:
- "**.md"
concurrency:
group: build-paths-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
jobs:
space-paths-linux:
runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 30
env:
MY_V_PATH: '你好 my $path, @с интервали'
steps:
- uses: actions/checkout@v2
with:
path: ${{env.MY_V_PATH}}
- name: Build V
run: |
echo '${{env.MY_V_PATH}}'
ls -la
cd '${{env.MY_V_PATH}}'
ls -la
make
- name: v doctor
run: |
cd '${{env.MY_V_PATH}}'
./v doctor
- name: v tests
run: |
cd '${{env.MY_V_PATH}}'
./v test vlib/builtin vlib/os
space-paths-macos:
runs-on: macOS-latest
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 30
env:
MY_V_PATH: '你好 my $path, @с интервали'
steps:
- uses: actions/checkout@v2
with:
path: ${{env.MY_V_PATH}}
- name: Build V
run: |
echo '${{env.MY_V_PATH}}'
ls -la
cd '${{env.MY_V_PATH}}'
ls -la
make
## prebuild cmd/tools/builders/js_builder, to minimise the
## chances of a sporadic "Killed" when running the tests later
./v -b js run examples/hello_world.v
- name: v doctor
run: |
cd '${{env.MY_V_PATH}}'
./v doctor
- name: v tests
run: |
cd '${{env.MY_V_PATH}}'
./v test vlib/builtin vlib/os
space-paths-windows:
runs-on: windows-2022
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 30
env:
MY_V_PATH: 'path with some $punctuation, and some spaces'
## NB: the following paths do not work for now:
##### MY_V_PATH: '你好 my $path, @с интервали'
##### MY_V_PATH: 'path with some $punctuation, and some spaces '
## tcc has a problem interpreting paths with non latin letters in them,
## by default, but that can be solved with passing -Bthirdparty/tcc
## but after that V fails self building with:
####### builder error: cannot write to folder
####### D:\a\v\v\你好 my $path, @с интервали: No such file or directory
## and that happens even for gcc builds, not just tcc ones
steps:
- uses: actions/checkout@v2
with:
path: ${{env.MY_V_PATH}}
- name: Build V
run: |
echo '${{env.MY_V_PATH}}'
dir
cd '${{env.MY_V_PATH}}'
dir
.\make.bat -tcc
- name: v doctor
run: |
cd '${{env.MY_V_PATH}}'
./v doctor
- name: v tests
run: |
cd '${{env.MY_V_PATH}}'
./v test vlib/builtin vlib/os

57
.github/workflows/periodic.yml vendored 100644
View File

@ -0,0 +1,57 @@
name: Periodic
on:
schedule:
- cron: '0 */6 * * *'
jobs:
network-tests-ubuntu:
runs-on: ubuntu-20.04
timeout-minutes: 30
env:
V_CI_PERIODIC: 1
steps:
- uses: actions/checkout@v2
- name: Install dependencies 1
run: sudo apt-get install --quiet -y libssl-dev sqlite3 libsqlite3-dev
- name: Build v
run: make
- name: Symlink V
run: sudo ./v symlink
## - name: Run network tests
## run: ./v -d network test vlib/net
network-tests-macos:
runs-on: macOS-latest
timeout-minutes: 30
env:
V_CI_PERIODIC: 1
steps:
- uses: actions/checkout@v2
- name: Setup openssl library path
run: export LIBRARY_PATH="$LIBRARY_PATH:/usr/local/opt/openssl/lib/"
- name: Build V
run: make
- name: Symlink V
run: sudo ./v symlink
- name: Ensure thirdparty/cJSON/cJSON.o is compiled, before running tests.
run: ./v examples/json.v
## - name: Run network tests
## run: ./v -d network test vlib/net
network-windows-msvc:
runs-on: windows-2019
timeout-minutes: 30
env:
V_CI_PERIODIC: 1
VFLAGS: -cc msvc
steps:
- uses: actions/checkout@v2
- name: Build
run: |
echo %VFLAGS%
echo $VFLAGS
.\make.bat -msvc
## - name: Run network tests
## run: .\v.exe -d network test vlib/net

62
.github/workflows/prebuilt.yml vendored 100644
View File

@ -0,0 +1,62 @@
name: Test prebuilt binaries
on:
release:
types: [edited, published]
jobs:
ubuntu:
runs-on: ubuntu-20.04
timeout-minutes: 5
steps:
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install --quiet -y postgresql libpq-dev libglfw3 libglfw3-dev libfreetype6-dev libssl-dev sqlite3 libsqlite3-dev valgrind
## sudo apt-get install --quiet -y libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libsdl2-image-dev
- name: Download V
run: |
tag=${GITHUB_REF##*/}
wget https://github.com/vlang/v/releases/download/$tag/v_linux.zip
unzip v_linux.zip
cd v
./v -version
- name: Test V
run: |
cd v
./v run examples/hello_world.v
macos:
runs-on: macOS-latest
timeout-minutes: 5
steps:
- name: Install dependencies
run: |
brew install freetype glfw openssl
# brew install sdl2 sdl2_ttf sdl2_mixer sdl2_image
export LIBRARY_PATH="$LIBRARY_PATH:/usr/local/opt/openssl/lib/"
- name: Download V
run: |
tag=${GITHUB_REF##*/}
wget https://github.com/vlang/v/releases/download/$tag/v_macos.zip
unzip v_macos.zip
cd v
./v -version
- name: Test V
run: |
cd v
./v run examples/hello_world.v
windows:
runs-on: windows-latest
timeout-minutes: 5
steps:
- name: Download V
run: |
Set-Variable -Name "tag" -Value $env:GITHUB_REF.split("/", 3)[-1]
& curl -L https://github.com/vlang/v/releases/download/$tag/v_windows.zip -o v_windows.zip
& unzip .\v_windows.zip
& cd v
& .\v.exe -version
- name: Test V
run: |
& cd v
& .\v.exe run .\examples\hello_world.v

48
.github/workflows/sdl_ci.yml vendored 100644
View File

@ -0,0 +1,48 @@
name: sdl CI
on:
push:
paths-ignore:
- "**.md"
pull_request:
paths-ignore:
- "**.md"
jobs:
v-compiles-sdl-examples:
runs-on: ubuntu-18.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 30
env:
VFLAGS: -cc tcc
steps:
- uses: actions/checkout@v2
- name: Build V
run: make && sudo ./v symlink
- name: Clone sdl into .vmodules
run: |
git clone --depth 1 https://github.com/vlang/sdl
cd sdl
mkdir -p ~/.vmodules
ln -s $(pwd) ~/.vmodules/sdl
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install --quiet -y libsdl2-dev libsdl2-ttf-dev
sudo apt-get install --quiet -y libsdl2-mixer-dev libsdl2-image-dev
- name: Run tests
run: ./v test sdl
- name: Build sdl shared
run: ./v -shared -g sdl
- name: Build sdl examples
run: |
v shader sdl/examples/sdl_opengl_and_sokol
for example in sdl/examples/*; do
echo "v $example"
v "$example";
done

73
.github/workflows/toml_ci.yml vendored 100644
View File

@ -0,0 +1,73 @@
name: toml CI
on:
push:
paths-ignore:
- "**.md"
pull_request:
paths-ignore:
- "**.md"
jobs:
toml-module-pass-external-test-suites:
runs-on: ubuntu-18.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 30
env:
TOML_BS_TESTS_PATH: vlib/toml/tests/testdata/burntsushi/toml-test
TOML_BS_TESTS_PINNED_COMMIT: 4634fdf
TOML_IARNA_TESTS_PATH: vlib/toml/tests/testdata/iarna/toml-test
TOML_IARNA_TESTS_PINNED_COMMIT: 1880b1a
TOML_AC_TESTS_PATH: vlib/toml/tests/testdata/alexcrichton/toml-test
TOML_AC_TESTS_PINNED_COMMIT: 499e8c4
VTEST_TOML_DO_LARGE_FILES: 1
VTEST_TOML_DO_YAML_CONVERSION: 1
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install --quiet -y jq libgc-dev
- name: Build V
run: make -j2 && ./v -cc gcc -o v cmd/v
- name: Show JQ Version
run: jq --version
- name: Run local TOML tests
run: ./v test vlib/toml
# Tests found at https://github.com/BurntSushi/toml-test
- name: Clone BurntSushi/toml-test
run: |
git clone -n https://github.com/BurntSushi/toml-test.git $TOML_BS_TESTS_PATH
git -C $TOML_BS_TESTS_PATH checkout $TOML_BS_TESTS_PINNED_COMMIT
- name: Run BurntSushi TOML tests
run: ./v vlib/toml/tests/burntsushi.toml-test_test.v
# Tests found at gist
- name: Get large_toml_file_test.toml
run: wget https://gist.githubusercontent.com/Larpon/89b0e3d94c6903851ff15559e5df7a05/raw/62a1f87a4e37bf157f2e0bfb32d85d840c98e422/large_toml_file_test.toml -O vlib/toml/tests/testdata/large_toml_file_test.toml
- name: Run large TOML file tests
run: ./v vlib/toml/tests/large_toml_file_test.v
# Tests found at https://github.com/iarna/toml-spec-tests
- name: Clone iarna/toml-spec-tests
run: |
git clone -n https://github.com/iarna/toml-spec-tests.git $TOML_IARNA_TESTS_PATH
git -C $TOML_IARNA_TESTS_PATH checkout $TOML_IARNA_TESTS_PINNED_COMMIT
- name: Run iarna TOML tests
run: ./v -gc boehm vlib/toml/tests/iarna.toml-spec-tests_test.v
# Tests found at https://github.com/alexcrichton/toml-rs
- name: Clone alexcrichton/toml-rs
run: |
git clone -n https://github.com/alexcrichton/toml-rs.git $TOML_AC_TESTS_PATH
git -C $TOML_AC_TESTS_PATH checkout $TOML_AC_TESTS_PINNED_COMMIT
- name: Run alexcrichton TOML tests
run: ./v vlib/toml/tests/alexcrichton.toml-rs-tests_test.v

View File

@ -0,0 +1,137 @@
name: V Apps and Modules
on:
push:
paths-ignore:
- "**.md"
pull_request:
paths-ignore:
- "**.md"
concurrency:
group: build-v-apps-and-modules-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
jobs:
v-apps-compile:
runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 121
steps:
- uses: actions/checkout@v2
- name: Build V
run: make && sudo ./v symlink
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install --quiet -y libgc-dev libsodium-dev libssl-dev sqlite3 libsqlite3-dev valgrind libfreetype6-dev libxi-dev libxcursor-dev libgl-dev xfonts-75dpi xfonts-base
sudo apt-get install --quiet -y --no-install-recommends gfortran liblapacke-dev libopenblas-dev
- name: Build V Language Server (VLS)
run: |
echo "Clone VLS"
git clone --depth 1 https://github.com/vlang/vls /tmp/vls
echo "Build VLS"
v /tmp/vls/cmd/vls
echo "Build VLS with -prod"
v -prod /tmp/vls/cmd/vls
echo "Build VLS with -gc boehm -skip-unused"
v -gc boehm -skip-unused /tmp/vls/cmd/vls
- name: Build V Coreutils
run: |
echo "Clone Coreutils"
git clone --depth 1 https://github.com/vlang/coreutils /tmp/coreutils
echo "Build Coreutils"
cd /tmp/coreutils; make
- name: Build VAB
run: |
echo "Install VAB"
v install vab
echo "Build vab"
v ~/.vmodules/vab
echo "Build vab with -gc boehm -skip-unused"
v -gc boehm -skip-unused ~/.vmodules/vab
- name: Build Gitly
run: |
echo "Install markdown"
v install markdown
echo "Clone Gitly"
git clone https://github.com/vlang/gitly /tmp/gitly
echo "Build Gitly"
v /tmp/gitly
echo "Build Gitly with -autofree"
v -autofree /tmp/gitly
echo "Run first_run.v"
v run /tmp/gitly/tests/first_run.v
# /tmp/gitly/gitly -ci_run
- name: Build libsodium
run: |
echo "Install the libsodium wrapper"
v install libsodium
echo "Test libsodium"
VJOBS=1 v test ~/.vmodules/libsodium
- name: Build VEX
run: |
echo "Install Vex"
v install nedpals.vex
echo "Compile all of the Vex examples"
v should-compile-all ~/.vmodules/nedpals/vex/examples
echo "Compile the simple Vex example with -gc boehm -skip-unused"
v -gc boehm -skip-unused ~/.vmodules/nedpals/vex/examples/simple_example.v
echo "Run Vex Tests"
v test ~/.vmodules/nedpals/vex
- name: Build go2v
run: |
echo "Clone Go2V"
git clone --depth=1 https://github.com/vlang/go2v /tmp/go2v/
echo "Build Go2V"
v /tmp/go2v/
echo "Run Go2V tests"
VJOBS=1 v -stats test /tmp/go2v/
- name: Build vlang/pdf
run: |
v install pdf
echo "PDF examples should compile"
v should-compile-all ~/.vmodules/pdf/examples
- name: Install UI through VPM
run: |
echo "Official VPM modules should be installable"
v install ui
echo "Examples of UI should compile"
v ~/.vmodules/ui/examples/build_examples.vsh
- name: Build VSL
run: |
echo "Install VSL"
v install vsl
echo "Execute Tests using Pure V Backend"
~/.vmodules/vsl/bin/test
echo "Execute Tests using Pure V Backend with Pure V Math"
~/.vmodules/vsl/bin/test --use-cblas
echo "Execute Tests using Pure V Backend and Garbage Collection enabled"
~/.vmodules/vsl/bin/test --use-gc boehm
echo "Execute Tests using Pure V Backend with Pure V Math and Garbage Collection enabled"
~/.vmodules/vsl/bin/test --use-cblas --use-gc boehm
- name: Build VTL
run: |
echo "Install VTL"
v install vtl
echo "Install dependencies"
echo "Execute Tests using Pure V Backend"
~/.vmodules/vtl/bin/test
echo "Execute Tests using Pure V Backend with Pure V Math"
~/.vmodules/vtl/bin/test --use-cblas
echo "Execute Tests using Pure V Backend and Garbage Collection enabled"
~/.vmodules/vtl/bin/test --use-gc boehm
echo "Execute Tests using Pure V Backend with Pure V Math and Garbage Collection enabled"
~/.vmodules/vtl/bin/test --use-cblas --use-gc boehm

74
.github/workflows/vab_ci.yml vendored 100644
View File

@ -0,0 +1,74 @@
name: vab CI
on:
push:
paths-ignore:
- "**.md"
pull_request:
paths-ignore:
- "**.md"
jobs:
vab-compiles-v-examples:
runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 121
env:
VAB_FLAGS: --api 30 --build-tools 29.0.0 -v 3
steps:
- uses: actions/setup-java@v2
with:
distribution: 'adopt'
java-version: 8
- uses: actions/checkout@v2
- name: Build V
run: make && sudo ./v symlink
- name: Install vab
run: |
v install vab
v -g ~/.vmodules/vab
sudo ln -s ~/.vmodules/vab/vab /usr/local/bin/vab
- name: Run tests
run: v test ~/.vmodules/vab
- name: Run vab --help
run: vab --help
- name: Run vab doctor
run: vab doctor
- name: Build graphical V examples as APK
run: |
declare -a v_examples=('flappylearning' '2048' 'fireworks' 'tetris' 'sokol/particles' 'sokol/drawing.v' 'sokol/freetype_raven.v' 'gg/polygons.v' 'gg/raven_text_rendering.v' 'gg/rectangles.v' 'gg/stars.v' 'gg/worker_thread.v')
mkdir apks
for example in "${v_examples[@]}"; do
safe_name=$(echo "$example" | sed 's%/%-%' | sed 's%\.%-%' )
vab examples/$example -o apks/$safe_name.apk
done
v-compiles-os-android:
runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- name: Build V
run: make && sudo ./v symlink
- name: Install vab
run: |
v install vab
v -g ~/.vmodules/vab
sudo ln -s ~/.vmodules/vab/vab /usr/local/bin/vab
- name: Run vab --help
run: vab --help
- name: Run vab doctor
run: vab doctor
- name: Check `v -os android` *without* -apk flag
run: .github/workflows/android_cross_compile.vsh

View File

@ -0,0 +1,37 @@
name: Build Vinix kernel
on:
pull_request:
paths-ignore:
- "**.md"
push:
branches:
- master
paths-ignore:
- "**.md"
jobs:
vinix-build:
runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
env:
VFLAGS: -gc none
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install build-essential meson -y
- name: Build V
run: make
- name: Clone current Vinix
run: git clone https://github.com/vlang/vinix.git
- name: Attempt to build the Vinix kernel (debug)
run: cd vinix/kernel && make PROD=false CFLAGS="-D__vinix__ -O2 -g -pipe" V="../../v" && make clean
- name: Attempt to build the Vinix kernel (prod)
run: cd vinix/kernel && make PROD=true CFLAGS="-D__vinix__ -O2 -g -pipe" V="../../v" && make clean

View File

@ -0,0 +1,91 @@
name: Websockets CI
on:
push:
paths-ignore:
- "**.md"
pull_request:
paths-ignore:
- "**.md"
jobs:
websocket_tests:
runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 121
env:
VFLAGS: -cc tcc -no-retry-compilation
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: sudo apt-get install --quiet -y libssl-dev
- name: Build v
run: |
echo $VFLAGS
sudo ln -s $PWD/thirdparty/tcc/tcc.exe /usr/local/bin/tcc ## TODO: remove
make -j4
./v -g -o v cmd/v
- name: v doctor
run: ./v doctor
- name: Run websockets tests
run: ./v -g test vlib/net/websocket/
## Autobahn integrations tests
- name: Run autobahn services
run: docker-compose -f ${{github.workspace}}/vlib/net/websocket/tests/autobahn/docker-compose.yml up -d
- name: Wait for the service to start
run: sleep 10s
- name: Build client test
run: docker exec autobahn_client "/src/v" "/src/vlib/net/websocket/tests/autobahn/autobahn_client.v"
- name: Run client test
run: docker exec autobahn_client "/src/vlib/net/websocket/tests/autobahn/autobahn_client"
- name: Build client wss test
run: docker exec autobahn_client "/src/v" "/src/vlib/net/websocket/tests/autobahn/autobahn_client_wss.v"
- name: Run client wss test
run: docker exec autobahn_client "/src/vlib/net/websocket/tests/autobahn/autobahn_client_wss"
- name: Run server test
run: docker exec autobahn_server "wstest" "-m" "fuzzingclient" "-s" "/config/fuzzingclient.json"
- name: Copy reports
run: docker cp autobahn_server:/reports ${{github.workspace}}/reports
- name: Copy reports wss
run: docker cp autobahn_server_wss:/reports ${{github.workspace}}/reports_wss
- name: Test success
run: docker exec autobahn_server "python" "/check_results.py"
- name: Test success WSS
run: docker exec autobahn_server_wss "python" "/check_results.py"
- name: Publish all reports
uses: actions/upload-artifact@v2
with:
name: full report
path: ${{github.workspace}}/reports
- name: Publish report client
uses: actions/upload-artifact@v2
with:
name: client
path: ${{github.workspace}}/reports/clients/index.html
- name: Publish report server
uses: actions/upload-artifact@v2
with:
name: server
path: ${{github.workspace}}/reports/servers/index.html
- name: Publish all reports WSS
uses: actions/upload-artifact@v2
with:
name: full report wss
path: ${{github.workspace}}/reports_wss
- name: Publish report client wss
uses: actions/upload-artifact@v2
with:
name: client wss
path: ${{github.workspace}}/reports_wss/clients/index.html

View File

@ -0,0 +1,16 @@
@echo off
curl -L https://www.libsdl.org/release/SDL2-devel-2.0.10-VC.zip -o SDL2.zip
curl -L https://www.libsdl.org/projects/SDL_ttf/release/SDL2_ttf-devel-2.0.15-VC.zip -o SDL2_ttf.zip
curl -L https://www.libsdl.org/projects/SDL_image/release/SDL2_image-devel-2.0.5-VC.zip -o SDL2_image.zip
curl -L https://www.libsdl.org/projects/SDL_mixer/release/SDL2_mixer-devel-2.0.4-VC.zip -o SDL2_mixer.zip
unzip SDL2.zip -d thirdparty/
unzip SDL2_ttf.zip -d thirdparty/
unzip SDL2_image.zip -d thirdparty/
unzip SDL2_mixer.zip -d thirdparty/
move /y thirdparty/SDL2-2.0.10 thirdparty/SDL2
move /y thirdparty/SDL2_ttf-2.0.15 thirdparty/SDL2_ttf
move /y thirdparty/SDL2_image-2.0.5 thirdparty/SDL2_image
move /y thirdparty/SDL2_mixer-2.0.4 thirdparty/SDL2_mixer

View File

@ -0,0 +1,11 @@
@echo off
curl -L https://www.sqlite.org/2022/sqlite-amalgamation-3380200.zip -o sqlite-amalgamation-3380200.zip
unzip sqlite-amalgamation-3380200.zip -d thirdparty\
del thirdparty\sqlite-amalgamation-3380200\shell.c
move /y thirdparty\sqlite-amalgamation-3380200 thirdparty\sqlite
dir thirdparty\sqlite

107
.gitignore vendored 100644
View File

@ -0,0 +1,107 @@
# ignore sub-level build binaries and v binary
*/**/*
v
v.exe
v2
v2.exe
vdbg
vdbg.exe
!*/
!*.*
*.exe
*.o
*.so
.*.c
*.tmp.c
*.obj
*.exp
*.ilk
*.pdb
*.dll
*.lib
*.bak
*.dylib
a.out
.noprefix.vrepl_temp
# ignore v build files
/vc
/v.c
/v.*.c
/v.c.out
/v_old
/v_old.exe
.vrepl_temp.v
fns.txt
.noprefix.vrepl_temp.v
# ignore temp directories
/temp
/tmp
# unignore special files without extension
!.github/PULL_REQUEST_TEMPLATE
!.editorconfig
!.gitattributes
!.gitignore
!BSDmakefile
!Dockerfile
!Dockerfile.alpine
!Dockerfile.cross
!LICENSE
!Makefile
!GNUmakefile
# ignore editor files
.idea
.project
.classpath
.c9
*.launch
.settings/
*.sublime-workspace
.vscode/
*.code-workspace
*~
*.swp
*.swo
*.swn
.env
# ignore debugger files
cachegrind.out.*
.gdb_history
*.dSYM
*.def
# ignore system files
.DS_Store
._*
thumbs.db
/.symlink
/.bin
_docs
# ignore vs databases
*.suo
*.VC.db
*.rsp
# ignore cmd/tools/.disable_autorecompilation, which some package managers use.
cmd/tools/.disable_autorecompilation
test.bin
# ignore codespace env
.venv/
.direnv/
shell.nix
default.nix
flake.nix
.envrc
thirdparty/stdatomic/nix/cpp/*.h
# ignore VLS log
vls.log

61
.woodpecker.yml 100644
View File

@ -0,0 +1,61 @@
platform: 'linux/amd64'
branches: ['master']
pipeline:
gen-vc:
# This is what the official CI uses as well
image: 'ubuntu:latest'
secrets:
- deploy_key
commands:
# Install necessary dependencies
- apt-get update -y && apt-get install openssh-client git build-essential -y
# Build the compiler
- make
# Run ssh-agent
- eval $(ssh-agent -s)
# Add ssh key
- echo "$DEPLOY_KEY" | tr -d '\r' | ssh-add -
# Create ssh dir with proper permissions
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
# Configure git credentials
- git config --global user.email 'vbot@rustybever.be'
- git config --global user.name 'vbot'
# Verify SSH keys
- ssh-keyscan git.rustybever.be > ~/.ssh/known_hosts
# The following is copied over from the official repo's CI
# https://github.com/vlang/v/blob/master/.github/workflows/gen_vc.yml
- export "COMMIT_HASH=$(git rev-parse --short HEAD)"
- export "COMMIT_MSG=$(git log -1 --oneline --pretty='%s' HEAD)"
- rm -rf vc
- git clone --depth=1 'git@git.rustybever.be:vieter/vc.git'
- rm -rf vc/v.c vc/v_win.c
- ./v -o vc/v.c -os cross cmd/v
- ./v -o vc/v_win.c -os windows -cc msvc cmd/v
- sed -i "1s/^/#define V_COMMIT_HASH \"$COMMIT_HASH\"\n/" vc/v.c
- sed -i "1s/^/#define V_COMMIT_HASH \"$COMMIT_HASH\"\n/" vc/v_win.c
# ensure the C files are over 5000 lines long, as a safety measure
- '[ $(wc -l < vc/v.c) -gt 5000 ]'
- '[ $(wc -l < vc/v_win.c) -gt 5000 ]'
- git -C vc add v.c v_win.c
- 'git -C vc commit -m "[v:master] $COMMIT_HASH - $COMMIT_MSG"'
# in case there are recent commits:
- git -C vc pull --rebase origin main
- git -C vc push
when:
event: push
publish:
image: woodpeckerci/plugin-docker-buildx
secrets: [ docker_username, docker_password ]
settings:
repo: chewingbever/vlang
tag: latest
dockerfile: Dockerfile.builder
platforms: [ linux/arm64/v8, linux/amd64 ]
# The build can run every time, because we should only push when there's
# actual changes
when:
event: push

View File

@ -0,0 +1,32 @@
matrix:
PLATFORM:
- 'linux/amd64'
- 'linux/arm64'
platform: ${PLATFORM}
branches: ['master']
depends_on:
- 'vc'
pipeline:
build:
image: 'menci/archlinuxarm:base-devel'
commands:
# Update packages
- pacman -Syu --noconfirm
# Create non-root user to perform build & switch to their home
- groupadd -g 1000 builder
- useradd -mg builder builder
- chown -R builder:builder "$PWD"
- "echo 'builder ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers"
- su builder
# Build the package
- makepkg -s --noconfirm --needed
publish:
image: 'curlimages/curl'
secrets:
- 'vieter_api_key'
commands:
# Publish the package
- 'for pkg in $(ls -1 *.pkg*); do curl -f -XPOST -T "$pkg" -H "X-API-KEY: $VIETER_API_KEY" https://arch.r8r.be/vieter/publish; done'

View File

@ -0,0 +1,18 @@
platform: 'linux/amd64'
branches: ['master']
depends_on:
- 'vc'
pipeline:
build-publish:
image: 'woodpeckerci/plugin-docker-buildx'
secrets: [ docker_username, docker_password ]
settings:
repo: chewingbever/vlang
tag: latest
dockerfile: Dockerfile.builder
platforms: [ linux/arm64/v8, linux/amd64 ]
# The build can run every time, because we should only push when there's
# actual changes
when:
event: push

View File

@ -0,0 +1,48 @@
platform: 'linux/amd64'
branches: ['master']
pipeline:
gen-vc:
# This is what the official CI uses as well
image: 'ubuntu:latest'
secrets:
- deploy_key
commands:
# Install necessary dependencies
- apt-get update -y && apt-get install openssh-client git build-essential -y
# Build the compiler
- make
# Run ssh-agent
- eval $(ssh-agent -s)
# Add ssh key
- echo "$DEPLOY_KEY" | tr -d '\r' | ssh-add -
# Create ssh dir with proper permissions
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
# Configure git credentials
- git config --global user.email 'vbot@rustybever.be'
- git config --global user.name 'vbot'
# Verify SSH keys
- ssh-keyscan git.rustybever.be > ~/.ssh/known_hosts
# The following is copied over from the official repo's CI
# https://github.com/vlang/v/blob/master/.github/workflows/gen_vc.yml
- export "COMMIT_HASH=$(git rev-parse --short HEAD)"
- export "COMMIT_MSG=$(git log -1 --oneline --pretty='%s' HEAD)"
- rm -rf vc
- git clone --depth=1 'git@git.rustybever.be:vieter-v/vc.git'
- rm -rf vc/v.c vc/v_win.c
- ./v -o vc/v.c -os cross cmd/v
- ./v -o vc/v_win.c -os windows -cc msvc cmd/v
- sed -i "1s/^/#define V_COMMIT_HASH \"$COMMIT_HASH\"\n/" vc/v.c
- sed -i "1s/^/#define V_COMMIT_HASH \"$COMMIT_HASH\"\n/" vc/v_win.c
# ensure the C files are over 5000 lines long, as a safety measure
- '[ $(wc -l < vc/v.c) -gt 5000 ]'
- '[ $(wc -l < vc/v_win.c) -gt 5000 ]'
- git -C vc add v.c v_win.c
- 'git -C vc commit -m "[v:master] $COMMIT_HASH - $COMMIT_MSG"'
# in case there are recent commits:
- git -C vc pull --rebase origin main
- git -C vc push
when:
event: push

493
CHANGELOG.md 100644
View File

@ -0,0 +1,493 @@
-## V 0.2.5
-*Not yet released, changelog is not full*
- Introduce `isize` and `usize` types, deprecate `size_t` in favor of `usize`.
- Add `datatypes` and `datatypes.fsm` modules.
- Add `compile_error` and `compile_warn` comptime functions.
-## V 0.2.4
-*Not yet released, changelog is not full*
- Bare metal support. Vinix OS kernel is now being developed in V.
- Builtin web framework vweb is now multithreaded, all CPU cores are used.
- String interpolation and struct stringers are now implemented in pure V
with a much cleaner and faster implementation. Previously libc's `sprintf`
was used.
- Improved `unused variable` warning. Assigning to a variable no longer marks it as used.
## V 0.2.2 - 0.2.3
*22 Jan 2021*
- Allow interfaces to define fields, not just methods.
- `vweb` now uses struct embedding: `app.vweb.text('hello') => app.text('hello')`.
- Consts can now be declared outside of `const()` blocks: `const x = 0`.
- Overloading of `>`, `<`, `!=`, `==`, `<=` and `>=` operators.
- New struct updating syntax: `User{ ...u, name: 'new' }` to replace `{ u | name: 'new' }`.
- `byte.str()` has been fixed and works like all other numbers. `byte.ascii_str()` has been added.
- Smart cast in for loops: `for mut x is string {}`.
- `[noinit]` struct attribute to disallow direct struct initialization with `Foo{}`.
- Array decompose: `[1, 2, 3]...` is now `...[1, 2, 3]`
- Treating `enum` as `int` and operations on `enum` except `==` and `!=` are removed for strict type checking.
- Support `[manualfree] fn f1(){}` and `[manualfree] module m1`, for functions doing their own memory management.
- Allow usage of `<` and `>` operators for struct in `.sort` method for arrays, i.e. `arr.sort(a < b)`.
- Auto generate assignment operators like `+=`, `-=`, `*=`, `/=` and `%=` if the operators are defined.
- Colorize and improve failing tests output.
- Fix `go` with a generic function: `go test<string>(c, 'abcd')`.
- Add comptime `x := $embed_file('v.png') println(x.len) println(ptr_str(x.data()))`, for embedding files into binaries.
- Advanced vdoc search on mobile layout.
- string's `left()`/`right` were removed in favor of slicing syntax: `str[..pos]`.
- gg: native graphics mode on macOS/iOS (using Cocoa Drawing API).
- Full path to consts must be specified everywhere. This makes it easy to distinguish them
from local variables.
- `__offsetof` for low level needs (works like `offsetof` in C).
- vfmt now preserves empty lines, like gofmt.
- Support for compile time environment variables via `$env('ENV_VAR')`.
- Allow method declaration of `==` and `<` operators and auto generate `!=`, `>`, `<=` and `>=`.
- support `dump(expr)`, i.e. tracing of both the location, name and value of an expression
- deprecate os.exec in favour of os.executable() which does *NOT* return an option, when the command was not found
## V 0.2.1
*30 Dec 2020*
- Hashmap bootstrapping fixes.
- Array decomposition to varargs: `fn sum(i ...int) int` => `a := [2,3,4] println(sum(a...))`
- HTML module docs generated by vdoc now have global search.
## V 0.2
*22 Dec 2020*
- Compile-time memory management via `-autofree`. [Video demonstration](https://www.youtube.com/watch?v=gmB8ea8uLsM).
It will be enabled by default in 0.3
- Channels and locks.
- Thread safe typed arrays via keyword `shared`.
- Struct embedding.
- IO streams.
- A powerful websocket module that conforms to RFC 6455 and passes the Autobahn test suite (498 client tests and 249 server tests).
- The `net` module is now non blocking and is more feature complete providing similar API to Go.
- V's graphics module now uses Metal/DirectX/OpenGL instead of just OpenGL.
- V can now run in the browser via WASM and execute V code by translating it to JavaScript:
https://v-wasm.now.sh
- V binaries for Linux/Windows/macOS are now built and deployed automatically via GitHub Actions.
- Smart casting for sumtypes and interfaces, including complex expressions: `if x.expr is int { println(x.expr + 1) }`.
- Clean and easy way to sort arrays: `users.sort(a.name > b.name)`.
- A huge amount of `vfmt` fixes and improvements. It has now reached a point where it can be safely used on any V source file.
- A new CI job that runs `v fmt -verify` on the entire code base, a new command that makes sure the file/directory
has been vfmt'ed. This ensures that all code submitted to the V project is formatted.
- A new tool `v vet` for analyzing the project and finding potential bugs and errors.
- A new `term.ui` module for building dynamic terminal UIs with an example editor written in it.
- Early iOS and Android support.
- All missing ORM features from the old backend were brought back.
- Magic `it` variable has been replaced with smart casts (the change is completely handled by vfmt).
- Cross-compiling to Windows and Linux brought back.
- C2V can now generate wrappers. Example: https://github.com/medvednikov/libsodium. (C2V will be released by 0.3)
- C++ compiler support: code, generated by the C backend can now by compiled by C++ compilers.
- Short generics syntax: `foo(5)` instead of `foo<int>(5)`.
- Cached modules via `-usecache`. Faster compilation due to not needing to rebuild the entire vlib for
each program. Will be enabled by default in 0.2.1.
- New improved sum types implementation.
- Lots of errors that happen often during the development cycle were turned into warnings to increase
development speed. They are still errors in production builds.
- Labeled `break` and `continue`.
- Lots of documentation. The official language documentation grew 3 times in size.
- `modules.vlang.io` is now generated automatically on every commit.
- Builtin compile-time JSON serializer now supports `time.Time`.
- Fixes in type aliases, to make them behave just like the types they alias.
- `array.contains(element)` is now generic.
- Lots of improvements in the JS backend and its type system.
- Simpler and more constinent function arg syntax: `foo(a int, b int, c string)` instead of `foo(a, b int, c string)`
- Lots of fixes and optimizations in the hashmap.
- Lots of missing checks in the type checker were added (for example, checking the correct usage of public struct fields).
- Mutability bug fixes
- Taking the address of a map value is no longer allowed, like in Go.
- Matrix multiplication.
- A new `#pkgconfig` flag to provide platform independent way to get compilation flags for C libraries/packages.
- Explicit parentheses requirement in complex boolean expressions.
- `println` was made even smarter, and can now handle complex types.
- Precompiled text templates can now be used outside of vweb via `$tmpl()`.
- Gitly, a big web application written in vweb has been released: https://github.com/vlang/gitly
- `['/:arg1/:arg2/action']` vweb action attribute for easily getting query parameters assigned to method arguments.
- Improved performance of text rendering, `gg.text_width()`.
- Webview module in V UI.
- Binary enum flags.
- `[export]` attribute to change exported function name (for example for calling from a C library).
- `unsafe` fixes and improvements.
- Improvements to rand: `rand.ulid()`, `rand.uuid()`, a unified customizable PRNG API.
- Hundreds of other fixes, features, and tests (from now on the changelog will be updated
right away as the feature/bug fix lands).
## V 0.1.27
*5 May 2020*
- vfmt has been re-written from scratch using the new AST parser.
It's much faster, cleaner, and can format
files with compilation errors.
- `strconv`, `sprintf`, and `printf` in native V, without any libc calls.
- Interfaces are now a lot more stable and have all expected features.
- Lots of x64 backend improvements: function calls, if expressions, for loops, local variables.
- `map()` and `filter()` methods can now be chained.
- New `[]int{cap:cap, len:len}` syntax for initializing array length and capacity.
- New `is` keyword for checking the type of sum types and interfaces.
- `as` can now be used to cast interfaces and sum types.
- Profiling with `-profile`. Prints a nice table with details about every single function call:
number of calls, average time per call, total time per function
- `import(xxx)` syntax has been removed in favor of `import xxx` for simplicity and greppability.
- Lots of fixes and improvements in the type checker.
- `time.StopWatch`
- `dl` module for dynamic loading.
- Automatic `str()` method generation for every single type, including all arrays.
- Short struct initialization syntax for imitating named function args: `foo(bar:0, baz:1)`.
- New operator `!in`.
- Performance improvements in critical parts of the builtin data structures (array, map).
- High order functions improvements (functions can now be returned etc).
- Anonymous functions that can be defined inside other functions.
- Built-in JSON module is back.
- Lots and lots of new tests added, including output tests that test error messages.
- Multiple errors are now printed, the compiler no longer stops after the first error.
- The new JS backend using the AST parser (almost complete).
- Variadic functions.
- `net.websocket` module (early stage).
- `vlib` is now memory leak free, lots of `autofree` improvements.
- Simplified and cleaned up `cmd/v`, `v.builder`.
- V UI was updated to work with the new backend.
## V 0.1.25
*1 Apr 2020*
- The entire compiler has been re-written with an AST parser.
The code is now a lot cleaner and more maintainable.
~15k lines of old compiler code were removed.
## V 0.1.24
*31 Dec 2019*
- A new parser/generator built on top of an AST that simplifies code greatly
and allows to implement new backends much faster.
- Sum types (`type Expr = IfExpr | MatchExpr | IntegerLiteral`).
- B-tree map (sped up the V compiler by ~10%).
- `v fmt -w`.
- The entire code base has been formatted with vfmt.
- Generic structs.
- SDL module.
- Arrays of pointers.
- os: `is_link()`, `is_dir()`, `exists()`.
- Ranging through fixed size arrays.
- Lots of fixes in ORM and vweb.
- The first tutorial: [building a simple web application with vweb](https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_vweb/README.md)
- Match expressions now must be exhaustive.
- freestanding: `malloc()`/`free()`.
- `++` is now required instead of `+= 1` for consistency.
- Interpolated strings now allow function calls: `println('val = $get_val()')`.
- `string.replace_each([])` for an efficient replacement of multiple values.
- More utf8 helper functions.
- `-prealloc` option for block allocations.
- `type` aliases.
- Running `v` with an unknown command will result in an error.
- `atof` implementation in pure V.
- Enums can now have negative values.
- New `filepath` module.
- `math.factorial`.
- `ftp` module.
- New syntax for casting: `val as Type`.
- Fewer libc functions used (soon V will have no dependency on libc).
## V 0.1.23
*30 Nov 2019*
- [Direct x64 machine code generation](https://github.com/vlang/v/issues/2849).
Hello world being built in 3 milliseconds.
- Bare metal support via the `-freestanding` flag, to build programs without linking to libc.
- Prebuilt V packages for Linux, macOS, and Windows.
- `string.index()` now returns `?int` instead of `int/-1`.
- Lots of fixes in Generics.
- vweb framework for developing web applications is back.
- Vorum, the forum/blogging software written in vweb, can now be compiled and has been added to CI.
- REPL, `v up` have been split up into separate applications to keep the core V compiler small.
- V now enforces short enum syntax (`.green` instead of `Color.green`) when it's enough.
- V UI for macOS.
- Interfaces have been rewritten. `[]interface` support.
- `os.cp()` for copying files and directores.
- Additional compile-time flags: `$if clang, msvc, mingw, x32, x64, big_endian, little_endian {`.
- All C functions now have to be declared, all missing C functions have been defined.
- Global variables (only with the `-enable-globals` flag)
for low level applications like kernels and drivers.
- Nothing can be cast to bool (previously code like `if bool(1) {` worked).
- `<<` and `>>` now work with all integer types.
- V detects Cygwin and shows an error (V supports Windows natively).
- Improved type checking of some operators (`%, |, &` etc).
- Windows 7 support.
- `println(true)` now prints `true` instead of `1`.
- `os.exec()` now uses `CreateProcess` on Windows.
- fast.vlang.io website for monitoring the performance of V after every commit.
- On Windows Visual Studio is now used automatically if GCC is not installed.
- vfmt!
- Lots of cleaning up in the compiler code.
- Multi-level pointers in unsafe code (`****int`).
- MSVC backtrace.
- `$if os {` blocks are now skipped on a different OS.
- C string literals (`c'hello'`).
- AlpineLinux/musl fixes + added to CI.
- Inline assembly.
- Clipboard module (Windows, macOS, X).
- `foo()?` syntax for error propagation.
- Docs have been migrated from HTML to `doc/docs.md`.
- `eventbus` module.
- Haiku OS support.
- `malloc/free` on bare metal.
- `utf8` helper functions (`to_lower()`, `to_upper()`, etc).
- Optimization of `for c in str {`.
- `string/array.left/right/slice/substr` were removed (use `[a..b]` slicing syntax instead).
## V 0.1.22
*28 Oct 2019*
- Generic functions (`fn foo<T>(bar T) T {`) with varargs support.
- `array[start..end]` and `string[start..end]` slicing syntax.
- Optimized `array.filter()` and `array.map()`.
- `sqlite` module.
- Cached modules for faster compilation.
- Dramatic compilation optimizations: [V now compiles itself in 0.10 - 0.30 seconds](https://github.com/vlang/v/wiki/The-V-language-now-compiles-itself-in-0.09-seconds)
- V scripts (simpler and cross-platform alternative to Bash).
- Infinite multi-dimensional arrays (`[][][]int`).
- `unsafe`.
- `[deprecated]` attribute.
- `[if]` function attributes for compile time function exclusion for performance.
- `switch` has been completely removed from the language and replaced by
`match` everywhere.
- `pub struct` and `pub const`, previously all structs and consts were public
by default.
- `musl` support (V can now run on, for example, Alpine Linux).
- Module header generation. V now supports closed source modules, which are still
used in some industries.
- Constants were added to typo suggestions.
- `color in [.green, .red, .blue]` now works without specifying `Color.green`.
- V compiler is now a module that can be used by other programs.
- Backtraces now have source lines on Linux.
- `runtime.nr_cpus()`.
- `fn init()` for module initialization.
- `a in [1, 2, 3]` optimization: no array gets allocated.
- Raw strings: `s := r'hello\nworld'`.
- `if a := func() { }` syntax for handling optionals.
- f32/f64 comparison now uses machine epsilon by default.
## V 0.1.21
*30 Sep 2019*
- `none` keyword for optionals.
- Solaris support.
- All table lookup functions now use `none`.
- varargs: `fn foo(bar int, params ...string) {`.
- Double quotes (`"`) can now also be used to denote strings.
- GitHub Actions CI in addition to Travis.
- `-compress` option. The V binary built with `-compress` is only ~90 KB!
- More memory management.
- Unused modules result in an error.
- "Unused variable/module" errors are now warnings in non-production builds.
- Duplicate methods with the same name can no longer be defined.
- Struct names must be capitalized, variable/function names must use snake_case.
- Error messages are now even nicer!
- Lots of fixes in automatic `.str()` method generation for structs and arrays.
- ~30% faster parser (files are no longer parsed separately for each pass).
- `_` is no longer a variable, but an actual syntax construct to skip unused values, like in Go.
- Multiple returns (`fn foo() (int, string) {`).
- `!` can now only be used with booleans.
## V 0.1.20
*17 Sep 2019*
- JavaScript backend!
- Hundreds of C warnings were fixed. `gcc v.c` now builds without
any warnings.
- The mutability check now applies to function args (mutable
receivers that are not modified result in a compilation error).
- V tests now show how long each test took.
- Official Android support (only console applications via Termux for now).
- Typo check. If a variable/function/module etc is misspelled,
V will suggest the correct name.
- Lots of Microsoft C fixes, and a separate Travis instance for
this backend.
- Bitwise operators `|`, `^`, `&` no longer work with booleans.
## V 0.1.19
*12 Sep 2019*
- Lots of refactoring, simplifications, and optimizations in the compiler.
- Experimental memory management at compilation (only for the V compiler itself for now).
- Lots of ORM fixes.
- Functions can now be inlined via the `[inline]` attribute.
- New `mysql` module.
- Better error format that is supported by all major editors (go to error).
- Error messages now point to the actual place where the error happened.
- Custom json field names: `struct User { last_name string [json:lastName] }`.
- Raw json fields via the `[raw]` attribute.
- All C code was removed from the `freetype` module.
- `gg` module can now render all Unicode characters.
- `[typedef]` attribute for imported C struct typedefs.
- Support of Objective C interfaces (primarily for using Cocoa).
- REPL: clear command and custom functions.
- REPL tests (which are also used for testing certain compiler errors).
- Syntax bug fixed: `foo[0] += 10` is now possible.
- http: support plain HTTP protocol and follow redirects.
- http: header data is now processed correctly.
- net: basic UDP support.
- `import const` was removed from the language.
- `array.contains()` was removed from the language (`in` should be used instead).
- `[0; len]` syntax was removed (replaced with a simpler `[0].repeat(len)`)
- Primitive aliases were removed to simplify the language.
- GitHub supports V now!
- Backtraces are now printed on panics.
- A new awesome `readline` module.
- V.c is now regenerated automatically after every commit.
- A bug with struct ordering was fixed, now structs can be declared in any order.
- V modules can now be built with `v build module`.
- `@FILE, @LINE, @FN, @COLUMN` for debugging.
## V 0.1.18
*16 Aug 2019*
- Built-in ORM (`uk_customers = db.select from Customer where country == 'uk' && nr_orders > 0`).
- Map initialization syntax: `m := { foo: bar, baz: foo }`.
- `map.delete(key)`.
- `libcurl` dependency was removed from the `http` module.
- All function arguments are now immutable by default (previously they could be
modifed inside the function).
- `http` functions now return optionals.
- `sync.WaitGroup`.
- `vweb` static files serving.
- `crypto.rand` module.
- `v up` to update V.
- SChannel support on Windows.
- `net.urllib` module.
- vpm package manager, `v install`.
- `()` are now required in complex bool expressions: `(a && b) || c` instead of `a && b || c`.
- All arrays now have a default `.str()` method.
- Bootstrapping V with MSVC.
- Experimental `≠` etc support.
- `encoding.csv` module.
- `$if debug {` for running code in debug mode only.
- Map struct fields are now initialized automatically, just like arrays.
- Maps now support array values.
- `json` functions can no longer be used if the `json` module is not imported.
## V 0.1.17
*29 Jul 2019*
- `vweb` module for developing web apps in V.
- vtalk, open source V forum software.
- Generics (very limited right now, but they will be gradually improved).
- Comptime codegen (`foo.$method()` where `method` is a string).
- @ for escaping keywords (e.g. `struct Foo { @type string }`).
- Windows Unicode fixes (V can now work with non-ASCII paths etc on Windows).
- Fix mutable args bugs + don't allow primitive arguments to be modified.
- Declaring a mutable variable and never modifying it results in a compilation error.
- Interactive debugging support.
- `sync` module for Windows.
- `#!` support on Unix systems (V scripts).
- Lots of Visual Studio fixes.
- `crypto.aes` and `crypto.rc4` modules.
- Internal modules.
## V 0.1.16
*23 Jul 2019*
- V can now be used with Visual Studio!
- Hot code reloading now works with graphical applications (e.g. graph.v, bounce.v).
- Compile time memory management for arrays.
- High order functions.
- `match` expression (replacing `switch`).
- Import cycle detection.
- `crypto/md5`, `crypto/sha256`, and `crypro/sha512` modules.
- `os.executable()` - a cross platform function that returns full path to current executable.
- `~/.vlang` and `VROOT` were removed entirely. The installation is a lot cleaner now.
- V can now be packaged for all Linux distros.
- Arch Linux package.
- `string(bytes_buffer, len)`, `string(bytes_array)` casts.
- Multiple `defer`s.
- `key in map` syntax (replacing `map.exists(key)`).
## V 0.1.15
*15 Jul 2019*
- FreeBSD, OpenBSD, NetBSD, DragonFly support.
- Hot reloading now works with graphical applications: [bounce.v](examples/hot_reload/bounce.v)
- VROOT was removed, the installation process is now much simpler.
- `defer` statement.
- map.v was re-written. It's now much faster.
- `for key, val in map` syntax.
- `flag` module for parsing command line arguments.
- `zip` module.
- `crypto/sha1` module.
- Submodules and module aliases (`import encoding.base64 as b64`).
## V 0.1.14
*12 Jul 2019*
- `gg` module Windows support, V Tetris runs on Windows.
- Compile `glad` and `cJSON` only once. Programs using `gg` or `json` compile a bit faster.
- `v.c` has been cleaned up and minimized (~16k => ~10k lines of code).
- `type` aliases can now have methods.
- Const overflow check during compilation (`byte(1000)` will no longer compile).
## V 0.1.13
*10 Jul 2019*
- New enum syntax (`token == .name`), enum values are no longer global consts.
- Submodules (`import encoding.base64`).
- Hot code reloading.
- Special `err` variable for getting error values.
- Complex numbers.
- `<<` can now append arrays (`numbers << [1, 2, 3]`).
- Lots of Windows fixes (Windows still needs some work).
- Lots of REPL improvements (e.g. `>> 2 + 3` works now, no `println` required).
- The website was made easily translatable, it's now partially available in several languages.
## V 0.1.12
*4 Jul 2019*
- V can finally compile itself on Windows (https://github.com/vlang/v#mingw-w64).
- `os` module now uses optionals in all functions that return `File`.
- Lots of bugs with optionals were fixed.
- `println` was optimized. It no longer results in allocations.
Now it also works correctly with all integer types.
- Lots of `vfmt` fixes, it will be enabled tomorrow.
- New `strings` module.
- Lots of other fixes and improvements, thanks to all the contributors.
## V 0.1.11
*1 Jul 2019*
- Cross compilation for Windows!
- Lots of Windows fixes.
- socket.v.
- maps fixed.
## V 0.1.9 - 0.1.10
*29 Jun 2019*
- Windows support via MinGW-w64. Pre-built Windows binary.
- File structure has been simplified: all vlib modules were moved to the vlib/ directory,
makefile was moved to the root.
- One single archive with pre-built binaries for all operating systems.
- `mut var := val` was fixed (previously `mut var = val` was allowed as well).
## V 0.1.8
*28 Jun 2019*
- Single file programs without `fn main` now work as expected.
- REPL has been fixed: it now supports imports, consts, function definitions, etc.
## V 0.1.7
*27 Jun 2019*
- All C code in the compiler and vlib has been replaced with V.
- `#` syntax for embedding C code has been removed.
- Exported functions now need to be marked with `pub`, all public vlib functions have been updated.
- CI has been set up (Travis + Azure). On every commit and PR it is made sure that V
can compile itself, all tests pass, and all examples compile.
- More tests have been uploaded.
- Cleaner bytes to string conversion: `tos2(bytes)` => `string(bytes)`.
- The home page has 3 more examples next to 'hello world' that show the features of the language.
- Lots of bugs and issues fixed.

View File

@ -0,0 +1,4 @@
# Code of Conduct
Be nice and respectful.

206
CONTRIBUTING.md 100644
View File

@ -0,0 +1,206 @@
## Code Structure
I tried to make the code of the compiler and vlib as simple and readable as
possible. One of V's goals is to be open to developers with different levels
of experience in compiler development. Compilers don't need to be black boxes
full of magic that only few people understand.
The V compiler is modular, and can be used by other applications. It is located
in `cmd/v/` and `vlib/v/`.
The most important and useful command to remember when working on the V compiler
is `v self`.
It rebuilds the V compiler.
Be careful, if you introduce a breaking change and rebuild V, you will no longer
be able to use V to build itself. So it's a good idea to make a backup copy of a
working compiler executable.
But don't worry, you can always simply run `make` (or `make.bat`), it will
download the C version of the compiler and rebuild it from scratch.
The architecture of the compiler is very simple and has three distinct steps:
Parse/generate AST (`v.parser`) => Check types (`v.checker`)
=> Generate C/JavaScript/machine code (`v.gen`)
The main files are:
1. `cmd/v/v.v` The entry point.
- V figures out the build mode.
- Constructs the compiler object (`struct V`).
- Creates a list of .v files that need to be parsed.
- Creates a parser object for each file and runs `parse()` on them.
- The correct backend is called (C, JS, native), and a binary is compiled.
2. `vlib/v/scanner` The scanner's job is to parse a list of characters and convert
them to tokens.
3. `vlib/v/token` This is simply a list of all tokens, their string values, and a
couple of helper functions.
4. `vlib/v/parser` The parser. It converts a list of tokens into an AST.
In V, objects can be used before declaration, so unknown types are marked as
unresolved. They are resolved later in the type checker.
5. `vlib/v/table` V creates one table object that is shared by all parsers. It
contains all types, consts, and functions, as well as several helpers to search
for objects by name, register new objects, modify types' fields, etc.
6. `vlib/v/checker` Type checker and resolver. It processes the AST and makes sure
the types are correct. Unresolved types are resolved, type information is added
to the AST.
7. `vlib/v/gen/c` C backend. It simply walks the AST and generates C code that can be
compiled with Clang, GCC, Visual Studio, and TCC.
8. `vlib/v/gen/js` JavaScript backend. It simply walks the AST and generates JS code that can be
executed on the browser or in NodeJS/Deno.
9. `vlib/v/gen/c/json.v` defines the json code generation. This file will be removed once V
supports comptime code generation, and it will be possible to do this using the
language's tools.
10. `vlib/v/gen/native` is the directory with all the machine code generation logic. It
defines a set of functions that translate assembly instructions to machine code
and build the binary from scratch byte by byte. It manually builds all headers,
segments, sections, symtable, relocations, etc. Right now it only has basic
support of the native platform (ELF, MACHO format).
The rest of the directories are vlib modules: `builtin/` (strings, arrays,
maps), `time/`, `os/`, etc. Their documentation is pretty clear.
## Example Workflow for Contributing
(provided by [@spytheman](https://github.com/spytheman))
(If you don't already have a GitHub account, please create one. Your GitHub
username will be referred to later as 'YOUR_GITHUB_USERNAME'. Change it
accordingly in the steps below.)
1. Fork https://github.com/vlang/v using GitHub's interface to your own account.
Let's say that the forked repository is at
`https://github.com/YOUR_GITHUB_USERNAME/v` .
2. Clone the main v repository https://github.com/vlang/v to a local folder on
your computer, say named nv/ (`git clone https://github.com/vlang/v nv`)
3. `cd nv`
4. `git remote add pullrequest https://github.com/YOUR_GITHUB_USERNAME/v`
NB: the remote named `pullrequest` should point to YOUR own forked repo, not the
main v repository! After this, your local cloned repository is prepared for
making pullrequests, and you can just do normal git operations such as:
`git pull` `git status` and so on.
5. When finished with a feature/bugfix/change, you can:
`git checkout -b fix_alabala`
- Don't forget to keep formatting standards, run `v fmt -w YOUR_MODIFIED_FILES` before committing
6. `git push pullrequest` # (NOTE: the `pullrequest` remote was setup on step 4)
7. On GitHub's web interface, go to: https://github.com/vlang/v/pulls
Here the UI shows a dialog with a button to make a new pull request based on
the new pushed branch.
(Example dialog: https://url4e.com/gyazo/images/364edc04.png)
8. After making your pullrequest (aka, PR), you can continue to work on the
branch `fix_alabala` ... just do again `git push pullrequest` when you have more
commits.
9. If there are merge conflicts, or a branch lags too much behind V's master,
you can do the following:
1. `git pull --rebase origin master` # solve conflicts and do
`git rebase --continue`
2. `git push pullrequest -f` # this will overwrite your current remote branch
with the updated version of your changes.
The point of doing the above steps, is to never directly push to the main V
repository, *only to your own fork*. Since your local `master` branch tracks the
main V repository's master, then `git checkout master`, as well as
`git pull --rebase origin master` will continue to work as expected
(these are actually used by `v up`) and git can always do it cleanly.
Git is very flexible, so there are other ways to accomplish the same thing.
See the [GitHub flow](https://guides.github.com/introduction/git-handbook/#github), for more information.
## Using Github's hub CLI tool
You can download the `hub` tool from https://hub.github.com/ . Using
`hub`, you will not need to go through the (sometimes) slow website
to make PRs. Most remote operations can be done through the `hub` CLI
command.
NB: You still need to have a GitHub account.
### Preparation:
(steps 1..3 need to be done just *once*):
1. `hub clone vlang/v my_v`
2. `cd my_v`
3. `hub fork --remote-name pullrequest`
4. `git checkout -b my_cool_feature` # Step 4 is better done *once per each new
feature/bugfix* that you make.
### Improve V by making commits:
5. `git commit -am "math: add a new function copysign"`
### Testing your commits locally:
You can test locally whether your changes have not broken something by
running: `v test-all`. See `TESTS.md` for more details.
### Publishing your commits to GitHub:
6. `git push pullrequest`
### Making a PR with `hub`:
(so that your changes can be merged to the main V repository)
7. `hub pull-request`
Optionally, you can track the status of your PR CI tests with:
8. `hub ci-status --verbose`
### Fixing failing tests:
If everything is OK, after 5-10 minutes, the CI tests should pass for
all platforms. If not, visit the URLs for the failing CI jobs, see
which tests have failed and then fix them by making more changes. Just use
`git push pullrequest` to publish your changes. The CI tests will
run with your updated code. Use `hub ci-status --verbose` to monitor
their status.
## Flags
V allows you to pass custom flags using `-d my_flag` that can then be checked
at compile time (see the documentation about
[compile-time if](https://github.com/vlang/v/blob/master/doc/docs.md#compile-time-if)).
There are numerous flags that can be passed when building the compiler
with `v self` or when creating a copy of the compiler, that will help
you when debugging.
Beware that the flags must be passed when building the compiler,
not the program, so do for example: `v -d time_parsing cmd/v` or
`v -d trace_checker self`.
Some flags can make the compiler very verbose, so it is recommended
to create a copy of the compiler rather than replacing it with `v self`.
| Flag | Usage |
|------|-------|
| `debugscanner` | Prints debug information during the scanning phase |
| `debug_codegen` | Prints automatically generated V code during the scanning phase |
| `debug_interface_table` | Prints generated interfaces during C generation |
| `debug_interface_type_implements` | Prints debug information when checking that a type implements in interface |
| `print_vweb_template_expansions` | Prints vweb compiled HTML files |
| `time_checking` | Prints the time spent checking files and other related information |
| `time_parsing` | Prints the time spent parsing files and other related information |
| `trace_autofree` | Prints details about how/when -autofree puts free() calls |
| `trace_autostr` | Prints details about `.str()` method auto-generated by the compiler during C generation |
| `trace_ccoptions` | Prints options passed down to the C compiler |
| `trace_checker` | Prints details about the statements being checked |
| `trace_gen` | Prints strings written to the generated C file. Beware, this flag is very verbose |
| `trace_parser` | Prints details about parsed statements and expressions |
| `trace_thirdparty_obj_files` | Prints details about built thirdparty obj files |
| `trace_usecache` | Prints details when -usecache is used |
| `trace_embed_file` | Prints details when $embed_file is used |
| `embed_only_metadata` | Embed only the metadata for the file(s) with `$embed_file('somefile')`; faster; for development, *not* distribution |

27
Dockerfile 100644
View File

@ -0,0 +1,27 @@
#same container that golang use
FROM buildpack-deps:buster-curl
LABEL maintainer="ANAGO Ronnel <anagoandy@gmail.com>"
WORKDIR /opt/vlang
ARG USE_LOCAL
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends gcc clang make git && \
apt-get clean && rm -rf /var/cache/apt/archives/* && \
rm -rf /var/lib/apt/lists/*
COPY . /vlang-local
RUN if [ -z "${USE_LOCAL}" ] ; then \
git clone https://github.com/vlang/v/ /opt/vlang && \
rm -rf /vlang-local ; \
else \
mv /vlang-local/* . && \
rm -rf /vlang-local ; \
fi
RUN make && \
ln -s /opt/vlang/v /usr/local/bin/v
CMD [ "v" ]

34
Dockerfile.alpine 100644
View File

@ -0,0 +1,34 @@
FROM alpine:3.12
LABEL maintainer="spytheman <spytheman@bulsynt.org>"
WORKDIR /opt/vlang
ENV VVV /opt/vlang
ENV PATH /opt/vlang:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
ENV VFLAGS -cc gcc
RUN mkdir -p /opt/vlang && ln -s /opt/vlang/v /usr/bin/v
ARG USE_LOCAL
RUN apk --no-cache add \
git make upx gcc bash \
musl-dev \
openssl-dev sqlite-dev \
libx11-dev glfw-dev freetype-dev
## RUN apk --no-cache add --virtual sdl2deps sdl2-dev sdl2_ttf-dev sdl2_mixer-dev sdl2_image-dev
COPY . /vlang-local
RUN if [[ -z "${USE_LOCAL}" ]] ; then \
git clone https://github.com/vlang/v/ /opt/vlang && \
rm -rf /vlang-local ; \
else \
mv /vlang-local/* . && \
rm -rf /vlang-local ; \
fi
RUN make && v -version
CMD ["v"]

33
Dockerfile.builder 100644
View File

@ -0,0 +1,33 @@
FROM alpine:3.16
ARG TARGETPLATFORM
WORKDIR /opt/vlang
ENV VVV /opt/vlang
ENV PATH /opt/vlang:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
ENV VFLAGS -cc gcc -gc none
ENV V_PATH /opt/vlang/v
RUN ln -s /opt/vlang/v /usr/bin/v && \
apk --no-cache add \
git make gcc curl openssl \
musl-dev \
openssl-libs-static openssl-dev \
zlib-static bzip2-static xz-dev expat-static zstd-static lz4-static \
sqlite-static sqlite-dev \
libx11-dev glfw-dev freetype-dev \
libarchive-static libarchive-dev \
diffutils \
mandoc
RUN git clone https://git.rustybever.be/vieter/v /opt/vlang && \
make && \
v -version
RUN if [ "$TARGETPLATFORM" = 'linux/amd64' ]; then \
wget -O /usr/local/bin/mc https://dl.min.io/client/mc/release/linux-amd64/mc && \
chmod +x /usr/local/bin/mc ; \
fi
CMD ["v"]

10
Dockerfile.cross 100644
View File

@ -0,0 +1,10 @@
FROM mstorsjo/llvm-mingw
LABEL maintainer="Delyan Angelov <delian66@gmail.com>"
COPY . .
RUN make
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
CMD [ "bash" ]

171
GNUmakefile 100644
View File

@ -0,0 +1,171 @@
CC ?= cc
CFLAGS ?=
LDFLAGS ?=
TMPDIR ?= /tmp
VROOT ?= .
VC ?= ./vc
V ?= ./v
VCREPO ?= https://git.rustybever.be/vieter/vc
TCCREPO ?= https://github.com/vlang/tccbin
VCFILE := v.c
TMPTCC := $(VROOT)/thirdparty/tcc
TCCOS := unknown
TCCARCH := unknown
GITCLEANPULL := git clean -xf && git pull --quiet
GITFASTCLONE := git clone --depth 1 --quiet --single-branch
#### Platform detections and overrides:
_SYS := $(shell uname 2>/dev/null || echo Unknown)
_SYS := $(patsubst MSYS%,MSYS,$(_SYS))
_SYS := $(patsubst MINGW%,MinGW,$(_SYS))
ifneq ($(filter $(_SYS),MSYS MinGW),)
WIN32 := 1
V:=./v.exe
endif
ifeq ($(_SYS),Linux)
LINUX := 1
TCCOS := linux
ifneq ($(shell ldd /bin/ls | grep musl),)
TCCOS := linuxmusl
endif
endif
ifeq ($(_SYS),Darwin)
MAC := 1
TCCOS := macos
endif
ifeq ($(_SYS),FreeBSD)
TCCOS := freebsd
LDFLAGS += -lexecinfo
endif
ifeq ($(_SYS),NetBSD)
TCCOS := netbsd
LDFLAGS += -lexecinfo
endif
ifdef ANDROID_ROOT
ANDROID := 1
undefine LINUX
TCCOS := android
endif
#####
ifdef WIN32
TCCOS := windows
VCFILE := v_win.c
endif
TCCARCH := $(shell uname -m 2>/dev/null || echo unknown)
ifeq ($(TCCARCH),x86_64)
TCCARCH := amd64
else
ifneq ($(filter x86%,$(TCCARCH)),)
TCCARCH := i386
else
ifeq ($(TCCARCH),arm64)
TCCARCH := arm64
else
ifneq ($(filter arm%,$(TCCARCH)),)
TCCARCH := arm
# otherwise, just use the arch name
endif
endif
endif
endif
.PHONY: all clean check fresh_vc fresh_tcc check_for_working_tcc
ifdef prod
VFLAGS+=-prod
endif
all: latest_vc latest_tcc
ifdef WIN32
$(CC) $(CFLAGS) -std=c99 -municode -w -o v1.exe $(VC)/$(VCFILE) $(LDFLAGS)
v1.exe -no-parallel -o v2.exe $(VFLAGS) cmd/v
v2.exe -o $(V) $(VFLAGS) cmd/v
del v1.exe
del v2.exe
else
$(CC) $(CFLAGS) -std=gnu99 -w -o v1.exe $(VC)/$(VCFILE) -lm -lpthread $(LDFLAGS)
./v1.exe -no-parallel -o v2.exe $(VFLAGS) cmd/v
./v2.exe -o $(V) $(VFLAGS) cmd/v
rm -rf v1.exe v2.exe
endif
@$(V) run cmd/tools/detect_tcc.v
@echo "V has been successfully built"
@$(V) -version
clean:
rm -rf $(TMPTCC)
rm -rf $(VC)
ifndef local
latest_vc: $(VC)/.git/config
cd $(VC) && $(GITCLEANPULL)
else
latest_vc:
@echo "Using local vc"
endif
check_for_working_tcc:
@$(TMPTCC)/tcc.exe --version > /dev/null 2> /dev/null || echo "The executable '$(TMPTCC)/tcc.exe' does not work."
fresh_vc:
rm -rf $(VC)
$(GITFASTCLONE) $(VCREPO) $(VC)
ifndef local
latest_tcc: $(TMPTCC)/.git/config
cd $(TMPTCC) && $(GITCLEANPULL)
@$(MAKE) --quiet check_for_working_tcc 2> /dev/null
else
latest_tcc:
@echo "Using local tcc"
@$(MAKE) --quiet check_for_working_tcc 2> /dev/null
endif
fresh_tcc:
rm -rf $(TMPTCC)
ifndef local
# Check wether a TCC branch exists for the user's system configuration.
ifneq (,$(findstring thirdparty-$(TCCOS)-$(TCCARCH), $(shell git ls-remote --heads $(TCCREPO) | sed 's/^[a-z0-9]*\trefs.heads.//')))
$(GITFASTCLONE) --branch thirdparty-$(TCCOS)-$(TCCARCH) $(TCCREPO) $(TMPTCC)
@$(MAKE) --quiet check_for_working_tcc 2> /dev/null
else
@echo 'Pre-built TCC not available for thirdparty-$(TCCOS)-$(TCCARCH) at $(TCCREPO), will use the system compiler: $(CC)'
$(GITFASTCLONE) --branch thirdparty-unknown-unknown $(TCCREPO) $(TMPTCC)
@$(MAKE) --quiet check_for_working_tcc 2> /dev/null
endif
else
@echo "Using local tccbin"
@$(MAKE) --quiet check_for_working_tcc 2> /dev/null
endif
$(TMPTCC)/.git/config:
$(MAKE) fresh_tcc
$(VC)/.git/config:
$(MAKE) fresh_vc
asan:
$(MAKE) all CFLAGS='-fsanitize=address,undefined'
selfcompile:
$(V) -cg -o v cmd/v
selfcompile-static:
$(V) -cg -cflags '--static' -o v-static cmd/v
### NB: Please keep this Makefile and make.bat simple.
install:
@echo 'Please use `sudo ./v symlink` instead.'
check:
$(V) test-all

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2019 Alexander Medvednikov
Copyright (c) 2019-2021 Alexander Medvednikov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

19
Makefile 100644
View File

@ -0,0 +1,19 @@
CC ?= cc
VFLAGS ?=
CFLAGS ?=
LDFLAGS ?=
.PHONY: all check
all:
rm -rf vc/
git clone --depth 1 --quiet https://git.rustybever.be/vieter/vc
$(CC) $(CFLAGS) -std=gnu11 -w -o v1 vc/v.c -lm -lexecinfo -lpthread $(LDFLAGS)
./v1 -no-parallel -o v2 $(VFLAGS) cmd/v
./v2 -o v $(VFLAGS) cmd/v
rm -rf v1 v2 vc/
@echo "V has been successfully built"
./v run ./cmd/tools/detect_tcc.v
check:
./v test-all

54
PKGBUILD 100644
View File

@ -0,0 +1,54 @@
# Maintainer: Jef Roosens
# This PKGBUILD is mostly copied over from the AUR
# https://aur.archlinux.org/packages/vlang-git
pkgname=vieter-v
pkgver=0.2.2.r796.gfbc02cbc5
pkgrel=1
pkgdesc='Simple, fast, safe, compiled language for developing maintainable software'
arch=('x86_64' 'aarch64')
url='https://vlang.io'
license=('MIT')
depends=('glibc')
makedepends=('git')
optdepends=('glfw: Needed for graphics support'
'freetype2: Needed for graphics support'
'openssl: Needed for http support')
provides=('vlang')
conflicts=('v' 'vlang' 'vlang-bin')
source=('vlang::git+https://git.rustybever.be/Chewing_Bever/v')
sha256sums=('SKIP')
pkgver() {
cd "${srcdir}/vlang"
# Weekly tags are considered older than semantic tags that are older than
# them, so to prevent version resolution problems we exclude weekly tags.
git describe --long --tags --exclude "weekly*" | sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g'
}
build() {
cd "${srcdir}/vlang"
# We don't require optimizations when compiling the bootstrap executable and
# -O2 actually breaks `./v self` (resulting in "cgen error:"), so we empty
# CFLAGS and LDFLAGS to ensure successful compilation.
CFLAGS="" LDFLAGS="" prod=1 make
# vpm and vdoc fail to compile with "unsupported linker option" when LDFLAGS
# is set
LDFLAGS="" ./v build-tools
}
package() {
cd "${srcdir}/vlang"
install -d "$pkgdir/usr/lib/vlang" "$pkgdir/usr/share/vlang" "$pkgdir/usr/bin"
install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
install -Dm755 v "$pkgdir/usr/lib/vlang"
cp -a cmd "$pkgdir/usr/lib/vlang/"
cp -a examples "$pkgdir/usr/share/vlang/"
cp -a thirdparty "$pkgdir/usr/lib/vlang/"
cp -a vlib "$pkgdir/usr/lib/vlang/"
cp v.mod "$pkgdir/usr/lib/vlang/"
ln -s /usr/lib/vlang/v "$pkgdir/usr/bin/v"
touch "$pkgdir/usr/lib/vlang/cmd/tools/.disable_autorecompilation"
}

305
README.md
View File

@ -1,10 +1,305 @@
# The V Programming Language
<div align="center">
<p>
<img width="80" src="https://raw.githubusercontent.com/vlang/v-logo/master/dist/v-logo.svg?sanitize=true">
</p>
<h1>The V Programming Language</h1>
V is going to be released on June 20, 2019. The source will be released on June 22, 2019.
[vlang.io](https://vlang.io) | [Docs](https://github.com/vlang/v/blob/master/doc/docs.md) | [Changelog](https://github.com/vlang/v/blob/master/CHANGELOG.md) | [Speed](https://fast.vlang.io/) | [Contributing & compiler design](https://github.com/vlang/v/blob/master/CONTRIBUTING.md)
https://vlang.io
</div>
<div align="center">
Documentation: https://vlang.io/docs
<!--
[![Build Status][WorkflowBadge]][WorkflowUrl]
-->
[![Sponsor][SponsorBadge]][SponsorUrl]
[![Patreon][PatreonBadge]][PatreonUrl]
[![Discord][DiscordBadge]][DiscordUrl]
[![Twitter][TwitterUrl]][TwitterBadge]
Twitter: https://twitter.com/v_language
</div>
## Key Features of V
- Simplicity: the language can be learned in less than an hour
- Fast compilation: ≈110k loc/s with a Clang backend,
≈1 million loc/s with native and tcc backends *(Intel i5-7500, SSD, no optimization)* ([demo video](https://www.youtube.com/watch?v=pvP6wmcl_Sc))
- Easy to develop: V compiles itself in less than a second
- Performance: as fast as C (V's main backend compiles to human-readable C)
- Safety: no null, no globals, no undefined behavior, immutability by default
- C to V translation ([Translating DOOM demo video](https://www.youtube.com/watch?v=6oXrz3oRoEg))
- Hot code reloading
- [Innovative memory management](https://vlang.io/#memory) ([demo video](https://www.youtube.com/watch?v=gmB8ea8uLsM))
- [Cross-platform UI library](https://github.com/vlang/ui)
- Built-in graphics library
- Easy cross-compilation
- REPL
- [Built-in ORM](https://github.com/vlang/v/blob/master/doc/docs.md#orm)
- [Built-in web framework](https://github.com/vlang/v/blob/master/vlib/vweb/README.md)
- C and JavaScript backends
- Great for writing low-level software ([Vinix OS](https://github.com/vlang/vinix))
## Stability guarantee and future changes
Despite being at an early development stage, the V language is relatively stable and has
backwards compatibility guarantee, meaning that the code you write today is guaranteed
to work a month, a year, or five years from now.
There still may be minor syntax changes before the 1.0 release, but they will be handled
automatically via `vfmt`, as has been done in the past.
The V core APIs (primarily the `os` module) will still have minor changes until
they are stabilized in V 1.0. Of course the APIs will grow after that, but without breaking
existing code.
Unlike many other languages, V is not going to be always changing, with new features
being introduced and old features modified. It is always going to be a small and simple
language, very similar to the way it is right now.
## Installing V - from source *(preferred method)*
### Linux, macOS, Windows, *BSD, Solaris, WSL, Android, etc.
Usually installing V is quite simple if you have an environment that already has a
functional `git` installation.
* *(* ***PLEASE NOTE:*** *If you run into any trouble or you have a different operating
system or Linux distribution that doesn't install or work immediately, please see
[Installation Issues](https://github.com/vlang/v/discussions/categories/installation-issues)
and search for your OS and problem. If you can't find your problem, please add it to an
existing discussion if one exists for your OS, or create a new one if a main discussion
doesn't yet exist for your OS.)*
To get started, simply try to execute the following in your terminal/shell:
```bash
git clone https://github.com/vlang/v
cd v
make
# HINT: Using Windows?: run make.bat in the cmd.exe shell
```
That should be it and you should find your V executable at `[path to V repo]/v`.
`[path to V repo]` can be anywhere.
(As in the hint above, on Windows `make` means running `make.bat`, so make sure you use
the `cmd.exe` terminal.)
Now you can try `./v run examples/hello_world.v` (`v.exe` on Windows).
* *Trouble? Please see the note above and link to
[Installation Issues](https://github.com/vlang/v/discussions/categories/installation-issues) for help.*
V is constantly being updated. To update V, simply run:
```bash
v up
```
### C compiler
It's recommended to use Clang, GCC, or Visual Studio.
If you are doing development, you most likely already have one of those installed.
Otherwise, follow these instructions:
- [Installing a C compiler on Linux and macOS](https://github.com/vlang/v/wiki/Installing-a-C-compiler-on-Linux-and-macOS)
- [Installing a C compiler on Windows](https://github.com/vlang/v/wiki/Installing-a-C-compiler-on-Windows)
However, if none is found when running `make` on Linux or Windows,
TCC is downloaded as the default C backend.
It's very lightweight (several MB) so this shouldn't take too long.
### Symlinking
NB: it is *highly recommended*, that you put V on your PATH. That saves
you the effort to type in the full path to your v executable every time.
V provides a convenience `v symlink` command to do that more easily.
On Unix systems, it creates a `/usr/local/bin/v` symlink to your
executable. To do that, run:
```bash
sudo ./v symlink
```
On Windows, start a new shell with administrative privileges, for
example by <kbd>Windows Key</kbd>, then type `cmd.exe`, right-click on its menu
entry, and choose `Run as administrator`. In the new administrative
shell, cd to the path, where you have compiled v.exe, then type:
```bat
.\v.exe symlink
```
That will make V available everywhere, by adding it to your PATH.
Please restart your shell/editor after that, so that it can pick
the new PATH variable.
NB: there is no need to run `v symlink` more than once - v will
continue to be available, even after `v up`, restarts, and so on.
You only need to run it again, if you decide to move the V repo
folder somewhere else.
### Docker
<details><summary>Expand Docker instructions</summary>
```bash
git clone https://github.com/vlang/v
cd v
docker build -t vlang .
docker run --rm -it vlang:latest
```
### Docker with Alpine/musl
```bash
git clone https://github.com/vlang/v
cd v
docker build -t vlang --file=Dockerfile.alpine .
docker run --rm -it vlang:latest
```
</details>
### Testing and running the examples
Make sure V can compile itself:
```bash
v self
```
```bash
$ v
V 0.2.x
Use Ctrl-C or `exit` to exit
>>> println('hello world')
hello world
>>>
```
```bash
cd examples
v hello_world.v && ./hello_world # or simply
v run hello_world.v # this builds the program and runs it right away
v run word_counter/word_counter.v word_counter/cinderella.txt
v run news_fetcher.v
v run tetris/tetris.v
```
<img src='https://raw.githubusercontent.com/vlang/v/master/examples/tetris/screenshot.png' width=300>
NB: In order to build Tetris or 2048 (or anything else using `sokol` or `gg` graphics modules)
on some Linux systems, you need to install `libxi-dev` and `libxcursor-dev` .
## V net.http, net.websocket, `v install`
If you plan to use the net.http module, or the net.websocket module, you also need to install
OpenSSL on non-Windows systems:
```bash
macOS:
brew install openssl
Debian/Ubuntu:
sudo apt install libssl-dev
Arch/Manjaro:
openssl is installed by default
Fedora:
sudo dnf install openssl-devel
```
## V sync
V's `sync` module and channel implementation uses libatomic.
It is most likely already installed on your system, but if not,
you can install it, by doing the following:
```bash
MacOS: already installed
Debian/Ubuntu:
sudo apt install libatomic1
Fedora/CentOS/RH:
sudo dnf install libatomic-static
```
## V UI
<a href="https://github.com/vlang/ui">
<img src='https://raw.githubusercontent.com/vlang/ui/master/examples/screenshot.png' width=712>
</a>
https://github.com/vlang/ui
<!---
## JavaScript backend
[examples/hello_v_js.v](examples/hello_v_js.v):
```v
fn main() {
for i in 0 .. 3 {
println('Hello from V.js')
}
}
```
```bash
v -o hi.js examples/hello_v_js.v && node hi.js
Hello from V.js
Hello from V.js
Hello from V.js
```
-->
## Android graphical apps
With V's `vab` tool, building V UI and graphical apps for Android can become as easy as:
```
./vab /path/to/v/examples/2048
```
[https://github.com/vlang/vab](https://github.com/vlang/vab).
<img src="https://user-images.githubusercontent.com/768942/107622846-c13f3900-6c58-11eb-8a66-55db12979b73.png">
## Developing web applications
Check out the [Building a simple web blog](https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_vweb/README.md)
tutorial and Gitly, a light and fast alternative to GitHub/GitLab:
https://github.com/vlang/gitly
<img src="https://user-images.githubusercontent.com/687996/85933714-b195fe80-b8da-11ea-9ddd-09cadc2103e4.png">
## Vinix, an OS/kernel written in V
V is great for writing low-level software like drivers and kernels.
Vinix is an OS/kernel that already runs bash, GCC, V, and nano.
https://github.com/vlang/vinix
<img src="https://github.com/vlang/vinix/raw/main/screenshot.png?raw=true">
## Troubleshooting
Please see the [Troubleshooting](https://github.com/vlang/v/wiki/Troubleshooting) section on our [wiki page](https://github.com/vlang/v/wiki)
[WorkflowBadge]: https://github.com/vlang/v/workflows/CI/badge.svg
[DiscordBadge]: https://img.shields.io/discord/592103645835821068?label=Discord&logo=discord&logoColor=white
[PatreonBadge]: https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3Dvlang%26type%3Dpledges
[SponsorBadge]: https://camo.githubusercontent.com/da8bc40db5ed31e4b12660245535b5db67aa03ce/68747470733a2f2f696d672e736869656c64732e696f2f7374617469632f76313f6c6162656c3d53706f6e736f72266d6573736167653d254532253944254134266c6f676f3d476974487562
[TwitterBadge]: https://twitter.com/v_language
[WorkflowUrl]: https://github.com/vlang/v/commits/master
[DiscordUrl]: https://discord.gg/vlang
[PatreonUrl]: https://patreon.com/vlang
[SponsorUrl]: https://github.com/sponsors/medvednikov
[TwitterUrl]: https://img.shields.io/twitter/follow/v_language.svg?style=flatl&label=Follow&logo=twitter&logoColor=white&color=1da1f2

34
ROADMAP.md 100644
View File

@ -0,0 +1,34 @@
## [Version 0.3](https://github.com/vlang/v/projects/5)
- [ ] [make `-autofree` the default](https://github.com/vlang/v/issues/6989)
- [x] [gc option]
- [ ] [coroutines](https://github.com/vlang/v/discussions/11582)
- [x] channels
- [x] lock{}
- [x] thread safe arrays
- [ ] [thread safe maps](https://github.com/vlang/v/discussions/11729)
- [ ] [C2V translator](https://github.com/vlang/v/issues/6985)
- [ ] doom.v
- [x] rune type
- [x] replace `ustring` with `[]rune`
- [x] fix `byte.str()`
- [x] maps with non-string keys
- [x] iOS/Android support
- [ ] parallel parser
- [ ] parallel checker
- [x] parallel cgen
- [ ] parallel C compilation
- [ ] `recover()` from panics
- [x] IO streams
- [x] struct embedding
- [x] interface embedding
- [x] interfaces: allow struct fields (not just methods)
- [ ] vfmt: fix common errors automatically (make vars mutable and vice versa, add missing imports)
- [ ] method expressions with an explicit receiver as the first argument
- [x] short generics syntax (`foo(5)` instead of `foo<int>(5)`)
- [ ] fix all remaining generics issues
- [ ] merge v.c and v_win.c
- [x] more advanced errors, not just `error('message')`
- [ ] VLS for autocomplete, refactoring, go to definition etc
- [ ] Recursive structs via optionals: `struct Node { next ?Node }`
- [ ] Remove `foo = 0` for `&Foo`
- [ ] Handle function pointers safely, remove `if function == 0 {`

177
TESTS.md 100644
View File

@ -0,0 +1,177 @@
# Automated tests
TLDR: do run `v test-all` locally, after making your changes,
and before submitting PRs.
Tip: use `v -cc tcc` when compiling tests, because TCC is much faster,
compared to most other C compilers like clang/gcc/msvc. Most test commands
will use the V compiler and the V tools many times, potentially
hundreds/thousands of times.
## `v test-all`
Test and build *everything*. Usefull to verify *locally*, that the CI will
most likely pass. Slowest, but most comprehensive.
It works, by running these in succession:
* `v test-cleancode`
* `v test-self`
* `v test-fmt`
* `v build-tools`
* `v build-examples`
* `v check-md -hide-warnings .`
* `v install nedpals.args`
# Details:
In the `v` repo there are many tests. The main types are:
## `_test.v` tests - these are the normal V test files.
All `test_` functions in these files, will be ran automatically by
V's test framework.
NB 1: You can run test files one by one, with:
`v file_test.v` - this will run the test_ functions in file_test.v,
and will exit with a 0 exit code, if they all had 0 failing assertions.
`v -stats file_test.v` - this will run the test_ functions, and show a
report about how much time it took to run each of them too.
NB 2: You can also run many test files at once (in parallel, depending on
how many cores you have), with:
`v test folder` - this will run *all* `_test.v` files in `folder`,
recursively.
`v -stats test folder` - same, but will also produce timing reports
about how fast each test_ function in each _test.v file ran.
## `v test vlib/v/tests`:
This folder contains _test.v files, testing the different features of the V
compiler. Each of them will be compiled, and all the features in them have
to work (verified by assertions).
## `v vlib/v/tests/inout/compiler_test.v`
This is a *test runner*, that checks whether the output of running a V program,
matches an expected .out file. You can also check for code that does panic
using this test runner - just paste the start of the `panic()` output in the
corresponding .out file.
NB: these tests, expect to find a pair of `.vv` and `.out` files, in the folder:
vlib/v/tests/inout
The test runner will run each `.vv` file, and will check that its output, matches
the contents of the `.out` file with the same base name. This is particularly useful
for checking that errors and panics are printed.
## `v vlib/v/gen/c/coutput_test.v`
coutput_test.v is a *test runner*, that checks whether the generated C source
code matches *all* expectations, specified in *.c.must_have files, in the
folder vlib/v/gen/c/testdata/ .
Each `.c.must_have` file, *has* to have a corresponding `.vv` file.
Each `.c.must_have` file, consists of multiple lines. Each of these
lines, *should* be present *at least once* in the output, when the .vv
file is compiled with `-o -` .
## `v vlib/v/tests/run_project_folders_test.v`
This *test runner*, checks whether whole project folders, can be compiled, and run.
NB: Each project in these folders, should finish with an exit code of 0,
and it should output `OK` as its last stdout line.
## `v vlib/v/tests/known_errors/known_errors_test.v`
This *test runner*, checks whether a known program, that was expected to compile,
but did NOT, due to a buggy checker, parser or cgen, continues to fail.
The negative programs are collected in the `vlib/v/tests/known_errors/testdata/` folder.
Each of them should FAIL to compile, due to a known/confirmed compiler bug/limitation.
The intended use of this, is for providing samples, that currently do NOT compile,
but that a future compiler improvement WILL be able to compile, and to
track, whether they were not fixed incidentally, due to an unrelated
change/improvement. For example, code that triggers generating invalid C code can go here,
and later when a bug is fixed, can be moved to a proper _test.v or .vv/.out pair, outside of
the `vlib/v/tests/known_errors/testdata/` folder.
## Test building of actual V programs (examples, tools, V itself)
* `v build-tools`
* `v build-examples`
* `v build-vbinaries`
## Formatting tests
In `vlib/v/fmt/` there are:
* `v vlib/v/fmt/fmt_test.v`
This checks `.out` tests.
* `v vlib/v/fmt/fmt_keep_test.v`
This verifies that all `_keep.vv` files in the `vlib/v/fmt/tests/` folder,
would be unchanged by `v fmt -w`, i.e. that the v source code formatter,
generates a stable source output, that does not change, once it is already
formatted once.
* `v vlib/v/fmt/fmt_vlib_test.v`
This checks that all V source files are formatted, and prints a summary.
This is not required.
* `v test-cleancode`
Check that most .v files, are invariant of `v fmt` runs.
* `v test-fmt`
This tests that all .v files in the current folder are already formatted.
It is useful for adding to CI jobs, to guarantee, that future contributions
will keep the existing source nice and clean.
## Markdown/documentation checks:
* `v check-md -hide-warnings .`
Ensure that all .md files in the project are formatted properly,
and that the V code block examples in them can be compiled/formatted too.
## `v test-self`
Run `vlib` module tests, *including* the compiler tests.
## `v vlib/v/compiler_errors_test.v`
This runs tests for:
* `vlib/v/scanner/tests/*.vv`
* `vlib/v/checker/tests/*.vv`
* `vlib/v/parser/tests/*.vv`
NB: there are special folders, that compiler_errors_test.v will try to
run/compile with specific options:
vlib/v/checker/tests/globals_run/ - `-enable-globals run`;
results stored in `.run.out` files, matching the .vv ones.
NB 2: in case you need to modify many .out files, run *twice* in a row:
`VAUTOFIX=1 ./v vlib/v/compiler_errors_test.v`
This will fail the first time, but it will record the new output for each
.vv file, and store it into the corresponding .out file. The second run
should be now successfull, and so you can inspect the difference, and
commit the new .out files with minimum manual effort.
NB 3: To run only some of the tests, use:
`VTEST_ONLY=mismatch ./v vlib/v/compiler_errors_test.v`
This will check only the .vv files, whose paths match the given filter.
## `.github/workflows/ci.yml`
This is a Github Actions configuration file, that runs various CI
tests in the main V repository, for example:
* `v vet vlib/v` - run a style checker.
* `v test-self` (run self tests) in various compilation modes.

View File

@ -1,49 +0,0 @@
// Copyright (c) 2019 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 base64
const (
Index = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
62, 63, 62, 62, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0,
0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 63, 0, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
47, 48, 49, 50, 51]
)
fn decode(data string) string {
p := data.cstr()
len := data.len
mut pad := 0
if len > 0 && (len % 4 != 0 || p[len - 1] == `=`) {
pad = 1
}
L := ((len + 3) / 4 - pad) * 4
str_len := L / 4 * 3 + pad
mut str := malloc(str_len + 2)
mut j := 0
for i := 0; i < L; i += 4 {
n := (Index[p[i]] << 18) | (Index[p[i + 1]] << 12) |
(Index[p[i + 2]] << 6) | (Index[p[i + 3]])
str[j] = n >> 16
j++
str[j] = n >> 8 & 0xff
j++
str[j] = n & 0xff
j++
}
if pad > 0 {
mut nn := (Index[p[L]] << 18) | (Index[p[L + 1]] << 12)
str[str_len - 1] = nn >> 16
if len > L + 2 && p[L + 2] != `=` {
nn = nn | (Index[p[L + 2]] << 6)
str[str_len] = nn >> 8 & 0xff
}
}
str[str_len + 1] = `\0`
return string(str)
}

View File

@ -1,12 +0,0 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
import base64
fn test_decode() {
assert base64.decode('TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=')
== 'Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.'
}

View File

@ -0,0 +1,56 @@
module main
import hash.fnv1a
import hash as wyhash
import rand
import benchmark
fn main() {
rand.seed([u32(42), 0])
sample_size := 10000000
min_str_len := 20
max_str_len := 40
println('Generating $sample_size strings between $min_str_len - $max_str_len chars long...')
mut checksum := u64(0)
mut start_pos := 0
mut bgenerating := benchmark.start()
mut bytepile := []u8{}
for _ in 0 .. sample_size * max_str_len {
bytepile << u8(rand.int_in_range(40, 125) or { 40 })
}
mut str_lens := []int{}
for _ in 0 .. sample_size {
str_lens << rand.int_in_range(min_str_len, max_str_len) or { min_str_len }
}
bgenerating.measure('generating strings')
println('Hashing each of the generated strings...')
//
mut bhashing_1 := benchmark.start()
start_pos = 0
checksum = 0
for len in str_lens {
end_pos := start_pos + len
checksum ^= wyhash.wyhash_c(unsafe { &u8(bytepile.data) + start_pos }, u64(len),
1)
start_pos = end_pos
}
bhashing_1.measure('wyhash.wyhash_c | checksum: ${checksum:22}')
mut bhashing_2 := benchmark.start()
start_pos = 0
checksum = 0
for len in str_lens {
end_pos := start_pos + len
checksum ^= wyhash.sum64(bytepile[start_pos..end_pos], 1)
start_pos = end_pos
}
bhashing_2.measure('wyhash.sum64 | checksum: ${checksum:22}')
mut bhashing_3 := benchmark.start()
start_pos = 0
checksum = 0
for len in str_lens {
end_pos := start_pos + len
checksum ^= fnv1a.sum64(bytepile[start_pos..end_pos])
start_pos = end_pos
}
bhashing_3.measure('fnv1a.sum64 | checksum: ${checksum:22}')
}

View File

@ -0,0 +1,11 @@
module main
import v.builder.cbuilder
// TODO: change bootstrapping to use the C code generated from
// `VEXE=v cmd/tools/builders/c_builder -os cross -o c.c cmd/tools/builders/c_builder.v`
// See also `cmd/v/v.v`
fn main() {
cbuilder.start()
}

View File

@ -0,0 +1,7 @@
module main
import v.builder.interpreterbuilder
fn main() {
interpreterbuilder.start()
}

View File

@ -0,0 +1,7 @@
module main
import v.builder.jsbuilder
fn main() {
jsbuilder.start()
}

View File

@ -0,0 +1,7 @@
module main
import v.builder.nativebuilder
fn main() {
nativebuilder.start()
}

View File

@ -0,0 +1,131 @@
module main
import os
import v.util
import v.util.diff
import v.pref
import v.builder
import v.builder.cbuilder
import v.ast
import rand
import term
const (
base_os = 'linux'
os_names = ['linux', 'macos', 'windows']
skip_modules = [
'builtin.bare',
'builtin.linux_bare.old',
'builtin.js',
'strconv',
'strconv.ftoa',
'hash',
'strings',
'crypto.rand',
'os.bare',
'os2',
'picohttpparser',
'picoev',
'szip',
'v.eval',
]
)
struct App {
diff_cmd string
is_verbose bool
modules []string
mut:
api_differences map[string]int
}
fn main() {
vexe := pref.vexe_path()
vroot := os.dir(vexe)
util.set_vroot_folder(vroot)
os.chdir(vroot)?
cmd := diff.find_working_diff_command() or { '' }
mut app := App{
diff_cmd: cmd
is_verbose: os.getenv('VERBOSE').len > 0
modules: if os.args.len > 1 { os.args[1..] } else { all_vlib_modules() }
}
for mname in app.modules {
if !app.is_verbose {
eprintln('Checking module: $mname ...')
}
api_base := app.gen_api_for_module_in_os(mname, base_os)
for oname in os_names {
if oname == base_os {
continue
}
api_os := app.gen_api_for_module_in_os(mname, oname)
app.compare_api(api_base, api_os, mname, base_os, oname)
}
}
howmany := app.api_differences.len
if howmany > 0 {
eprintln(term.header('Found $howmany modules with different APIs', '='))
for m in app.api_differences.keys() {
eprintln('Module: $m')
}
exit(1)
}
}
fn all_vlib_modules() []string {
mut vlib_v_files := os.walk_ext('vlib', '.v')
mut vmodulesmap := map[string]int{}
for f in vlib_v_files {
if f.contains('/tests/') || f.ends_with('_test.v') {
continue
}
vmodulename := os.dir(f).replace('/', '.').replace('vlib.', '')
if vmodulename in skip_modules {
continue
}
vmodulesmap[vmodulename] = vmodulesmap[vmodulename] + 1
}
mut modules := vmodulesmap.keys()
modules.sort()
return modules
}
fn (app App) gen_api_for_module_in_os(mod_name string, os_name string) string {
if app.is_verbose {
eprintln('Checking module: ${mod_name:-30} for OS: ${os_name:-10} ...')
}
mpath := os.join_path('vlib', mod_name.replace('.', '/'))
tmpname := '/tmp/${mod_name}_${os_name}.c'
prefs, _ := pref.parse_args([], ['-os', os_name, '-o', tmpname, '-shared', mpath])
mut b := builder.new_builder(prefs)
cbuilder.compile_c(mut b)
mut res := []string{}
for f in b.parsed_files {
for s in f.stmts {
if s is ast.FnDecl {
if s.is_pub {
fn_signature := s.stringify(b.table, mod_name, map[string]string{})
fn_mod := s.modname()
if fn_mod == mod_name {
fline := '$fn_mod: $fn_signature'
res << fline
}
}
}
}
}
res.sort()
return res.join('\n')
}
fn (mut app App) compare_api(api_base string, api_os string, mod_name string, os_base string, os_target string) {
res := diff.color_compare_strings(app.diff_cmd, rand.ulid(), api_base, api_os)
if res.len > 0 {
summary := 'Different APIs found for module: `$mod_name`, between OS base: `$os_base` and OS: `$os_target`'
eprintln(term.header(summary, '-'))
eprintln(res)
eprintln(term.h_divider('-'))
app.api_differences[mod_name] = 1
}
}

View File

@ -0,0 +1,17 @@
fn main() {
$if tinyc {
println('Your `tcc` is working. Good - it is much faster at compiling C source code.')
exit(0)
}
$if !macos {
println('
Note: `tcc` was not used, so unless you install it yourself, your backend
C compiler will be `cc`, which is usually either `clang`, `gcc` or `msvc`.
These C compilers, are several times slower at compiling C source code,
compared to `tcc`. They do produce more optimised executables, but that
is done at the cost of compilation speed.
')
}
}

5
cmd/tools/fast/.gitignore vendored 100644
View File

@ -0,0 +1,5 @@
fast
index.html
table.html
v.c
v2

View File

@ -0,0 +1,200 @@
// Copyright (c) 2019-2022 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
import os
import time
const voptions = ' -usecache -skip-unused -show-timings -stats '
const exe = os.executable()
const fast_dir = os.dir(exe)
const vdir = @VEXEROOT
fn main() {
dump(fast_dir)
dump(vdir)
os.chdir(fast_dir)?
if !os.exists('$vdir/v') && !os.is_dir('$vdir/vlib') {
println('fast.html generator needs to be located in `v/cmd/tools/fast`')
}
println('fast.html generator\n')
if !os.args.contains('-noupdate') {
println('Fetching updates...')
ret := os.system('$vdir/v up')
if ret != 0 {
println('failed to update V')
return
}
}
// fetch the last commit's hash
commit := exec('git rev-parse HEAD')[..8]
if !os.exists('table.html') {
os.create('table.html')?
}
mut table := os.read_file('table.html')?
if os.exists('website/index.html') {
uploaded_index := os.read_file('website/index.html')?
if uploaded_index.contains('>$commit<') {
println('nothing to benchmark')
exit(1)
return
}
}
message := exec('git log --pretty=format:"%s" -n1 $commit')
println('\nBenchmarking commit $commit "$message"')
// build an optimized V
println(' Building vprod...')
os.chdir(vdir)?
if os.args.contains('-noprod') {
exec('./v -o vprod cmd/v') // for faster debugging
} else {
exec('./v -o vprod -prod -prealloc cmd/v')
}
// cache vlib modules
exec('$vdir/v wipe-cache')
exec('$vdir/v -o v2 -prod cmd/v')
// measure
diff1 := measure('$vdir/vprod $voptions -o v.c cmd/v', 'v.c')
mut tcc_path := 'tcc'
$if freebsd {
tcc_path = '/usr/local/bin/tcc'
if vdir.contains('/tmp/cirrus-ci-build') {
tcc_path = 'clang'
}
}
if os.args.contains('-clang') {
tcc_path = 'clang'
}
diff2 := measure('$vdir/vprod $voptions -cc $tcc_path -o v2 cmd/v', 'v2')
diff3 := 0 // measure('$vdir/vprod -native $vdir/cmd/tools/1mil.v', 'native 1mil')
diff4 := measure('$vdir/vprod -usecache $voptions -cc clang examples/hello_world.v',
'hello.v')
vc_size := os.file_size('v.c') / 1000
scan, parse, check, cgen, vlines := measure_steps(vdir)
commit_date := exec('git log -n1 --pretty="format:%at" $commit')
date := time.unix(commit_date.int())
os.chdir(fast_dir)?
mut out := os.create('table.html')?
// place the new row on top
html_message := message.replace_each(['<', '&lt;', '>', '&gt;'])
table =
' <tr>
<td>$date.format()</td>
<td><a target=_blank href="https://github.com/vlang/v/commit/$commit">$commit</a></td>
<td>$html_message</td>
<td>${diff1}ms</td>
<td>${diff2}ms</td>
<td>${diff3}ms</td>
<td>${diff4}ms</td>
<td>$vc_size KB</td>
<td>${parse}ms</td>
<td>${check}ms</td>
<td>${cgen}ms</td>
<td>${scan}ms</td>
<td>$vlines</td>
<td>${int(f64(vlines) / f64(diff1) * 1000.0)}</td>
</tr>\n' +
table.trim_space()
out.writeln(table)?
out.close()
// regenerate index.html
header := os.read_file('header.html')?
footer := os.read_file('footer.html')?
mut res := os.create('index.html')?
res.writeln(header)?
res.writeln(table)?
res.writeln(footer)?
res.close()
// upload the result to github pages
if os.args.contains('-upload') {
println('uploading...')
os.chdir('website')?
os.execute_or_exit('git checkout gh-pages')
os.cp('../index.html', 'index.html')?
os.rm('../index.html')?
os.system('git commit -am "update benchmark"')
os.system('git push origin gh-pages')
}
}
fn exec(s string) string {
e := os.execute_or_exit(s)
return e.output.trim_right('\r\n')
}
// measure returns milliseconds
fn measure(cmd string, description string) int {
println(' Measuring $description')
println(' Warming up...')
println(cmd)
for _ in 0 .. 3 {
exec(cmd)
}
println(' Building...')
mut runs := []int{}
for r in 0 .. 5 {
println(' Sample ${r + 1}/5')
sw := time.new_stopwatch()
exec(cmd)
runs << int(sw.elapsed().milliseconds())
}
// discard lowest and highest time
runs.sort()
runs = runs[1..4]
mut sum := 0
for run in runs {
sum += run
}
return int(sum / 3)
}
fn measure_steps(vdir string) (int, int, int, int, int) {
resp := os.execute_or_exit('$vdir/vprod $voptions -o v.c cmd/v')
mut scan, mut parse, mut check, mut cgen, mut vlines := 0, 0, 0, 0, 0
lines := resp.output.split_into_lines()
if lines.len == 3 {
parse = lines[0].before('.').int()
check = lines[1].before('.').int()
cgen = lines[2].before('.').int()
} else {
ms_lines := lines.map(it.split(' ms '))
for line in ms_lines {
if line.len == 2 {
if line[1] == 'SCAN' {
scan = line[0].int()
}
if line[1] == 'PARSE' {
parse = line[0].int()
}
if line[1] == 'CHECK' {
check = line[0].int()
}
if line[1] == 'C GEN' {
cgen = line[0].int()
}
} else {
// fetch number of V lines
if line[0].contains('V') && line[0].contains('source') && line[0].contains('size') {
start := line[0].index(':') or { 0 }
end := line[0].index('lines,') or { 0 }
s := line[0][start + 1..end]
vlines = s.trim_space().int()
}
}
}
}
return scan, parse, check, cgen, vlines
}

View File

@ -0,0 +1,37 @@
// Copyright (c) 2019-2022 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
import os
import time
// A job that runs in the background, checks for repo updates,
// runs fast.v, pushes the HTML result to the fast.vlang.io GH pages repo.
fn main() {
println(time.now())
if !os.exists('website') {
println('cloning the website repo...')
os.system('git clone git@github.com:/vlang/website.git')
}
if !os.exists('fast') {
println('"fast" binary (built with `v fast.v`) was not found')
return
}
for {
res_pull := os.execute('git pull --rebase')
if res_pull.exit_code != 0 {
println('failed to git pull. uncommitted changes?')
return
}
// println('running ./fast')
resp := os.execute('./fast -upload')
if resp.exit_code < 0 {
println(resp.output)
return
}
if resp.exit_code != 0 {
println('resp != 0, skipping')
println(resp.output)
}
time.sleep(180 * time.second)
}
}

View File

@ -0,0 +1,67 @@
const delta = 18;
(function () {
var table = document.querySelector("table");
var isTbody = table.children[0].nodeName == "TBODY";
var trs = isTbody
? table.children[0].querySelectorAll("tr")
: table.querySelectorAll("tr");
trs.forEach(function (tr, idx) {
if (idx != 0 && idx + 1 < trs.length) {
var vc = 3, vv = 4, vf = 5, vh = 6;
var textContent = {
vc: tr.children[vc].textContent,
vv: tr.children[vv].textContent,
vf: tr.children[vf].textContent,
vh: tr.children[vh].textContent
};
var currentData = {
vc: int(textContent.vc.slice(0, -2)),
vv: int(textContent.vv.slice(0, -2)),
vf: int(textContent.vf.slice(0, -2)),
vh: int(textContent.vh.slice(0, -2))
};
var prevData = {
vc: int(trs[idx + 1].children[vc].textContent.slice(0, -2)),
vv: int(trs[idx + 1].children[vv].textContent.slice(0, -2)),
vf: int(trs[idx + 1].children[vf].textContent.slice(0, -2)),
vh: int(trs[idx + 1].children[vh].textContent.slice(0, -2))
};
var result = {
vc: currentData.vc - prevData.vc,
vv: currentData.vv - prevData.vv,
vf: currentData.vf - prevData.vf,
vh: currentData.vh - prevData.vh
};
if (Math.abs(result.vc) > delta)
tr.children[vc].appendChild(createElement(result.vc));
if (Math.abs(result.vv) > delta * 2)
tr.children[vv].appendChild(createElement(result.vv));
if (Math.abs(result.vf) > delta * 2)
tr.children[vf].appendChild(createElement(result.vf));
if (Math.abs(result.vh) > delta * 2)
tr.children[vh].appendChild(createElement(result.vh));
}
});
function int(src) {
return src - 0;
}
function getClassName(x) {
if (x == 0)
return "equal";
return x < 0 ? "plus" : "minus";
}
function createElement(result) {
var el = document.createElement("span");
var parsedResult = parseResult(result);
el.classList.add("diff");
el.classList.add(getClassName(result));
el.textContent = parsedResult;
return el;
}
function parseResult(x) {
if (x == 0)
return "0";
return x > 0 ? "+" + x : x;
}
})();

View File

@ -0,0 +1,4 @@
</table>
<script src="main.js"></script>
</body>
</html>

View File

@ -0,0 +1,65 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Is V still fast?</title>
<style>
*, body {
font-family: Menlo, Monospace, 'Courier New';
}
table {
width: 1800px;
}
table, td {
border-collapse: collapse;
border: 1px solid #dfdfdf;
}
td {
padding: 5px;
position: relative;
}
.diff {
border-radius: 2.5px;
color: #ffffff;
padding: 0 5px 0 5px;
position: absolute;
right: 5px;
}
.minus {
background-color: rgb(195, 74, 104);
}
.plus {
background-color: #8BC34A;
}
.equal {
background-color: rgb(113, 68, 172);
}
</style>
</head>
<body>
<h2>Is V still fast?</h2>
Monitoring compilation speed for each commit. <br><br>
Running on a free tier AWS t2.micro instance (1 vCPU). Typical desktop hardware is 2-3 times faster. <br><br>
Source code: <a target=blank href='https://github.com/vlang/v/blob/master/cmd/tools/fast/fast.v'>fast.v</a> <br><br>
<table>
<tr>
<td style='width:180px'>timestamp</td>
<td style='width:85px'>commit</td>
<td>commit message</td>
<td style='width:120px'>v -o v.c</td>
<td style='width:120px'>v -o v</td>
<td style='width:130px'>v -native 1mil.v</td>
<td style='width:120px'>v hello.v</td>
<td style='width:85px'>v.c size</td>
<td style='width:55px'>parse</td>
<td style='width:55px'>check</td>
<td style='width:55px'>cgen</td>
<td style='width:55px'>scan</td>
<td style='width:80px'>V lines</td>
<td style='width:95px'>V lines/s</td>
</tr>

View File

@ -0,0 +1,18 @@
#!/bin/sh
cores=$(nproc --all)
echo Number of cores: $cores
echo Compiling...
./v -cc clang -o cmd/tools/fuzz/map_fuzz cmd/tools/fuzz/map_fuzz.v -prod -cflags "-fsanitize=memory"
echo Fuzzing:
while true
do
for ((i=1;i<=cores;++i))
do
sleep 0.001
./cmd/tools/fuzz/map_fuzz &
done
wait
done

View File

@ -0,0 +1,144 @@
import rand
import time
fn generate_strings(str_len int, arr_len int) []string {
mut arr := []string{len: arr_len}
for i in 0 .. arr_len {
arr[i] = rand.string(str_len)
}
return arr
}
fn fuzz1() {
amount := 200000 - rand.intn(100000) or { 0 }
amount2 := 200000 - rand.intn(100000) or { 0 }
len := 25 - rand.intn(10) or { 0 }
arr := generate_strings(len, amount)
arr2 := generate_strings(len, amount2)
mut m := map[string]int{}
for i in 0 .. amount {
m[arr[i]] = i
assert i == m[arr[i]]
}
for i in 0 .. amount {
assert i == m[arr[i]]
}
for i in 0 .. amount2 {
assert 0 == m[arr2[i]]
}
unsafe {
m.free()
arr.free()
}
}
fn fuzz2() {
mut m := map[string]int{}
amount := rand.intn(500000) or { 0 } + 1
len := 25 - rand.intn(10) or { 0 }
arr := generate_strings(len, amount)
for i, x in arr {
m[x] = i
}
mut i := 0
for key, val in m {
assert key == arr[i]
assert val == i
i++
}
unsafe {
m.free()
arr.free()
}
}
fn fuzz3() {
mut m := map[string]int{}
amount := rand.intn(500000) or { 0 } + 1
len := 25 - rand.intn(10) or { 0 }
arr := generate_strings(len, amount)
for i, x in arr {
if (i % 10000) == 0 {
keys := m.keys()
assert keys.len == i
assert keys == arr[0..i]
}
m[x] = i
}
assert m.keys() == arr
assert m.keys().len == amount
unsafe {
m.free()
arr.free()
}
}
fn fuzz4() {
amount := rand.intn(500000) or { 0 }
len := 25 - rand.intn(10) or { 0 }
arr := generate_strings(len, amount)
mut m := map[string]int{}
for i in 0 .. amount {
m[arr[i]] = i
}
for i in 0 .. amount {
m.delete(arr[i])
assert m[arr[i]] == 0
}
assert m.len == 0
unsafe {
m.free()
arr.free()
}
}
fn fuzz5() {
amount := rand.intn(500000) or { 0 } + 1
arr := generate_strings(20, amount)
mut m := map[string]int{}
for i in 0 .. amount {
m[arr[i]] = i
assert (arr[i] in m) == true
}
for i in 0 .. amount {
m.delete(arr[i])
assert (arr[i] !in m) == true
assert m.len == amount - i - 1
}
assert m.len == 0
unsafe {
m.free()
arr.free()
}
}
fn fuzz6() {
mut m := map[string]int{}
amount := rand.intn(500000) or { 0 } + 1
len := 25 - rand.intn(10) or { 0 }
arr := generate_strings(len, amount)
for i, x in arr {
m[x]++
m[x] += i
assert m[x] == i + 1
}
for i, x in arr {
assert m[x] == i + 1
}
unsafe {
m.free()
arr.free()
}
}
fn main() {
seed := u32(time.ticks())
println('seed: $seed.hex()')
rand.seed([seed, seed])
fuzz1()
fuzz2()
fuzz3()
fuzz4()
fuzz5()
fuzz6()
}

16
cmd/tools/gen1m.v 100644
View File

@ -0,0 +1,16 @@
fn main() {
for i in 0 .. 100000 {
println('
fn foo${i}() {
x := $i
mut a := 1 + x
a += 2
print(a)
a = 0
a = 1
}
')
}
// println('fn main() {foo1()} ')
println('fn main() { println("1m DONE") } ')
}

370
cmd/tools/gen_vc.v 100644
View File

@ -0,0 +1,370 @@
module main
import os
import log
import flag
import time
import vweb
import net.urllib
// This tool regenerates V's bootstrap .c files
// every time the V master branch is updated.
// if run with the --serve flag it will run in webhook
// server mode awaiting a request to http://host:port/genhook
// available command line flags:
// --work-dir gen_vc's working directory
// --purge force purge the local repositories
// --serve run in webhook server mode
// --port port for http server to listen on
// --log-to either 'file' or 'terminal'
// --log-file path to log file used when --log-to is 'file'
// --dry-run dont push anything to remote repo
// --force force update even if already up to date
// git credentials
const (
git_username = os.getenv('GITUSER')
git_password = os.getenv('GITPASS')
)
// repository
const (
// git repo
git_repo_v = 'github.com/vlang/v'
git_repo_vc = 'github.com/vlang/vc'
// local repo directories
git_repo_dir_v = 'v'
git_repo_dir_vc = 'vc'
)
// gen_vc
const (
// name
app_name = 'gen_vc'
// version
app_version = '0.1.2'
// description
app_description = "This tool regenerates V's bootstrap .c files every time the V master branch is updated."
// assume something went wrong if file size less than this
too_short_file_limit = 5000
// create a .c file for these os's
vc_build_oses = [
'nix',
// all nix based os
'windows',
]
)
// default options (overridden by flags)
const (
// gen_vc working directory
work_dir = '/tmp/gen_vc'
// dont push anything to remote repo
dry_run = false
// server port
server_port = 7171
// log file
log_file = '$work_dir/log.txt'
// log_to is either 'file' or 'terminal'
log_to = 'terminal'
)
// errors
const (
err_msg_build = 'error building'
err_msg_make = 'make failed'
err_msg_gen_c = 'failed to generate .c file'
err_msg_cmd_x = 'error running cmd'
)
struct GenVC {
// logger
// flag options
options FlagOptions
mut:
logger &log.Log
// true if error was experienced running generate
gen_error bool
}
// webhook server
struct WebhookServer {
vweb.Context
mut:
gen_vc &GenVC = 0 // initialized in init_server
}
// storage for flag options
struct FlagOptions {
work_dir string
purge bool
serve bool
port int
log_to string
log_file string
dry_run bool
force bool
}
fn main() {
mut fp := flag.new_flag_parser(os.args.clone())
fp.application(app_name)
fp.version(app_version)
fp.description(app_description)
fp.skip_executable()
show_help := fp.bool('help', 0, false, 'Show this help screen\n')
flag_options := parse_flags(mut fp)
if show_help {
println(fp.usage())
exit(0)
}
fp.finalize() or {
eprintln(err)
println(fp.usage())
return
}
// webhook server mode
if flag_options.serve {
vweb.run<WebhookServer>(&WebhookServer{}, flag_options.port)
} else {
// cmd mode
mut gen_vc := new_gen_vc(flag_options)
gen_vc.init()
gen_vc.generate()
}
}
// new GenVC
fn new_gen_vc(flag_options FlagOptions) &GenVC {
mut logger := &log.Log{}
logger.set_level(.debug)
if flag_options.log_to == 'file' {
logger.set_full_logpath(flag_options.log_file)
}
return &GenVC{
options: flag_options
logger: logger
}
}
// WebhookServer init
pub fn (mut ws WebhookServer) init_server() {
mut fp := flag.new_flag_parser(os.args.clone())
flag_options := parse_flags(mut fp)
ws.gen_vc = new_gen_vc(flag_options)
ws.gen_vc.init()
// ws.gen_vc = new_gen_vc(flag_options)
}
pub fn (mut ws WebhookServer) index() {
eprintln('WebhookServer.index() called')
}
// gen webhook
pub fn (mut ws WebhookServer) genhook() {
// request data
// println(ws.vweb.req.data)
// TODO: parse request. json or urlencoded
// json.decode or net.urllib.parse
ws.gen_vc.generate()
// error in generate
if ws.gen_vc.gen_error {
ws.json('{status: "failed"}')
return
}
ws.json('{status: "ok"}')
}
pub fn (ws &WebhookServer) reset() {
}
// parse flags to FlagOptions struct
fn parse_flags(mut fp flag.FlagParser) FlagOptions {
return FlagOptions{
serve: fp.bool('serve', 0, false, 'run in webhook server mode')
work_dir: fp.string('work-dir', 0, work_dir, 'gen_vc working directory')
purge: fp.bool('purge', 0, false, 'force purge the local repositories')
port: fp.int('port', 0, server_port, 'port for web server to listen on')
log_to: fp.string('log-to', 0, log_to, "log to is 'file' or 'terminal'")
log_file: fp.string('log-file', 0, log_file, "log file to use when log-to is 'file'")
dry_run: fp.bool('dry-run', 0, dry_run, 'when specified dont push anything to remote repo')
force: fp.bool('force', 0, false, 'force update even if already up to date')
}
}
fn (mut gen_vc GenVC) init() {
// purge repos if flag is passed
if gen_vc.options.purge {
gen_vc.purge_repos()
}
}
// regenerate
fn (mut gen_vc GenVC) generate() {
// set errors to false
gen_vc.gen_error = false
// check if gen_vc dir exists
if !os.is_dir(gen_vc.options.work_dir) {
// try create
os.mkdir(gen_vc.options.work_dir) or { panic(err) }
// still dosen't exist... we have a problem
if !os.is_dir(gen_vc.options.work_dir) {
gen_vc.logger.error('error creating directory: $gen_vc.options.work_dir')
gen_vc.gen_error = true
return
}
}
// cd to gen_vc dir
os.chdir(gen_vc.options.work_dir) or {}
// if we are not running with the --serve flag (webhook server)
// rather than deleting and re-downloading the repo each time
// first check to see if the local v repo is behind master
// if it isn't behind theres no point continuing further
if !gen_vc.options.serve && os.is_dir(git_repo_dir_v) {
gen_vc.cmd_exec('git -C $git_repo_dir_v checkout master')
// fetch the remote repo just in case there are newer commits there
gen_vc.cmd_exec('git -C $git_repo_dir_v fetch')
git_status := gen_vc.cmd_exec('git -C $git_repo_dir_v status')
if !git_status.contains('behind') && !gen_vc.options.force {
gen_vc.logger.warn('v repository is already up to date.')
return
}
}
// delete repos
gen_vc.purge_repos()
// clone repos
gen_vc.cmd_exec('git clone --depth 1 https://$git_repo_v $git_repo_dir_v')
gen_vc.cmd_exec('git clone --depth 1 https://$git_repo_vc $git_repo_dir_vc')
// get output of git log -1 (last commit)
git_log_v := gen_vc.cmd_exec('git -C $git_repo_dir_v log -1 --format="commit %H%nDate: %ci%nDate Unix: %ct%nSubject: %s"')
git_log_vc := gen_vc.cmd_exec('git -C $git_repo_dir_vc log -1 --format="Commit %H%nDate: %ci%nDate Unix: %ct%nSubject: %s"')
// date of last commit in each repo
ts_v := git_log_v.find_between('Date:', '\n').trim_space()
ts_vc := git_log_vc.find_between('Date:', '\n').trim_space()
// parse time as string to time.Time
last_commit_time_v := time.parse(ts_v) or { panic(err) }
last_commit_time_vc := time.parse(ts_vc) or { panic(err) }
// git dates are in users local timezone and v time.parse does not parse
// timezones at the moment, so for now get unix timestamp from output also
t_unix_v := git_log_v.find_between('Date Unix:', '\n').trim_space().int()
t_unix_vc := git_log_vc.find_between('Date Unix:', '\n').trim_space().int()
// last commit hash in v repo
last_commit_hash_v := git_log_v.find_between('commit', '\n').trim_space()
last_commit_hash_v_short := last_commit_hash_v[..7]
// subject
last_commit_subject := git_log_v.find_between('Subject:', '\n').trim_space().replace("'",
'"')
// log some info
gen_vc.logger.debug('last commit time ($git_repo_v): ' + last_commit_time_v.format_ss())
gen_vc.logger.debug('last commit time ($git_repo_vc): ' + last_commit_time_vc.format_ss())
gen_vc.logger.debug('last commit hash ($git_repo_v): $last_commit_hash_v')
gen_vc.logger.debug('last commit subject ($git_repo_v): $last_commit_subject')
// if vc repo already has a newer commit than the v repo, assume it's up to date
if t_unix_vc >= t_unix_v && !gen_vc.options.force {
gen_vc.logger.warn('vc repository is already up to date.')
return
}
// try build v for current os (linux in this case)
gen_vc.cmd_exec('make -C $git_repo_dir_v')
v_exec := '$git_repo_dir_v/v'
// check if make was successful
gen_vc.assert_file_exists_and_is_not_too_short(v_exec, err_msg_make)
// build v.c for each os
for os_name in vc_build_oses {
c_file := if os_name == 'nix' { 'v.c' } else { 'v_win.c' }
v_flags := if os_name == 'nix' { '-os cross' } else { '-os $os_name' }
// try generate .c file
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
gen_vc.cmd_exec('sed -i \'1s/^/#define V_COMMIT_HASH "$last_commit_hash_v_short"\\n/\' $c_file')
// move to vc repo
gen_vc.cmd_exec('mv $c_file $git_repo_dir_vc/$c_file')
// add new .c file to local vc repo
gen_vc.cmd_exec('git -C $git_repo_dir_vc add $c_file')
}
// check if the vc repo actually changed
git_status := gen_vc.cmd_exec('git -C $git_repo_dir_vc status')
if git_status.contains('nothing to commit') {
gen_vc.logger.error('no changes to vc repo: something went wrong.')
gen_vc.gen_error = true
}
// commit changes to local vc repo
gen_vc.cmd_exec_safe("git -C $git_repo_dir_vc commit -m '[v:master] $last_commit_hash_v_short - $last_commit_subject'")
// push changes to remote vc repo
gen_vc.cmd_exec_safe('git -C $git_repo_dir_vc push https://${urllib.query_escape(git_username)}:${urllib.query_escape(git_password)}@$git_repo_vc master')
}
// only execute when dry_run option is false, otherwise just log
fn (mut gen_vc GenVC) cmd_exec_safe(cmd string) string {
return gen_vc.command_execute(cmd, gen_vc.options.dry_run)
}
// always execute command
fn (mut gen_vc GenVC) cmd_exec(cmd string) string {
return gen_vc.command_execute(cmd, false)
}
// execute command
fn (mut gen_vc GenVC) command_execute(cmd string, dry bool) string {
// if dry is true then dont execute, just log
if dry {
return gen_vc.command_execute_dry(cmd)
}
gen_vc.logger.info('cmd: $cmd')
r := os.execute(cmd)
if r.exit_code < 0 {
gen_vc.logger.error('$err_msg_cmd_x: "$cmd" could not start.')
gen_vc.logger.error(r.output)
// something went wrong, better start fresh next time
gen_vc.purge_repos()
gen_vc.gen_error = true
return ''
}
if r.exit_code != 0 {
gen_vc.logger.error('$err_msg_cmd_x: "$cmd" failed.')
gen_vc.logger.error(r.output)
// something went wrong, better start fresh next time
gen_vc.purge_repos()
gen_vc.gen_error = true
return ''
}
return r.output
}
// just log cmd, dont execute
fn (mut gen_vc GenVC) command_execute_dry(cmd string) string {
gen_vc.logger.info('cmd (dry): "$cmd"')
return ''
}
// delete repo directories
fn (mut gen_vc GenVC) purge_repos() {
// delete old repos (better to be fully explicit here, since these are destructive operations)
mut repo_dir := '$gen_vc.options.work_dir/$git_repo_dir_v'
if os.is_dir(repo_dir) {
gen_vc.logger.info('purging local repo: "$repo_dir"')
gen_vc.cmd_exec('rm -rf $repo_dir')
}
repo_dir = '$gen_vc.options.work_dir/$git_repo_dir_vc'
if os.is_dir(repo_dir) {
gen_vc.logger.info('purging local repo: "$repo_dir"')
gen_vc.cmd_exec('rm -rf $repo_dir')
}
}
// check if file size is too short
fn (mut gen_vc GenVC) assert_file_exists_and_is_not_too_short(f string, emsg string) {
if !os.exists(f) {
gen_vc.logger.error('$err_msg_build: $emsg .')
gen_vc.gen_error = true
return
}
fsize := os.file_size(f)
if fsize < too_short_file_limit {
gen_vc.logger.error('$err_msg_build: $f exists, but is too short: only $fsize bytes.')
gen_vc.gen_error = true
return
}
}

View File

@ -0,0 +1,67 @@
import os
import time
import v.ast
import v.pref
import v.parser
import v.errors
import v.scanner
fn main() {
files := os.args#[1..]
if files.len > 0 && files[0].starts_with('@') {
lst_path := files[0].all_after('@')
listed_files := os.read_file(lst_path)?.split('\n')
process_files(listed_files)?
return
}
process_files(files)?
}
fn process_files(files []string) ? {
mut table := ast.new_table()
mut pref := pref.new_preferences()
pref.is_fmt = true
pref.skip_warnings = true
pref.output_mode = .silent
mut sw := time.new_stopwatch()
mut total_us := i64(0)
mut total_bytes := i64(0)
mut total_tokens := i64(0)
for f in files {
if f == '' {
continue
}
if f.ends_with('_test.v') {
continue
}
// do not measure the scanning, but only the parsing:
mut p := new_parser(f, .skip_comments, table, pref)
///
sw.restart()
_ := p.parse()
f_us := sw.elapsed().microseconds()
///
total_us += f_us
total_bytes += p.scanner.text.len
total_tokens += p.scanner.all_tokens.len
println('${f_us:10}us ${p.scanner.all_tokens.len:10} ${p.scanner.text.len:10} ${(f64(p.scanner.text.len) / p.scanner.all_tokens.len):7.3} ${p.errors.len:4} $f')
}
println('${total_us:10}us ${total_tokens:10} ${total_bytes:10} ${(f64(total_tokens) / total_bytes):7.3} | speed: ${(f64(total_bytes) / total_us):2.5f} MB/s')
}
fn new_parser(path string, comments_mode scanner.CommentsMode, table &ast.Table, pref &pref.Preferences) &parser.Parser {
mut p := &parser.Parser{
scanner: scanner.new_scanner_file(path, comments_mode, pref) or { panic(err) }
comments_mode: comments_mode
table: table
pref: pref
scope: &ast.Scope{
start_pos: 0
parent: table.global_scope
}
errors: []errors.Error{}
warnings: []errors.Warning{}
}
p.set_path(path)
return p
}

View File

@ -0,0 +1,42 @@
import os
import time
import v.scanner
import v.pref
fn main() {
files := os.args#[1..]
if files.len > 0 && files[0].starts_with('@') {
lst_path := files[0].all_after('@')
listed_files := os.read_file(lst_path)?.split('\n')
process_files(listed_files)?
return
}
process_files(files)?
}
fn process_files(files []string) ? {
mut pref := pref.new_preferences()
pref.is_fmt = true
pref.skip_warnings = true
pref.output_mode = .silent
mut sw := time.new_stopwatch()
mut total_us := i64(0)
mut total_bytes := i64(0)
mut total_tokens := i64(0)
for f in files {
if f == '' {
continue
}
if f.ends_with('_test.v') {
continue
}
sw.restart()
s := scanner.new_scanner_file(f, .skip_comments, pref)?
f_us := sw.elapsed().microseconds()
total_us += f_us
total_bytes += s.text.len
total_tokens += s.all_tokens.len
println('${f_us:10}us ${s.all_tokens.len:10} ${s.text.len:10} ${(f64(s.text.len) / s.all_tokens.len):7.3f} $f')
}
println('${total_us:10}us ${total_tokens:10} ${total_bytes:10} ${(f64(total_tokens) / total_bytes):7.3f} | speed: ${(f64(total_bytes) / total_us):2.5f} MB/s')
}

View File

@ -0,0 +1,187 @@
module scripting
import os
import term
import time
const (
term_colors = term.can_show_color_on_stdout()
)
pub fn set_verbose(on bool) {
// setting a global here would be the obvious solution,
// but V does not have globals normally.
if on {
os.setenv('VERBOSE', '1', true)
} else {
os.unsetenv('VERBOSE')
}
}
pub fn cprint(omessage string) {
mut message := omessage
if scripting.term_colors {
message = term.cyan(message)
}
print(message)
flush_stdout()
}
pub fn cprint_strong(omessage string) {
mut message := omessage
if scripting.term_colors {
message = term.bright_green(message)
}
print(message)
flush_stdout()
}
pub fn cprintln(omessage string) {
cprint(omessage)
println('')
flush_stdout()
}
pub fn cprintln_strong(omessage string) {
cprint_strong(omessage)
println('')
flush_stdout()
}
pub fn verbose_trace(label string, message string) {
if os.getenv('VERBOSE').len > 0 {
slabel := '$time.now().format_ss_milli() $label'
cprintln('# ${slabel:-43s} : $message')
}
}
pub fn verbose_trace_strong(label string, omessage string) {
if os.getenv('VERBOSE').len > 0 {
slabel := '$time.now().format_ss_milli() $label'
mut message := omessage
if scripting.term_colors {
message = term.bright_green(message)
}
cprintln('# ${slabel:-43s} : $message')
}
}
pub fn verbose_trace_exec_result(x os.Result) {
if os.getenv('VERBOSE').len > 0 {
cprintln('# cmd.exit_code : ${x.exit_code.str():-4s} cmd.output:')
mut lnum := 1
lines := x.output.split_into_lines()
for oline in lines {
mut line := oline
if scripting.term_colors {
line = term.bright_green(line)
}
cprintln('# ${lnum:3d}: $line')
lnum++
}
cprintln('# ----------------------------------------------------------------------')
}
}
fn modfn(mname string, fname string) string {
return '${mname}.$fname'
}
pub fn chdir(path string) {
verbose_trace_strong(modfn(@MOD, @FN), 'cd $path')
os.chdir(path) or {
verbose_trace(modfn(@MOD, @FN), '## failed.')
return
}
}
pub fn mkdir(path string) ? {
verbose_trace_strong(modfn(@MOD, @FN), 'mkdir $path')
os.mkdir(path) or {
verbose_trace(modfn(@MOD, @FN), '## failed.')
return err
}
}
pub fn mkdir_all(path string) ? {
verbose_trace_strong(modfn(@MOD, @FN), 'mkdir -p $path')
os.mkdir_all(path) or {
verbose_trace(modfn(@MOD, @FN), '## failed.')
return err
}
}
pub fn rmrf(path string) {
verbose_trace_strong(modfn(@MOD, @FN), 'rm -rf $path')
if os.exists(path) {
if os.is_dir(path) {
os.rmdir_all(path) or { panic(err) }
} else {
os.rm(path) or { panic(err) }
}
}
}
// execute a command, and return a result, or an error, if it failed in any way.
pub fn exec(cmd string) ?os.Result {
verbose_trace_strong(modfn(@MOD, @FN), cmd)
x := os.execute(cmd)
if x.exit_code != 0 {
verbose_trace(modfn(@MOD, @FN), '## failed.')
return error(x.output)
}
verbose_trace_exec_result(x)
return x
}
// run a command, tracing its results, and returning ONLY its output
pub fn run(cmd string) string {
verbose_trace_strong(modfn(@MOD, @FN), cmd)
x := os.execute(cmd)
if x.exit_code < 0 {
verbose_trace(modfn(@MOD, @FN), '## failed.')
return ''
}
verbose_trace_exec_result(x)
if x.exit_code == 0 {
return x.output.trim_right('\r\n')
}
return ''
}
pub fn exit_0_status(cmd string) bool {
verbose_trace_strong(modfn(@MOD, @FN), cmd)
x := os.execute(cmd)
if x.exit_code < 0 {
verbose_trace(modfn(@MOD, @FN), '## failed.')
return false
}
verbose_trace_exec_result(x)
if x.exit_code == 0 {
return true
}
return false
}
pub fn tool_must_exist(toolcmd string) {
verbose_trace(modfn(@MOD, @FN), toolcmd)
if exit_0_status('type $toolcmd') {
return
}
eprintln('Missing tool: $toolcmd')
eprintln('Please try again after you install it.')
exit(1)
}
pub fn used_tools_must_exist(tools []string) {
for t in tools {
tool_must_exist(t)
}
}
pub fn show_sizes_of_files(files []string) {
for f in files {
size := os.file_size(f)
println('$size $f') // println('${size:10d} $f')
}
}

View File

@ -0,0 +1,602 @@
module testing
import os
import time
import term
import benchmark
import sync.pool
import v.pref
import v.util.vtest
import runtime
pub const github_job = os.getenv('GITHUB_JOB')
pub const show_start = os.getenv('VTEST_SHOW_START') == '1'
pub const hide_skips = os.getenv('VTEST_HIDE_SKIP') == '1'
pub const hide_oks = os.getenv('VTEST_HIDE_OK') == '1'
pub const fail_fast = os.getenv('VTEST_FAIL_FAST') == '1'
pub const fail_flaky = os.getenv('VTEST_FAIL_FLAKY') == '1'
pub const test_only = os.getenv('VTEST_ONLY').split_any(',')
pub const test_only_fn = os.getenv('VTEST_ONLY_FN').split_any(',')
pub const is_node_present = os.execute('node --version').exit_code == 0
pub const all_processes = os.execute('ps ax').output.split_any('\r\n')
pub struct TestSession {
pub mut:
files []string
skip_files []string
vexe string
vroot string
vtmp_dir string
vargs string
fail_fast bool
benchmark benchmark.Benchmark
rm_binaries bool = true
silent_mode bool
progress_mode bool
root_relative bool // used by CI runs, so that the output is stable everywhere
nmessages chan LogMessage // many publishers, single consumer/printer
nmessage_idx int // currently printed message index
nprint_ended chan int // read to block till printing ends, 1:1
failed_cmds shared []string
}
enum MessageKind {
ok
fail
skip
info
sentinel
}
struct LogMessage {
message string
kind MessageKind
}
pub fn (mut ts TestSession) add_failed_cmd(cmd string) {
lock ts.failed_cmds {
ts.failed_cmds << cmd
}
}
pub fn (mut ts TestSession) show_list_of_failed_tests() {
for i, cmd in ts.failed_cmds {
eprintln(term.failed('Failed command ${i + 1}:') + ' $cmd')
}
}
pub fn (mut ts TestSession) append_message(kind MessageKind, msg string) {
ts.nmessages <- LogMessage{
message: msg
kind: kind
}
}
pub fn (mut ts TestSession) print_messages() {
empty := term.header(' ', ' ')
mut print_msg_time := time.new_stopwatch()
for {
// get a message from the channel of messages to be printed:
mut rmessage := <-ts.nmessages
if rmessage.kind == .sentinel {
// a sentinel for stopping the printing thread
if !ts.silent_mode && ts.progress_mode {
eprintln('')
}
ts.nprint_ended <- 0
return
}
if rmessage.kind != .info {
ts.nmessage_idx++
}
msg := rmessage.message.replace_each([
'TMP1',
'${ts.nmessage_idx:1d}',
'TMP2',
'${ts.nmessage_idx:2d}',
'TMP3',
'${ts.nmessage_idx:3d}',
'TMP4',
'${ts.nmessage_idx:4d}',
])
is_ok := rmessage.kind == .ok
//
time_passed := print_msg_time.elapsed().seconds()
if time_passed > 10 && ts.silent_mode && is_ok {
// Even if OK tests are suppressed,
// show *at least* 1 result every 10 seconds,
// otherwise the CI can seem stuck ...
eprintln(msg)
print_msg_time.restart()
continue
}
if ts.progress_mode {
// progress mode, the last line is rewritten many times:
if is_ok && !ts.silent_mode {
print('\r$empty\r$msg')
flush_stdout()
} else {
// the last \n is needed, so SKIP/FAIL messages
// will not get overwritten by the OK ones
eprint('\r$empty\r$msg\n')
}
continue
}
if !ts.silent_mode || !is_ok {
// normal expanded mode, or failures in -silent mode
eprintln(msg)
continue
}
}
}
pub fn new_test_session(_vargs string, will_compile bool) TestSession {
mut skip_files := []string{}
if will_compile {
$if msvc {
skip_files << 'vlib/v/tests/const_comptime_eval_before_vinit_test.v' // _constructor used
}
$if solaris {
skip_files << 'examples/gg/gg2.v'
skip_files << 'examples/pico/pico.v'
skip_files << 'examples/sokol/fonts.v'
skip_files << 'examples/sokol/drawing.v'
}
$if macos {
skip_files << 'examples/database/mysql.v'
skip_files << 'examples/database/orm.v'
skip_files << 'examples/database/psql/customer.v'
}
$if windows {
skip_files << 'examples/database/mysql.v'
skip_files << 'examples/database/orm.v'
skip_files << 'examples/smtp/mail.v' // requires OpenSSL
skip_files << 'examples/websocket/ping.v' // requires OpenSSL
skip_files << 'examples/websocket/client-server/client.v' // requires OpenSSL
skip_files << 'examples/websocket/client-server/server.v' // requires OpenSSL
$if tinyc {
skip_files << 'examples/database/orm.v' // try fix it
}
}
$if windows {
// TODO: remove when closures on windows are supported
skip_files << 'examples/pendulum-simulation/animation.v'
skip_files << 'examples/pendulum-simulation/full.v'
skip_files << 'examples/pendulum-simulation/parallel.v'
skip_files << 'examples/pendulum-simulation/parallel_with_iw.v'
skip_files << 'examples/pendulum-simulation/sequential.v'
}
if testing.github_job != 'sokol-shaders-can-be-compiled' {
// These examples need .h files that are produced from the supplied .glsl files,
// using by the shader compiler tools in https://github.com/floooh/sokol-tools-bin/archive/pre-feb2021-api-changes.tar.gz
skip_files << 'examples/sokol/simple_shader_glsl/simple_shader.v'
skip_files << 'examples/sokol/02_cubes_glsl/cube_glsl.v'
skip_files << 'examples/sokol/03_march_tracing_glsl/rt_glsl.v'
skip_files << 'examples/sokol/04_multi_shader_glsl/rt_glsl.v'
skip_files << 'examples/sokol/05_instancing_glsl/rt_glsl.v'
// Skip obj_viewer code in the CI
skip_files << 'examples/sokol/06_obj_viewer/show_obj.v'
}
if testing.github_job != 'ubuntu-tcc' {
skip_files << 'examples/c_interop_wkhtmltopdf.v' // needs installation of wkhtmltopdf from https://github.com/wkhtmltopdf/packaging/releases
skip_files << 'examples/call_v_from_python/test.v' // the example only makes sense to be compiled, when python is installed
// the ttf_test.v is not interactive, but needs X11 headers to be installed, which is done only on ubuntu-tcc for now
skip_files << 'vlib/x/ttf/ttf_test.v'
skip_files << 'vlib/vweb/vweb_app_test.v' // imports the `sqlite` module, which in turn includes sqlite3.h
}
if testing.github_job != 'audio-examples' {
skip_files << 'examples/sokol/sounds/melody.v'
skip_files << 'examples/sokol/sounds/wav_player.v'
skip_files << 'examples/sokol/sounds/simple_sin_tones.v'
}
}
vargs := _vargs.replace('-progress', '').replace('-progress', '')
vexe := pref.vexe_path()
vroot := os.dir(vexe)
new_vtmp_dir := setup_new_vtmp_folder()
if term.can_show_color_on_stderr() {
os.setenv('VCOLORS', 'always', true)
}
return TestSession{
vexe: vexe
vroot: vroot
skip_files: skip_files
fail_fast: testing.fail_fast
vargs: vargs
vtmp_dir: new_vtmp_dir
silent_mode: _vargs.contains('-silent')
progress_mode: _vargs.contains('-progress')
}
}
pub fn (mut ts TestSession) init() {
ts.files.sort()
ts.benchmark = benchmark.new_benchmark_no_cstep()
}
pub fn (mut ts TestSession) add(file string) {
ts.files << file
}
pub fn (mut ts TestSession) test() {
// Ensure that .tmp.c files generated from compiling _test.v files,
// are easy to delete at the end, *without* affecting the existing ones.
current_wd := os.getwd()
if current_wd == os.wd_at_startup && current_wd == ts.vroot {
ts.root_relative = true
}
//
ts.init()
mut remaining_files := []string{}
for dot_relative_file in ts.files {
file := os.real_path(dot_relative_file)
$if windows {
if file.contains('sqlite') || file.contains('httpbin') {
continue
}
}
$if !macos {
if file.contains('customer') {
continue
}
}
$if msvc {
if file.contains('asm') {
continue
}
}
remaining_files << dot_relative_file
}
remaining_files = vtest.filter_vtest_only(remaining_files, fix_slashes: false)
ts.files = remaining_files
ts.benchmark.set_total_expected_steps(remaining_files.len)
mut njobs := runtime.nr_jobs()
if remaining_files.len < njobs {
njobs = remaining_files.len
}
ts.benchmark.njobs = njobs
mut pool_of_test_runners := pool.new_pool_processor(callback: worker_trunner)
// for handling messages across threads
ts.nmessages = chan LogMessage{cap: 10000}
ts.nprint_ended = chan int{cap: 0}
ts.nmessage_idx = 0
go ts.print_messages()
pool_of_test_runners.set_shared_context(ts)
pool_of_test_runners.work_on_pointers(unsafe { remaining_files.pointers() })
ts.benchmark.stop()
ts.append_message(.sentinel, '') // send the sentinel
_ := <-ts.nprint_ended // wait for the stop of the printing thread
eprintln(term.h_divider('-'))
// cleanup generated .tmp.c files after successful tests:
if ts.benchmark.nfail == 0 {
if ts.rm_binaries {
os.rmdir_all(ts.vtmp_dir) or {}
}
}
ts.show_list_of_failed_tests()
}
fn worker_trunner(mut p pool.PoolProcessor, idx int, thread_id int) voidptr {
mut ts := &TestSession(p.get_shared_context())
if ts.fail_fast {
if ts.failed_cmds.len > 0 {
return pool.no_result
}
}
tmpd := ts.vtmp_dir
show_stats := '-stats' in ts.vargs.split(' ')
// tls_bench is used to format the step messages/timings
mut tls_bench := &benchmark.Benchmark(p.get_thread_context(idx))
if isnil(tls_bench) {
tls_bench = benchmark.new_benchmark_pointer()
tls_bench.set_total_expected_steps(ts.benchmark.nexpected_steps)
p.set_thread_context(idx, tls_bench)
}
tls_bench.no_cstep = true
tls_bench.njobs = ts.benchmark.njobs
mut relative_file := os.real_path(p.get_item<string>(idx))
mut cmd_options := [ts.vargs]
mut run_js := false
is_fmt := ts.vargs.contains('fmt')
is_vet := ts.vargs.contains('vet')
produces_file_output := !(is_fmt || is_vet)
if relative_file.ends_with('js.v') {
if produces_file_output {
cmd_options << ' -b js'
run_js = true
}
}
if relative_file.contains('global') && !is_fmt {
cmd_options << ' -enable-globals'
}
if ts.root_relative {
relative_file = relative_file.replace(ts.vroot + os.path_separator, '')
}
file := os.real_path(relative_file)
normalised_relative_file := relative_file.replace('\\', '/')
// Ensure that the generated binaries will be stored in the temporary folder.
// Remove them after a test passes/fails.
fname := os.file_name(file)
generated_binary_fname := if os.user_os() == 'windows' && !run_js {
fname.replace('.v', '.exe')
} else if !run_js {
fname.replace('.v', '')
} else {
fname.replace('.v', '')
}
generated_binary_fpath := os.join_path_single(tmpd, generated_binary_fname)
if produces_file_output {
if os.exists(generated_binary_fpath) {
if ts.rm_binaries {
os.rm(generated_binary_fpath) or {}
}
}
cmd_options << ' -o ${os.quoted_path(generated_binary_fpath)}'
}
cmd := '${os.quoted_path(ts.vexe)} ' + cmd_options.join(' ') + ' ${os.quoted_path(file)}'
ts.benchmark.step()
tls_bench.step()
if relative_file.replace('\\', '/') in ts.skip_files {
ts.benchmark.skip()
tls_bench.skip()
if !testing.hide_skips {
ts.append_message(.skip, tls_bench.step_message_skip(normalised_relative_file))
}
return pool.no_result
}
if show_stats {
ts.append_message(.ok, term.h_divider('-'))
mut status := os.system(cmd)
if status != 0 {
details := get_test_details(file)
os.setenv('VTEST_RETRY_MAX', '$details.retry', true)
for retry := 1; retry <= details.retry; retry++ {
ts.append_message(.info, ' [stats] retrying $retry/$details.retry of $relative_file ; known flaky: $details.flaky ...')
os.setenv('VTEST_RETRY', '$retry', true)
status = os.system(cmd)
if status == 0 {
unsafe {
goto test_passed_system
}
}
time.sleep(500 * time.millisecond)
}
if details.flaky && !testing.fail_flaky {
ts.append_message(.info, ' *FAILURE* of the known flaky test file $relative_file is ignored, since VTEST_FAIL_FLAKY is 0 . Retry count: $details.retry .')
unsafe {
goto test_passed_system
}
}
ts.benchmark.fail()
tls_bench.fail()
ts.add_failed_cmd(cmd)
return pool.no_result
} else {
test_passed_system:
ts.benchmark.ok()
tls_bench.ok()
}
} else {
if testing.show_start {
ts.append_message(.info, ' starting $relative_file ...')
}
mut r := os.execute(cmd)
if r.exit_code < 0 {
ts.benchmark.fail()
tls_bench.fail()
ts.append_message(.fail, tls_bench.step_message_fail(normalised_relative_file))
ts.add_failed_cmd(cmd)
return pool.no_result
}
if r.exit_code != 0 {
details := get_test_details(file)
os.setenv('VTEST_RETRY_MAX', '$details.retry', true)
for retry := 1; retry <= details.retry; retry++ {
ts.append_message(.info, ' retrying $retry/$details.retry of $relative_file ; known flaky: $details.flaky ...')
os.setenv('VTEST_RETRY', '$retry', true)
r = os.execute(cmd)
if r.exit_code == 0 {
unsafe {
goto test_passed_execute
}
}
}
if details.flaky && !testing.fail_flaky {
ts.append_message(.info, ' *FAILURE* of the known flaky test file $relative_file is ignored, since VTEST_FAIL_FLAKY is 0 . Retry count: $details.retry .')
unsafe {
goto test_passed_execute
}
}
ts.benchmark.fail()
tls_bench.fail()
ending_newline := if r.output.ends_with('\n') { '\n' } else { '' }
ts.append_message(.fail, tls_bench.step_message_fail('$normalised_relative_file\n$r.output.trim_space()$ending_newline'))
ts.add_failed_cmd(cmd)
} else {
test_passed_execute:
ts.benchmark.ok()
tls_bench.ok()
if !testing.hide_oks {
ts.append_message(.ok, tls_bench.step_message_ok(normalised_relative_file))
}
}
}
if produces_file_output && os.exists(generated_binary_fpath) && ts.rm_binaries {
os.rm(generated_binary_fpath) or {}
}
return pool.no_result
}
pub fn vlib_should_be_present(parent_dir string) {
vlib_dir := os.join_path_single(parent_dir, 'vlib')
if !os.is_dir(vlib_dir) {
eprintln('$vlib_dir is missing, it must be next to the V executable')
exit(1)
}
}
pub fn prepare_test_session(zargs string, folder string, oskipped []string, main_label string) TestSession {
vexe := pref.vexe_path()
parent_dir := os.dir(vexe)
vlib_should_be_present(parent_dir)
vargs := zargs.replace(vexe, '')
eheader(main_label)
if vargs.len > 0 {
eprintln('v compiler args: "$vargs"')
}
mut session := new_test_session(vargs, true)
files := os.walk_ext(os.join_path_single(parent_dir, folder), '.v')
mut mains := []string{}
mut skipped := oskipped.clone()
next_file: for f in files {
fnormalised := f.replace('\\', '/')
// Note: a `testdata` folder, is the preferred name of a folder, containing V code,
// that you *do not want* the test framework to find incidentally for various reasons,
// for example module import tests, or subtests, that are compiled/run by other parent tests
// in specific configurations, etc.
if fnormalised.contains('testdata/') || fnormalised.contains('modules/')
|| fnormalised.contains('preludes/') {
continue
}
$if windows {
// skip pico and process/command examples on windows
if fnormalised.ends_with('examples/pico/pico.v')
|| fnormalised.ends_with('examples/process/command.v') {
continue
}
}
c := os.read_file(f) or { panic(err) }
maxc := if c.len > 500 { 500 } else { c.len }
start := c[0..maxc]
if start.contains('module ') && !start.contains('module main') {
skipped_f := f.replace(os.join_path_single(parent_dir, ''), '')
skipped << skipped_f
}
for skip_prefix in oskipped {
skip_folder := skip_prefix + '/'
if fnormalised.starts_with(skip_folder) {
continue next_file
}
}
mains << f
}
session.files << mains
session.skip_files << skipped
return session
}
pub type FnTestSetupCb = fn (mut session TestSession)
pub fn v_build_failing_skipped(zargs string, folder string, oskipped []string, cb FnTestSetupCb) bool {
main_label := 'Building $folder ...'
finish_label := 'building $folder'
mut session := prepare_test_session(zargs, folder, oskipped, main_label)
cb(mut session)
session.test()
eprintln(session.benchmark.total_message(finish_label))
return session.failed_cmds.len > 0
}
pub fn build_v_cmd_failed(cmd string) bool {
res := os.execute(cmd)
if res.exit_code < 0 {
return true
}
if res.exit_code != 0 {
eprintln('')
eprintln(res.output)
return true
}
return false
}
pub fn building_any_v_binaries_failed() bool {
eheader('Building V binaries...')
eprintln('VFLAGS is: "' + os.getenv('VFLAGS') + '"')
vexe := pref.vexe_path()
parent_dir := os.dir(vexe)
vlib_should_be_present(parent_dir)
os.chdir(parent_dir) or { panic(err) }
mut failed := false
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 {
bmark.step()
if build_v_cmd_failed(cmd) {
bmark.fail()
failed = true
eprintln(bmark.step_message_fail('command: $cmd . See details above ^^^^^^^'))
eprintln('')
continue
}
bmark.ok()
if !testing.hide_oks {
eprintln(bmark.step_message_ok('command: $cmd'))
}
}
bmark.stop()
eprintln(term.h_divider('-'))
eprintln(bmark.total_message('building v binaries'))
return failed
}
pub fn eheader(msg string) {
eprintln(term.header_left(msg, '-'))
}
pub fn header(msg string) {
println(term.header_left(msg, '-'))
flush_stdout()
}
pub fn setup_new_vtmp_folder() string {
now := time.sys_mono_now()
new_vtmp_dir := os.join_path(os.temp_dir(), 'v', 'test_session_$now')
os.mkdir_all(new_vtmp_dir) or { panic(err) }
os.setenv('VTMP', new_vtmp_dir, true)
return new_vtmp_dir
}
pub struct TestDetails {
pub mut:
retry int
flaky bool // when flaky tests fail, the whole run is still considered successfull, unless VTEST_FAIL_FLAKY is 1
}
pub fn get_test_details(file string) TestDetails {
mut res := TestDetails{}
lines := os.read_lines(file) or { [] }
for line in lines {
if line.starts_with('// vtest retry:') {
res.retry = line.all_after(':').trim_space().int()
}
if line.starts_with('// vtest flaky:') {
res.flaky = line.all_after(':').trim_space().bool()
}
}
return res
}
pub fn find_started_process(pname string) ?string {
for line in testing.all_processes {
if line.contains(pname) {
return line
}
}
return error('could not find process matching $pname')
}

View File

@ -0,0 +1,197 @@
module vgit
import os
import flag
import scripting
pub fn check_v_commit_timestamp_before_self_rebuilding(v_timestamp int) {
if v_timestamp >= 1561805697 {
return
}
eprintln('##################################################################')
eprintln('# WARNING: v self rebuilding, before 5b7a1e8 (2019-06-29 12:21) #')
eprintln('# required the v executable to be built *inside* #')
eprintln('# the toplevel compiler/ folder. #')
eprintln('# #')
eprintln('# That is not supported by this tool. #')
eprintln('# You will have to build it manually there. #')
eprintln('##################################################################')
}
pub fn validate_commit_exists(commit string) {
if commit.len == 0 {
return
}
cmd := "git cat-file -t '$commit' "
if !scripting.exit_0_status(cmd) {
eprintln('Commit: "$commit" does not exist in the current repository.')
exit(3)
}
}
pub fn line_to_timestamp_and_commit(line string) (int, string) {
parts := line.split(' ')
return parts[0].int(), parts[1]
}
pub fn normalized_workpath_for_commit(workdir string, commit string) string {
nc := 'v_at_' + commit.replace('^', '_').replace('-', '_').replace('/', '_')
return os.real_path(workdir + os.path_separator + nc)
}
fn get_current_folder_commit_hash() string {
vline := scripting.run('git rev-list -n1 --timestamp HEAD')
_, v_commithash := line_to_timestamp_and_commit(vline)
return v_commithash
}
pub fn prepare_vc_source(vcdir string, cdir string, commit string) (string, string) {
scripting.chdir(cdir)
// Building a historic v with the latest vc is not always possible ...
// It is more likely, that the vc *at the time of the v commit*,
// or slightly before that time will be able to build the historic v:
vline := scripting.run('git rev-list -n1 --timestamp "$commit" ')
v_timestamp, v_commithash := line_to_timestamp_and_commit(vline)
scripting.verbose_trace(@FN, 'v_timestamp: $v_timestamp | v_commithash: $v_commithash')
check_v_commit_timestamp_before_self_rebuilding(v_timestamp)
scripting.chdir(vcdir)
scripting.run('git checkout --quiet master')
//
mut vccommit := ''
vcbefore_subject_match := scripting.run('git rev-list HEAD -n1 --timestamp --grep=${v_commithash[0..7]} ')
scripting.verbose_trace(@FN, 'vcbefore_subject_match: $vcbefore_subject_match')
if vcbefore_subject_match.len > 3 {
_, vccommit = line_to_timestamp_and_commit(vcbefore_subject_match)
} else {
scripting.verbose_trace(@FN, 'the v commit did not match anything in the vc log; try --timestamp instead.')
vcbefore := scripting.run('git rev-list HEAD -n1 --timestamp --before=$v_timestamp ')
_, vccommit = line_to_timestamp_and_commit(vcbefore)
}
scripting.verbose_trace(@FN, 'vccommit: $vccommit')
scripting.run('git checkout --quiet "$vccommit" ')
scripting.run('wc *.c')
scripting.chdir(cdir)
return v_commithash, vccommit
}
pub fn clone_or_pull(remote_git_url string, local_worktree_path string) {
// Note: after clone_or_pull, the current repo branch is === HEAD === master
if os.is_dir(local_worktree_path) && os.is_dir(os.join_path_single(local_worktree_path, '.git')) {
// Already existing ... Just pulling in this case is faster usually.
scripting.run('git -C "$local_worktree_path" checkout --quiet master')
scripting.run('git -C "$local_worktree_path" pull --quiet ')
} else {
// Clone a fresh
scripting.run('git clone --quiet "$remote_git_url" "$local_worktree_path" ')
}
}
pub struct VGitContext {
pub:
cc string = 'cc' // what compiler to use
workdir string = '/tmp' // the base working folder
commit_v string = 'master' // the commit-ish that needs to be prepared
path_v string // where is the local working copy v repo
path_vc string // where is the local working copy vc repo
v_repo_url string // the remote v repo URL
vc_repo_url string // the remote vc repo URL
pub mut:
// these will be filled by vgitcontext.compile_oldv_if_needed()
commit_v__hash string // the git commit of the v repo that should be prepared
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/ or cmd/v, depending on v version
make_fresh_tcc bool // whether to do 'make fresh_tcc' before compiling an old V.
}
pub fn (mut vgit_context VGitContext) compile_oldv_if_needed() {
vgit_context.vexename = if os.user_os() == 'windows' { 'v.exe' } else { 'v' }
vgit_context.vexepath = os.real_path(os.join_path_single(vgit_context.path_v, vgit_context.vexename))
mut command_for_building_v_from_c_source := ''
mut command_for_selfbuilding := ''
if 'windows' == os.user_os() {
command_for_building_v_from_c_source = '$vgit_context.cc -std=c99 -I ./thirdparty/stdatomic/win -municode -w -o cv.exe "$vgit_context.path_vc/v_win.c" '
command_for_selfbuilding = './cv.exe -o $vgit_context.vexename {SOURCE}'
} else {
command_for_building_v_from_c_source = '$vgit_context.cc -std=gnu11 -I ./thirdparty/stdatomic/nix -w -o cv "$vgit_context.path_vc/v.c" -lm -lpthread'
command_for_selfbuilding = './cv -o $vgit_context.vexename {SOURCE}'
}
scripting.chdir(vgit_context.workdir)
clone_or_pull(vgit_context.v_repo_url, vgit_context.path_v)
clone_or_pull(vgit_context.vc_repo_url, vgit_context.path_vc)
scripting.chdir(vgit_context.path_v)
scripting.run('git checkout --quiet $vgit_context.commit_v')
if os.is_dir(vgit_context.path_v) && os.exists(vgit_context.vexepath) {
// already compiled, so no need to compile v again
vgit_context.commit_v__hash = get_current_folder_commit_hash()
return
}
v_commithash, vccommit_before := prepare_vc_source(vgit_context.path_vc, vgit_context.path_v,
'HEAD')
vgit_context.commit_v__hash = v_commithash
vgit_context.commit_vc_hash = vccommit_before
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
}
// Recompilation is needed. Just to be sure, clean up everything first.
scripting.run('git clean -xf')
if vgit_context.make_fresh_tcc {
scripting.run('make fresh_tcc')
}
scripting.run(command_for_building_v_from_c_source)
build_cmd := command_for_selfbuilding.replace('{SOURCE}', vgit_context.vvlocation)
scripting.run(build_cmd)
// At this point, there exists a file vgit_context.vexepath
// which should be a valid working V executable.
}
pub struct VGitOptions {
pub mut:
workdir string // the working folder (typically /tmp), where the tool will write
v_repo_url string // the url of the V repository. It can be a local folder path, if you want to eliminate network operations...
vc_repo_url string // the url of the vc repository. It can be a local folder path, if you want to eliminate network operations...
show_help bool // whether to show the usage screen
verbose bool // should the tool be much more verbose
}
pub fn add_common_tool_options(mut context VGitOptions, mut fp flag.FlagParser) []string {
tdir := os.temp_dir()
context.workdir = os.real_path(fp.string('workdir', `w`, context.workdir, 'A writable base folder. Default: $tdir'))
context.v_repo_url = fp.string('vrepo', 0, context.v_repo_url, 'The url of the V repository. You can clone it locally too. See also --vcrepo below.')
context.vc_repo_url = fp.string('vcrepo', 0, context.vc_repo_url, 'The url of the vc repository. You can clone it
${flag.space}beforehand, and then just give the local folder
${flag.space}path here. That will eliminate the network ops
${flag.space}done by this tool, which is useful, if you want
${flag.space}to script it/run it in a restrictive vps/docker.
')
context.show_help = fp.bool('help', `h`, false, 'Show this help screen.')
context.verbose = fp.bool('verbose', `v`, false, 'Be more verbose.')
if context.show_help {
println(fp.usage())
exit(0)
}
if context.verbose {
scripting.set_verbose(true)
}
if os.is_dir(context.v_repo_url) {
context.v_repo_url = os.real_path(context.v_repo_url)
}
if os.is_dir(context.vc_repo_url) {
context.vc_repo_url = os.real_path(context.vc_repo_url)
}
commits := fp.finalize() or {
eprintln('Error: $err')
exit(1)
}
for commit in commits {
validate_commit_exists(commit)
}
return commits
}

View File

@ -0,0 +1,14 @@
module vhelp
import os
pub fn show_topic(topic string) {
vexe := os.real_path(os.getenv('VEXE'))
vroot := os.dir(vexe)
target_topic := os.join_path(vroot, 'cmd', 'v', 'help', '${topic}.txt')
content := os.read_file(target_topic) or {
eprintln('Unknown topic: $topic')
exit(1)
}
println(content)
}

185
cmd/tools/oldv.v 100644
View File

@ -0,0 +1,185 @@
import os
import flag
import scripting
import vgit
const (
tool_version = '0.0.3'
tool_description = ' Checkout an old V and compile it as it was on specific commit.
| This tool is useful, when you want to discover when something broke.
| It is also useful, when you just want to experiment with an older historic V.
|
| The VCOMMIT argument can be a git commitish like HEAD or master and so on.
| When oldv is used with git bisect, you probably want to give HEAD. For example:
| git bisect start
| git bisect bad
| git checkout known_good_commit
| git bisect good
| ## Now git will automatically checkout a middle commit between the bad and the good
| cmd/tools/oldv --bisect --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:
| git bisect bad
| ## Now you just repeat the above steps, each time running oldv with the same command, then mark the result as good or bad,
| ## until you find the commit, where the problem first occurred.
| ## When you finish, do not forget to do:
| git bisect reset'.strip_margin()
)
struct Context {
mut:
vgo vgit.VGitOptions
vgcontext vgit.VGitContext
commit_v string = 'master' // the commit from which you want to produce a working v compiler (this may be a commit-ish too)
commit_v_hash string // this will be filled from the commit-ish commit_v using rev-list. It IS a commit hash.
path_v string // the full path to the v folder inside workdir.
path_vc string // the full path to the vc folder inside workdir.
cmd_to_run string // the command that you want to run *in* the oldv repo
cc string = 'cc' // the C compiler to use for bootstrapping.
cleanup bool // should the tool run a cleanup first
use_cache bool // use local cached copies for --vrepo and --vcrepo in
fresh_tcc bool // do use `make fresh_tcc`
is_bisect bool // bisect mode; usage: `cmd/tools/oldv -b -c './v run bug.v'`
}
fn (mut c Context) compile_oldv_if_needed() {
c.vgcontext = vgit.VGitContext{
workdir: c.vgo.workdir
v_repo_url: c.vgo.v_repo_url
vc_repo_url: c.vgo.vc_repo_url
cc: c.cc
commit_v: c.commit_v
path_v: c.path_v
path_vc: c.path_vc
make_fresh_tcc: c.fresh_tcc
}
c.vgcontext.compile_oldv_if_needed()
c.commit_v_hash = c.vgcontext.commit_v__hash
if !os.exists(c.vgcontext.vexepath) && c.cmd_to_run.len > 0 {
// Note: 125 is a special code, that git bisect understands as 'skip this commit'.
// it is used to inform git bisect that the current commit leads to a build failure.
exit(125)
}
}
const cache_oldv_folder = os.join_path(os.cache_dir(), 'oldv')
const cache_oldv_folder_v = os.join_path(cache_oldv_folder, 'v')
const cache_oldv_folder_vc = os.join_path(cache_oldv_folder, 'vc')
fn sync_cache() {
scripting.verbose_trace(@FN, 'start')
if !os.exists(cache_oldv_folder) {
scripting.verbose_trace(@FN, 'creating $cache_oldv_folder')
scripting.mkdir_all(cache_oldv_folder) or {
scripting.verbose_trace(@FN, '## failed.')
exit(1)
}
}
scripting.chdir(cache_oldv_folder)
for reponame in ['v', 'vc'] {
repofolder := os.join_path(cache_oldv_folder, reponame)
if !os.exists(repofolder) {
scripting.verbose_trace(@FN, 'cloning to $repofolder')
scripting.exec('git clone --quiet https://github.com/vlang/$reponame $repofolder') or {
scripting.verbose_trace(@FN, '## error during clone: $err')
exit(1)
}
}
scripting.chdir(repofolder)
scripting.exec('git pull --quiet') or {
scripting.verbose_trace(@FN, 'pulling to $repofolder')
scripting.verbose_trace(@FN, '## error during pull: $err')
exit(1)
}
}
scripting.verbose_trace(@FN, 'done')
}
fn main() {
scripting.used_tools_must_exist(['git', 'cc'])
//
// Resetting VEXE here allows for `v run cmd/tools/oldv.v'.
// the parent V would have set VEXE, which later will
// affect the V's run from the tool itself.
os.setenv('VEXE', '', true)
//
mut context := Context{}
context.vgo.workdir = cache_oldv_folder
mut fp := flag.new_flag_parser(os.args)
fp.application(os.file_name(os.executable()))
fp.version(tool_version)
fp.description(tool_description)
fp.arguments_description('VCOMMIT')
fp.skip_executable()
context.use_cache = fp.bool('cache', `u`, true, 'Use a cache of local repositories for --vrepo and --vcrepo in \$HOME/.cache/oldv/')
if context.use_cache {
context.vgo.v_repo_url = cache_oldv_folder_v
context.vgo.vc_repo_url = cache_oldv_folder_vc
} else {
context.vgo.v_repo_url = 'https://github.com/vlang/v'
context.vgo.vc_repo_url = 'https://github.com/vlang/vc'
}
should_sync := fp.bool('cache-sync', `s`, false, 'Update the local cache')
context.is_bisect = fp.bool('bisect', `b`, false, 'Bisect mode. Use the current commit in the repo where oldv is.')
if !should_sync && !context.is_bisect {
fp.limit_free_args(1, 1)?
}
////
context.cleanup = fp.bool('clean', 0, false, 'Clean before running (slower).')
context.fresh_tcc = fp.bool('fresh_tcc', 0, true, 'Do `make fresh_tcc` when preparing a V compiler.')
context.cmd_to_run = fp.string('command', `c`, '', 'Command to run in the old V repo.\n')
commits := vgit.add_common_tool_options(mut context.vgo, mut fp)
if should_sync {
sync_cache()
exit(0)
}
if context.use_cache {
if !os.is_dir(cache_oldv_folder_v) || !os.is_dir(cache_oldv_folder_vc) {
sync_cache()
}
}
if commits.len > 0 {
context.commit_v = commits[0]
if context.is_bisect {
eprintln('In bisect mode, you should not pass any commits, since oldv will use the current one.')
exit(2)
}
} else {
context.commit_v = scripting.run('git rev-list -n1 HEAD')
}
scripting.cprintln('################# context.commit_v: $context.commit_v #####################')
context.path_v = vgit.normalized_workpath_for_commit(context.vgo.workdir, context.commit_v)
context.path_vc = vgit.normalized_workpath_for_commit(context.vgo.workdir, 'vc')
if !os.is_dir(context.vgo.workdir) {
eprintln('Work folder: $context.vgo.workdir , does not exist.')
exit(2)
}
ecc := os.getenv('CC')
if ecc != '' {
context.cc = ecc
}
if context.cleanup {
scripting.rmrf(context.path_v)
scripting.rmrf(context.path_vc)
}
context.compile_oldv_if_needed()
scripting.chdir(context.path_v)
shorter_hash := context.commit_v_hash[0..10]
scripting.cprintln('# v commit hash: $shorter_hash | folder: $context.path_v')
if context.cmd_to_run.len > 0 {
scripting.cprintln_strong('# command: ${context.cmd_to_run:-34s}')
cmdres := os.execute_or_exit(context.cmd_to_run)
if cmdres.exit_code != 0 {
scripting.cprintln_strong('# exit code: ${cmdres.exit_code:-4d}')
}
scripting.cprint_strong('# result: ')
print(cmdres.output)
if !cmdres.output.ends_with('\n') {
println('')
}
exit(cmdres.exit_code)
}
}

View File

@ -0,0 +1,217 @@
import os
import flag
import scripting
import vgit
const (
tool_version = '0.0.5'
tool_description = " Compares V executable size and performance,
| between 2 commits from V's local git history.
| When only one commit is given, it is compared to master.
| ".strip_margin()
)
struct Context {
cwd string // current working folder
mut:
vgo vgit.VGitOptions
a string // the full path to the 'after' folder inside workdir
b string // the full path to the 'before' folder inside workdir
vc string // the full path to the vc folder inside workdir. It is used during bootstrapping v from the C source.
commit_before string // the git commit for the 'before' state
commit_after string // the git commit for the 'after' state
warmups int // how many times to execute a command before gathering stats
hyperfineopts string // use for additional CLI options that will be given to the hyperfine command
vflags string // other v options to pass to compared v commands
}
fn new_context() Context {
return Context{
cwd: os.getwd()
commit_after: 'master'
warmups: 4
}
}
fn (c Context) compare_versions() {
// Input is validated at this point...
// Cleanup artifacts from previous runs of this tool:
scripting.chdir(c.vgo.workdir)
scripting.run('rm -rf "$c.a" "$c.b" "$c.vc" ')
// clone the VC source *just once per comparison*, and reuse it:
scripting.run('git clone --quiet "$c.vgo.vc_repo_url" "$c.vc" ')
println('Comparing V performance of commit $c.commit_before (before) vs commit $c.commit_after (after) ...')
c.prepare_v(c.b, c.commit_before)
c.prepare_v(c.a, c.commit_after)
scripting.chdir(c.vgo.workdir)
if c.vflags.len > 0 {
os.setenv('VFLAGS', c.vflags, true)
}
// The first is the baseline, against which all the others will be compared.
// It is the fastest, since hello_world.v has only a single println in it,
mut perf_files := []string{}
perf_files << c.compare_v_performance('source_hello', [
'vprod @DEBUG@ -o source.c examples/hello_world.v',
'vprod -o source.c examples/hello_world.v',
'v @DEBUG@ -o source.c examples/hello_world.v',
'v -o source.c examples/hello_world.v',
])
perf_files << c.compare_v_performance('source_v', [
'vprod @DEBUG@ -o source.c @COMPILER@',
'vprod -o source.c @COMPILER@',
'v @DEBUG@ -o source.c @COMPILER@',
'v -o source.c @COMPILER@',
])
perf_files << c.compare_v_performance('binary_hello', [
'vprod -o hello examples/hello_world.v',
'v -o hello examples/hello_world.v',
])
perf_files << c.compare_v_performance('binary_v', [
'vprod -o binary @COMPILER@',
'v -o binary @COMPILER@',
])
println('All performance files:')
for f in perf_files {
println(' $f')
}
}
fn (c &Context) prepare_v(cdir string, commit string) {
mut cc := os.getenv('CC')
if cc == '' {
cc = 'cc'
}
mut vgit_context := vgit.VGitContext{
cc: cc
commit_v: commit
path_v: cdir
path_vc: c.vc
workdir: c.vgo.workdir
v_repo_url: c.vgo.v_repo_url
vc_repo_url: c.vgo.vc_repo_url
}
vgit_context.compile_oldv_if_needed()
scripting.chdir(cdir)
println('Making a v compiler in $cdir')
scripting.run('./v -cc $cc -o v $vgit_context.vvlocation')
println('Making a vprod compiler in $cdir')
scripting.run('./v -cc $cc -prod -o vprod $vgit_context.vvlocation')
println('Stripping and compressing cv v and vprod binaries in $cdir')
scripting.run('cp cv cv_stripped')
scripting.run('cp v v_stripped')
scripting.run('cp vprod vprod_stripped')
scripting.run('strip *_stripped')
scripting.run('cp cv_stripped cv_stripped_upxed')
scripting.run('cp v_stripped v_stripped_upxed')
scripting.run('cp vprod_stripped vprod_stripped_upxed')
scripting.run('upx -qqq --lzma cv_stripped_upxed')
scripting.run('upx -qqq --lzma v_stripped_upxed')
scripting.run('upx -qqq --lzma vprod_stripped_upxed')
scripting.show_sizes_of_files(['$cdir/cv', '$cdir/cv_stripped', '$cdir/cv_stripped_upxed'])
scripting.show_sizes_of_files(['$cdir/v', '$cdir/v_stripped', '$cdir/v_stripped_upxed'])
scripting.show_sizes_of_files(['$cdir/vprod', '$cdir/vprod_stripped',
'$cdir/vprod_stripped_upxed'])
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 == 'cmd/v' {
if os.exists('vlib/v/ast/ast.v') {
println('Source lines of the compiler: ' +
scripting.run('find cmd/v/ vlib/v/ -name "*.v" | grep -v /tests/ | xargs wc | tail -n -1'))
} else {
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'))
}
}
fn (c Context) compare_v_performance(label string, commands []string) string {
println('---------------------------------------------------------------------------------')
println('Compare v performance when doing the following commands ($label):')
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 { '-cg ' } else { '-debug ' }
debug_option_b := if timestamp_b > 1570877641 { '-cg ' } else { '-debug ' }
mut hyperfine_commands_arguments := []string{}
for cmd in commands {
println(cmd)
}
for cmd in commands {
hyperfine_commands_arguments << ' \'cd ${c.b:-34s} ; ./$cmd \' '.replace_each([
'@COMPILER@',
source_location_b,
'@DEBUG@',
debug_option_b,
])
}
for cmd in commands {
hyperfine_commands_arguments << ' \'cd ${c.a:-34s} ; ./$cmd \' '.replace_each([
'@COMPILER@',
source_location_a,
'@DEBUG@',
debug_option_a,
])
}
// /////////////////////////////////////////////////////////////////////////////
cmd_stats_file := os.real_path([c.vgo.workdir, 'v_performance_stats_${label}.json'].join(os.path_separator))
comparison_cmd := 'hyperfine $c.hyperfineopts ' + '--export-json $cmd_stats_file ' +
'--time-unit millisecond ' + '--style full --warmup $c.warmups ' +
hyperfine_commands_arguments.join(' ')
// /////////////////////////////////////////////////////////////////////////////
if c.vgo.verbose {
println(comparison_cmd)
}
os.system(comparison_cmd)
println('The detailed performance comparison report was saved to: $cmd_stats_file .')
println('')
return cmd_stats_file
}
fn main() {
scripting.used_tools_must_exist(['cp', 'rm', 'strip', 'make', 'git', 'upx', 'cc', 'wc', 'tail',
'find', 'xargs', 'hyperfine'])
mut context := new_context()
mut fp := flag.new_flag_parser(os.args)
fp.application(os.file_name(os.executable()))
fp.version(tool_version)
fp.description(tool_description)
fp.arguments_description('COMMIT_BEFORE [COMMIT_AFTER]')
fp.skip_executable()
fp.limit_free_args(1, 2)?
context.vflags = fp.string('vflags', 0, '', 'Additional options to pass to the v commands, for example "-cc tcc"')
context.hyperfineopts = fp.string('hyperfine_options', 0, '', 'Additional options passed to hyperfine.
${flag.space}For example on linux, you may want to pass:
$flag.space--hyperfine_options "--prepare \'sync; echo 3 | sudo tee /proc/sys/vm/drop_caches\'"
')
commits := vgit.add_common_tool_options(mut context.vgo, mut fp)
context.commit_before = commits[0]
if commits.len > 1 {
context.commit_after = commits[1]
}
context.b = vgit.normalized_workpath_for_commit(context.vgo.workdir, context.commit_before)
context.a = vgit.normalized_workpath_for_commit(context.vgo.workdir, context.commit_after)
context.vc = vgit.normalized_workpath_for_commit(context.vgo.workdir, 'vc')
if !os.is_dir(context.vgo.workdir) {
msg := 'Work folder: ' + context.vgo.workdir + ' , does not exist.'
eprintln(msg)
exit(2)
}
context.compare_versions()
}

View File

@ -0,0 +1,79 @@
import os
import term
import flag
const tools_folder = os.real_path(os.dir(os.executable()))
const oldvexe = fullpath(tools_folder, 'oldv')
const oldv_source = fullpath(tools_folder, 'oldv.v')
const vroot = os.real_path(os.dir(os.dir(tools_folder)))
const vexe = fullpath(vroot, 'v')
fn fullpath(folder string, fname string) string {
return os.real_path(os.join_path_single(folder, exename(fname)))
}
fn exename(n string) string {
if n.ends_with('.v') || os.user_os() != 'windows' {
return n
}
return '${n}.exe'
}
struct Context {
mut:
old_commit string
new_commit string
command string
}
fn main() {
mut fp := flag.new_flag_parser(os.args)
mut context := Context{}
fp.application(os.file_name(os.executable()))
fp.version('0.0.2')
fp.description('\n Find at what commit a regression occurred.
To find when a regression happened (regression_bug.v should fail on master):
./v run cmd/tools/regress.v --old a7019ac --command " ./v run /abs/path/to/regression_bug.v"
To find when a feature was implemented (feature.v should succeed on master):
./v run cmd/tools/regress.v --old a7019ac --command "! ./v run /abs/path/to/feature.v"')
fp.skip_executable()
//
context.new_commit = fp.string('new', `n`, 'master', 'The new commit, by default: master.')
context.old_commit = fp.string('old', `o`, '', 'A known old commit, required (for it, COMMAND should exit with 0).')
context.command = fp.string('command', `c`, '', 'A command to execute. Should exit with 0 for the *old* commits.')
fp.finalize() or {}
if context.old_commit == '' {
eprintln('--old COMMIT is required')
exit(1)
}
if context.command == '' {
eprintln('--command "COMMAND" is required')
exit(2)
}
if !os.exists(oldvexe) {
if 0 != execute('${os.quoted_path(vexe)} -o ${os.quoted_path(oldvexe)} ${os.quoted_path(oldv_source)}') {
panic('can not compile $oldvexe')
}
}
os.execute('git checkout master')
os.execute('git bisect reset')
os.execute('git checkout $context.new_commit')
os.execute('git bisect start')
os.execute('git bisect new')
os.execute('git checkout $context.old_commit')
os.execute('git bisect old')
println(term.colorize(term.bright_yellow, term.header('', '-')))
execute('git bisect run ${os.quoted_path(oldvexe)} --bisect -c "$context.command"')
println(term.colorize(term.bright_yellow, term.header('', '-')))
os.execute('git bisect reset')
os.execute('git checkout master')
}
fn execute(cmd string) int {
eprintln('### $cmd')
return os.system(cmd)
}

379
cmd/tools/repeat.v 100644
View File

@ -0,0 +1,379 @@
module main
import os
import flag
import time
import term
import math
import scripting
struct CmdResult {
mut:
runs int
cmd string
icmd int
outputs []string
oms map[string][]int
summary map[string]Aints
timings []int
atiming Aints
}
struct Context {
mut:
count int
series int
warmup int
show_help bool
show_output bool
use_newline bool // use \n instead of \r, so the last line is not overwritten
fail_on_regress_percent int
fail_on_maxtime int // in ms
verbose bool
commands []string
results []CmdResult
cmd_template string // {T} will be substituted with the current command
cmd_params map[string][]string
cline string // a terminal clearing line
cgoback string
nmins int // number of minimums to discard
nmaxs int // number of maximums to discard
}
[unsafe]
fn (mut result CmdResult) free() {
unsafe {
result.cmd.free()
result.outputs.free()
result.oms.free()
result.summary.free()
result.timings.free()
result.atiming.free()
}
}
[unsafe]
fn (mut context Context) free() {
unsafe {
context.commands.free()
context.results.free()
context.cmd_template.free()
context.cmd_params.free()
context.cline.free()
context.cgoback.free()
}
}
struct Aints {
values []int
mut:
imin int
imax int
average f64
stddev f64
nmins int // number of discarded fastest results
nmaxs int // number of discarded slowest results
}
[unsafe]
fn (mut a Aints) free() {
unsafe { a.values.free() }
}
fn new_aints(ovals []int, extreme_mins int, extreme_maxs int) Aints {
mut res := Aints{
values: ovals // remember the original values
nmins: extreme_mins
nmaxs: extreme_maxs
}
mut sum := i64(0)
mut imin := math.max_i32
mut imax := -math.max_i32
// discard the extremes:
mut vals := []int{}
for x in ovals {
vals << x
}
vals.sort()
if vals.len > extreme_mins + extreme_maxs {
vals = vals[extreme_mins..vals.len - extreme_maxs].clone()
} else {
vals = []
}
// statistical processing of the remaining values:
for i in vals {
sum += i
if i < imin {
imin = i
}
if i > imax {
imax = i
}
}
res.imin = imin
res.imax = imax
if vals.len > 0 {
res.average = sum / f64(vals.len)
}
//
mut devsum := f64(0.0)
for i in vals {
x := f64(i) - res.average
devsum += (x * x)
}
res.stddev = math.sqrt(devsum / f64(vals.len))
// eprintln('\novals: $ovals\n vals: $vals\n vals.len: $vals.len | res.imin: $res.imin | res.imax: $res.imax | res.average: $res.average | res.stddev: $res.stddev')
return res
}
fn bold(s string) string {
return term.colorize(term.bold, s)
}
fn (a Aints) str() string {
return bold('${a.average:6.2f}') +
'ms ± σ: ${a.stddev:4.1f}ms, min: ${a.imin:4}ms, max: ${a.imax:4}ms, runs:${a.values.len:3}, nmins:${a.nmins:2}, nmaxs:${a.nmaxs:2}'
}
const (
max_fail_percent = 100 * 1000
max_time = 60 * 1000 // ms
performance_regression_label = 'Performance regression detected, failing since '
)
fn main() {
mut context := Context{}
context.parse_options()?
context.run()
context.show_diff_summary()
}
fn (mut context Context) parse_options() ? {
mut fp := flag.new_flag_parser(os.args)
fp.application(os.file_name(os.executable()))
fp.version('0.0.1')
fp.description('Repeat command(s) and collect statistics. Note: you have to quote each command, if it contains spaces.')
fp.arguments_description('CMD1 CMD2 ...')
fp.skip_executable()
fp.limit_free_args_to_at_least(1)?
context.count = fp.int('count', `c`, 10, 'Repetition count.')
context.series = fp.int('series', `s`, 2, 'Series count. `-s 2 -c 4 a b` => aaaabbbbaaaabbbb, while `-s 3 -c 2 a b` => aabbaabbaabb.')
context.warmup = fp.int('warmup', `w`, 2, 'Warmup runs. These are done *only at the start*, and are ignored.')
context.show_help = fp.bool('help', `h`, false, 'Show this help screen.')
context.use_newline = fp.bool('newline', `n`, false, 'Use \\n, do not overwrite the last line. Produces more output, but easier to diagnose.')
context.show_output = fp.bool('output', `O`, false, 'Show command stdout/stderr in the progress indicator for each command. Note: slower, for verbose commands.')
context.verbose = fp.bool('verbose', `v`, false, 'Be more verbose.')
context.fail_on_maxtime = fp.int('max_time', `m`, max_time, 'Fail with exit code 2, when first cmd takes above M milliseconds (regression).')
context.fail_on_regress_percent = fp.int('fail_percent', `f`, max_fail_percent, 'Fail with exit code 3, when first cmd is X% slower than the rest (regression).')
context.cmd_template = fp.string('template', `t`, '{T}', 'Command template. {T} will be substituted with the current command.')
cmd_params := fp.string_multi('parameter', `p`, 'A parameter substitution list. `{p}=val1,val2,val2` means that {p} in the template, will be substituted with each of val1, val2, val3.')
context.nmins = fp.int('nmins', `i`, 0, 'Ignore the BOTTOM X results (minimum execution time). Makes the results more robust to performance flukes.')
context.nmaxs = fp.int('nmaxs', `a`, 1, 'Ignore the TOP X results (maximum execution time). Makes the results more robust to performance flukes.')
for p in cmd_params {
parts := p.split(':')
if parts.len > 1 {
context.cmd_params[parts[0]] = parts[1].split(',')
}
}
if context.show_help {
println(fp.usage())
exit(0)
}
if context.verbose {
scripting.set_verbose(true)
}
commands := fp.finalize() or {
eprintln('Error: $err')
exit(1)
}
context.commands = context.expand_all_commands(commands)
context.results = []CmdResult{len: context.commands.len, cap: 20, init: CmdResult{
outputs: []string{cap: 500}
timings: []int{cap: 500}
}}
if context.use_newline {
context.cline = '\n'
context.cgoback = '\n'
} else {
context.cline = '\r' + term.h_divider('')
context.cgoback = '\r'
}
}
fn flushed_print(s string) {
print(s)
flush_stdout()
}
fn (mut context Context) clear_line() {
flushed_print(context.cline)
}
fn (mut context Context) expand_all_commands(commands []string) []string {
mut all_commands := []string{}
for cmd in commands {
maincmd := context.cmd_template.replace('{T}', cmd)
mut substituted_commands := []string{}
substituted_commands << maincmd
for paramk, paramlist in context.cmd_params {
for paramv in paramlist {
mut new_substituted_commands := []string{}
for cscmd in substituted_commands {
scmd := cscmd.replace(paramk, paramv)
new_substituted_commands << scmd
}
for sc in new_substituted_commands {
substituted_commands << sc
}
}
}
for sc in substituted_commands {
all_commands << sc
}
}
mut unique := map[string]int{}
for x in all_commands {
if x.contains('{') && x.contains('}') {
continue
}
unique[x] = 1
}
return unique.keys()
}
fn (mut context Context) run() {
mut run_warmups := 0
for si in 1 .. context.series + 1 {
for icmd, cmd in context.commands {
mut runs := 0
mut duration := 0
mut sum := 0
mut oldres := ''
println('Series: ${si:4}/${context.series:-4}, command: $cmd')
if context.warmup > 0 && run_warmups < context.commands.len {
for i in 1 .. context.warmup + 1 {
flushed_print('${context.cgoback}warming up run: ${i:4}/${context.warmup:-4} for ${cmd:-50s} took ${duration:6} ms ...')
mut sw := time.new_stopwatch()
res := os.execute(cmd)
if res.exit_code != 0 {
continue
}
duration = int(sw.elapsed().milliseconds())
}
run_warmups++
}
context.clear_line()
for i in 1 .. (context.count + 1) {
avg := f64(sum) / f64(i)
flushed_print('${context.cgoback}Average: ${avg:9.3f}ms | run: ${i:4}/${context.count:-4} | took ${duration:6} ms')
if context.show_output {
flushed_print(' | result: ${oldres:s}')
}
mut sw := time.new_stopwatch()
res := scripting.exec(cmd) or { continue }
duration = int(sw.elapsed().milliseconds())
if res.exit_code != 0 {
eprintln('${i:10} non 0 exit code for cmd: $cmd')
continue
}
trimed_output := res.output.trim_right('\r\n')
trimed_normalized := trimed_output.replace('\r\n', '\n')
lines := trimed_normalized.split('\n')
for line in lines {
context.results[icmd].outputs << line
}
context.results[icmd].timings << duration
sum += duration
runs++
oldres = res.output.replace('\n', ' ')
}
context.results[icmd].cmd = cmd
context.results[icmd].icmd = icmd
context.results[icmd].runs += runs
context.results[icmd].atiming = new_aints(context.results[icmd].timings, context.nmins,
context.nmaxs)
context.clear_line()
flushed_print(context.cgoback)
mut m := map[string][]int{}
ioutputs := context.results[icmd].outputs
for o in ioutputs {
x := o.split(':')
if x.len > 1 {
k := x[0]
v := x[1].trim_left(' ').int()
m[k] << v
}
}
mut summary := map[string]Aints{}
for k, v in m {
// show a temporary summary for the current series/cmd cycle
s := new_aints(v, context.nmins, context.nmaxs)
println(' $k: $s')
summary[k] = s
}
// merge current raw results to the previous ones
old_oms := context.results[icmd].oms.move()
mut new_oms := map[string][]int{}
for k, v in m {
if old_oms[k].len == 0 {
new_oms[k] = v
} else {
new_oms[k] << old_oms[k]
new_oms[k] << v
}
}
context.results[icmd].oms = new_oms.move()
// println('')
}
}
// create full summaries, taking account of all runs
for icmd in 0 .. context.results.len {
mut new_full_summary := map[string]Aints{}
for k, v in context.results[icmd].oms {
new_full_summary[k] = new_aints(v, context.nmins, context.nmaxs)
}
context.results[icmd].summary = new_full_summary.move()
}
}
fn (mut context Context) show_diff_summary() {
context.results.sort_with_compare(fn (a &CmdResult, b &CmdResult) int {
if a.atiming.average < b.atiming.average {
return -1
}
if a.atiming.average > b.atiming.average {
return 1
}
return 0
})
println('Summary (commands are ordered by ascending mean time), after $context.series series of $context.count repetitions:')
base := context.results[0].atiming.average
mut first_cmd_percentage := f64(100.0)
mut first_marker := ''
for i, r in context.results {
first_marker = ' '
cpercent := (r.atiming.average / base) * 100 - 100
if r.icmd == 0 {
first_marker = bold('>')
first_cmd_percentage = cpercent
}
println(' $first_marker${(i + 1):3} | ${cpercent:5.1f}% slower | ${r.cmd:-57s} | $r.atiming')
}
$if debugcontext ? {
println('context: $context')
}
if int(base) > context.fail_on_maxtime {
flushed_print(performance_regression_label)
println('average time: ${base:6.1f} ms > $context.fail_on_maxtime ms threshold.')
exit(2)
}
if context.fail_on_regress_percent == max_fail_percent || context.results.len < 2 {
return
}
fail_threshold_max := f64(context.fail_on_regress_percent)
if first_cmd_percentage > fail_threshold_max {
flushed_print(performance_regression_label)
println('${first_cmd_percentage:5.1f}% > ${fail_threshold_max:5.1f}% threshold.')
exit(3)
}
}

View File

@ -0,0 +1,88 @@
module main
// This program verifies that `v test` propagates errors
// and that it exits with code 1, when at least 1 FAIL happen.
import os
import rand
const (
vexe = get_vexe_path()
vroot = os.dir(vexe)
tdir = new_tdir()
)
fn get_vexe_path() string {
env_vexe := os.getenv('VEXE')
if env_vexe != '' {
return env_vexe
}
me := os.executable()
eprintln('me: $me')
mut vexe_ := os.join_path(os.dir(os.dir(os.dir(me))), 'v')
if os.user_os() == 'windows' {
vexe_ += '.exe'
}
return vexe_
}
fn new_tdir() string {
tdir_ := os.join_path(os.temp_dir(), rand.ulid())
if os.exists(tdir_) {
os.rmdir(tdir_) or { panic(err) }
}
os.mkdir(tdir_) or { panic(err) }
C.atexit(cleanup_tdir)
return tdir_
}
fn cleanup_tdir() {
println('... removing tdir: $tdir')
os.rmdir_all(tdir) or { eprintln(err) }
}
fn create_test(tname string, tcontent string) ?string {
tpath := os.join_path(tdir, tname)
os.write_file(tpath, tcontent)?
eprintln('>>>>>>>> tpath: $tpath | tcontent: $tcontent')
return tpath
}
fn main() {
defer {
os.chdir(os.wd_at_startup) or {}
}
println('> vroot: $vroot | vexe: $vexe | tdir: $tdir')
ok_fpath := create_test('a_single_ok_test.v', 'fn test_ok(){ assert true }')?
check_ok('"$vexe" "$ok_fpath"')
check_ok('"$vexe" test "$ok_fpath"')
check_ok('"$vexe" test "$tdir"')
fail_fpath := create_test('a_single_failing_test.v', 'fn test_fail(){ assert 1 == 2 }')?
check_fail('"$vexe" "$fail_fpath"')
check_fail('"$vexe" test "$fail_fpath"')
check_fail('"$vexe" test "$tdir"')
rel_dir := os.join_path(tdir, rand.ulid())
os.mkdir(rel_dir)?
os.chdir(rel_dir)?
check_ok('"$vexe" test "..${os.path_separator + os.base(ok_fpath)}"')
println('> all done')
}
fn check_ok(cmd string) string {
println('> check_ok cmd: $cmd')
res := os.execute(cmd)
if res.exit_code != 0 {
eprintln('> check_ok failed.\n$res.output')
exit(1)
}
return res.output
}
fn check_fail(cmd string) string {
println('> check_fail cmd: $cmd')
res := os.execute(cmd)
if res.exit_code == 0 {
eprintln('> check_fail succeeded, but it should have failed.\n$res.output')
exit(1)
}
return res.output
}

View File

@ -0,0 +1,82 @@
module main
import os
import time
import os.cmdline
enum Target {
both
stderr
stdout
alternate
}
fn s2target(s string) Target {
return match s {
'both' { Target.both }
'stderr' { Target.stderr }
'alternate' { Target.alternate }
else { Target.stdout }
}
}
struct Context {
mut:
timeout_ms int
period_ms int
exitcode int
target Target
omode Target
is_verbose bool
}
fn (mut ctx Context) println(s string) {
if ctx.target == .alternate {
ctx.omode = if ctx.omode == .stderr { Target.stdout } else { Target.stderr }
}
if ctx.target in [.both, .stdout] || ctx.omode == .stdout {
println('stdout, $s')
}
if ctx.target in [.both, .stderr] || ctx.omode == .stderr {
eprintln('stderr, $s')
}
}
fn do_timeout(c &Context) {
mut ctx := unsafe { c }
time.sleep(ctx.timeout_ms * time.millisecond)
exit(ctx.exitcode)
}
fn main() {
mut ctx := Context{}
args := os.args[1..]
if '-h' in args || '--help' in args {
println("Usage:
test_os_process [-v] [-h] [-target stderr/stdout/both/alternate] [-exitcode 0] [-timeout_ms 200] [-period_ms 50]
Prints lines periodically (-period_ms), to stdout/stderr (-target).
After a while (-timeout_ms), exit with (-exitcode).
This program is useful for platform independent testing
of child process/standart input/output control.
It is used in V's `os` module tests.
")
return
}
ctx.is_verbose = '-v' in args
ctx.target = s2target(cmdline.option(args, '-target', 'both'))
ctx.exitcode = cmdline.option(args, '-exitcode', '0').int()
ctx.timeout_ms = cmdline.option(args, '-timeout_ms', '200').int()
ctx.period_ms = cmdline.option(args, '-period_ms', '50').int()
if ctx.target == .alternate {
ctx.omode = .stdout
}
if ctx.is_verbose {
eprintln('> args: $args | context: $ctx')
}
go do_timeout(&ctx)
for i := 1; true; i++ {
ctx.println('$i')
time.sleep(ctx.period_ms * time.millisecond)
}
time.sleep(100 * time.second)
}

View File

@ -0,0 +1,50 @@
// Copyright (c) 2019-2022 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
import v.util
const vexe = os.getenv('VEXE')
fn main() {
vmodules := os.vmodules_dir()
c2v_dir := os.join_path(vmodules, 'c2v')
mut c2v_bin := os.join_path(c2v_dir, 'c2v')
$if windows {
c2v_bin += '.exe'
}
// Git clone c2v
if !os.exists(c2v_dir) {
println('C2V is not installed. Cloning C2V to $c2v_dir ...')
os.chdir(vmodules)?
res := os.execute('git clone https://github.com/vlang/c2v')
if res.exit_code != 0 {
eprintln('Failed to download C2V.')
exit(1)
}
}
// Compile c2v
if !os.exists(c2v_bin) {
os.chdir(c2v_dir)?
println('Compiling c2v ...')
res2 := os.execute('${os.quoted_path(vexe)} -o ${os.quoted_path(c2v_bin)} -keepc -g -experimental .')
if res2.exit_code != 0 {
eprintln(res2.output)
eprintln('Failed to compile C2V. This should not happen. Please report it via GitHub.')
exit(2)
}
}
if os.args.len < 3 {
eprintln('Wrong number of arguments. Use `v translate file.c` .')
exit(3)
}
passed_args := util.args_quote_paths(os.args[2..])
// println(passed_args)
os.chdir(os.wd_at_startup)?
res := os.system('$c2v_bin $passed_args')
if res != 0 {
eprintln('C2V failed to translate the C files. Please report it via GitHub.')
exit(4)
}
}

View File

@ -0,0 +1,114 @@
module main
import json
struct UseJson {
x int
}
fn suppress_json_warning() {
json.encode(UseJson{})
}
// struct C.cJSON {}
fn C.cJSON_CreateObject() &C.cJSON
fn C.cJSON_CreateArray() &C.cJSON
// fn C.cJSON_CreateBool(bool) &C.cJSON
fn C.cJSON_CreateTrue() &C.cJSON
fn C.cJSON_CreateFalse() &C.cJSON
fn C.cJSON_CreateNull() &C.cJSON
// fn C.cJSON_CreateNumber() &C.cJSON
// fn C.cJSON_CreateString() &C.cJSON
fn C.cJSON_CreateRaw(&u8) &C.cJSON
fn C.cJSON_IsInvalid(voidptr) bool
fn C.cJSON_IsFalse(voidptr) bool
// fn C.cJSON_IsTrue(voidptr) bool
fn C.cJSON_IsBool(voidptr) bool
fn C.cJSON_IsNull(voidptr) bool
fn C.cJSON_IsNumber(voidptr) bool
fn C.cJSON_IsString(voidptr) bool
fn C.cJSON_IsArray(voidptr) bool
fn C.cJSON_IsObject(voidptr) bool
fn C.cJSON_IsRaw(voidptr) bool
fn C.cJSON_AddItemToObject(voidptr, &u8, voidptr)
fn C.cJSON_AddItemToArray(voidptr, voidptr)
fn C.cJSON_Delete(voidptr)
fn C.cJSON_Print(voidptr) &u8
[inline]
fn create_object() &C.cJSON {
return C.cJSON_CreateObject()
}
[inline]
fn create_array() &C.cJSON {
return C.cJSON_CreateArray()
}
[inline]
fn create_string(val string) &C.cJSON {
return C.cJSON_CreateString(val.str)
}
[inline]
fn create_number(val f64) &C.cJSON {
return C.cJSON_CreateNumber(val)
}
[inline]
fn create_bool(val bool) &C.cJSON {
return C.cJSON_CreateBool(val)
}
[inline]
fn create_true() &C.cJSON {
return C.cJSON_CreateTrue()
}
[inline]
fn create_false() &C.cJSON {
return C.cJSON_CreateFalse()
}
[inline]
fn create_null() &C.cJSON {
return C.cJSON_CreateNull()
}
[inline]
fn delete(b voidptr) {
C.cJSON_Delete(b)
}
[inline]
fn add_item_to_object(obj &C.cJSON, key string, item &C.cJSON) {
C.cJSON_AddItemToObject(obj, key.str, item)
}
[inline]
fn add_item_to_array(obj &C.cJSON, item &C.cJSON) {
C.cJSON_AddItemToArray(obj, item)
}
fn json_print(json &C.cJSON) string {
s := C.cJSON_Print(json)
return unsafe { tos3(s) }
}

1
cmd/tools/vast/test/.gitignore vendored 100644
View File

@ -0,0 +1 @@
demo.json

View File

@ -0,0 +1,131 @@
// usage test: v ast path_to_v/cmd/tools/vast/test/demo.v
// will generate demo.json
// comment for module
module main
// import module
import os
import math
import time { Time, now }
// const decl
const (
a = 1
b = 3
c = 'c'
)
// struct decl
struct Point {
x int
mut:
y int
pub:
z int
pub mut:
name string
}
// method of Point
pub fn (p Point) get_x() int {
return p.x
}
// embed struct
struct MyPoint {
Point
title string
}
// enum type
enum Color {
red
green
blue
}
// type alias
type Myint = int
// sum type
type MySumType = bool | int | string
// function type
type Myfn = fn (int) int
// interface type
interface Myinterfacer {
add(int, int) int
sub(int, int) int
}
// main funciton
fn main() {
add(1, 3)
println(add(1, 2))
println('ok') // comment println
arr := [1, 3, 5, 7]
for a in arr {
println(a)
add(1, 3)
}
color := Color.red
println(color)
println(os.args)
m := math.max(1, 3)
println(m)
println(now())
t := Time{}
println(t)
p := Point{
x: 1
y: 2
z: 3
}
println(p)
my_point := MyPoint{
// x: 1
// y: 3
// z: 5
}
println(my_point.get_x())
}
// normal function
fn add(x int, y int) int {
return x + y
}
// function with defer stmt
fn defer_fn() {
mut x := 1
println('start fn')
defer {
println('in defer block')
println(x)
}
println('end fn')
}
// generic function
fn g_fn<T>(p T) T {
return p
}
// generic struct
struct GenericStruct<T> {
point Point
mut:
model T
}
// generic interface
interface Gettable<T> {
get() T
}
// generic sumtype
struct None {}
type MyOption<T> = Error | None | T

2323
cmd/tools/vast/vast.v 100644

File diff suppressed because it is too large Load Diff

146
cmd/tools/vbin2v.v 100644
View File

@ -0,0 +1,146 @@
module main
import os
import flag
import strings
const (
tool_version = '0.0.4'
tool_description = 'Converts a list of arbitrary files into a single v module file.'
)
struct Context {
mut:
files []string
prefix string
show_help bool
module_name string
write_file string
}
fn (context Context) header() string {
mut header_s := ''
header_s += 'module $context.module_name\n'
header_s += '\n'
allfiles := context.files.join(' ')
mut options := []string{}
if context.prefix.len > 0 {
options << '-p $context.prefix'
}
if context.module_name.len > 0 {
options << '-m $context.module_name'
}
if context.write_file.len > 0 {
options << '-w $context.write_file'
}
soptions := options.join(' ')
header_s += '// File generated by:\n'
header_s += '// v bin2v $allfiles $soptions\n'
header_s += '// Please, do not edit this file.\n'
header_s += '// Your changes may be overwritten.\n'
header_s += 'const (\n'
return header_s
}
fn (context Context) footer() string {
return ')\n'
}
fn (context Context) file2v(bname string, fbytes []u8, bn_max int) string {
mut sb := strings.new_builder(1000)
bn_diff_len := bn_max - bname.len
sb.write_string('\t${bname}_len' + ' '.repeat(bn_diff_len - 4) + ' = $fbytes.len\n')
fbyte := fbytes[0]
bnmae_line := '\t$bname' + ' '.repeat(bn_diff_len) + ' = [u8($fbyte), '
sb.write_string(bnmae_line)
mut line_len := bnmae_line.len + 3
for i := 1; i < fbytes.len; i++ {
b := int(fbytes[i]).str()
if line_len > 94 {
sb.go_back(1)
sb.write_string('\n\t\t')
line_len = 8
}
if i == fbytes.len - 1 {
sb.write_string(b)
line_len += b.len
} else {
sb.write_string('$b, ')
line_len += b.len + 2
}
}
sb.write_string(']!\n')
return sb.str()
}
fn (context Context) bname_and_bytes(file string) ?(string, []u8) {
fname := os.file_name(file)
fname_escaped := fname.replace_each(['.', '_', '-', '_'])
byte_name := '$context.prefix$fname_escaped'.to_lower()
fbytes := os.read_bytes(file) or { return error('Error: $err.msg()') }
return byte_name, fbytes
}
fn (context Context) max_bname_len(bnames []string) int {
mut max := 0
for n in bnames {
if n.len > max {
max = n.len
}
}
// Add 4 to max due to "_len" suffix
return max + 4
}
fn main() {
mut context := Context{}
mut fp := flag.new_flag_parser(os.args[1..])
fp.application('v bin2v')
fp.version(tool_version)
fp.description(tool_description)
fp.arguments_description('FILE [FILE]...')
context.show_help = fp.bool('help', `h`, false, 'Show this help screen.')
context.module_name = fp.string('module', `m`, 'binary', 'Name of the generated module.')
context.prefix = fp.string('prefix', `p`, '', 'A prefix put before each resource name.')
context.write_file = fp.string('write', `w`, '', 'Write directly to a file with the given name.')
if context.show_help {
println(fp.usage())
exit(0)
}
files := fp.finalize() or {
eprintln('Error: $err.msg()')
exit(1)
}
real_files := files.filter(it != 'bin2v')
if real_files.len == 0 {
println(fp.usage())
exit(0)
}
context.files = real_files
if context.write_file != '' && os.file_ext(context.write_file) !in ['.vv', '.v'] {
context.write_file += '.v'
}
mut file_byte_map := map[string][]u8{}
for file in real_files {
bname, fbytes := context.bname_and_bytes(file) or {
eprintln(err.msg())
exit(1)
}
file_byte_map[bname] = fbytes
}
max_bname := context.max_bname_len(file_byte_map.keys())
if context.write_file.len > 0 {
mut out_file := os.create(context.write_file)?
out_file.write_string(context.header())?
for bname, fbytes in file_byte_map {
out_file.write_string(context.file2v(bname, fbytes, max_bname))?
}
out_file.write_string(context.footer())?
} else {
print(context.header())
for bname, fbytes in file_byte_map {
print(context.file2v(bname, fbytes, max_bname))
}
print(context.footer())
}
}

169
cmd/tools/vbug.v 100644
View File

@ -0,0 +1,169 @@
import net.urllib
import os
import readline
const vroot = @VMODROOT
// get output from `v doctor`
fn get_vdoctor_output(is_verbose bool) string {
vexe := os.getenv('VEXE')
verbose_flag := if is_verbose { '-v' } else { '' }
result := os.execute('${os.quoted_path(vexe)} $verbose_flag doctor')
if result.exit_code != 0 {
eprintln('unable to get `v doctor` output: $result.output')
return ''
}
return result.output
}
// get ouput from `v -g -o vdbg cmd/v && vdbg file.v`
fn get_v_build_output(is_verbose bool, is_yes bool, file_path string) string {
mut vexe := os.getenv('VEXE')
// prepare a V compiler with -g to have better backtraces if possible
wd := os.getwd()
os.chdir(vroot) or {}
verbose_flag := if is_verbose { '-v' } else { '' }
vdbg_path := $if windows { '$vroot/vdbg.exe' } $else { '$vroot/vdbg' }
vdbg_compilation_cmd := '${os.quoted_path(vexe)} $verbose_flag -g -o ${os.quoted_path(vdbg_path)} cmd/v'
vdbg_result := os.execute(vdbg_compilation_cmd)
os.chdir(wd) or {}
if vdbg_result.exit_code == 0 {
vexe = vdbg_path
} else {
eprintln('unable to compile V in debug mode: $vdbg_result.output\ncommand: $vdbg_compilation_cmd\n')
}
//
mut result := os.execute('${os.quoted_path(vexe)} $verbose_flag ${os.quoted_path(file_path)}')
defer {
os.rm(vdbg_path) or {
if is_verbose {
eprintln('unable to delete `vdbg`: $err')
}
}
}
if result.exit_code == 0 {
defer {
mut generated_file := file_path.all_before_last('.')
$if windows {
generated_file += '.exe'
}
os.rm(generated_file) or {
if is_verbose {
eprintln('unable to delete generated file: $err')
}
}
}
run := is_yes
|| ask('It looks like the compilation went well, do you want to run the file?')
if run {
result = os.execute('${os.quoted_path(vexe)} $verbose_flag run ${os.quoted_path(file_path)}')
if result.exit_code == 0 && !is_yes {
confirm_or_exit('It looks like the file ran correctly as well, are you sure you want to continue?')
}
}
}
return result.output
}
fn ask(msg string) bool {
prompt := os.input_opt('$msg [Y/n] ') or { 'y' }
return prompt == '' || prompt[0].ascii_str().to_lower() != 'n'
}
fn confirm_or_exit(msg string) {
if !ask(msg) {
exit(1)
}
}
fn main() {
mut file_path := ''
mut is_verbose := false
mut is_yes := false
for arg in os.args[2..] {
match arg {
'-v' {
is_verbose = true
}
'-y' {
is_yes = true
}
else {
if !arg.ends_with('.v') && !arg.ends_with('.vsh') && !arg.ends_with('.vv') {
eprintln('unknown argument: `$arg`')
exit(1)
}
if file_path != '' {
eprintln('only one V file can be submitted')
exit(1)
}
file_path = arg
}
}
}
if file_path == '' {
eprintln('v bug: no v file listed to report')
exit(1)
}
os.unsetenv('VCOLORS')
// collect error information
// output from `v doctor`
vdoctor_output := get_vdoctor_output(is_verbose)
// file content
file_content := os.read_file(file_path) or {
eprintln('unable to get file "$file_path" content: $err')
''
}
// output from `v -g -o vdbg cmd/v && vdbg file.v`
build_output := get_v_build_output(is_verbose, is_yes, file_path)
// ask the user if he wants to submit even after an error
if !is_yes && (vdoctor_output == '' || file_content == '' || build_output == '') {
confirm_or_exit('An error occurred retrieving the information, do you want to continue?')
}
expected_result := readline.read_line('What did you expect to see? ') or {
// Ctrl-C was pressed
eprintln('\nCanceled')
exit(1)
}
// open prefilled issue creation page, or print link as a fallback
if !is_yes && vdoctor_output.contains('behind V master') {
confirm_or_exit('It looks like your installation of V is outdated, we advise you to run `v up` before submitting an issue. Are you sure you want to continue?')
}
// When updating this template, make sure to update `.github/ISSUE_TEMPLATE/bug_report.md` too
raw_body := '<!-- It is advisable to update all relevant modules using `v outdated` and `v install` -->
**V doctor:**
```
$vdoctor_output
```
**What did you do?**
`v -g -o vdbg cmd/v && vdbg $file_path`
{file_content}
**What did you expect to see?**
$expected_result
**What did you see instead?**
```
$build_output```'
mut encoded_body := urllib.query_escape(raw_body.replace_once('{file_content}', '```v\n$file_content\n```'))
mut generated_uri := 'https://github.com/vlang/v/issues/new?labels=Bug&body=$encoded_body'
if generated_uri.len > 8192 {
// GitHub doesn't support URLs longer than 8192 characters
encoded_body = urllib.query_escape(raw_body.replace_once('{file_content}', 'See attached file `$file_path`'))
generated_uri = 'https://github.com/vlang/v/issues/new?labels=Bug&body=$encoded_body'
println('Your file is too big to be submitted. Head over to the following URL and attach your file.')
println(generated_uri)
} else {
os.open_uri(generated_uri) or {
if is_verbose {
eprintln(err)
}
println(generated_uri)
}
}
}

View File

@ -0,0 +1,31 @@
module main
import os
import testing
const vroot = @VMODROOT
const efolders = [
'examples/viewer',
]
fn main() {
args_string := os.args[1..].join(' ')
params := args_string.all_before('build-examples')
skip_prefixes := efolders.map(os.real_path(os.join_path_single(vroot, it)).replace('\\',
'/'))
res := testing.v_build_failing_skipped(params, 'examples', skip_prefixes, fn (mut session testing.TestSession) {
for x in efolders {
pathsegments := x.split_any('/')
session.add(os.real_path(os.join_path(vroot, ...pathsegments)))
}
})
if res {
exit(1)
}
if testing.v_build_failing_skipped(params + '-live', os.join_path_single('examples',
'hot_reload'), skip_prefixes, fn (mut session testing.TestSession) {})
{
exit(1)
}
}

Some files were not shown because too many files have changed in this diff Show More