Skip to content

Commit be7c93f

Browse files
committed
feat: ldconfig + ld.so.cache for find_library (maintainer request)
Maintainer: 'Keep the ldconfig binary and continue to generate the ldconfig.so.cache to allow find_library to continue to work'. python3/ parity, ported to python/: - libc-bin added to the image (provides /sbin/ldconfig) via PYTHON_PACKAGES in config.bzl - per-arch ld.so.cache generated by running ldconfig inside the image (python/ldconfig.bzl + ldconfig.sh, ported from python3; bazel run //python:update_ldconfig) and shipped at /etc/ld.so.cache - python/ldconfig.bzl: oci_load + do_load genrules + update_ldconfig + check_ldconfig_{arch}_test + test suite (caches for amd64, arm64, s390x, riscv64 generated and committed) - find_library CST test added to testdata/python3.yaml - check-ldconfig.yaml workflow extended to cover //python too - README find_library section rewritten (works now; regenerate via bazel run //python:update_ldconfig) Verified: find_library('c'/'ssl'/'z') resolve in the image; check_ldconfig tests 4/4 pass; CST (incl. find_library) + smoke test pass.
1 parent 0b6d1ee commit be7c93f

12 files changed

Lines changed: 178 additions & 7 deletions

File tree

.github/workflows/check-ldconfig.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,4 @@ jobs:
2323
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4
2424

2525
- name: Check ldconfig caches
26-
run: bazel test //python3:check_ldconfig_tests
26+
run: bazel test //python3:check_ldconfig_tests //python:check_ldconfig_tests

python/BUILD

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
load(":config.bzl", "PYTHON_ARCHITECTURES", "PYTHON_DISTROS", "PYTHON_MAJOR_VERSIONS")
1+
load("//private/util:tar.bzl", "tar")
2+
load(":config.bzl", "PYTHON_ARCHITECTURES", "PYTHON_DISTROS", "PYTHON_MAJOR_VERSIONS", "PYTHON_PACKAGES")
3+
load(":ldconfig.bzl", "python_ldconfig")
24
load(":python.bzl", "python_image", "python_image_index")
35

46
package(default_visibility = ["//visibility:public"])
@@ -19,7 +21,7 @@ sh_test(
1921
arch = arch,
2022
distro = distro,
2123
major_version = major_version,
22-
packages = [],
24+
packages = PYTHON_PACKAGES[distro],
2325
)
2426
for distro in PYTHON_DISTROS
2527
for major_version in PYTHON_MAJOR_VERSIONS
@@ -35,3 +37,25 @@ sh_test(
3537
for distro in PYTHON_DISTROS
3638
for major_version in PYTHON_MAJOR_VERSIONS
3739
]
40+
41+
# ld.so.cache per arch, generated by running ldconfig inside the image
42+
# (bazel run //python:update_ldconfig), shipped at /etc/ld.so.cache so
43+
# ctypes.util.find_library() works.
44+
[
45+
tar(
46+
name = "ldconfig_cache_{}".format(arch),
47+
srcs = ["ldconfig/ld.so.cache.{}".format(arch)],
48+
args = [
49+
"--format",
50+
"gnutar",
51+
],
52+
extension = "tar.gz",
53+
mtree = ["etc/ld.so.cache uid=0 gid=0 uname=root gname=root mode=0644 time=0 type=file content=$(location ldconfig/ld.so.cache.{})".format(arch)],
54+
)
55+
for arch in PYTHON_ARCHITECTURES["debian13"]["3.14"]
56+
]
57+
58+
python_ldconfig(
59+
architectures = PYTHON_ARCHITECTURES["debian13"]["3.14"],
60+
distro = "debian13",
61+
)

python/README.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,13 @@ RUN ln -s /usr/local/bin/python3.14 /python/bin/python3.14
4747

4848
### ctypes.util.find_library
4949

50-
Unlike the deb-based `python3` images, these images do **not** ship a pre-generated
51-
`ld.so.cache`, so `ctypes.util.find_library()` returns `None`. Use absolute library
52-
paths or an explicit `ctypes.CDLL("/lib/.../libX.so.Y")` instead.
50+
`ctypes.util.find_library()` works: the image ships `/sbin/ldconfig` (from `libc-bin`)
51+
and a pre-generated `ld.so.cache` at `/etc/ld.so.cache`. The cache is generated per
52+
arch by running ldconfig inside the image:
53+
54+
bazel run //python:update_ldconfig
55+
56+
and verified by `bazel test //python:check_ldconfig_tests` (also in CI).
5357

5458
## Provenance
5559

python/config.bzl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,9 @@ PYTHON_ARCHITECTURES = {
1313
"3.14": ["amd64", "arm64", "s390x", "riscv64"],
1414
},
1515
}
16+
17+
# deb packages added to the image: libc-bin provides /sbin/ldconfig so
18+
# ctypes.util.find_library() works via a generated ld.so.cache (python3/ parity).
19+
PYTHON_PACKAGES = {
20+
"debian13": ["libc-bin"],
21+
}

