diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 0799d87..3abc58a 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -64,16 +64,16 @@ jobs: with: python-version: ${{ matrix.python-version }} -# - name: Install tools (macOS) -# if: contains(matrix.os, 'macos') -# # Install coreutils which includes `nproc` used by `make -j` in suitesparse.sh -# # -# # GitHub actions comes with libomp already installed, but for its native arch only. Must build universal one -# # manually so that both x86 and arm builds can be built. -# run: | -# brew install coreutils -# brew install libomp -# sh add_arm_to_libomp_dylib.sh + - name: Install tools (macOS) + if: contains(matrix.os, 'macos') + # Install coreutils which includes `nproc` used by `make -j` in suitesparse.sh + # + # GitHub actions comes with libomp already installed, but for its native arch only. Must build universal one + # manually so that both x86 and arm builds can be built. + run: | + brew install coreutils + brew install libomp + sh add_arm_to_libomp_dylib.sh - name: Build Wheels env: @@ -92,8 +92,8 @@ jobs: # Uncomment to only build CPython wheels # CIBW_BUILD: "cp*" - # macOS: build x86_64 and arm64 - #CIBW_ARCHS_MACOS: "x86_64 arm64" + # macOS: build both x86_64 and arm64. + CIBW_ARCHS_MACOS: "x86_64 arm64" # No 32-bit builds CIBW_SKIP: "*-win32 *_i686 *musl*" diff --git a/add_arm_to_libomp_dylib.sh b/add_arm_to_libomp_dylib.sh index 8492c7c..5109f98 100755 --- a/add_arm_to_libomp_dylib.sh +++ b/add_arm_to_libomp_dylib.sh @@ -1,4 +1,23 @@ #!/bin/sh +# Construct a universal2 version of homebrew's libomp. +# +# Homebrew's libomp works well to patch Apple clang's missing OpenMP support. The problem is a combination of: +# - Brew installs libomp built for x86 *or* ARM, matching the architecture of the machine it is running on. +# - GitHub Actions only has x86 runners as of now. Check back in Q4 2023. https://github.com/github/roadmap/issues/528 +# - The linker will select the first found libomp, and if that version does not include the expected architecture then +# linking will fail. +# +# One solution is to build a universal2 version of libomp that includes both architectures. That's what this script +# does. It adds the ARM version of libomp to the x86 version. +# +# This script assumes it is running on x86 with x86 libomp already installed. + +if [ "$(arch)" != "x86_64" ] && [ "$(arch)" != "i386" ]; then + echo "Not running on x86 as expected. Running on:" + arch + echo "If the above says arm64 then this hack is no longer necessary. Remove this script from the build." + exit 1; +fi #mkdir x86lib mkdir armlib @@ -11,8 +30,21 @@ brew fetch --force --bottle-tag=arm64_big_sur libomp #tar -xzf $(brew --cache --bottle-tag=x86_64_monterey libomp) --strip-components 2 -C x86lib tar -xzf $(brew --cache --bottle-tag=arm64_big_sur libomp) --strip-components 2 -C armlib -# merge +# ARM and x86 dylibs have different install names due to different brew install directories. +# The x86 install name will be expected so make the ARM install name match. +X86_INSTALL_NAME="$(otool -X -D $(brew --prefix libomp)/lib/libomp.dylib)" +install_name_tool -id "${X86_INSTALL_NAME}" armlib/lib/libomp.dylib +codesign --force -s - armlib/lib/libomp.dylib + +# merge the downloaded (arm) libomp with the already installed (x86) libomp to create a universal libomp lipo armlib/lib/libomp.dylib $(brew --prefix libomp)/lib/libomp.dylib -output libomp.dylib -create + +# print contents of universal library for reference +otool -arch all -L libomp.dylib + +# replace the x86-only libomp with the newly-created universal one cp -f libomp.dylib $(brew --prefix libomp)/lib + +# clean up rm libomp.dylib rm -rf armlib diff --git a/suitesparse.sh b/suitesparse.sh index 26b29fb..b478ab5 100755 --- a/suitesparse.sh +++ b/suitesparse.sh @@ -25,9 +25,8 @@ if [ -n "${BREW_LIBOMP}" ]; then cmake_params+=(-DOpenMP_libomp_LIBRARY="omp") export LDFLAGS="-L$(brew --prefix libomp)/lib" - export CFLAGS="-arch x86_64" -# # build both x86 and ARM -# export CFLAGS="-arch x86_64 -arch arm64" + # build both x86 and ARM + export CFLAGS="-arch x86_64 -arch arm64" fi if [ -n "${CMAKE_GNUtoMS}" ]; then