Skip to main content

Buildfile

The buildfile plugin is a package provider that scans the workspace for BUILD files containing Starlark code and parses target definitions from them. It evaluates each BUILD file as Starlark to extract targets along with their drivers, configuration, labels, and transitive dependencies, then hands those targets to the build engine. BUILD file naming is customizable through glob patterns, and load() statements let you share symbols across BUILD files so common definitions can be reused throughout the workspace.

Provider

A provider discovers targets and makes them available to the build engine without running any of the work itself; the actual execution is delegated to whatever driver each target names.

Enabling it

Built-in. Register in .hephconfig under plugins with builtin: buildfile. The plugin scans the workspace for BUILD files (or custom patterns) and automatically makes those targets available to the build engine.

Configuration

Register the provider and, optionally, tune which file names it treats as BUILD files:

.hephconfig
plugins:
- builtin: buildfile
options:
patterns:
- BUILD
- "*.BUILD"

Options

OptionTypeDefaultDescription
patternsstring[]["BUILD"]Glob patterns for BUILD file names to recognize.
skipstring[][]Workspace-relative glob patterns for directories to exclude from the BUILD file walk.
defaultDriverstring""Driver name to use when a target omits driver. An explicit driver in target() always wins.

Skipping directories

Use skip to prevent the provider from descending into directories you don't want scanned — vendored code, generated output trees, large third-party subtrees, etc. Each pattern is matched against the workspace-relative path of the directory.

.hephconfig
plugins:
- builtin: buildfile
options:
skip:
- vendor
- "third_party/**"
- "generated/*"

skip is a hard boundary: it applies to discovery and to direct target resolution. Addressing a target inside a skipped subtree — for example heph run //vendor/pkg:lib — returns not found, the same as if the BUILD file were never there.

Default driver

If most targets in your workspace use the same driver, set defaultDriver so target() calls can omit the driver field:

.hephconfig
plugins:
- builtin: buildfile
options:
defaultDriver: bash
BUILD
# driver is omitted — falls back to "bash"
target(
name = "hello",
run = ["echo hello"],
out = "hello.txt",
)

# explicit driver always wins
target(
name = "gen",
driver = "exec",
run = ["./gen.sh"],
out = "gen.txt",
)

A target that omits driver when no defaultDriver is configured is an error.

Usage

A matching BUILD file defines targets by calling target() in Starlark:

BUILD
target(
name = "hello",
driver = "sh",
run = ["echo hello"],
)

Authoring BUILD files

BUILD files are written in Starlark — a small, deterministic dialect of Python. The plugin exposes a fixed set of builtins:

BuiltinReturnsPurpose
target(name, driver, **kwargs)the target's addressDeclare a target.
file(path, abs=False)a file addressReference one workspace file as an input.
glob(pattern, exclude=None, abs=False)a glob addressReference many files by pattern.
query(expr)a query addressReference all targets matching a query expression.
struct(**kwargs)a structBundle named values to pass into a field.
get_pkg()the current package pathCompute addresses relative to where the BUILD file lives. Prefer heph.core.pkg().
provider_state(provider, **kwargs)Hand package-level state to a provider.
heph.corenamespaceHost platform info and current package. See below.

file() and glob() resolve to filesystem addresses; query() resolves to a query address. All three drop straight into a dependency field:

BUILD
target(
name = "lint",
driver = "group",
deps = [query("//... && label(lint)")],
)

target(
name = "lib",
driver = "bash",
deps = [glob("src/**/*.go", exclude = "src/**/*_test.go")],
run = "go build -o $OUT ./src",
out = "lib",
)

By default paths are resolved relative to the BUILD file's package; pass abs = True to resolve from the workspace root instead.

target() — what buildfile reads, and what it forwards

target() interprets only the fields that describe the target to the engine — everything else is handed verbatim to the driver:

FieldRequiredMeaning
nameyesThe target's name within its package.
driverno*The driver that executes it (bash, go_golist, group, …). Required when defaultDriver is not set.
labelsnoA label or list of labels, used by query and matchers.
transitivenoSandbox settings propagated to targets that depend on this one.
note

Any other keyword (run, deps, out, env, cache, codegen, …) is driver-defined. buildfile does not interpret it — it forwards the value to the named driver, which decides what it means. For the fields a given driver accepts, see that driver's page, e.g. Exec for bash/sh/exec.

target() returns the new target's address, so you can bind it to a variable and reference it from another target instead of retyping the address:

BUILD
lib = target(name = "lib", driver = "bash", run = "go build -o $OUT .", out = "lib")

target(
name = "image",
driver = "bash",
deps = {"bin": lib},
run = "cp $SRC_BIN $OUT",
out = "image",
)

Relative addresses

Inside a BUILD file, deps (and any other address field) accept relative address forms resolved against the BUILD file's own package. This avoids repeating the package path for targets that live nearby.

app/BUILD
util = target(name = "util", driver = "exec", run = "...", out = "util")

target(
name = "server",
driver = "exec",
deps = [
":util", # same as //app:util
"./proto:api", # same as //app/proto:api
"../shared:lib", # same as //shared:lib
],
run = "...",
out = "server",
)

See Addresses → Relative forms for the full reference table.

heph.core — host platform

heph.core is a namespace available in every BUILD file. Use it to read the host platform so targets can vary by operating system or CPU architecture.

FunctionReturnsExample values
heph.core.os()normalized OS name"darwin", "linux", "windows"
heph.core.arch()normalized architecture"amd64", "arm64"
heph.core.os_raw()host OS identifier"macos", "linux"
heph.core.arch_raw()host architecture identifier"x86_64", "aarch64"
heph.core.pkg()current package path"//tools/build"

heph.core.os() and heph.core.arch() return normalized names that match the conventions used by container registries and most package distribution tools. heph.core.os_raw() and heph.core.arch_raw() return the exact identifiers the host reports — use those when a tool or URL scheme expects non-normalized names.

heph.core.pkg() returns the current package path — the same value as the top-level get_pkg(), which still works but heph.core.pkg() is preferred.

BUILD
# Download a platform-specific binary
target(
name = "tool",
driver = "exec",
run = [
"curl -fsSLo $OUT https://releases.example.com/tool/{}/{}/tool".format(
heph.core.os(),
heph.core.arch(),
),
],
out = "tool",
)
BUILD
# Build different targets per platform
if heph.core.os() == "linux":
target(name = "bundle", driver = "exec", run = ["./package-linux.sh"], out = "bundle")
else:
target(name = "bundle", driver = "exec", run = ["./package-mac.sh"], out = "bundle")

Sharing symbols with load()

load() imports symbols from another Starlark file so common definitions live in one place. A loaded helper registers its targets against the package that calls it, not the file it is defined in:

BUILD
load("//build/defs:go.star", "go_service")

go_service(name = "api")