pyenv-binary: record direct system dependencies in Linux/FreeBSD (#3520)

This commit is contained in:
Ayush 2026-08-16 01:17:27 +05:30 committed by GitHub
parent 0f16606e4f
commit 50fbc8c6cc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 38 additions and 8 deletions

View file

@ -92,12 +92,16 @@ fi
if [ "$os" = "Linux" ]; then
libc="$(getconf GNU_LIBC_VERSION 2>/dev/null || true)"
fi
if [ "$os" != "Darwin" ] && ! LC_ALL=C readelf --version &>/dev/null; then
echo "pyenv-binary: need readelf to inspect shared libraries" >&2
exit 1
fi
# List the external shared libraries the install links against: those that
# resolve outside its own prefix, so they must already exist on the target.
# The interpreter plus every bundled shared object are inspected.
system_deps() {
local f
local f needed
{
for f in "${prefix}"/bin/python*; do
[ -e "$f" ] && printf '%s\n' "$f"
@ -111,8 +115,19 @@ system_deps() {
otool -L "$f" 2>/dev/null | tail -n +2 | awk -v pfx="${prefix}/" \
'$1 !~ /^@/ && substr($1, 1, length(pfx)) != pfx { print $1 }'
else
ldd "$f" 2>/dev/null | awk -v pfx="${prefix}/" \
'$2 == "=>" && $3 ~ /^\// && substr($3, 1, length(pfx)) != pfx { print $1 }'
# GNU binutils 2.44 writes "(NEEDED) ... [name]"; FreeBSD 15.1 writes
# "NEEDED ... [name]". readelf(1) documents -d, not its text format.
needed="$(LC_ALL=C readelf -dW "$f" 2>/dev/null | awk \
'$2 == "(NEEDED)" || $2 == "NEEDED" { sub(/^.*\[/, ""); sub(/\].*$/, ""); print }' | tr '\n' ' ')"
# Linux and FreeBSD ldd write resolved libraries as
# "name => /absolute/path (address)"; entries without "=>" are ignored.
LC_ALL=C ldd "$f" 2>/dev/null | awk -v needed="$needed" -v pfx="${prefix}/" '
BEGIN {
split(needed, deps, " ")
for (i in deps) direct[deps[i]] = 1
}
$1 in direct && $2 == "=>" && $3 ~ /^\// && substr($3, 1, length(pfx)) != pfx { print $1 }
'
fi
done | sort -u
}

View file

@ -10,6 +10,7 @@ stub_build_environment() {
create_stub pyenv-latest 'while (($#)); do case "$1" in -f|-k);; *)break;; esac; shift; done; echo "$*"'
create_stub uname 'case "$1" in -s) echo Linux;; -m) echo x86_64;; esac'
create_stub getconf 'echo "glibc 2.17"'
create_stub readelf true
}
@test "-v|--verbose runs pyenv install verbosely" {

View file

@ -79,7 +79,16 @@ platform() {
assert_line "archive=3.12.7-$(platform).tar.gz"
}
@test "records only the libraries ldd resolves outside the prefix" {
@test "fails when readelf is not available" {
create_version "3.12.7"
create_stub uname 'case "$1" in -s) echo Linux;; -m) echo x86_64;; esac'
PATH="$(path_without readelf)" run pyenv-binary-save "3.12.7" "${BATS_TEST_TMPDIR}/dist"
assert_failure "pyenv-binary: need readelf to inspect shared libraries"
assert [ ! -e "${BATS_TEST_TMPDIR}/dist/3.12.7-$(platform).tar.gz" ]
}
@test "records only direct libraries resolved outside the prefix" {
create_version "3.12.7"
touch "${PYENV_ROOT}/versions/3.12.7/bin/python3.12"
create_path_executable uname <<'STUB'
@ -97,13 +106,18 @@ cat <<EOF
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4a3b800000)
/lib64/ld-linux-x86-64.so.2 (0x00007f4a3c200000)
EOF
STUB
create_path_executable readelf <<'STUB'
cat <<EOF
0x0000000000000001 (NEEDED) Shared library: [libpython3.12.so.1.0]
0x0000000000000001 (NEEDED) Shared library: [libm.so.6]
EOF
STUB
run pyenv-binary-save "3.12.7" "${BATS_TEST_TMPDIR}/dist"
assert_success
run grep '^dep=' "${BATS_TEST_TMPDIR}/dist/"*.meta
assert_output "dep=libc.so.6
dep=libm.so.6"
assert_output "dep=libm.so.6"
}
@test "records only the libraries otool resolves outside the prefix" {

View file

@ -12,7 +12,7 @@ FROM bash:$BASH
apk add sed coreutils findutils \
;fi
# Bats
RUN apk add --update parallel ncurses git \
RUN apk add --update parallel ncurses git binutils \
&& mkdir -p ~/.parallel \
&& touch ~/.parallel/will-cite
COPY --from=bats /root/bats-core /root/bats-core

View file

@ -165,7 +165,7 @@ path_without() {
if [ "$found" != "${PYENV_ROOT}/shims" ]; then
alt="${PYENV_TEST_DIR}/$(echo "${found#/}" | tr '/' '-')"
mkdir -p "$alt"
for util in bash head cut readlink greadlink tr sed xargs basename sort; do
for util in basename bash cut dirname find greadlink gzip head mkdir readlink sed sort tar tr xargs; do
if [ -x "${found}/$util" ]; then
ln -s "${found}/$util" "${alt}/$util"
fi