• 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/backend/codegen/protobuf/java/rules.py
1
# Copyright 2020 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
UNCOV
3
from __future__ import annotations
×
4

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

UNCOV
8
from pants.backend.codegen.protobuf import protoc
×
UNCOV
9
from pants.backend.codegen.protobuf.java import dependency_inference, symbol_mapper
×
UNCOV
10
from pants.backend.codegen.protobuf.java.subsystem import JavaProtobufGrpcSubsystem
×
UNCOV
11
from pants.backend.codegen.protobuf.protoc import Protoc
×
UNCOV
12
from pants.backend.codegen.protobuf.target_types import (
×
13
    ProtobufGrpcToggleField,
14
    ProtobufSourceField,
15
    ProtobufSourcesGeneratorTarget,
16
    ProtobufSourceTarget,
17
)
UNCOV
18
from pants.backend.experimental.java.register import rules as java_backend_rules
×
UNCOV
19
from pants.backend.java.target_types import JavaSourceField
×
UNCOV
20
from pants.core.goals.resolves import ExportableTool
×
UNCOV
21
from pants.core.util_rules.external_tool import download_external_tool
×
UNCOV
22
from pants.core.util_rules.source_files import SourceFilesRequest
×
UNCOV
23
from pants.core.util_rules.stripped_source_files import strip_source_roots
×
UNCOV
24
from pants.engine.fs import (
×
25
    AddPrefix,
26
    CreateDigest,
27
    Digest,
28
    Directory,
29
    FileEntry,
30
    MergeDigests,
31
    RemovePrefix,
32
)
UNCOV
33
from pants.engine.internals.graph import transitive_targets
×
UNCOV
34
from pants.engine.intrinsics import (
×
35
    create_digest,
36
    digest_to_snapshot,
37
    get_digest_entries,
38
    merge_digests,
39
    remove_prefix,
40
)
UNCOV
41
from pants.engine.platform import Platform
×
UNCOV
42
from pants.engine.process import Process, fallible_to_exec_result_or_raise
×
UNCOV
43
from pants.engine.rules import collect_rules, concurrently, implicitly, rule
×
UNCOV
44
from pants.engine.target import GeneratedSources, GenerateSourcesRequest, TransitiveTargetsRequest
×
UNCOV
45
from pants.engine.unions import UnionRule
×
UNCOV
46
from pants.jvm.resolve.coursier_fetch import ToolClasspathRequest, materialize_classpath_for_tool
×
UNCOV
47
from pants.jvm.resolve.jvm_tool import GenerateJvmLockfileFromTool
×
UNCOV
48
from pants.jvm.target_types import PrefixedJvmJdkField, PrefixedJvmResolveField
×
UNCOV
49
from pants.source.source_root import SourceRootRequest, get_source_root
×
UNCOV
50
from pants.util.logging import LogLevel
×
51

52

UNCOV
53
class GenerateJavaFromProtobufRequest(GenerateSourcesRequest):
×
UNCOV
54
    input = ProtobufSourceField
×
UNCOV
55
    output = JavaSourceField
×
56

57

UNCOV
58
@dataclass(frozen=True)
×
UNCOV
59
class ProtobufJavaGrpcPlugin:
×
UNCOV
60
    digest: Digest
×
UNCOV
61
    path: str
×
62

63

UNCOV
64
@rule
×
UNCOV
65
async def resolve_protobuf_java_grpc_plugin(
×
66
    platform: Platform,
67
    tool: JavaProtobufGrpcSubsystem,
68
) -> ProtobufJavaGrpcPlugin:
69
    lockfile_request = GenerateJvmLockfileFromTool.create(tool)
×
70
    classpath = await materialize_classpath_for_tool(
×
71
        ToolClasspathRequest(lockfile=lockfile_request)
72
    )
73

74
    # TODO: Improve `ToolClasspath` API so that the filenames corresponding to a coordinate are identified by a
75
    # mapping. Work-around the lack of such information by looking for a platform-specific string in the filenames
76
    # provided in the classpath.
77
    platform_part = {
×
78
        Platform.macos_arm64: "exe_osx-aarch_64",
79
        Platform.macos_x86_64: "exe_osx-x86_64",
80
        Platform.linux_arm64: "exe_linux-aarch_64",
81
        Platform.linux_x86_64: "exe_linux-x86_64",
82
    }[platform]
83

84
    classpath_entries = await get_digest_entries(classpath.digest)
×
85
    candidate_plugin_entries = []
×
86
    for classpath_entry in classpath_entries:
×
87
        if isinstance(classpath_entry, FileEntry):
×
88
            path = PurePath(classpath_entry.path)
×
89
            if platform_part in path.name:
×
90
                candidate_plugin_entries.append(classpath_entry)
×
91

92
    assert len(candidate_plugin_entries) == 1
×
93

94
    plugin_digest = await create_digest(
×
95
        CreateDigest(
96
            [
97
                FileEntry(
98
                    path="protoc-gen-grpc-java",
99
                    file_digest=candidate_plugin_entries[0].file_digest,
100
                    is_executable=True,
101
                )
102
            ]
103
        )
104
    )
105

