diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml new file mode 100644 index 0000000..083f965 --- /dev/null +++ b/.github/workflows/build_wheels.yml @@ -0,0 +1,168 @@ +# Workflow to build and test wheels. +name: Wheel builder + +on: + push: + tags: + # Trigger on version tags (e.g., v1.0.0) + - "v[0-9].[0-9].[0-9]*" + - "[0-9].[0-9].[0-9]*" + # Trigger on pre-release tags (e.g., v1.0.0-alpha.1) + - "v[0-9].[0-9].[0-9]*-*" + - "[0-9].[0-9].[0-9]*-*" +env: + # The name of the package to be published to PyPI and TestPyPI. + PYPI_NAME: qc-PyCI + +permissions: + contents: read # to fetch code (actions/checkout) + +# ensure that only one wheel builder runs at a time +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + build_wheels: + name: Wheel, ${{ matrix.python[0] }}-${{ matrix.buildplat[1] }} + ${{ matrix.buildplat[2] }} ${{ matrix.buildplat[3] }} + ${{ matrix.buildplat[4] }} + runs-on: ${{ matrix.buildplat[0] }} + + strategy: + # Ensure that a wheel builder finishes even if another fails + fail-fast: false + matrix: + # If need to exclude several configurations see: + # https://github.com/github/feedback/discussions/7835#discussioncomment-1769026 + buildplat: + # Different architectures are in different jobs because the need of different compiler configurations + - [ubuntu-22.04, manylinux, x86_64, "", ""] + - [ubuntu-22.04, musllinux, x86_64, "", ""] + - [ubuntu-24.04-arm, manylinux, aarch64, "", ""] + - [ubuntu-24.04-arm, musllinux, aarch64, "", ""] + # - [macos-13, macosx, x86_64, openblas, "10.13"] + # - [macos-13, macosx, x86_64, accelerate, "14.0"] + # - [macos-14, macosx, arm64, openblas, "12.3"] + # - [macos-14, macosx, arm64, accelerate, "14.0"] + # - [windows-2019, win, AMD64, "", ""] + # python[0] is the python version of the wheel and python[1] is the python version of the configuration + python: [["cp39", "3.9"],["cp310", "3.10"], ["cp311", "3.11"], ["cp312", "3.12"], ["cp313", "3.13"], ["cp313t", "3.13"]] + + env: + # set 32-bit flag accessable in the build script + IS_32_BIT: ${{ matrix.buildplat[2] == 'x86' }} + + steps: + - name: Checkout pyci + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: win_amd64 - mingw-w64 + # TODO: finish the windows build actions + run: | + # mingw-w64 + if [[ ${{ matrix.buildplat[0] }} == 'windows-2019' && ${{ matrix.buildplat[2] }} == 'AMD64' ]]; then + choco install -y mingw + export PATH="/c/Program Files/mingw-w64/x86_64-8.1.0-posix-seh-rt_v6-rev0/mingw64/bin:$PATH" + echo "PATH=$PATH" >> $GITHUB_ENV + fi + + - name: windows - set PKG_CONFIG_PATH + if: ${{ runner.os == 'Windows' }} + run: | + $env:CIBW = "${{ github.workspace }}" + # It seems somewhere in the env passing, `\` is not + # passed through, so convert it to '/' + $env:CIBW=$env:CIBW.replace("\","/") + echo "CIBW_ENVIRONMENT=PKG_CONFIG_PATH=$env:CIBW" >> $env:GITHUB_ENV + + - name: Setup macOS + if: startsWith( matrix.buildplat[0], 'macos-' ) + run: | + if [[ ${{ matrix.buildplat[3] }} == 'accelerate' ]]; then + echo CIBW_CONFIG_SETTINGS=\"setup-args=-Dblas=accelerate\" >> "$GITHUB_ENV" + # Always use preinstalled gfortran for Accelerate builds + ln -s $(which gfortran-13) gfortran + export PATH=$PWD:$PATH + echo "PATH=$PATH" >> "$GITHUB_ENV" + LIB_PATH=$(dirname $(gfortran --print-file-name libgfortran.dylib)) + fi + if [[ ${{ matrix.buildplat[4] }} == '10.13' ]]; then + # 20241017 macos-13 images span Xcode 14.1-->15.2 + XCODE_VER='14.1' + else + XCODE_VER='15.2' + fi + CIBW="sudo xcode-select -s /Applications/Xcode_${XCODE_VER}.app" + echo "CIBW_BEFORE_ALL=$CIBW" >> $GITHUB_ENV + # setting SDKROOT necessary when using the gfortran compiler + # installed in cibw_before_build_macos.sh + sudo xcode-select -s /Applications/Xcode_${XCODE_VER}.app + CIBW="MACOSX_DEPLOYMENT_TARGET=${{ matrix.buildplat[4] }}\ + SDKROOT=$(xcrun --sdk macosx --show-sdk-path)\ + PKG_CONFIG_PATH=${{ github.workspace }}" + echo "CIBW_ENVIRONMENT=$CIBW" >> "$GITHUB_ENV" + + echo "REPAIR_PATH=$LIB_PATH" >> "$GITHUB_ENV" + + PREFIX=DYLD_LIBRARY_PATH="\$(dirname \$(gfortran --print-file-name libgfortran.dylib))" + # remove libgfortran from location used for linking (if any), to + # check wheel has bundled things correctly and all tests pass without + # needing installed gfortran + POSTFIX=" sudo rm -rf /opt/gfortran-darwin-x86_64-native &&\ + sudo rm -rf /usr/local/gfortran/lib" + CIBW="$PREFIX delocate-listdeps -d {wheel} && echo "-----------" &&\ + $PREFIX delocate-wheel -v $EXCLUDE --require-archs \ + {delocate_archs} -w {dest_dir} {wheel} && echo "-----------" &&\ + delocate-listdeps -d {dest_dir}/*.whl && echo "-----------" &&\ + $POSTFIX" + + # Rename x86 Accelerate wheel to test on macOS 13 runner + if [[ ${{ matrix.buildplat[0] }} == 'macos-13' && ${{ matrix.buildplat[4] }} == '14.0' ]]; then + CIBW+=" && mv {dest_dir}/\$(basename {wheel}) \ + {dest_dir}/\$(echo \$(basename {wheel}) | sed 's/14_0/13_0/')" + fi + + # macos-arm64-openblas wheels that target macos-12 need a + # MACOS_DEPLOYMENT_TARGET of 12.3 otherwise delocate complains. + # Unclear of cause, possibly build tool related. + # This results in wheels that have 12_3 in their name. Since Python + # has no concept of minor OS versions in packaging land rename the + # wheel back to 12. + if [[ ${{ matrix.buildplat[0] }} == 'macos-14' && ${{ matrix.buildplat[4] }} == '12.3' ]]; then + CIBW+=" && echo \$(ls {dest_dir}) && \ + mv {dest_dir}/*.whl \$(find {dest_dir} -type f -name '*.whl' | sed 's/12_3/12_0/')" + fi + echo "CIBW_REPAIR_WHEEL_COMMAND_MACOS=$CIBW" >> "$GITHUB_ENV" + + - name: Build wheels + uses: pypa/cibuildwheel@7940a4c0e76eb2030e473a5f864f291f63ee879b # v2.21.3 + env: + CIBW_BUILD: ${{ matrix.python[0] }}-${{ matrix.buildplat[1] }}* + CIBW_ARCHS: ${{ matrix.buildplat[2] }} + CIBW_PRERELEASE_PYTHONS: True + CIBW_FREE_THREADED_SUPPORT: True + + - name: Rename macOS wheels + if: startsWith( matrix.buildplat[0], 'macos-' ) + run: | + # macos-x86_64-accelerate wheels targeting macos-14 were renamed to 13 + # so they could be tested. Shift wheel name back to targeting 14. + if [[ ${{ matrix.buildplat[0] }} == 'macos-13' && ${{ matrix.buildplat[4] }} == '14.0' ]]; then + mv ./wheelhouse/*.whl $(find ./wheelhouse -type f -name '*.whl' | sed 's/13_0/14_0/') + fi + + - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + with: + path: ./wheelhouse/*.whl + name: ${{ matrix.python[0] }}-${{ matrix.buildplat[1] }} + ${{ matrix.buildplat[2] }} ${{ matrix.buildplat[3] }} + ${{ matrix.buildplat[4] }} + + # TODO: Test the build wheels + # - name: Test the build wheels + + # TODO: Upload the build wheels to the release + \ No newline at end of file diff --git a/Makefile b/Makefile index 6c500e7..15b3afa 100644 --- a/Makefile +++ b/Makefile @@ -74,7 +74,7 @@ all: pyci/_pyci.so.$(PYCI_VERSION) pyci/_pyci.so.$(VERSION_MAJOR) pyci/_pyci.so .PHONY: test test: - $(PYTHON) -m pytest -sv ./pyci + @set -e; $(PYTHON) -m pytest -sv ./pyci .PHONY: clean clean: @@ -104,13 +104,13 @@ pyci/_pyci.so: pyci/_pyci.so.$(PYCI_VERSION) ln -sf $(notdir $(<)) $(@) deps/eigen: - @git clone https://gitlab.com/libeigen/eigen.git $(@) + [ -d $@ ] || git clone https://gitlab.com/libeigen/eigen.git $@ deps/spectra: - @git clone https://github.com/yixuan/spectra.git $(@) + [ -d $@ ] || git clone https://github.com/yixuan/spectra.git $@ deps/parallel-hashmap: - @git clone https://github.com/greg7mdp/parallel-hashmap.git $(@) + [ -d $@ ] || git clone https://github.com/greg7mdp/parallel-hashmap.git $@ deps/pybind11: - @git clone https://github.com/pybind/pybind11.git $(@) + [ -d $@ ] || git clone https://github.com/pybind/pybind11.git $@ diff --git a/tools/wheels/cibw_before_build_linux.sh b/tools/wheels/cibw_before_build_linux.sh new file mode 100644 index 0000000..1866932 --- /dev/null +++ b/tools/wheels/cibw_before_build_linux.sh @@ -0,0 +1,53 @@ +set -xe + + +# # NIGHTLY_FLAG="" + +# # if [ "$#" -eq 1 ]; then +# # PROJECT_DIR="$1" +# # elif [ "$#" -eq 2 ] && [ "$1" = "--nightly" ]; then +# # NIGHTLY_FLAG="--nightly" +# # PROJECT_DIR="$2" +# # else +# # echo "Usage: $0 [--nightly] " +# # exit 1 +# # fi + +# printenv +# # Update license +# cat $PROJECT_DIR/tools/wheels/LICENSE_linux.txt >> $PROJECT_DIR/LICENSE.txt + +# TODO: delete along with enabling build isolation by unsetting +# CIBW_BUILD_FRONTEND when scipy is buildable under free-threaded +# python with a released version of cython +# FREE_THREADED_BUILD="$(python -c"import sysconfig; print(bool(sysconfig.get_config_var('Py_GIL_DISABLED')))")" +# if [[ $FREE_THREADED_BUILD == "True" ]]; then +# python -m pip install -U --pre pip +# python -m pip install -i https://pypi.anaconda.org/scientific-python-nightly-wheels/simple numpy cython +# python -m pip install git+https://github.com/serge-sans-paille/pythran +# python -m pip install ninja meson-python pybind11 +# fi + +yum install -y git make + +# download dependencies +# echo $PROJECT_DIR/deps +# mkdir -p $PROJECT_DIR/deps +# cd $PROJECT_DIR/deps +# git clone https://gitlab.com/libeigen/eigen.git +# git clone https://gitlab.com/libeigen/eigen.git +# git clone https://github.com/greg7mdp/parallel-hashmap.git +# git clone https://github.com/pybind/pybind11.git +# cd $PROJECT_DIR + +# install python dependencies +python -m pip install -U --pre pip +#TODO: doubt, what will happen if the numpy version is different from the one in the wheel +python -m pip install numpy +python -m pip install scipy +python -m pip install pytest + + +# compile library +make +make test \ No newline at end of file