Skip to main content

Query

The query plugin selects targets dynamically. Instead of listing targets by address, you write a predicate — a boolean expression over labels, package paths, and addresses — and heph resolves the matching set for you. New targets that satisfy the predicate are picked up automatically as the repo grows; targets that no longer match drop out without anyone editing a list.

A query expands into a group of matching targets, so you can pass it anywhere a list of targets is accepted. See group for how groups execute.

Provider

A provider is a plugin that contributes targets to the graph without registering its own execution driver — the targets it emits run on an existing driver. The query plugin is a provider: it emits group targets that run on the group driver. It registers no driver of its own.

Enabling it

Built-in and always-on; no registration required in .hephconfig.

Query language

Queries are written in a small expression language.

Patterns

PatternMatches
//pkgexactly package //pkg
//pkg/...//pkg and every package beneath it
//pkg:namethe single target //pkg:name
.the current package
./subthe sub-package relative to the current directory
../siblinga sibling package

A bare //foo (no :) is a package matcher. To match a specific target address, include the colon: //foo:bar.

Functions

FunctionMatches
label(x)targets that carry label x
addr(//pkg:name)the target at that address
package(//pkg)targets in exactly that package
package_prefix(//pkg)targets in //pkg or any sub-package
tree_output(pkg)targets whose codegen tree writes into pkg

Labels containing spaces can be quoted: label("my label").

Operators

OperatorMeaning
a && bboth must match
a || beither must match
!anegate
(…)grouping

Precedence: ! binds tightest, then &&, then ||. Use parentheses to override.

Grammar

or := and ( "||" and )*
and := not ( "&&" not )*
not := "!" not | atom
atom := "(" or ")" | func | pattern

Examples

# All lint targets under //cmd, excluding the vendor subtree
heph run -e '//cmd/... && label(lint) && !//cmd/vendor/...'

# Every target in either //a or //b
heph query -e '//a/... || //b/...'

# Targets generating code for the gen package
heph run -e 'tree_output(gen)'

# Combine group membership with a codegen scope
heph run -e '(//a/... || //b/...) && tree_output(gen)'

CLI: -e / --expr

heph run and heph query accept -e <EXPR> (long form --expr) as an alternative to a positional target address. The flag and the positional arguments are mutually exclusive — use one or the other.

heph run //pkg:name # positional address (unchanged)
heph run label //pkg/... # label + package matcher (unchanged)
heph run -e '//pkg/... && label(ci)' # query expression

heph query -e '//... && !//vendor/...'
note

The old -e/--exclude flag was removed. Use ! inside a query expression to exclude targets — for example //... && !//vendor/....

query() in BUILD files

The query() builtin lets a BUILD file select targets dynamically, the same way file() and glob() select files. Pass a query expression; query() returns an address that the engine expands at build time.

BUILD
# Build every target with the lint label in this subtree
target(
name = "lint-all",
driver = "group",
deps = [query("//... && label(lint)")],
)

Relative patterns in a query() expression resolve against the package that contains the BUILD file. query() sees sibling targets in the same BUILD file — it opts out of the engine's automatic provider exclusion so the query can enumerate its own package.

app/BUILD
lib = target(name = "lib", driver = "bash", run = "...", out = "lib", labels = ["lint"])
test = target(name = "test", driver = "bash", run = "...", out = "test", labels = ["lint"])
util = target(name = "util", driver = "bash", run = "...", out = "util")

# Selects //app:lib and //app:test; //app:util has no lint label.
target(
name = "lint",
driver = "group",
deps = [query("label(lint)")],
)

@heph/query addresses

Under the hood, query() and -e both produce @heph/query addresses. The address format is:

//@heph/query:query@expr=<expression>

You rarely need to write these by hand — query() and -e generate them for you. The table in Addresses → Built-in @heph/* packages lists them for reference.