Skip to content

Commit 9850236

Browse files
committed
docker: Add ability to build shared library flavours of CPython.
Enable via: PY_SHARED=1 \ PLATFORM=$(uname -m) POLICY=manylinux2014 COMMIT_SHA=latest \ ./build.sh If set, builds both static & shared versions of CPython into the images. Shared versions end up in (eg) /opt/python/cp37-cp37m-shared/ alongside the existing static /opt/python/cp37-cp37m/, with a Python binary as /usr/local/bin/python3.7-shared.
1 parent b124c44 commit 9850236

File tree

5 files changed

+91
-34
lines changed

5 files changed

+91
-34
lines changed

README.rst

+18
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,24 @@ current (root) directory:
178178

179179
Please note that the Docker build is using `buildx <https://github.com/docker/buildx>`_.
180180

181+
Shared Library CPython
182+
~~~~~~~~~~~~~~~~~~~~~~
183+
184+
You can build the Docker images with shared library CPython builds alongside the
185+
static CPython builds. This might be useful for special packaging requirements,
186+
but ``libpythonX.Y`` is `not a library that a manylinux extension is allowed to
187+
link to <https://www.python.org/dev/peps/pep-0513/#libpythonx-y-so-1>`, so it is
188+
not part of the default manylinux images.
189+
190+
To build the Docker images with shared library CPython builds:
191+
192+
$ PY_SHARED=1 PLATFORM=$(uname -m) POLICY=manylinux2014 COMMIT_SHA=latest ./build.sh
193+
194+
The shared CPython interpreters are installed in
195+
``/opt/python/<python tag>-<abi tag>-shared``. The directories are named after
196+
the PEP 425 tags for each environment -- e.g. ``/opt/python/cp37-cp37m-shared``
197+
contains a shared library CPython 3.7 build.
198+
181199
Updating the requirements
182200
-------------------------
183201

build.sh

+7-1
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,20 @@ else
6666
echo "Unsupported policy: '${POLICY}'"
6767
exit 1
6868
fi
69+
70+
# default is not to include shared interpreter builds
71+
: "${PY_SHARED:=0}"
72+
6973
export BASEIMAGE
7074
export DEVTOOLSET_ROOTPATH
7175
export PREPEND_PATH
7276
export LD_LIBRARY_PATH_ARG
77+
export PY_SHARED
7378

7479
BUILD_ARGS_COMMON="
7580
--build-arg POLICY --build-arg PLATFORM --build-arg BASEIMAGE
7681
--build-arg DEVTOOLSET_ROOTPATH --build-arg PREPEND_PATH --build-arg LD_LIBRARY_PATH_ARG
82+
--build-arg PY_SHARED
7783
--rm -t quay.io/pypa/${POLICY}_${PLATFORM}:${COMMIT_SHA}
7884
-f docker/Dockerfile docker/
7985
"
@@ -94,7 +100,7 @@ elif [ "${MANYLINUX_BUILD_FRONTEND}" == "buildkit" ]; then
94100
--import-cache type=local,src=$(pwd)/.buildx-cache-${POLICY}_${PLATFORM} \
95101
--export-cache type=local,dest=$(pwd)/.buildx-cache-staging-${POLICY}_${PLATFORM} \
96102
--opt build-arg:POLICY=${POLICY} --opt build-arg:PLATFORM=${PLATFORM} --opt build-arg:BASEIMAGE=${BASEIMAGE} \
97-
--opt "build-arg:DEVTOOLSET_ROOTPATH=${DEVTOOLSET_ROOTPATH}" --opt "build-arg:PREPEND_PATH=${PREPEND_PATH}" --opt "build-arg:LD_LIBRARY_PATH_ARG=${LD_LIBRARY_PATH_ARG}" \
103+
--opt "build-arg:DEVTOOLSET_ROOTPATH=${DEVTOOLSET_ROOTPATH}" --opt "build-arg:PREPEND_PATH=${PREPEND_PATH}" --opt "build-arg:LD_LIBRARY_PATH_ARG=${LD_LIBRARY_PATH_ARG}" --opt "build-arg:PY_SHARED=${PY_SHARED}"\
98104
--output type=docker,name=quay.io/pypa/${POLICY}_${PLATFORM}:${COMMIT_SHA} | docker load
99105
else
100106
echo "Unsupported build frontend: '${MANYLINUX_BUILD_FRONTEND}'"

