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

pantsbuild / pants / 20632486505

01 Jan 2026 04:21AM UTC coverage: 43.231% (-37.1%) from 80.281%
20632486505

Pull #22962

github

web-flow
Merge 08d5c63b0 into f52ab6675
Pull Request #22962: Bump the gha-deps group across 1 directory with 6 updates

26122 of 60424 relevant lines covered (43.23%)

0.86 hits per line

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

0.0
/src/python/pants/backend/codegen/protobuf/go/rules.py
1
# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3
from __future__ import annotations
×
4

5
import logging
×
6
import os
×
7
import re
×
8
import textwrap
×
9
from collections import defaultdict
×
10
from dataclasses import dataclass
×
11

12
from pants.backend.codegen.protobuf import protoc
×
13
from pants.backend.codegen.protobuf.protoc import Protoc
×
14
from pants.backend.codegen.protobuf.target_types import (
×
15
    AllProtobufTargets,
16
    ProtobufGrpcToggleField,
17
    ProtobufSourceField,
18
    ProtobufSourcesGeneratorTarget,
19
    ProtobufSourceTarget,
20
)
21
from pants.backend.go import target_type_rules
×
22
from pants.backend.go.dependency_inference import (
×
23
    GoImportPathsMappingAddressSet,
24
    GoModuleImportPathsMapping,
25
    GoModuleImportPathsMappings,
26
    GoModuleImportPathsMappingsHook,
27
)
28
from pants.backend.go.target_type_rules import (
×
29
    GoImportPathMappingRequest,
30
    map_import_paths_to_packages,
31
)
32
from pants.backend.go.target_types import GoOwningGoModAddressField, GoPackageSourcesField
×
33
from pants.backend.go.util_rules import (
×
34
    assembly,
35
    build_pkg,
36
    build_pkg_target,
37
    first_party_pkg,
38
    go_mod,
39
    link,
40
    sdk,
41
    third_party_pkg,
42
)
43
from pants.backend.go.util_rules.build_opts import GoBuildOptions
×
44
from pants.backend.go.util_rules.build_pkg import (
×
45
    BuildGoPackageRequest,
46
    FallibleBuildGoPackageRequest,
47
)
48
from pants.backend.go.util_rules.build_pkg_target import (
×
49
    BuildGoPackageTargetRequest,
50
    GoCodegenBuildRequest,
51
    required_build_go_package_request,
52
)
53
from pants.backend.go.util_rules.first_party_pkg import FallibleFirstPartyPkgAnalysis
×
54
from pants.backend.go.util_rules.go_mod import OwningGoModRequest, find_owning_go_mod
×
55
from pants.backend.go.util_rules.pkg_analyzer import PackageAnalyzerSetup
×
56
from pants.backend.go.util_rules.sdk import GoSdkProcess
×
57
from pants.backend.python.util_rules import pex
×
58
from pants.build_graph.address import Address
×
59
from pants.core.util_rules.external_tool import download_external_tool
×
60
from pants.core.util_rules.source_files import SourceFilesRequest, determine_source_files
×
61
from pants.core.util_rules.stripped_source_files import strip_source_roots
×
62
from pants.engine.fs import (
×
63
    AddPrefix,
64
    CreateDigest,
65
    Digest,
66
    Directory,
67
    FileContent,
68
    MergeDigests,
69
    RemovePrefix,
70
)
71
from pants.engine.internals.graph import hydrate_sources, resolve_source_paths, transitive_targets
×
72
from pants.engine.internals.native_engine import EMPTY_DIGEST
×
73
from pants.engine.internals.selectors import concurrently
×
74
from pants.engine.intrinsics import (
×
75
    create_digest,
76
    digest_to_snapshot,
77
    execute_process,
78
    get_digest_contents,
79
    merge_digests,
80
    remove_prefix,
81
)
82
from pants.engine.platform import Platform
×
83
from pants.engine.process import Process, fallible_to_exec_result_or_raise
×
84
from pants.engine.rules import collect_rules, implicitly, rule
×
85
from pants.engine.target import (
×
86
    GeneratedSources,
87
    GenerateSourcesRequest,
88
    HydrateSourcesRequest,
89
    SourcesPathsRequest,
90
    TransitiveTargetsRequest,
91
)
92
from pants.engine.unions import UnionRule
×
93
from pants.source.source_root import (
×
94
    SourceRootRequest,
95
    SourceRootsRequest,
96
    get_source_root,
97
    get_source_roots,
98
)
99
from pants.util.dirutil import group_by_dir
×
100
from pants.util.frozendict import FrozenDict
×
101
from pants.util.logging import LogLevel
×
102
from pants.util.strutil import softwrap
×
103

