Pants Concepts

To use Pants effectively, it helps to understand a few concepts:

  • Goals help users tell pants what actions to take
  • Tasks are the pants modules that run actions
  • Targets describe what files to take those actions upon
  • Target types define the types of operations that can be performed on a target
  • Addresses describe the location of a target in the repo

Goals and Tasks

Goals are the "verbs" of Pants. When you invoke Pants, you name goals on the command line to say what Pants should do. For example, to run tests, you would invoke Pants with the test goal. To create a bundle--an archive containing a runnable binary and resource files--you would invoke Pants with the bundle goal.

Tasks actually perform the work. When you invoke a goal, it finds all the tasks needed to make that goal work. Sometimes, there is a one to one mapping between a task name and a goal name, but often multiple tasks are registered in a goal. For example, the junit task runs tests for JVM projects. It is registered in the test goal and sometimes referred to as test.junit.

In a nutshell, goals are the way users specify tasks. Sometimes goals are helpful groupings of tasks to make it easier for users to tell pants what they want to do.

Tasks can depend on other tasks. When you invoke a goal in Pants, it builds a list of dependent tasks that need to be executed first. So, when you invoke the test goal, it will also invoke the tasks in the gen, resolve and compile goals, because tasks for code generation, artifact resolution, and compilation need to occur before a test can be run. To find out more about how task dependencies are computed, read about product types in Developing a Pants Task.

Targets

Targets are the "nouns" of Pants, things pants can act upon. You annotate your source code with BUILD files to define these targets. For example, if your tests/com/twitter/mybird/ directory contains JUnit tests, you have a tests/com/twitter/mybird/BUILD file with a junit_tests target definition. As you change your source code, you'll occasionally change the set of Targets by editing BUILD files. E.g., if you refactor some code, moving part of it to a new directory, you'll probably set up a new BUILD file with a target to build that new directory's code.

Targets can "depend" on other targets. For example, if your foo code imports code from another target bar, then foo depends on bar. You specify this dependency in foo's target definition in its BUILD file. If you invoke Pants to compile foo, it "knows" it also needs to compile bar, and does so.

Target Types

Target Types describe the kind of target to be operated on. Each Pants build target has a type, such as java_library or python_binary. Tasks choose to work on particular targets by selecting the target types they are interested in from the build graph.

For a list of all Target types (and other things that can go in BUILD files), see the BUILD Dictionary. The following list describes the most common target types:

Library Targets
To define an "importable" thing, you want a library target type, such as java_library or python_library. Another target whose code imports a library target's code should list the library target in its dependencies.

Binary Targets
To define a "runnable" thing, you want a jvm_binary or python_binary target. A binary probably has a main and dependency libraries. (We encourage a binary's main to be separate from the libraries it uses to run, if any.)

External Dependencies
Not everything is source code is in your repository. Your targets can depend on .jars or .whls from elsewhere.

Test Targets
To define a collection of tests, you want a junit_tests or python_tests target. The test target depends upon the targets whose code it tests. This isn't just logical, it's handy, too: you can compute dependencies to figure out what tests to run if you change some target's code.

Addresses

Addresses describe the location of a target in the build graph. The address has two parts: the directory to the BUILD file and the name of the target within that BUILD file.

For example, if you have a tests/com/twitter/mybird/BUILD file with a junit_tests(name='test-flight) target definition, you would write the address as:

tests/com/twitter/mybird:test-flight

You use an address whenever you specify a target to build on the command line or when one target depends on another in a BUILD file. To find out more about addresses, see Target Addresses.

What Pants Does

When you invoke Pants, you specify goals (actions to take) and targets (things to act upon).

Pants plans a list of tasks. You specify one or more goals on the command line. Pants finds the tasks needed to complete that goal. Pants knows that some tasks depend on others. If you invoke Pants with, say, the test goal to test some code, Pants knows it must first compile code; before it can compile code, it needs to resolve artifact dependencies and generate code from IDL files (e.g., Thrift). Pants thus generates a topologically-sorted list of goals and tasks to perform, a build execution plan. This plan might look something like

./pants --explain test src/java::
Goal Execution Order:

bootstrap -> imports -> unpack-jars -> deferred-sources -> gen
   -> jvm-platform-validate -> resolve -> compile -> resources -> test

Pants does not consider targets while planning; some of these tasks might thus turn out to be no-ops. E.g., Pants might plan a gen (generate code) task even if you don't, in fact, use any generated code.

Pants computes a target dependencies graph. It starts with the target[s] you specify on the command line. It notes which targets they depend on, which targets those targets depend on, which targets those targets depend on, and so on.

Pants then attempts to carry out its planned tasks. It proceeds goal by goal. If it has a problem carrying out one task, it does not continue to the others. (Thus, if you attempt to test targets A and B, but there's a compilation error in A, then Pants won't test B even if it compiled fine.)

For each task in the plan, Pants executes that task to all targets in its computed dependency tree[s]. It starts with depended-upon targets and works its way up to depending targets. Each Pants target has a type; Pants uses this to determine how to apply a task to that target. In many cases, applying a task to a target is a no-op. In the more interesting cases, Pants does something. It probably invokes other tools. For example, depending on the code in the relevant targets, that "compile" goal might invoke javac a few times and scalac.

Pants caches things it builds. Thus, if you change one source file and re-build, Pants probably doesn't "build the world." It just builds a few things. Pants keys its cache on hashed file contents. This is a straightforward way to build the right things after some files' contents change. (It can surprise you if you touch a file, start a compile, and nothing happens. If you want to, e.g., see Foo.java's compile warnings again, instead of using touch, you might append a newline.)

Next Step

If you're ready to give Pants a try, go to First Tutorial.

Generated by publish_docs from dist/markdown/html/src/docs/first_concepts.html 2022-12-03T01:08:59.486561