Skip to content

Commit a96888a

Browse files
committed
Add python 3.14
1 parent c981cd8 commit a96888a

File tree

5 files changed

+343
-0
lines changed

5 files changed

+343
-0
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,8 @@ jobs:
239239
python/3.13/noble: 'python/3.13/noble/**'
240240
python/3.13/slim-jammy: 'python/3.13/slim-jammy/**'
241241
python/3.13/slim-noble: 'python/3.13/slim-noble/**'
242+
python/3.14/noble: 'python/3.14/noble/**'
243+
python/3.14/slim-noble: 'python/3.14/slim-noble/**'
242244
ros/humble/ubuntu/jammy: 'ros/humble/ubuntu/jammy/**'
243245
ros/jazzy/ubuntu/noble: 'ros/jazzy/ubuntu/noble/**'
244246
ros/kilted/ubuntu/noble: 'ros/kilted/ubuntu/noble/**'

python/3.14/noble/Containerfile

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# syntax=docker/dockerfile:1
2+
ARG BASE_IMAGE=docker.io/boxcutter/buildpack-deps:noble
3+
# hadolint ignore=DL3006
4+
FROM $BASE_IMAGE
5+
6+
# ensure local python is preferred over distribution python
7+
ENV PATH=/usr/local/bin:$PATH
8+
9+
# runtime dependencies
10+
RUN set -eux; \
11+
apt-get update; \
12+
apt-get install -y --no-install-recommends \
13+
libbluetooth-dev \
14+
tk-dev \
15+
uuid-dev \
16+
; \
17+
rm -rf /var/lib/apt/lists/*
18+
19+
ENV PYTHON_VERSION=3.14.2
20+
ENV PYTHON_SHA256=ce543ab854bc256b61b71e9b27f831ffd1bfd60a479d639f8be7f9757cf573e9
21+
22+
# hadolint ignore=DL3003,DL3047
23+
RUN set -eux; \
24+
\
25+
wget -O python.tar.xz "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz"; \
26+
echo "$PYTHON_SHA256 *python.tar.xz" | sha256sum -c -; \
27+
mkdir -p /usr/src/python; \
28+
tar --extract --directory /usr/src/python --strip-components=1 --file python.tar.xz; \
29+
rm python.tar.xz; \
30+
\
31+
cd /usr/src/python; \
32+
gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)"; \
33+
./configure \
34+
--build="$gnuArch" \
35+
--enable-loadable-sqlite-extensions \
36+
--enable-optimizations \
37+
--enable-option-checking=fatal \
38+
--enable-shared \
39+
$(test "${gnuArch%%-*}" != 'riscv64' && echo '--with-lto') \
40+
--with-ensurepip \
41+
; \
42+
nproc="$(nproc)"; \
43+
EXTRA_CFLAGS="$(dpkg-buildflags --get CFLAGS)"; \
44+
LDFLAGS="$(dpkg-buildflags --get LDFLAGS)"; \
45+
arch="$(dpkg --print-architecture)"; arch="${arch##*-}"; \
46+
# https://docs.python.org/3.12/howto/perf_profiling.html
47+
# https://github.com/docker-library/python/pull/1000#issuecomment-2597021615
48+
case "$arch" in \
49+
amd64|arm64) \
50+
# only add "-mno-omit-leaf" on arches that support it
51+
# https://gcc.gnu.org/onlinedocs/gcc-14.2.0/gcc/x86-Options.html#index-momit-leaf-frame-pointer-2
52+
# https://gcc.gnu.org/onlinedocs/gcc-14.2.0/gcc/AArch64-Options.html#index-momit-leaf-frame-pointer
53+
EXTRA_CFLAGS="${EXTRA_CFLAGS:-} -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer"; \
54+
;; \
55+
i386) \
56+
# don't enable frame-pointers on 32bit x86 due to performance drop.
57+
;; \
58+
*) \
59+
# other arches don't support "-mno-omit-leaf"
60+
EXTRA_CFLAGS="${EXTRA_CFLAGS:-} -fno-omit-frame-pointer"; \
61+
;; \
62+
esac; \
63+
make -j "$nproc" \
64+
"EXTRA_CFLAGS=${EXTRA_CFLAGS:-}" \
65+
"LDFLAGS=${LDFLAGS:-}" \
66+
; \
67+
# https://github.com/docker-library/python/issues/784
68+
# prevent accidental usage of a system installed libpython of the same version
69+
rm python; \
70+
make -j "$nproc" \
71+
"EXTRA_CFLAGS=${EXTRA_CFLAGS:-}" \
72+
"LDFLAGS=${LDFLAGS:--Wl},-rpath='\$\$ORIGIN/../lib'" \
73+
python \
74+
; \
75+
make install; \
76+
\
77+
# enable GDB to load debugging data: https://github.com/docker-library/python/pull/701
78+
bin="$(readlink -ve /usr/local/bin/python3)"; \
79+
dir="$(dirname "$bin")"; \
80+
mkdir -p "/usr/share/gdb/auto-load/$dir"; \
81+
cp -vL Tools/gdb/libpython.py "/usr/share/gdb/auto-load/$bin-gdb.py"; \
82+
\
83+
cd /; \
84+
rm -rf /usr/src/python; \
85+
\
86+
find /usr/local -depth \
87+
\( \
88+
\( -type d -a \( -name test -o -name tests -o -name idle_test \) \) \
89+
-o \( -type f -a \( -name '*.pyc' -o -name '*.pyo' -o -name 'libpython*.a' \) \) \
90+
\) -exec rm -rf '{}' + \
91+
; \
92+
\
93+
ldconfig; \
94+
\
95+
export PYTHONDONTWRITEBYTECODE=1; \
96+
python3 --version; \
97+
pip3 --version
98+
99+
# make some useful symlinks that are expected to exist ("/usr/local/bin/python" and friends)
100+
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
101+
RUN set -eux; \
102+
for src in idle3 pip3 pydoc3 python3 python3-config; do \
103+
dst="$(echo "$src" | tr -d 3)"; \
104+
[ -s "/usr/local/bin/$src" ]; \
105+
[ ! -e "/usr/local/bin/$dst" ]; \
106+
ln -svT "$src" "/usr/local/bin/$dst"; \
107+
done
108+
109+
CMD ["python3"]

python/3.14/noble/docker-bake.hcl

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
variable "TAG_PREFIX" {
2+
default = "docker.io/boxcutter/python"
3+
}
4+
5+
variable "VERSION" {
6+
default = "3.14.2"
7+
}
8+
9+
# There's no darwin-based Docker, so if we're running on macOS, change the platform to linux
10+
variable "LOCAL_PLATFORM" {
11+
default = regex_replace("${BAKE_LOCAL_PLATFORM}", "^(darwin)", "linux")
12+
}
13+
14+
target "_common" {
15+
dockerfile = "Containerfile"
16+
tags = [
17+
"${TAG_PREFIX}:${VERSION}-noble",
18+
"${TAG_PREFIX}:${join(".", slice(split(".", "${VERSION}"), 0, 2))}-noble",
19+
]
20+
labels = {
21+
"org.opencontainers.image.source" = "https://github.com/boxcutter/oci"
22+
"org.opencontainers.image.licenses" = "Apache-2.0"
23+
"org.opencontainers.image.description" = "Python is an interpreted, interactive, object-oriented, open-source programming language."
24+
"org.opencontainers.image.title" = "${TAG_PREFIX}"
25+
"org.opencontainers.image.created" = "${timestamp()}"
26+
"dev.boxcutter.image.readme-filepath" = "python/README.md"
27+
}
28+
}
29+
30+
target "local" {
31+
inherits = ["_common"]
32+
platforms = ["${LOCAL_PLATFORM}"]
33+
}
34+
35+
target "default" {
36+
inherits = ["_common"]
37+
platforms = ["linux/amd64", "linux/arm64/v8"]
38+
}
39+
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# syntax=docker/dockerfile:1
2+
ARG BASE_IMAGE=docker.io/ubuntu:noble-20251013
3+
# hadolint ignore=DL3006
4+
FROM $BASE_IMAGE
5+
6+
# ensure local python is preferred over distribution python
7+
ENV PATH=/usr/local/bin:$PATH
8+
9+
# runtime dependencies
10+
RUN set -eux; \
11+
apt-get update; \
12+
apt-get install -y --no-install-recommends \
13+
ca-certificates \
14+
netbase \
15+
tzdata \
16+
; \
17+
rm -rf /var/lib/apt/lists/*
18+
19+
ENV PYTHON_VERSION=3.14.2
20+
ENV PYTHON_SHA256=ce543ab854bc256b61b71e9b27f831ffd1bfd60a479d639f8be7f9757cf573e9
21+
22+
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
23+
# hadolint ignore=DL3003,DL3047,SC2086
24+
RUN set -eux; \
25+
\
26+
# Snapshot *existing* manual packages (Ubuntu marks many base packages manual)
27+
before="$(apt-mark showmanual | tr ' ' '\n' | sed '/^$/d' | sort -u)"; \
28+
\
29+
apt-get update; \
30+
apt-get install -y --no-install-recommends \
31+
dpkg-dev \
32+
gcc \
33+
gnupg \
34+
libbluetooth-dev \
35+
libbz2-dev \
36+
libc6-dev \
37+
libdb-dev \
38+
libffi-dev \
39+
libgdbm-dev \
40+
liblzma-dev \
41+
libncursesw5-dev \
42+
libreadline-dev \
43+
libsqlite3-dev \
44+
libssl-dev \
45+
libzstd-dev \
46+
make \
47+
tk-dev \
48+
uuid-dev \
49+
wget \
50+
xz-utils \
51+
zlib1g-dev \
52+
; \
53+
\
54+
wget -O python.tar.xz "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz"; \
55+
echo "$PYTHON_SHA256 *python.tar.xz" | sha256sum -c -; \
56+
mkdir -p /usr/src/python; \
57+
tar --extract --directory /usr/src/python --strip-components=1 --file python.tar.xz; \
58+
rm python.tar.xz; \
59+
\
60+
cd /usr/src/python; \
61+
gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)"; \
62+
./configure \
63+
--build="$gnuArch" \
64+
--enable-loadable-sqlite-extensions \
65+
--enable-optimizations \
66+
--enable-option-checking=fatal \
67+
--enable-shared \
68+
$(test "${gnuArch%%-*}" != 'riscv64' && echo '--with-lto') \
69+
--with-ensurepip \
70+
; \
71+
nproc="$(nproc)"; \
72+
EXTRA_CFLAGS="$(dpkg-buildflags --get CFLAGS)"; \
73+
LDFLAGS="$(dpkg-buildflags --get LDFLAGS)"; \
74+
LDFLAGS="${LDFLAGS:--Wl},--strip-all"; \
75+
arch="$(dpkg --print-architecture)"; arch="${arch##*-}"; \
76+
# https://docs.python.org/3.12/howto/perf_profiling.html
77+
# https://github.com/docker-library/python/pull/1000#issuecomment-2597021615
78+
case "$arch" in \
79+
amd64|arm64) \
80+
# only add "-mno-omit-leaf" on arches that support it
81+
# https://gcc.gnu.org/onlinedocs/gcc-14.2.0/gcc/x86-Options.html#index-momit-leaf-frame-pointer-2
82+
# https://gcc.gnu.org/onlinedocs/gcc-14.2.0/gcc/AArch64-Options.html#index-momit-leaf-frame-pointer
83+
EXTRA_CFLAGS="${EXTRA_CFLAGS:-} -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer"; \
84+
;; \
85+
i386) \
86+
# don't enable frame-pointers on 32bit x86 due to performance drop.
87+
;; \
88+
*) \
89+
# other arches don't support "-mno-omit-leaf"
90+
EXTRA_CFLAGS="${EXTRA_CFLAGS:-} -fno-omit-frame-pointer"; \
91+
;; \
92+
esac; \
93+
make -j "$nproc" \
94+
"EXTRA_CFLAGS=${EXTRA_CFLAGS:-}" \
95+
"LDFLAGS=${LDFLAGS:-}" \
96+
; \
97+
# https://github.com/docker-library/python/issues/784
98+
# prevent accidental usage of a system installed libpython of the same version
99+
rm python; \
100+
make -j "$nproc" \
101+
"EXTRA_CFLAGS=${EXTRA_CFLAGS:-}" \
102+
"LDFLAGS=${LDFLAGS:--Wl},-rpath='\$\$ORIGIN/../lib'" \
103+
python \
104+
; \
105+
make install; \
106+
\
107+
cd /; \
108+
rm -rf /usr/src/python; \
109+
\
110+
find /usr/local -depth \
111+
\( \
112+
\( -type d -a \( -name test -o -name tests -o -name idle_test \) \) \
113+
-o \( -type f -a \( -name '*.pyc' -o -name '*.pyo' -o -name 'libpython*.a' \) \) \
114+
\) -exec rm -rf '{}' + \
115+
; \
116+
\
117+
ldconfig; \
118+
\
119+
# Identify runtime-shared lib providers and mark them manual (so purge keeps them)
120+
find /usr/local -type f -executable -not \( -name '*tkinter*' \) -exec ldd '{}' ';' \
121+
| awk '/=>/ { so = $(NF-1); if (index(so, "/usr/local/") == 1) { next } ; gsub("^/(usr/)?", "", so); printf "*%s\n", so }' \
122+
| sort -u \
123+
| xargs -rt dpkg-query --search \
124+
# https://manpages.debian.org/bookworm/dpkg/dpkg-query.1.en.html#S (we ignore diversions and it'll be really unusual for more than one package to provide any given .so file)
125+
| awk 'sub(":$", "", $1) { print $1 }' \
126+
| sort -u \
127+
| xargs -r apt-mark manual \
128+
; \
129+
\
130+
# Compute *new* manuals introduced by our build, then flip only those back to auto
131+
after="$(apt-mark showmanual | tr ' ' '\n' | sed '/^$/d' | sort -u)"; \
132+
new_manual="$(comm -13 <(printf '%s\n' "${before}") <(printf '%s\n' "${after}"))"; \
133+
if [ -n "${new_manual}" ]; then \
134+
printf '%s\n' "${new_manual}" | xargs -r apt-mark auto; \
135+
fi; \
136+
\
137+
# Remove build deps that are no longer needed; clean caches
138+
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
139+
rm -rf /var/lib/apt/lists/*; \
140+
\
141+
export PYTHONDONTWRITEBYTECODE=1; \
142+
python3 --version; \
143+
pip3 --version
144+
145+
# make some useful symlinks that are expected to exist ("/usr/local/bin/python" and friends)
146+
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
147+
RUN set -eux; \
148+
for src in idle3 pip3 pydoc3 python3 python3-config; do \
149+
dst="$(echo "$src" | tr -d 3)"; \
150+
[ -s "/usr/local/bin/$src" ]; \
151+
[ ! -e "/usr/local/bin/$dst" ]; \
152+
ln -svT "$src" "/usr/local/bin/$dst"; \
153+
done
154+
155+
CMD ["python3"]
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
variable "TAG_PREFIX" {
2+
default = "docker.io/boxcutter/python"
3+
}
4+
5+
variable "VERSION" {
6+
default = "3.14.2"
7+
}
8+
9+
# There's no darwin-based Docker, so if we're running on macOS, change the platform to linux
10+
variable "LOCAL_PLATFORM" {
11+
default = regex_replace("${BAKE_LOCAL_PLATFORM}", "^(darwin)", "linux")
12+
}
13+
14+
target "_common" {
15+
dockerfile = "Containerfile"
16+
tags = [
17+
"${TAG_PREFIX}:${VERSION}-slim-noble",
18+
"${TAG_PREFIX}:${join(".", slice(split(".", "${VERSION}"), 0, 2))}-slim-noble",
19+
]
20+
labels = {
21+
"org.opencontainers.image.source" = "https://github.com/boxcutter/oci"
22+
"org.opencontainers.image.licenses" = "Apache-2.0"
23+
"org.opencontainers.image.description" = "Python is an interpreted, interactive, object-oriented, open-source programming language."
24+
"org.opencontainers.image.title" = "${TAG_PREFIX}"
25+
"org.opencontainers.image.created" = "${timestamp()}"
26+
"dev.boxcutter.image.readme-filepath" = "python/README.md"
27+
}
28+
}
29+
30+
target "local" {
31+
inherits = ["_common"]
32+
platforms = ["${LOCAL_PLATFORM}"]
33+
}
34+
35+
target "default" {
36+
inherits = ["_common"]
37+
platforms = ["linux/amd64", "linux/arm64/v8"]
38+
}

0 commit comments

Comments
 (0)