• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pantsbuild / pants / 18517631058

15 Oct 2025 04:18AM UTC coverage: 69.207% (-11.1%) from 80.267%
18517631058

Pull #22745

github

web-flow
Merge 642a76ca1 into 99919310e
Pull Request #22745: [windows] Add windows support in the stdio crate.

53815 of 77759 relevant lines covered (69.21%)

2.42 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.57
/src/python/pants/core/environments/target_types.py
1
# Copyright 2025 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

4
from __future__ import annotations
7✔
5

6
from dataclasses import dataclass
7✔
7

8
from pants.engine.environment import LOCAL_ENVIRONMENT_MATCHER, LOCAL_WORKSPACE_ENVIRONMENT_MATCHER
7✔
9
from pants.engine.platform import Platform
7✔
10
from pants.engine.process import ProcessCacheScope
7✔
11
from pants.engine.target import (
7✔
12
    COMMON_TARGET_FIELDS,
13
    BoolField,
14
    StringField,
15
    StringSequenceField,
16
    Target,
17
)
18
from pants.util.enums import match
7✔
19
from pants.util.strutil import help_text
7✔
20

21

22
class EnvironmentField(StringField):
7✔
23
    alias = "environment"
7✔
24
    default = LOCAL_ENVIRONMENT_MATCHER
7✔
25
    value: str
7✔
26
    help = help_text(
7✔
27
        f"""
28
        Specify which environment target to consume environment-sensitive options from.
29

30
        Once environments are defined in `[environments-preview].names`, you can specify the environment
31
        for this target by its name. Any fields that are defined in that environment will override
32
        the values from options set by `pants.toml`, command line values, or environment variables.
33

34
        You can specify multiple valid environments by using `parametrize`. If
35
        `{LOCAL_ENVIRONMENT_MATCHER}` is specified, Pants will fall back to the `local_environment`
36
        defined for the current platform, or no environment if no such environment exists.
37
        """
38
    )
39

40

41
class FallbackEnvironmentField(StringField):
7✔
42
    alias = "fallback_environment"
7✔
43
    default = None
7✔
44

45

46
class CompatiblePlatformsField(StringSequenceField):
7✔
47
    alias = "compatible_platforms"
7✔
48
    default = tuple(plat.value for plat in Platform)
7✔
49
    valid_choices = Platform
7✔
50
    value: tuple[str, ...]
7✔
51
    help = help_text(
7✔
52
        f"""
53
        Which platforms this environment can be used with.
54

55
        This is used for Pants to automatically determine which environment target to use for
56
        the user's machine when the environment is set to the special value
57
        `{LOCAL_ENVIRONMENT_MATCHER}`. Currently, there cannot be more than one environment target
58
        registered in `[environments-preview].names` for a particular platform. If there is no
59
        environment target for a certain platform, Pants will use the options system instead to
60
        determine environment variables and executable search paths.
61
        """
62
    )
63

64

65
class LocalCompatiblePlatformsField(CompatiblePlatformsField):
7✔
66
    pass
7✔
67

68

69
class LocalFallbackEnvironmentField(FallbackEnvironmentField):
7✔
70
    help = help_text(
7✔
71
        f"""
72
        The environment to fallback to when this local environment cannot be used because the
73
        field `{CompatiblePlatformsField.alias}` is not compatible with the local host.
74

75
        Must be an environment name from the option `[environments-preview].names`, the
76
        special string `{LOCAL_ENVIRONMENT_MATCHER}` to use the relevant local environment, or the
77
        Python value `None` to error when this specific local environment cannot be used.
78

79
        Tip: when targeting Linux, it can be particularly helpful to fallback to a
80
        `docker_environment` or `remote_environment` target. That allows you to prefer using the
81
        local host when possible, which often has less overhead (particularly compared to Docker).
82
        If the local host is not compatible, then Pants will use Docker or remote execution to
83
        still run in a similar environment.
84
        """
85
    )
86

87

88
class LocalEnvironmentTarget(Target):
7✔
89
    alias = "local_environment"
7✔
90
    core_fields = (
7✔
91
        *COMMON_TARGET_FIELDS,
92
        LocalCompatiblePlatformsField,
93
        LocalFallbackEnvironmentField,
94
    )
