From c8e1365d319fb2f2682ff3e305b2290948afa720 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 22 Aug 2026 14:46:23 -0400 Subject: [PATCH] pypy: Stop the prebuilt interpreters depending on Tcl/Tk 8.5 `prebuilt.nix` (3.10, 3.11) asked for Tcl/Tk 8.5 on Linux, but the `_tkinter` extension in those tarballs is linked against `libtcl8.6.so` and `libtk8.6.so`. It resolved anyway, because the tarball vendors its own copy of both under `lib/`. So the declared dependency was doing nothing, and `tkinter` -- which is stdlib, not an optional extra -- was quietly running against a bundled Tcl that we do not update. Point it at our 8.6 instead, and delete the vendored libraries so it has to. The vendored *script* libraries have to go too: `init.tcl` checks the exact patchlevel, so our 8.6.16 rejects the bundled 8.6.14 copy as "not usable" and `Tcl_Init` fails. Note that the existing `installCheckPhase` does not catch that, as `import tkinter` never creates an interpreter; `tkinter.Tcl()` does. `prebuilt_2_7.nix` genuinely needs 8.5 -- its `tklib_cffi` asks for `libtcl8.5.so` and its `installPhase` does not keep the bundled `lib/` -- but it exists only to bootstrap the source build of PyPy2, where nothing imports `tkinter`. Let the module go unresolved and drop it from the import check rather than keep an end-of-life Tcl alive for it. Assisted-by: Claude Code (Claude Opus 5) --- .../interpreters/python/pypy/prebuilt.nix | 30 +++++++++++-------- .../interpreters/python/pypy/prebuilt_2_7.nix | 21 ++++++++----- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/pkgs/development/interpreters/python/pypy/prebuilt.nix b/pkgs/development/interpreters/python/pypy/prebuilt.nix index 6d5ce0fc11c9..685d9e194d67 100644 --- a/pkgs/development/interpreters/python/pypy/prebuilt.nix +++ b/pkgs/development/interpreters/python/pypy/prebuilt.nix @@ -11,8 +11,6 @@ gdbm, ncurses6, sqlite, - tcl-8_5, - tk-8_5, tcl-8_6, tk-8_6, zlib, @@ -83,12 +81,6 @@ stdenv.mkDerivation { sqlite zlib stdenv.cc.cc.libgcc or null - ] - ++ lib.optionals stdenv.hostPlatform.isLinux [ - tcl-8_5 - tk-8_5 - ] - ++ lib.optionals stdenv.hostPlatform.isDarwin [ tcl-8_6 tk-8_6 ]; @@ -102,10 +94,24 @@ stdenv.mkDerivation { echo "Moving files to $out" mv -t $out bin include lib mv $out/bin/libpypy*-c${stdenv.hostPlatform.extensions.sharedLibrary} $out/lib/ - ${lib.optionalString stdenv.hostPlatform.isLinux '' - rm $out/bin/*.debug - ''} - + '' + + lib.optionalString stdenv.hostPlatform.isLinux '' + rm $out/bin/*.debug + '' + # The tarball vendors Tcl/Tk. Drop it so `tkinter` -- which is stdlib, not an + # optional extra -- resolves against ours instead: the same 8.6 the extension + # module asks for by soname, but one that gets updated. + # + # The script libraries have to go with the shared ones. `init.tcl` checks the + # exact patchlevel, so our 8.6.16 rejects the bundled 8.6.14 copy as "not + # usable" and `Tcl_Init` fails -- which `import tkinter` alone does not + # catch, since that never creates an interpreter. + + lib.optionalString stdenv.hostPlatform.isLinux '' + rm $out/lib/libtcl8.6${stdenv.hostPlatform.extensions.sharedLibrary} + rm $out/lib/libtk8.6${stdenv.hostPlatform.extensions.sharedLibrary} + rm -r $out/lib/tcl8.6 $out/lib/tk8.6 + '' + + '' echo "Removing bytecode" find . -name "__pycache__" -type d -depth -delete diff --git a/pkgs/development/interpreters/python/pypy/prebuilt_2_7.nix b/pkgs/development/interpreters/python/pypy/prebuilt_2_7.nix index ea547c5f8bac..c19a30815fa7 100644 --- a/pkgs/development/interpreters/python/pypy/prebuilt_2_7.nix +++ b/pkgs/development/interpreters/python/pypy/prebuilt_2_7.nix @@ -11,8 +11,6 @@ gdbm, ncurses6, sqlite, - tcl-8_5, - tk-8_5, tcl-8_6, tk-8_6, zlib, @@ -84,10 +82,6 @@ stdenv.mkDerivation { zlib stdenv.cc.cc.libgcc or null ] - ++ lib.optionals stdenv.hostPlatform.isLinux [ - tcl-8_5 - tk-8_5 - ] ++ lib.optionals stdenv.hostPlatform.isDarwin [ tcl-8_6 tk-8_6 @@ -95,6 +89,15 @@ stdenv.mkDerivation { nativeBuildInputs = lib.optionals stdenv.hostPlatform.isLinux [ autoPatchelfHook ]; + # Only a bootstrap interpreter, so its `tkinter` is never imported. The + # tarball bundles the Tcl/Tk it wants but `installPhase` does not keep + # `lib/`, so rather than depend on a Tcl this old just to satisfy a module + # nobody loads, let the module stay unresolved. + autoPatchelfIgnoreMissingDeps = lib.optionals stdenv.hostPlatform.isLinux [ + "libtcl8.5.so" + "libtk8.5.so" + ]; + installPhase = '' runHook preInstall @@ -150,10 +153,12 @@ stdenv.mkDerivation { "sys" "curses" ] - ++ lib.optionals (!isPy3k) [ + # Only Linux gives up on `tkinter`; Darwin still links it against a Tcl + # of ours in `preFixup`. + ++ lib.optionals (!isPy3k && !stdenv.hostPlatform.isLinux) [ "Tkinter" ] - ++ lib.optionals isPy3k [ + ++ lib.optionals (isPy3k && !stdenv.hostPlatform.isLinux) [ "tkinter" ]; imports = lib.concatMapStringsSep "; " (x: "import ${x}") modules;