Skip to content

Commit f32a576

Browse files
committed
Enable native builds on Linux aarch64
I just published an LLVM 20 toolchain for aarch64. The toolchain has support for PGO and BOLT. This commit switches the Linux aarch64 builds to be performed natively on aarch64 machines. PGO and BOLT are enabled on the builds, hopefully making them a bit faster.
1 parent c26a0f0 commit f32a576

File tree

11 files changed

+128
-47
lines changed

11 files changed

+128
-47
lines changed

.github/workflows/linux.yml

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: linux
22

33
on:
44
push:
5-
branches: [main]
5+
branches: [ main ]
66
pull_request:
77

88
concurrency:
@@ -56,15 +56,17 @@ jobs:
5656
fail-fast: false
5757
matrix:
5858
image:
59-
- build
60-
- build.cross
61-
- build.cross-riscv64
62-
- gcc
63-
- xcb
64-
- xcb.cross
65-
- xcb.cross-riscv64
66-
name: ${{ matrix.image }}
67-
runs-on: depot-ubuntu-22.04
59+
- name: build
60+
- name: build.cross
61+
- name: build.cross.riscv64
62+
- name: build.debian9
63+
arm: true
64+
- name: gcc
65+
- name: xcb
66+
- name: xcb.cross
67+
- name: xcb.cross-riscv64
68+
name: ${{ matrix.image.name }}
69+
runs-on: ${{ matric.image.arm && 'depot-ubuntu-22.04-arm' || 'depot-ubuntu-22.04' }}
6870
permissions:
6971
packages: write
7072
steps:
@@ -98,30 +100,30 @@ jobs:
98100
uses: docker/build-push-action@v5
99101
with:
100102
context: .
101-
file: build/${{ matrix.image }}.Dockerfile
103+
file: build/${{ matrix.image.name }}.Dockerfile
102104
labels: org.opencontainers.image.source=https://github.com/${{ env.REPO_NAME }}
103105
# Cache from/to the current branch of the current repo as the primary cache key.
104106
# Cache from the default branch of the current repo so branches can have cache hits.
105107
# Cache from the default branch of the canonical repo so forks can have cache hits.
106108
# Ignore errors on cache writes so CI of forks works without a valid GHCR config.
107109
cache-from: |
108-
type=registry,ref=ghcr.io/${{ env.REPO_NAME }}:${{ matrix.image }}-${{ env.GIT_REF_NAME }}
109-
type=registry,ref=ghcr.io/${{ env.REPO_NAME }}:${{ matrix.image }}-main
110-
type=registry,ref=ghcr.io/astral-sh/python-build-standalone:${{ matrix.image }}-main
110+
type=registry,ref=ghcr.io/${{ env.REPO_NAME }}:${{ matrix.image.name }}-${{ env.GIT_REF_NAME }}
111+
type=registry,ref=ghcr.io/${{ env.REPO_NAME }}:${{ matrix.image.name }}-main
112+
type=registry,ref=ghcr.io/astral-sh/python-build-standalone:${{ matrix.image.name }}-main
111113
cache-to: |
112-
type=registry,ref=ghcr.io/${{ env.REPO_NAME }}:${{ matrix.image }}-${{ env.GIT_REF_NAME }},ignore-error=true
114+
type=registry,ref=ghcr.io/${{ env.REPO_NAME }}:${{ matrix.image.name }}-${{ env.GIT_REF_NAME }},ignore-error=true
113115
outputs: |
114-
type=docker,dest=build/image-${{ matrix.image }}.tar
116+
type=docker,dest=build/image-${{ matrix.image.name }}.tar
115117
116118
- name: Compress Image
117119
run: |
118-
echo ${{ steps.build-image.outputs.imageid }} > build/image-${{ matrix.image }}
120+
echo ${{ steps.build-image.outputs.imageid }} > build/image-${{ matrix.image.name }}
119121
zstd -v -T0 -6 --rm build/image-*.tar
120122
121123
- name: Upload Docker Image
122124
uses: actions/upload-artifact@v4
123125
with:
124-
name: image-${{ matrix.image }}
126+
name: image-${{ matrix.image.name }}
125127
path: build/image-*
126128

127129
generate-matrix:

ci-runners.yaml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@ depot-ubuntu-22.04:
55
platform: linux
66
free: false
77

8-
# TODO: Enable this runner to perform native builds for aarch64
9-
# depot-ubuntu-22.04-arm:
10-
# arch: aarch64
11-
# platform: linux
12-
# free: false
8+
depot-ubuntu-22.04-arm:
9+
arch: aarch64
10+
platform: linux
11+
free: false
1312

1413
depot-macos-latest:
1514
arch: x86_64

