Skip to content

Commit 3dbb7b6

Browse files
authored
Merge branch 'main' into ramon/add-build_file_path-var
2 parents 4624ce1 + fea6313 commit 3dbb7b6

23 files changed

+1064
-102
lines changed

docs/docs/using-pants/advanced-target-selection.mdx

+34-1
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ Whereas `tags` are useful for _decentralized_ allow/block lists, `--spec-files`
145145
To pipe a Pants run, use your shell's `|` pipe operator and `xargs`:
146146

147147
```bash
148-
pants dependents helloworld/util | xargs pants list
148+
pants dependents helloworld/util | xargs pants list
149149
```
150150

151151
You can, of course, pipe multiple times:
@@ -167,6 +167,14 @@ $ pants dependencies helloworld/util > util_dependencies.txt
167167
$ pants --spec-files=util_dependencies.txt lint
168168
```
169169

170+
Using spec files is also more robust because when piping output of a Pants goal to `xargs`, the specified command
171+
may be invoked by `xargs` as many times as necessary to use up the list of input items.
172+
This may break the structured data output, for instance, when you want to `peek` the targets as JSON:
173+
174+
```bash
175+
$ pants list --filter-target-type=resource :: | xargs pants peek
176+
```
177+
170178
If you don't want to save the output to an actual file—such as to not pollute version control—you can use a variable and a named pipe:
171179

172180
```bash
@@ -185,3 +193,28 @@ For other goals, you can leverage shell piping to partition the input targets in
185193
```bash
186194
pants list :: | awk 'NR % 5 == 0' | xargs pants package
187195
```
196+
197+
## Using CLI aliases
198+
199+
If setting tags on individual targets is not feasible, there are a few other options available to refer to multiple targets.
200+
201+
If you have an operation that you perform often on a certain group of targets, you can use the
202+
[cli](../../reference/subsystems/cli) subsystem options to create shortcuts. For instance, this alias
203+
would let you run `pants publish-libraries` to publish all Python distributions declared in the `src/libA` and `src/libB`
204+
directories.
205+
206+
```toml title="pants.toml"
207+
[cli.alias]
208+
publish-libraries = "--filter-target-type=python_distribution --filter-address-regex=\"['^src/libA/,^src/libB/']\" publish src::"
209+
```
210+
211+
You can use any argument or goal, and the alias doesn't need to be a "full" invocation of Pants.
212+
For instance, you could combine filtering arguments along with `--changed-since` flag and a tag to refer to long-running
213+
integration tests that have been recently modified:
214+
215+
```toml title="pants.toml"
216+
[cli.alias]
217+
--integration-long = "--changed-since --filter-target-type=python_test --tag=long"
218+
```
219+
220+
You can now invoke `pants --integration-long test tests::` to run the relevant tests.

docs/docs/using-pants/key-concepts/targets-and-build-files.mdx

+44
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,50 @@ You can use the prefix `!!` to transitively exclude a dependency, meaning that e
166166
Transitive excludes can only be used in target types that conventionally are not depended upon by other targets, such as `pex_binary`, `python_distribution`, 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.)
167167
:::
168168

169+
## Using the generic `target`
170+
171+
[`target`](../../..reference/targets/target) is a generic target with no specific type.
172+
It can be used as a generic collection of targets to group related, but distinct targets into one single target.
173+
174+
### Referring to a group of targets
175+
176+
You could use the generic `target` when you need to group multiple targets to refer to them as a unit
177+
(a single dependency) to reduce repetition:
178+
179+
```python title="BUILD"
180+
target(
181+
name="python-libs",
182+
dependencies=["src/python/libraries/libA", "src/python/libraries/libB"],
183+
)
184+
```
185+
186+
If declared in the root of your workspace, you can now address the Python libraries by `//:python-libs`:
187+
188+
```bash
189+
$ pants dependencies //:python-libs
190+
````
191+
192+
### Creating aliases for targets
193+
194+
If you have some targets declared in BUILD files that are stored deep within the directory structure of your workspace,
195+
you can make it easier to refer to that target when listing that target among dependencies of other targets.
196+
197+
For example, you can simplify accessing a target by creating another target that will serve as an alias definition in
198+
a BUILD file stored in a more convenient location in the workspace, for instance, in the build root directory:
199+
200+
```python title="BUILD"
201+
target(
202+
name="deployment-bins",
203+
dependencies=["src/golang/production/cloud/deployment/binaries:tools"]
204+
)
205+
```
206+
207+
You can now refer to that target more concisely in BUILD files:
208+
209+
```python title="BUILD"
210+
python_sources(dependencies=["//:deployment-bins"])
211+
```
212+
169213
## Field default values
170214

171215
As mentioned above in [BUILD files](./targets-and-build-files.mdx#build-files), most target fields have sensible defaults. And it's easy to override those values on a specific target. But applying the same non-default value on many targets can get unwieldy, error-prone and hard to maintain. Enter `__defaults__`.

docs/docs/using-pants/project-introspection.mdx

+4
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,10 @@ pants dependents helloworld/main.py:lib | xargs pants peek --exclude-defaults
250250
]
251251
```
252252

