Compare commits
31 Commits
460c2ee77b
...
e9aca12560
Author | SHA1 | Date |
---|---|---|
Jef Roosens | e9aca12560 | |
Jef Roosens | ad966c29c2 | |
Jef Roosens | 0e3efb655e | |
Jef Roosens | b817f8ebc9 | |
Jef Roosens | 3d0ecd0a2d | |
Jef Roosens | 77b5845c2c | |
CC | d336b7b877 | |
Delyan Angelov | ccc3271493 | |
Emirhan Yener | e5bbb23389 | |
Wertzui123 | c10ba6d81a | |
Delyan Angelov | b0fe21f018 | |
Alexander Medvednikov | c17200c33d | |
Larpon | 298dc77c38 | |
yuyi | e9a8f5fcc7 | |
Wertzui123 | 587101a1ea | |
Delyan Angelov | 2524207d1c | |
Alexander Medvednikov | 78c527b243 | |
Alexander Medvednikov | dbc51a4579 | |
l-m | ed8c63cc0b | |
Delyan Angelov | a7108ff05c | |
Delyan Angelov | b8d9bfec16 | |
Delyan Angelov | 436081a9f5 | |
Delyan Angelov | 1b87a4770c | |
Delyan Angelov | 856270cac2 | |
Alexander Medvednikov | 989c5e26f5 | |
Alexander Medvednikov | fe673e7963 | |
Alexander Medvednikov | ae2183043b | |
Dialga | 5cd5d551e3 | |
Alexander Medvednikov | c9ab086029 | |
yuyi | e6c3de2f46 | |
Larpon | 3fb88500a2 |
|
@ -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')
|
||||
}
|
||||
}
|
||||
}
|
|
@ -42,11 +42,6 @@ jobs:
|
|||
./v -os windows cmd/v
|
||||
./v -os windows examples/2048/2048.v
|
||||
|
||||
- name: Compile to raw Android (non-graphic) compatible
|
||||
run: |
|
||||
# Test that V can compile non-graphic app to Android compatible code *without* using the -apk flag
|
||||
./v -os android -gc none examples/toml.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'
|
||||
|
@ -64,6 +59,7 @@ jobs:
|
|||
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
|
||||
|
||||
|
@ -95,12 +91,6 @@ jobs:
|
|||
./v -os windows examples/2048/2048.v
|
||||
ls -lart examples/2048/2048.exe
|
||||
|
||||
- name: toml.v can be compiled to raw Android C
|
||||
run: |
|
||||
# Test that V can compile non-graphic app to Android compatible code *without* using the -apk flag
|
||||
./v -os android -gc none examples/toml.v
|
||||
|
||||
|
||||
windows-cross:
|
||||
runs-on: windows-2019
|
||||
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
|
||||
|
|
|
@ -48,3 +48,27 @@ jobs:
|
|||
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
|
||||
|
|
|
@ -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
|
|
@ -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'
|
|
@ -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
|
|
@ -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
|
|
@ -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"]
|
|
@ -5,7 +5,7 @@ TMPDIR ?= /tmp
|
|||
VROOT ?= .
|
||||
VC ?= ./vc
|
||||
V ?= ./v
|
||||
VCREPO ?= https://github.com/vlang/vc
|
||||
VCREPO ?= https://git.rustybever.be/vieter/vc
|
||||
TCCREPO ?= https://github.com/vlang/tccbin
|
||||
|
||||
VCFILE := v.c
|
||||
|
|
2
Makefile
2
Makefile
|
@ -7,7 +7,7 @@ LDFLAGS ?=
|
|||
|
||||
all:
|
||||
rm -rf vc/
|
||||
git clone --depth 1 --quiet https://github.com/vlang/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
|
||||
|
|
|
@ -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"
|
||||
}
|
|
@ -27,7 +27,7 @@
|
|||
- 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
|
||||
- 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)
|
||||
|
|
|
@ -5,17 +5,22 @@ 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_alpha')
|
||||
c2v_bin := os.join_path(c2v_dir, 'c2v')
|
||||
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 --depth 1 git@github.com:/vlang/c2v_alpha.git')
|
||||
res := os.execute('git clone https://github.com/vlang/c2v')
|
||||
if res.exit_code != 0 {
|
||||
eprintln('Failed to download C2V. Perhaps it is not released yet? Is it June 20 yet?')
|
||||
eprintln('Failed to download C2V.')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
@ -23,15 +28,15 @@ fn main() {
|
|||
if !os.exists(c2v_bin) {
|
||||
os.chdir(c2v_dir)?
|
||||
println('Compiling c2v ...')
|
||||
res2 := os.execute('v -keepc -g -experimental -o 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 never happen, please report it via GitHub.')
|
||||
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 args. Use `v translate file.c`.')
|
||||
eprintln('Wrong number of arguments. Use `v translate file.c` .')
|
||||
exit(3)
|
||||
}
|
||||
passed_args := util.args_quote_paths(os.args[2..])
|
||||
|
|
32
doc/docs.md
32
doc/docs.md
|
@ -107,7 +107,7 @@ For more details and troubleshooting, please visit the [vab GitHub repository](h
|
|||
</td><td width=33% valign=top>
|
||||
|
||||
* [Functions 2](#functions-2)
|
||||
* [Pure functions by default](#pure-functions-by-default)
|
||||
* [Immutable function args by default](#immutable-function-args-by-default)
|
||||
* [Mutable arguments](#mutable-arguments)
|
||||
* [Variable number of arguments](#variable-number-of-arguments)
|
||||
* [Anonymous & higher-order functions](#anonymous--higher-order-functions)
|
||||
|
@ -343,8 +343,7 @@ the expression `T(v)` converts the value `v` to the
|
|||
type `T`.
|
||||
|
||||
Unlike most other languages, V only allows defining variables in functions.
|
||||
Global (module level) variables are not allowed. There's no global state in V
|
||||
(see [Pure functions by default](#pure-functions-by-default) for details).
|
||||
Global (module level) variables are not allowed. There's no global state in V.
|
||||
|
||||
For consistency across different code bases, all variable and function names
|
||||
must use the `snake_case` style, as opposed to type names, which must use `PascalCase`.
|
||||
|
@ -2279,22 +2278,24 @@ Note that the embedded struct arguments are not necessarily stored in the order
|
|||
|
||||
## Functions 2
|
||||
|
||||
### Pure functions by default
|
||||
### Immutable function args by default
|
||||
|
||||
V functions are pure by default, meaning that their return values are a function of their
|
||||
arguments only, and their evaluation has no side effects (besides I/O).
|
||||
In V function arguments are immutable by default, and mutable args have to be
|
||||
marked on call.
|
||||
|
||||
This is achieved by a lack of global variables and all function arguments being
|
||||
immutable by default, even when [references](#references) are passed.
|
||||
Since there are also no globals, that means that the return values of the functions,
|
||||
are a function of their arguments only, and their evaluation has no side effects
|
||||
(unless the function uses I/O).
|
||||
|
||||
V is not a purely functional language however.
|
||||
Function arguments are immutable by default, even when [references](#references) are passed.
|
||||
|
||||
Note that V is not a purely functional language however.
|
||||
|
||||
There is a compiler flag to enable global variables (`-enable-globals`), but this is
|
||||
intended for low-level applications like kernels and drivers.
|
||||
|
||||
### Mutable arguments
|
||||
|
||||
It is possible to modify function arguments by using the keyword `mut`:
|
||||
It is possible to modify function arguments by declaring them with the keyword `mut`:
|
||||
|
||||
```v
|
||||
struct User {
|
||||
|
@ -2313,7 +2314,7 @@ user.register()
|
|||
println(user.is_registered) // "true"
|
||||
```
|
||||
|
||||
In this example, the receiver (which is simply the first argument) is marked as mutable,
|
||||
In this example, the receiver (which is just the first argument) is explicitly marked as mutable,
|
||||
so `register()` can change the user object. The same works with non-receiver arguments:
|
||||
|
||||
```v
|
||||
|
@ -3601,8 +3602,8 @@ println(compare(1.1, 1.2)) // -1
|
|||
|
||||
## Concurrency
|
||||
### Spawning Concurrent Tasks
|
||||
V's model of concurrency is very similar to Go's. To run `foo()` concurrently in
|
||||
a different thread, just call it with `go foo()`:
|
||||
V's model of concurrency is going to be very similar to Go's.
|
||||
For now, `go foo()` runs `foo()` concurrently in a different thread:
|
||||
|
||||
```v
|
||||
import math
|
||||
|
@ -3618,6 +3619,9 @@ fn main() {
|
|||
}
|
||||
```
|
||||
|
||||
> In V 0.4 `go foo()` will be automatically renamed via vfmt to `spawn foo()`,
|
||||
and there will be a way to launch a coroutine (a lightweight thread managed by the runtime).
|
||||
|
||||
Sometimes it is necessary to wait until a parallel thread has finished. This can
|
||||
be done by assigning a *handle* to the started thread and calling the `wait()` method
|
||||
to this handle later:
|
||||
|
|
|
@ -787,7 +787,7 @@ fn (mut app App) undo() {
|
|||
fn (mut app App) on_key_down(key gg.KeyCode) {
|
||||
// these keys are independent from the game state:
|
||||
match key {
|
||||
.a { app.is_ai_mode = !app.is_ai_mode }
|
||||
.c { app.is_ai_mode = !app.is_ai_mode }
|
||||
.escape { app.gg.quit() }
|
||||
.n, .r { app.new_game() }
|
||||
.backspace { app.undo() }
|
||||
|
@ -797,12 +797,14 @@ fn (mut app App) on_key_down(key gg.KeyCode) {
|
|||
else {}
|
||||
}
|
||||
if app.state in [.play, .freeplay] {
|
||||
match key {
|
||||
.w, .up { app.move(.up) }
|
||||
.a, .left { app.move(.left) }
|
||||
.s, .down { app.move(.down) }
|
||||
.d, .right { app.move(.right) }
|
||||
else {}
|
||||
if !app.is_ai_mode {
|
||||
match key {
|
||||
.w, .up { app.move(.up) }
|
||||
.a, .left { app.move(.left) }
|
||||
.s, .down { app.move(.down) }
|
||||
.d, .right { app.move(.right) }
|
||||
else {}
|
||||
}
|
||||
}
|
||||
}
|
||||
if app.state == .victory {
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Test file uploading data transformation</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h2>File Upload and Data Transformation</h2>
|
||||
|
||||
Upload the <b>sample_input.txt</b> located in this example folder.
|
||||
<br>
|
||||
<br>
|
||||
V will apply data transformation, adding the first two columns into a third column, and return the results as a download. <br>
|
||||
<br>
|
||||
|
||||
For example:
|
||||
<table style="color:blue;">
|
||||
<tr>
|
||||
<td>10</td>
|
||||
<td>13</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>20</td>
|
||||
<td>54</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>30</td>
|
||||
<td>82</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>40</td>
|
||||
<td>11</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>50</td>
|
||||
<td>47</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br>
|
||||
Becomes...
|
||||
<br>
|
||||
<table style="color:blue;">
|
||||
<tr>
|
||||
<td>10</td>
|
||||
<td>13</td>
|
||||
<td>23</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>20</td>
|
||||
<td>54</td>
|
||||
<td>74</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>30</td>
|
||||
<td>82</td>
|
||||
<td>112</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>40</td>
|
||||
<td>11</td>
|
||||
<td>51</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>50</td>
|
||||
<td>47</td>
|
||||
<td>97</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr>
|
||||
|
||||
File form:
|
||||
<form method="POST" enctype="multipart/form-data" action="http://localhost:8082/upload">
|
||||
<input type="file" name="upfile"/>
|
||||
<input type="submit" value="Upload & Transform"/>
|
||||
</form>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,5 @@
|
|||
10 13
|
||||
20 54
|
||||
30 82
|
||||
40 11
|
||||
50 47
|
|
@ -0,0 +1,2 @@
|
|||
<meta charset="utf-8">
|
||||
<a href="/">Back</a>
|
|
@ -0,0 +1,40 @@
|
|||
module main
|
||||
|
||||
import vweb
|
||||
|
||||
const port = 8082
|
||||
|
||||
struct App {
|
||||
vweb.Context
|
||||
}
|
||||
|
||||
fn main() {
|
||||
vweb.run(&App{}, port)
|
||||
}
|
||||
|
||||
pub fn (mut app App) index() vweb.Result {
|
||||
return $vweb.html()
|
||||
}
|
||||
|
||||
['/upload'; post]
|
||||
pub fn (mut app App) upload() vweb.Result {
|
||||
fdata := app.files['upfile']
|
||||
|
||||
data_rows := fdata[0].data.split('\n')
|
||||
|
||||
mut output_data := ''
|
||||
|
||||
for elem in data_rows {
|
||||
delim_row := elem.split('\t')
|
||||
output_data += '${delim_row[0]}\t${delim_row[1]}\t${delim_row[0].int() + delim_row[1].int()}\n'
|
||||
}
|
||||
|
||||
output_data = output_data.all_before_last('\n')
|
||||
|
||||
println(output_data)
|
||||
|
||||
app.add_header('Content-Disposition', 'attachment; filename=results.txt')
|
||||
app.send_response_to_client('application/octet-stream', output_data)
|
||||
|
||||
return $vweb.html()
|
||||
}
|
|
@ -0,0 +1,338 @@
|
|||
In this tutorial I'll demonstrate C2V, a fully automatic C to V source code translator,
|
||||
that has just been released.
|
||||
|
||||
We'll run some simple examples and then translate the entire original DOOM,
|
||||
the legendary shooter, the first ever 3D game released in 1993 and open sourced in 1997.
|
||||
|
||||
A video version of this tutorial is available on YouTube:
|
||||
|
||||
https://www.youtube.com/watch?v=6oXrz3oRoEg
|
||||
|
||||
Let's start.
|
||||
|
||||
### A simple C program
|
||||
|
||||
First, We have a simple C program that prints prime numbers. It's a small program,
|
||||
but it has some of the most widely used language features:
|
||||
function definitions, control flow via for loops and if conditions, a libc function call,
|
||||
and various expressions.
|
||||
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
bool is_prime(int x) {
|
||||
for (int i = 2; i <= x / 2; i++) {
|
||||
if (x % i == 0) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int main() {
|
||||
for (int i = 2; i < 100; i++) {
|
||||
if (is_prime(i)) {
|
||||
printf("%i\n", i);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
We can translate the C file to V by simply running
|
||||
|
||||
```
|
||||
v translate primes.c
|
||||
```
|
||||
|
||||
It will create `primes.v` with the following contents:
|
||||
|
||||
|
||||
```v
|
||||
[translated]
|
||||
module main
|
||||
|
||||
fn is_prime(x int) bool {
|
||||
for i := 2; i <= x / 2; i++ {
|
||||
if x % i == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fn main() {
|
||||
for i := 2; i < 100; i++ {
|
||||
if is_prime(i) {
|
||||
C.printf(c'%i\n', i)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
This is a valid V program, and running it will give us the same result.
|
||||
|
||||
This code is pretty similar to the original, you can notice one major difference.
|
||||
C allows one statement code blocks without brackets, and V doesn't, so in the
|
||||
translated code all blocks are explicitely marked with brackets.
|
||||
|
||||
C2V successfully converts C's bool type to V's bool.
|
||||
|
||||
### Wrapper generation
|
||||
|
||||
C2V also has a wrapper generation mode.
|
||||
|
||||
For many projects it would make more sense not to translate the entire C code base,
|
||||
but just to generate V wrappers on top of C APIs.
|
||||
|
||||
For example, let's say you have some complicated crypto or networking library in C
|
||||
with lots of functions and types, and you don't want to manually type their definitions in V.
|
||||
|
||||
For this C2V has a wrapper mode.
|
||||
|
||||
We have a simple file `usersapi.h`. By running `v translate -wrapper usersapi.c`
|
||||
we get `usersapi.v` with just the function definitions. Function bodies are skipped.
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
|
||||
void usersapi_create_user(char* name, char* password, int age) {
|
||||
// ...
|
||||
puts("User created!");
|
||||
}
|
||||
|
||||
int usersapi_get_number_of_users() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
module usersapi
|
||||
|
||||
fn C.usersapi_create_user(name &i8, password &i8, age int)
|
||||
|
||||
pub fn f create_user(name &i8, password &i8, age int) {
|
||||
C.usersapi_create_user(name, password, age)
|
||||
}
|
||||
|
||||
fn C.usersapi_get_number_of_users() int
|
||||
|
||||
pub fn f get_number_of_users() int {
|
||||
return C.usersapi_get_number_of_users()
|
||||
}
|
||||
```
|
||||
|
||||
C doesn't have modules, so in most C libraries all functions start with the same prefix,
|
||||
the name of the virtual module. C2V helpfully strips those prefixes and generates nice
|
||||
clean wrapper functions that can be called with `usersapi.create_user()` instead of
|
||||
`usersapi.usersapi_create_user()`
|
||||
|
||||
Perhaps in the future C2V will translate C strings (char pointers) to V strings as well.
|
||||
|
||||
### Translating DOOM
|
||||
|
||||
All right, this works, and it's nice, but these are very trivial examples.
|
||||
Let's try something a lot more fun, like the DOOM game.
|
||||
|
||||
|
||||
We think it's very important to have large functioning examples to demonstrate the power
|
||||
of the tools but also to have something to test via CI to avoid regressions.
|
||||
During V's release we had lots of various examples, now for C2V we have DOOM.
|
||||
This is the primary test of C2V.
|
||||
|
||||
DOOM is a very complex project that uses some really tricky elements of the C language.
|
||||
And making sure C2V can always handle DOOM not only serves as a good C2V test but also
|
||||
as a test of the V compiler itself, since it can run entire DOOM in pure V.
|
||||
|
||||
First let's download the DOOM source and build it to make sure everything works.
|
||||
The original 1997 open source release doesn't work nicely on modern OSs, so the community
|
||||
has been working on different forks. I'll be using Chocolate Doom, it's one of the most
|
||||
popular forks, and its goal is to be as close to the original as possible
|
||||
(they even keep the original bugs).
|
||||
|
||||
|
||||
```bash
|
||||
git clone https://github.com/vlang/doom
|
||||
```
|
||||
|
||||
The main difference in this fork is using SDL for cross platform rendering, so we'll need
|
||||
to install SDL2 before we can build it.
|
||||
|
||||
A great thing about DOOM is that it has a very simple structure and is easy to build.
|
||||
It's just a bunch of C files that are compiled into object files and are later linked
|
||||
into a single executable.
|
||||
|
||||
To build the C version:
|
||||
|
||||
```bash
|
||||
cd doom/chocolate-doom
|
||||
cmake .
|
||||
make chocolate-doom
|
||||
```
|
||||
|
||||
We'll also need a freely distributed shareware doom1.wad with game resources and
|
||||
the first free levels.
|
||||
|
||||
Now let's run the compiled game
|
||||
|
||||
```bash
|
||||
./src/chocolate-doom -width 640
|
||||
```
|
||||
|
||||
Great, as expected, it all works, now let's translate it to V and try to build that.
|
||||
|
||||
|
||||
In the previous example we translated a single file, this time we'll translate
|
||||
an entire directory.
|
||||
|
||||
```
|
||||
v translate src/doom
|
||||
```
|
||||
|
||||
This creates a new directory `src/doom/doom_v/`.
|
||||
|
||||
And we can use any name we want, there's a c2v configuration file.
|
||||
|
||||
`ll src/doom/doom_v`
|
||||
|
||||
It has a bunch of files translated from C to V.
|
||||
|
||||
We can open any file, for example g_game.c and verify that it is indeed DOOM code
|
||||
translated from C to V. It's also been nicely formatted by vfmt.
|
||||
|
||||
Now let's build it.
|
||||
|
||||
|
||||
```bash
|
||||
cd src/doom
|
||||
sh build_doom_v.sh
|
||||
```
|
||||
|
||||
I'll explain what we have in the shell script a bit later.
|
||||
|
||||
But first let's run the translated code!
|
||||
|
||||
The script compiles into the `src/doom/doomv` binary. Everything works. We have the music,
|
||||
we can watch the demo, go to the settings, start the game, walk around and shoot enemies.
|
||||
|
||||
There's a slight rendering bug at the bottom, but it can be cleaned up by pressing tab twice
|
||||
and forcing a re-render.
|
||||
|
||||
There's a bug somewhere in C2V, it has just been released and needs some fixes.
|
||||
|
||||
|
||||
Now let's look at those shell scripts and see how we built the V translation of DOOM.
|
||||
|
||||
C2V doesn't understand makefiles yet, so we have to do things manually.
|
||||
|
||||
In the future making such scripts won't be necessary.
|
||||
|
||||
|
||||
Since doom uses C libraries like textscreen, pcsound, and opl, and we are only translating
|
||||
doom itself, we need to build doom.o and then link it with the rest of object files.
|
||||
|
||||
`build_doom_v.sh`:
|
||||
|
||||
```bash
|
||||
export DOOM=~/code/doom/chocolate-doom
|
||||
|
||||
v -o doom_v/doom.o -w -translated doom_v
|
||||
|
||||
|
||||
cc -o doomv \
|
||||
$DOOM/src/doom/doom_v/doom.o \
|
||||
|
||||
$DOOM/src/CMakeFiles/chocolate-doom.dir/*.o \
|
||||
$DOOM/textscreen/CMakeFiles/textscreen.dir/*.o \
|
||||
$DOOM/pcsound/CMakeFiles/pcsound.dir/*.o \
|
||||
$DOOM/opl/CMakeFiles/opl.dir/*.o \
|
||||
$(sdl2-config --libs) -lSDL2_mixer -lSDL2_net -lpng $LDFLAGS
|
||||
```
|
||||
|
||||
The first line compiles all V files in `doom_v/` directory into a single doom.o object file.
|
||||
`-translated` tells V that the code was translated from C, so the compiler is less strict.
|
||||
For example it allows using `i++` as an expression (in V it always must be a statement).
|
||||
In the future C2V will be able to translate such C expressions into valid V code,
|
||||
and `-translated` will be removed entirely.
|
||||
|
||||
After generating doom.o, we can compile the entire project.
|
||||
|
||||
|
||||
The second line links it with the rest of the object files.
|
||||
|
||||
Here we have the shared library used by other games in the chocolate doom project:
|
||||
Hexen, Heretic, and Strife.
|
||||
|
||||
Then textscreen, pcsound, and opl.
|
||||
|
||||
|
||||
In the near future we'll also translate all these C libraries DOOM depends on, and this
|
||||
shell script and interacting with the C compiler won't be needed. The entire project will
|
||||
be runnable with a simple `v translate src/doom && v .`
|
||||
|
||||
|
||||
|
||||
One other thing I wanted to do is to show you that it actually is V code we are running.
|
||||
|
||||
Let's go to `p_enemy.v` and modify the `P_Move` function. We can add a simple println with
|
||||
some V specific code, like a reversed V array.
|
||||
|
||||
|
||||
`println([1, 2, 3, 4, 5].reverse())`
|
||||
|
||||
Let's rebuild it and run again. As expected, we're getting our array printed every time an
|
||||
enemy moves.
|
||||
|
||||
### Replacing C with V file by file
|
||||
|
||||
We translated the entire project. But sometimes it may be better to do more gradual transition
|
||||
from C to V. For example, if you want to move your project from C to V, you might prefer doing
|
||||
it slowly and carefully, file by file. After each file migration you can run tests and see if
|
||||
your application runs correctly.
|
||||
|
||||
You can do that with c2v. All you need to do is just translate a single file, generate an object
|
||||
file, and replace your current C object file with the new V object file.
|
||||
|
||||
We can do this with DOOM too. In fact that's how we made the entire thing work. We were doing it
|
||||
for every single file, one by one, fixing compilation errors, bugs in C2V, and runtime bugs.
|
||||
|
||||
```
|
||||
c2v -o p_enemy.o p_enemy.v
|
||||
cp p_enemy.o ...
|
||||
make clean
|
||||
make chocolate-doom
|
||||
```
|
||||
|
||||
|
||||
It is actually amazing how well it all works considering there have been two translation
|
||||
processes. From C to V via C2V and then From V back to C again via the V compiler.
|
||||
|
||||
In fact we can actually look at the generated C code after all the translation and compare it
|
||||
to the original. Very clean and barely changed:
|
||||
|
||||
![image](https://user-images.githubusercontent.com/687996/175178560-58e04248-bcb3-4e33-aebd-ad66bb94a887.png)
|
||||
|
||||
Both C2V and V generate clean human readable code.
|
||||
|
||||
|
||||
The comments and C defines are missing right now, that's obviously something very nice to have,
|
||||
so they will be translated as well.
|
||||
|
||||
|
||||
### Conclusion
|
||||
|
||||
|
||||
C2V has just been released on June 22th 2022, so it's going to mature with the help of
|
||||
the community. With bug fixes and new C features supported, it should support the
|
||||
entire C standard soon.
|
||||
Luckily C is a relatively small language. Any kind of software written in C will be
|
||||
translatable to V fully automatically.
|
||||
|
||||
I will also be publishing the same demo with Sqlite and Quake translation.
|
||||
|
||||
It's a huge milestone for V and gives V developers access to huge amounts of software
|
||||
written in C.
|
||||
|
||||
We're very excited about this release.
|
|
@ -1130,6 +1130,23 @@ pub fn (s string) contains_any(chars string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// contains_only returns `true`, if the string contains only the characters in `chars`.
|
||||
pub fn (s string) contains_only(chars string) bool {
|
||||
if chars.len == 0 {
|
||||
return false
|
||||
}
|
||||
for ch in s {
|
||||
mut res := 0
|
||||
for i := 0; i < chars.len && res == 0; i++ {
|
||||
res += int(ch == unsafe { chars.str[i] })
|
||||
}
|
||||
if res == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// contains_any_substr returns `true` if the string contains any of the strings in `substrs`.
|
||||
pub fn (s string) contains_any_substr(substrs []string) bool {
|
||||
if substrs.len == 0 {
|
||||
|
|
|
@ -447,6 +447,13 @@ fn test_contains_any() {
|
|||
assert !''.contains_any('')
|
||||
}
|
||||
|
||||
fn test_contains_only() {
|
||||
assert '23885'.contains_only('0123456789')
|
||||
assert '23gg885'.contains_only('01g23456789')
|
||||
assert !'hello;'.contains_only('hello')
|
||||
assert !''.contains_only('')
|
||||
}
|
||||
|
||||
fn test_contains_any_substr() {
|
||||
s := 'Some random text'
|
||||
assert s.contains_any_substr(['false', 'not', 'rand'])
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
module os
|
||||
|
||||
fn test_find_abs_path_of_executable() ? {
|
||||
tfolder := join_path(temp_dir(), 'v', 'tests', 'filepath_test')
|
||||
rmdir_all(tfolder) or {}
|
||||
assert !is_dir(tfolder)
|
||||
mkdir_all(tfolder)?
|
||||
defer {
|
||||
rmdir_all(tfolder) or {}
|
||||
}
|
||||
//
|
||||
original_path := getenv('PATH')
|
||||
original_wdir := getwd()
|
||||
defer {
|
||||
chdir(original_wdir) or {}
|
||||
}
|
||||
//
|
||||
new_path := tfolder + path_delimiter + original_path
|
||||
setenv('PATH', new_path, true)
|
||||
//
|
||||
mut myclang_file := 'myclang'
|
||||
$if windows {
|
||||
myclang_file += '.bat'
|
||||
}
|
||||
//
|
||||
chdir(tfolder)?
|
||||
write_file(myclang_file, 'echo hello')?
|
||||
chmod(myclang_file, 0o0777)?
|
||||
dump(real_path(myclang_file))
|
||||
dump(is_executable(myclang_file))
|
||||
defer {
|
||||
rm(myclang_file) or {}
|
||||
}
|
||||
//
|
||||
fpath := find_abs_path_of_executable('myclang') or {
|
||||
assert false
|
||||
return
|
||||
}
|
||||
dump(fpath)
|
||||
//
|
||||
setenv('PATH', original_path, true)
|
||||
if x := find_abs_path_of_executable('myclang') {
|
||||
eprintln('> find_abs_path_of_executable should have failed, but instead it found: $x')
|
||||
assert false
|
||||
}
|
||||
}
|
|
@ -404,7 +404,7 @@ pub fn is_executable(path string) bool {
|
|||
// 04 Read-only
|
||||
// 06 Read and write
|
||||
p := real_path(path)
|
||||
return exists(p) && p.ends_with('.exe')
|
||||
return exists(p) && (p.ends_with('.exe') || p.ends_with('.bat') || p.ends_with('.cmd'))
|
||||
}
|
||||
$if solaris {
|
||||
statbuf := C.stat{}
|
||||
|
|
|
@ -12,6 +12,8 @@ pub const (
|
|||
args = []string{}
|
||||
)
|
||||
|
||||
const executable_suffixes = ['']
|
||||
|
||||
fn get_path_delimiter() string {
|
||||
delimiter := ':'
|
||||
$if js_node {
|
||||
|
|
37
vlib/os/os.v
37
vlib/os/os.v
|
@ -452,27 +452,34 @@ fn error_failed_to_find_executable() IError {
|
|||
return IError(&ExecutableNotFoundError{})
|
||||
}
|
||||
|
||||
// find_exe_path walks the environment PATH, just like most shell do, it returns
|
||||
// find_abs_path_of_executable walks the environment PATH, just like most shell do, it returns
|
||||
// the absolute path of the executable if found
|
||||
pub fn find_abs_path_of_executable(exepath string) ?string {
|
||||
if exepath == '' {
|
||||
return error('expected non empty `exepath`')
|
||||
}
|
||||
if is_abs_path(exepath) {
|
||||
return real_path(exepath)
|
||||
}
|
||||
mut res := ''
|
||||
path := getenv('PATH')
|
||||
paths := path.split(path_delimiter)
|
||||
for p in paths {
|
||||
found_abs_path := join_path_single(p, exepath)
|
||||
if exists(found_abs_path) && is_executable(found_abs_path) {
|
||||
res = found_abs_path
|
||||
break
|
||||
|
||||
for suffix in executable_suffixes {
|
||||
fexepath := exepath + suffix
|
||||
if is_abs_path(fexepath) {
|
||||
return real_path(fexepath)
|
||||
}
|
||||
mut res := ''
|
||||
path := getenv('PATH')
|
||||
paths := path.split(path_delimiter)
|
||||
for p in paths {
|
||||
found_abs_path := join_path_single(p, fexepath)
|
||||
$if trace_find_abs_path_of_executable ? {
|
||||
dump(found_abs_path)
|
||||
}
|
||||
if exists(found_abs_path) && is_executable(found_abs_path) {
|
||||
res = found_abs_path
|
||||
break
|
||||
}
|
||||
}
|
||||
if res.len > 0 {
|
||||
return real_path(res)
|
||||
}
|
||||
}
|
||||
if res.len > 0 {
|
||||
return real_path(res)
|
||||
}
|
||||
return error_failed_to_find_executable()
|
||||
}
|
||||
|
|
|
@ -1,41 +1,107 @@
|
|||
module os
|
||||
|
||||
struct C.AAsset {
|
||||
#include <asset_manager.h>
|
||||
#include <asset_manager_jni.h>
|
||||
|
||||
pub enum AssetMode {
|
||||
buffer = C.AASSET_MODE_BUFFER // Caller plans to ask for a read-only buffer with all data.
|
||||
random = C.AASSET_MODE_RANDOM // Read chunks, and seek forward and backward.
|
||||
streaming = C.AASSET_MODE_STREAMING // Read sequentially, with an occasional forward seek.
|
||||
unknown = C.AASSET_MODE_UNKNOWN // No specific information about how data will be accessed.
|
||||
}
|
||||
|
||||
struct C.ANativeActivity {
|
||||
assetManager &AssetManager // Pointer to the Asset Manager instance for the application.
|
||||
clazz voidptr // (jobject) The NativeActivity object handle.
|
||||
env voidptr // (JNIEnv *) JNI context for the main thread of the app.
|
||||
externalDataPath &char // Path to this application's external (removable/mountable) data directory.
|
||||
instance voidptr // This is the native instance of the application.
|
||||
internalDataPath &char // Path to this application's internal data directory.
|
||||
obbPath &char // Available starting with Honeycomb: path to the directory containing the application's OBB files (if any).
|
||||
sdkVersion int // The platform's SDK version code.
|
||||
vm voidptr // (JavaVM *) The global handle on the process's Java VM.
|
||||
}
|
||||
|
||||
// NativeActivity defines the native side of an android.app.NativeActivity.
|
||||
pub type NativeActivity = C.ANativeActivity
|
||||
|
||||
struct C.AAssetManager {
|
||||
}
|
||||
|
||||
struct C.ANativeActivity {
|
||||
assetManager voidptr
|
||||
}
|
||||
// AssetManager provides access to an application's raw assets by creating Asset objects.
|
||||
pub type AssetManager = C.AAssetManager
|
||||
|
||||
fn C.AAssetManager_open(&C.AAssetManager, &char, int) &C.AAsset
|
||||
|
||||
// open opens an Android `Asset`
|
||||
pub fn (am &AssetManager) open(filename string, mode AssetMode) !&Asset {
|
||||
asset := C.AAssetManager_open(am, filename.str, int(mode))
|
||||
if isnil(asset) {
|
||||
return error('file `$filename` not found')
|
||||
}
|
||||
return asset
|
||||
}
|
||||
|
||||
struct C.AAsset {
|
||||
}
|
||||
|
||||
pub type Asset = C.AAsset
|
||||
|
||||
fn C.AAsset_getBuffer(&C.AAsset) voidptr
|
||||
|
||||
// get_buffer returns a pointer to a buffer holding the entire contents of the asset.
|
||||
pub fn (a &Asset) get_buffer() voidptr {
|
||||
return C.AAsset_getBuffer(a)
|
||||
}
|
||||
|
||||
fn C.AAsset_getLength(&C.AAsset) int
|
||||
|
||||
fn C.AAsset_read(&C.AAsset, voidptr, int) int
|
||||
// get_length returns the total size of the asset data.
|
||||
pub fn (a &Asset) get_length() int {
|
||||
return C.AAsset_getLength(a)
|
||||
}
|
||||
|
||||
fn C.AAsset_getLength64(&C.AAsset) i64
|
||||
|
||||
// get_length_64 returns the total size of the asset data.
|
||||
// get_length_64 returns the size using a 64-bit number insted of 32-bit as `get_length`.
|
||||
pub fn (a &Asset) get_length_64() i64 {
|
||||
return C.AAsset_getLength64(a)
|
||||
}
|
||||
|
||||
fn C.AAsset_read(&C.AAsset, voidptr, usize) int
|
||||
|
||||
// read attempts to read 'count' bytes of data from the current offset.
|
||||
// read returns the number of bytes read, zero on EOF, or < 0 on error.
|
||||
pub fn (a &Asset) read(buffer voidptr, count usize) int {
|
||||
return C.AAsset_read(a, buffer, count)
|
||||
}
|
||||
|
||||
fn C.AAsset_close(&C.AAsset)
|
||||
|
||||
pub fn read_apk_asset(file string) ?[]u8 {
|
||||
// close closes the asset, freeing all associated resources.
|
||||
pub fn (a &Asset) close() {
|
||||
C.AAsset_close(a)
|
||||
}
|
||||
|
||||
// read_apk_asset returns all the data located at `path`.
|
||||
// `path` is expected to be relative to the `assets`
|
||||
pub fn read_apk_asset(path string) ![]u8 {
|
||||
$if apk {
|
||||
act := &C.ANativeActivity(C.sapp_android_get_native_activity())
|
||||
act := &NativeActivity(C.sapp_android_get_native_activity())
|
||||
if isnil(act) {
|
||||
return error('Could not get reference to Android activity')
|
||||
return error('could not get reference to Android activity')
|
||||
}
|
||||
asset := C.AAssetManager_open(&C.AAssetManager(act.assetManager), file.str, C.AASSET_MODE_STREAMING)
|
||||
if isnil(asset) {
|
||||
return error('File `$file` not found')
|
||||
}
|
||||
len := C.AAsset_getLength(asset)
|
||||
asset_manager := &AssetManager(act.assetManager)
|
||||
asset := asset_manager.open(path, .streaming)!
|
||||
len := asset.get_length()
|
||||
buf := []u8{len: len}
|
||||
for {
|
||||
if C.AAsset_read(asset, buf.data, len) > 0 {
|
||||
if asset.read(buf.data, usize(len)) > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
C.AAsset_close(asset)
|
||||
asset.close()
|
||||
return buf
|
||||
} $else {
|
||||
return error(@FN + ' can only be used with APK/AAB packaged Android apps')
|
||||
|
|
|
@ -17,6 +17,8 @@ pub const (
|
|||
path_delimiter = ':'
|
||||
)
|
||||
|
||||
const executable_suffixes = ['']
|
||||
|
||||
const (
|
||||
stdin_value = 0
|
||||
stdout_value = 1
|
||||
|
|
|
@ -14,6 +14,8 @@ fn C.CreateHardLinkW(&u16, &u16, C.SECURITY_ATTRIBUTES) int
|
|||
|
||||
fn C._getpid() int
|
||||
|
||||
const executable_suffixes = ['.exe', '.bat', '.cmd', '']
|
||||
|
||||
pub const (
|
||||
path_separator = '\\'
|
||||
path_delimiter = ';'
|
||||
|
|
|
@ -657,10 +657,10 @@ pub fn (mut g Gen) init() {
|
|||
g.cheaders.writeln('#include <stddef.h>')
|
||||
} else {
|
||||
tcc_undef_has_include := '
|
||||
#if defined(__TINYC__) && defined(__has_include)
|
||||
// tcc does not support has_include properly yet, turn it off completely
|
||||
#undef __has_include
|
||||
#endif'
|
||||
#if defined(__TINYC__) && defined(__has_include)
|
||||
// tcc does not support has_include properly yet, turn it off completely
|
||||
#undef __has_include
|
||||
#endif'
|
||||
g.cheaders.writeln(tcc_undef_has_include)
|
||||
g.includes.writeln(tcc_undef_has_include)
|
||||
if g.pref.os == .freebsd {
|
||||
|
|
|
@ -111,7 +111,9 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) {
|
|||
is_var_mut := expr.is_auto_deref_var()
|
||||
str_fn_name := g.get_str_fn(typ)
|
||||
if is_ptr && !is_var_mut {
|
||||
g.write('str_intp(1, _MOV((StrIntpData[]){{_SLIT("&"), $si_s_code ,{.d_s=')
|
||||
g.write('str_intp(1, _MOV((StrIntpData[]){{_SLIT("&"), $si_s_code ,{.d_s = isnil(')
|
||||
g.expr(expr)
|
||||
g.write(') ? _SLIT("nil") : ')
|
||||
}
|
||||
g.write('${str_fn_name}(')
|
||||
if str_method_expects_ptr && !is_ptr {
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import os
|
||||
|
||||
const vexe = os.getenv('VEXE')
|
||||
|
||||
const vroot = os.dir(vexe)
|
||||
|
||||
fn test_pkgconfig_can_be_compiled() ? {
|
||||
tmp_exe := os.join_path(os.temp_dir(), '${os.getpid()}_pkgconfig.exe')
|
||||
pkgconfig_v_file := os.real_path(os.join_path(vroot, 'vlib/v/pkgconfig/bin/pkgconfig.v'))
|
||||
assert !os.exists(tmp_exe)
|
||||
res := os.system('${os.quoted_path(vexe)} -o ${os.quoted_path(tmp_exe)} ${os.quoted_path(pkgconfig_v_file)}')
|
||||
if res != 0 {
|
||||
assert false
|
||||
}
|
||||
assert os.exists(tmp_exe)
|
||||
os.rm(tmp_exe)?
|
||||
}
|
|
@ -3,7 +3,7 @@ module pkgconfig
|
|||
import flag
|
||||
import strings
|
||||
|
||||
struct Main {
|
||||
pub struct Main {
|
||||
pub mut:
|
||||
opt &MainOptions
|
||||
res string
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
struct Node {
|
||||
val int
|
||||
left &Node
|
||||
right &Node
|
||||
}
|
||||
|
||||
fn test_string_ref_struct() {
|
||||
n := Node{123, 0, 0}
|
||||
println(n.left)
|
||||
assert '$n.left' == '&nil'
|
||||
}
|
Loading…
Reference in New Issue