ci-targets.yaml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,10 @@ linux:
1313
- "3.14"
1414
build_options:
1515
- debug
16-
- noopt
17-
- lto
16+
- pgo+lto
1817
build_options_conditional:
1918
- options:
2019
- freethreaded+debug
21-
- freethreaded+noopt
22-
- freethreaded+lto
20+
- freethreaded+pgo+lto
2321
minimum-python-version: "3.13"
2422

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Debian Stretch.
2+
FROM debian@sha256:c5c5200ff1e9c73ffbf188b4a67eb1c91531b644856b4aefe86a58d2f0cb05be
3+
MAINTAINER Gregory Szorc <[email protected]>
4+
5+
RUN groupadd -g 1000 build && \
6+
useradd -u 1000 -g 1000 -d /build -s /bin/bash -m build && \
7+
mkdir /tools && \
8+
chown -R build:build /build /tools
9+
10+
ENV HOME=/build \
11+
SHELL=/bin/bash \
12+
USER=build \
13+
LOGNAME=build \
14+
HOSTNAME=builder \
15+
DEBIAN_FRONTEND=noninteractive
16+
17+
CMD ["/bin/bash", "--login"]
18+
WORKDIR '/build'
19+
20+
RUN for s in debian_stretch debian_stretch-updates debian-security_stretch/updates; do \
21+
echo "deb http://snapshot.debian.org/archive/${s%_*}/20230423T032736Z/ ${s#*_} main"; \
22+
done > /etc/apt/sources.list && \
23+
( echo 'quiet "true";'; \
24+
echo 'APT::Get::Assume-Yes "true";'; \
25+
echo 'APT::Install-Recommends "false";'; \
26+
echo 'Acquire::Check-Valid-Until "false";'; \
27+
echo 'Acquire::Retries "5";'; \
28+
) > /etc/apt/apt.conf.d/99cpython-portable
29+
30+
# apt iterates all available file descriptors up to rlim_max and calls
31+
# fcntl(fd, F_SETFD, FD_CLOEXEC). This can result in millions of system calls
32+
# (we've seen 1B in the wild) and cause operations to take seconds to minutes.
33+
# Setting a fd limit mitigates.
34+
#
35+
# Attempts at enforcing the limit globally via /etc/security/limits.conf and
36+
# /root/.bashrc were not successful. Possibly because container image builds
37+
# don't perform a login or use a shell the way we expect.
38+
RUN ulimit -n 10000 && apt-get update

cpython-unix/build-main.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,13 @@
2929

3030
def main():
3131
if sys.platform == "linux":
32-
host_platform = "linux64"
33-
default_target_triple = "x86_64-unknown-linux-gnu"
32+
machine = platform.machine()
33+
if machine == "aarch64":
34+
host_platform = "linux_aarch64"
35+
default_target_triple = "aarch64-unknown-linux-gnu"
36+
else:
37+
host_platform = "linux64"
38+
default_target_triple = "x86_64-unknown-linux-gnu"
3439
elif sys.platform == "darwin":
3540
host_platform = "macos"
3641
machine = platform.machine()
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{% include 'base.debian9.Dockerfile' %}
2+
3+
RUN ulimit -n 10000 && apt-get install \
4+
bzip2 \
5+
file \
6+
libc6-dev \
7+
libffi-dev \
8+
make \
9+
patch \
10+
perl \
11+
pkg-config \
12+
tar \
13+
xz-utils \
14+
unzip \
15+
zip \
16+
zlib1g-dev

cpython-unix/build.py

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,17 @@ def add_target_env(env, build_platform, target_triple, build_env):
117117
extra_host_ldflags = []
118118

119119
if build_platform == "linux64":
120-
env["BUILD_TRIPLE"] = "x86_64-unknown-linux-gnu"
120+
machine = platform.machine()
121121

122-
env["TARGET_TRIPLE"] = (
123-
target_triple.replace("x86_64_v2-", "x86_64-")
124-
.replace("x86_64_v3-", "x86_64-")
125-
.replace("x86_64_v4-", "x86_64-")
126-
)
122+
if machine == "aarch64":
123+
env["BUILD_TRIPLE"] = "aarch64-unknown-linux-gnu"
124+
else:
125+
env["BUILD_TRIPLE"] = "x86_64-unknown-linux-gnu"
126+
env["TARGET_TRIPLE"] = (
127+
target_triple.replace("x86_64_v2-", "x86_64-")
128+
.replace("x86_64_v3-", "x86_64-")
129+
.replace("x86_64_v4-", "x86_64-")
130+
)
127131

