Skip to main content
Version: 2.8 (deprecated)

Targets and BUILD files

Metadata for your code.


Most goals require metadata about your code. For example, to run a test, you need to know about all the transitive dependencies of that test. You may also want to set a timeout on that test.

Targets are an addressable set of metadata describing your code.

For example:

  • shell_source and python_test describe first-party code
  • python_requirement describes third-party requirements
  • pex_binary and archive describe artifacts you'd like Pants to build

To reduce boilerplate, some targets also generate other targets:

  • python_tests -> python_test
  • shell_sources -> shell_source
  • go_mod -> go_third_party_package

BUILD files

Targets are defined in files with the name BUILD. For example:

helloworld/greet/BUILD
python_tests(
name="tests",
timeout=120,
)

pex_binary(
name="bin",
entry_point="app.py:main",
)

Each target type has different fields, or individual metadata values. Run ./pants help $target to see which fields a particular target type has, e.g. ./pants help file. Most fields are optional and use sensible defaults.

All target types have a name field, which is used to identify the target. Target names must be unique within a directory.

Use ./pants tailor to automate generating BUILD files.

Target addresses

A target is identified by its unique address, in the form path/to/dir:name. The above example has the addresses helloworld/greet:tests and helloworld/greet:bin.

Addresses are used in the dependencies field to depend on other targets. Addresses can also be used as command-line arguments, such as ./pants fmt path/to:tgt.

(Generated targets have a variant of this syntax; see the section "Target generation" below.)

Default for the name field

The name field defaults to the directory name. So, this target has the address helloworld/greet:greet.

# helloworld/greet/BUILD
python_sources()

You can refer to this target with either helloworld/greet:greet or the abbreviated form helloworld/greet.

Use //:tgt for the root of your repository

Addressed defined in the BUILD file at the root of your repository are prefixed with //, e.g. //:my_tgt.

source and sources field

Targets like python_test and resource have a source: str field, while target generators like python_tests and resources have a sources: list[str] field. This determines which source files belong to the target.

Values are relative to the BUILD file's directory. Sources must be in or below this directory, i.e. ../ is not allowed.

The sources field also supports * and ** as globs. To exclude a file or glob, prefix with !. For example, ["*.py", "!exclude_*.py"] will include f.py but not exclude_me.py.

BUILD
resource(name="logo", source="logo.png")

python_tests(
name="tests",
sources=["*_test.py"],
)
Overlapping source fields can cause confusion

It's valid to include the same file in the source / sources field for multiple targets. When would you do this? Sometimes you may have conflicting metadata for the same source file, such as toggling between a GPU vs. CPU version of a dependency:

python_source(
name="lib_cpu",
source="lib.py",
dependencies=["3rdparty:tensorflow"],
)

python_source(
name="lib_gpu",
source="lib.py",
dependencies=["3rdparty:tensorflow-gpu"],
)

However, including the same file in the source / sources field for multiple targets can result in two confusing behaviors:

  • Pants will no longer be able to infer dependencies on this file because it cannot disambiguate which of the targets you want to use. You must use explicit dependencies instead.
  • File arguments will run over all owning targets, e.g. ./pants test path/to/test.ext would run both test targets as two separate subprocesses, even though you might only expect a single subprocess.

You can run ./pants list path/to/file.ext to see all "owning" targets to check if >1 target has the file in its source field.

dependencies field

A target's dependencies determines which other first-party code and third-party requirements to include when building the target.

Usually, you leave off the dependencies field thanks to dependency inference. Pants will read your import statements and map those imports back to your first-party code and your third-party requirements. You can run ./pants dependencies path/to:target to see what dependencies Pants infers.

However, dependency inference cannot infer everything, such as dependencies on resource and file targets.

To add an explicit dependency, add the target's address to the dependencies field. This augments any dependencies that were inferred.

helloworld/greet/BUILD
python_sources(
name="lib",
dependencies=[
"3rdparty/python:ansicolors",
"assets:logo,
],
)

