|
| 1 | +FROM --platform=linux/386 alpine:3.22 AS base_builder |
| 2 | + |
| 3 | +ENV PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \ |
| 4 | + LANG=C.UTF-8 \ |
| 5 | + PYTHON_VERSION=3.13.13 \ |
| 6 | + IS_32BIT=1 \ |
| 7 | + ZLIB_VERSION=1.3.2 \ |
| 8 | + FFI_VERSION=3.5.2 \ |
| 9 | + EXPAT_VERSION=2.7.5 \ |
| 10 | + GDBM_VERSION=1.26 \ |
| 11 | + LZMA_VERSION=4.32.7 \ |
| 12 | + GETTEXT_VERSION=1.0 \ |
| 13 | + NCURSES_VERSION=6.6 \ |
| 14 | + OPENSSL_VERSION=3.5.6 \ |
| 15 | + READLINE_VERSION=8.3 \ |
| 16 | + TCL_VERSION=8.6.17 \ |
| 17 | + XZ_VERSION=5.8.3 \ |
| 18 | + BZIP2_VERSION=1.0.8 \ |
| 19 | + UUID_VERSION=2.40.4 \ |
| 20 | + SQLITE_VERSION=3530000 \ |
| 21 | + SQLITE_VERSION_LITERIAL=3.53.0 \ |
| 22 | + SQLITE_YEAR=2026 \ |
| 23 | + PYTHON_PIP_VERSION=26.0.1 \ |
| 24 | + PYTHON_SETUPTOOLS_VERSION=82.0.1 |
| 25 | + |
| 26 | +WORKDIR /src |
| 27 | +RUN set -eux && apk add --no-cache ca-certificates tzdata && \ |
| 28 | + apk add --no-cache --virtual .build-deps tar xz bluez-dev dpkg-dev dpkg findutils gcc file g++ \ |
| 29 | + libc-dev libnsl-dev libtirpc-dev linux-headers make pax-utils tk tk-dev util-linux-dev wget \ |
| 30 | + bison flex gawk patch |
| 31 | + |
| 32 | +# # =========== musl =========== |
| 33 | +FROM --platform=linux/386 debian:bookworm AS musl_builder |
| 34 | +ARG DEBIAN_FRONTEND=noninteractive |
| 35 | +ENV TZ=Asia/Taipei \ |
| 36 | + IS_32BIT=1 |
| 37 | + |
| 38 | +RUN apt-get update -y && apt-get upgrade -y && \ |
| 39 | + apt-get install -y build-essential wget git file tar xz-utils dpkg-dev libnsl-dev libtirpc-dev uuid-dev patch |
| 40 | + |
| 41 | +WORKDIR /src |
| 42 | +COPY ["common/build/deplib/build_musl.sh", "3.13/x86/deplib/config.mak", "."] |
| 43 | +RUN set -eux && ./build_musl.sh |
| 44 | + |
| 45 | +# =========== libz =========== |
| 46 | +FROM base_builder AS libz_builder |
| 47 | +COPY ["common/build/deplib/build_zlib.sh", "."] |
| 48 | +RUN set -eux && ./build_zlib.sh |
| 49 | + |
| 50 | +# =========== ffi =========== |
| 51 | +FROM libz_builder AS ffi_builder |
| 52 | +COPY ["common/build/deplib/build_ffi.sh", "."] |
| 53 | +RUN set -eux && ./build_ffi.sh |
| 54 | + |
| 55 | +# =========== expat =========== |
| 56 | +FROM ffi_builder AS expat_builder |
| 57 | +COPY ["common/build/deplib/build_expat.sh", "."] |
| 58 | +RUN set -eux && ./build_expat.sh |
| 59 | + |
| 60 | +# =========== gdbm =========== |
| 61 | +FROM expat_builder AS gdbm_builder |
| 62 | +COPY ["common/build/deplib/build_gdbm.sh", "."] |
| 63 | +RUN set -eux && ./build_gdbm.sh |
| 64 | + |
| 65 | +# =========== lzma =========== |
| 66 | +FROM gdbm_builder AS lzma_builder |
| 67 | +COPY ["common/build/deplib/build_lzma.sh", "."] |
| 68 | +RUN set -eux && ./build_lzma.sh |
| 69 | + |
| 70 | +# =========== gettext =========== |
| 71 | +FROM lzma_builder AS gettext_builder |
| 72 | +COPY ["common/build/deplib/build_gettext.sh", "."] |
| 73 | +RUN set -eux && ./build_gettext.sh |
| 74 | + |
| 75 | +# =========== ncurses =========== |
| 76 | +FROM gettext_builder AS ncurses_builder |
| 77 | +COPY ["common/build/deplib/build_ncurses.sh", "."] |
| 78 | +RUN set -eux && ./build_ncurses.sh |
| 79 | + |
| 80 | +# =========== openssl =========== |
| 81 | +FROM ncurses_builder AS openssl_builder |
| 82 | +COPY ["common/build/deplib/build_openssl.sh", "."] |
| 83 | +RUN set -eux && ./build_openssl.sh |
| 84 | + |
| 85 | +# =========== readline =========== |
| 86 | +FROM openssl_builder AS readline_builder |
| 87 | +COPY ["common/build/deplib/build_readline.sh", "common/patches/common/ncurses/fix-ncurses-underlinking.patch", "."] |
| 88 | +RUN set -eux && ./build_readline.sh |
| 89 | + |
| 90 | +# =========== tcl =========== |
| 91 | +FROM readline_builder AS tcl_builder |
| 92 | +COPY ["common/build/deplib/build_tcl.sh", "."] |
| 93 | +RUN set -eux && ./build_tcl.sh |
| 94 | + |
| 95 | +# =========== xz =========== |
| 96 | +FROM tcl_builder AS xz_builder |
| 97 | +COPY ["common/build/deplib/build_xz.sh", "."] |
| 98 | +RUN set -eux && ./build_xz.sh |
| 99 | + |
| 100 | +# =========== bzip2 =========== |
| 101 | +FROM xz_builder AS bzip2_builder |
| 102 | +COPY ["common/build/deplib/build_bzip2.sh", "."] |
| 103 | +RUN set -eux && ./build_bzip2.sh |
| 104 | + |
| 105 | +# =========== uuid =========== |
| 106 | +FROM bzip2_builder AS uuid_builder |
| 107 | +COPY ["common/build/deplib/build_uuid.sh", "."] |
| 108 | +RUN set -eux && ./build_uuid.sh |
| 109 | + |
| 110 | +# =========== sqlite3 =========== |
| 111 | +FROM uuid_builder AS sqlite3_builder |
| 112 | +COPY ["common/build/deplib/build_sqlite3.sh", "."] |
| 113 | +RUN set -eux && ./build_sqlite3.sh |
| 114 | + |
| 115 | +# =========== Python =========== |
| 116 | +FROM sqlite3_builder AS python_builder |
| 117 | + |
| 118 | +COPY ["common/build/deplib/build_python.sh", "common/build/deplib/install_pip.sh", "."] |
| 119 | +RUN set -eux && ./build_python.sh && ./install_pip.sh |
| 120 | + |
| 121 | +# =========== launcher (static C binary, replaces shell wrappers) =========== |
| 122 | +FROM musl_builder AS launcher_builder |
| 123 | +ENV MUSL_ARCH=i386 |
| 124 | +WORKDIR /src |
| 125 | +COPY ["common/build/wrappers/launcher.c", "./launcher.c"] |
| 126 | +# config.mak carries `MUSL_VER = X.Y.Z`; drop the patch component and bake |
| 127 | +# `X.Y` into the launcher as MUSL_VERSION so the site hook can hand pip the |
| 128 | +# matching `musllinux_X_Y_*` wheel tag without subprocess'ing ld.so every |
| 129 | +# Python startup. |
| 130 | +RUN set -eux && \ |
| 131 | + MUSL_VER=$(awk -F'=' '/^MUSL_VER[[:space:]]*=/ { gsub(/[[:space:]]/, "", $2); print $2 }' /src/config.mak) && \ |
| 132 | + MUSL_TAG=$(echo "$MUSL_VER" | cut -d. -f1,2) && \ |
| 133 | + test -n "$MUSL_TAG" && \ |
| 134 | + /opt/musl/bin/${MUSL_ARCH}-linux-musl-gcc -static -Os -s \ |
| 135 | + -DMUSL_ARCH="\"${MUSL_ARCH}\"" \ |
| 136 | + -DMUSL_VERSION="\"${MUSL_TAG}\"" \ |
| 137 | + launcher.c -o launcher && \ |
| 138 | + file launcher |
| 139 | + |
| 140 | +# =========== patch_stage =========== |
| 141 | +FROM --platform=linux/386 debian:latest AS patch_stage |
| 142 | +ARG DEBIAN_FRONTEND=noninteractive |
| 143 | +ENV TZ=Asia/Taipei \ |
| 144 | + IS_32BIT=1 |
| 145 | + |
| 146 | +RUN apt-get update -y && apt-get install -y patchelf file |
| 147 | + |
| 148 | +COPY --from=python_builder /opt/shared_libraries /opt/shared_libraries |
| 149 | +COPY --from=python_builder /opt/python /opt/python |
| 150 | + |
| 151 | +COPY ["common/build/wrappers/packing-initializer", "."] |
| 152 | +RUN ./packing-initializer && rm -rf packing-initializer |
| 153 | + |
| 154 | +COPY --from=launcher_builder /src/launcher /tmp/launcher |
| 155 | +RUN install -m 0755 /tmp/launcher /opt/python/bin/python3 && \ |
| 156 | + install -m 0755 /tmp/launcher /opt/python/bin/pip3 && \ |
| 157 | + rm /tmp/launcher && \ |
| 158 | + cd /opt/python/bin && \ |
| 159 | + ln -sf python3 python && \ |
| 160 | + ln -sf pip3 pip |
| 161 | + |
| 162 | +# Copy shipped musl runtime and rewrite RPATH on Python's bundled ELFs so |
| 163 | +# $ORIGIN-relative lookups resolve inside the installed tree. No .interp |
| 164 | +# patching is needed — the static launcher invokes ld-musl.so directly. |
| 165 | +COPY --from=musl_builder /opt/musl /opt/musl |
| 166 | +COPY ["common/build/wrappers/rpath-patcher.sh", "."] |
| 167 | +RUN set -eux && ./rpath-patcher.sh |
| 168 | + |
| 169 | +# Site-level hook that tells pip/packaging the shipped libc is musl so wheel |
| 170 | +# resolution picks `musllinux_*` wheels instead of glibc-built manylinux |
| 171 | +# wheels. Runs on every Python startup via the .pth but is a no-op unless |
| 172 | +# the launcher has set _STANDALONE_PYTHON_MUSL_VERSION in the env. |
| 173 | +COPY ["common/build/wrappers/standalone_python_musl.py", \ |
| 174 | + "common/build/wrappers/standalone_python_musl.pth", \ |
| 175 | + "/opt/python/lib/python3.13/site-packages/"] |
| 176 | + |
| 177 | +# =========== final =========== |
| 178 | +FROM --platform=linux/386 debian:latest AS final |
| 179 | +ARG DEBIAN_FRONTEND=noninteractive |
| 180 | +ENV TZ=Asia/Taipei |
| 181 | + |
| 182 | +COPY --from=patch_stage /opt/python /opt/python |
0 commit comments