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

pantsbuild / pants / 20332790708

18 Dec 2025 09:48AM UTC coverage: 64.992% (-15.3%) from 80.295%
20332790708

Pull #22949

github

web-flow
Merge f730a56cd into 407284c67
Pull Request #22949: Add experimental uv resolver for Python lockfiles

54 of 97 new or added lines in 5 files covered. (55.67%)

8270 existing lines in 295 files now uncovered.

48990 of 75379 relevant lines covered (64.99%)

1.81 hits per line

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

27.11
/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
4✔
5

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

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

83

84
@dataclass(frozen=True)
4✔
85
class BuildGoPackageTargetRequest(EngineAwareParameter):
4✔
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
4✔
90
    build_opts: GoBuildOptions
4✔
91
    is_main: bool = False
4✔
92
    for_tests: bool = False
4✔
93
    for_xtests: bool = False
4✔
94

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

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

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

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

111

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

116

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

122

123
@rule
4✔
124
async def resolve_go_stdlib_embed_config(
4✔
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)
4✔
165
class BuildGoPackageRequestForStdlibRequest:
4✔
166
    import_path: str
4✔
167
    build_opts: GoBuildOptions
4✔
168

169

170
@rule
4✔
171
async def setup_build_go_package_target_request_for_stdlib(
4✔
172
    request: BuildGoPackageRequestForStdlibRequest,
173
    goroot: GoRoot,
174
) -> FallibleBuildGoPackageRequest:
175
    stdlib_packages = await analyze_go_stdlib_packages(
×
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]
×
183

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

196
    direct_dependencies_wrapped = await concurrently(
×
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] = []
×
210
    for dep in direct_dependencies_wrapped:
×
211
        assert dep.request is not None
×
212
        direct_dependencies.append(dep.request)
×
213
    direct_dependencies.sort(key=lambda p: p.import_path)
×
214

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

217
    embed_config: EmbedConfig | None = None
×
218
    if pkg_info.embed_patterns and pkg_info.embed_files:
×
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(
×
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])
4✔
261
@dataclass(frozen=True)
4✔
262
class GoCodegenBuildRequest:
4✔
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
4✔
278
    build_opts: GoBuildOptions
4✔
279

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

282

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

289

290
def maybe_get_codegen_request_type(
4✔
291
    tgt: Target, build_opts: GoBuildOptions, union_membership: UnionMembership
292
) -> GoCodegenBuildRequest | None:
293
    if not tgt.has_field(SourcesField):
×
294
        return None
×
295
    generate_request_types = union_membership.get(GoCodegenBuildRequest)
×
296
    sources_field = tgt[SourcesField]
×
297
    relevant_requests = [
×
298
        req for req in generate_request_types if isinstance(sources_field, req.generate_from)
299
    ]
300
    if len(relevant_requests) > 1:
×
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
×
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)
4✔
315
async def setup_build_go_package_target_request(
4✔
316
    request: BuildGoPackageTargetRequest,
317
    union_membership: UnionMembership,
318
    goroot: GoRoot,
319
) -> FallibleBuildGoPackageRequest:
320
    wrapped_target = await resolve_target(
×
321
        WrappedTargetRequest(request.address, description_of_origin="<build_pkg_target.py>"),
322
        **implicitly(),
323
    )
324
    target = wrapped_target.target
×
325

326
    codegen_request = maybe_get_codegen_request_type(target, request.build_opts, union_membership)
×
327
    if codegen_request:
×
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(
×
330
            **implicitly({codegen_request: GoCodegenBuildRequest})
331
        )
332
        return codegen_result
×
333

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

337
    if target.has_field(GoPackageSourcesField):
