Build Configuration
#config reads defines: named values handed to a build, such as a feature switch, a vendor name or a limit. They come from the manifest and the command line, are fixed for the whole build, and are read while compiling, so a when can keep only the code a configuration needs.
import Core::{ #config };
| Query | Type | Result |
|---|---|---|
#config.Has("Name") | bool | whether the build defines Name, whatever its value |
#config.Get("Name") | char8[..] | the value of Name, or an empty slice when it is not defined |
Get alone cannot tell "not defined" from "defined as empty"; ask Has when the difference matters. Names are case-sensitive.
Supplying defines
In the manifest. A [Build.Defines] table in Rux.toml gives the package's defaults. A value may be a TOML string, boolean or integer, and is always read as text:
[Build.Defines]
Name = "Grace"
Retries = 3
Fast = true
Here #config.Get("Retries") is "3" and #config.Get("Fast") is "true". The table holds at most 128 defines; see the manifest reference.
On the command line. --define NAME=VALUE sets or overrides one define for one build of rux build, rux run, rux check or rux test, and may be repeated. --define NAME without a value defines it as "true"; --define NAME= defines it as empty.
rux run --define Name=Ada --define Verbose
flowchart LR
m["Rux.toml<br/>[Build.Defines]"] --> d{"the build's defines"}
c["--define NAME[=VALUE]"] -- "overrides" --> d
d --> has["#config.Has"]
d --> get["#config.Get"]Reading defines
The argument to Has and Get must be a string literal written at the call, because the lookup happens while compiling. Even a constant holding the name is refused:
error: the argument to 'Has' must be a string literal written in the source
help: the value is looked up while compiling, so it cannot come from a variable
Both queries work in a when condition — Get compares with == and != against a string — and in ordinary expressions, where they compile to constants:
import Core::{ #config };
import Io::PrintLine;
when #config.Has("Verbose") {
const Verbose: bool = true;
} else {
const Verbose: bool = false;
}
func Main() -> int {
when #config.Has("Name") {
let name = #config.Get("Name");
} else {
let name = "World";
}
PrintLine("Hello, {}!", name);
when #config.Get("Name") == "Ada" {
PrintLine("(a special greeting)");
}
PrintLine("verbose: {}", Verbose);
return 0;
}
| Started with | Output |
|---|---|
rux run | Hello, World! · verbose: false |
rux run --define Name=Ada --define Verbose | Hello, Ada! · (a special greeting) · verbose: true |
A define is folded in while compiling: changing one means rebuilding. An executable built earlier keeps the defines it was built with, and the code for a configuration it was not built with is not in it at all.