From bc6f0a9207a3c20de995b3f9ca2ffce70d0608b4 Mon Sep 17 00:00:00 2001 From: macayu17 Date: Sat, 15 Aug 2026 19:35:21 +0530 Subject: [PATCH] pyenv-binary-save: record direct system dependencies --- .../pyenv-binary/libexec/pyenv-binary-save | 17 +++++++++++--- plugins/pyenv-binary/test/package.bats | 1 + plugins/pyenv-binary/test/save.bats | 23 ++++++++++++++++--- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/plugins/pyenv-binary/libexec/pyenv-binary-save b/plugins/pyenv-binary/libexec/pyenv-binary-save index 4b1047a3..3caf2ba2 100755 --- a/plugins/pyenv-binary/libexec/pyenv-binary-save +++ b/plugins/pyenv-binary/libexec/pyenv-binary-save @@ -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 2>&1; 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,15 @@ 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 }' + needed="$(LC_ALL=C readelf -dW "$f" 2>/dev/null | awk \ + '$2 == "(NEEDED)" || $2 == "NEEDED" { sub(/^.*\[/, ""); sub(/\].*$/, ""); print }' | tr '\n' ' ')" + 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 } diff --git a/plugins/pyenv-binary/test/package.bats b/plugins/pyenv-binary/test/package.bats index b954fa6d..51da03a2 100644 --- a/plugins/pyenv-binary/test/package.bats +++ b/plugins/pyenv-binary/test/package.bats @@ -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" { diff --git a/plugins/pyenv-binary/test/save.bats b/plugins/pyenv-binary/test/save.bats index 03616856..c0c58d38 100644 --- a/plugins/pyenv-binary/test/save.bats +++ b/plugins/pyenv-binary/test/save.bats @@ -2,6 +2,10 @@ load test_helper +_setup() { + create_path_executable readelf "exit 0" +} + create_version() { mkdir -p "${PYENV_ROOT}/versions/$1/bin" } @@ -79,7 +83,15 @@ 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" + + 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 +109,18 @@ cat < /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 <