There are some Bazel queries I find myself running manually quite often to inspect things as I go. Or, in today’s world, I might have those queries defined in an AGENTS.md file or a skill.
That works fine. But what if there were a way to encode a query into the Bazel target graph and execute it like a normal target?
Enter genquery
genquery is basically a way to turn a Bazel query expression into a Bazel target.
For example, if you want to list all the dependencies of a given target, you might run something like this:
bazel query 'deps(//ios/app:library)'
This would print all the transitive dependencies of our iOS app’s library target (including the target itself).
Now, what if we could instead run:
bazel build //ios/app:deps
And have Bazel produce a text file containing all those dependencies at bazel-bin/ios/app/deps?
Here’s what that would look like in ios/app/BUILD:
genquery(
name = "deps",
expression = "deps(//ios/app:library)",
scope = ["//ios/app:library"],
)
The expression is the query we want to execute, while scope defines which targets the query is allowed to traverse. In this case, that’s the transitive dependency graph of //ios/app:library.
A few other things
Somewhat frustratingly, genquery doesn’t allow wildcard target patterns (e.g. //ios/... or //ios/app:*). But this fits Bazel’s model of explicitness, so it makes sense. If you want to query all targets of a given kind across a package or package tree without explicitly listing them, you’ll still need to use bazel query from the command line.
Of course, there are other attributes besides expression and scope. You can learn more about them in Bazel docs.
Finally, as you might have guessed, other targets can depend on a genquery target. This means the generated text file can be fed into another build rule for further processing.
Conclusion
I think this is an ideal way to encode frequently used Bazel queries for both humans and AI agents. And don’t forget that it can also be useful when writing build tools that consume query results, since those results are now part of the build graph rather than something you have to generate manually.