# V builds itself in under a second, and that self-build fetches a second compiler

> V is a MIT-licensed compiled language whose main backend emits human-readable C and whose toolchain builds from source in three commands. The interesting parts are the bootstrap compiler it pulls from another repository, the Makefile's architecture-specific flag handling, and a pre-1.0 promise about API stability that has not arrived yet.

**vlang/v** — Simple, fast, safe, compiled language for developing maintainable software. Compiles itself in <1s with zero library dependencies. Supports automatic C => V translation.

- Repository: https://github.com/vlang/v
- Stars: 37,919 · Forks: 2,290
- Language: V
- License: MIT
- Published: 2026-08-08 · Updated: 2026-08-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/vlang-v

## The build target downloads a bootstrap compiler out of a second repository

Building V from source is three commands, and the preferred method is a shallow clone followed by make. What make actually does first is not obvious from that. The default target is all, and all depends on download_vc and v, so the build reaches out to the network before it compiles anything. The download_vc target checks for an existing vc/v.c; if it is there and git is available, it runs a rebase pull against the vc directory, and if it is not there it clones github.com/vlang/vc into vc/ with a blob filter. The failure branch is explicit: without git it echoes that git is required to download vc and exits 1. So a machine with a compiler but no git cannot bootstrap V at all, and a build that looks local is in fact pulling source from a second repository every time. The claim of zero library dependencies refers to V's own code, not to the toolchain that produces it, and the compiler binary it uses to compile itself is a separate project you do not have yet at that point in the script.

```bash
git clone --depth=1 https://github.com/vlang/v
cd v
make
```

The result is an executable called v sitting at the root of the checkout, and the path to that checkout can be anywhere, so the install is really a clone rather than a package. Updating later means running v up, which refreshes the checkout and also refreshes the bundled TCC binaries used for fast C compilation. That is worth noticing: update means track the tip of the branch, not upgrade to a tagged release.

## The Makefile calls -O2 unsafe and quietly drops those flags on arm

The Makefile is a shell script wearing a makefile costume, and it contains the sort of logic that bites on one architecture and not another. CFLAGS is split in a for loop: the exact strings -O, -O0 and -O1 are appended to the compiler flags as they are, while any other flag starting with -O sets a variable called unsafe_o to 1. Then the script switches on uname. On Linux, an architecture matching arm or arm64 sets a linker flag of -latomic, and if unsafe_o is also set the bootstrap compiler flags are cleared entirely, so the level chosen for the generated C is not the level the bootstrap compiler is built with. On FreeBSD, NetBSD and OpenBSD the script appends -lexecinfo instead. The practical consequence is that the same command line behaves differently on your x86 laptop and on an arm build machine, and a project that compiles locally can fail on hardware you did not test. There is a defined constant for this too, VC_BOOTSTRAP_DEFINE set to a v1_fallback define, with a comment explaining that portable compiler snapshots do not embed the newer generation and must be kept on the full compatibility path.

```make
CC ?= cc
VEXE ?= ./v
VFLAGS ?=
CFLAGS ?=
LDFLAGS ?=

# Portable VC snapshots do not embed V3. Keep their v1 executable on the full
# compatibility compiler path even when the generated C is built on a V3 host.
VC_BOOTSTRAP_DEFINE = -DCUSTOM_DEFINE_v1_fallback

all: download_vc v
```

## docker build compiles a fresh clone unless you pass USE_LOCAL

The container recipe looks routine until you read the conditional. It starts from buildpack-deps:buster-curl, installs gcc, clang, make, git and binutils, and copies the build context into /vlang-local. Then it declares a build argument named USE_LOCAL and branches on it: when the argument is empty, which is the default, the recipe clones github.com/vlang/v into the working directory and deletes the copy it just made from your context; when the argument is set, it moves the local files into place instead. The upshot is that a plain docker build in a modified checkout produces a compiler built from upstream master, and the binary you get back has nothing to do with your edits unless you pass the build argument. That is a quiet way to spend an afternoon, since the image builds successfully and the symptom is a V that does not have your change. The base image is also an older Debian buster line rather than a current one, so the toolchain inside the container is not the toolchain on your machine. The image finishes with make, a symlink from /opt/vlang/v into /usr/local/bin, and a default command of v.

```dockerfile
#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 update && \
    DEBIAN_FRONTEND=noninteractive apt install -y --no-install-recommends gcc clang make git binutils && \
    apt clean && rm -rf /var/cache/apt/archives/* && \
    rm -rf /var/lib/apt/lists/*

COPY . /vlang-local

RUN if [ -z "${USE_LOCAL}" ] ; then \
      git clone --depth=1 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" ]
```

## Pre-1.0 means the os module is named as the thing that will still move

There is a section on stability, and it is worth reading closely because it makes two very different promises. The first is present tense: the language is described as being at an early development stage yet relatively stable and not changing often, with most syntax changes handled automatically by vfmt rather than by you. The second is conditional: there will be changes before 1.0, the core APIs, primarily the os module, will have minor changes until they are stabilised in 1.0, and after 1.0 the language enters a feature freeze with no breaking changes, only bug fixes and performance improvements, compared explicitly to Go. A further line says a V 2.0 is not expected within a decade after 1.0, perhaps not ever. Every reassuring sentence in the second group is about a version that does not exist. The concrete risk is named rather than implied: if you build your file handling, process control, and paths on the os module, you are building on the one module the project has singled out as still changing, and the guarantee against breakage begins at a release date that has not arrived.

```bash
v up
```

## Undefined behaviour is on the safety list with a wip next to it