95
    help = help_text(
7✔
96
        f"""
97
        Configuration of a local execution environment for specific platforms.
98

99
        Environment configuration includes the platforms the environment is compatible with, and
100
        optionally a fallback environment, along with environment-aware options (such as
101
        environment variables and search paths) used by Pants to execute processes in this
102
        environment.
103

104
        To use this environment, map this target's address with a memorable name in
105
        `[environments-preview].names`. You can then consume this environment by specifying the name in
106
        the `environment` field defined on other targets.
107

108
        Only one `local_environment` may be defined in `[environments-preview].names` per platform, and
109
        when `{LOCAL_ENVIRONMENT_MATCHER}` is specified as the environment, the
110
        `local_environment` that matches the current platform (if defined) will be selected.
111
        """
112
        # TODO(#17096) Add a link to the environments docs once they land.
113
    )
114

115

116
class LocalWorkspaceCompatiblePlatformsField(CompatiblePlatformsField):
7✔
117
    pass
7✔
118

119

120
class LocalWorkspaceEnvironmentTarget(Target):
7✔
121
    alias = "experimental_workspace_environment"
7✔
122
    core_fields = (
7✔
123
        *COMMON_TARGET_FIELDS,
124
        LocalWorkspaceCompatiblePlatformsField,
125
    )
126
    help = help_text(
7✔
127
        f"""
128
        Configuration of a "workspace" execution environment for specific platforms.
129

130
        A "workspace" environment is a local environment which executes build processes within
131
        the repository and not in the usual execution sandbox. This is useful when interacting with
132
        third-party build orchestration tools which may not run correctly when run from within the Pants
133
        execution sandbox.
134

135
        Environment configuration includes the platforms the environment is compatible with along with
136
        environment-aware options (such as environment variables and search paths) used by Pants to execute
137
        processes in this environment.
138

139
        To use this environment, map this target's address with a memorable name in
140
        `[environments-preview].names`. You can then consume this environment by specifying the name in
141
        the `environment` field defined on other targets.
142

143
        Only one `experimental_workspace_environment` may be defined in `[environments-preview].names` per platform, and
144
        when `{LOCAL_WORKSPACE_ENVIRONMENT_MATCHER}` is specified as the environment, the
145
        `experimental_workspace_environment` that matches the current platform (if defined) will be selected.
146

147
        Caching and reproducibility:
148

149
        Pants' caching relies on all process being reproducible based solely on inputs in the repository.
150
        Processes executed in a workspace environment can easily accidentally read unexpected files,
151
        that aren't specified as a dependency.  Thus, Pants puts that burden on you, the Pants user, to ensure
152
        a process output only depends on its specified input files, and doesn't read anything else.
153

154
        If a process isn't reproducible, re-running a build from the same source code could fail unexpectedly,
155
        or give different output to an earlier build.
156

157
        NOTE: This target type is EXPERIMENTAL and may change its semantics in subsequent Pants versions
158
        without a deprecation cycle.
159
        """
160
    )
161

162

163
class DockerImageField(StringField):
7✔
164
    alias = "image"
7✔
165
    required = True
7✔
166
    value: str
7✔
167
    help = help_text(
7✔
168
        """
169
        The docker image ID to use when this environment is loaded.
170

171
        This value may be any image identifier that the local Docker installation can accept.
172
        This includes image names with or without tags (e.g. `centos6` or `centos6:latest`), or
173
        image names with an immutable digest (e.g. `centos@sha256:<some_sha256_value>`).
174

175
        The choice of image ID can affect the reproducibility of builds. Consider using an
176
        immutable digest if reproducibility is needed, but regularly ensure that the image
177
        is free of relevant bugs or security vulnerabilities.
178

179
        Note that in order to use an image as a `docker_environment` it must have a few tools:
180
        - `/bin/sh`
181
        - `/usr/bin/env`
182
        - `bash`
183
        - `tar`
184

185
        While most images will have these preinstalled, users of base images such as Distroless or scratch will need to bake these tools into the image themselves. All of these except `bash` are available via busybox.
186
        """
187
    )
188

189

190
class DockerPlatformField(StringField):
7✔
191
    alias = "platform"
7✔
192
    default = None
7✔
193
    valid_choices = Platform
7✔
194
    help = help_text(
7✔
195
        """
196
        If set, Docker will always use the specified platform when pulling and running the image.
197

198
        If unset, Pants will default to the CPU architecture of your local host machine. For
199
        example, if you are running on Apple Silicon, it will use `linux_arm64`, whereas running on
200
        Intel macOS will use `linux_x86_64`. This mirrors Docker's behavior when `--platform` is
201
        left off.
202
        """
203
    )
204

205
    @property
7✔
206
    def normalized_value(self) -> Platform:
7✔
207
        if self.value is not None:
3✔
208
            return Platform(self.value)