104
_logger = logging.getLogger(__name__)
×
105

106

107
class GoCodegenBuildProtobufRequest(GoCodegenBuildRequest):
×
108
    generate_from = ProtobufSourceField
×
109

110

111
class GenerateGoFromProtobufRequest(GenerateSourcesRequest):
×
112
    input = ProtobufSourceField
×
113
    output = GoPackageSourcesField
×
114

115

116
@dataclass(frozen=True)
×
117
class _SetupGoProtocPlugin:
×
118
    digest: Digest
×
119

120

121
_QUOTE_CHAR = r"(?:'|\")"
×
122
_IMPORT_PATH_RE = re.compile(rf"^\s*option\s+go_package\s+=\s+{_QUOTE_CHAR}(.*){_QUOTE_CHAR};")
×
123

124

125
def parse_go_package_option(content_raw: bytes) -> str | None:
×
126
    content = content_raw.decode()
×
127
    for line in content.splitlines():
×
128
        m = _IMPORT_PATH_RE.match(line)
×
129
        if m:
×
130
            return m.group(1)
×
131
    return None
×
132

133

134
class ProtobufGoModuleImportPathsMappingsHook(GoModuleImportPathsMappingsHook):
×
135
    pass
×
136

137

138
@rule(desc="Map import paths for all Go Protobuf targets.", level=LogLevel.DEBUG)
×
139
async def map_import_paths_of_all_go_protobuf_targets(
×
140
    _request: ProtobufGoModuleImportPathsMappingsHook,
141
    all_protobuf_targets: AllProtobufTargets,
142
) -> GoModuleImportPathsMappings:
143
    sources = await concurrently(
×
144
        hydrate_sources(
145
            HydrateSourcesRequest(
146
                tgt[ProtobufSourceField],
147
                for_sources_types=(ProtobufSourceField,),
148
                enable_codegen=True,
149
            ),
150
            **implicitly(),
151
        )
152
        for tgt in all_protobuf_targets
153
    )
154

155
    all_contents = await concurrently(
×
156
        get_digest_contents(source.snapshot.digest) for source in sources
157
    )
158

159
    go_protobuf_mapping_metadata = []
×
160
    owning_go_mod_gets = []
×
161
    for tgt, contents in zip(all_protobuf_targets, all_contents):
×
162
        if not contents:
×
163
            continue
×
164
        if len(contents) > 1:
×
165
            raise AssertionError(
×
166
                f"Protobuf target `{tgt.address}` mapped to more than one source file."
167
            )
168

169
        import_path = parse_go_package_option(contents[0].content)
×
170
        if not import_path:
×
171
            continue
×
172

173
        owning_go_mod_gets.append(
×
174
            find_owning_go_mod(OwningGoModRequest(tgt.address), **implicitly())
175
        )
176
        go_protobuf_mapping_metadata.append((import_path, tgt.address))
×
177

178
    owning_go_mod_targets = await concurrently(owning_go_mod_gets)
×
179

180
    import_paths_by_module: dict[Address, dict[str, set[Address]]] = defaultdict(
×
181
        lambda: defaultdict(set)
182
    )
183

184
    for owning_go_mod, (import_path, address) in zip(
×
185
        owning_go_mod_targets, go_protobuf_mapping_metadata
186
    ):
187
        import_paths_by_module[owning_go_mod.address][import_path].add(address)
×
188

189
    return GoModuleImportPathsMappings(
×
190
        FrozenDict(
191
            {
192
                go_mod_addr: GoModuleImportPathsMapping(
193
                    mapping=FrozenDict(
194
                        {
195
                            import_path: GoImportPathsMappingAddressSet(
196
                                addresses=tuple(sorted(addresses)), infer_all=True
197
                            )
198
                            for import_path, addresses in import_path_mapping.items()
199
                        }
200
                    ),
201
                    address_to_import_path=FrozenDict(
202
                        {
203
                            address: import_path
204
                            for import_path, addresses in import_path_mapping.items()
205
                            for address in addresses
206
                        }
207
                    ),
208
                )
209
                for go_mod_addr, import_path_mapping in import_paths_by_module.items()
210
            }
211
        )
212
    )
