Skip to content

Reference | Variables

olegz edited this page Mar 16, 2026 · 1 revision

Typed Variables

Xake provides a typed variable system for passing values into recipes from CLI arguments or environment variables. Variables are declared as an anonymous record, registered with varschema, and bound in recipes with let!.

Quick example

let vars = {|
    Config  = Var.string(description = "Build configuration") |> withDefault "Debug"
    Version = Var.string() |> required
    Threads = Var.int() |> withDefault 4
    Verbose = Var.bool()
|}

do xakeScript {
    varschema vars

    rules [
        command "build" {
            let! config  = vars.Config    // string
            let! version = vars.Version   // string
            let! threads = vars.Threads   // int
            let! verbose = vars.Verbose   // bool option
            do! trace Info "Building %s v%s" config version
        }
    ]
}
dotnet fsi build.fsx -- -- build -d Config=Release -d Version=1.0.0

Factory methods

Method Returns Notes
Var.create<'t> OptionalVar<'t> Generic factory
Var.string OptionalVar<string> Shorthand
Var.int OptionalVar<int> Shorthand
Var.bool OptionalVar<bool> Shorthand
Var.env<'t> OptionalVar<'t> Environment variable only
Var.arg<'t> OptionalVar<'t> CLI argument only

All factories accept optional named parameters: ?name, ?cliArg, ?envVar, ?description.

Var.env accepts only ?envVar and ?description. Var.arg accepts only ?cliArg and ?description.

Resolution order

Each variable declared with Var.create or a typed shorthand resolves from two sources in priority order:

  1. CLI argument-d <name>=<value>
  2. Environment variable — name auto-derived as UPPER_SNAKE_CASE

Auto-derived env var names:

buildConfig → BUILD_CONFIG
apiKey      → API_KEY
threads     → THREADS

Name resolution

Parameters supplied CLI lookup key Env var derived from
neither name nor cliArg field name field name
name only name value name value
cliArg only cliArg value field name
name + cliArg cliArg value name value

The envVar parameter overrides auto-derivation when specified explicitly.

Scoped lookup

Var.env — environment only

CLI args are never consulted. Useful for secrets that must not appear on the command line.

let vars = {|
    NUGET_KEY = Var.env<string>(description = "NuGet API key") |> withDefault ""
|}

Var.arg — CLI only

Environment variables are never consulted. Useful for explicit build-time overrides.

let vars = {|
    OutputPath = Var.arg<string>(cliArg = "output") |> withDefault "out"
|}

Optional vs required

The result type in a recipe depends on how the variable is declared:

Declaration Result type
Var.string() string option
Var.string() |> required string
Var.string() |> withDefault "Debug" string
Var.int() |> withDefault 4 int
Var.bool() bool option

Combinators

Combinator Purpose
describe "text" Add a description shown in --help output
required Throw at runtime if no value found
withDefault value Use fallback when CLI and env are absent

The description named parameter on factory methods is equivalent to piping through describe.

// These are equivalent:
Var.string(description = "Build configuration")
Var.string() |> describe "Build configuration"

Registering with varschema

Register the variable record with varschema inside xakeScript:

do xakeScript {
    varschema vars
    rules [ ... ]
}

This enables --help output listing all declared variables.

Help output

Running with --help lists all declared variables:

Script variables:
  -d Config=<value> [string], default: "Debug" — Build configuration
  -d Version=<value> [string] (required)
  -d Threads=<value> [int], default: 4
  -d Verbose=<value> [bool]
  $NUGET_KEY [string], default: "" — NuGet API key

Dependency tracking

Variable reads are recorded as dependencies. If a variable's value changes between builds, targets using it will be rebuilt — just like file dependencies.

Clone this wiki locally