Skip to content

Commit 1d266a9

Browse files
authored
Linux aarch64 wheels (#84)
* Mac ARM wheels * Linux aarch64 wheels Uses cibuildwheel's QEMU mechanism.
1 parent 7bc3e93 commit 1d266a9

File tree

3 files changed

+101
-20
lines changed

3 files changed

+101
-20
lines changed

.github/workflows/wheels.yml

+46-16
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232

3333

3434
build_wheels:
35-
name: Wheels on ${{ matrix.platform_id }} - ${{ matrix.os }}
35+
name: Wheels - ${{ matrix.cibw_archs }} - ${{ matrix.os }}
3636
runs-on: ${{ matrix.os }}
3737
strategy:
3838
fail-fast: false
@@ -43,17 +43,30 @@ jobs:
4343
- os: windows-latest
4444
python-version: "3.8"
4545
platform_id: win_amd64
46+
cibw_archs: "auto"
4647

4748
# Linux 64 bit manylinux2014
4849
- os: ubuntu-latest
4950
python-version: "3.8"
5051
platform_id: manylinux_x86_64
5152
manylinux_image: manylinux2014
53+
cibw_archs: "native"
5254

53-
# Use x86 macOS runner to build both x86 and ARM. GitHub does not offer M1/M2 yet (only self-hosted).
55+
# Linux 64 bit manylinux2014 for aarch64
56+
# Separate runner because this requires emulation (only x86 runners are available) and is very slow.
57+
- os: ubuntu-latest
58+
python-version: "3.8"
59+
platform_id: manylinux_x86_64
60+
manylinux_image: manylinux2014
61+
cibw_archs: "aarch64"
62+
63+
# Use x86 macOS runner to build both x86 and ARM.
64+
# GitHub does not offer Apple Silicon yet (only for self-hosted).
65+
# See https://github.com/github/roadmap/issues/528
5466
- os: macos-latest
5567
python-version: "3.8"
5668
platform_id: macosx_x86_64
69+
cibw_archs: "x86_64 arm64"
5770

5871
steps:
5972
- uses: actions/checkout@v3
@@ -64,16 +77,31 @@ jobs:
6477
with:
6578
python-version: ${{ matrix.python-version }}
6679

67-
# - name: Install tools (macOS)
68-
# if: contains(matrix.os, 'macos')
69-
# # Install coreutils which includes `nproc` used by `make -j` in suitesparse.sh
70-
# #
71-
# # GitHub actions comes with libomp already installed, but for its native arch only. Must build universal one
72-
# # manually so that both x86 and arm builds can be built.
73-
# run: |
74-
# brew install coreutils
75-
# brew install libomp
76-
# sh add_arm_to_libomp_dylib.sh
80+
# aarch64 Linux builds are cross-compiled on x86 runners using emulation
81+
# see https://cibuildwheel.readthedocs.io/en/stable/faq/#emulation
82+
- name: Setup QEMU (for aarch64)
83+
if: matrix.cibw_archs == 'aarch64'
84+
uses: docker/setup-qemu-action@v2
85+
with:
86+
platforms: arm64
87+
88+
- name: Setup env (for aarch64)
89+
if: matrix.cibw_archs == 'aarch64'
90+
# Ask suitesparse.sh to compile faster by optimizing fewer types. Otherwise, the build takes too long to finish
91+
# in 6 hour limit.
92+
run: |
93+
echo "SUITESPARSE_FAST_BUILD=1" >> $GITHUB_ENV
94+
95+
- name: Install tools (macOS)
96+
if: contains(matrix.os, 'macos')
97+
# Install coreutils which includes `nproc` used by `make -j` in suitesparse.sh
98+
#
99+
# GitHub actions comes with libomp already installed, but for its native arch only. Must build universal one
100+
# manually so that both x86 and arm builds can be built.
101+
run: |
102+
brew install coreutils
103+
brew install libomp
104+
sh add_arm_to_libomp_dylib.sh
77105
78106
- name: Build Wheels
79107
env:
@@ -83,6 +111,8 @@ jobs:
83111
# Build SuiteSparse
84112
CIBW_BEFORE_ALL: bash suitesparse.sh ${{ github.ref }}
85113

114+
CIBW_ENVIRONMENT_LINUX: SUITESPARSE_FAST_BUILD=${{ env.SUITESPARSE_FAST_BUILD }}
115+
86116
# CMAKE_GNUtoMS=ON asks suitesparse.sh to build libraries in MSVC style on Windows.
87117
CIBW_ENVIRONMENT_WINDOWS: CMAKE_GNUtoMS=ON GRAPHBLAS_PREFIX="C:/GraphBLAS"
88118

@@ -92,11 +122,11 @@ jobs:
92122
# Uncomment to only build CPython wheels
93123
# CIBW_BUILD: "cp*"
94124

95-
# macOS: build x86_64 and arm64
96-
#CIBW_ARCHS_MACOS: "x86_64 arm64"
125+
# Architectures to build specified in matrix
126+
CIBW_ARCHS: ${{ matrix.cibw_archs }}
97127

98-
# No 32-bit builds
99-
CIBW_SKIP: "*-win32 *_i686 *musl*"
128+
# No 32-bit builds, no musllinux, no PyPy aarch64 (only due to build speed, numpy does not ship aarch64 pypy wheels)
129+
CIBW_SKIP: "*-win32 *_i686 *musl* pp*aarch64"
100130

101131
# Use delvewheel on Windows.
102132
# This copies graphblas.dll into the wheel. "repair" in cibuildwheel parlance includes copying any shared

add_arm_to_libomp_dylib.sh

+33-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,23 @@
11
#!/bin/sh
2+
# Construct a universal2 version of homebrew's libomp.
3+
#
4+
# Homebrew's libomp works well to patch Apple clang's missing OpenMP support. The problem is a combination of:
5+
# - Brew installs libomp built for x86 *or* ARM, matching the architecture of the machine it is running on.
6+
# - GitHub Actions only has x86 runners as of now. Check back in Q4 2023. https://github.com/github/roadmap/issues/528
7+
# - The linker will select the first found libomp, and if that version does not include the expected architecture then
8+
# linking will fail.
9+
#
10+
# One solution is to build a universal2 version of libomp that includes both architectures. That's what this script
11+
# does. It adds the ARM version of libomp to the x86 version.
12+
#
13+
# This script assumes it is running on x86 with x86 libomp already installed.
14+
15+
if [ "$(arch)" != "x86_64" ] && [ "$(arch)" != "i386" ]; then
16+
echo "Not running on x86 as expected. Running on:"
17+
arch
18+
echo "If the above says arm64 then this hack is no longer necessary. Remove this script from the build."
19+
exit 1;
20+
fi
221

322
#mkdir x86lib
423
mkdir armlib
@@ -11,8 +30,21 @@ brew fetch --force --bottle-tag=arm64_big_sur libomp
1130
#tar -xzf $(brew --cache --bottle-tag=x86_64_monterey libomp) --strip-components 2 -C x86lib
1231
tar -xzf $(brew --cache --bottle-tag=arm64_big_sur libomp) --strip-components 2 -C armlib
1332

14-
# merge
33+
# ARM and x86 dylibs have different install names due to different brew install directories.
34+
# The x86 install name will be expected so make the ARM install name match.
35+
X86_INSTALL_NAME="$(otool -X -D $(brew --prefix libomp)/lib/libomp.dylib)"
36+
install_name_tool -id "${X86_INSTALL_NAME}" armlib/lib/libomp.dylib
37+
codesign --force -s - armlib/lib/libomp.dylib
38+
39+
# merge the downloaded (arm) libomp with the already installed (x86) libomp to create a universal libomp
1540
lipo armlib/lib/libomp.dylib $(brew --prefix libomp)/lib/libomp.dylib -output libomp.dylib -create
41+
42+
# print contents of universal library for reference
43+
otool -arch all -L libomp.dylib
44+
45+
# replace the x86-only libomp with the newly-created universal one
1646
cp -f libomp.dylib $(brew --prefix libomp)/lib
47+
48+
# clean up
1749
rm libomp.dylib
1850
rm -rf armlib

suitesparse.sh

+22-3
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,8 @@ if [ -n "${BREW_LIBOMP}" ]; then
2525
cmake_params+=(-DOpenMP_libomp_LIBRARY="omp")
2626
export LDFLAGS="-L$(brew --prefix libomp)/lib"
2727

28-
export CFLAGS="-arch x86_64"
29-
# # build both x86 and ARM
30-
# export CFLAGS="-arch x86_64 -arch arm64"
28+
# build both x86 and ARM
29+
export CFLAGS="-arch x86_64 -arch arm64"
3130
fi
3231

3332
if [ -n "${CMAKE_GNUtoMS}" ]; then
@@ -63,6 +62,26 @@ echo "#define GxB_NO_UINT32 1" >> ../Source/GB_control.h
6362
# echo "#define GxB_NO_UINT64 1" >> ../Source/GB_control.h #
6463
echo "#define GxB_NO_UINT8 1" >> ../Source/GB_control.h
6564

65+
if [ -n "${SUITESPARSE_FAST_BUILD}" ]; then
66+
echo "suitesparse.sh: Fast build requested."
67+
# Disable optimizing even more types. This is for builds that don't finish in runner resource limits,
68+
# such as emulated aarm64.
69+
70+
echo "#define GxB_NO_BOOL 1" >> ../Source/GB_control.h
71+
# echo "#define GxB_NO_FP32 1" >> ../Source/GB_control.h
72+
# echo "#define GxB_NO_FP64 1" >> ../Source/GB_control.h
73+
echo "#define GxB_NO_FC32 1" >> ../Source/GB_control.h
74+
echo "#define GxB_NO_FC64 1" >> ../Source/GB_control.h
75+
echo "#define GxB_NO_INT16 1" >> ../Source/GB_control.h
76+
echo "#define GxB_NO_INT32 1" >> ../Source/GB_control.h
77+
# echo "#define GxB_NO_INT64 1" >> ../Source/GB_control.h
78+
# echo "#define GxB_NO_INT8 1" >> ../Source/GB_control.h
79+
echo "#define GxB_NO_UINT16 1" >> ../Source/GB_control.h
80+
echo "#define GxB_NO_UINT32 1" >> ../Source/GB_control.h
81+
echo "#define GxB_NO_UINT64 1" >> ../Source/GB_control.h
82+
echo "#define GxB_NO_UINT8 1" >> ../Source/GB_control.h
83+
fi
84+
6685
# Disable all Source/Generated2 kernels. For workflow development only.
6786
#cmake_params+=(-DCMAKE_CUDA_DEV=1)
6887

0 commit comments

Comments
 (0)