213

214

215
@dataclass(frozen=True)
×
216
class _SetupGoProtobufPackageBuildRequest:
×
217
    """Request type used to trigger setup of a BuildGoPackageRequest for entire generated Go
218
    Protobuf package.
219

220
    This type is separate so that a build of the full package can be cached no matter which one of
221
    its component source files was requested. This occurs because a request to build any one of the
222
    source files will be converted into this type and then built.
223
    """
224

225
    addresses: tuple[Address, ...]
×
226
    import_path: str
×
227
    build_opts: GoBuildOptions
×
228

229

230
@rule
×
231
async def setup_full_package_build_request(
×
232
    request: _SetupGoProtobufPackageBuildRequest,
233
    protoc: Protoc,
234
    go_protoc_plugin: _SetupGoProtocPlugin,
235
    analyzer: PackageAnalyzerSetup,
236
    platform: Platform,
237
) -> FallibleBuildGoPackageRequest:
238
    output_dir = "_generated_files"
×
239
    protoc_relpath = "__protoc"
×
240
    protoc_go_plugin_relpath = "__protoc_gen_go"
×
241

242
    (
×
243
        transitive_targets_for_protobuf_source,
244
        downloaded_protoc_binary,
245
        empty_output_dir,
246
    ) = await concurrently(
247
        transitive_targets(TransitiveTargetsRequest(request.addresses), **implicitly()),
248
        download_external_tool(protoc.get_request(platform)),
249
        create_digest(CreateDigest([Directory(output_dir)])),
250
    )
251

252
    go_mod_addr = await find_owning_go_mod(
×
253
        OwningGoModRequest(transitive_targets_for_protobuf_source.roots[0].address), **implicitly()
254
    )
255
    package_mapping = await map_import_paths_to_packages(
×
256
        GoImportPathMappingRequest(go_mod_addr.address), **implicitly()
257
    )
258

259
    all_sources = await determine_source_files(
×
260
        SourceFilesRequest(
261
            sources_fields=(
262
                tgt[ProtobufSourceField]
263
                for tgt in transitive_targets_for_protobuf_source.closure
264
                if tgt.has_field(ProtobufSourceField)
265
            ),
266
            for_sources_types=(ProtobufSourceField,),
267
            enable_codegen=True,
268
        )
269
    )
270
    source_roots, input_digest = await concurrently(
×
271
        get_source_roots(SourceRootsRequest.for_files(all_sources.files)),
272
        merge_digests(MergeDigests([all_sources.snapshot.digest, empty_output_dir])),
273
    )
274

275
    source_root_paths = sorted({sr.path for sr in source_roots.path_to_root.values()})
×
276

277
    pkg_sources = await concurrently(
×
278
        resolve_source_paths(SourcesPathsRequest(tgt[ProtobufSourceField]), **implicitly())
279
        for tgt in transitive_targets_for_protobuf_source.roots
280
    )
281
    pkg_files = sorted({f for ps in pkg_sources for f in ps.files})
×
282

283
    maybe_grpc_plugin_args = []
×
284
    if any(
×
285
        tgt.get(ProtobufGrpcToggleField).value
286
        for tgt in transitive_targets_for_protobuf_source.roots
287
    ):
288
        maybe_grpc_plugin_args = [
×
289
            f"--go-grpc_out={output_dir}",
290
            "--go-grpc_opt=paths=source_relative",
291
        ]
292