The safety claim is stated in four parts: no null, no globals, no undefined behavior, and immutability by default. Three of those are unconditional. The third carries a qualifier in parentheses, work in progress, which changes what you can build on. If your design assumes the compiler will stop you from reading uninitialised memory or from relying on signed overflow, you are relying on the one item on that list that is explicitly unfinished, and nothing in the documentation tells you which subset is already enforced. Immutability is qualified in the other direction, by the word default, so it is a property you can turn off rather than a guarantee. The rest of the language story sits alongside this: null is simply not in the type system, and the main backend compiles to human-readable C, which is a deliberate trade where you can read exactly what your program asked the C compiler to do, and can also read what it did not ask for.

```bash
# Void Linux
# xbps-install -Su base-devel
# xbps-install libatomic-devel
$ git clone --depth=1 https://github.com/vlang/v
$ cd v
$ make
```

## Memory management is a compile-time choice, and -gc none is yours to own

V does not pick one memory model, it offers four and makes you name it at compile time. The garbage collector is the default. Manual management is opt-in with v -gc none. Arena allocation comes from v -prealloc, and autofree from v -autofree. Each of those changes what the compiler inserts, so the choice is a property of the build rather than of a function call, and switching later means rebuilding and re-auditing rather than changing a flag in one place. The default is the safe-looking choice and the manual mode is the fast one, and the gap between them is where your allocation discipline either exists or does not. There is a tell in the static build example: it compiles a hello world with -gc none, which is the shape of a program where removing the collector costs nothing, and that is precisely why it demonstrates the flag. Turning the collector off also drops a build dependency, since the garbage collected path needs the Boehm GC C library that has to be installed separately on FreeBSD, OpenBSD, and Termux.

```bash
# FreeBSD
pkg install boehm-gc-threaded gmake
git clone --depth=1 https://github.com/vlang/v
cd v
gmake
```

## Every platform has its own prerequisite list, from Void to Termux

The claim that installing V is quite simple holds when you have a working git installation, and the rest of the page is a list of where that stops being true. On Windows you run makev.bat instead of make, from CMD or as ./makev.bat in PowerShell. On FreeBSD, OpenBSD, NetBSD, DragonFly and Solaris you install GNU make and invoke it as gmake. On Ubuntu and Debian you may need sudo apt install git build-essential make first. Void Linux wants base-devel and libatomic-devel through xbps. OpenBSD, named at release 7.8, additionally wants openssl%3.5 alongside boehm-gc. Termux on Android wants clang, libexecinfo, libgc, libgc-static, tcc, make and git, then a final ./v symlink, with an explicit note that sudo is not needed there because it is not installed. Nothing here goes through a system package manager for V itself. The preferred method is a source build on every platform, and the only per-platform difference is which C toolchain and which garbage collector library have to be present first.

```bash
pkg install clang libexecinfo libgc libgc-static tcc make git
pkg update
git clone --depth=1 https://github.com/vlang/v
cd v
make
./v symlink
```

## The branch runs well ahead of the last tagged release

Version numbers and branch activity are moving at different speeds. The tagged releases are 0.5.2 from 2026-07-12 and 0.5.1 from 2026-03-09, four months apart, and alongside them sits a tag named weekly.2026.08 from 2026-02-17, which is a different naming scheme entirely and signals a rolling build line rather than a point release. The last push to the default branch is dated 2026-09-27, more than two months after the newest numbered release. So there are two ways to be current in V and they are not the same thing: a pinned 0.5.x release, or the tip of master, which is what you get from a shallow clone and what v up keeps you on. Because v up refreshes the checkout rather than moving you to a release, updating is a decision to track unreleased changes, and the documentation does not present it as anything else. The repository root backs this up, with a CHANGELOG.md, a ROADMAP.md, a separate changelogs0.x directory, an AGENTS.md, and both a Makefile and a GNUmakefile for the two make implementations the platform notes ask you to choose between.

## Conclusion

V is a reasonable thing to reach for when you want C-like speed, a self-hosting toolchain you can read end to end, and a standard library that ships an ORM, a web framework, a UI layer, and a compiler backend. It is not yet a safe bet on API stability, because the project states plainly that core APIs, the os module above all, will keep changing until 1.0, and the safety list still marks undefined behaviour as work in progress. Before you commit, run the build on your actual target architecture, since the Makefile treats optimisation flags differently on arm, and read the Dockerfile before you trust docker build, which by default compiles a fresh clone rather than your working tree.

## FAQ

### What does vlang mean?

vlang is the domain behind the V programming language, whose source lives in the vlang/v repository and whose homepage is vlang.io. The project describes V as a simple, fast, safe compiled language for maintainable software, with a main backend that emits human-readable C, and it is MIT licensed.

### v lang vs rust

No comparison with Rust is published in the project's own documentation. V is set against C instead, with runtime performance described as as fast as C, compilation around 110k lines per second using a Clang backend and around 500k using the native and tcc backends on an Intel i5-7500 with an SSD and no optimization, and self-compilation in under a second. No benchmark against another language is given.

### How do you install the V compiler?

From source, which the project calls the preferred method: clone with git clone --depth=1 https://github.com/vlang/v, change into the v directory, and run make. The executable is the v file at the root of the checkout, and you can then run ./v run examples/hello_world.v. On Windows use makev.bat instead of make, and on the BSDs and Solaris invoke GNU make as gmake.

### What is the current release status of V?

The project is pre-1.0. The tagged releases are 0.5.2 from 2026-07-12 and 0.5.1 from 2026-03-09, with a rolling tag named weekly.2026.08 from 2026-02-17, while the last push to master is dated 2026-09-27. Core APIs, primarily the os module, are stated to still receive minor changes until 1.0.

## Sources

- [Official README](https://github.com/vlang/v#readme)
- [Project repository](https://github.com/vlang/v)
- [Release notes](https://github.com/vlang/v/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/vlang-v
