Compile-time code
Conditional compilation, target flags, and macros.
On this page
Lines starting with # run while compiling. They pick which code is built for the target, the build flags, or the type a generic function was called with.
Conditional compilation#
#if, #elif, #else and #end keep or drop the lines between them. Code in a dropped branch is not compiled:
fn main() {
#if OS == "linux"
println("Linux")
#elif OS == "macos"
println("macOS")
#else
println("Windows")
#end
#if ARCH == "x64"
println("x86-64")
#end
}
#if works inside functions and between declarations, so a whole function or link line can be per platform.
Definitions#
| Name | Value |
|---|---|
OS |
"linux", "macos" or "win" |
ARCH |
"x64" or "arm64" |
TEST |
true in --test builds |
DEBUG |
true in debug builds (-d) |
STATIC |
true when dependencies are linked statically (--static) |
VALK_VERSION |
The compiler version, such as "0.8.4" |
| your own | Passed with --def |
Pass your own with --def, and check whether one is set with is_defined:
fn main() {
#if is_defined(TRACE)
println("tracing on")
#end
#if is_defined(LEVEL) && LEVEL == "3"
println("level 3")
#end
}
valk build src -o app --def TRACE --def LEVEL=3
valk build src -o app --def "TRACE,LEVEL=3"
A definition can also be used as a value, such as println(LEVEL), when the build sets it.
Source location#
| Expression | Value |
|---|---|
__FILE__ |
Path of the file, relative to its package's source directory |
__PATH__ |
Full path of the file |
__DIR__ |
Directory of the file |
__LINE__ |
Current line, from 1 |
fn main() {
println("at %{__FILE__}:%{__LINE__}")
}
Branch on a type#
In generic code, #if can test the type parameter. Types compare with == and !=, and size_of(T) gives the size in bytes:
fn describe[T](value: T) String {
#if T.$is_integer
return "integer of %{size_of(T)} bytes"
#elif T == String
return "text"
#else
return "something else"
#end
}
fn main() {
println(describe(5)) // integer of 8 bytes
println(describe("hi")) // text
println(describe(1.5)) // something else
}
| Check | True when T is |
|---|---|
T.$is_integer, T.$is_signed, T.$is_float, T.$is_bool |
That kind of number or a bool |
T.$is_enum |
An enum |
T.$is_nullable |
A ?T type |
T.$is_pointer |
A class, raw pointer or other pointer type |
T.$is_raw_pointer |
ptr or *T |
T.$is_gc |
Managed by the garbage collector |
T.$is_void |
void |
T.$is_type_of(String) |
Exactly that type |
T.$has_method(name) |
A type with a method of that name |
Loop over properties#
#loop object value as prop repeats its body for each property of a class or struct value. prop.name is the property name, and prop itself reads the property:
class User {
name: String
age: int
}
fn field_names[T](value: T) Array[String] {
let names = Array[String]{}
#loop object value as prop
names.append(prop.name)
#end
return names
}
fn main() {
let user = User { name: "Ada", age: 36 }
println(field_names(user).join(", ")) // name, age
}
Stop or report a build#
#error "message" fails the build when its branch is kept. #print value prints a value while compiling:
fn main() {
#if OS == "plan9"
#error "Plan 9 is not supported"
#end
#print OS
}
CPU features#
--cpu native lets the whole build use this machine's CPU features, and function flags such as $target_avx2 enable one feature for a single function. See SIMD vectors.