128132
# This will make x86_64_v2, etc count as cross-compiling. This is
129133
# semantically correct, since the current machine may not support
@@ -516,10 +520,12 @@ def python_build_info(
516520

517521
binary_suffix = ""
518522

519-
if platform == "linux64":
523+
if platform in ("linux64", "linux_aarch64"):
524+
arch = "aarch64" if platform == "linux_aarch64" else "x86_64"
525+
520526
bi["core"]["static_lib"] = (
521-
"install/lib/python{version}/config-{version}{binary_suffix}-x86_64-linux-gnu/libpython{version}{binary_suffix}.a".format(
522-
version=version, binary_suffix=binary_suffix
527+
"install/lib/python{version}/config-{version}{binary_suffix}-{arch}-linux-gnu/libpython{version}{binary_suffix}.a".format(
528+
version=version, binary_suffix=binary_suffix, arch=arch,
523529
)
524530
)
525531

@@ -599,7 +605,7 @@ def python_build_info(
599605
if lib.startswith("-l"):
600606
lib = lib[2:]
601607

602-
if platform == "linux64" and lib not in linux_allowed_system_libraries:
608+
if platform in ("linux64", "linux_aarch64") and lib not in linux_allowed_system_libraries:
603609
raise Exception("unexpected library in LIBS (%s): %s" % (libs, lib))
604610
elif platform == "macos" and lib not in MACOS_ALLOW_SYSTEM_LIBRARIES:
605611
raise Exception("unexpected library in LIBS (%s): %s" % (libs, lib))
@@ -867,7 +873,7 @@ def build_cpython(
867873
extension_module_loading = ["builtin"]
868874
crt_features = []
869875

870-
if host_platform == "linux64":
876+
if host_platform in ("linux64", "linux_aarch64"):
871877
if "static" in parsed_build_options:
872878
crt_features.append("static")
873879
else:

cpython-unix/targets.yml

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -153,19 +153,24 @@ aarch64-apple-ios:
153153

154154
aarch64-unknown-linux-gnu:
155155
host_platforms:
156-
- linux64
156+
- linux_aarch64
157157
pythons_supported:
158158
- '3.9'
159159
- '3.10'
160160
- '3.11'
161161
- '3.12'
162162
- '3.13'
163163
- '3.14'
164-
docker_image_suffix: .cross
165-
host_cc: /usr/bin/x86_64-linux-gnu-gcc
166-
host_cxx: /usr/bin/x86_64-linux-gnu-g++
167-
target_cc: /usr/bin/aarch64-linux-gnu-gcc
168-
target_cxx: /usr/bin/aarch64-linux-gnu-g++
164+
docker_image_suffix: .debian9
165+
need_toolchain: true
166+
host_cc: clang
167+
host_cxx: clang++
168+
target_cc: clang
169+
target_cxx: clang++
170+
target_cflags:
171+
- '-fvisibility=hidden'
172+
# Needed to prevent BOLT from crashing.
173+
- '-fdebug-default-version=4'
169174
needs:
170175
- autoconf
171176
- bdb
@@ -191,6 +196,7 @@ aarch64-unknown-linux-gnu:
191196
- xz
192197
- zlib
193198
openssl_target: linux-aarch64
199+
bolt_capable: true
194200

195201
arm64-apple-tvos:
196202
host_platforms:

docs/running.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ Common configurations include:
143143
Profile guided optimization.
144144

145145
Starting with CPython 3.12, BOLT is also applied alongside traditional
146-
PGO on platforms supporting BOLT. (Currently just Linux x86-64.)
146+
PGO on platforms supporting BOLT. (Currently just Linux aarch64 and x86-64.)
147147

148148
``lto``
149149
Link-time optimization.

pythonbuild/downloads.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,13 @@
186186
"version": "14.0.3+20220508",
187187
},
188188
# Remember to update LLVM_URL in src/release.rs whenever upgrading.
189+
"llvm-20-aarch64-linux": {
190+
"url": "https://github.com/indygreg/toolchain-tools/releases/download/toolchain-bootstrap%2F20250511/llvm-20.1.4+20250511-gnu_only-aarch64-unknown-linux-gnu.tar.zst",
191+
"size": 255946687,
192+
"sha256": "e70753f294b8f83fffbaf07af36857c27ceaef0291cb10f724ada6af11b0a5bc",
193+
"version": "20.1.4+20250511",
194+
},
195+
# Remember to update LLVM_URL in src/release.rs whenever upgrading.
189196
"llvm-20-x86_64-linux": {
190197
"url": "https://github.com/indygreg/toolchain-tools/releases/download/toolchain-bootstrap%2F20250511/llvm-20.1.4+20250511-gnu_only-x86_64-unknown-linux-gnu.tar.zst",
191198
"size": 299883811,

0 commit comments

Comments
 (0)