The velt command
One binary is the whole toolchain: compiler, runner, test runner, formatter, package manager,
language server and documentation generator. velt <command> --help (or velt help <command>)
prints every command's options and examples.
| Command | What it does |
|---|---|
velt new <name> | create a package in a new directory from a template |
velt init | turn the current directory into a package; --editor vscode only adds the VS Code files |
velt build | compile a file or the current package |
velt run | build and run a file or the current package |
velt check [--json] | type-check a file or the current package without building it (velt check); --ts-compat lints code shared with TypeScript (below) |
velt dev | run, then hot-swap or restart on every change (velt dev) |
velt test | run the tests, skipping files whose kept result can't have changed (velt test, Testing) |
velt fmt | format .vlt (and .ts, .tsx) files (Formatter) |
velt clean [--cache] | remove the package's target/ directory; --cache: the machine's build cache |
velt cache warm | compile the standard library into the build cache ahead of a first build |
velt add, install, update, publish, native build | packages (Packages) |
velt manifest [--json] | check the package's manifest, or print it as JSON for other tools (package.vlt) |
velt search, yank, owner | find and manage published packages (Registries) |
velt login, logout | store or forget your token for a registry server (Users, owners and yanking) |
velt doc | generate HTML API documentation, JSON for other tools, or the standard library's TypeScript declarations (velt doc) |
velt lsp | the language server (Editors) |
velt playground | write and run programs in the browser (WebAssembly) |
velt registry serve, registry user, registry owner | serve a package registry over HTTP, and manage its users and owners (Packages) |
velt target list, add, remove | the targets velt build --target builds for: install or remove target packs (Cross-compiling) |
velt toolchain list, install, remove, self-update, prune, default, which, link, unlink | the velt versions installed side by side, and which one runs (velt toolchain) |
velt doctor | check the installation and run a hello world |
velt completions <shell> | print a completion script for bash, zsh, fish or PowerShell |
velt help <command> | the same as velt <command> --help |
Files and packages
Every build command works on a single file or on a package:
A file
velt run hello.vlt builds ./target/velt/hello (hello.exe on Windows),
relative to the current directory, and runs it; a file inside a package is built into that
package's target/velt/, wherever velt runs, so the members of a
workspace keep their programs (and build stamps)
apart. The file, and the modules it imports, may also
be .ts or .tsx files (TypeScript files). If the file is inside a package, the package's
dependencies are installed first.
- A package: without a file argument,
veltsearches upward from the current directory forpackage.vltand builds the package's entry (defaultsrc/main.vlt) to<package>/target/velt/<name>. A library-only package can't be run.
Workspaces
In a workspace, velt check, test, build, fmt and doc take
--workspace, -p <glob> (repeatable; a member's name, or a scoped name without its scope) and
--affected [<base>] to run on several members, and
run on every member at the workspace's root outside its packages; --fail-fast starts no more
members after a failure. velt run -p <member> and velt dev -p <member> run one member's
program, with the program's arguments after -- (velt run -p server -- --port 8080). A file
argument doesn't go with a selection.
velt build and velt run
velt build [<file.vlt>] [-o <out>] [--release] [-g] [--target <triple>] [--cpu <name>]
[--backend cranelift|llvm] [--emit vir|llvm|obj|exe] [--locked] [--json] [-v]
[--timings] [--report numbers]
velt run [<file.vlt>] [--release] [-g] [--target <triple>] [--cpu <name>]
[--backend cranelift|llvm] [--locked] [-v] [--timings] [--report numbers]
[-- <program args>...]-
Debug builds (the default) use Cranelift: fast to compile, with line tables for debuggers on Linux and macOS (function symbols on Windows).
-
--releaseruns Velt's optimizer, then LLVM-O3when clang 16 or newer is found (VELT_CLANG,PATH, the standard install directories); otherwise Cranelift, with a one-line note.-gkeeps debug info in a release build (Debugging). An LLVM build (--release, or--backend llvm) of a large program (above about 32 000 VIR statements) is split into codegen units that clang compiles in parallel, one object file each, which builds it several times faster on a multi-core machine. Functions that call each other, and small helpers, stay in their caller's unit, so in the measurements split programs run as fast as unsplit ones (within ±3 %, one faster). Smaller programs are one unit.VELT_CODEGEN_UNITS=Nasks for N units (at most the core count);VELT_CODEGEN_UNITS=1turns splitting off. -
--backend llvmwithout--releasegives an unoptimized build with full line information. -
Nothing is compiled twice (below): a build whose inputs did not change since the last build of the same output only links (or does nothing), and code compiled once on a machine is taken from the build cache by every later build, in any project.
-
--target wasm32-wasip1and--target wasm32-unknown-unknownbuild WebAssembly (WebAssembly);--target x86_64-apple-darwincross-builds on Apple silicon. -
--cpu <name>chooses the CPU the program is built for:baseline,x86-64-v2(the default on x86_64: every x86_64 CPU since about 2009),x86-64-v3,x86-64-v4,native(this machine's CPU) or a CPU name clang knows for the target, such asznver4orneoverse-n1(an unknown one is an error that names the closest). Without it,$VELT_CPUand then the package'sbuild.cpudecide;velt runandvelt testwithout--releasebuild for this machine's CPU. A program built for x86-64-v2 or above checks the CPU when it starts and names what is missing instead of crashing. Levels, defaults per target and the check: Platforms, CPU. -
--emit vir/--emit llvmprint the intermediate representation and stop;--emit objwrites only the object file. -
-vprints per-stage timings;--timingsadds each optimizer pass and code generation step. -
build --jsonprints one JSON document on stdout instead of diagnostics on stderr, for editors (the VS Code extension's F5 runs it):{"executable", "debugInfo", "lldbScript", "diagnostics", "errors", "warnings"}.executableis the absolute path of the program (nullif the build failed or made none, as with--emit obj);debugInfosays whether it has line information for debuggers (falsefor the default build on Windows, which has none yet);lldbScriptis the toolchain's LLDB script for Velt values (share/velt/lldb/velt_lldb.py,nullif missing); the diagnostics are those ofvelt check --json. It exits likevelt build. -
--report numberslists, on stderr, thenumbervariables assigned inside loops that the optimizer keeps as doubles, with the first reason it found for not storing each one as an integer:it may hold a fractionit may be NaN-
errorit may reach ±2^53 (values in [lo, hi]): no loop bound or `| 0` limits it
it is set from a division, a call or memoryit may be -0, which line N can tell from 0(orwhich a use can tell from 0)it is set from a value that is not a 32-bit integerit is a parameter, a call result or stored in memory
A variable that is only converted from an integer and used as a double is not listed: as an integer it would only add conversions. A
numberthat is whole and bounded is stored as an integer, with exactly the double's results:Textnumbers: 1 `number` variable in loops stays doubles grid.vlt:7:7: `s`: it may hold a fraction -
runexits with the program's exit code. As withnode file.js a b, the arguments after the file go to the program (velt run app.vlt --port 8080), so options for Velt come before the file. Without a file (the package's entry) program arguments follow--(velt run -- --port 8080); a--right after a file is accepted too. Compile errors exit with 1 and run nothing.
Build cache
A build never compiles the same thing twice:
- Nothing changed.
velt build,velt runandvelt testrecord what a build read in a stamp beside the program (<program>.build-stamp): every source file (std, packages,.d.tsfiles) by content, every path imports were looked up at and every directory listed for them,package.vlt,package.json,tsconfig.jsonandvelt.lock.json, the build's options and the compiler. When none of that changed, the next build parses and compiles nothing; running the program again costs only its start. Saving a file without changing it changes nothing either; a file changed while a build read it counts as changed. A new file that an import would now find (a.tsbeside a.vlt, a package in anode_modulesnearer the importer) counts as a change. A build that printed warnings gets no stamp, so the warnings show every time.VELT_BUILD_STAMP=offcompiles every time. - Something changed. The code each function compiles to is kept in a cache that every build
on the machine shares, whatever the project:
$VELT_HOME/build-cache(~/.velt/build-cache), orVELT_BUILD_CACHE. A debug build compiles only the functions whose code differs from any compiled before, on every core; std's functions, a package's, and generic instances such asMap<string, number>compile once per machine. A release build takes each codegen unit's object from the cache when clang compiled the same code with the same flags before. - Entries are found by their contents, the target, the CPU and the version of what compiled them, so they are never out of date, and projects pinned to different Velt versions keep their entries side by side.
- The cache stays under
VELT_BUILD_CACHE_SIZE(default2G) of disk space: about once an hour, a build that added code removes the code used least recently until the cache is under three quarters of that. Small entries (most compiled functions) are packed into a few files instead of a file each, so they take the space of their contents.velt clean --cacheremoves it all (only what the cache made: aVELT_BUILD_CACHEdirectory that holds other files keeps them);VELT_BUILD_CACHE=offbuilds without it. - Entries are used as they are, so a
VELT_BUILD_CACHEdirectory must be writable only by users you trust: whoever can write to it can put code into your builds. - Warm from the start. Installing a Velt version (the install scripts,
velt toolchain install, or the launcher installing the version a package pins) startsvelt cache warmin the background at low priority: it compiles a set of programs shipped with the toolchain (share/velt/warm/) that reach most of std, for this machine's CPU (whatvelt runandvelt testbuild for) and the default one (velt build). A project's first build then compiles little more than its own code. It runs once per Velt version;velt cache warmruns it in the foreground,--backgrounddetached. The installers skip it in CI (CIset), where the cache goes away with the runner, and whenVELT_CACHE_WARM=off. - Std compiled once (experimental). With
VELT_STD_OBJECT=1, a release build compiles each larger std function it uses on its own, stores it in the build cache, and links it; the next program that compiles the same std function links it from the cache instead of compiling it again.VELT_STD_OBJECT_STATS=1shows what it found, compiled and left in the program. - Std parsed once. Every command that loads a program (
check,build,run,test, the language server) keeps the standard library's parsed files in the build cache, so a program parses only its own files and its packages'. An edited std file (aVELT_STDcheckout) is parsed again: entries are found by the file's contents and the compiler. velt build -vshows how much came from the cache (codegen ... (637 of 637 functions from the build cache)) and when a build was up to date.
velt check
velt check [<file.vlt>] [--json] [--locked] [-v]
velt check --ts-compat [<file|dir>...] [--json] [--locked] [-v]
velt check --std [--json] [-v]Parses and type-checks a file with every file it imports, or the whole current package, and prints
the diagnostics like velt build, but builds nothing, so it answers in tens of milliseconds.
It exits with 0 when there are no errors (warnings are allowed) and with 1 when there are.
- A library module needs no
main:velt check lib.vltchecks every function in it, including exported functions nothing calls.velt buildandvelt runneed a program: a root file with top-level statements or amainfunction (Scripts); otherwise they stop witherror: `main` function not found in the root module. - In a package,
velt checkwithout a file checks the whole package, liketscchecks a project: every.vlt,.tsand.tsxmodule undersrc/andtests/(recursively, skippingtarget/,node_modules/, hidden and symlinked directories, and nested packages: a directory with its ownpackage.vltis that package's, so check it there;.d.tsfiles are not modules), includingsrc/lib.vltnext tosrc/main.vlt, modules nothing imports, and test files. The entry (package.entry, defaultsrc/main.vlt) must be a program: top-level statements or a validmain; every other module is checked as a library module. A library package (no configured entry and nosrc/main.vlt) checkssrc/lib.vltand the rest the same way. All modules are checked together, so a module several of them import is checked, and its errors reported, once. Two files in one directory whose names differ only in the extension (src/dup.vltandsrc/dup.ts) are an error: an import can't tell them apart. Other directories (examples/,bench/, scripts next topackage.vlt) usually hold programs of their own: check them withvelt check <file>. velt check <file>checks that file and the files it imports, and nothing else.- A syntax error stops a build, but not
velt check: it reports every file's syntax errors, and then the type errors of the files without any, astscdoes. A file importing one with a syntax error is still checked; a name it imports that the broken file mentions but doesn't declare (the error may have cut the declaration short) reports nothing, and neither do its uses. --jsonprints one JSON document on stdout instead, for editors and other tools:{"diagnostics": [...], "errors": n, "warnings": n}, each diagnostic with itsseverity,message,location(file, 1-basedline/column,endLine/endColumn), furtherlabelsandnotes, pluscodeandfix(nullexcept for--ts-compatfindings).-vprints per-stage timings.--stdchecks the whole standard library ($VELT_STD, else the toolchain's): every file but its tests, every definition, also those no program reaches. A program checks only the std code it uses, and may import only std's public modules, so this is what the standard library's own development (and its CI) runs.
velt test
velt test [<file|dir>] [--filter <text> [--exact]] [--release] [--cpu <name>] [--locked]
[--jobs <n>] [--watch] [--no-cache]
velt test [<file|dir>] [--filter <text> [--exact]] [--release] [--cpu <name>] [--locked] --no-run [--json]Every export function test_*() (or export async function test_*()) in a *.test.vlt,
*.test.ts or *.test.tsx file is a test, and so is every it (or test) written with
velt/test's describe (Testing). Without a path, velt test runs the
package's tests (or those under the current directory). It prints ok <name> or
FAILED <name> per test and a summary, and exits with 1 if a test failed. Each test file runs
in a process of its own, several at once (--jobs, default one per core), and its output is
printed in file order.
-
--filter <text>runs the tests whose name contains<text>; with--exact, only the test named<text>. -
--watchreruns the tests whenever a file they import, a test file or the manifest changes. -
--no-runbuilds the test programs and prints their paths, to run one under a debugger;--jsondescribes them for tools. -
Kept results. A test file whose program didn't change since all its tests passed, and that reaches nothing outside itself, isn't run again: the runner prints its last report, marked
(cached). "Outside" is the file system, network, clock or time zone, environment, child processes, random numbers, standard input, system functions and native code. A file that reaches outside runs every time and says what it reaches. A change to anything the test file imports, to std or to the compiler runs it again, and a file with a failed test always runs.--no-cacheruns every file, keeping and using no results;VELT_BUILD_STAMP=offdoes too. The details: results that are kept.Textrunning tests/pure.test.vlt (cached) ok test_add running tests/clock.test.vlt (ran: not cacheable, it uses the clock) ok test_clock -
In a workspace, a member whose every test file was kept and of which nothing changed isn't started at all: it is listed as
Unchanged <member> (<dir>): N passed. -
The standard library's own tests. In a checkout of the Velt repository,
velt test std/runs std's white-box tests, thestd/<module>/<name>.test.vltfiles next to the code. They count as std code, so they may import the module's internal files, which no other program may; the standard library a toolchain ships leaves them out.
Code shared with TypeScript: --ts-compat
A file in the common subset of TypeScript and Velt compiles with both tsc and velt and
behaves the same under both, so a client and a Velt server can share models, validation and
components. velt check --ts-compat checks exactly the files you pass (for a directory, its
.vlt, .ts and .tsx files, found as for a package), then reports what in them tsc would
reject or run differently. Without paths, it lints the folders the package's tsCompat lists
in package.vlt:
$ velt check --ts-compat src/models
src/models/user.ts:3:14: error: `f64` is not a TypeScript type
= note: `f64` is Velt's other name for `number`, the only name TypeScript knows
= note: write `number`
= note: ts-compat(velt-number-type)velt check --ts-compatwith no paths, inside a package, lints every.vlt,.tsand.tsxfile in itstsCompatfolders, named from the current directory. Outside a package, or in one withouttsCompat, it fails and says to list the folders or name the paths; so does a listed folder that doesn't exist, or folders without any source file. Explicit paths ignoretsCompat.- Plain
velt check(without--ts-compat) never lints, not even thetsCompatfolders: the lint is opt-in, so a package check stays the compiler's verdict and CI decides where the TypeScript rules apply. The editor shows the findings live (editors). - The files are checked first, together, as library modules; a file with errors of its own
reports them and isn't linted. They must all be in one package (or none in a package): lint
one package per run. Two files with the same module path (
dup.vltanddup.ts) are an error, as in a package check; a declaration file (.d.ts) can't be passed. - Every finding says what TypeScript does, why Velt differs and what to write, and ends with a
note naming its rule,
ts-compat(<code>). With--json, each finding also carries itscodeand, when the replacement is mechanical, afix:{"location", "replacement", "title"}. - A relative import must stay among the files passed (or in the
tsCompatfolders):tsccompiles every file a shared file imports. - An import of a std module (
velt/fs) is an error, as std has no JavaScript runtime, exceptimport type { … }: TypeScript erases it, and thevelttype package declares the types fortsc. - Some rules need types: they ask the checker about what a name refers to and what type a
value has (
=== nullon aMap.get,${xs}of an array,sort()of numbers), so the files are checked once more for them. That about doubles the time of the check and grows linearly with the code: 21,000 lines (3,000 small functions) take 155 ms to check and 304 ms with--ts-compat(a release build on an Apple M4). - A finding that depends on values the lint can't see is a warning (
${x}wherexmay beundefinedin JavaScript, string lengths and positions,xs.length - 1); the others are errors. - It exits with 1 when there is an error, from the check or from the lint; warnings alone
don't fail it.
velt buildnever runs the lint.
velt new and velt init
velt new <name> [--template app|cli|api|websocket|lib] [--lib]
velt init [--template <t>] [--name <name>] [--force]
velt init --editor vscode [--dir <dir>]| Template | What you get |
|---|---|
app (default) | hello world split into a module, with a test |
cli | a command-line tool: velt/cli argument parsing, subcommands, --help, exit codes, tests |
api | a JSON HTTP API: routes, validation, typed errors as status codes, tests with fetch against a live server |
websocket | a WebSocket chat server and terminal client, with an end-to-end test |
lib | a library: exports with /** */ doc comments for velt doc, tests, ready for velt publish |
The api and websocket projects depend on the http-server package from the Velt repository
on GitHub, at the tag of the toolchain's release (tag: "v0.5.0"), so a project gets the server
its toolchain was released with; a toolchain built from any other commit takes it from the
main branch (http-server, git
dependencies).
Every template builds, passes velt test and is formatted. velt init writes the same files
into the current directory; it never overwrites files without --force, always keeps an
existing README.md, and names the package after the directory unless --name is given.
Packages also get a .gitattributes line, *.vlt linguist-language=TypeScript, so GitHub
highlights .vlt files: GitHub has no Velt language yet, and Velt is TypeScript syntax plus a
few keywords and types. Like .gitignore, it is added to an existing file.
Packages also get .vscode/launch.json, so F5 in VS Code builds and debugs the program, and
.vscode/extensions.json, which recommends the Velt and CodeLLDB extensions
(Debugging); existing ones are kept. velt init --editor vscode adds
only those two files, to the root of the current package (or to the current directory outside
a package, or to --dir), and never overwrites one.
velt doc
velt doc [<file|dir>...] [--std] [--format html|json|types] [-o <dir|file>]Generates HTML documentation for exported items: their signatures and the doc comment right
above each declaration, a JSDoc /** … */ comment or a block of /// lines (a comment block at
the top of a file, followed by a blank line, documents the module). Plain // comments are not
documentation; on the lines between a doc comment and its declaration
(// eslint-disable-next-line) they are skipped. Without paths, it documents the package's src/ (.vlt, .ts and .tsx files) into
<package>/target/doc; --std documents
the standard library. The output has one page per module and a client-side search.
- Signatures are shown in one canonical form whatever the source's layout:
function pick<T extends Comparable<T>, U>(items: T[], limit?: i64): U | null, withstatic,async,get/seton members and the type'sextends/implements. - Type names link to their documentation: types the module declares, imports (also
ns.Typethroughimport * as ns) or re-exports, among the modules documented together. Prelude types link when the prelude is part of the docs (--std, the docs website); a package's docs don't include std, so its prelude types stay plain text. Type parameters and parameter names never link. - Re-exports are documented under the re-exporting module:
export { x as y } from "…"showsx's documentation asy,export * from "…"every export the module doesn't declare or list itself, each with a link to where it is declared. A re-export from a module that isn't documented alongside (another package; std when documenting a package) is listed as one line. Names in a localexport { a, b as c };list are documented too. An interface declared more than once is documented once, with the members of every declaration. - Tags in doc comments are rendered as sections:
@param name - textas a parameter list (a{type}is ignored, as the signature shows the type),@returns(@return),@throws,@example(code, shown in a code block),@deprecated [text](the item is also marked deprecated) and@see. Inline{@link name}showsnameas code;{@link name text}shows the text; a URL target becomes a link. Other tags stay in the text as written.
JSON output
velt doc --format json writes the same documentation as one JSON document, for tools that
render API pages in their own design (the docs website builds its API reference, its search
index and the standard library catalog from it). -o names a .json file, a directory (which
gets api.json; the default is the same directory as the HTML) or - for stdout:
velt doc --std --format json -o std-api.jsonThe document is { "schemaVersion": 1, "title": …, "modules": [ … ] }; schemaVersion changes
when a field changes meaning or goes away (new fields may be added without it). Every field is
always present (null or [] when empty). A module's record:
| Field | |
|---|---|
module | its name in the docs (std/fs, mypkg/util); targets refer to modules by it |
specifier | how it is imported (velt/fs, mypkg/util) |
file | its source file (std/fs/index.vlt, src/util.vlt); for files and directories given on the command line, relative to the current directory when they are under it (velt doc ./lib and velt doc D:/pkg/lib run in D:/pkg both give lib/util.vlt), otherwise the path as given |
doc, summary | the module doc (Markdown) and its first sentence |
links | the {@link}s in the module doc (as for items, below) |
exports | its exported items, in source order, then the re-exported ones |
An item (an export, or a member in members, which has the same shape):
| Field | |
|---|---|
id | a key unique in the document: <specifier>#<name> (velt/fs#readFile), #<Type>.<member> for a member (velt/fs#FileWriter.close); a setter is <member>:set, a static member whose name an instance member has too static:<member>, a type whose module exports a value of the same name (export type Color = …; export const Color = …) type:<name> (pkg#type:Color; the value is pkg#Color), an extend block extend:<type> (~2 for a second one on the same type) |
name | its exported name |
kind | function, class, struct, interface, enum, type, const, extend (an extend block, named after its type), re-export (from a module not documented alongside); members: field, method, getter, setter, constructor, member (of an enum) |
modifiers | static, async, readonly, abstract, protected, public, mut as written, and deprecated |
signature | the canonical signature (one line per overload, and per differing header of a merged interface) |
spans | the type names in signature (of members too) that link to their documentation: {start, end, role, target}, offsets in UTF-16 code units (JavaScript string indices); role is where the name is: param, return, throws, extends, implements, typeArg (a bound or default of the item's own type parameters), field (a field's type) or type (a constant's or type alias's type, the type an extend block extends); a type argument has the role of the type it belongs to (Promise<Point> as a return type: return) |
doc, summary | the doc comment's text without its tags (Markdown; {@link} already rendered as in HTML) and its first sentence |
tags | params ([{name, doc}]), returns, throws ([…]), examples ([…], verbatim), deprecated (null without a @deprecated tag, "" for a tag without text, otherwise its text), see ([…]) |
links | the {@link}s in the doc comment and its tags: {text, ref, target}, text as the Markdown shows it, ref as written, target null when nothing documented has that name; a name that is both a type and a value (type Color and const Color) links to the value |
source | {file, line} of the declaration (a re-exported item's is where it is declared); null for a re-export; for an interface declared more than once (the declarations merge, and are one item with the members of all), the first declaration |
reexportedFrom | {module, name} where a re-exported item is declared, else null |
members | the public fields, accessors, constructor and methods (static ones too) of a class, struct or interface; an enum's members; an extend block's methods |
A target is {id, module, kind, name, member}: the item name that module declares, with
that kind (and its member member, or null); id is that item's (or member's) id, so
cards and backlinks can key on it. For example, a method of a class in a package:
{
"id": "pkg/lib#Point.moved",
"name": "moved",
"kind": "method",
"modifiers": [],
"signature": "moved(dx: f64, dy: f64): Point",
"spans": [
{"start": 25, "end": 30, "role": "return", "target": {"id": "pkg/lib#Point", "module": "pkg/lib", "kind": "class", "name": "Point", "member": null}}
],
"doc": "The point moved by `dx`, `dy`.",
"summary": "The point moved by `dx`, `dy`.",
"tags": {
"params": [
{"name": "dx", "doc": "how far along x"},
{"name": "dy", "doc": "how far along y; see `Point.length`"}
],
"returns": "a new point",
"throws": [],
"examples": [],
"deprecated": null,
"see": []
},
"links": [
{"text": "`Point.length`", "ref": "Point.length", "target": {"id": "pkg/lib#Point.length", "module": "pkg/lib", "kind": "class", "name": "Point", "member": "length"}}
],
"source": {"file": "src/lib.vlt", "line": 64},
"reexportedFrom": null,
"members": []
}TypeScript declarations: the velt type package
velt doc --std --format types writes the standard library's declarations as an npm package
named velt (into -o, default target/velt-types), so that tsc, editors and bundlers
resolve velt/* imports in TypeScript: code shared with a client
(--ts-compat) can import type std's types.
velt doc --std --format types -o node_modules/veltimport type { Deque } from "velt/collections";
export function oldest(queue: Deque<string>): string | undefined {
return queue.peekFront();
}package.jsonmaps each public module to its declarations ("./fs": { "types": "./fs/index.d.ts" }). The prelude's types that TypeScript lacks (Mutex,Atomic,StringBuilder, …) are exported by the package'sglobals.d.ts, which the modules import them from: animport typefromvelt/…adds nothing to the client's globals.- The declarations are std's in TypeScript's types: the integer and float types are
number,boolisboolean,shared<T>isT, and the error type ofPromise<T, E>and of the iterators is left out (TypeScript has nothrows). A parameter with a default is optional. Doc comments come along, for editors' hovers. - An
extendblock of a std type adds its methods to the type's interface; one of a narrower type (extend Box<string>) gives each method athisparameter of that type, as TypeScript's ownflatdoes. Extensions of TypeScript's own types (Array,string, numbers) are left out, since declaring them would change the client's globals; a bound that such a type meets through one (K extends Comparable<K>, whichstringand the numbers implement by extensions) takes it in:K extends Comparable<K> | boolean | number | string. - A type std uses in a public signature but doesn't export (an internal class in a constructor
only std calls) is opaque:
unknown. - The package declares types only; the modules run in Velt. It needs a
libwithesnext.disposable("lib": ["esnext"], TypeScript 5.2 or newer) forSymbol.dispose.
velt toolchain
Several velt versions can be installed side by side, each in <root>/toolchains/<version>/
(<root> is ~/.velt, or %LOCALAPPDATA%\velt on Windows). The velt on your PATH is a
small launcher, <root>/bin/velt, that runs one of them with the same arguments. The
installers set it up (Installing a release).
velt +<toolchain> <command>or$VELT_TOOLCHAIN, for one command: a version (+0.2.0), a requirement read like a pin (+0.2: the newest 0.2.x), or a linked name (+dev);- else the
veltfield of the nearestpackage.vlt: the version the package'svelt.lock.jsonrecords when the field accepts it (installed first if it is missing), so every machine builds a commit with the same velt; otherwise the newest installed version the field accepts.velt updatetakes the newest published one the field accepts, installing it first, or the newest installed one when that is newer (without the network, the newest installed one), which then records itself in the lock; - else the default (
velt toolchain default), which is also what loose.vltfiles outside a package use.
A version the package pins that isn't installed is downloaded from the release on first use and
installed, with a message on stderr. A new patch version gets the target packs
(velt target add) the newest older patch of its minor had, so
velt build --target … keeps working after an update. The release's SHA256SUMS and the list of releases are
signed with the velt release key, which is built into velt, so a mirror or a replaced file can't
substitute a toolchain. A requirement such as "0.1" picks the newest release it accepts that
hasn't been yanked (withdrawn for a serious bug). Set
VELT_TOOLCHAIN_AUTO_INSTALL=0 to turn that off: the command then fails and names the
velt toolchain install command to run.
$ velt toolchain which
0.1.4 (velt: "0.1" in /home/me/app/package.vlt:5)
/home/me/.velt/toolchains/0.1.4
$ velt toolchain list
* 0.2.0
> 0.1.4| Command | What it does |
|---|---|
list [--available] | installed versions, newest first, and links; * marks the default and > the one this directory selects. --available lists the published versions, and marks the yanked ones, which a requirement such as 0.1 skips |
install <version> [--default] | install 0.1.3, or the newest published version a requirement such as 0.1 accepts; the first one installed becomes the default |
install --from <file> [--unverified] [--default] | install a toolchain archive downloaded before (velt-<version>-<triple>.tar.gz for this machine), without the network: checked against its release's SHA256SUMS and SHA256SUMS.sig beside it, or, with --unverified, not checked. Without the network it checks only the revocations this root already stored: a key revoked after the root last read the index of releases is still trusted. Installing a version that is already installed keeps it, and says so |
remove <toolchain> [--force] | remove a version or a link. Debug executables link the runtime of the toolchain that built them, so the ones a removed version built stop running until rebuilt. The default needs --force. A version a running program has open (Windows) is reported in use and left whole |
self-update | replace the launcher (~/.velt/bin/velt) with the one of the newest release, installing that release first if needed; the launcher says when a newer one is out. The new launcher is run before it replaces the old one, so one that can't run on this machine is refused and velt keeps working. Run it as velt, not a copy of the launcher under another name |
prune [--dry-run] | remove every installed version but the default, the one this directory runs, those a link points into, and, for each major.minor, the one a pin like "0.1" runs (its newest release, or its newest pre-release when it has no release yet), saying how much space each frees; --dry-run only lists them. A version a lockfile still names is installed again when it is next used |
default [<toolchain>] | show or set the default |
which | the toolchain this directory runs, why, and its directory |
--version | the launcher's own version, velt-launcher <version> (velt --version is the selected toolchain's) |
link <name> <prefix>, unlink <name> | use a toolchain prefix built elsewhere (a checkout's; it holds bin/velt and std/) as <name>: velt +dev test |
The launcher runs velt toolchain itself, so it works the same whichever version a package
pins. A toolchain's own bin/velt, started directly, says so instead.
When the release that is about to run has a problem (it was yanked, or the index of releases
carries an advisory for it, such as a security one), the launcher says so on stderr, at most
once a day per version, with the newest release of the same minor that doesn't have it and how
to move to it (velt update in a package pinned to the version, or velt toolchain install <version>). It reads the last index it fetched, so it needs no network. Text from the index is
shown with control characters replaced and cut at 200 characters.
velt doctor
Checks the runtime library (and, for debug builds, the shared runtime), the standard library, the linker (the bundled lld with the
target's link kit, or the system linker and why the bundled one is not used), the installed
target packs (velt target), the WebAssembly
linker (the bundled lld, or the Rust toolchain's rust-lld when Rust is installed), clang, the
debugger scripts (share/velt/lldb/velt_lldb.py, optional), that VELT_HOME is writable, and
which toolchain version runs and why (velt toolchain), then compiles and runs a hello world
(debug, plus release through LLVM when clang is found). On macOS it also measures how long a new
program takes to start, since the system checks every new executable on its first launch, and
says how to turn that check off for your terminal. Problems are marked ✗ (required) or
! (optional) with a fix: hint. It exits with 0 when every required check passes.
$ velt doctor
✓ velt velt 0.1.1 (4efaa8d x86_64-pc-windows-msvc)
✓ runtime lib C:\Users\me\AppData\Local\velt\toolchains\0.1.1\lib\velt_rt.lib
✓ std C:\Users\me\AppData\Local\velt\toolchains\0.1.1\std
✓ linker bundled C:\Users\me\AppData\Local\velt\toolchains\0.1.1\lib\velt\lld.exe (kit …\lib\targets\x86_64-pc-windows-msvc)
✓ other targets none (`velt target add <triple>` to cross-compile)
✓ wasm linker C:\Users\me\AppData\Local\velt\toolchains\0.1.1\lib\velt\lld.exe
✓ clang C:\Program Files\LLVM\bin\clang.exe
✓ debugger scripts C:\Users\me\AppData\Local\velt\share\velt\lldb\velt_lldb.py
✓ velt home registry C:\Users\me\.velt\registry, cache C:\Users\me\.velt\cache
✓ toolchain 0.1.1 (the default); launcher C:\Users\me\AppData\Local\velt\bin\velt.exe, default 0.1.1
✓ hello (debug) built with Cranelift and ran
✓ hello (release) built with LLVM and ranShell completions
velt completions bash > ~/.local/share/bash-completion/completions/velt
velt completions zsh > ~/.zfunc/_velt # with ~/.zfunc on $fpath
velt completions fish > ~/.config/fish/completions/velt.fish
velt completions powershell >> $PROFILE # PowerShellOutput and exit codes
- Diagnostics go to stderr as
path:line:col: error: message(Diagnostics). - A program that panics prints
panic: <message> at <path>:<line>:<col>and exits with 101; an uncaught error printsUncaught <Type>: <message> at <location>and exits with 1. - Usage errors exit with 2 and suggest the closest command or option
(
did you mean `velt build`?). An internal compiler error exits with 101. - Colors are on when the output is a terminal.
NO_COLOR=1turns them off,CLICOLOR_FORCE=1forces them.
Environment variables
| Variable | Meaning |
|---|---|
VELT_STD | standard library directory |
VELT_SHARE | directory of the debugger scripts (<it>/lldb/velt_lldb.py; default <prefix>/share/velt) |
VELT_HOME | package manager home (default ~/.velt: cache/, registry/) |
VELT_REGISTRY | package registry: a directory (default $VELT_HOME/registry) or an http(s):// URL |
VELT_REGISTRY_TOKEN | a registry token sent to every registry server, overriding the tokens velt login stored (for CI) |
VELT_CA_FILE | PEM file of extra CA certificates to trust for https:// registries |
VELT_CLANG | clang for the LLVM backend |
VELT_LLVM_OPT | optimization level for release builds (clang, or opt for WebAssembly): 3 (default), 2, 1, s or z |
VELT_CPU | the CPU builds are for when --cpu does not say (CPU); a CPU of another architecture does not apply |
VELT_CPU_DISABLE | read by programs: features (popcnt,sse4.2) the start-up CPU check treats as missing, to test the check (CPU) |
VELT_CODEGEN_UNITS | how many codegen units (parallel clang processes) an LLVM build uses, at most the core count; 1 turns splitting off; default: from the program's size (one unit below about 32 000 VIR statements) |
VELT_BUILD_CACHE | the build cache's directory (default $VELT_HOME/build-cache); off turns it off |
VELT_BUILD_CACHE_SIZE | how large the build cache may grow (500M, 10G; default 2G) before the code used least recently is removed |
VELT_BUILD_STAMP | off: compile even when no input changed since the last build of the same output, and run every test file |
VELT_CACHE_WARM | off: installing a toolchain does not warm the build cache (nor does CI, CI set) |
VELT_STD_OBJECT | 1: release builds link std functions compiled once in the build cache (experimental; build cache) |
VELT_RT_LIB | runtime library (default: next to velt, or <prefix>/lib when installed) |
VELT_RT_LINK | static: debug builds link the static runtime instead of the shared one (Platforms) |
VELT_NATIVE_FROM_SOURCE | 1: build a package's native library from source when no prebuilt one exists (Packages) |
VELT_DEV_POLL, VELT_DEV_DEBUG_INFO, VELT_DEV_STOP_GRACE_MS | velt dev: check files by polling, turn off JIT debug info (0), how long a stopped program may finish in-flight work in milliseconds (default 1500) (velt dev) |
VELT_INSTALL_BASE_URL | the repository whose releases the launcher, velt toolchain install, velt target add and the installers download from (default https://github.com/velt-lang/velt) |
VELT_TOOLCHAIN | the toolchain the launcher runs for this command: a version or a linked name, overriding the package's velt field (velt toolchain) |
VELT_TOOLCHAIN_AUTO_INSTALL | 0: the launcher fails instead of installing a missing version |
VELT_INSTALL_PUBLIC_KEY | the Ed25519 keys (64 hex digits each, separated by commas) release signatures (SHA256SUMS.sig, releases.json.sig) are checked with, instead of the velt release keys: for the releases of another build |
VELT_LINKER | bundled: link with the toolchain's lld and link kit, an error if it has none; system: the system linker (link.exe, cc); a path: that linker program, given the system linker's arguments; default: the bundled linker when the toolchain has it (Platforms) |
VELT_LLVM_BIN | directory with LLVM's opt and llc, for WebAssembly |
VELT_WASI_SYSROOT | wasi-libc directory, for wasm32-wasip1 |
VELT_WASM_RUNNER | program that runs wasm32-wasip1 modules for velt run (default: wasmtime) |
VELT_THREADS | number of runtime worker threads (default: one per core) |
VELT_RESCUE | 0: the runtime never serves a worker that makes no progress (its timers, sockets and waiting tasks) from another one, and starts no spare workers. Together with VELT_THREADS=1, a program's work then doesn't depend on how its threads are scheduled; with more workers, tasks still move to sleeping ones. For counting instructions only (task placement) |
MIMALLOC_* | the allocator's options (mimalloc); Velt programs commit memory as they use it (MIMALLOC_ARENA_EAGER_COMMIT=0), which keeps a multi-threaded program's resident memory near what its data needs; MIMALLOC_ARENA_EAGER_COMMIT=1 commits arenas up front (on Linux with transparent huge pages, each worker then makes 2 MiB resident at once) |
VELT_SCHEDULER | Unset or velt (the default): the runtime's own scheduler. Work stays on the worker that made it, and another worker is taken on when a worker's input (tasks woken by sockets, timers or other threads) has waited 200 µs or more on average over 10 ms, or the worker was awake for 95 % of those 10 ms with input waiting; a task woken by another task runs next on the waker's worker; a worker that runs out of work runs input waiting on another awake one, and one that makes no progress for 2 ms (a long computation, or the OS took it off its core) has its sockets, timers and half of its queue served by another. TCP, the HTTP server, WebSockets, PostgreSQL over TCP and timers run on it natively, the rest (files, processes, Redis, fetch, UDP, DNS) on a small tokio runtime beside it. stealing: tokio's work-stealing scheduler (idle workers take tasks from busy ones). per-core: one tokio event loop per worker; a task never moves after its first poll, so a CPU-bound task delays everything on its worker. |
MACOSX_DEPLOYMENT_TARGET | oldest macOS a program runs on (default and minimum: 11.0 on arm64, 10.12 on x86_64) |
NO_COLOR | disable colored output |
CLICOLOR_FORCE | force colored output |
COLUMNS | terminal width for velt search results (default: asked from the terminal) |