Skip to content

Commit 0ebbd7d

Browse files
authored
Enable crosscompiling aarch64 python wheels under dockcross manylinux docker image (#8280)
This uses the dockcross manylinux docker image to enable crosscompiling aarch64 python wheels. The wheels built for aarch64 linux are excluded from the release for now, pending testing (pending, in PR#8392).
1 parent aedb8d7 commit 0ebbd7d

File tree

3 files changed

+91
-6
lines changed

3 files changed

+91
-6
lines changed

kokoro/release/python/linux/build_artifacts.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,31 @@ build_artifact_version() {
4949
sudo rm -rf $REPO_DIR
5050
}
5151

52+
build_crosscompiled_aarch64_artifact_version() {
53+
# crosscompilation is only supported with the dockcross manylinux2014 image
54+
DOCKER_IMAGE=dockcross/manylinux2014-aarch64
55+
PLAT=aarch64
56+
57+
# TODO(jtatermusch): currently when crosscompiling, "auditwheel repair" will be disabled
58+
# since auditwheel doesn't work for crosscomiled wheels.
59+
build_artifact_version $@
60+
}
61+
5262
build_artifact_version 2.7
5363
build_artifact_version 3.5
5464
build_artifact_version 3.6
5565
build_artifact_version 3.7
5666
build_artifact_version 3.8
5767
build_artifact_version 3.9
68+
69+
build_crosscompiled_aarch64_artifact_version 3.7
70+
build_crosscompiled_aarch64_artifact_version 3.8
71+
build_crosscompiled_aarch64_artifact_version 3.9
72+
73+
# Put the aarch64 manylinux wheels under the "unofficial" subdirectory.
74+
# Only wheels directly under the artifacts/ directory will be published
75+
# to PyPI as part of the protobuf release process.
76+
# TODO(jtattermusch): include aarch64 wheels in the release
77+
# once they are sufficiently tested.
78+
mkdir -p $ARTIFACT_DIR/unofficial
79+
mv $ARTIFACT_DIR/protobuf-*-manylinux*_aarch64.whl $ARTIFACT_DIR/unofficial

kokoro/release/python/linux/config.sh

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,38 @@ function pre_build {
66
# Runs in the root directory of this repository.
77
pushd protobuf
88

9-
yum install -y devtoolset-2-libatomic-devel
9+
if [ "$PLAT" == "aarch64" ]
10+
then
11+
local configure_host_flag="--host=aarch64"
12+
else
13+
yum install -y devtoolset-2-libatomic-devel
14+
fi
1015

11-
# Build protoc
16+
# Build protoc and libprotobuf
1217
./autogen.sh
13-
./configure
14-
15-
CXXFLAGS="-fPIC -g -O2" ./configure
18+
CXXFLAGS="-fPIC -g -O2" ./configure $configure_host_flag
1619
make -j8
1720

21+
if [ "$PLAT" == "aarch64" ]
22+
then
23+
# we are crosscompiling for aarch64 while running on x64
24+
# the simplest way for build_py command to be able to generate
25+
# the protos is by running the protoc process under
26+
# an emulator. That way we don't have to build a x64 version
27+
# of protoc. The qemu-arm emulator is already included
28+
# in the dockcross docker image.
29+
# Running protoc under an emulator is fast as protoc doesn't
30+
# really do much.
31+
32+
# create a simple shell wrapper that runs crosscompiled protoc under qemu
33+
echo '#!/bin/bash' >protoc_qemu_wrapper.sh
34+
echo 'exec qemu-aarch64 "../src/protoc" "$@"' >>protoc_qemu_wrapper.sh
35+
chmod ugo+x protoc_qemu_wrapper.sh
36+
37+
# PROTOC variable is by build_py step that runs under ./python directory
38+
export PROTOC=../protoc_qemu_wrapper.sh
39+
fi
40+
1841
# Generate python dependencies.
1942
pushd python
2043
python setup.py build_py
@@ -35,7 +58,20 @@ function bdist_wheel_cmd {
3558
# Modify build version
3659
pwd
3760
ls
38-
python setup.py bdist_wheel --cpp_implementation --compile_static_extension
61+
62+
if [ "$PLAT" == "aarch64" ]
63+
then
64+
# when crosscompiling for aarch64, --plat-name needs to be set explicitly
65+
# to end up with correctly named wheel file
66+
# the value should be manylinuxABC_ARCH and dockcross docker image
67+
# conveniently provides the value in the AUDITWHEEL_PLAT env
68+
local plat_name_flag="--plat-name=$AUDITWHEEL_PLAT"
69+
70+
# override the value of EXT_SUFFIX to make sure the crosscompiled .so files in the wheel have the correct filename suffix
71+
export PROTOCOL_BUFFERS_OVERRIDE_EXT_SUFFIX="$(python -c 'import sysconfig; print(sysconfig.get_config_var("EXT_SUFFIX").replace("-x86_64-linux-gnu.so", "-aarch64-linux-gnu.so"))')"
72+
fi
73+
74+
python setup.py bdist_wheel --cpp_implementation --compile_static_extension $plat_name_flag
3975
cp dist/*.whl $abs_wheelhouse
4076
}
4177

@@ -48,3 +84,12 @@ function run_tests {
4884
python --version
4985
python -c "from google.protobuf.pyext import _message;"
5086
}
87+
88+
if [ "$PLAT" == "aarch64" ]
89+
then
90+
# when crosscompiling for aarch64, override the default multibuild's repair_wheelhouse logic
91+
# since "auditwheel repair" doesn't work for crosscompiled wheels
92+
function repair_wheelhouse {
93+
echo "Skipping repair_wheelhouse since auditwheel requires build architecture to match wheel architecture."
94+
}
95+
fi

python/setup.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
from distutils.command.build_py import build_py as _build_py
2020
from distutils.command.clean import clean as _clean
21+
from distutils.command.build_ext import build_ext as _build_ext
2122
from distutils.spawn import find_executable
2223

2324
# Find the Protocol Compiler.
@@ -157,6 +158,22 @@ def find_package_modules(self, package, package_dir):
157158
if not any(fnmatch.fnmatchcase(fil, pat=pat) for pat in exclude)]
158159

159160

161+
class build_ext(_build_ext):
162+
def get_ext_filename(self, ext_name):
163+
# since python3.5, python extensions' shared libraries use a suffix that corresponds to the value
164+
# of sysconfig.get_config_var('EXT_SUFFIX') and contains info about the architecture the library targets.
165+
# E.g. on x64 linux the suffix is ".cpython-XYZ-x86_64-linux-gnu.so"
166+
# When crosscompiling python wheels, we need to be able to override this suffix
167+
# so that the resulting file name matches the target architecture and we end up with a well-formed
168+
# wheel.
169+
filename = _build_ext.get_ext_filename(self, ext_name)
170+
orig_ext_suffix = sysconfig.get_config_var("EXT_SUFFIX")
171+
new_ext_suffix = os.getenv("PROTOCOL_BUFFERS_OVERRIDE_EXT_SUFFIX")
172+
if new_ext_suffix and filename.endswith(orig_ext_suffix):
173+
filename = filename[:-len(orig_ext_suffix)] + new_ext_suffix
174+
return filename
175+
176+
160177
class test_conformance(_build_py):
161178
target = 'test_python'
162179
def run(self):
@@ -291,6 +308,7 @@ def get_option_from_sys_argv(option_str):
291308
cmdclass={
292309
'clean': clean,
293310
'build_py': build_py,
311+
'build_ext': build_ext,
294312
'test_conformance': test_conformance,
295313
},
296314
install_requires=install_requires,

0 commit comments

Comments
 (0)