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

pantsbuild / pants / 19015773527

02 Nov 2025 05:33PM UTC coverage: 17.872% (-62.4%) from 80.3%
19015773527

Pull #22816

github

web-flow
Merge a12d75757 into 6c024e162
Pull Request #22816: Update Pants internal Python to 3.14

4 of 5 new or added lines in 3 files covered. (80.0%)

28452 existing lines in 683 files now uncovered.

9831 of 55007 relevant lines covered (17.87%)

0.18 hits per line

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

0.0
/src/python/pants/jvm/package/deploy_jar.py
1
# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

UNCOV
4
import logging
×
UNCOV
5
from dataclasses import dataclass
×
UNCOV
6
from pathlib import PurePath
×
7

UNCOV
8
from pants.core.goals.package import (
×
9
    BuiltPackage,
10
    BuiltPackageArtifact,
11
    OutputPathField,
12
    PackageFieldSet,
13
)
UNCOV
14
from pants.core.goals.run import RunFieldSet, RunInSandboxBehavior
×
UNCOV
15
from pants.engine.addresses import Addresses
×
UNCOV
16
from pants.engine.fs import EMPTY_DIGEST, AddPrefix, MergeDigests
×
UNCOV
17
from pants.engine.intrinsics import add_prefix, merge_digests
×
UNCOV
18
from pants.engine.rules import collect_rules, implicitly, rule
×
UNCOV
19
from pants.engine.target import Dependencies
×
UNCOV
20
from pants.engine.unions import UnionRule
×
UNCOV
21
from pants.jvm import classpath
×
UNCOV
22
from pants.jvm.classpath import classpath as classpath_get
×
UNCOV
23
from pants.jvm.compile import (
×
24
    ClasspathDependenciesRequest,
25
    ClasspathEntry,
26
    ClasspathEntryRequest,
27
    CompileResult,
28
    FallibleClasspathEntry,
29
    compile_classpath_entries,
30
)
UNCOV
31
from pants.jvm.jar_tool.jar_tool import JarToolRequest
×
UNCOV
32
from pants.jvm.jar_tool.jar_tool import rules as jar_tool_rules
×
UNCOV
33
from pants.jvm.jar_tool.jar_tool import run_jar_tool
×
UNCOV
34
from pants.jvm.shading.rules import ShadeJarRequest
×
UNCOV
35
from pants.jvm.shading.rules import rules as shaded_jar_rules
×
UNCOV
36
from pants.jvm.shading.rules import shade_jar
×
UNCOV
37
from pants.jvm.strip_jar.strip_jar import StripJarRequest, strip_jar
×
UNCOV
38
from pants.jvm.subsystems import JvmSubsystem
×
UNCOV
39
from pants.jvm.target_types import (
×
40
    DeployJarDuplicatePolicyField,
41
    DeployJarExcludeFilesField,
42
    DeployJarShadingRulesField,
43
    JvmDependenciesField,
44
    JvmJdkField,
45
    JvmMainClassNameField,
46
)
47

UNCOV
48
logger = logging.getLogger(__name__)
×
49

50

UNCOV
51
@dataclass(frozen=True)
×
UNCOV
52
class DeployJarFieldSet(PackageFieldSet, RunFieldSet):
×
UNCOV
53
    required_fields = (
×
54
        JvmMainClassNameField,
55
        JvmJdkField,
56
        Dependencies,
57
        OutputPathField,
58
    )
UNCOV
59
    run_in_sandbox_behavior = RunInSandboxBehavior.RUN_REQUEST_HERMETIC
×
60

61
    main_class: JvmMainClassNameField
62
    output_path: OutputPathField
63
    dependencies: JvmDependenciesField
64
    jdk_version: JvmJdkField
65
    duplicate_policy: DeployJarDuplicatePolicyField
66
    shading_rules: DeployJarShadingRulesField
67
    exclude_files: DeployJarExcludeFilesField
68

69

UNCOV
70
class DeployJarClasspathEntryRequest(ClasspathEntryRequest):
×
UNCOV
71
    field_sets = (DeployJarFieldSet,)
×
72
    # A `deploy_jar` can have a Classpath requested for it, but should not be used as a dependency.
UNCOV
73
    root_only = True
×
74

75

UNCOV
76
@rule
×
UNCOV
77
async def deploy_jar_classpath(
×
78
    request: DeployJarClasspathEntryRequest,
79
) -> FallibleClasspathEntry:
80
    if len(request.component.members) > 1:
×
81
        # If multiple DeployJar targets were coarsened into a single instance, it's because they
82
        # formed a cycle among themselves... but at a high level, they shouldn't have dependencies
83
        # on one another anyway.
84
        raise Exception(
×
85
            "`deploy_jar` targets should not depend on one another:\n"
86
            f"{request.component.bullet_list()}"
87
        )
88
    fallible_entries = await compile_classpath_entries(
×
89
        **implicitly(ClasspathDependenciesRequest(request))
90
    )
91
    classpath_entries = fallible_entries.if_all_succeeded()
×
92
    if classpath_entries is None:
×
93
        return FallibleClasspathEntry(
×
94
            description=str(request.component),
95
            result=CompileResult.DEPENDENCY_FAILED,
96
            output=None,
97
            exit_code=1,
98
        )
99
    return FallibleClasspathEntry(
×
100
        description=str(request.component),
101
        result=CompileResult.SUCCEEDED,
102
        output=ClasspathEntry(EMPTY_DIGEST, dependencies=classpath_entries),
103
        exit_code=0,
104
    )
105

106

UNCOV
107
@rule
×
UNCOV
108
async def package_deploy_jar(
×
109
    jvm: JvmSubsystem,
110
    field_set: DeployJarFieldSet,
111
) -> BuiltPackage:
112
    """
113
    Constructs a deploy ("fat") JAR file by
114
    1. Resolving/compiling a Classpath for the `root_address` target,
115
    2. Creating a deploy jar with a valid ZIP index and deduplicated entries
116
    3. (optionally) Stripping the jar of all metadata that may cause it to be non-reproducible (https://reproducible-builds.org)
117
    4. (optionally) Apply shading rules to the bytecode inside the jar file
118
    """
119

120
    if field_set.main_class.value is None:
×
121
        raise Exception("Needs a `main` argument")
×
122

123
    #
124
    # 1. Produce thin JARs containing the transitive classpath
125
    #
126

127
    classpath = await classpath_get(**implicitly(Addresses([field_set.address])))
×
128
    classpath_digest = await merge_digests(MergeDigests(classpath.digests()))
×
129

130
    #
131
    # 2. Use Pants' JAR tool to build a runnable fat JAR
132
    #
133

134
    output_filename = PurePath(field_set.output_path.value_or_default(file_ending="jar"))
×
135
    jar_digest = await run_jar_tool(
×
136
        JarToolRequest(
137
            jar_name=output_filename.name,
138
            digest=classpath_digest,
139
            main_class=field_set.main_class.value,
140
            jars=classpath.args(),
141
            policies=[
142
                (rule.pattern, rule.action)
143
                for rule in field_set.duplicate_policy.value_or_default()
144
            ],
145
            skip=[*(jvm.deploy_jar_exclude_files or []), *(field_set.exclude_files.value or [])],
146
            compress=True,
147
        ),
148
        **implicitly(),
149
    )
150

151
    #
152
    # 3. Strip the JAR from  all non-reproducible metadata if requested so
153
    #
154
    if jvm.reproducible_jars:
×
155
        jar_digest = await strip_jar(
×
156
            **implicitly(
157
                StripJarRequest(
158
                    digest=jar_digest,
159
                    filenames=(output_filename.name,),
160
                )
161
            )
162
        )
163

164
    jar_digest = await add_prefix(AddPrefix(jar_digest, str(output_filename.parent)))
×
165

166
    #
167
    # 4. Apply shading rules
168
    #
169
    if field_set.shading_rules.value:
×
170
        shaded_jar = await shade_jar(
×
171
            ShadeJarRequest(
172
                path=output_filename,
173
                digest=jar_digest,
174
                rules=field_set.shading_rules.value,
175
                skip_manifest=False,
176
            ),
177
            **implicitly(),
178
        )
179
        jar_digest = shaded_jar.digest
×
180

181
    artifact = BuiltPackageArtifact(relpath=str(output_filename))
×
182
    return BuiltPackage(digest=jar_digest, artifacts=(artifact,))
×
183

184

UNCOV
185
def rules():
×
UNCOV
186
    return [
×
187
        *collect_rules(),
188
        *classpath.rules(),
189
        *jar_tool_rules(),
190
        *shaded_jar_rules(),
191
        UnionRule(PackageFieldSet, DeployJarFieldSet),
192
        UnionRule(ClasspathEntryRequest, DeployJarClasspathEntryRequest),
193
    ]
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