3✔
209
        return match(
1✔
210
            Platform.create_for_localhost(),
211
            {
212
                Platform.linux_x86_64: Platform.linux_x86_64,
213
                Platform.macos_x86_64: Platform.linux_x86_64,
214
                Platform.linux_arm64: Platform.linux_arm64,
215
                Platform.macos_arm64: Platform.linux_arm64,
216
            },
217
        )
218

219

220
class DockerFallbackEnvironmentField(FallbackEnvironmentField):
7✔
221
    help = help_text(
7✔
222
        f"""
223
        The environment to fallback to when this Docker environment cannot be used because either
224
        the global option `--docker-execution` is false, or the
225
        field `{DockerPlatformField.alias}` is not compatible with the local host's CPU
226
        architecture (this is only an issue when the local host is Linux; macOS is fine).
227

228
        Must be an environment name from the option `[environments-preview].names`, the
229
        special string `{LOCAL_ENVIRONMENT_MATCHER}` to use the relevant local environment, or the
230
        Python value `None` to error when this specific Docker environment cannot be used.
231
        """
232
    )
233

234

235
class DockerEnvironmentTarget(Target):
7✔
236
    alias = "docker_environment"
7✔
237
    core_fields = (
7✔
238
        *COMMON_TARGET_FIELDS,
239
        DockerImageField,
240
        DockerPlatformField,
241
        DockerFallbackEnvironmentField,
242
    )
243
    help = help_text(
7✔
244
        """
245
        Configuration of a Docker environment used for building your code.
246

247
        Environment configuration includes both Docker-specific information (including the image
248
        and platform choice), as well as environment-aware options (such as environment variables
249
        and search paths) used by Pants to execute processes in this Docker environment.
250

251
        To use this environment, map this target's address with a memorable name in
252
        `[environments-preview].names`. You can then consume this environment by specifying the name in
253
        the `environment` field defined on other targets.
254

255
        Before running Pants using this environment, if you are using Docker Desktop, make sure the option
256
        **Enable default Docker socket** is enabled, you can find it in **Docker Desktop Settings > Advanced**
257
        panel. That option tells Docker to create a socket at `/var/run/docker.sock` which Pants can use to
258
        communicate with Docker.
259
        """
260
        # TODO(#17096) Add a link to the environments docs once they land.
261
    )
262

263

264
class RemotePlatformField(StringField):
7✔
265
    alias = "platform"
7✔
266
    default = Platform.linux_x86_64.value
7✔
267
    valid_choices = Platform
7✔
268
    help = "The platform used by the remote execution environment."
7✔
269

270

271
class RemoteExtraPlatformPropertiesField(StringSequenceField):
7✔
272
    alias = "extra_platform_properties"
7✔
273
    default = ()
7✔
274
    value: tuple[str, ...]
7✔
275
    help = help_text(
7✔
276
        """
277
        Platform properties to set on remote execution requests.
278

279
        Format: `property=value`. Multiple values should be specified as multiple
280
        occurrences of this flag.
281

282
        Pants itself may add additional platform properties.
283
        """
284
    )
285

286

287
class RemoteFallbackEnvironmentField(FallbackEnvironmentField):
7✔
288
    help = help_text(
7✔
289
        f"""
290
        The environment to fallback to when remote execution is disabled via the global option
291
        `--remote-execution`.
292

293
        Must be an environment name from the option `[environments-preview].names`, the
294
        special string `{LOCAL_ENVIRONMENT_MATCHER}` to use the relevant local environment, or the
295
        Python value `None` to error when remote execution is disabled.
296

297
        Tip: if you are using a Docker image with your remote execution environment (usually
298
        enabled by setting the field `{RemoteExtraPlatformPropertiesField.alias}`), then it can be
299
        useful to fallback to an equivalent `docker_image` target so that you have a consistent
300
        execution environment.
301
        """
302
    )
303

304

305
class RemoteEnvironmentCacheBinaryDiscovery(BoolField):
7✔
306
    alias = "cache_binary_discovery"
7✔
307
    default = False
7✔
308
    help = help_text(
7✔
309
        f"""
310
        If true, will cache system binary discovery, e.g. finding Python interpreters.
311

312
        When safe to do, it is preferable to set this option to `True` for faster performance by
313
        avoiding wasted work. Otherwise, Pants will search for system binaries whenever the
314
        Pants daemon is restarted.
315

316
        However, it is only safe to set this to `True` if the remote execution environment has a
317
        stable environment, e.g. the server will not change versions of installed system binaries.
318
        Otherwise, you risk caching results that become stale when the server changes its
319
        environment, which may break your builds. With some remote execution servers, you can
320
        specify a Docker image to run with via the field
321
        `{RemoteExtraPlatformPropertiesField.alias}`; if you are able to specify what Docker image
322
        to use, and also use a pinned tag of the image, it is likely safe to set this field to true.
323
        """
324
    )
