diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index f9163e88..61164304 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -128,6 +128,16 @@ jobs: - name: Install packaging requirements run: python -m pip install --upgrade twine + - name: Install (pre-)compiled dependencies (macOS) + if: runner.os == 'macOS' + run: | + bash deploy/pkg/buildwheel_deps_macos.sh + if [[ $(uname -m) == 'arm64' ]]; then + echo "MACOSX_VERS_MIN=11.0" >> $GITHUB_ENV + elif [[ $(uname -m) == 'x86_64' ]]; then + echo "MACOSX_VERS_MIN=10.14" >> $GITHUB_ENV + fi + - name: Build built distribution uses: pypa/cibuildwheel@v2.18.1 with: @@ -135,16 +145,16 @@ jobs: output-dir: dist/ config-file: pyproject.toml env: - # Default to GCC compiler and OpenMP on macOS. - # HACK: Hardcode g++ compiler choice on recent GitHub macOS - # runners owing to unusual Homebrew paths. In the CIBW workflow, - # Homebrew 'fftw' formula installs g++-14 as a dependency which - # conflicts with the runner image. - CIBW_ENVIRONMENT_MACOS: >- - PY_CXX=$(brew --prefix)/bin/g++-14 - PY_CXXFLAGS=-I$(brew --prefix)/include - PY_LDFLAGS=-L$(brew --prefix)/lib - PY_OMP=1 + # # Default to GCC compiler and OpenMP on macOS. + # # HACK: Hardcode g++ compiler choice on recent GitHub macOS + # # runners owing to unusual Homebrew paths. In the CIBW workflow, + # # Homebrew 'fftw' formula installs g++-14 as a dependency which + # # conflicts with the runner image. + # CIBW_ENVIRONMENT_MACOS: >- + # PY_CXX=$(brew --prefix)/bin/g++-14 + # PY_CXXFLAGS=-I$(brew --prefix)/include + # PY_LDFLAGS=-L$(brew --prefix)/lib + # PY_OMP=1 # # Switch to LLVM compiler and OpenMP on macOS. # CIBW_ENVIRONMENT_MACOS: >- # PY_CXX=$(brew --prefix llvm@15)/bin/clang++ @@ -156,8 +166,18 @@ jobs: # HACK: Hardcode macOS deployment target to align with the # GitHub runner image version. In the CIBW workflow, # Homebrew-installed libraries and `delocate-wheel` enforce this. - MACOSX_DEPLOYMENT_TARGET: ${{ env.IMAGE_VERSION }} + # MACOSX_DEPLOYMENT_TARGET: ${{ env.IMAGE_VERSION }} + # SYSTEM_VERSION_COMPAT: '0' + MACOSX_DEPLOYMENT_TARGET: ${{ env.MACOSX_VERS_MIN }} SYSTEM_VERSION_COMPAT: '0' + CIBW_ENVIRONMENT_MACOS: >- + PY_CXX=clang++ + PY_CXXFLAGS="-mmacosx-version-min=${{ env.MACOSX_VERS_MIN }} -Itmp/deps/include" + PY_LDFLAGS=-Ltmp/deps/lib + PY_OMP=1 + PY_CXXFLAGS_OMP="-Xpreprocessor -fopenmp" + PY_LDFLAGS_OMP="-Xpreprocessor -fopenmp" + CIBW_BEFORE_ALL_MACOS: CIBW_REPAIR_WHEEL_COMMAND_MACOS: | delocate-listdeps {wheel} delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel} diff --git a/deploy/pkg/buildwheel_deps_macos.sh b/deploy/pkg/buildwheel_deps_macos.sh index 89586907..ed85b496 100644 --- a/deploy/pkg/buildwheel_deps_macos.sh +++ b/deploy/pkg/buildwheel_deps_macos.sh @@ -8,7 +8,7 @@ # ---- Configuration ----------------------------------------------------- -set -e -x +set -x # Detect macOS architecture and set deployment target. ARCH=$(uname -m) @@ -21,42 +21,111 @@ fi # Set up paths and directories. ROOT=$(pwd)/tmp SOURCE_DIR=${ROOT}/source -INSTALL_DIR=${ROOT}/build +BUILD_DIR=${ROOT}/build +INSTALL_DIR=${ROOT}/deps -mkdir -p ${SOURCE_DIR} ${INSTALL_DIR} +mkdir -p ${SOURCE_DIR} ${BUILD_DIR} ${INSTALL_DIR}/include ${INSTALL_DIR}/lib cd ${ROOT} +# ---- OMP --------------------------------------------------------------- + +# Download and extract OpenMP libraries. +if [[ $ARCH == "arm64" ]]; then + SOURCE_ARCH="osx-arm64" + SOURCE_FILENAME="llvm-openmp-18.1.5-hde57baf_0.conda" +elif [[ $ARCH == "x86_64" ]]; then + SOURCE_ARCH="osx-64" + SOURCE_FILENAME="llvm-openmp-18.1.5-h39e0ece_0.conda" +fi + +SOURCE_FILE=${SOURCE_DIR}/${SOURCE_FILENAME} + +mkdir -p ${SOURCE_DIR}/llvm-openmp ${BUILD_DIR}/llvm-openmp +curl -L https://anaconda.org/conda-forge/llvm-openmp/18.1.5/download/${SOURCE_ARCH}/${SOURCE_FILENAME} -o ${SOURCE_FILE} +tar -zxvf ${SOURCE_FILE} -C ${SOURCE_DIR}/llvm-openmp && rm ${SOURCE_FILE} +tar -zxvf ${SOURCE_DIR}/llvm-openmp/pkg-llvm-openmp-* -C ${BUILD_DIR}/llvm-openmp + +# Copy OpenMP libraries to installation directory. +cp -r ${BUILD_DIR}/llvm-openmp/include/. ${INSTALL_DIR}/include +cp -r ${BUILD_DIR}/llvm-openmp/lib/. ${INSTALL_DIR}/lib + + # ---- FFTW -------------------------------------------------------------- -# Download and extract FFTW source. -SOURCE_FILENAME="fftw-3.3.10.tar.gz" +# ------------------------------------------------------------------------ +# # Download and extract FFTW source. +# SOURCE_FILENAME="fftw-3.3.10.tar.gz" +# SOURCE_FILE=${ROOT}/${SOURCE_FILENAME} + +# curl -L ftp://ftp.fftw.org/pub/fftw/${SOURCE_FILENAME} -o ${SOURCE_FILE} +# tar -zxvf ${SOURCE_FILE} -C ${SOURCE_DIR} && rm ${SOURCE_FILE} + +# # Configure and build GSL. +# cd ${SOURCE_DIR}/fftw-* +# mkdir -p ${BUILD_DIR}/fftw +# CC=${CXX} ./configure --enable-shared --enable-openmp --prefix=${BUILD_DIR}/fftw +# make +# make install +# cd - +# ------------------------------------------------------------------------ + +# Download and extract FFTW libraries. +if [[ $ARCH == "arm64" ]]; then + SOURCE_ARCH="osx-arm64" + SOURCE_FILENAME="fftw-3.3.10-nompi_h3046061_108.conda" +elif [[ $ARCH == "x86_64" ]]; then + SOURCE_ARCH="osx-64" + SOURCE_FILENAME="fftw-3.3.10-nompi_h4fa670e_108.conda" +fi + SOURCE_FILE=${ROOT}/${SOURCE_FILENAME} -curl -L ftp://ftp.fftw.org/pub/fftw/${SOURCE_FILENAME} -o ${SOURCE_FILE} -tar -zxvf ${SOURCE_FILE} -C ${SOURCE_DIR} && rm ${SOURCE_FILE} +mkdir -p ${SOURCE_DIR}/fftw-3.3.10 ${BUILD_DIR}/fftw-3.3.10 +curl -L https://anaconda.org/conda-forge/fftw/3.3.10/download/${SOURCE_ARCH}/${SOURCE_FILENAME} -o ${SOURCE_FILENAME} +tar -zxvf ${SOURCE_FILE} -C ${SOURCE_DIR}/fftw-3.3.10 && rm ${SOURCE_FILE} +tar -zxvf ${SOURCE_DIR}/fftw-3.3.10/pkg-fftw-3.3.10-* -C ${BUILD_DIR}/fftw-3.3.10 + +# Move FFTW libraries to installation directory. +cp -r ${BUILD_DIR}/fftw-3.3.10/include/. ${INSTALL_DIR}/include +cp -r ${BUILD_DIR}/fftw-3.3.10/lib/. ${INSTALL_DIR}/lib -# Configure and build GSL. -cd ${SOURCE_DIR}/fftw-* -mkdir -p ${INSTALL_DIR}/fftw -CC=${CXX} ./configure --enable-shared --enable-openmp --prefix=${INSTALL_DIR}/fftw -make -make install -cd - # ---- GSL --------------------------------------------------------------- -# Download and extract GSL source. -SOURCE_FILENAME="gsl-latest.tar.gz" -SOURCE_FILE=${ROOT}/${SOURCE_FILENAME} +# ------------------------------------------------------------------------ +# # Download and extract GSL source. +# SOURCE_FILENAME="gsl-latest.tar.gz" +# SOURCE_FILE=${ROOT}/${SOURCE_FILENAME} + +# curl -L https://mirror.ibcp.fr/pub/gnu/gsl/${SOURCE_FILENAME} -o ${SOURCE_FILE} +# tar -zxvf ${SOURCE_FILE} -C ${SOURCE_DIR} && rm ${SOURCE_FILE} + +# # Configure and build GSL. +# cd ${SOURCE_DIR}/gsl-* +# mkdir -p ${BUILD_DIR}/gsl +# CC=${CXX} ./configure --prefix=${BUILD_DIR}/gsl +# make +# make install +# cd - +# ------------------------------------------------------------------------ + +# Download and extract GSL libraries. +if [[ $ARCH == "arm64" ]]; then + SOURCE_ARCH="osx-arm64" + SOURCE_FILENAME="gsl-2.7-h6e638da_0.tar.bz2" +elif [[ $ARCH == "x86_64" ]]; then + SOURCE_ARCH="osx-64" + SOURCE_FILENAME="gsl-2.7-h93259b0_0.tar.bz2" +fi + +SOURCE_FILE=${SOURCE_DIR}/${SOURCE_FILENAME} -curl -L https://mirror.ibcp.fr/pub/gnu/gsl/${SOURCE_FILENAME} -o ${SOURCE_FILE} -tar -zxvf ${SOURCE_FILE} -C ${SOURCE_DIR} && rm ${SOURCE_FILE} +mkdir -p ${BUILD_DIR}/gsl-2.7 +curl -L https://anaconda.org/conda-forge/gsl/2.7/download/${SOURCE_ARCH}/${SOURCE_FILENAME} -o ${SOURCE_FILE} +tar -zxvf ${SOURCE_FILE} -C ${BUILD_DIR}/gsl-2.7 -# Configure and build GSL. -cd ${SOURCE_DIR}/gsl-* -mkdir -p ${INSTALL_DIR}/gsl -CC=${CXX} ./configure --prefix=${INSTALL_DIR}/gsl -make -make install -cd - +# Copy GSL libraries to installation directory. +cp -r ${BUILD_DIR}/gsl-2.7/include/. ${INSTALL_DIR}/include +find ${BUILD_DIR}/gsl-2.7/lib/* -type d -exec cp -r {} ${INSTALL_DIR}/lib \; +find ${BUILD_DIR}/gsl-2.7/lib/* -type f \( -name "*.a" -or -name "libgsl.*.dylib" \) -exec cp -r {} ${INSTALL_DIR}/lib \;