Build flags parameterize an elide.pkl manifest from the command line. A flag is a name, optionally with a value; the manifest reads it during evaluation and can shape itself around it: pick an entrypoint, add a dependency, stamp a version, switch a toolchain setting.
Passing flags
Flags are spelled --flag=NAME[=VALUE] or -fNAME[=VALUE] and may appear in any position, before or after build targets:
elide build run --args="Hello World π" --flag=RELEASE --flag=VERSION=1.2.3
elide build run --args="Hello World π" -fRELEASE -fVERSION=1.2.3The detached forms (--flag NAME, -f NAME) work as well. Everything at or after the -- passthrough separator belongs to the script, tool, or subprocess being invoked, and is never read as a build flag.
Every project-aware command accepts flags:
elide run -fRELEASE -fVERSION=1.2.3 -- "Hello World π"
elide install --flag=RELEASE
elide format -fNO_KOTLIN
elide build -fRELEASE
elide test -fFAST
elide manifest -fRELEASE # handy for inspecting the evaluated resultRules worth knowing:
- A flag with no value (
-fRELEASE) is a bare flag: it reads astruefrombool(...), and as an error fromstring(...). - A value is everything after the first
=, verbatim:-fEXPR=a=bis the valuea=b, and values may contain spaces, commas, and any Unicode. - Passing the same flag twice keeps both values: the scalar accessors read the last one, the list accessors read all of them (see Repeated flags).
- Nothing needs to be declared ahead of time. An unrecognized name is simply absent, so a manifest decides what a flag means.
- A flag name may not contain
,or=, and may not be empty; anything else is rejected with a message naming the flag.
Reading flags in a manifest
Flags arrive on build.flags, alongside the rest of the build state (see the elide.pkl Reference):
amends "elide:project.pkl"
local isRelease = build.flags.bool("RELEASE")
name = if (isRelease) "demo-release" else "demo-debug"
version = build.flags.stringOr("VERSION", "0.0.0")| Accessor | Behavior |
|---|---|
has(name) | whether the flag was passed at all, with or without a value |
count(name) | how many times the flag was passed; 0 when absent |
string(name) | the value; null when absent; throws when the flag was passed bare |
stringOr(name, orElse) | the value, or orElse when absent |
bool(name) | false when absent, true when bare; accepts true/false, yes/no, on/off, 1/0, case-insensitively; throws otherwise |
int(name) | the value as an Int; null when absent; throws when not an integer |
strings(name) | every value, in command-line order; empty when absent; throws on a bare occurrence |
stringsOr(name, orElse) | every value, or orElse when absent |
ints(name) | every value as Ints; empty when absent; throws when any is not an integer |
bools(name) | every value as Booleans; empty when absent; a bare occurrence reads as true |
raw | every flagβs effective value as a Mapping<String, String>; a bare flag maps to the empty string |
all | every flagβs values as a Mapping<String, List<String>> |
A flag passed once is just a list of one, so strings(...) is the accessor to reach for whenever a manifest wants to accept βone or moreβ of something.
The typed accessors validate, so a typo fails the build with a clear message rather than silently reading as false:
ββ Pkl Error ββ
build flag `RELEASE` is not a boolean: `perhaps`Repeated flags
A name repeated on the command line aggregates into a list, in the order it was passed:
elide build -fARG=one -fARG=two -fARG='three, and a half'local args = build.flags.strings("ARG") // List("one", "two", "three, and a half")
local first = build.flags.all["ARG"].first
local latest = build.flags.string("ARG") // "three, and a half" β scalars read the last valueEach occurrence travels on its own channel, so a value keeps commas, spaces, =, and any Unicode no matter how many times the flag was passed. The typed list accessors validate every element, so one bad occurrence fails the build:
ββ Pkl Error ββ
build flag `N` is not an integer: `x1`Flags inside nested blocks
At module level, read flags as build.flags. Inside a nested object body an unqualified build does not resolve due to Pkl scoping rules; bind a module-level local instead:
local wantsExtra = build.flags.bool("EXTRA")
dependencies {
maven {
packages {
"com.google.guava:guava:33.0.0-jre"
when (wantsExtra) {
"com.example:extra:1.0.0"
}
}
}
}Flags apply to the whole build, so a flag that changes a dependency or a source set changes the task graph too; see Build Tasks for how targets and caching work.