Skip to content

Commit 92fe187

Browse files
committed
unix: rewrite shebangs of Python scripts to reference local interpreter
Before, the shebangs referenced a path in the build environment, which likely didn't exist in the run-time environment. After this change, the shebang uses a multiline shell script to call `exec` on the `python*` binary in the same directory as the script. The solution is similar to #61 but the technical approach is a bit different. We perform the rewrite inside the build environment, so this should work when building in Docker containers. And we use binary I/O everywhere, ensuring bytes are preserved. We also always exec the canonical `python*` binary to avoid symlink overhead.
1 parent a37cd3d commit 92fe187

File tree

2 files changed

+44
-21
lines changed

2 files changed

+44
-21
lines changed

cpython-unix/build-cpython.sh

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,50 @@ if [ -n "${PYTHON_BINARY_SUFFIX}" ]; then
933933
${ROOT}/out/python/install/bin/python${PYTHON_MAJMIN_VERSION}
934934
fi
935935

936+
if [ ! -f ${ROOT}/out/python/install/bin/python3 ]; then
937+
echo "python3 executable does not exist"
938+
exit 1
939+
fi
940+
941+
# Fixup shebangs in Python scripts to reference the local python interpreter.
942+
cat > ${ROOT}/fix_shebangs.py << EOF
943+
import os
944+
import sys
945+
946+
ROOT = sys.argv[1]
947+
948+
for f in sorted(os.listdir(ROOT)):
949+
full = os.path.join(ROOT, f)
950+
951+
if os.path.islink(full) or not os.path.isfile(full):
952+
continue
953+
954+
with open(full, "rb") as fh:
955+
initial = fh.read(64)
956+
957+
if not initial.startswith(b"#!"):
958+
continue
959+
960+
print("rewriting shebang in %s" % full)
961+
962+
lines = []
963+
964+
with open(full, "rb") as fh:
965+
next(fh)
966+
967+
lines.extend([
968+
b"#!/bin/sh\n",
969+
b'"exec" "\$(dirname \$0)/python${PYTHON_MAJMIN_VERSION}${PYTHON_BINARY_SUFFIX}" "\$0" "\$@"\n',
970+
])
971+
972+
lines.extend(fh)
973+
974+
with open(full, "wb") as fh:
975+
fh.write(b"".join(lines))
976+
EOF
977+
978+
${BUILD_PYTHON} ${ROOT}/fix_shebangs.py ${ROOT}/out/python/install/bin
979+
936980
# Also copy object files so they can be linked in a custom manner by
937981
# downstream consumers.
938982
for d in Modules Objects Parser Parser/pegen Programs Python; do

docs/quirks.rst

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,6 @@
44
Behavior Quirks
55
===============
66

7-
.. _quirk_shebangs:
8-
9-
Bad Shebangs in Python Scripts
10-
==============================
11-
12-
Various Python scripts under ``install/bin/`` (e.g. ``pip``) have
13-
shebangs looking like ``#!/build/out/python/install/bin/python3``.
14-
This ``/build/out/`` directory is where the distribution is built
15-
from. Python is writing out shebangs for Python scripts with
16-
that absolute path.
17-
18-
To work around this issue, you can mass rewrite the shebangs to
19-
point the directory where the distribution is extracted/installed
20-
to. Here is a sample shell one-liner to get you started::
21-
22-
$ find install/bin/ -type f -exec sed -i '1 s/^#!.*python.*/#!.\/python3/' {} \;
23-
24-
Alternatively, you can sometimes execute ``python3 -m <module>``
25-
to get equivalent functionality to what the installed script would
26-
do. e.g. to run pip, ``python3 -m pip ...``.
27-
287
.. _quirk_backspace_key:
298

309
Backscape Key Doesn't work in Python REPL

0 commit comments

Comments
 (0)