×
338
        _maybe_first_party_pkg_analysis, _maybe_first_party_pkg_digest = await concurrently(
×
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:
×
348
            return FallibleBuildGoPackageRequest(
×
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:
×
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
×
362
        _first_party_pkg_digest = _maybe_first_party_pkg_digest.pkg_digest
×
363

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

374
        go_file_names = _first_party_pkg_analysis.go_files
×
375
        embed_config = _first_party_pkg_digest.embed_config
×
376
        if request.for_tests:
×
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
×
382
            if _first_party_pkg_digest.test_embed_config:
×
383
                if embed_config:
×
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
×
387
        s_files = _first_party_pkg_analysis.s_files
×
388
        cgo_files = _first_party_pkg_analysis.cgo_files
×
389
        cgo_flags = _first_party_pkg_analysis.cgo_flags
×
390
        c_files = _first_party_pkg_analysis.c_files
×
391
        h_files = _first_party_pkg_analysis.h_files
×
392
        cxx_files = _first_party_pkg_analysis.cxx_files
×
393
        objc_files = _first_party_pkg_analysis.m_files
×
394
        fortran_files = _first_party_pkg_analysis.f_files
×
395
        prebuilt_object_files = _first_party_pkg_analysis.syso_files
×
396

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

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

424
        _go_mod_address = target.address.maybe_convert_to_target_generator()
×
425
        _go_mod_info = await determine_go_mod_info(GoModInfoRequest(_go_mod_address))
×
426
        _third_party_pkg_info = await extract_package_info(
×
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:
×
439
            raise _third_party_pkg_info.error
×
440

441
        imports = set(_third_party_pkg_info.imports)
×
442
        dir_path = _third_party_pkg_info.dir_path
×
443
        pkg_name = _third_party_pkg_info.name
×
444
        digest = _third_party_pkg_info.digest
×
445
        minimum_go_version = _third_party_pkg_info.minimum_go_version
×
446
        go_file_names = _third_party_pkg_info.go_files
×
447
        s_files = _third_party_pkg_info.s_files
×
448
        embed_config = _third_party_pkg_info.embed_config
×
449
        cgo_files = _third_party_pkg_info.cgo_files
×
450
        cgo_flags = _third_party_pkg_info.cgo_flags
×
451
        c_files = _third_party_pkg_info.c_files
×
452
        h_files = _third_party_pkg_info.h_files
×
453
        cxx_files = _third_party_pkg_info.cxx_files
×
454
        objc_files = _third_party_pkg_info.m_files
×
455
        fortran_files = _third_party_pkg_info.f_files
×
456
        prebuilt_object_files = _third_party_pkg_info.syso_files
×
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 (
×
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, ...] = ()
×
472
    if target.has_field(GoCompilerFlagsField):
×
473
        compiler_flags_field = target.get(GoCompilerFlagsField)
×
474
        if compiler_flags_field and compiler_flags_field.value:
×
475
            pkg_specific_compiler_flags = compiler_flags_field.value
×
476

477
    pkg_specific_assembler_flags: tuple[str, ...] = ()
×
478
    if target.has_field(GoAssemblerFlagsField):
×
479
        assembler_flags_field = target.get(GoAssemblerFlagsField)
×
480
        if assembler_flags_field and assembler_flags_field.value:
×
481
            pkg_specific_assembler_flags = assembler_flags_field.value
×
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)
×
488
    if cgo_files:
×
489
        extra_stdlib_dependencies.update(["runtime/cgo", "syscall"])
×
490

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

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

506
    first_party_dep_import_path_results = await concurrently(
×
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 = {
×
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}
×
518
    pkg_dependency_addresses_set: set[Address] = set()
×
519

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

558
    stdlib_packages = await analyze_go_stdlib_packages(
×
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)
×
566
    maybe_pkg_direct_dependencies = await concurrently(
×
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(
×
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 = []
×
586
    for maybe_pkg_dep in maybe_pkg_direct_dependencies:
×
587
        if maybe_pkg_dep.request is None:
×
588
            return dataclasses.replace(
×
589
                maybe_pkg_dep,
590
                dependency_failed=True,
591
            )
592
        pkg_direct_dependencies.append(maybe_pkg_dep.request)
×
593

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

598
    # Allow xtest packages to depend on the base package (with tests).
599
    if request.for_xtests and any(
×
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(
×
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:
×
613
            return dataclasses.replace(
×
614
                maybe_base_pkg_dep,
615
                dependency_failed=True,
616
            )
617
        pkg_direct_dependencies.append(maybe_base_pkg_dep.request)
×
618

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

625
    result = BuildGoPackageRequest(
×
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)
×
651

652

653
@rule
4✔
654
async def required_build_go_package_request(
4✔
655
    fallible_request: FallibleBuildGoPackageRequest,
656
) -> BuildGoPackageRequest:
657
    if fallible_request.request is not None:
×
658
        return fallible_request.request
×
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:
4✔
669
    coverage_config = build_opts.coverage_config
×
670
    if not coverage_config:
×
671
        return False
×
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":
×
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 (
×
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:
×
686
        if match_simple_pattern(pattern)(import_path):
×
687
            return True
×
688

689
    return False
×
690

691

692
def rules():
4✔
693
    return (
4✔
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