• 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/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).
UNCOV
3
from __future__ import annotations
×
4

UNCOV
5
import textwrap
×
UNCOV
6
from dataclasses import dataclass
×
7

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

28

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

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

40

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

UNCOV
45
    digest: Digest
×
46

47

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

53

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

86

UNCOV
87
@rule
×
UNCOV
88
async def link_go_binary(
×
89
    request: LinkGoBinaryRequest,
90
    linker_setup: LinkerSetup,
91
    union_membership: UnionMembership,
92
) -> LinkedGoBinary:
93
    implict_linker_deps_hooks = union_membership.get(ImplicitLinkerDependenciesHook)
×
94
    implicit_linker_deps = await concurrently(
×
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 = []
×
102
    import_paths_to_pkg_a_files = dict(request.import_paths_to_pkg_a_files)
×
103
    for implicit_linker_dep in implicit_linker_deps:
×
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"
×
113
    link_tmp_dir_digest = await create_digest(CreateDigest([Directory(link_tmp_dir)]))
×
114

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

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

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

180

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