325

326

327
class RemoteEnvironmentTarget(Target):
7✔
328
    alias = "remote_environment"
7✔
329
    core_fields = (
7✔
330
        *COMMON_TARGET_FIELDS,
331
        RemotePlatformField,
332
        RemoteExtraPlatformPropertiesField,
333
        RemoteFallbackEnvironmentField,
334
        RemoteEnvironmentCacheBinaryDiscovery,
335
    )
336
    help = help_text(
7✔
337
        """
338
        Configuration of a remote execution environment used for building your code.
339

340
        Environment configuration includes platform properties and a fallback environment, as well
341
        as environment-aware options (such as environment variables and search paths) used by Pants
342
        to execute processes in this remote environment.
343

344
        Note that you must also configure remote execution with the global options like
345
        `remote_execution` and `remote_execution_address`.
346

347
        To use this environment, map this target's address with a memorable name in
348
        `[environments-preview].names`. You can then consume this environment by specifying the name in
349
        the `environment` field defined on other targets.
350

351
        Often, it is only necessary to have a single `remote_environment` target for your
352
        repository, but it can be useful to have >1 so that you can set different
353
        `extra_platform_properties`. For example, with some servers, you could use this to
354
        configure a different Docker image per environment.
355
        """
356
        # TODO(#17096) Add a link to the environments docs once they land.
357
    )
358

359

360
@dataclass(frozen=True)
7✔
361
class EnvironmentTarget:
7✔
362
    name: str | None
7✔
363
    val: Target | None
7✔
364

365
    def executable_search_path_cache_scope(
7✔
366
        self, *, cache_failures: bool = False
367
    ) -> ProcessCacheScope:
368
        """Whether it's safe to cache executable search path discovery or not.
369

370
        If the environment may change on us, e.g. the user upgrades a binary, then it's not safe to
371
        cache the discovery to disk. Technically, in that case, we should recheck the binary every
372
        session (i.e. Pants run), but we instead settle for every Pantsd lifetime to have more
373
        acceptable performance.
374

375
        Meanwhile, when running with Docker, we already invalidate whenever the image changes
376
        thanks to https://github.com/pantsbuild/pants/pull/17101.
377

378
        Remote execution often is safe to cache, but depends on the remote execution server.
379
        So, we rely on the user telling us what is safe.
380
        """
381
        caching_allowed = self.val and (
3✔
382
            self.val.has_field(DockerImageField)
383
            or (
384
                self.val.has_field(RemoteEnvironmentCacheBinaryDiscovery)
385
                and self.val[RemoteEnvironmentCacheBinaryDiscovery].value
386
            )
387
        )
388
        if cache_failures:
3✔
389
            return (
1✔
390
                ProcessCacheScope.ALWAYS
391
                if caching_allowed
392
                else ProcessCacheScope.PER_RESTART_ALWAYS
393
            )
394
        return (
3✔
395
            ProcessCacheScope.SUCCESSFUL
396
            if caching_allowed
397
            else ProcessCacheScope.PER_RESTART_SUCCESSFUL
398
        )
399

400
    def sandbox_base_path(self) -> str:
7✔
401
        if self.val and self.val.has_field(LocalWorkspaceCompatiblePlatformsField):
×
402
            return "{chroot}"
×
403
        else:
404
            return ""
×
405

406
    @property
7✔
407
    def default_cache_scope(self) -> ProcessCacheScope:
7✔
408
        if self.val and self.val.has_field(LocalWorkspaceCompatiblePlatformsField):
×
409
            return ProcessCacheScope.PER_SESSION
×
410
        else:
411
            return ProcessCacheScope.SUCCESSFUL
×
412

413
    @property
7✔
414
    def use_working_directory_as_base_for_output_captures(self) -> bool:
7✔
415
        if self.val and self.val.has_field(LocalWorkspaceCompatiblePlatformsField):
×
416
            return False
×
417
        return True
×
418

419
    @property
7✔
420
    def can_access_local_system_paths(self) -> bool:
7✔
421
        tgt = self.val
1✔
422
        if not tgt:
1✔
423
            return True
×
424

425
        return tgt.has_field(LocalCompatiblePlatformsField) or tgt.has_field(
1✔
426
            LocalWorkspaceCompatiblePlatformsField
427
        )
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc