17. Compilation Targets
Target selection: @target#
A build is made for a target, an operating system and an architecture written <os>-<arch> (--target=darwin-arm64), by default the machine the compiler runs on.
- The OS selects code:
@targetchooses the body of a function by OS. - The architecture selects no code (no
@targeton it), and sets values such as the pointer width.
Os has the variants Windows, Linux, Darwin, Wasi and Browser; Arch has X86_64, Arm64 and Wasm32. WebAssembly is an architecture, on which two OSes run: Wasi has a POSIX-shaped filesystem, Browser has none.
One declaration, several bodies#
// path.vader: the declaration. Carries the signature, the export, the doc.
import "std/string"
/// True iff the path is absolute.
@target
export is_absolute :: fn(p: string) -> bool = p.starts_with("/")
// path-windows.vader: a body. Same module; the file name means nothing.
@target(.Windows)
is_absolute :: fn(p: string) -> bool { /* drive letter, UNC */ }
A bare @target marks the declaration of a function, carrying its signature, visibility, doc and decorators, and optionally a general body. @target(.Os, ...) marks a body for the OSes it lists, in any file of the module. A body may come from the linker (@target(.Windows) @extern("CreateFileW")): the bare form makes a declaration, not the presence of { ... }.
A build keeps one body per declaration, the one listing the target's OS or else the general body, and gives it the parameters, return type and type parameters of the declaration. Bodies not selected are neither resolved nor type-checked; vader check --target=<os>-<arch> checks another platform's from any machine.
Rules#
// beside the `is_absolute` above, each line is an error
@target(.Drawin) is_absolute :: fn(p: string) -> bool = false // error: not an OS
@target(Os.Linux) is_absolute :: fn(p: string) -> bool = false // error: write `.Linux`
@target(.Linux) is_relative :: fn(p: string) -> bool = false // error: no such declaration
@target(.Linux) export is_absolute :: fn(p: string) -> bool = false // error: exported body
@target(.Windows) is_absolute :: fn(p: string) -> bool = false // error: two Windows bodies
@target(.Linux, .Linux) is_absolute :: fn(p: string) -> bool = false // error: Linux twice
@target is_absolute :: fn(p: i32) -> bool = false // error: declared twice
@target(.Linux) is_absolute :: fn(path: string) -> bool = false // error: `path`, not `p`
@target LIMIT :: 3 // error: not a function
Whatever the target being built, each line above is an error, and so are: a body carrying a decorator other than @extern and @intrinsic; a body with another number of parameters (a body binds them by position, and their types are not compared); @target on anything but a top-level free function (a constant that depends on the target uses VADER_OS).
A declaration with no general body and no body for the OS being built is an error too.
VADER_OS: the target as a value#
VADER_OS and VADER_ARCH are prelude constants, known at compile time, holding the target's OS and architecture. Their types Os and Arch live in std/target: comparing with a bare variant needs no import (VADER_OS == .Windows), naming the type does (Os.Windows, a parameter typed Os).
SEP :: if VADER_OS == .Windows { '\\' } else { '/' }
VADER_OS says what the build is for, and current_os() (std/target) what the program runs on: they differ in a cross build, and wherever code runs on another machine than it was built for. Code that resolves a path, spawns a process or otherwise acts on its host uses current_os().
Tests are never targeted#
A test is never selected away: @test goes on the bare @target declaration, never on a body (Rules). A test whose expectation depends on the platform computes it from VADER_OS:
@test
test_separator_matches_the_platform :: fn() -> void {
want :: if VADER_OS == .Windows { '\\' } else { '/' }
assert_eq(SEP, want)
}
Native (Linux, macOS, Windows)#
A native build writes portable C, compiles it with the system C compiler (docs/TOOLING.md vader build) and links the Vader C runtime, which provides the garbage collector (§8). Direct assembly backends, one per target, may come later.
WebAssembly#
A WebAssembly backend is planned. The wasi-wasm32 and browser-wasm32 targets select @target bodies, which vader check --target=wasi-wasm32 checks, but no build emits a module yet. A module will declare its imports and exports with @extern and @export (§13).