Bazel Blog

Introducing Bazel Cquery

(If you’re already familiar with bazel’s query command, skip ahead to the section titled ‘Introducing Cquery’.)

Querying Your Build

Have you ever wondered why making a certain change causes Bazel to rebuild a seemingly unrelated binary? Have you ever wanted to run a particular subset of tests based on some shared attribute value but didn’t know how to gather them? Or maybe you have targets that work across a variety of platforms and you want to make sure none of them rely on incompatible dependencies? Bazel’s query commands can help remedy such issues.

Bazel uses the Bazel Query Language to articulate questions such as those listed above. This language includes a set of functions, such as ‘deps’ and ‘somepath’, which can be combined and nested to describe more complex queries.

The query commands also support a set of flag-based options that govern query behavior. For example, --noimplicit_deps limits results to only include targets that are explicitly declared in a BUILD file. The flags also support a selection of output formats for the results of the query, such as a graph output (especially helpful for visualizing the collection of paths between two nodes).

Introducing bazel cquery

Bazel has long supported the query command. We’re excited to announce a second* command, cquery!

What’s the difference? query doesn’t understand build flags and returns all possible answers to a given query expression. cquery (configurable query) runs at a later point in the build process, after flag evaluation and configurable attribute resolution. Thus, it can understand build options and give the answer to a specific Bazel invocation as dictated by its set of flags. Since it runs at a later point, cquery is by nature slower than query.

cquery has yet to reach feature parity with query, but as we develop it further, cquery will also be able to expose information to which query does not have access. See the comparison below for what’s currently supported.

query cquery both
Performance faster, less acurate slower, accurate
Functions siblings, buildfiles, tests config allpaths, attrs, dep, filter, kind, labels, loadfiles, rdeps, somepath, visible
Output Formats build, label, label_kind, minrank, maxrank, location, package, graph, xml labelandconfiguration, transitions proto
Options query options cquery options, build options common query options

So, if your priority is speed and over-approximation of results isn’t a problem, query is your engine. If your priority is results that match a specific bazel invocation’s flags and fancy output formats aren’t too important to you, then cquery is the better choice.

* Bazel also supports the Sky Query engine which automatically kicks in with a specific set of options used with query. It supports a few extra functions and in some circumstances may be faster and less memory-intensive than query

Some Motivating Examples

The library //third_party/zlib:zlibonly isn't in the BUILD file for //foo:foo, but it is an indirect dependency. Why?

$ bazel query "somepath(//foo:foo, third_party/zlib:zlibonly)"
//foo:foo
//translations/tools:translator
//translations/base:base
//third_party/py/MySQL:MySQL
//third_party/py/MySQL:_MySQL.so
//third_party/mysql:mysql
//third_party/zlib:zlibonly

The Bazel query how-to contains many common examples of how query is used. Many of the examples can also apply to cquery. In the following example, we see where cquery’s strength lies (properly resolving a select statement).

# tree/BUILD
sh_library(
    name = "ash",
    deps = select({
        ":excelsior": [":manna-ash"],
        ":americana": [":white-ash"],
        "//conditions:default": [":common-ash"],
    }),
)
sh_library(name = "manna-ash")
sh_library(name = "white-ash")
sh_library(name = "common-ash")
config_setting(
    name = "excelsior",
    values = {"define": "species=excelsior"},
)
config_setting(
    name = "americana",
    values = {"define": "species=americana"},
)
# Traditional query
$ bazel query "deps(//tree:ash)" --define species=excelsior
...error because query does not understand --define...

$ bazel query "deps(//tree:ash)"
//tree:ash
//tree:white-ash
//tree:manna-ash
//tree:common-ash
...

# cquery
$ bazel cquery "deps(//tree:ash)" --define species=excelsior
//tree:ash (hash-of-config)
//tree:manna-ash (hash-of-config)
...