BUILD files
A large, well-organized codebase divides into many small components. These components, and the code dependencies between them, form a directed graph.
In Pants parlance, these components are called targets. The information about your
targets and their dependencies lives in files named BUILD
, scattered throughout your
source tree. A BUILD
file in a given directory describes targets that own the
source files in or under that directory.
- See tutorial for an easy introduction to
BUILD
files. - See the BUILD Dictionary for an exhaustive list of all
BUILD
file syntax.
Target Granularity
A target can encapsulate any amount of code, from a single source file to (as a silly hypothetical) the entire codebase. In practice, you'll want to pick a granularity that reflects a "sensible" level at which to express dependencies: Too coarse, and you lose the benefits of fine-grained invalidation. Too fine, and you drown in BUILD boilerplate.
Many programming languages (E.g., Java, Python, Go) have a concept of a package, usually corresponding to a single filesystem directory. It turns out that this is often the appropriate level of granularity for targets. The idiom of having one target per directory, representing a single package, is sometimes referred to as the 1:1:1 rule. It's by no means required, but has proven in practice to be a good rule of thumb. And of course there are always exceptions.
Note that Pants forbids circular dependencies between targets. That is, the dependency graph must be a DAG. In fact, this is good codebase hygiene in general. So if you have any tightly-bound cross dependencies between certain packages, they will all have to be part of a single target until you untangle the dependency "hairball".
Target Definitions
A target definition in a BUILD
file looks something like
java_library( name='util', dependencies = [ '3rdparty:commons-math', '3rdparty:thrift', 'src/java/org/pantsbuild/auth', ':base' ], sources=['*.java', '!Base.java'], tags={'common'}, )
type
Each target will have a different target type, which is java_library
in this case.
This tells Pants tasks what can be done with the target.
name
The target's name, along with the path to its BUILD
file, forms its address.
The address has two important roles:
- It's used on the command line to specify which targets to operate on.
- It's used in other
BUILD
files to reference the target as a dependency.
If a target does not explicitly pass a name, it will be assigned the name of the current directory (of course, at most one target may do this per directory).
dependencies
List of targets that this target depends upon. If this target's code imports or otherwise depends on code in other targets, list those targets here.
- To reference a target
target
defined inpath/to/BUILD
usepath/to:target
. - If the target has the same name as the BUILD file's directory, you can omit the repetition:
path/to/target
instead ofpath/to/target:target
. - If the target is defined in the same BUILD file, you can omit the path:
:target
instead ofpath/to:target
. - More details on how to address targets in a list of dependencies.
sources
The source files in this target. These are defined in one of two ways:
- If the
sources
argument is not passed, many target types define a sensible defaultsources
value to collect all relevant files in the current directory. When possible, this style is recommended because it encourages 1:1:1 (see the Target Granularity section for more information).- For example,
java_library
defaults to collecting['*.java', '!*Test.java']
.
- For example,
- Explicitly providing file names and globs:
sources=['App.java', '*Util.java']
.
You can exclude files by prefixing the file name or glob with !
. For example, to collect unit tests
but not integration tests you could use something like this:
sources=['*_test.py', '!*_integration_test.py']
.
You can also recursively glob over files in all subdirectories of the BUILD file's directory: sources=['**/*.java']
.
However this is discouraged as it tends to lead to coarse-grained dependencies, and Pants's
advantages come into play when you have many fine-grained dependencies.
tags
Tags are a set of strings used to describe or categorize targets. They can be inspected during a build to allow for features such as filtering task targets (ex. skip linting targets with a particular tag) or focused testing (ex. running only unit tests by excluding targets with a integration
tag).
Tags can be configured for targets in three ways:
- Directly in the BUILD
file (tags={'common'}
in the example above)
- In pants.toml
:
[target-tag-assignments] tag_targets_mappings = """ { 'tag1': ['path/to/target:', 'path/to/another/target:bar'], 'tag2': ['path/to/another/target:bar'] } """
- In a
JSON
file, referenced from thepants.toml
or command line, for example:
./pants list src/python/pants/base:exceptions --target-tag-assignments-tag-targets-mappings=@/path/to/target_tag_definitions.json
BUILD.*
files
BUILD files are usually just named BUILD
, but they can also be named BUILD.ext
, with any
extension. Pants considers all files matching BUILD(.*)
in a single directory to be a single
logical BUILD file. In particular, they share a single namespace, so target names must be
distinct across all such files.
This has various uses, such as the ability to separate internal-only BUILD definitions from those
that should be pushed to an open-source mirror of an internal repo: You can put the former
in BUILD.internal
files and the latter in BUILD.oss
files.
Debugging BUILD Files
If you're curious to know how Pants interprets your BUILD
files, these
techniques can be especially helpful:
What targets does a BUILD file define? Use the list
goal:
$ ./pants list src/python/myproject/example src/python/myproject/example:example
Are any BUILD files broken?
List every target to see if there are any errors:
Use the recursive wildcard ::
with the list goal:
$ ./pants list :: ...lots of output... File "pants/commands/command.py", line 79, in __init__ File "pants/commands/goal_runner.py", line 144, in setup_parser File "pants/base/build_graph.py", line 351, in inject_address_closure TransitiveLookupError: great was not found in BUILD file examples/src/java/org/pantsbuild/example/h ello/greet/BUILD. Perhaps you meant: :greet referenced from examples/src/scala/org/pantsbuild/example/hello/welcome:welcome
Do I pull in the transitive dependencies I expect? Use depmap
:
$ ./pants depmap examples/tests/java/org/pantsbuild/example/hello/greet internal-examples.tests.java.org.pantsbuild.example.hello.greet.greet internal-3rdparty.junit internal-3rdparty.hamcrest-core org.hamcrest-hamcrest-core-1.3 junit-junit-dep-4.11 internal-examples.src.java.org.pantsbuild.example.hello.greet.greet internal-examples.src.resources.org.pantsbuild.example.hello.hello junit-junit-dep-4.11 org.hamcrest-hamcrest-core-1.3
What source files do I depend on? Use filedeps
:
$ ./pants filedeps examples/src/java/org/pantsbuild/example/hello/main ~archie/workspace/pants/examples/src/java/org/pantsbuild/example/hello/greet/BUILD ~archie/workspace/pants/examples/src/java/org/pantsbuild/example/hello/main/config/greetee.txt ~archie/workspace/pants/examples/src/resources/org/pantsbuild/example/hello/BUILD ~archie/workspace/pants/examples/src/java/org/pantsbuild/example/hello/main/HelloMain.java ~archie/workspace/pants/examples/src/resources/org/pantsbuild/example/hello/world.txt ~archie/workspace/pants/examples/src/java/org/pantsbuild/example/hello/main/BUILD ~archie/workspace/pants/examples/src/java/org/pantsbuild/example/hello/greet/Greeting.java
Use the -h
flag to get help on these commands and their various options.