106
    return ProtobufJavaGrpcPlugin(digest=plugin_digest, path="protoc-gen-grpc-java")
×
107

108

UNCOV
109
@rule(desc="Generate Java from Protobuf", level=LogLevel.DEBUG)
×
UNCOV
110
async def generate_java_from_protobuf(
×
111
    request: GenerateJavaFromProtobufRequest,
112
    protoc: Protoc,
113
    grpc_plugin: ProtobufJavaGrpcPlugin,  # TODO: Don't access grpc plugin unless gRPC codegen is enabled.
114
    platform: Platform,
115
) -> GeneratedSources:
116
    download_protoc_request = download_external_tool(protoc.get_request(platform))
×
117

118
    output_dir = "_generated_files"
×
119
    create_output_dir_request = create_digest(CreateDigest([Directory(output_dir)]))
×
120

121
    # Protoc needs all transitive dependencies on `protobuf_source` to work properly. It won't
122
    # actually generate those dependencies; it only needs to look at their .proto files to work
123
    # with imports.
124
    transitive_targets_for_protobuf_source = await transitive_targets(
×
125
        TransitiveTargetsRequest([request.protocol_target.address]), **implicitly()
126
    )
127

128
    # NB: By stripping the source roots, we avoid having to set the value `--proto_path`
129
    # for Protobuf imports to be discoverable.
130
    all_stripped_sources_request = strip_source_roots(
×
131
        **implicitly(
132
            SourceFilesRequest(
133
                tgt[ProtobufSourceField]
134
                for tgt in transitive_targets_for_protobuf_source.closure
135
                if tgt.has_field(ProtobufSourceField)
136
            )
137
        )
138
    )
139
    target_stripped_sources_request = strip_source_roots(
×
140
        **implicitly(SourceFilesRequest([request.protocol_target[ProtobufSourceField]]))
141
    )
142

143
    (
×
144
        downloaded_protoc_binary,
145
        empty_output_dir,
146
        all_sources_stripped,
147
        target_sources_stripped,
148
    ) = await concurrently(
149
        download_protoc_request,
150
        create_output_dir_request,
151
        all_stripped_sources_request,
152
        target_stripped_sources_request,
153
    )
154

155
    unmerged_digests = [
×
156
        all_sources_stripped.snapshot.digest,
157
        downloaded_protoc_binary.digest,
158
        empty_output_dir,
159
    ]
160
    input_digest = await merge_digests(MergeDigests(unmerged_digests))
×
161

162
    immutable_input_digests = {}
×
163
    if request.protocol_target.get(ProtobufGrpcToggleField).value:
×
164
        immutable_input_digests["__grpc"] = grpc_plugin.digest
×
165

166
    argv = [downloaded_protoc_binary.exe, "--java_out", output_dir]
×
167
    if request.protocol_target.get(ProtobufGrpcToggleField).value:
×
168
        argv.extend(
×
169
            [
170
                f"--plugin=protoc-gen-grpc-java=./__grpc/{grpc_plugin.path}",
171
                f"--grpc-java_out={output_dir}",
172
            ]
173
        )
174

175
    argv.extend(target_sources_stripped.snapshot.files)
×
176
    result = await fallible_to_exec_result_or_raise(
×
177
        **implicitly(
178
            Process(
179
                argv,
180
                input_digest=input_digest,
181
                immutable_input_digests=immutable_input_digests,
182
                description=f"Generating Java sources from {request.protocol_target.address}.",
183
                level=LogLevel.DEBUG,
184
                output_directories=(output_dir,),
185
            )
186
        )
187
    )
188

189
    normalized_digest, source_root = await concurrently(
×
190
        remove_prefix(RemovePrefix(result.output_digest, output_dir)),
191
        get_source_root(SourceRootRequest.for_target(request.protocol_target)),
192
    )
193

194
    source_root_restored = (
×
195
        await digest_to_snapshot(**implicitly(AddPrefix(normalized_digest, source_root.path)))
196
        if source_root.path != "."
197
        else await digest_to_snapshot(normalized_digest)
198
    )
199
    return GeneratedSources(source_root_restored)
×
200

201

UNCOV
202
def rules():
×
UNCOV
203
    return [
×
204
        *collect_rules(),
205
        *dependency_inference.rules(),
206
        *symbol_mapper.rules(),
207
        *protoc.rules(),
208
        UnionRule(GenerateSourcesRequest, GenerateJavaFromProtobufRequest),
209
        UnionRule(ExportableTool, JavaProtobufGrpcSubsystem),
210
        ProtobufSourceTarget.register_plugin_field(PrefixedJvmJdkField),
211
        ProtobufSourcesGeneratorTarget.register_plugin_field(PrefixedJvmJdkField),
212
        ProtobufSourceTarget.register_plugin_field(PrefixedJvmResolveField),
213
        ProtobufSourcesGeneratorTarget.register_plugin_field(PrefixedJvmResolveField),
214
        # Bring in the Java backend (since this backend compiles Java code) to avoid rule graph errors.
215
        # TODO: Figure out whether a subset of rules can be brought in to still avoid rule graph errors.
216
        *java_backend_rules(),
217
    ]
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