python/ldconfig.bzl

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
"Macros for generating ldconfig cache for python images"
2+
3+
load("@rules_oci//oci:defs.bzl", "oci_load")
4+
5+
def python_ldconfig(architectures, distro):
6+
"""Generates ldconfig cache targets for python images.
7+
8+
Args:
9+
architectures: list of architectures to support
10+
distro: the distribution name (e.g. debian13)
11+
"""
12+
13+
# 1. Create the oci_load targets (the scripts that load images into docker)
14+
for arch in architectures:
15+
oci_load(
16+
name = "load_python314_root_{}_{}".format(arch, distro),
17+
image = ":python314_root_{}_{}".format(arch, distro),
18+
repo_tags = ["bazel/python:python314_root_{}_{}".format(arch, distro)],
19+
)
20+
21+
# 2. Create the genrules that actually trigger the loading during 'bazel build'
22+
for arch in architectures:
23+
native.genrule(
24+
name = "do_load_{}".format(arch),
25+
outs = ["do_load_{}.done".format(arch)],
26+
cmd = "$(location :load_python314_root_{arch}_{distro}) && touch $@".format(
27+
arch = arch,
28+
distro = distro,
29+
),
30+
tags = [
31+
"local",
32+
"no-sandbox",
33+
],
34+
tools = [":load_python314_root_{}_{}".format(arch, distro)],
35+
)
36+
37+
# 3. Create the update_ldconfig binary
38+
native.sh_binary(
39+
name = "update_ldconfig",
40+
srcs = ["ldconfig/ldconfig.sh"],
41+
args = ["update"] + architectures,
42+
data = ["ldconfig/ldconfig.sh"] + [":do_load_{}".format(arch) for arch in architectures],
43+
tags = ["local"],
44+
)
45+
46+
# 4. Create the architecture-specific tests
47+
for arch in architectures:
48+
native.sh_test(
49+
name = "check_ldconfig_{}_test".format(arch),
50+
srcs = ["ldconfig/ldconfig.sh"],
51+
args = [
52+
"check",
53+
arch,
54+
],
55+
data = [
56+
"ldconfig/ld.so.cache.{}".format(arch),
57+
":do_load_{}".format(arch),
58+
],
59+
tags = [
60+
arch,
61+
"local",
62+
"manual",
63+
"external",
64+
"no-cache",
65+
],
66+
)
67+
68+
# 5. Create the test suite
69+
native.test_suite(
70+
name = "check_ldconfig_tests",
71+
tests = [
72+
":check_ldconfig_{}_test".format(arch)
73+
for arch in architectures
74+
],
75+
tags = ["manual"],
76+
)

python/ldconfig/ld.so.cache.amd64

1.74 KB
Binary file not shown.

python/ldconfig/ld.so.cache.arm64

1.82 KB
Binary file not shown.
1.77 KB
Binary file not shown.

python/ldconfig/ld.so.cache.s390x

1.69 KB
Binary file not shown.

python/ldconfig/ldconfig.sh

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#!/usr/bin/env bash
2+
set -eo pipefail
3+
4+
MODE=$1
5+
shift
6+
7+
if [[ "${MODE}" != "update" && "${MODE}" != "check" ]]; then
8+
echo "Usage: $0 <update|check> <arch>..." >&2
9+
exit 1
10+
fi
11+
12+
# Root to find the reference cache files
13+
ROOT="python/ldconfig"
14+
if [[ -n "${BUILD_WORKSPACE_DIRECTORY}" ]]; then
15+
cd "${BUILD_WORKSPACE_DIRECTORY}"
16+
elif [[ ! -d "${ROOT}" ]]; then
17+
# Fallback to finding it in the same dir as the script
18+
ROOT=$(dirname "$0")
19+
fi
20+
21+
CID=""
22+
TEMP_DIR=""
23+
cleanup() {
24+
local exit_status=$?
25+
if [[ -n "${CID}" ]]; then docker rm -f "${CID}"; fi
26+
if [[ -n "${TEMP_DIR}" ]]; then rm -rf "${TEMP_DIR}"; fi
27+
exit "${exit_status}"
28+
}
29+
trap cleanup EXIT
30+
31+
for arch in "$@"; do
32+
if [ "$MODE" = "update" ]; then echo "===> Updating ldconfig cache for ${arch}..."; else echo "===> Checking ldconfig cache for ${arch}..."; fi
33+
34+
# Run ldconfig and extract the cache.
35+
# We assume the image is ALREADY loaded (handled by Bazel build dependencies)
36+
CID=$(docker create --platform "linux/${arch}" --entrypoint /sbin/ldconfig "bazel/python:python314_root_${arch}_debian13")
37+
docker start -a "${CID}"
38+
39+
if [[ "${MODE}" == "update" ]]; then
40+
docker cp "${CID}:/etc/ld.so.cache" "${ROOT}/ld.so.cache.${arch}"
41+
echo "✅ Updated ${arch} cache."
42+
else
43+
TEMP_DIR=$(mktemp -d)
44+
docker cp "${CID}:/etc/ld.so.cache" "${TEMP_DIR}/ld.so.cache.${arch}"
45+
if ! cmp -s "${TEMP_DIR}/ld.so.cache.${arch}" "${ROOT}/ld.so.cache.${arch}"; then
46+
echo "❌ ERROR: ldconfig cache for ${arch} is out of date!" >&2
47+
echo "Run 'bazel run //python:update_ldconfig' to regenerate." >&2
48+
exit 1
49+
fi
50+
rm -rf "${TEMP_DIR}"
51+
TEMP_DIR=""
52+
echo "✅ ldconfig cache for ${arch} is up to date."
53+
fi
54+
docker rm -f "${CID}"
55+
CID=""
56+
done

0 commit comments

Comments
 (0)