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

pantsbuild / pants / 25443604553

06 May 2026 03:05PM UTC coverage: 92.879% (-0.04%) from 92.915%
25443604553

push

github

web-flow
[pants_ng] Scaffolding for a pants_ng mode. (#23319)

In this mode the command line is parsed as an
NG invocation, and dispatched appropriately.

Of course at the moment there are no
implementations to dispatch to. That will follow.

This does expose a new option, `pants_ng` to users. 
There is a big warning not to set it, but we're not trying
to hide that we're working on a new thing, so I am
comfortable with this.

25 of 76 new or added lines in 9 files covered. (32.89%)

1294 existing lines in 76 files now uncovered.

92234 of 99306 relevant lines covered (92.88%)

4.05 hits per line

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

90.14
/src/python/pants/backend/go/util_rules/build_pkg_target.py
1
# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

4
from __future__ import annotations
7✔
5

6
import dataclasses
7✔
7
import json
7✔
8
from dataclasses import dataclass
7✔
9
from typing import ClassVar
7✔
10

11
from pants.backend.go.go_sources.load_go_binary import LoadedGoBinaryRequest, setup_go_binary
7✔
12
from pants.backend.go.target_type_rules import (
7✔
13
    GoImportPathMappingRequest,
14
    map_import_paths_to_packages,
15
)
16
from pants.backend.go.target_types import (
7✔
17
    GoAssemblerFlagsField,
18
    GoCompilerFlagsField,
19
    GoImportPathField,
20
    GoPackageSourcesField,
21
    GoThirdPartyPackageDependenciesField,
22
)
23
from pants.backend.go.util_rules import build_opts
7✔
24
from pants.backend.go.util_rules.build_opts import GoBuildOptions
7✔
25
from pants.backend.go.util_rules.build_pkg import (
7✔
26
    BuildGoPackageRequest,
27
    FallibleBuildGoPackageRequest,
28
)
29
from pants.backend.go.util_rules.cgo import CGoCompilerFlags
7✔
30
from pants.backend.go.util_rules.coverage import GoCoverMode
7✔
31
from pants.backend.go.util_rules.embedcfg import EmbedConfig
7✔
32
from pants.backend.go.util_rules.first_party_pkg import (
7✔
33
    FirstPartyPkgAnalysisRequest,
34
    FirstPartyPkgDigestRequest,
35
    FirstPartyPkgImportPathRequest,
36
    analyze_first_party_package,
37
    compute_first_party_package_import_path,
38
    setup_first_party_pkg_digest,
39
)
40
from pants.backend.go.util_rules.go_mod import (
7✔
41
    GoModInfoRequest,
42
    OwningGoModRequest,
43
    determine_go_mod_info,
44
    find_owning_go_mod,
45
)
46
from pants.backend.go.util_rules.goroot import GoRoot
7✔
47
from pants.backend.go.util_rules.import_analysis import (
7✔
48
    GoStdLibPackage,
49
    GoStdLibPackagesRequest,
50
    analyze_go_stdlib_packages,
51
)
52
from pants.backend.go.util_rules.pkg_pattern import match_simple_pattern
7✔
53
from pants.backend.go.util_rules.third_party_pkg import (
7✔
54
    ThirdPartyPkgAnalysisRequest,
55
    extract_package_info,
56
)
57
from pants.build_graph.address import Address
7✔
58
from pants.engine.engine_aware import EngineAwareParameter
7✔
59
from pants.engine.environment import EnvironmentName
7✔
60
from pants.engine.fs import CreateDigest, FileContent
7✔
61
from pants.engine.internals.graph import (
7✔
62
    AmbiguousCodegenImplementationsException,
63
    resolve_target,
64
    resolve_targets,
65
)
66
from pants.engine.internals.native_engine import EMPTY_DIGEST, MergeDigests
7✔
67
from pants.engine.internals.selectors import concurrently
7✔
68
from pants.engine.intrinsics import create_digest, execute_process, merge_digests
7✔
69
from pants.engine.process import Process
7✔
70
from pants.engine.rules import collect_rules, implicitly, rule
7✔
71
from pants.engine.target import (
7✔
72
    Dependencies,
73
    DependenciesRequest,
74
    SourcesField,
75
    Target,
76
    WrappedTargetRequest,
77
)
78
from pants.engine.unions import UnionMembership, union
7✔
79
from pants.util.frozendict import FrozenDict
7✔
80
from pants.util.logging import LogLevel
7✔
81
from pants.util.strutil import bullet_list
7✔
82

83

84
@dataclass(frozen=True)
7✔
85
class BuildGoPackageTargetRequest(EngineAwareParameter):
7✔
86
    """Build a `go_package`, `go_third_party_package`, or Go codegen target and its dependencies as
87
    `__pkg__.a` files."""
88

89
    address: Address
7✔
90
    build_opts: GoBuildOptions
7✔
91
    is_main: bool = False
7✔
92
    for_tests: bool = False
7✔
93
    for_xtests: bool = False
7✔
94

95
    # If True, then force coverage instead of applying import path patterns from `build_opts.coverage_config`.
96
    with_coverage: bool = False
7✔
97

98
    # Extra standard library dependencies to force on the target. Useful for implicit linker dependencies.
99
    extra_stdlib_dependencies: tuple[str, ...] = ()
7✔
100

101
    def debug_hint(self) -> str:
7✔
102
        return str(self.address)
4✔
103

104
    def __post_init__(self):
7✔
105
        if self.for_tests and self.for_xtests:
7✔
106
            raise ValueError(
×
107
                "`BuildGoPackageTargetRequest.for_tests` and `BuildGoPackageTargetRequest.for_xtests` "
108
                "cannot be set together."
109
            )
110

111

112
@dataclass(frozen=True)
7✔
113
class _ResolveStdlibEmbedConfigRequest:
7✔
114
    package: GoStdLibPackage
7✔
115

116

117
@dataclass(frozen=True)
7✔
118
class _ResolveStdlibEmbedConfigResult:
7✔
119
    embed_config: EmbedConfig | None
7✔
120
    stderr: str | None
7✔
121

122

123
@rule
7✔
124
async def resolve_go_stdlib_embed_config(
7✔
125
    request: _ResolveStdlibEmbedConfigRequest,
126
) -> _ResolveStdlibEmbedConfigResult:
127
    patterns_json = json.dumps(
×
128
        {
129
            "EmbedPatterns": request.package.embed_patterns,
130
            "TestEmbedPatterns": [],
131
            "XTestEmbedPatterns": [],
132
        }
133
    ).encode("utf-8")
134

135
    embedder, patterns_json_digest = await concurrently(
×
136
        setup_go_binary(
137
            LoadedGoBinaryRequest("embedcfg", ("main.go",), "./embedder"), **implicitly()
138
        ),
139
        create_digest(CreateDigest([FileContent("patterns.json", patterns_json)])),
140
    )
141
    input_digest = await merge_digests(MergeDigests((patterns_json_digest, embedder.digest)))
×
142
    embed_result = await execute_process(
×
143
        Process(
144
            ("./embedder", "patterns.json", request.package.pkg_source_path),
145
            input_digest=input_digest,
146
            description=f"Create embed mapping for {request.package.import_path}",
147
            level=LogLevel.DEBUG,
148
        ),
149
        **implicitly(),
150
    )
151
    if embed_result.exit_code != 0:
×
152
        return _ResolveStdlibEmbedConfigResult(
×
153
            embed_config=None,
154
            stderr=embed_result.stderr.decode(),
155
        )
156
    metadata = json.loads(embed_result.stdout)
×
157
    embed_config = EmbedConfig.from_json_dict(metadata.get("EmbedConfig", {}))
×
158
    return _ResolveStdlibEmbedConfigResult(
×
159
        embed_config=embed_config,
160
        stderr=None,
161
    )
162

163

164
@dataclass(frozen=True)
7✔
165
class BuildGoPackageRequestForStdlibRequest:
7✔
166
    import_path: str
7✔
167
    build_opts: GoBuildOptions
7✔
168

169

170
@rule
7✔
171
async def setup_build_go_package_target_request_for_stdlib(
7✔
172
    request: BuildGoPackageRequestForStdlibRequest,
173
    goroot: GoRoot,
174
) -> FallibleBuildGoPackageRequest:
175
    stdlib_packages = await analyze_go_stdlib_packages(
7✔
176
        GoStdLibPackagesRequest(
177
            with_race_detector=request.build_opts.with_race_detector,
178
            cgo_enabled=request.build_opts.cgo_enabled,
179
        )
180
    )
181

182
    pkg_info = stdlib_packages[request.import_path]
7✔
183

184
    direct_dependency_import_pats = set(pkg_info.imports)
7✔
185
    if pkg_info.cgo_files:
7✔
186
        if request.import_path != "runtime/cgo":
3✔
187
            direct_dependency_import_pats.add("runtime/cgo")
2✔
188
        if pkg_info.import_path not in (
3✔
189
            "runtime/cgo",
190
            "runtime/race",
191
            "runtime/msan",
192
            "runtime/asan",
193
        ):
194
            direct_dependency_import_pats.add("syscall")
1✔
195

196
    direct_dependencies_wrapped = await concurrently(
7✔
197
        # TODO need to move setup_build_go_package_target_request_for_stdlib around above this rule
198
        setup_build_go_package_target_request_for_stdlib(
199
            BuildGoPackageRequestForStdlibRequest(
200
                import_path=dep_import_path,
201
                build_opts=request.build_opts,
202
            ),
203
            **implicitly(),
204
        )
205
        for dep_import_path in sorted(direct_dependency_import_pats)
206
        if dep_import_path not in {"builtin", "C", "unsafe"}
207
    )
208

209
    direct_dependencies: list[BuildGoPackageRequest] = []
7✔
210
    for dep in direct_dependencies_wrapped:
7✔
211
        assert dep.request is not None
7✔
212
        direct_dependencies.append(dep.request)
7✔
213
    direct_dependencies.sort(key=lambda p: p.import_path)
7✔
214

215
    with_coverage = _is_coverage_enabled_for_stdlib_package(request.import_path, request.build_opts)
7✔
216

217
    embed_config: EmbedConfig | None = None
7✔
218
    if pkg_info.embed_patterns and pkg_info.embed_files:
7✔
219
        embed_config_result = await resolve_go_stdlib_embed_config(
×
220
            _ResolveStdlibEmbedConfigRequest(pkg_info)
221
        )
222
        if not embed_config_result.embed_config:
×
223
            assert embed_config_result.stderr is not None
×
224
            return FallibleBuildGoPackageRequest(
×
225
                request=None,
226
                import_path=request.import_path,
227
                exit_code=1,
228
                stderr=embed_config_result.stderr,
229
            )
230
        embed_config = embed_config_result.embed_config
×
231

232
    return FallibleBuildGoPackageRequest(
7✔
233
        request=BuildGoPackageRequest(
234
            import_path=pkg_info.import_path,
235
            pkg_name=pkg_info.name,
236
            digest=EMPTY_DIGEST,
237
            dir_path=pkg_info.pkg_source_path,
238
            build_opts=request.build_opts,
239
            go_files=pkg_info.go_files,
240
            s_files=pkg_info.s_files,
241
            direct_dependencies=tuple(direct_dependencies),
242
            import_map=pkg_info.import_map,
243
            minimum_go_version=goroot.version,
244
            cgo_files=pkg_info.cgo_files,
245
            c_files=pkg_info.c_files,
246
            header_files=pkg_info.h_files,
247
            cxx_files=pkg_info.cxx_files,
248
            objc_files=pkg_info.m_files,
249
            fortran_files=pkg_info.f_files,
250
            prebuilt_object_files=pkg_info.syso_files,
251
            cgo_flags=pkg_info.cgo_flags,
252
            with_coverage=with_coverage,
253
            is_stdlib=True,
254
            embed_config=embed_config,
255
        ),
256
        import_path=request.import_path,
257
    )
258

259

260
@union(in_scope_types=[EnvironmentName])
7✔
261
@dataclass(frozen=True)
7✔
262
class GoCodegenBuildRequest:
7✔
263
    """The plugin hook to build/compile Go code.
264

265
    Note that you should still use the normal `GenerateSourcesRequest` plugin hook from
266
    `pants.engine.target` too, which is necessary for integrations like the `export-codegen` goal.
267
    However, that is only helpful to generate the raw `.go` files; you also need to use this
268
    plugin hook so that Pants knows how to compile those generated `.go` files.
269

270
    Subclass this and set the class property `generate_from`. Define a rule that goes from your
271
    subclass to `BuildGoPackageRequest` - the request must result in valid compilation, which you
272
    should test for by using `rule_runner.request(BuiltGoPackage, BuildGoPackageRequest)` in your
273
    tests. For example, make sure to set up any third-party packages needed by the generated code.
274
    Finally, register `UnionRule(GoCodegenBuildRequest, MySubclass)`.
275
    """
276

277
    target: Target
7✔
278
    build_opts: GoBuildOptions
7✔
279

280
    generate_from: ClassVar[type[SourcesField]]
7✔
281

282

283
@rule(polymorphic=True)
7✔
284
async def get_build_request_for_generated_code(
7✔
285
    req: GoCodegenBuildRequest, env_name: EnvironmentName
286
) -> FallibleBuildGoPackageRequest:
287
    raise NotImplementedError()
×
288

289

290
def maybe_get_codegen_request_type(
7✔
291
    tgt: Target, build_opts: GoBuildOptions, union_membership: UnionMembership
292
) -> GoCodegenBuildRequest | None:
293
    if not tgt.has_field(SourcesField):
7✔
294
        return None
4✔
295
    generate_request_types = union_membership.get(GoCodegenBuildRequest)
7✔
296
    sources_field = tgt[SourcesField]
7✔
297
    relevant_requests = [
7✔
298
        req for req in generate_request_types if isinstance(sources_field, req.generate_from)
299
    ]
300
    if len(relevant_requests) > 1:
7✔
301
        generate_from_sources = relevant_requests[0].generate_from.__name__
×
302
        raise AmbiguousCodegenImplementationsException(
×
303
            f"Multiple registered code generators from {GoCodegenBuildRequest.__name__} can "
304
            f"generate from {generate_from_sources}. It is ambiguous which implementation to "
305
            f"use.\n\n"
306
            f"Possible implementations:\n\n"
307
            f"{bullet_list(sorted(generator.__name__ for generator in relevant_requests))}"
308
        )
309
    return relevant_requests[0](tgt, build_opts) if relevant_requests else None
7✔
310

311

312
# NB: We must have a description for the streaming of this rule to work properly
313
# (triggered by `FallibleBuildGoPackageRequest` subclassing `EngineAwareReturnType`).
314
@rule(desc="Set up Go compilation request", level=LogLevel.DEBUG)
7✔
315
async def setup_build_go_package_target_request(
7✔
316
    request: BuildGoPackageTargetRequest,
317
    union_membership: UnionMembership,
318
    goroot: GoRoot,
319
) -> FallibleBuildGoPackageRequest:
320
    wrapped_target = await resolve_target(
7✔
321
        WrappedTargetRequest(request.address, description_of_origin="<build_pkg_target.py>"),
322
        **implicitly(),
323
    )
324
    target = wrapped_target.target
7✔
325

326
    codegen_request = maybe_get_codegen_request_type(target, request.build_opts, union_membership)
7✔
327
    if codegen_request:
7✔
328
        # TODO need to move setup_build_go_package_target_request_for_stdlib around to resolve circular dependency
329
        codegen_result = await get_build_request_for_generated_code(
2✔
330
            **implicitly({codegen_request: GoCodegenBuildRequest})
331
        )
332
        return codegen_result
2✔
333

334
    embed_config: EmbedConfig | None = None
7✔
335
    import_map: FrozenDict[str, str] = FrozenDict({})
7✔
336

337
    if target.has_field(GoPackageSourcesField):
7✔
338
        _maybe_first_party_pkg_analysis, _maybe_first_party_pkg_digest = await concurrently(
7✔
339
            analyze_first_party_package(
340
                FirstPartyPkgAnalysisRequest(target.address, build_opts=request.build_opts),
341
                **implicitly(),
342
            ),
343
            setup_first_party_pkg_digest(
344
                FirstPartyPkgDigestRequest(target.address, build_opts=request.build_opts)
345
            ),
346
        )
347
        if _maybe_first_party_pkg_analysis.analysis is None:
7✔
348
            return FallibleBuildGoPackageRequest(
3✔
349
                None,
350
                _maybe_first_party_pkg_analysis.import_path,
351
                exit_code=_maybe_first_party_pkg_analysis.exit_code,
352
                stderr=_maybe_first_party_pkg_analysis.stderr,
353
            )
354
        if _maybe_first_party_pkg_digest.pkg_digest is None:
7✔
355
            return FallibleBuildGoPackageRequest(
×
356
                None,
357
                _maybe_first_party_pkg_analysis.import_path,
358
                exit_code=_maybe_first_party_pkg_digest.exit_code,
359
                stderr=_maybe_first_party_pkg_digest.stderr,
360
            )
361
        _first_party_pkg_analysis = _maybe_first_party_pkg_analysis.analysis
7✔
362
        _first_party_pkg_digest = _maybe_first_party_pkg_digest.pkg_digest
7✔
363

364
        digest = _first_party_pkg_digest.digest
7✔
365
        pkg_name = _first_party_pkg_analysis.name
7✔
366
        import_path = _first_party_pkg_analysis.import_path
7✔
367
        base_import_path = import_path
7✔
368
        imports = set(_first_party_pkg_analysis.imports)
7✔
369
        if request.for_tests:
7✔
370
            imports.update(_first_party_pkg_analysis.test_imports)
5✔
371
        dir_path = _first_party_pkg_analysis.dir_path
7✔
372
        minimum_go_version = _first_party_pkg_analysis.minimum_go_version
7✔
373

374
        go_file_names = _first_party_pkg_analysis.go_files
7✔
375
        embed_config = _first_party_pkg_digest.embed_config
7✔
376
        if request.for_tests:
7✔
377
            # TODO: Build the test sources separately and link the two object files into the
378
            #  package archive?
379
            # TODO: The `go` tool changes the displayed import path for the package when it has
380
            #  test files. Do we need to do something similar?
381
            go_file_names += _first_party_pkg_analysis.test_go_files
5✔
382
            if _first_party_pkg_digest.test_embed_config:
5✔
383
                if embed_config:
1✔
384
                    embed_config = embed_config.merge(_first_party_pkg_digest.test_embed_config)
×
385
                else:
386
                    embed_config = _first_party_pkg_digest.test_embed_config
1✔
387
        s_files = _first_party_pkg_analysis.s_files
7✔
388
        cgo_files = _first_party_pkg_analysis.cgo_files
7✔
389
        cgo_flags = _first_party_pkg_analysis.cgo_flags
7✔
390
        c_files = _first_party_pkg_analysis.c_files
7✔
391
        h_files = _first_party_pkg_analysis.h_files
7✔
392
        cxx_files = _first_party_pkg_analysis.cxx_files
7✔
393
        objc_files = _first_party_pkg_analysis.m_files
7✔
394
        fortran_files = _first_party_pkg_analysis.f_files
7✔
395
        prebuilt_object_files = _first_party_pkg_analysis.syso_files
7✔
396

397
        # If the xtest package was requested, then replace analysis with the xtest values.
398
        if request.for_xtests:
7✔
399
            import_path = f"{import_path}_test"
2✔
400
            pkg_name = f"{pkg_name}_test"
2✔
401
            imports = set(_first_party_pkg_analysis.xtest_imports)
2✔
402
            go_file_names = _first_party_pkg_analysis.xtest_go_files
2✔
403
            s_files = ()
2✔
404
            cgo_files = ()
2✔
405
            cgo_flags = CGoCompilerFlags(
2✔
406
                cflags=(),
407
                cppflags=(),
408
                cxxflags=(),
409
                fflags=(),
410
                ldflags=(),
411
                pkg_config=(),
412
            )
413
            c_files = ()
2✔
414
            h_files = ()
2✔
415
            cxx_files = ()
2✔
416
            objc_files = ()
2✔
417
            fortran_files = ()
2✔
418
            embed_config = _first_party_pkg_digest.xtest_embed_config
2✔
419

420
    elif target.has_field(GoThirdPartyPackageDependenciesField):
4✔
421
        import_path = target[GoImportPathField].value
4✔
422
        base_import_path = import_path
4✔
423

424
        _go_mod_address = target.address.maybe_convert_to_target_generator()
4✔
425
        _go_mod_info = await determine_go_mod_info(GoModInfoRequest(_go_mod_address))
4✔
426
        _third_party_pkg_info = await extract_package_info(
4✔
427
            ThirdPartyPkgAnalysisRequest(
428
                import_path,
429
                _go_mod_address,
430
                _go_mod_info.digest,
431
                _go_mod_info.mod_path,
432
                build_opts=request.build_opts,
433
            )
434
        )
435

436
        # We error if trying to _build_ a package with issues (vs. only generating the target and
437
        # using in project introspection).
438
        if _third_party_pkg_info.error:
4✔
439
            raise _third_party_pkg_info.error
×
440

441
        imports = set(_third_party_pkg_info.imports)
4✔
442
        dir_path = _third_party_pkg_info.dir_path
4✔
443
        pkg_name = _third_party_pkg_info.name
4✔
444
        digest = _third_party_pkg_info.digest
4✔
445
        minimum_go_version = _third_party_pkg_info.minimum_go_version
4✔
446
        go_file_names = _third_party_pkg_info.go_files
4✔
447
        s_files = _third_party_pkg_info.s_files
4✔
448
        embed_config = _third_party_pkg_info.embed_config
4✔
449
        cgo_files = _third_party_pkg_info.cgo_files
4✔
450
        cgo_flags = _third_party_pkg_info.cgo_flags
4✔
451
        c_files = _third_party_pkg_info.c_files
4✔
452
        h_files = _third_party_pkg_info.h_files
4✔
453
        cxx_files = _third_party_pkg_info.cxx_files
4✔
454
        objc_files = _third_party_pkg_info.m_files
4✔
455
        fortran_files = _third_party_pkg_info.f_files
4✔
456
        prebuilt_object_files = _third_party_pkg_info.syso_files
4✔
457

458
    else:
459
        raise AssertionError(
×
460
            f"Unknown how to build `{target.alias}` target at address {request.address} with Go. "
461
            "Please open a bug at https://github.com/pantsbuild/pants/issues/new/choose with this "
462
            "message!"
463
        )
464

465
    assert import_path not in (
7✔
466
        "C",
467
        "builtin",
468
        "unsafe",
469
    ), f"Internal error: Attempting to build marker/intrinsic package `{import_path}`."
470

471
    pkg_specific_compiler_flags: tuple[str, ...] = ()
7✔
472
    if target.has_field(GoCompilerFlagsField):
7✔
473
        compiler_flags_field = target.get(GoCompilerFlagsField)
7✔
474
        if compiler_flags_field and compiler_flags_field.value:
7✔
UNCOV
475
            pkg_specific_compiler_flags = compiler_flags_field.value
1✔
476

477
    pkg_specific_assembler_flags: tuple[str, ...] = ()
7✔
478
    if target.has_field(GoAssemblerFlagsField):
7✔
479
        assembler_flags_field = target.get(GoAssemblerFlagsField)
7✔
480
        if assembler_flags_field and assembler_flags_field.value:
7✔
UNCOV
481
            pkg_specific_assembler_flags = assembler_flags_field.value
1✔
482

483
    # Add implicit dependencies for Cgo generated code.
484
    # Note: This rule does not apply to standard library so we do not need to be concerned with excluding these
485
    # dependencies if we were actually building one of these packages. See the counterpart logic below in the
486
    # other rule.
487
    extra_stdlib_dependencies = set(request.extra_stdlib_dependencies)
7✔
488
    if cgo_files:
7✔
489
        extra_stdlib_dependencies.update(["runtime/cgo", "syscall"])
2✔
490

491
    direct_dependencies = await resolve_targets(
7✔
492
        **implicitly(DependenciesRequest(target[Dependencies]))
493
    )
494

495
    first_party_dep_import_path_targets = []
7✔
496
    third_party_dep_import_path_targets = []
7✔
497
    codegen_dep_import_path_targets = []
7✔
498
    for dep in direct_dependencies:
7✔
499
        if dep.has_field(GoPackageSourcesField):
5✔
500
            first_party_dep_import_path_targets.append(dep)
3✔
501
        elif dep.has_field(GoImportPathField):
5✔
502
            third_party_dep_import_path_targets.append(dep)
4✔
503
        elif bool(maybe_get_codegen_request_type(dep, request.build_opts, union_membership)):
5✔
504
            codegen_dep_import_path_targets.append(dep)
2✔
505

506
    first_party_dep_import_path_results = await concurrently(
7✔
507
        compute_first_party_package_import_path(FirstPartyPkgImportPathRequest(tgt.address))
508
        for tgt in first_party_dep_import_path_targets
509
    )
510
    first_party_dep_import_paths = {
7✔
511
        result.import_path: tgt.address
512
        for tgt, result in zip(
513
            first_party_dep_import_path_targets, first_party_dep_import_path_results
514
        )
515
    }
516

517
    remaining_imports_set = {*imports, *extra_stdlib_dependencies}
7✔
518
    pkg_dependency_addresses_set: set[Address] = set()
7✔
519

520
    pkg_dependency_addresses_set.update(
7✔
521
        address
522
        for dep_import_path, address in first_party_dep_import_paths.items()
523
        if dep_import_path in remaining_imports_set
524
    )
525
    remaining_imports_set.difference_update(
7✔
526
        dep_import_path
527
        for dep_import_path in first_party_dep_import_paths.keys()
528
        if dep_import_path in remaining_imports_set
529
    )
530

531
    pkg_dependency_addresses_set.update(
7✔
532
        dep_tgt.address
533
        for dep_tgt in third_party_dep_import_path_targets
534
        if dep_tgt[GoImportPathField].value in remaining_imports_set
535
    )
536
    remaining_imports_set.difference_update(
7✔
537
        dep_tgt[GoImportPathField].value
538
        for dep_tgt in third_party_dep_import_path_targets
539
        if dep_tgt[GoImportPathField].value in remaining_imports_set
540
    )
541

542
    if codegen_dep_import_path_targets:
7✔
543
        go_mod_addr = await find_owning_go_mod(OwningGoModRequest(request.address), **implicitly())
2✔
544
        import_paths_mapping = await map_import_paths_to_packages(
2✔
545
            GoImportPathMappingRequest(go_mod_addr.address), **implicitly()
546
        )
547
        for dep_tgt in codegen_dep_import_path_targets:
2✔
548
            codegen_dep_import_path = import_paths_mapping.address_to_import_path.get(
2✔
549
                dep_tgt.address
550
            )
551
            if codegen_dep_import_path is None:
2✔
552
                # TODO: Emit warning?
553
                continue
×
554
            if codegen_dep_import_path in remaining_imports_set:
2✔
555
                pkg_dependency_addresses_set.add(dep_tgt.address)
2✔
556
                remaining_imports_set.difference_update([codegen_dep_import_path])
2✔
557

558
    stdlib_packages = await analyze_go_stdlib_packages(
7✔
559
        GoStdLibPackagesRequest(
560
            with_race_detector=request.build_opts.with_race_detector,
561
            cgo_enabled=request.build_opts.cgo_enabled,
562
        )
563
    )
564

565
    pkg_dependency_addresses = sorted(pkg_dependency_addresses_set)
7✔
566
    maybe_pkg_direct_dependencies = await concurrently(
7✔
567
        setup_build_go_package_target_request(
568
            BuildGoPackageTargetRequest(address, build_opts=request.build_opts), **implicitly()
569
        )
570
        for address in pkg_dependency_addresses
571
    )
572
    pkg_stdlib_dependencies = await concurrently(
7✔
573
        setup_build_go_package_target_request_for_stdlib(
574
            BuildGoPackageRequestForStdlibRequest(
575
                import_path=remaining_import,
576
                build_opts=request.build_opts,
577
            ),
578
            **implicitly(),
579
        )
580
        for remaining_import in remaining_imports_set
581
        if remaining_import not in {"builtin", "C", "unsafe"}
582
        and remaining_import in stdlib_packages
583
    )
584

585
    pkg_direct_dependencies = []
7✔
586
    for maybe_pkg_dep in maybe_pkg_direct_dependencies:
7✔
587
        if maybe_pkg_dep.request is None:
5✔
588
            return dataclasses.replace(
2✔
589
                maybe_pkg_dep,
590
                dependency_failed=True,
591
            )
592
        pkg_direct_dependencies.append(maybe_pkg_dep.request)
4✔
593

594
    for maybe_pkg_dep in pkg_stdlib_dependencies:
7✔
595
        assert maybe_pkg_dep.request
7✔
596
        pkg_direct_dependencies.append(maybe_pkg_dep.request)
7✔
597

598
    # Allow xtest packages to depend on the base package (with tests).
599
    if request.for_xtests and any(
7✔
600
        dep_import_path == base_import_path for dep_import_path in imports
601
    ):
602
        # TODO need to move setup_build_go_package_target_request_for_stdlib around to resolve circular dependency
603
        maybe_base_pkg_dep = await setup_build_go_package_target_request(
1✔
604
            BuildGoPackageTargetRequest(
605
                request.address,
606
                for_tests=True,
607
                with_coverage=request.with_coverage,
608
                build_opts=request.build_opts,
609
            ),
610
            **implicitly(),
611
        )
612
        if maybe_base_pkg_dep.request is None:
1✔
613
            return dataclasses.replace(
×
614
                maybe_base_pkg_dep,
615
                dependency_failed=True,
616
            )
617
        pkg_direct_dependencies.append(maybe_base_pkg_dep.request)
1✔
618

619
    with_coverage = request.with_coverage
7✔
620
    coverage_config = request.build_opts.coverage_config
7✔
621
    if coverage_config:
7✔
622
        for pattern in coverage_config.import_path_include_patterns:
2✔
623
            with_coverage = with_coverage or match_simple_pattern(pattern)(import_path)
1✔
624

625
    result = BuildGoPackageRequest(
7✔
626
        digest=digest,
627
        import_path="main" if request.is_main else import_path,
628
        pkg_name=pkg_name,
629
        dir_path=dir_path,
630
        build_opts=request.build_opts,
631
        go_files=go_file_names,
632
        s_files=s_files,
633
        cgo_files=cgo_files,
634
        cgo_flags=cgo_flags,
635
        c_files=c_files,
636
        header_files=h_files,
637
        cxx_files=cxx_files,
638
        objc_files=objc_files,
639
        fortran_files=fortran_files,
640
        prebuilt_object_files=prebuilt_object_files,
641
        minimum_go_version=minimum_go_version,
642
        direct_dependencies=tuple(sorted(pkg_direct_dependencies, key=lambda p: p.import_path)),
643
        import_map=import_map,
644
        for_tests=request.for_tests,
645
        embed_config=embed_config,
646
        with_coverage=with_coverage,
647
        pkg_specific_compiler_flags=tuple(pkg_specific_compiler_flags),
648
        pkg_specific_assembler_flags=tuple(pkg_specific_assembler_flags),
649
    )
650
    return FallibleBuildGoPackageRequest(result, import_path)
7✔
651

652

653
@rule
7✔
654
async def required_build_go_package_request(
7✔
655
    fallible_request: FallibleBuildGoPackageRequest,
656
) -> BuildGoPackageRequest:
657
    if fallible_request.request is not None:
6✔
658
        return fallible_request.request
6✔
659
    raise Exception(
×
660
        f"Failed to determine metadata to compile {fallible_request.import_path}:\n"
661
        f"{fallible_request.stderr}"
662
    )
663

664

665
# Return True if coverage should be enabled for a standard library package.
666
# See https://github.com/golang/go/blob/1e9ff255a130200fcc4ec5e911d28181fce947d5/src/cmd/go/internal/test/test.go#L839-L853
667
# for the exceptions.
668
def _is_coverage_enabled_for_stdlib_package(import_path: str, build_opts: GoBuildOptions) -> bool:
7✔
669
    coverage_config = build_opts.coverage_config
7✔
670
    if not coverage_config:
7✔
671
        return False
7✔
672

673
    # Silently ignore attempts to run coverage on sync/atomic when using atomic coverage mode.
674
    # Atomic coverage mode uses sync/atomic, so we can't also do coverage on it.
675
    if coverage_config.cover_mode == GoCoverMode.ATOMIC and import_path == "sync/atomic":
2✔
676
        return False
×
677

678
    # If using the race detector, silently ignore attempts to run coverage on the runtime packages.
679
    # It will cause the race detector to be invoked before it has been initialized.
680
    if build_opts.with_race_detector and (
2✔
681
        import_path == "runtime" or import_path.startswith("runtime/internal")
682
    ):
683
        return False
×
684

685
    for pattern in coverage_config.import_path_include_patterns:
2✔
686
        if match_simple_pattern(pattern)(import_path):
1✔
687
            return True
×
688

689
    return False
2✔
690

691

692
def rules():
7✔
693
    return (
7✔
694
        *collect_rules(),
695
        *build_opts.rules(),
696
    )
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