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

pantsbuild / pants / 23173035367

17 Mar 2026 12:47AM UTC coverage: 91.371% (-1.6%) from 92.933%
23173035367

push

github

web-flow
update helm (and friends) to a recent 3.x seies (#23143)

v4 is a major breaking change, so holding off on that. (There was also
just a 3.20, but 3.19 has this reassuring series of bug fixes and was
also quite recent.)

2 of 2 new or added lines in 2 files covered. (100.0%)

1263 existing lines in 73 files now uncovered.

86196 of 94336 relevant lines covered (91.37%)

3.87 hits per line

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

96.83
/src/python/pants/backend/go/util_rules/link.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
10✔
4

5
import textwrap
10✔
6
from dataclasses import dataclass
10✔
7

8
from pants.backend.go.subsystems.golang import GolangSubsystem
10✔
9
from pants.backend.go.util_rules import import_config
10✔
10
from pants.backend.go.util_rules.build_opts import GoBuildOptions
10✔
11
from pants.backend.go.util_rules.cgo_binaries import CGoBinaryPathRequest, find_cgo_binary_path
10✔
12
from pants.backend.go.util_rules.import_config import ImportConfigRequest, generate_import_config
10✔
13
from pants.backend.go.util_rules.link_defs import (
10✔
14
    ImplicitLinkerDependenciesHook,
15
    get_implicit_linker_dependencies,
16
)
17
from pants.backend.go.util_rules.sdk import GoSdkProcess, GoSdkToolIDRequest, compute_go_tool_id
10✔
18
from pants.core.util_rules.system_binaries import BashBinary, BinaryPathTest
10✔
19
from pants.engine.fs import CreateDigest, Digest, Directory, FileContent
10✔
20
from pants.engine.internals.native_engine import AddPrefix, MergeDigests
10✔
21
from pants.engine.internals.selectors import concurrently
10✔
22
from pants.engine.intrinsics import add_prefix, create_digest, merge_digests
10✔
23
from pants.engine.process import fallible_to_exec_result_or_raise
10✔
24
from pants.engine.rules import collect_rules, implicitly, rule
10✔
25
from pants.engine.unions import UnionMembership
10✔
26
from pants.util.frozendict import FrozenDict
10✔
27

28

29
@dataclass(frozen=True)
10✔
30
class LinkGoBinaryRequest:
10✔
31
    """Link a Go binary from package archives and an import configuration."""
32

33
    input_digest: Digest
10✔
34
    archives: tuple[str, ...]
10✔
35
    build_opts: GoBuildOptions
10✔
36
    import_paths_to_pkg_a_files: FrozenDict[str, str]
10✔
37
    output_filename: str
10✔
38
    description: str
10✔
39

40

41
@dataclass(frozen=True)
10✔
42
class LinkedGoBinary:
10✔
43
    """A linked Go binary stored in a `Digest`."""
44

45
    digest: Digest
10✔
46

47

48
@dataclass(frozen=True)
10✔
49
class LinkerSetup:
10✔
50
    digest: Digest
10✔
51
    extld_wrapper_path: str
10✔
52

53

54
@rule
10✔
55
async def setup_go_linker(
10✔
56
    bash: BashBinary, golang_subsystem: GolangSubsystem.EnvironmentAware
57
) -> LinkerSetup:
58
    extld_binary = await find_cgo_binary_path(
9✔
59
        CGoBinaryPathRequest(
60
            binary_name=golang_subsystem.external_linker_binary_name,
61
            binary_path_test=BinaryPathTest(["--version"]),
62
        ),
63
        **implicitly(),
64
    )
65

66
    extld_wrapper_path = "__pants_extld_wrapper__"
9✔
67
    digest = await create_digest(
9✔
68
        CreateDigest(
69
            [
70
                FileContent(
71
                    path=extld_wrapper_path,
72
                    content=textwrap.dedent(
73
                        f"""\
74
                        #!{bash.path}
75
                        args=("${{@//__PANTS_SANDBOX_ROOT__/$__PANTS_SANDBOX_ROOT__}}")
76
                        exec {extld_binary.path} "${{args[@]}}"
77
                        """
78
                    ).encode(),
79
                    is_executable=True,
80
                ),
81
            ]
82
        )
83
    )
84
    return LinkerSetup(digest, extld_wrapper_path)
9✔
85

86

87
@rule
10✔
88
async def link_go_binary(
10✔
89
    request: LinkGoBinaryRequest,
90
    linker_setup: LinkerSetup,
91
    union_membership: UnionMembership,
92
) -> LinkedGoBinary:
93
    implict_linker_deps_hooks = union_membership.get(ImplicitLinkerDependenciesHook)
9✔
94
    implicit_linker_deps = await concurrently(
9✔
95
        get_implicit_linker_dependencies(
96
            **implicitly({hook(build_opts=request.build_opts): ImplicitLinkerDependenciesHook})
97
        )
98
        for hook in implict_linker_deps_hooks
99
    )
100

101
    implicit_dep_digests = []
9✔
102
    import_paths_to_pkg_a_files = dict(request.import_paths_to_pkg_a_files)
9✔
103
    for implicit_linker_dep in implicit_linker_deps:
9✔
104
        for (
3✔
105
            dep_import_path,
106
            pkg_archive_path,
107
        ) in implicit_linker_dep.import_paths_to_pkg_a_files.items():
108
            if dep_import_path not in import_paths_to_pkg_a_files:
3✔
UNCOV
109
                import_paths_to_pkg_a_files[dep_import_path] = pkg_archive_path
×
UNCOV
110
                implicit_dep_digests.append(implicit_linker_dep.digest)
×
111

112
    link_tmp_dir = "link-tmp"
9✔
113
    link_tmp_dir_digest = await create_digest(CreateDigest([Directory(link_tmp_dir)]))
9✔
114

115
    link_tool_id, import_config = await concurrently(
9✔
116
        compute_go_tool_id(GoSdkToolIDRequest("link")),
117
        generate_import_config(
118
            ImportConfigRequest(
119
                FrozenDict(import_paths_to_pkg_a_files), build_opts=request.build_opts
120
            )
121
        ),
122
    )
123

124
    import_config_digest = await add_prefix(AddPrefix(import_config.digest, link_tmp_dir))
9✔
125

126
    input_digest = await merge_digests(
9✔
127
        MergeDigests(
128
            [
129
                request.input_digest,
130
                link_tmp_dir_digest,
131
                linker_setup.digest,
132
                import_config_digest,
133
                *implicit_dep_digests,
134
            ]
135
        )
136
    )
137

138
    maybe_race_arg = ["-race"] if request.build_opts.with_race_detector else []
9✔
139
    maybe_msan_arg = ["-msan"] if request.build_opts.with_msan else []
9✔
140
    maybe_asan_arg = ["-asan"] if request.build_opts.with_asan else []
9✔
141

142
    result = await fallible_to_exec_result_or_raise(
9✔
143
        **implicitly(
144
            GoSdkProcess(
145
                input_digest=input_digest,
146
                command=(
147
                    "tool",
148
                    "link",
149
                    # Put the linker's temporary directory into the input root.
150
                    "-tmpdir",
151
                    f"__PANTS_SANDBOX_ROOT__/{link_tmp_dir}",
152
                    # Force `go tool link` to use a wrapper script as the "external linker" so that the script can
153
                    # replace any instances of `__PANTS_SANDBOX_ROOT__` in the linker arguments. This also allows
154
                    # Pants to know which external linker is in use and invalidate this `Process` as needed.
155
                    "-extld",
156
                    f"__PANTS_SANDBOX_ROOT__/{linker_setup.extld_wrapper_path}",
157
                    *maybe_race_arg,
158
                    *maybe_msan_arg,
159
                    *maybe_asan_arg,
160
                    "-importcfg",
161
                    f"{link_tmp_dir}/{import_config.CONFIG_PATH}",
162
                    "-o",
163
                    request.output_filename,
164
                    "-buildmode=exe",  # seen in `go build -x` output
165
                    *request.build_opts.linker_flags,
166
                    *request.archives,
167
                ),
168
                env={
169
                    "__PANTS_GO_LINK_TOOL_ID": link_tool_id.tool_id,
170
                },
171
                description=f"Link Go binary: {request.output_filename}",
172
                output_files=(request.output_filename,),
173
                replace_sandbox_root_in_args=True,
174
            ),
175
        )
176
    )
177

178
    return LinkedGoBinary(result.output_digest)
9✔
179

180

181
def rules():
10✔
182
    return (
10✔
183
        *collect_rules(),
184
        *import_config.rules(),
185
    )
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