docker/Dockerfile

+2
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ ARG PLATFORM=x86_64
55
ARG DEVTOOLSET_ROOTPATH=
66
ARG LD_LIBRARY_PATH_ARG=
77
ARG PREPEND_PATH=
8+
ARG PY_SHARED=0
89

910
FROM $BASEIMAGE AS runtime_base
1011
ARG POLICY
1112
ARG PLATFORM
1213
ARG DEVTOOLSET_ROOTPATH
1314
ARG LD_LIBRARY_PATH_ARG
1415
ARG PREPEND_PATH
16+
ARG PY_SHARED
1517
LABEL maintainer="The ManyLinux project"
1618

1719
ENV AUDITWHEEL_POLICY=${POLICY} AUDITWHEEL_ARCH=${PLATFORM} AUDITWHEEL_PLAT=${POLICY}_${PLATFORM}

docker/build_scripts/build-cpython.sh

+53-30
Original file line numberDiff line numberDiff line change
@@ -30,36 +30,59 @@ fetch_source Python-${CPYTHON_VERSION}.tgz.asc ${CPYTHON_DOWNLOAD_URL}/${CPYTHON
3030
gpg --import ${MY_DIR}/cpython-pubkeys.txt
3131
gpg --verify Python-${CPYTHON_VERSION}.tgz.asc
3232
tar -xzf Python-${CPYTHON_VERSION}.tgz
33-
pushd Python-${CPYTHON_VERSION}
34-
PREFIX="/opt/_internal/cpython-${CPYTHON_VERSION}"
35-
mkdir -p ${PREFIX}/lib
36-
if [ "${AUDITWHEEL_POLICY}" == "manylinux2010" ]; then
37-
# The _ctypes stdlib module build started to fail with 3.10.0rc1
38-
# No clue what changed exactly yet
39-
# This workaround fixes the build
40-
LIBFFI_INCLUDEDIR=$(pkg-config --cflags-only-I libffi | tr -d '[:space:]')
41-
LIBFFI_INCLUDEDIR=${LIBFFI_INCLUDEDIR:2}
42-
cp ${LIBFFI_INCLUDEDIR}/ffi.h ${LIBFFI_INCLUDEDIR}/ffitarget.h /usr/include/
43-
fi
44-
# configure with hardening options only for the interpreter & stdlib C extensions
45-
# do not change the default for user built extension (yet?)
46-
./configure \
47-
CFLAGS_NODIST="${MANYLINUX_CFLAGS} ${MANYLINUX_CPPFLAGS}" \
48-
LDFLAGS_NODIST="${MANYLINUX_LDFLAGS}" \
49-
--prefix=${PREFIX} --disable-shared --with-ensurepip=no > /dev/null
50-
make > /dev/null
51-
make install > /dev/null
52-
if [ "${AUDITWHEEL_POLICY}" == "manylinux2010" ]; then
53-
rm -f /usr/include/ffi.h /usr/include/ffitarget.h
54-
fi
55-
popd
56-
rm -rf Python-${CPYTHON_VERSION} Python-${CPYTHON_VERSION}.tgz Python-${CPYTHON_VERSION}.tgz.asc
5733

58-
# we don't need libpython*.a, and they're many megabytes
59-
find ${PREFIX} -name '*.a' -print0 | xargs -0 rm -f
34+
function build {
35+
IS_SHARED=$1
36+
pushd Python-${CPYTHON_VERSION}
37+
PREFIX="/opt/_internal/cpython-${CPYTHON_VERSION}"
38+
if [ ${IS_SHARED} -eq 1 ]; then
39+
PREFIX="${PREFIX}-shared"
40+
fi
41+
mkdir -p ${PREFIX}/lib
42+
if [ "${AUDITWHEEL_POLICY}" == "manylinux2010" ]; then
43+
# The _ctypes stdlib module build started to fail with 3.10.0rc1
44+
# No clue what changed exactly yet
45+
# This workaround fixes the build
46+
LIBFFI_INCLUDEDIR=$(pkg-config --cflags-only-I libffi | tr -d '[:space:]')
47+
LIBFFI_INCLUDEDIR=${LIBFFI_INCLUDEDIR:2}
48+
cp ${LIBFFI_INCLUDEDIR}/ffi.h ${LIBFFI_INCLUDEDIR}/ffitarget.h /usr/include/
49+
fi
50+
# configure with hardening options only for the interpreter & stdlib C extensions
51+
# do not change the default for user built extension (yet?)
52+
if [ ${IS_SHARED} -eq 1 ]; then
53+
FLAVOR="--enable-shared"
54+
FLAVOR_LDFLAGS="-Wl,-rpath=${PREFIX}/lib"
55+
else
56+
FLAVOR="--disable-shared"
57+
FLAVOR_LDFLAGS=
58+
fi
59+
60+
./configure \
61+
CFLAGS_NODIST="${MANYLINUX_CFLAGS} ${MANYLINUX_CPPFLAGS}" \
62+
LDFLAGS_NODIST="${MANYLINUX_LDFLAGS} ${FLAVOR_LDFLAGS}" \
63+
--prefix=${PREFIX} ${FLAVOR} --with-ensurepip=no > /dev/null
64+
make > /dev/null
65+
make install > /dev/null
66+
if [ "${AUDITWHEEL_POLICY}" == "manylinux2010" ]; then
67+
rm -f /usr/include/ffi.h /usr/include/ffitarget.h
68+
fi
69+
popd
70+
71+
if [ ${IS_SHARED} -eq 0 ]; then
72+
# we don't need libpython*.a, and they're many megabytes
73+
find ${PREFIX} -name '*.a' -print0 | xargs -0 rm -f
74+
fi
6075

61-
# We do not need precompiled .pyc and .pyo files.
62-
clean_pyc ${PREFIX}
76+
# We do not need precompiled .pyc and .pyo files.
77+
clean_pyc ${PREFIX}
6378

64-
# Strip ELF files found in ${PREFIX}
65-
strip_ ${PREFIX}
79+
# Strip ELF files found in ${PREFIX}
80+
strip_ ${PREFIX}
81+
}
82+
83+
build 0
84+
if [ ${PY_SHARED-0} -eq 1 ]; then
85+
build 1
86+
fi
87+
88+
rm -rf Python-${CPYTHON_VERSION} Python-${CPYTHON_VERSION}.tgz Python-${CPYTHON_VERSION}.tgz.asc

docker/build_scripts/finalize.sh

+11-3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ source $MY_DIR/build_utils.sh
1111

1212
mkdir /opt/python
1313
for PREFIX in $(find /opt/_internal/ -mindepth 1 -maxdepth 1 \( -name 'cpython*' -o -name 'pypy*' \)); do
14+
if [[ "${PREFIX}" =~ -shared$ ]]; then
15+
SUFFIX=-shared
16+
else
17+
SUFFIX=
18+
fi
19+
1420
# Some python's install as bin/python3. Make them available as
1521
# bin/python.
1622
if [ -e ${PREFIX}/bin/python3 ] && [ ! -e ${PREFIX}/bin/python ]; then
@@ -24,14 +30,16 @@ for PREFIX in $(find /opt/_internal/ -mindepth 1 -maxdepth 1 \( -name 'cpython*'
2430
# Since we fall back on a canned copy of pip, we might not have
2531
# the latest pip and friends. Upgrade them to make sure.
2632
${PREFIX}/bin/pip install -U --require-hashes -r ${MY_DIR}/requirements${PY_VER}.txt
33+
2734
# Create a symlink to PREFIX using the ABI_TAG in /opt/python/
35+
2836
ABI_TAG=$(${PREFIX}/bin/python ${MY_DIR}/python-tag-abi-tag.py)
29-
ln -s ${PREFIX} /opt/python/${ABI_TAG}
37+
ln -s ${PREFIX} /opt/python/${ABI_TAG}${SUFFIX:-}
3038
# Make versioned python commands available directly in environment.
3139
if [[ "${PREFIX}" == *"/pypy"* ]]; then
32-
ln -s ${PREFIX}/bin/python /usr/local/bin/pypy${PY_VER}
40+
ln -s ${PREFIX}/bin/python /usr/local/bin/pypy${PY_VER}${SUFFIX}
3341
else
34-
ln -s ${PREFIX}/bin/python /usr/local/bin/python${PY_VER}
42+
ln -s ${PREFIX}/bin/python /usr/local/bin/python${PY_VER}${SUFFIX}
3543
fi
3644
done
3745

0 commit comments

Comments
 (0)