293
    gen_result = await execute_process(
×
294
        Process(
295
            argv=[
296
                os.path.join(protoc_relpath, downloaded_protoc_binary.exe),
297
                f"--plugin=go={os.path.join('.', protoc_go_plugin_relpath, 'protoc-gen-go')}",
298
                f"--plugin=go-grpc={os.path.join('.', protoc_go_plugin_relpath, 'protoc-gen-go-grpc')}",
299
                f"--go_out={output_dir}",
300
                "--go_opt=paths=source_relative",
301
                *(f"--proto_path={source_root}" for source_root in source_root_paths),
302
                *maybe_grpc_plugin_args,
303
                *pkg_files,
304
            ],
305
            # Note: Necessary or else --plugin option needs absolute path.
306
            env={"PATH": protoc_go_plugin_relpath},
307
            input_digest=input_digest,
308
            immutable_input_digests={
309
                protoc_relpath: downloaded_protoc_binary.digest,
310
                protoc_go_plugin_relpath: go_protoc_plugin.digest,
311
            },
312
            description=f"Generating Go sources from {request.import_path}.",
313
            level=LogLevel.DEBUG,
314
            output_directories=(output_dir,),
315
        ),
316
        **implicitly(),
317
    )
318
    if gen_result.exit_code != 0:
×
319
        return FallibleBuildGoPackageRequest(
×
320
            request=None,
321
            import_path=request.import_path,
322
            exit_code=gen_result.exit_code,
323
            stderr=gen_result.stderr.decode(),
324
        )
325

326
    # Ensure that the generated files are in a single package directory.
327
    gen_sources = await digest_to_snapshot(gen_result.output_digest)
×
328
    files_by_dir = group_by_dir(gen_sources.files)
×
329
    if len(files_by_dir) != 1:
×
330
        return FallibleBuildGoPackageRequest(
×
331
            request=None,
332
            import_path=request.import_path,
333
            exit_code=1,
334
            stderr=textwrap.dedent(
335
                f"""
336
                Expected Go files generated from Protobuf sources to be output to a single directory.
337
                - import path: {request.import_path}
338
                - protobuf files: {", ".join(pkg_files)}
339
                """
340
            ).strip(),
341
        )
342
    gen_dir = list(files_by_dir.keys())[0]
×
343

344
    # Analyze the generated sources.
345
    input_digest = await merge_digests(MergeDigests([gen_sources.digest, analyzer.digest]))
×
346
    result = await execute_process(
×
347
        Process(
348
            (analyzer.path, gen_dir),
349
            input_digest=input_digest,
350
            description=f"Determine metadata for generated Go package for {request.import_path}",
351
            level=LogLevel.DEBUG,
352
            env={"CGO_ENABLED": "0"},  # protobuf files should not have cgo!
353
        ),
354
        **implicitly(),
355
    )
356

357
    # Parse the metadata from the analysis.
358
    fallible_analysis = FallibleFirstPartyPkgAnalysis.from_process_result(
×
359
        result,
360
        dir_path=gen_dir,
361
        import_path=request.import_path,
362
        minimum_go_version="",
363
        description_of_source=f"Go package generated from protobuf targets `{', '.join(str(addr) for addr in request.addresses)}`",
364
    )
365
    if not fallible_analysis.analysis:
×
366
        return FallibleBuildGoPackageRequest(
×
367
            request=None,
368
            import_path=request.import_path,
369
            exit_code=fallible_analysis.exit_code,
370
            stderr=fallible_analysis.stderr,
371
        )
372
    analysis = fallible_analysis.analysis
×
373

374
    # Obtain build requests for third-party dependencies.
375
    # TODO: Consider how to merge this code with existing dependency inference code.
376
    dep_build_request_addrs: set[Address] = set()
×
377
    for dep_import_path in (*analysis.imports, *analysis.test_imports, *analysis.xtest_imports):
×
378
        # Infer dependencies on other Go packages.
379
        candidate_addresses = package_mapping.mapping.get(dep_import_path)
×
380
        if candidate_addresses:
×
381
            # TODO: Use explicit dependencies to disambiguate? This should never happen with Go backend though.
382
            if candidate_addresses.infer_all:
×
383
                dep_build_request_addrs.update(candidate_addresses.addresses)
×
384
            else:
385
                if len(candidate_addresses.addresses) > 1:
×
386
                    return FallibleBuildGoPackageRequest(
×
387
                        request=None,
388
                        import_path=request.import_path,
389
                        exit_code=result.exit_code,
390
                        stderr=textwrap.dedent(
391
                            f"""
392
                            Multiple addresses match import of `{dep_import_path}`.
393

394
                            addresses: {", ".join(str(a) for a in candidate_addresses.addresses)}
395
                            """
396
                        ).strip(),
397
                    )
398
                dep_build_request_addrs.update(candidate_addresses.addresses)
×
399