You only need to declare direct dependencies. Pants will pull in transitive dependencies—i.e. the dependencies of your dependencies—for you.

Ignore dependencies with ! and !!

If you don't like that Pants inferred a certain dependency—as reported by ./pants dependencies path/to:tgt—tell Pants to ignore it with !:

python_sources(
name="lib",
dependencies=["!3rdparty/python:numpy"],
)

You can use the prefix !! to transitively exclude a dependency, meaning that even if a target's dependencies include the bad dependency, the final result will not include the value.

Transitive excludes can only be used in target types that conventionally are not dependend upon by other targets, such as pex_binary and python_test / python_tests. This is meant to limit confusion, as using !! in something like a python_source / python_sources target could result in surprising behavior for everything that depends on it. (Pants will print a helpful error when using !! when it's not legal.)

Target generation

To reduce boilerplate, Pants provides target types that generate other targets. For example:

  • files -> file
  • python_tests -> python_test
  • go_mod -> go_third_party_package

Usually, prefer these target generators. ./pants tailor will automatically add them for you.

Run ./pants help targets to see how the target determines what to generate. Targets for first-party code, like resources and python_tests, will generate one target for each file in their sources field.

python_sources(
name="lib",
# Will generate two `python_source` targets.
sources=["app.py", "util.py"],
)

(Usually, you can leave off the sources field. When possible, it defaults to all relevant files in the current directory.)

Typically, fields declared in the target generator will be inherited by each generated target. For example, if you set timeout=120 in a python_tests target, each generated python_test target will have timeout=120. You can instead use the overrides field for more granular metadata:

helloworld/BUILD
python_tests(
name="tests",
# This applies to every generated target.
extra_env_vars=["MY_ENV_VAR"],
# These only apply to the relevant generated targets.
overrides={
"dirutil_test.py": {"timeout": 30},
("osutil_test.py", "strutil_test.py"): {"timeout": 15},
},
)

The address for generated targets depends if the generated target is for first-party code or not:

Generated target typeGenerated address syntax
First-party, e.g. python_source and filepath/to/file.ext:tgt_generator

Example: src/py/app.py:lib

The address always starts with the path to the file.

If the file lives in the same directory as the target generator and the target generator left off the name field, you can use just the file path. For example, src/py/app.py (without the :lib suffix).

If the file lives in a subdirectory of the target generator, the suffix will look like ../tgt_generator. For example, src/py/subdir/f.py:../lib, where the target generator is src/py:lib.
All other targets, e.g. go_third_party_packagepath/to:tgt_generator#generated_name

Example:src/go:mod#github.com/google/uuid

Run ./pants help $target_type on the target generator to see how it sets the generated name. For example, go_mod uses the Go package's name.

If the target generator left off the name field, you can leave it off for the generated address too, e.g. src/go#github.com/google/uuid (without the :mod portion).

Run ./pants list dir: in the directory of the target generator to see all generated target addresses, and ./pants peek dir: to see all their metadata.

You can use the address for the target generator as an alias for all of its generated targets. For example, if you have the files target assets:logos, adding dependencies=["assets:logos"]to another target will add a dependency on each generated file target. Likewise, if you have a python_tests target project:tests, then ./pants test project:tests will run on each generated python_test target.

Tip: one BUILD file per directory

Target generation means that it is technically possible to put everything in a single BUILD file.

However, we've found that it usually scales much better to use a single BUILD file per directory. Even if you start with using the defaults for everything, projects usually need to change some metadata over time, like adding a timeout to a test file or adding dependencies on resources.

It's useful for metadata to be as fine-grained as feasible, such as by using the overrides field to only change the files you need to. Fine-grained metadata is key to having smaller cache keys (resulting in more cache hits), and allows you to more accurately reflect the status of your project. We have found that using one BUILD file per directory encourages fine-grained metadata by defining the metadata adjacent to where the code lives.

./pants tailor will automatically create targets that only apply metadata for the directory.