Some build work, such as counting lines of code, needs only information found in source files, and doesn't require any other data.
Other work may require more information about the source files. For example, to run a test you need to know about all the transitive dependencies of that test.
This extra information lives in entities called
targets. A target is a collection of source files, with some extra metadata attached.
Target information lives in files with the name
BUILD. For example:
python_library( name = 'greet', sources = ['*.py', '!*_test.py'], dependencies = [ "//:translate", ":greetings", "helloworld/util" ], compatibility = ">=3.6", )
Let's break this down:
python_library: The target's type. In this case, a library containing Python code.
name = 'greet': The name of the target, which defaults to the name of the directory but may be explicitly specified. Target names must be unique within a directory.
sources = ['*.py', '!*_test.py']: The source files belonging to the target.
dependencies = [...]: The other targets on which this target directly depends.
Some target types are built in, but most are provided by backends. For example, the
python_library target type is provided by the
All target types have a
name field, and almost all have
dependencies fields. Specific target types will have extra fields, as needed. For example, the
python_binary target has an
entry_point field that points to the main module for the binary.
A target is identified by its
address. A target address has the form
path/to/directory:name. For example, the target in the example above has the address
dependencies field references the targets it depends on using their addresses. Addresses are also sometimes used as command-line arguments.
Addresses are globally unique, which is guaranteed by the fact that target names must be unique within a BUILD file.
If the target name is the same as the name of the directory containing it, then you can omit the target name. For example,
helloworld/greetis an abbreviated form of
Almost all targets have a
sources field, which determines which source files belong to the target.
The value of the
sources field is a list of names and/or glob patterns, relative to the BUILD file's directory. Sources must be in or below this directory, i.e., patterns containing
../ are not allowed.
! prefix excludes matching files that would otherwise be included. For example,
['*.py', '!exclude_*.py'] will include
foo.py but exclude
While it is not recommended for a target to own source files in subdirectories (with the 1:1:1 method preferred), it is possible with the following syntax:
** is a recursive glob that matches zero or more directories. This replaces the pre-v1.25 syntax which used
rglobs to indicate that a glob was recursive.
Almost all targets have a
dependencies field, which determines the direct dependencies of the target.
The value of the
dependencies field is a list of addresses of other targets. If code in target
A directly depends on code in target
B (e.g., by importing a symbol from
A should declare a dependency on
B. There is no need to declare indirect dependencies (that is, transitive dependencies of your direct dependencies).
If you depend on another target in the same BUILD file, you can omit the address and just use the name. For example, for dependencies listed in
helloworld/greet, the string
:greetingsis shorthand helloworld/greet:greetings`.
To cut down on boilerplate:
- A target's
namedefaults to the name of the directory it's in.
- Some target types will have a sensible default for the
For example, the default value for the
sourcesfield of a
['*.py', '!*_test.py', '!test_*.py', '!conftest.py']. I.e., "all Python source files in this target's directory, that aren't test-related".
So, for example, the target above might be rewritten more succinctly as:
python_library( dependencies = [ "//:translate", ":greetings", "helloworld/util" ], compatibility = ">=3.6", )
The set of targets in a repo form the build graph. The vertices in this graph are the targets, and the (directed) edges are the dependencies. The build graph must not contain directed cycles, i.e., it must form a DAG (a Directed Acyclic Graph).
A target's sources can be as fine-grained as a single file, or as course-grained as an entire tree of files. Target granularity has implications for invalidation and caching, and therefore for performance.
Different granularities work best for different scenarios. But we've found that having one library and/or one test target per directory often tends to work well in practice. The default
sources values for various target types reflect this.
Upcoming feature: dependency inference
We are working on a feature where Pants understands your Python import statements and will automatically add them to the
dependenciesfield for your targets. This means that you will be able to leave off
dependenciesin most of your BUILD files.
Updated over 3 years ago