• 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/cgo_pkgconfig.py
1
# Copyright 2022 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
from dataclasses import dataclass
×
6

UNCOV
7
from pants.backend.go.util_rules import cgo_binaries
×
UNCOV
8
from pants.backend.go.util_rules.cgo_binaries import CGoBinaryPathRequest, find_cgo_binary_path
×
UNCOV
9
from pants.backend.go.util_rules.cgo_security import (
×
10
    check_compiler_flags,
11
    check_linker_flags,
12
    safe_arg,
13
)
UNCOV
14
from pants.core.util_rules.system_binaries import BinaryPathTest
×
UNCOV
15
from pants.engine.internals.selectors import concurrently
×
UNCOV
16
from pants.engine.process import Process, fallible_to_exec_result_or_raise
×
UNCOV
17
from pants.engine.rules import collect_rules, implicitly, rule
×
18

19
# Adapted from the Go toolchain
20
#
21
# Original copyright:
22
#   // Copyright 2011 The Go Authors. All rights reserved.
23
#   // Use of this source code is governed by a BSD-style
24
#   // license that can be found in the LICENSE file.
25

26

UNCOV
27
@dataclass(frozen=True)
×
UNCOV
28
class CGoPkgConfigFlagsRequest:
×
29
    """Request resolution of pkg-config arguments into CFLAGS and LDFLAGS."""
30

UNCOV
31
    pkg_config_args: tuple[str, ...]
×
32

33

UNCOV
34
@dataclass(frozen=True)
×
UNCOV
35
class CGoPkgConfigFlagsResult:
×
UNCOV
36
    cflags: tuple[str, ...]
×
UNCOV
37
    ldflags: tuple[str, ...]
×
38

39

40
# _split_pkg_config_output parses the pkg-config output into a tuple of flags. This implements the algorithm from
41
# https://github.com/pkgconf/pkgconf/blob/master/libpkgconf/argvsplit.c
42
# See https://github.com/golang/go/blob/54182ff54a687272dd7632c3a963e036ce03cb7c/src/cmd/go/internal/work/exec.go#L1414-L1473
UNCOV
43
def _split_pkg_config_output(content: bytes) -> tuple[str, ...]:
×
UNCOV
44
    if not content:
×
UNCOV
45
        return ()
×
46

UNCOV
47
    flags: list[str] = []
×
UNCOV
48
    flag = bytearray()
×
UNCOV
49
    escaped = False
×
UNCOV
50
    quote = 0
×
UNCOV
51
    for c in content:
×
UNCOV
52
        if escaped:
×
UNCOV
53
            if quote != 0:
×
UNCOV
54
                if c not in b'$`"\\':
×
UNCOV
55
                    flag.extend(b"\\")
×
UNCOV
56
                flag.append(c)
×
57
            else:
UNCOV
58
                flag.append(c)
×
UNCOV
59
            escaped = False
×
UNCOV
60
        elif quote != 0:
×
UNCOV
61
            if c == quote:
×
UNCOV
62
                quote = 0
×
63
            else:
UNCOV
64
                if c == ord("\\"):
×
UNCOV
65
                    escaped = True
×
66
                else:
UNCOV
67
                    flag.append(c)
×
UNCOV
68
        elif c not in b" \t\n\v\f\r":
×
UNCOV
69
            if c == ord(b"\\"):
×
UNCOV
70
                escaped = True
×
UNCOV
71
            elif c in b"'\"":
×
UNCOV
72
                quote = c
×
73
            else:
UNCOV
74
                flag.append(c)
×
UNCOV
75
        elif len(flag) != 0:
×
UNCOV
76
            flags.append(flag.decode())
×
UNCOV
77
            flag = bytearray()
×
78

UNCOV
79
    if escaped:
×
80
        raise ValueError("broken character escaping in pkg-config output")
×
UNCOV
81
    if quote != 0:
×
82
        raise ValueError("unterminated quoted string in pkgconf output")
×
UNCOV
83
    elif len(flag) != 0:
×
UNCOV
84
        flags.append(flag.decode())
×
85

UNCOV
86
    return tuple(flags)
×
87

88

UNCOV
89
@rule
×
UNCOV
90
async def resolve_cgo_pkg_config_args(request: CGoPkgConfigFlagsRequest) -> CGoPkgConfigFlagsResult:
×
91
    if not request.pkg_config_args:
×
92
        return CGoPkgConfigFlagsResult(cflags=(), ldflags=())
×
93

94
    pkg_config_flags = []
×
95
    pkgs = []
×
96
    for arg in request.pkg_config_args:
×
97
        if arg == "--":
×
98
            # Skip the `--` separator as we will add our own later.
99
            pass
×
100
        elif arg.startswith("--"):
×
101
            pkg_config_flags.append(arg)
×
102
        else:
103
            pkgs.append(arg)
×
104

105
    for pkg in pkgs:
×
106
        if not safe_arg(pkg):
×
107
            raise ValueError(f"invalid pkg-config package name: {pkg}")
×
108

109
    pkg_config_path = await find_cgo_binary_path(
×
110
        CGoBinaryPathRequest(
111
            binary_name="pkg-config",
112
            binary_path_test=BinaryPathTest(["--version"]),
113
        ),
114
        **implicitly(),
115
    )
116

117
    cflags_result, ldflags_result = await concurrently(
×
118
        fallible_to_exec_result_or_raise(
119
            **implicitly(
120
                Process(
121
                    argv=[pkg_config_path.path, "--cflags", *pkg_config_flags, "--", *pkgs],
122
                    description=f"Run pkg-config for CFLAGS for packages: {pkgs}",
123
                )
124
            )
125
        ),
126
        fallible_to_exec_result_or_raise(
127
            **implicitly(
128
                Process(
129
                    argv=[pkg_config_path.path, "--libs", *pkg_config_flags, "--", *pkgs],
130
                    description=f"Run pkg-config for LDFLAGS for packages: {pkgs}",
131
                )
132
            )
133
        ),
134
    )
135

136
    cflags: tuple[str, ...] = ()
×
137
    if cflags_result.stdout:
×
138
        cflags = _split_pkg_config_output(cflags_result.stdout)
×
139
        check_compiler_flags(cflags, "pkg-config --cflags")
×
140

141
    ldflags: tuple[str, ...] = ()
×
142
    if ldflags_result.stdout:
×
143
        # NOTE: we don't attempt to parse quotes and unescapes here. pkg-config
144
        # is typically used within shell backticks, which treats quotes literally.
145
        ldflags = tuple(arg.decode() for arg in ldflags_result.stdout.split())
×
146
        check_linker_flags(ldflags, "pkg-config --libs")
×
147

148
    return CGoPkgConfigFlagsResult(cflags=cflags, ldflags=ldflags)
×
149

150

UNCOV
151
def rules():
×
UNCOV
152
    return (
×
153
        *collect_rules(),
154
        *cgo_binaries.rules(),
155
    )
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