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

pantsbuild / pants / 23177125175

17 Mar 2026 03:32AM UTC coverage: 52.677% (-40.3%) from 92.932%
23177125175

Pull #23177

github

web-flow
Merge 1824dfbf4 into 0b9fdfb0e
Pull Request #23177: Bump the gha-deps group across 1 directory with 4 updates

31687 of 60153 relevant lines covered (52.68%)

1.05 hits per line

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

93.65
/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
2✔
4

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

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

28

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

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

40

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

45
    digest: Digest
2✔
46

47

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

53

54
@rule
2✔
55
async def setup_go_linker(
2✔
56
    bash: BashBinary, golang_subsystem: GolangSubsystem.EnvironmentAware
57
) -> LinkerSetup:
58
    extld_binary = await find_cgo_binary_path(
2✔
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__"
2✔
67
    digest = await create_digest(
2✔
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)
2✔
85

86

87
@rule
2✔
88
async def link_go_binary(
2✔
89
    request: LinkGoBinaryRequest,
90
    linker_setup: LinkerSetup,
91
    union_membership: UnionMembership,
92
) -> LinkedGoBinary:
93
    implict_linker_deps_hooks = union_membership.get(ImplicitLinkerDependenciesHook)
2✔
94
    implicit_linker_deps = await concurrently(
2✔
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 = []
2✔
102
    import_paths_to_pkg_a_files = dict(request.import_paths_to_pkg_a_files)
2✔
103
    for implicit_linker_dep in implicit_linker_deps:
2✔
104
        for (
×
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:
×
109
                import_paths_to_pkg_a_files[dep_import_path] = pkg_archive_path
×
110
                implicit_dep_digests.append(implicit_linker_dep.digest)
×
111

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

115
    link_tool_id, import_config = await concurrently(
2✔
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))
2✔
125

126
    input_digest = await merge_digests(
2✔
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 []
2✔
139
    maybe_msan_arg = ["-msan"] if request.build_opts.with_msan else []
2✔
140
    maybe_asan_arg = ["-asan"] if request.build_opts.with_asan else []
2✔
141

142
    result = await fallible_to_exec_result_or_raise(
2✔
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)
2✔
179

180

181
def rules():
2✔
182
    return (
2✔
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