400
    dep_build_requests = await concurrently(
×
401
        required_build_go_package_request(
402
            **implicitly(BuildGoPackageTargetRequest(addr, build_opts=request.build_opts))
403
        )
404
        for addr in sorted(dep_build_request_addrs)
405
    )
406

407
    return FallibleBuildGoPackageRequest(
×
408
        request=BuildGoPackageRequest(
409
            import_path=request.import_path,
410
            pkg_name=analysis.name,
411
            digest=gen_sources.digest,
412
            dir_path=analysis.dir_path,
413
            go_files=analysis.go_files,
414
            s_files=analysis.s_files,
415
            direct_dependencies=dep_build_requests,
416
            minimum_go_version=analysis.minimum_go_version,
417
            build_opts=request.build_opts,
418
        ),
419
        import_path=request.import_path,
420
    )
421

422

423
@rule
×
424
async def setup_build_go_package_request_for_protobuf(
×
425
    request: GoCodegenBuildProtobufRequest,
426
) -> FallibleBuildGoPackageRequest:
427
    # Hydrate the protobuf source to parse for the Go import path.
428
    sources = await hydrate_sources(
×
429
        HydrateSourcesRequest(request.target[ProtobufSourceField]), **implicitly()
430
    )
431
    sources_content = await get_digest_contents(sources.snapshot.digest)
×
432
    assert len(sources_content) == 1
×
433
    import_path = parse_go_package_option(sources_content[0].content)
×
434
    if not import_path:
×
435
        return FallibleBuildGoPackageRequest(
×
436
            request=None,
437
            import_path="",
438
            exit_code=1,
439
            stderr=f"No import path was set in Protobuf file via `option go_package` directive for {request.target.address}.",
440
        )
441

442
    go_mod_addr = await find_owning_go_mod(
×
443
        OwningGoModRequest(request.target.address), **implicitly()
444
    )
445
    package_mapping = await map_import_paths_to_packages(
×
446
        GoImportPathMappingRequest(go_mod_addr.address), **implicitly()
447
    )
448

449
    # Request the full build of the package. This indirection is necessary so that requests for two or more
450
    # Protobuf files in the same Go package result in a single cacheable rule invocation.
451
    protobuf_target_addrs_set_for_import_path = package_mapping.mapping.get(import_path)
×
452
    if not protobuf_target_addrs_set_for_import_path:
×
453
        return FallibleBuildGoPackageRequest(
×
454
            request=None,
455
            import_path=import_path,
456
            exit_code=1,
457
            stderr=softwrap(
458
                f"""
459
                No Protobuf files exists for import path `{import_path}`.
460
                Consider whether the import path was set correctly via the `option go_package` directive.
461
                """
462
            ),
463
        )
464

465
    return await setup_full_package_build_request(
×
466
        _SetupGoProtobufPackageBuildRequest(
467
            addresses=protobuf_target_addrs_set_for_import_path.addresses,
468
            import_path=import_path,
469
            build_opts=request.build_opts,
470
        ),
471
        **implicitly(),
472
    )
473

474

475
@rule(desc="Generate Go source files from Protobuf", level=LogLevel.DEBUG)
×
476
async def generate_go_from_protobuf(
×
477
    request: GenerateGoFromProtobufRequest,
478
    protoc: Protoc,
479
    go_protoc_plugin: _SetupGoProtocPlugin,
480
    platform: Platform,
481
) -> GeneratedSources:
482
    output_dir = "_generated_files"
×
483
    protoc_relpath = "__protoc"
×
484
    protoc_go_plugin_relpath = "__protoc_gen_go"
×
485

486
    (
×
487
        downloaded_protoc_binary,
488
        empty_output_dir,
489
        transitive_targets_for_protobuf_source,
490
    ) = await concurrently(
491
        download_external_tool(protoc.get_request(platform)),
492
        create_digest(CreateDigest([Directory(output_dir)])),
493
        transitive_targets(
494
            TransitiveTargetsRequest([request.protocol_target.address]), **implicitly()
495
        ),
496
    )
497

498
    # NB: By stripping the source roots, we avoid having to set the value `--proto_path`
499
    # for Protobuf imports to be discoverable.
500
    all_sources_stripped, target_sources_stripped = await concurrently(
×
501
        strip_source_roots(
502
            **implicitly(
503
                SourceFilesRequest(
504
                    tgt[ProtobufSourceField]
505
                    for tgt in transitive_targets_for_protobuf_source.closure
506
                    if tgt.has_field(ProtobufSourceField)
507
                )
508
            )
509
        ),
510
        strip_source_roots(
511
            **implicitly(SourceFilesRequest([request.protocol_target[ProtobufSourceField]]))
512
        ),
513
    )
514

515
    input_digest = await merge_digests(
×
516
        MergeDigests([all_sources_stripped.snapshot.digest, empty_output_dir])
517
    )
518

519
    maybe_grpc_plugin_args = []
×
520
    if request.protocol_target.get(ProtobufGrpcToggleField).value:
×
521
        maybe_grpc_plugin_args = [
×
522
            f"--go-grpc_out={output_dir}",
523
            "--go-grpc_opt=paths=source_relative",
524
        ]
525

526
    result = await fallible_to_exec_result_or_raise(
×
527
        **implicitly(
528
            Process(
529
                argv=[
530
                    os.path.join(protoc_relpath, downloaded_protoc_binary.exe),
531
                    f"--plugin=go={os.path.join('.', protoc_go_plugin_relpath, 'protoc-gen-go')}",
532
                    f"--plugin=go-grpc={os.path.join('.', protoc_go_plugin_relpath, 'protoc-gen-go-grpc')}",
533
                    f"--go_out={output_dir}",
534
                    "--go_opt=paths=source_relative",
535
                    *maybe_grpc_plugin_args,
536
                    *target_sources_stripped.snapshot.files,
537
                ],
538
                # Note: Necessary or else --plugin option needs absolute path.
539
                env={"PATH": protoc_go_plugin_relpath},
540
                input_digest=input_digest,
541
                immutable_input_digests={
542
                    protoc_relpath: downloaded_protoc_binary.digest,
543
                    protoc_go_plugin_relpath: go_protoc_plugin.digest,
544
                },
545
                description=f"Generating Go sources from {request.protocol_target.address}.",
546
                level=LogLevel.DEBUG,
547
                output_directories=(output_dir,),
548
            )
549
        )
550
    )
551

552
    normalized_digest, source_root = await concurrently(
×
553
        remove_prefix(RemovePrefix(result.output_digest, output_dir)),
554
        get_source_root(SourceRootRequest.for_target(request.protocol_target)),
555
    )
556

557
    source_root_restored = (
×
558
        await digest_to_snapshot(**implicitly(AddPrefix(normalized_digest, source_root.path)))
559
        if source_root.path != "."
560
        else await digest_to_snapshot(normalized_digest)
561
    )
562
    return GeneratedSources(source_root_restored)
×
563

564

565
# Note: The versions of the Go protoc and gRPC plugins are hard coded in the following go.mod. To update,
566
# copy the following go.mod and go.sum contents to go.mod and go.sum files in a new directory. Then update the
567
# versions and run `go mod download all`. Copy the go.mod and go.sum contents back into these constants,
568
# making sure to replace tabs with `\t`.
569

570
GO_PROTOBUF_GO_MOD = """\
×
571
module org.pantsbuild.backend.go.protobuf
572

573
go 1.17
574

575
require (
576
\tgoogle.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0
577
\tgoogle.golang.org/protobuf v1.27.1
578
)
579

580
require (
581
\tgithub.com/golang/protobuf v1.5.0 // indirect
582
\tgithub.com/google/go-cmp v0.5.5 // indirect
583
\tgolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect
584
)
585
"""
586

587
GO_PROTOBUF_GO_SUM = """\
×
588
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
589
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
590
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
591
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
592
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
593
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
594
google.golang.org/grpc v1.2.0 h1:v8eFdETH8nqZHQ9x+0f2PLuU6W7zo5PFZuVEwH5126Y=
595
google.golang.org/grpc v1.2.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
596
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 h1:TLkBREm4nIsEcexnCjgQd5GQWaHcqMzwQV0TX9pq8S0=
597
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0/go.mod h1:DNq5QpG7LJqD2AamLZ7zvKE0DEpVl2BSEVjFycAAjRY=
598
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
599
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
600
"""
601

602

603
@rule
×
604
async def setup_go_protoc_plugin() -> _SetupGoProtocPlugin:
×
605
    go_mod_digest = await create_digest(
×
606
        CreateDigest(
607
            [
608
                FileContent("go.mod", GO_PROTOBUF_GO_MOD.encode()),
609
                FileContent("go.sum", GO_PROTOBUF_GO_SUM.encode()),
610
            ]
611
        )
612
    )
613

614
    download_sources_result = await fallible_to_exec_result_or_raise(
×
615
        **implicitly(
616
            GoSdkProcess(
617
                ["mod", "download", "all"],
618
                input_digest=go_mod_digest,
619
                output_directories=("gopath",),
620
                description="Download Go `protoc` plugin sources.",
621
                allow_downloads=True,
622
            )
623
        )
624
    )
625

626
    go_plugin_build_result, go_grpc_plugin_build_result = await concurrently(
×
627
        fallible_to_exec_result_or_raise(
628
            **implicitly(
629
                GoSdkProcess(
630
                    ["install", "google.golang.org/protobuf/cmd/protoc-gen-go@v1.27.1"],
631
                    input_digest=download_sources_result.output_digest,
632
                    output_files=["gopath/bin/protoc-gen-go"],
633
                    description="Build Go protobuf plugin for `protoc`.",
634
                    # Allow `go` to contact the Go module proxy since it will run its own build.
635
                    allow_downloads=True,
636
                )
637
            )
638
        ),
639
        fallible_to_exec_result_or_raise(
640
            **implicitly(
641
                GoSdkProcess(
642
                    [
643
                        "install",
644
                        "google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2.0",
645
                    ],
646
                    input_digest=download_sources_result.output_digest,
647
                    output_files=["gopath/bin/protoc-gen-go-grpc"],
648
                    description="Build Go gRPC protobuf plugin for `protoc`.",
649
                    # Allow `go` to contact the Go module proxy since it will run its own build.
650
                    allow_downloads=True,
651
                )
652
            )
653
        ),
654
    )
655
    if go_plugin_build_result.output_digest == EMPTY_DIGEST:
×
656
        raise AssertionError(
×
657
            f"Failed to build protoc-gen-go:\n"
658
            f"stdout:\n{go_plugin_build_result.stdout.decode()}\n\n"
659
            f"stderr:\n{go_plugin_build_result.stderr.decode()}"
660
        )
661
    if go_grpc_plugin_build_result.output_digest == EMPTY_DIGEST:
×
662
        raise AssertionError(
×
663
            f"Failed to build protoc-gen-go-grpc:\n"
664
            f"stdout:\n{go_grpc_plugin_build_result.stdout.decode()}\n\n"
665
            f"stderr:\n{go_grpc_plugin_build_result.stderr.decode()}"
666
        )
667

668
    merged_output_digests = await merge_digests(
×
669
        MergeDigests(
670
            [go_plugin_build_result.output_digest, go_grpc_plugin_build_result.output_digest]
671
        )
672
    )
673
    plugin_digest = await remove_prefix(RemovePrefix(merged_output_digests, "gopath/bin"))
×
674
    return _SetupGoProtocPlugin(plugin_digest)
×
675

676

677
def rules():
×
678
    return (
×
679
        *collect_rules(),
680
        UnionRule(GenerateSourcesRequest, GenerateGoFromProtobufRequest),
681
        UnionRule(GoCodegenBuildRequest, GoCodegenBuildProtobufRequest),
682
        UnionRule(GoModuleImportPathsMappingsHook, ProtobufGoModuleImportPathsMappingsHook),
683
        ProtobufSourcesGeneratorTarget.register_plugin_field(GoOwningGoModAddressField),
684
        ProtobufSourceTarget.register_plugin_field(GoOwningGoModAddressField),
685
        *protoc.rules(),
686
        # Rules needed for this to pass src/python/pants/init/load_backends_integration_test.py:
687
        *assembly.rules(),
688
        *build_pkg.rules(),
689
        *build_pkg_target.rules(),
690
        *first_party_pkg.rules(),
691
        *go_mod.rules(),
692
        *link.rules(),
693
        *sdk.rules(),
694
        *target_type_rules.rules(),
695
        *third_party_pkg.rules(),
696
        *pex.rules(),
697
    )
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

© 2026 Coveralls, Inc