| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License. |
| |
| name: Build Python Wheels |
| |
| concurrency: |
| group: python-wheel-${{ github.ref }} |
| cancel-in-progress: false |
| |
| on: |
| # Trigger the workflow on push or pull request, |
| # but only for the main branch |
| push: |
| branches: |
| - "main" |
| paths: |
| - 'cpp/**' |
| - 'python/**' |
| - '.github/workflows/python-wheel-workflow.yml' |
| pull_request: |
| branches: |
| - "main" |
| paths: |
| - 'cpp/**' |
| - 'python/**' |
| - '.github/workflows/python-wheel-workflow.yml' |
| workflow_dispatch: |
| inputs: |
| publish_pypi: |
| description: "Publish to PyPI (manual runs only)" |
| required: true |
| default: false |
| type: boolean |
| |
| jobs: |
| build_sdist: |
| name: Build source distribution |
| runs-on: ubuntu-24.04 |
| steps: |
| # fetch-depth 0 is required: since version |
| # is derived from git history |
| - uses: actions/checkout@v7 |
| with: |
| fetch-depth: 0 |
| |
| - name: Set up Python |
| uses: actions/setup-python@v6 |
| with: |
| python-version: "3.10" |
| |
| - name: Install dependencies |
| run: | |
| python -m pip install --upgrade pip |
| pip install build twine |
| |
| - name: Build sdist |
| run: | |
| # Bundle C++ sources into python/ so the sdist contains them. |
| rm -rf python/_bundled_cpp |
| cp -a cpp python/_bundled_cpp |
| cd python |
| python -m build --sdist |
| |
| - name: Store artifacts |
| uses: actions/upload-artifact@v4 |
| with: |
| name: sdist |
| path: python/dist/* |
| |
| build_wheels: |
| name: Build wheels on ${{ matrix.runner }} |
| runs-on: ${{ matrix.runner }} |
| needs: build_sdist |
| strategy: |
| matrix: |
| include: |
| # Job 1: Native x86_64 build |
| - platform: x86_64 |
| runner: ubuntu-24.04 # This is the standard x86_64 runner |
| os: linux |
| manylinux: _2_28 |
| |
| # Job 2: Native aarch64 build |
| - platform: aarch64 |
| runner: ubuntu-24.04-arm # This is a native ARM64 runner |
| os: linux |
| manylinux: _2_28 |
| |
| # Job 3: macOS arm64 build |
| # Pinned, not macos-latest: the runner OS sets the effective macOS |
| # deployment target (see CIBW_ENVIRONMENT_MACOS), so a mutable label |
| # could silently change the minimum macOS of the produced wheels. |
| - platform: arm64 |
| runner: macos-15 |
| os: macos |
| |
| env: |
| # Must match the pyarrow pinned in python/pyproject.toml (pyarrow == 24.0.0). |
| # Single source of truth for the Arrow version: it is used by the macOS |
| # steps (build from source) and by the Linux dnf pins below. |
| ARROW_VERSION: "24.0.0" |
| CIBW_PLATFORM: ${{ matrix.os }} |
| # Keep in sync with requires-python (>= 3.10) and the classifiers in |
| # python/pyproject.toml. pyarrow ~= 24.0 (the runtime Arrow provider) has |
| # wheels for cp310-cp314 on every platform we build. |
| CIBW_BUILD: "cp310-* cp311-* cp312-* cp313-* cp314-*" |
| CIBW_SKIP: "*-musllinux_*" |
| # Use uv on macOS to avoid transient GitHub rate limits when cibuildwheel |
| CIBW_BUILD_FRONTEND: ${{ matrix.os == 'macos' && 'build[uv]' || 'build' }} |
| # Pin arch to the matrix platform |
| CIBW_ARCHS: ${{ matrix.platform }} |
| CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.os == 'linux' && format('manylinux{0}', matrix.manylinux) || '' }} |
| CIBW_MANYLINUX_AARCH64_IMAGE: ${{ matrix.os == 'linux' && format('manylinux{0}', matrix.manylinux) || '' }} |
| CIBW_ENVIRONMENT_MACOS: ${{ matrix.os == 'macos' && 'MACOSX_DEPLOYMENT_TARGET=$(sw_vers -productVersion) CMAKE_OSX_DEPLOYMENT_TARGET=$(sw_vers -productVersion) CFLAGS=-mmacosx-version-min=$(sw_vers -productVersion) CXXFLAGS=-mmacosx-version-min=$(sw_vers -productVersion) LDFLAGS=-mmacosx-version-min=$(sw_vers -productVersion)' || '' }} |
| CIBW_BEFORE_BUILD_LINUX: | |
| set -eux |
| if [ -f /etc/system-release-cpe ]; then |
| ALMA_MAJOR="$(cut -d: -f5 /etc/system-release-cpe | cut -d. -f1)" |
| else |
| . /etc/os-release |
| ALMA_MAJOR="${VERSION_ID%%.*}" |
| fi |
| dnf install -y 'dnf-command(config-manager)' || dnf install -y dnf-plugins-core || true |
| # Follow official Apache Arrow install instructions for AlmaLinux/RHEL-family |
| dnf install -y epel-release || dnf install -y oracle-epel-release-el$(cut -d: -f5 /etc/system-release-cpe | cut -d. -f1) || dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-$(cut -d: -f5 /etc/system-release-cpe | cut -d. -f1).noarch.rpm |
| dnf install -y https://packages.apache.org/artifactory/arrow/almalinux/$(cut -d: -f5 /etc/system-release-cpe | cut -d. -f1)/apache-arrow-release-latest.rpm |
| dnf config-manager --set-enabled epel || : |
| dnf config-manager --set-enabled powertools || : |
| dnf config-manager --set-enabled crb || : |
| dnf config-manager --set-enabled ol$(cut -d: -f5 /etc/system-release-cpe | cut -d. -f1)_codeready_builder || : |
| dnf config-manager --set-enabled codeready-builder-for-rhel-$(cut -d: -f5 /etc/system-release-cpe | cut -d. -f1)-rhui-rpms || : |
| subscription-manager repos --enable codeready-builder-for-rhel-$(cut -d: -f5 /etc/system-release-cpe | cut -d. -f1)-$(arch)-rpms || : |
| # Pin the exact Arrow version: the wheel must be built against the same |
| # 24.0.0 that pyarrow == 24.0.0 ships (and that ARROW_VERSION above |
| # pins for macOS), because Arrow is resolved at runtime from the |
| # sibling pyarrow package instead of being bundled (see |
| # CIBW_REPAIR_WHEEL_COMMAND_* below). Installing the unpinned "latest" |
| # (e.g. 25.x) would produce libs whose SONAME does not exist in |
| # pyarrow, breaking every wheel. |
| dnf install -y arrow-devel-24.0.0 # For C++ |
| dnf install -y arrow-glib-devel-24.0.0 # For GLib (C) |
| dnf install -y arrow-dataset-devel-24.0.0 # For Apache Arrow Dataset C++ |
| dnf install -y arrow-dataset-glib-devel-24.0.0 # For Apache Arrow Dataset GLib (C) |
| dnf install -y arrow-acero-devel-24.0.0 # For Apache Arrow Acero C++ |
| dnf install -y arrow-flight-devel-24.0.0 # For Apache Arrow Flight C++ |
| dnf install -y arrow-flight-glib-devel-24.0.0 # For Apache Arrow Flight GLib (C) |
| dnf install -y arrow-flight-sql-devel-24.0.0 # For Apache Arrow Flight SQL C++ |
| dnf install -y arrow-flight-sql-glib-devel-24.0.0 # For Apache Arrow Flight SQL GLib (C) |
| dnf install -y parquet-devel-24.0.0 # For Apache Parquet C++ |
| dnf install -y parquet-glib-devel-24.0.0 # For Apache Parquet GLib (C) |
| # Do NOT bundle Arrow/Parquet/Dataset/Acero/Compute into the wheel: the |
| # python/CMakeLists.txt RPATH ($ORIGIN/../pyarrow on Linux, |
| # @loader_path/../pyarrow on macOS) resolves them at runtime from the |
| # sibling pyarrow package (pyarrow ~= 24.0). auditwheel/delocate keep the |
| # preserved RPATH entry and only bundle whatever else is left (e.g. re2, |
| # zstd from the manylinux container). |
| CIBW_REPAIR_WHEEL_COMMAND_LINUX: | |
| auditwheel repair --exclude libarrow.so.* --exclude libarrow_compute.so.* --exclude libarrow_dataset.so.* --exclude libarrow_acero.so.* --exclude libparquet.so.* -w {dest_dir} {wheel} |
| CIBW_REPAIR_WHEEL_COMMAND_MACOS: | |
| delocate-wheel --exclude libarrow --exclude libarrow_compute --exclude libarrow_dataset --exclude libarrow_acero --exclude libparquet -w {dest_dir} -v {wheel} |
| |
| steps: |
| - name: Checkout (needed for some tooling) |
| uses: actions/checkout@v7 |
| with: |
| # fetch-depth 0 is required: the sdist is extracted inside this |
| # checkout and setuptools-scm (search_parent_directories) picks up the |
| # parent .git. Without tags the wheel version collapses to "0.1.dev1" |
| # instead of reproducing the version baked into the sdist's PKG-INFO. |
| fetch-depth: 0 |
| |
| - name: Set up Python |
| uses: actions/setup-python@v6 |
| with: |
| python-version: "3.10" |
| |
| - name: Install Arrow (macOS) |
| if: matrix.os == 'macos' |
| shell: bash |
| run: | |
| set -euxo pipefail |
| brew bundle --file=cpp/Brewfile |
| # Homebrew's apache-arrow is built without ORC support, so it is |
| # removed here and Arrow is built from source in the next step (with |
| # ARROW_ORC), exactly like the python.yml CI workflow does. |
| brew uninstall apache-arrow || true |
| echo "CMAKE_PREFIX_PATH=$GITHUB_WORKSPACE/arrow-dist" >> "$GITHUB_ENV" |
| |
| - name: Cache Arrow build (macOS) |
| if: matrix.os == 'macos' |
| id: arrow-cache |
| uses: actions/cache@v6 |
| with: |
| path: ${{ github.workspace }}/arrow-dist |
| # The hash of this workflow is part of the key so that changing the |
| # Arrow CMake flags below invalidates the cached build. |
| key: arrow-${{ runner.os }}-${{ runner.arch }}-${{ env.ARROW_VERSION }}-${{ hashFiles('.github/workflows/python-wheel-workflow.yml') }} |
| |
| - name: Build Arrow from source (macOS, with ORC) |
| if: matrix.os == 'macos' && steps.arrow-cache.outputs.cache-hit != 'true' |
| run: | |
| set -euxo pipefail |
| curl -fLsS -o "apache-arrow-${ARROW_VERSION}.tar.gz" \ |
| "https://archive.apache.org/dist/arrow/arrow-${ARROW_VERSION}/apache-arrow-${ARROW_VERSION}.tar.gz" |
| tar xzf "apache-arrow-${ARROW_VERSION}.tar.gz" |
| cmake -S "apache-arrow-${ARROW_VERSION}/cpp" -B arrow-build \ |
| -DCMAKE_BUILD_TYPE=Release \ |
| -DCMAKE_INSTALL_PREFIX="${CMAKE_PREFIX_PATH}" \ |
| -DCMAKE_INSTALL_LIBDIR=lib \ |
| -DARROW_BUILD_SHARED=ON \ |
| -DARROW_BUILD_STATIC=OFF \ |
| -DARROW_DEPENDENCY_SOURCE=BUNDLED \ |
| -DARROW_PARQUET=ON \ |
| -DARROW_DATASET=ON \ |
| -DARROW_ACERO=ON \ |
| -DARROW_ORC=ON \ |
| -DARROW_CSV=ON \ |
| -DARROW_JSON=ON \ |
| -DARROW_COMPUTE=ON \ |
| -DARROW_FILESYSTEM=ON \ |
| -DARROW_WITH_SNAPPY=ON \ |
| -DARROW_WITH_ZSTD=ON \ |
| -DARROW_WITH_LZ4=ON \ |
| -DARROW_WITH_BZ2=ON \ |
| -DARROW_WITH_BROTLI=ON \ |
| -DARROW_WITH_ZLIB=ON \ |
| -DARROW_WITH_UTF8PROC=ON \ |
| -DARROW_WITH_RE2=ON \ |
| -DARROW_S3=OFF \ |
| -DARROW_GCS=OFF \ |
| -DARROW_AZURE=OFF \ |
| -DARROW_FLIGHT=OFF \ |
| -DARROW_JEMALLOC=OFF \ |
| -DARROW_MIMALLOC=OFF \ |
| -DARROW_BUILD_TESTS=OFF \ |
| -DARROW_BUILD_BENCHMARKS=OFF \ |
| -DARROW_BUILD_EXAMPLES=OFF \ |
| -DARROW_BUILD_INTEGRATION=OFF \ |
| -DARROW_BUILD_UTILITIES=OFF \ |
| -DARROW_PYTHON=OFF |
| cmake --build arrow-build --target install -j "$(sysctl -n hw.ncpu)" |
| |
| - name: Download sdist artifact |
| uses: actions/download-artifact@v4 |
| with: |
| name: sdist |
| path: sdist |
| |
| - name: Extract sdist |
| shell: bash |
| run: | |
| set -euxo pipefail |
| ls -lah sdist |
| SDIST_FILE="" |
| for f in sdist/*.tar.gz sdist/*.zip; do |
| if [ -f "$f" ]; then |
| SDIST_FILE="$f" |
| break |
| fi |
| done |
| if [ -z "$SDIST_FILE" ]; then |
| echo "No sdist file found in sdist/" >&2 |
| exit 1 |
| fi |
| mkdir -p sdist_pkg |
| case "$SDIST_FILE" in |
| *.tar.gz) tar -xzf "$SDIST_FILE" -C sdist_pkg ;; |
| *.zip) unzip -q "$SDIST_FILE" -d sdist_pkg ;; |
| esac |
| PKGDIR="$(find sdist_pkg -mindepth 1 -maxdepth 1 -type d | head -n 1)" |
| if [ -z "$PKGDIR" ]; then |
| echo "Failed to locate extracted sdist directory" >&2 |
| exit 1 |
| fi |
| echo "PKGDIR=$PKGDIR" >> "$GITHUB_ENV" |
| |
| - name: Build wheels |
| shell: bash |
| run: | |
| set -euxo pipefail |
| python -m pip install --upgrade pip |
| if [ "${{ matrix.os }}" = "macos" ]; then |
| python -m pip install packaging "cibuildwheel[uv]>=2.22" |
| else |
| python -m pip install packaging "cibuildwheel>=2.22" |
| fi |
| mkdir -p python/dist |
| python -m cibuildwheel --output-dir python/dist "$PKGDIR" |
| |
| - name: Store artifacts |
| uses: actions/upload-artifact@v4 |
| with: |
| name: wheels-${{ matrix.os }}-${{ matrix.platform }} |
| path: python/dist/* |
| |
| - name: Smoke test wheel |
| shell: bash |
| run: | |
| set -euxo pipefail |
| python -m pip uninstall -y graphar || true |
| |
| python -m pip install --find-links python/dist graphar |
| |
| python -c "import graphar; import graphar._core; print('GraphAr imported successfully')" |
| graphar --help >/dev/null |
| |
| upload_test_pypi: |
| name: Publish to TestPyPI (auto) |
| needs: [build_wheels, build_sdist] |
| runs-on: ubuntu-24.04 |
| if: github.event_name == 'push' |
| permissions: |
| contents: read |
| id-token: write |
| steps: |
| - name: Download artifacts |
| uses: actions/download-artifact@v4 |
| with: |
| path: dist |
| |
| - name: Move artifacts to correct location |
| run: | |
| mkdir -p python/dist |
| find dist -name "*" -type f -exec mv {} python/dist/ \; |
| - name: Publish to Test PyPI |
| uses: pypa/gh-action-pypi-publish@release/v1 |
| with: |
| repository-url: https://test.pypi.org/legacy/ |
| packages-dir: python/dist/ |
| |
| upload_pypi: |
| name: Publish (manual) |
| needs: [build_wheels, build_sdist] |
| runs-on: ubuntu-24.04 |
| if: github.event_name == 'workflow_dispatch' && inputs.publish_pypi |
| permissions: |
| contents: read |
| id-token: write |
| steps: |
| - name: Download artifacts |
| uses: actions/download-artifact@v4 |
| with: |
| path: dist |
| |
| - name: Move artifacts to correct location |
| run: | |
| mkdir -p python/dist |
| find dist -name "*" -type f -exec mv {} python/dist/ \; |
| - name: Publish to PyPI |
| uses: pypa/gh-action-pypi-publish@release/2473ec6c6aa87f38946284d51289219fd0b87264 |
| with: |
| packages-dir: python/dist/ |