Skip to main content
A target A depends upon a target B if B is needed by A at build or execution time. The depends upon relation induces a Directed Acyclic Graph (DAG) over targets, and it is called a dependency graph. A target’s direct dependencies are those other targets reachable by a path of length 1 in the dependency graph. A target’s transitive dependencies are those targets upon which it depends via a path of any length through the graph. In fact, in the context of builds, there are two dependency graphs, the graph of actual dependencies and the graph of declared dependencies. Most of the time, the two graphs are so similar that this distinction need not be made, but it is useful for the discussion below.

Actual and declared dependencies

A target X is actually dependent on target Y if Y must be present, built, and up-to-date in order for X to be built correctly. Built could mean generated, processed, compiled, linked, archived, compressed, executed, or any of the other kinds of tasks that routinely occur during a build. A target X has a declared dependency on target Y if there is a dependency edge from X to Y in the package of X. For correct builds, the graph of actual dependencies A must be a subgraph of the graph of declared dependencies D. That is, every pair of directly-connected nodes x --> y in A must also be directly connected in D. It can be said that D is an overapproximation of A. Important: D should not be too much of an overapproximation of A because redundant declared dependencies can make builds slower and binaries larger. BUILD file writers must explicitly declare all of the actual direct dependencies for every rule to the build system, and no more. Failure to observe this principle causes undefined behavior: the build may fail, but worse, the build may depend on some prior operations, or upon transitive declared dependencies the target happens to have. Bazel checks for missing dependencies and report errors, but it’s not possible for this checking to be complete in all cases. You need not (and should not) attempt to list everything indirectly imported, even if it is needed by A at execution time. During a build of target X, the build tool inspects the entire transitive closure of dependencies of X to ensure that any changes in those targets are reflected in the final result, rebuilding intermediates as needed. The transitive nature of dependencies leads to a common mistake. Sometimes, code in one file may use code provided by an indirect dependency — a transitive but not direct edge in the declared dependency graph. Indirect dependencies don’t appear in the BUILD file. Because the rule doesn’t directly depend on the provider, there is no way to track changes, as shown in the following example timeline: