Configuration reference
Use labels to compose packages, parameters to vary a target’s behavior, and platforms to choose what its artifacts run on.
Workspaces
A package is a directory containing a BUILD file, or one named BUILD.vms.
A workspace groups packages beneath a directory containing a WORKSPACE file, which may be empty.
Name a package’s file BUILD.vms when the directory also holds a build directory, such as a project’s build output.
Where file names are compared without case, as on macOS’s default filesystem or with git’s core.ignorecase,
BUILD and build are the same name: the two can’t sit side by side,
and a .gitignore entry for build silently ignores BUILD as well.
A directory named BUILD or build is never taken for a BUILD file.
A package has one BUILD file: a directory with both BUILD and BUILD.vms is an error,
rather than one of them being silently ignored.
Everything said here of a BUILD file applies to a BUILD.vms file, and labels don’t change:
//ui:app names the target app of ui/BUILD or ui/BUILD.vms.
project/├── WORKSPACE├── BUILD├── backend/│ └── BUILD└── ui/ └── BUILDvamos searches for the nearest WORKSPACE in -C’s directory and its ancestors.
Without one, -C’s directory is the workspace root.
A WORKSPACE that can’t be checked, such as one in a directory vamos can’t search, stops the search with an error,
rather than letting another directory become the root.
| Reference | Meaning |
|---|---|
app or :app | Target in the current BUILD file’s package |
//:tools | Target in the workspace root’s BUILD file |
//backend:schema | Target in backend/BUILD, or backend/BUILD.vms, beneath the workspace root |
Use labels wherever a target reference is accepted, including base, add(), dep(), and index entries:
task( name = "generate", base = "tools", steps = [ add("//backend:schema", "/workspace"), run(["pnpm", "graphql-codegen"]), ], source = "/workspace/src/generated", include = ["**/*.ts"],)A label’s path is a clean path to a directory within the workspace, reached through no symbolic link. A referenced glob keeps paths relative to its own BUILD directory. A mistake in a BUILD file that loads because another names one of its targets is reported where it is. vamos loads only the packages needed by the requested targets and reports dependency cycles.
Packages may name each other’s targets, as where their files sit is often for other tools to say.
A package’s BUILD file runs when another first names one of its targets, while that other file is still running;
it can then name the other file’s targets declared above that point, as a file names its own earlier targets:
# BUILDlayer(name = "jre", base = "//:java") # declared before scala/BUILD runslayer(name = "app", base = "//scala:server") # runs scala/BUILD, which names //:jre# scala/BUILDlayer(name = "server", base = "//:jre", steps = [add("jar", "/app")])A target declared below that point isn’t declared yet when the other file names it,
which is an error saying where the first file stopped; declare the target above that line.
A reference made by a function of ctx, such as base = lambda ctx: "//:later", is resolved
once every BUILD file has run, so it can name any target.
Targets that name each other in a cycle, such as //:app -> //scala:server -> //:app, are an error.
The workspace root does not need a BUILD file unless a reference or command needs targets from it.
On the command line, unqualified names refer to -C’s package:
vamos build //backend:test //ui:lint -C projectvamos load app ui:dev -C project/uiParameters
Targets other than pulls accept a params dictionary.
A plain value declares both a default and a type:
params = { "mode": "release", "verbose": False, "workers": 4, "languages": ["en"],}Supported types are strings, booleans, integers, and lists of strings.
Use param() to constrain values or require an argument:
params = { "mode": param(default = "release", values = ["debug", "release"]), "languages": param(default = ["en"], values = ["en", "es", "fr"]), "version": param(type = "string"),}param(default = ..., values = ..., type = ...) accepts these optional arguments:
| Argument | Meaning |
|---|---|
default | Value used when no argument is given; determines the type |
values | Nonempty list of allowed strings, for a string or list parameter |
type | "string", "bool", "int", or "list"; required when there is no default |
Provide a default or a type.
If both are given, they must agree.
A parameter without a default must be supplied on every use.
For list parameters, values constrains each item.
Functions of ctx
An attribute can be a function that receives the target’s configuration:
glob( name = "translations", params = {"languages": ["en"]}, include = lambda ctx: ["locales/%s" % language for language in ctx.languages],)Read user parameters as ctx.<name>, so a parameter’s name is an identifier.
The function returns the whole attribute, such as a complete steps list or include list.
Individual list entries cannot be functions of ctx.
Such functions configure existing targets; they cannot declare new targets.
The target’s name and params cannot be functions of ctx.
A mistake that a function makes names the configuration it was given,
such as (configuring //:app with mode="debug").
dep() and arg()
dep(label, platform = ..., **arguments) names a target with explicit parameter arguments.
It is accepted wherever a plain target label is accepted.
Omit platform to inherit the using target’s platform.
add(dep("translations", languages = ["en", "es"]), "/app")base = dep("tools", platform = EXEC_PLATFORM)arg("name") forwards the using target’s parameter value as an argument of dep():
layer( name = "app", params = {"mode": "release"}, base = "scratch", steps = [add(dep("compile", mode = arg("mode")), "/")], command = ["/app"],)The referenced target must declare the argument’s parameter. Arguments are not automatically propagated beyond that target. A plain label uses all the referenced target’s defaults.
CLI values
Repeat --set name=value for each parameter to override:
vamos build compile --set mode=debugvamos build bundle --set languages=en,es --set verbose=true --set workers=8Lists use comma-separated strings and preserve order.
An empty value, such as --set languages=, gives an empty list.
Boolean values must be lowercase true or false.
An integer parameter’s value must be an integer, and that of a parameter with values must be one of them,
or for a list, each of its items.
Give each parameter once.
With several targets, build applies each flag to every named target declaring that parameter.
A flag that matches none of the named targets is an error.
Parameters affect reuse through the configured inputs and operations. Changing an unused parameter does not by itself force a target to run again.
Platforms
A platform is os/arch or os/arch/variant, for example linux/arm64 or linux/arm/v7.
Targets normally inherit the requested platform through their dependencies.
The command defaults to the host’s platform unless --platform is given.
vamos build app --platform linux/arm64Functions of ctx can read these built-in fields:
| Field | Meaning | Example |
|---|---|---|
ctx.platform | Full target platform | "linux/arm64" |
ctx.os | Target operating system | "linux" |
ctx.arch | Target architecture | "arm64" |
ctx.variant | Architecture variant, or an empty string | "v7" |
ctx.exec_platform | Platform executing build steps natively | "linux/amd64" |
These field names are reserved and cannot be user parameters.
Globs are shared across platforms and cannot read ctx.platform, ctx.os, ctx.arch, or ctx.variant.
Native tools and cross-compilation
EXEC_PLATFORM asks dep() for the executor’s platform, rather than the target’s platform:
task( name = "compile", base = dep("golang", platform = EXEC_PLATFORM), steps = lambda ctx: [ add("source", "/src"), workdir("/src"), build_env(CGO_ENABLED = "0", GOOS = ctx.os, GOARCH = ctx.arch), run(["go", "build", "-o", "/out/app", "."]), ], source = "/out", include = ["app"],)This runs native compiler tools while producing a binary for the requested platform. A task using native tools can share one result across platforms when its configured work is identical, as for a platform-independent JavaScript bundle. Reading the target platform here changes the compiler environment, producing a separate result for each platform. A layer’s base must match the layer’s own platform; use a task to separate native tools from target output.
The execution platform is chosen among those the executor offers, before the BUILD files are read:
the one --exec-platform names if it is given, otherwise the one a coordinator’s --default-exec-platform names,
otherwise this machine’s platform if the executor offers it, and otherwise the only one it offers.
Since it configures EXEC_PLATFORM, it is part of those targets’ cache keys:
a coordinator with --default-exec-platform gives clients on laptops and CI runners of other architectures
the same keys, so they reuse each other’s results.
The executor built into vamos offers this machine’s platform alone; a coordinator offers the
platforms of its workers, so a client on an Apple-silicon laptop building against an amd64 pool runs its commands
on linux/amd64, while --platform still says what to build and defaults to the client’s own platform.
A coordinator runs each layer’s and task’s commands on a worker of its image’s platform
when the deployment offers that platform, and on the execution platform otherwise,
so an index for linux/amd64 and linux/arm64 builds natively on a pool with workers of both.
The execution platform runs what EXEC_PLATFORM names, and tasks on scratch.
Where a target runs is no part of its cache key, so a result built on either platform’s worker is reused.
If a run() executes another architecture’s programs, the kernel needs a registered emulator for that architecture.
Otherwise the command can fail with exec format error.
Against a coordinator, such a target is refused before anything runs, with a message naming both platforms:
give --platform to build for the platform that runs the commands, or --emulate to let them run under emulation
on a worker that has an emulator for the image’s platform.
With --emulate, a platform that no worker of the deployment emulates fails the target at once, with a message that
says so; one the deployment offers emulation of waits for such a worker.
Index delivery
An index supplies its own platform list, so it cannot be named with --platform.
| Command naming an index | Images built and delivered |
|---|---|
build | Build all images into the cache |
push | Build and publish all images, preserving index order |
publish | Build and publish all images to each repository a publication gives the index, under every tag |
load | Build and load only the Docker daemon’s platform |
save | Build and save only the host’s platform |
Selecting a platform missing from the index is an error.
To select an arbitrary platform for delivery, name its pull or layer directly and use --platform.
Shared .star files
Use Starlark load() to share functions and constants:
load("//:defs.star", "go_checks")
go_checks(name = "test", base = "tools", source = "source")For example, defs.star can contain:
def go_checks(name, base, source): task( name = name, base = base, steps = [ add(source, "/workspace"), workdir("/workspace"), run(["go", "test", "./..."]), ], )Module labels start with // and name a .star file in the workspace.
A mistake in a module is reported where it is in the .star file, and a module that loads itself,
through the modules it loads, is an error.
A module cannot declare targets at its top level while loading, nor name a target by a label that doesn’t start
with //; its functions can declare targets when called by a BUILD file.
The arguments that a module’s dep() gives are checked where a BUILD file uses it.
References inside those functions resolve relative to the calling BUILD file’s package.
References in functions of ctx resolve relative to the target being configured.