253+
Keep in mind, however, that the `peek` goal may be invoked by `xargs` as many times as necessary to use up the list
254+
of input items. This may break the structured data output, so it may be safer to use the
255+
[`--spec-files`](../../reference/global-options#spec_files) option.
256+
253257
:::
254258

255259
## `paths` - find dependency paths

docs/docs/writing-plugins/the-rules-api/rules-and-the-target-api.mdx

+25-1
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ class MyTarget(Target):
235235
Then, to resolve the addresses, you can use `UnparsedAddressInputs`:
236236

237237
```python
238-
from pants.engine.addresses import Address, Addresses, UnparsedAddressInputs
238+
from pants.engine.addresses import Addresses, UnparsedAddressInputs
239239
from pants.engine.target import Targets
240240
from pants.engine.rules import Get, rule
241241

@@ -291,6 +291,30 @@ async def demo(...) -> Foo:
291291

292292
`SourceFilesRequest` expects an iterable of `SourcesField` objects. `SourceFiles` has a field `snapshot: Snapshot` with the merged snapshot of all resolved input sources fields.
293293

294+
To convert a list of target addresses to existing source file names, you can request `HydratedSources` for every input target:
295+
296+
```python
297+
from itertools import chain
298+
from pants.engine.addresses import Addresses
299+
from pants.engine.collection import DeduplicatedCollection
300+
from pants.engine.rules import Get, MultiGet, rule
301+
from pants.engine.target import (HydratedSources, HydrateSourcesRequest, SourcesField, UnexpandedTargets)
302+
303+
304+
class ProjectSources(DeduplicatedCollection[str]):
305+
pass
306+
307+
308+
@rule
309+
async def addresses_to_source_files(addresses: Addresses) -> ProjectSources:
310+
targets = await Get(UnexpandedTargets, Addresses, addresses)
311+
all_sources = await MultiGet(Get(HydratedSources, HydrateSourcesRequest(tgt.get(SourcesField))) for tgt in targets)
312+
return ProjectSources(chain.from_iterable(sources.snapshot.files for sources in all_sources))
313+
```
314+
315+
This is often useful when you need to pass target addresses to commands that are not Pants goals and would not
316+
be able to interpret them properly.
317+
294318
### Enabling codegen
295319

296320
If you want your plugin to work with code generation, you must set the argument `enable_codegen=True`, along with `for_sources_types` with the types of `SourcesField` you're expecting.

src/python/pants/backend/docker/goals/package_image.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,8 @@ def parse_image_id_from_docker_build_output(docker: DockerBinary, *outputs: byte
522522
(
523523
# BuildKit output.
524524
r"(writing image (?P<digest>sha256:\S+) done)",
525+
# BuildKit with containerd-snapshotter output.
526+
r"(exporting manifest list (?P<manifest_list>sha256:\S+) done)",
525527
# Docker output.
526528
r"(Successfully built (?P<short_id>\S+))",
527529
),
@@ -540,7 +542,11 @@ def parse_image_id_from_docker_build_output(docker: DockerBinary, *outputs: byte
540542
None,
541543
)
542544
if image_id_match:
543-
image_id = image_id_match.group("digest") or image_id_match.group("short_id")
545+
image_id = (
546+
image_id_match.group("digest")
547+
or image_id_match.group("short_id")
548+
or image_id_match.group("manifest_list")
549+
)
544550
return image_id
545551

546552
return "<unknown>"

src/python/pants/backend/docker/goals/package_image_test.py

+41
Original file line numberDiff line numberDiff line change
@@ -1646,6 +1646,47 @@ def test_get_context_root(
16461646
),
16471647
"",
16481648
),
1649+
# Buildkit with containerd-snapshotter
1650+
(
1651+
DockerBinary("/bin/docker", "1234", is_podman=False),
1652+
"sha256:b2b51838586286a9e544ddb31b3dbf7f6a99654d275b6e56b5f69f90138b4c0e",
1653+
dedent(
1654+
"""\
1655+
#9 exporting to image
1656+
#9 exporting layers done
1657+
#9 exporting manifest sha256:7802087e8e0801f6451d862a00a6ce8af3e4829b09bc890dea0dd2659c11b25a done
1658+
#9 exporting config sha256:c83bed954709ba0c546d66d8f29afaac87c597f01b03fec158f3b21977c3e143 done
1659+
#9 exporting attestation manifest sha256:399891f9628cfafaba9e034599bdd55675ac0a3bad38151ed1ebf03993669545 done
1660+
#9 exporting manifest list sha256:b2b51838586286a9e544ddb31b3dbf7f6a99654d275b6e56b5f69f90138b4c0e done
1661+
#9 naming to myhost.com/my_app:latest done
1662+
#9 unpacking to myhost.com/my_app:latest done
1663+
#9 DONE 0.0s
1664+
"""
1665+
),
1666+
"",
1667+
),
1668+
# Buildkit with containerd-snapshotter and cross platform
1669+
(
1670+
DockerBinary("/bin/docker", "1234", is_podman=False),
1671+
"sha256:3c72de0e05bb75247e68e124e6500700f6e0597425db2ee9f08fd59ef28cea0f",
1672+
dedent(
1673+
"""\
1674+
#12 exporting to image
1675+
#12 exporting layers done
1676+
#12 exporting manifest sha256:452598369b55c27d752c45736cf26c0339612077f17df31fb0cdd79c5145d081 done
1677+
#12 exporting config sha256:6fbcebfde0ec24b487045516c3b5ffd3f0633e756a6d5808c2e5ad75809e0ca6 done
1678+
#12 exporting attestation manifest sha256:32fcf615e85bc9c2f606f863e8db3ca16dd77613a1e175e5972f39267e106dfb done
1679+
#12 exporting manifest sha256:bcb911a3efbec48e3c58c2acfd38fe92321eed731c53253f0b5c883918420187 done
1680+
#12 exporting config sha256:86e7fd0c4fa2356430d4ca188ed9e86497b8d03996ccba426d92c7e145e69990 done
1681+
#12 exporting attestation manifest sha256:66f9e7af29dd04e6264b8e113571f7b653f1681ba124a386530145fb39ff0102 done
1682+
#12 exporting manifest list sha256:3c72de0e05bb75247e68e124e6500700f6e0597425db2ee9f08fd59ef28cea0f done
1683+
#12 naming to myhost.com/my_app:latest done
1684+
#12 unpacking to myhost.com/my_app:latest done
1685+
#12 DONE 0.0s
1686+
"""
1687+
),
1688+
"",
1689+
),
16491690
# Podman
16501691
(
16511692
DockerBinary("/bin/podman", "abcd", is_podman=True),

src/python/pants/backend/project_info/dependencies.py

+17-13
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,7 @@ class DependenciesSubsystem(LineOriented, GoalSubsystem):
4141
)
4242
closed = BoolOption(
4343
default=False,
44-
help="Include the input targets in the output, along with the dependencies. This option "
45-
"only applies when using the `text` format.",
44+
help="Include the input targets in the output, along with the dependencies.",
4645
)
4746
format = EnumOption(
4847
default=DependenciesOutputFormat.text,
@@ -58,11 +57,7 @@ class Dependencies(Goal):
5857
async def list_dependencies_as_json(
5958
addresses: Addresses, dependencies_subsystem: DependenciesSubsystem, console: Console
6059
) -> None:
61-
"""Get dependencies for given addresses and list them in the console in JSON.
62-
63-
Note that `--closed` option is ignored as it doesn't make sense to duplicate source address in
64-
the list of its dependencies.
65-
"""
60+
"""Get dependencies for given addresses and list them in the console in JSON."""
6661
# NB: We must preserve target generators for the roots, i.e. not replace with their
6762
# generated targets.
6863
target_roots = await Get(UnexpandedTargets, Addresses, addresses)
@@ -80,10 +75,16 @@ async def list_dependencies_as_json(
8075
)
8176

8277
iterated_targets = []
83-
for transitive_targets in transitive_targets_group:
84-
iterated_targets.append(
85-
sorted([str(tgt.address) for tgt in transitive_targets.dependencies])
86-
)
78+
for idx, transitive_targets in enumerate(transitive_targets_group):
79+
targets_collection = {
80+
str(tgt.address)
81+
for tgt in (
82+
transitive_targets.closure
83+
if dependencies_subsystem.closed
84+
else transitive_targets.dependencies
85+
)
86+
}
87+
iterated_targets.append(sorted(targets_collection))
8788

8889
else:
8990
dependencies_per_target_root = await MultiGet(
@@ -98,8 +99,11 @@ async def list_dependencies_as_json(
9899
)
99100

100101
iterated_targets = []
101-
for targets in dependencies_per_target_root:
102-
iterated_targets.append(sorted([str(tgt.address) for tgt in targets]))
102+
for idx, targets in enumerate(dependencies_per_target_root):
103+
targets_collection = {str(tgt.address) for tgt in targets}
104+
if dependencies_subsystem.closed:
105+
targets_collection.add(str(target_roots[idx].address))
106+
iterated_targets.append(sorted(targets_collection))
103107

104108
# The assumption is that when iterating the targets and sending dependency requests
105109
# for them, the lists of dependencies are returned in the very same order.

src/python/pants/backend/project_info/dependencies_test.py

+76
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,32 @@ def test_python_dependencies_output_format_json_direct_deps(rule_runner: PythonR
282282
],
283283
},
284284
)
285+
# input: directory, recursively, closed
286+
assert_deps(
287+
specs=["some::"],
288+
transitive=False,
289+
closed=True,
290+
expected={
291+
"some/target:target": [
292+
"some/target/a.py",
293+
"some/target:target",
294+
],
295+
"some/target/a.py": [
296+
"3rdparty/python:req1",
297+
"dep/target/a.py",
298+
"some/target/a.py",
299+
],
300+
"some/other/target:target": [
301+
"some/other/target/a.py",
302+
"some/other/target:target",
303+
],
304+
"some/other/target/a.py": [
305+
"3rdparty/python:req2",
306+
"some/other/target/a.py",
307+
"some/target/a.py",
308+
],
309+
},
310+
)
285311
assert_deps(
286312
specs=["some/other/target:target"],
287313
transitive=True,
@@ -295,6 +321,21 @@ def test_python_dependencies_output_format_json_direct_deps(rule_runner: PythonR
295321
]
296322
},
297323
)
324+
assert_deps(
325+
specs=["some/other/target:target"],
326+
transitive=True,
327+
closed=True,
328+
expected={
329+
"some/other/target:target": [
330+
"3rdparty/python:req1",
331+
"3rdparty/python:req2",
332+
"dep/target/a.py",
333+
"some/other/target/a.py",
334+
"some/other/target:target",
335+
"some/target/a.py",
336+
]
337+
},
338+
)
298339

299340

300341
def test_python_dependencies_output_format_json_transitive_deps(
@@ -366,3 +407,38 @@ def test_python_dependencies_output_format_json_transitive_deps(
366407
],
367408
},
368409
)
410+
411+
# input: directory, recursively, closed
412+
assert_deps(
413+
specs=["some::"],
414+
transitive=True,
415+
closed=True,
416+
expected={
417+
"some/target:target": [
418+
"3rdparty/python:req1",
419+
"dep/target/a.py",
420+
"some/target/a.py",
421+
"some/target:target",
422+
],
423+
"some/target/a.py": [
424+
"3rdparty/python:req1",
425+
"dep/target/a.py",
426+
"some/target/a.py",
427+
],
428+
"some/other/target:target": [
429+
"3rdparty/python:req1",
430+
"3rdparty/python:req2",
431+
"dep/target/a.py",
432+
"some/other/target/a.py",
433+
"some/other/target:target",
434+
"some/target/a.py",
435+
],
436+
"some/other/target/a.py": [
437+
"3rdparty/python:req1",
438+
"3rdparty/python:req2",
439+
"dep/target/a.py",
440+
"some/other/target/a.py",
441+
"some/target/a.py",
442+
],
443+
},
444+
)

0 commit comments

Comments
 (0)